mirror of
https://github.com/directus/directus.git
synced 2026-02-19 10:14:33 -05:00
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:
4
src/components/v-notice/index.ts
Normal file
4
src/components/v-notice/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import VNotice from './v-notice.vue';
|
||||
|
||||
export { VNotice };
|
||||
export default VNotice;
|
||||
28
src/components/v-notice/v-notice.readme.md
Normal file
28
src/components/v-notice/v-notice.readme.md
Normal 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);` |
|
||||
85
src/components/v-notice/v-notice.story.ts
Normal file
85
src/components/v-notice/v-notice.story.ts
Normal 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>
|
||||
`
|
||||
});
|
||||
79
src/components/v-notice/v-notice.test.ts
Normal file
79
src/components/v-notice/v-notice.test.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
98
src/components/v-notice/v-notice.vue
Normal file
98
src/components/v-notice/v-notice.vue
Normal 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>
|
||||
Reference in New Issue
Block a user