Notice component (#44)

* initial commit

* finish up notice

* fix unit tests

* Add icon prop

* Use css vars for colors and update readme / story

* Use warning icon for warnings

* Only allow one class at a time

* Add tests for icon prop / success default

* Remove unneeded class

Co-authored-by: Rijk van Zanten <rijkvanzanten@me.com>
This commit is contained in:
Nitwel
2020-02-18 18:42:57 +01:00
committed by GitHub
parent 42ad64f5e7
commit 08873c3e88
5 changed files with 294 additions and 0 deletions

View File

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

View File

@@ -0,0 +1,28 @@
# Notice
```html
<v-notice>I'm a notice!</v-notice>
```
## Props
| Prop | Description | Default |
|-----------|--------------------------------------------------------------------|---------|
| `success` | Shows the success notice | `false` |
| `warning` | Shows the warning notice | `false` |
| `danger` | Shows the danger notice | `false` |
| `icon` | Custom icon name, or false if you want to hide the icon completely | `null` |
## Slots
| Slot | Description | Data |
|-----------|-------------|------|
| _default_ | | -- |
## Events
n/a
## CSS Variables
| Variable | Default |
|-------------------------------|----------------------------|
| `--v-notice-color` | `var(--foreground-color);` |
| `--v-notice-background-color` | `var(--action-light);` |
| `--v-notice-icon-color` | `var(--action);` |

View File

@@ -0,0 +1,85 @@
import {
withKnobs,
text,
boolean,
number,
color,
select,
optionsKnob as options
} from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import Vue from 'vue';
import VNotice from './v-notice.vue';
import VIcon from '../v-icon/';
import markdown from './v-notice.readme.md';
import withPadding from '../../../.storybook/decorators/with-padding';
Vue.component('v-notice', VNotice);
Vue.component('v-icon', VIcon);
export default {
title: 'Components / Notice',
component: VNotice,
decorators: [withKnobs, withPadding],
parameters: {
notes: markdown
}
};
export const withText = () => ({
props: {
text: {
default: text('Text', 'This is a notice')
},
success: {
default: boolean('Success', false)
},
warning: {
default: boolean('Warning', false)
},
danger: {
default: boolean('Danger', false)
}
},
template: `<v-notice :success="success" :warning="warning" :danger="danger">{{text}}</v-notice>`
});
export const withCustomColors = () => ({
props: {
color: {
default: text('Color', 'red')
},
backgroundColor: {
default: text('Background Color', 'papayawhip')
},
iconColor: {
default: text('Icon Color', 'blue')
}
},
template: `
<v-notice
:style="{
'--v-notice-color': color,
'--v-notice-background-color': backgroundColor,
'--v-notice-icon-color': iconColor
}"
>This is a notice</v-notice>
`
});
export const withCustomIcon = () => ({
props: {
icon: {
default: text('Icon', 'translate')
}
},
template: `
<v-notice :icon="icon">This is a notice</v-notice>
`
});
export const noIcon = () => ({
template: `
<v-notice :icon="false">This is a notice</v-notice>
`
});

View File

@@ -0,0 +1,79 @@
import { mount, createLocalVue, Wrapper } from '@vue/test-utils';
import VueCompositionAPI from '@vue/composition-api';
import VNotice from './v-notice.vue';
import VIcon from '@/components/v-icon/';
const localVue = createLocalVue();
localVue.use(VueCompositionAPI);
localVue.component('v-icon', VIcon);
describe('Notice', () => {
let component: Wrapper<Vue>;
beforeEach(() => {
component = mount(VNotice, {
localVue,
slots: {
default: 'I like pizza'
}
});
});
it('Renders the default slot in the notice', () => {
expect(component.text()).toContain('I like pizza');
});
it('Uses the right color / icon combo for success', async () => {
component.setProps({
success: true
});
await component.vm.$nextTick();
expect(component.classes()).toContain('success');
expect((component.vm as any).iconName).toBe('check_circle');
});
it('Uses the right color / icon combo for warning', async () => {
component.setProps({
warning: true
});
await component.vm.$nextTick();
expect(component.classes()).toContain('warning');
expect((component.vm as any).iconName).toBe('warning');
});
it('Uses the right color / icon combo for danger', async () => {
component.setProps({
danger: true
});
await component.vm.$nextTick();
expect(component.classes()).toContain('danger');
expect((component.vm as any).iconName).toBe('error');
});
it('Defaults to success if all props are given', async () => {
component.setProps({
success: true,
warning: true,
danger: true
});
await component.vm.$nextTick();
expect((component.vm as any).iconName).toBe('check_circle');
expect(component.classes()).toContain('success');
});
it('Allows setting a custom icon', async () => {
component.setProps({
icon: 'person'
});
await component.vm.$nextTick();
expect((component.vm as any).iconName).toBe('person');
});
});

View File

@@ -0,0 +1,98 @@
<template>
<div class="v-notice" :class="className">
<v-icon v-if="icon !== false" :name="iconName" left />
<slot />
</div>
</template>
<script lang="ts">
import { createComponent, computed } from '@vue/composition-api';
export default createComponent({
props: {
success: {
type: Boolean,
default: false
},
warning: {
type: Boolean,
default: false
},
danger: {
type: Boolean,
default: false
},
icon: {
type: [String, Boolean],
default: null
}
},
setup(props) {
const iconName = computed(() => {
if (props.icon !== false && typeof props.icon === 'string') {
return props.icon;
}
if (props.success) {
return 'check_circle';
} else if (props.warning) {
return 'warning';
} else if (props.danger) {
return 'error';
} else {
return 'info';
}
});
const className = computed<string | null>(() => {
if (props.success) {
return 'success';
} else if (props.warning) {
return 'warning';
} else if (props.danger) {
return 'danger';
} else {
return null;
}
});
return { iconName, className };
}
});
</script>
<style lang="scss" scoped>
.v-notice {
--v-notice-color: var(--foreground-color);
--v-notice-background-color: var(--action-light);
--v-notice-icon-color: var(--action);
display: flex;
align-items: center;
justify-content: flex-start;
width: auto;
padding: 12px 16px;
color: var(--v-notice-color);
background-color: var(--v-notice-background-color);
border-radius: var(--input-border-radius);
.v-icon {
--v-icon-color: var(--v-notice-icon-color);
}
&.success {
--v-notice-icon-color: var(--success);
--v-notice-background-color: var(--success-light);
}
&.warning {
--v-notice-icon-color: var(--warning);
--v-notice-background-color: var(--warning-light);
}
&.danger {
--v-notice-icon-color: var(--danger);
--v-notice-background-color: var(--danger-light);
}
}
</style>