[WIP] Card component (#115)

* Start on card component

* Add stories for card

* Add card actions component

* Right align actions

* Update storybook

* Update readme, tweak dialog card spacing

* Fix tests

* Move type styles into mixins
This commit is contained in:
Rijk van Zanten
2020-03-16 15:21:59 -04:00
committed by GitHub
parent a0c421e4e9
commit 08546e8105
12 changed files with 429 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
import VCard from './v-card.vue';
export { VCard };
export default VCard;

View File

@@ -0,0 +1,167 @@
# Card
Renders a card. A card is nothing but a v-sheet with predefined building blocks to enforce consistency.
## Usage
```html
<v-card>
<v-card-title>Hello, world!</v-card-title>
<v-card-subtitle>This is a card</v-card-subtitle>
<v-card-text>Consectetur enim ullamco sint sit deserunt proident consectetur.</v-card-test>
<v-card-actions>
<v-button>Save</v-button>
<v-card-actions>
</v-card>
```
Cards can be used to consistently style dialogs:
```html
<v-dialog>
<template #activator="{ on }">
<v-button @click="on">Show dialog</v-button>
</template>
<v-card>
<v-card-title>Are you sure you want to delete 1 item?</v-card-title>
<v-card-actions>
<v-button secondary outlined>Cancel</v-button>
<v-button>Yes</v-button>
</v-card-actions>
</v-card>
</v-dialog>
```
## Props
| Prop | Description | Default |
|------------|---------------------------------------------------|---------|
| `disabled` | Disable the card, prevents all cursor interaction | `false` |
| `tile` | Render without rounded corners | `false` |
## Events
n/a
## Slots
| Slot | Description | Data |
|-----------|-------------|------|
| _default_ | | |
## CSS Variables
| Variable | Default |
|-----------------------------|--------------------|
| `--v-card-min-width` | `none` |
| `--v-card-max-width` | `400px` |
| `--v-card-min-height` | `none` |
| `--v-card-max-height` | `none` |
| `--v-card-padding` | `16px` |
| `--v-card-background-color` | `var(--highlight)` |
---
# Card Title
Functional component that enforces consistent styling.
## Usage
```html
<v-card-title>Hello, world!</v-card-title>
```
## Props
n/a
## Events
n/a
## Slots
| Slot | Description | Data |
|-----------|-------------|------|
| _default_ | | |
## CSS Variables
n/a
---
# Card Subtitle
Functional component that enforces consistent styling.
## Usage
```html
<v-card-subtitle>Hello from the subtitle</v-card-subtitle>
```
## Props
n/a
## Events
n/a
## Slots
| Slot | Description | Data |
|-----------|-------------|------|
| _default_ | | |
## CSS Variables
n/a
---
# Card Text
Functional component that enforces consistent styling.
## Usage
```html
<v-card-text>Nisi anim deserunt Lorem reprehenderit laborum.</v-card-text>
```
## Props
n/a
## Events
n/a
## Slots
| Slot | Description | Data |
|-----------|-------------|------|
| _default_ | | |
## CSS Variables
n/a
---
# Card Actions
Functional component that enforces consistent styling.
## Usage
```html
<v-card-actions>
<v-button />
<v-button />
</v-card-actions>
```
## Props
n/a
## Events
n/a
## Slots
| Slot | Description | Data |
|-----------|-------------|------|
| _default_ | | |
## CSS Variables
n/a
---

View File

@@ -0,0 +1,15 @@
<template functional>
<div class="v-card-actions"><slot /></div>
</template>
<style lang="scss" scoped>
.v-card-actions {
display: flex;
justify-content: flex-end;
padding: var(--v-card-padding);
& ::v-deep > .v-button + .v-button {
margin-left: 8px;
}
}
</style>

View File

@@ -0,0 +1,15 @@
<template function>
<div class="v-card-subtitle"><slot /></div>
</template>
<style lang="scss" scoped>
@import '@/styles/mixins/type-styles';
.v-card-subtitle {
margin-top: -16px;
padding: 16px;
padding-top: 0;
@include type-card-subtitle;
}
</style>

View File

@@ -0,0 +1,14 @@
<template functional>
<div class="v-card-text"><slot /></div>
</template>
<style lang="scss" scoped>
@import '@/styles/mixins/type-styles';
.v-card-text {
padding: var(--v-card-padding);
padding-top: 0;
@include type-card-text;
}
</style>

View File

@@ -0,0 +1,17 @@
<template functional>
<div class="v-card-title"><slot /></div>
</template>
<style lang="scss" scoped>
@import '@/styles/mixins/type-styles';
.v-card-title {
display: flex;
flex-wrap: wrap;
align-items: center;
padding: var(--v-card-padding);
word-break: break-all;
@include type-card-title;
}
</style>

View File

@@ -0,0 +1,93 @@
import { withKnobs, boolean } from '@storybook/addon-knobs';
import Vue from 'vue';
import VCard from './v-card.vue';
import VCardTitle from './v-card-title.vue';
import VCardSubtitle from './v-card-subtitle.vue';
import VCardText from './v-card-text.vue';
import VCardActions from './v-card-actions.vue';
import markdown from './readme.md';
import withPadding from '../../../.storybook/decorators/with-padding';
import { defineComponent, ref } from '@vue/composition-api';
Vue.component('v-card', VCard);
Vue.component('v-card-title', VCardTitle);
Vue.component('v-card-subtitle', VCardSubtitle);
Vue.component('v-card-text', VCardText);
Vue.component('v-card-actions', VCardActions);
export default {
title: 'Components / Card',
decorators: [withKnobs, withPadding],
parameters: {
notes: markdown
}
};
export const basic = () =>
defineComponent({
props: {
disabled: {
default: boolean('Disabled', false)
},
tile: {
default: boolean('Tile', false)
}
},
template: `
<v-card :disabled="disabled" :tile="tile">
<v-card-title>Hello World!</v-card-title>
<v-card-subtitle>This is the subtitle</v-card-subtitle>
<v-card-text>Black ray-bans, you know she's with the band. Such a sight to see and it's all for me. Heaven is jealous of our love, angels are crying from up above. Turned the bedroom into a fair (a fair!) Its in the palm of your hand now baby.</v-card-text>
</v-card>
`
});
export const withImage = () =>
defineComponent({
template: `
<v-card :disabled="disabled" :tile="tile">
<img src="https://images.unsplash.com/photo-1581587118469-a117038c0249?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=600&ixlib=rb-1.2.1&q=80&w=800" width="800" height="600" style="width: 100%; display: block; height: auto; "/>
<v-card-title>Hello World!</v-card-title>
<v-card-subtitle>This is the subtitle</v-card-subtitle>
<v-card-text>Black ray-bans, you know she's with the band. Such a sight to see and it's all for me. Heaven is jealous of our love, angels are crying from up above. Turned the bedroom into a fair (a fair!) Its in the palm of your hand now baby.</v-card-text>
</v-card>
`
});
export const withActions = () =>
defineComponent({
template: `
<v-card :disabled="disabled" :tile="tile">
<v-card-title>Hello World!</v-card-title>
<v-card-subtitle>This is the subtitle</v-card-subtitle>
<v-card-text>Black ray-bans, you know she's with the band. Such a sight to see and it's all for me. Heaven is jealous of our love, angels are crying from up above. Turned the bedroom into a fair (a fair!) Its in the palm of your hand now baby.</v-card-text>
<v-card-actions>
<v-button small secondary>Click me</v-button>
<v-button small>Click me</v-button>
</v-card-actions>
</v-card>
`
});
export const asDialog = () =>
defineComponent({
setup() {
const active = ref(false);
return { active };
},
template: `
<v-dialog v-model="active">
<template #activator="{ on }">
<v-button @click="on">Show dialog</v-button>
</template>
<v-card>
<v-card-title>Are you sure you want to quit?</v-card-title>
<v-card-text>All unsaved changes will be lost.</v-card-text>
<v-card-actions>
<v-button secondary @click="active = !active">Cancel</v-button>
<v-button @click="active = !active">Quit</v-button>
</v-card-actions>
</v-card>
</v-dialog>
`
});

View File

@@ -0,0 +1,13 @@
import VCard from './v-card.vue';
import VueCompositionAPI from '@vue/composition-api';
import { shallowMount, createLocalVue } from '@vue/test-utils';
const localVue = createLocalVue();
localVue.use(VueCompositionAPI);
describe('Components / Card', () => {
it('Renders', () => {
const component = shallowMount(VCard, { localVue });
expect(component.isVueInstance()).toBe(true);
});
});

View File

@@ -0,0 +1,61 @@
<template>
<div class="v-card" :class="{ disabled, tile }">
<slot />
</div>
</template>
<script lang="ts">
import { defineComponent } from '@vue/composition-api';
export default defineComponent({
props: {
disabled: {
type: Boolean,
default: false
},
tile: {
type: Boolean,
default: false
}
},
setup() {
return {};
}
});
</script>
<style lang="scss" scoped>
.v-card {
--v-card-min-width: none;
--v-card-max-width: 400px;
--v-card-min-height: none;
--v-card-max-height: none;
--v-card-padding: 16px;
--v-card-background-color: var(--highlight);
min-width: var(--v-card-min-width);
max-width: var(--v-card-max-width);
min-height: var(--v-card-min-height);
max-height: var(--v-card-max-height);
background-color: var(--v-card-background-color);
border-radius: var(--input-border-radius);
& > :first-child {
border-top-left-radius: var(--input-border-radius);
border-top-right-radius: var(--input-border-radius);
}
&.disabled {
cursor: not-allowed;
pointer-events: none;
& > * {
opacity: 0.4;
}
}
&.tile {
border-radius: 0;
}
}
</style>

View File

@@ -68,6 +68,11 @@ export default defineComponent({
transition: opacity var(--medium) var(--transition);
pointer-events: none;
.v-card {
--v-card-min-width: 400px;
--v-card-padding: 24px;
}
.v-sheet {
--v-sheet-padding: 24px;
}

View File

@@ -76,3 +76,26 @@
font-family: var(--family-sans-serif);
line-height: 26px;
}
@mixin type-card-title {
color: var(--heading-text-color);
font-weight: 500;
font-size: 1.25rem;
line-height: 2rem;
letter-spacing: 0.0125em;
}
@mixin type-card-subtitle {
color: var(--foreground-color-secondary);
font-weight: 400;
font-size: 0.875rem;
line-height: 1.375rem;
letter-spacing: 0.007em;
}
@mixin type-card-text {
font-weight: 400;
font-size: 0.875rem;
line-height: 1.375rem;
letter-spacing: 0.007em;
}

View File

@@ -3,10 +3,12 @@ import VueCompositionAPI, { ref } from '@vue/composition-api';
import DrawerDetail from './drawer-detail.vue';
import * as GroupableComposition from '@/compositions/groupable/groupable';
import VIcon from '@/components/v-icon';
import TransitionExpand from '@/components/transition/expand';
const localVue = createLocalVue();
localVue.use(VueCompositionAPI);
localVue.component('v-icon', VIcon);
localVue.component('transition-expand', TransitionExpand);
describe('Drawer Detail', () => {
it('Uses the useGroupable composition', () => {