diff --git a/packages/dialtone-css/lib/build/less/components/notice.less b/packages/dialtone-css/lib/build/less/components/notice.less index 6ed13e7913..c9fc12d898 100644 --- a/packages/dialtone-css/lib/build/less/components/notice.less +++ b/packages/dialtone-css/lib/build/less/components/notice.less @@ -41,6 +41,14 @@ box-shadow: var(--notice-box-shadow); } +.d-toast-alternate { + --notice-alternate-border: var(--dt-size-100) solid; + --notice-alternate-border-color: var(--dt-color-border-subtle); + + border: var(--notice-alternate-border); + border-color: var(--notice-alternate-border-color); +} + // ============================================================================ // $ NOTICE AREAS // ============================================================================ @@ -122,6 +130,10 @@ } } +.d-toast--failure { + --notice-alternate-border-color: var(--dt-color-border-critical-subtle); +} + // $$ INFO // ---------------------------------------------------------------------------- .d-notice--info, @@ -154,6 +166,10 @@ } } +.d-toast--positive { + --notice-alternate-border-color: var(--dt-color-border-success-subtle); +} + // $$ WARNING // ---------------------------------------------------------------------------- .d-notice--warning, @@ -170,6 +186,29 @@ } } +.d-toast--neutral { + --notice-alternate-border-color: var(--dt-color-border-subtle); +} + +// $$ ASSIST +// ---------------------------------------------------------------------------- +.d-toast--assist { + --notice-alternate-border-color: var(--dt-color-border-accent); +} + +// $$ PLAYBOOK +// ---------------------------------------------------------------------------- +.d-toast--playbook { + --notice-alternate-border-color: var(--dt-color-border-accent); +} + +// $$ CHAT +// ---------------------------------------------------------------------------- +.d-toast--chat { + --notice-alternate-border-color: var(--dt-color-border-subtle); +} + + // $$ TRUNCATE TEXT // ---------------------------------------------------------------------------- .d-notice.d-notice--truncate { diff --git a/packages/dialtone-vue2/components/notice/notice_constants.js b/packages/dialtone-vue2/components/notice/notice_constants.js index af02d3f07e..cce3c53d2e 100644 --- a/packages/dialtone-vue2/components/notice/notice_constants.js +++ b/packages/dialtone-vue2/components/notice/notice_constants.js @@ -1,4 +1,4 @@ -export const NOTICE_KINDS = ['base', 'error', 'info', 'success', 'warning']; +export const NOTICE_KINDS = ['base', 'error', 'info', 'success', 'warning', 'assist', 'playbook', 'chat', 'system']; export const NOTICE_ROLES = ['alert', 'alertdialog', 'status']; export default { diff --git a/packages/dialtone-vue2/components/notice/notice_icon.vue b/packages/dialtone-vue2/components/notice/notice_icon.vue index 0c871105bf..b76706c32c 100644 --- a/packages/dialtone-vue2/components/notice/notice_icon.vue +++ b/packages/dialtone-vue2/components/notice/notice_icon.vue @@ -21,6 +21,9 @@ import { DtIconAlertTriangle, DtIconAlertCircle, DtIconBell, + DtIconDialpadSparkle, + DtIconMessage, + DtIconNotes, } from '@dialpad/dialtone-icons/vue2'; import { NOTICE_KINDS } from './notice_constants.js'; @@ -30,6 +33,10 @@ const kindToIcon = new Map([ ['warning', DtIconAlertTriangle], ['error', DtIconAlertCircle], ['base', DtIconBell], + ['assist', DtIconDialpadSparkle], + ['chat', DtIconMessage], + ['playbook', DtIconNotes], + ['system', DtIconInfo], ]); export default { @@ -41,6 +48,9 @@ export default { DtIconAlertTriangle, DtIconAlertCircle, DtIconBell, + DtIconDialpadSparkle, + DtIconMessage, + DtIconNotes, }, props: { diff --git a/packages/dialtone-vue2/components/toast/layouts/toast_layout_alternate.vue b/packages/dialtone-vue2/components/toast/layouts/toast_layout_alternate.vue new file mode 100644 index 0000000000..c80ea96676 --- /dev/null +++ b/packages/dialtone-vue2/components/toast/layouts/toast_layout_alternate.vue @@ -0,0 +1,184 @@ + + + + + diff --git a/packages/dialtone-vue2/components/toast/layouts/toast_layout_default.test.js b/packages/dialtone-vue2/components/toast/layouts/toast_layout_default.test.js new file mode 100644 index 0000000000..052694d35a --- /dev/null +++ b/packages/dialtone-vue2/components/toast/layouts/toast_layout_default.test.js @@ -0,0 +1,44 @@ +import { createLocalVue } from '@vue/test-utils'; +import ToastLayoutDefault from './toast_layout_default.vue'; + +const testContext = {}; + +describe('Toast Default Layout Tests', () => { + beforeAll(() => { + testContext.localVue = createLocalVue(); + }); + + describe('Validation Tests', () => { + describe('Role Validator', () => { + const MOCK_PROP = ToastLayoutDefault.props.role; + + describe('When provided role is in TOAST_ROLES', () => { + it('passes custom prop validation', () => { + expect(MOCK_PROP.validator(MOCK_PROP.default)).toBe(true); + }); + }); + + describe('When provided role is not in TOAST_ROLES', () => { + it('fails custom prop validation', () => { + expect(MOCK_PROP.validator(`INVALID_ROLE`)).toBe(false); + }); + }); + }); + + describe('Kind Validator', () => { + const MOCK_PROP = ToastLayoutDefault.props.kind; + + describe('When provided kind is in NOTICE_KINDS', () => { + it('passes custom prop validation', () => { + expect(MOCK_PROP.validator(MOCK_PROP.default)).toBe(true); + }); + }); + + describe('When provided kind is not in NOTICE_KINDS', () => { + it('fails custom prop validation', () => { + expect(MOCK_PROP.validator(`INVALID_KIND`)).toBe(false); + }); + }); + }); + }); +}); diff --git a/packages/dialtone-vue2/components/toast/layouts/toast_layout_default.vue b/packages/dialtone-vue2/components/toast/layouts/toast_layout_default.vue new file mode 100644 index 0000000000..95dc9be95b --- /dev/null +++ b/packages/dialtone-vue2/components/toast/layouts/toast_layout_default.vue @@ -0,0 +1,193 @@ + + + diff --git a/packages/dialtone-vue2/components/toast/toast.mdx b/packages/dialtone-vue2/components/toast/toast.mdx index 209775d154..e678f3e30a 100644 --- a/packages/dialtone-vue2/components/toast/toast.mdx +++ b/packages/dialtone-vue2/components/toast/toast.mdx @@ -1,5 +1,4 @@ import { Meta } from '@storybook/blocks'; - import * as ToastStories from './toast.stories'; import RedirectToDocs from "@/common/snippets/redirect-to-docs.mdx" diff --git a/packages/dialtone-vue2/components/toast/toast.test.js b/packages/dialtone-vue2/components/toast/toast.test.js index c8c999d40a..847b4edeb2 100644 --- a/packages/dialtone-vue2/components/toast/toast.test.js +++ b/packages/dialtone-vue2/components/toast/toast.test.js @@ -1,4 +1,4 @@ -import { createLocalVue, shallowMount } from '@vue/test-utils'; +import { createLocalVue, mount } from '@vue/test-utils'; import DtToast from './toast.vue'; import { TOAST_MIN_DURATION } from './toast_constants'; @@ -12,21 +12,21 @@ const testContext = {}; describe('DtToast Tests', () => { let wrapper; let toast; - let actionChildStub; - let contentChildStub; - let iconChildStub; + let actionChild; + let contentChild; + let iconChild; const updateWrapper = () => { - wrapper = shallowMount(DtToast, { + wrapper = mount(DtToast, { propsData: { ...baseProps, ...mockProps }, slots: { ...baseSlots, ...mockSlots }, localVue: testContext.localVue, }); toast = wrapper.find('[data-qa="dt-toast"]'); - actionChildStub = wrapper.find('dt-notice-action-stub'); - contentChildStub = wrapper.find('dt-notice-content-stub'); - iconChildStub = wrapper.find('dt-notice-icon-stub'); + actionChild = wrapper.findComponent({ name: 'dt-notice-action' }); + contentChild = wrapper.findComponent({ name: 'dt-notice-content' }); + iconChild = wrapper.findComponent({ name: 'dt-notice-icon' }); }; beforeAll(() => { @@ -65,15 +65,15 @@ describe('DtToast Tests', () => { }); it('action slot is passed down correctly', () => { - expect(actionChildStub.text()).toBe(mockSlots.action); + expect(actionChild.text()).toBe(mockSlots.action); }); it('default slot is passed down correctly', () => { - expect(contentChildStub.text()).toBe(mockSlots.default); + expect(contentChild.text()).toBe(mockSlots.default); }); it('icon slot is passed down correctly', () => { - expect(iconChildStub.text()).toBe(mockSlots.icon); + expect(iconChild.text()).toBe(mockSlots.icon); }); }); @@ -91,23 +91,23 @@ describe('DtToast Tests', () => { }); it('titleId prop is passed down correctly', () => { - expect(contentChildStub.props('titleId')).toBe(mockProps.titleId); + expect(contentChild.props('titleId')).toBe(mockProps.titleId); }); it('contentId prop is passed down correctly', () => { - expect(contentChildStub.props('contentId')).toBe(mockProps.contentId); + expect(contentChild.props('contentId')).toBe(mockProps.contentId); }); it('title prop is passed down correctly', () => { - expect(contentChildStub.props('title')).toBe(mockProps.title); + expect(contentChild.props('title')).toBe(mockProps.title); }); it('message prop is passed down correctly', () => { - expect(contentChildStub.text()).toBe(mockProps.message); + expect(contentChild.find('[data-qa="notice-content-message"]').text()).toBe(mockProps.message); }); it('hideClose prop is passed down correctly', () => { - expect(actionChildStub.props('hideClose')).toBe(mockProps.hideClose); + expect(actionChild.props('hideClose')).toBe(mockProps.hideClose); }); }); @@ -187,7 +187,7 @@ describe('DtToast Tests', () => { const MOCK_ROLE = DtToast.props.role.default; it('shows correct default role', () => { - expect(contentChildStub.attributes('role')).toBe(MOCK_ROLE); + expect(contentChild.attributes('role')).toBe(MOCK_ROLE); }); it('should have aria-hidden set to false when toast is shown', () => { @@ -201,44 +201,12 @@ describe('DtToast Tests', () => { updateWrapper(); - expect(contentChildStub.attributes('role')).toBe('alert'); + expect(contentChild.attributes('role')).toBe('alert'); }); }); }); describe('Validation Tests', () => { - describe('Role Validator', () => { - const MOCK_PROP = DtToast.props.role; - - describe('When provided role is in TOAST_ROLES', () => { - it('passes custom prop validation', () => { - expect(MOCK_PROP.validator(MOCK_PROP.default)).toBe(true); - }); - }); - - describe('When provided role is not in TOAST_ROLES', () => { - it('fails custom prop validation', () => { - expect(MOCK_PROP.validator(`INVALID_ROLE`)).toBe(false); - }); - }); - }); - - describe('Kind Validator', () => { - const MOCK_PROP = DtToast.props.kind; - - describe('When provided kind is in NOTICE_KINDS', () => { - it('passes custom prop validation', () => { - expect(MOCK_PROP.validator(MOCK_PROP.default)).toBe(true); - }); - }); - - describe('When provided kind is not in NOTICE_KINDS', () => { - it('fails custom prop validation', () => { - expect(MOCK_PROP.validator(`INVALID_KIND`)).toBe(false); - }); - }); - }); - describe('Duration Validator', () => { const MOCK_PROP = DtToast.props.duration; const MOCK_DURATION = TOAST_MIN_DURATION; @@ -272,7 +240,7 @@ describe('DtToast Tests', () => { updateWrapper(); - MOCK_ELEMENT = actionChildStub; + MOCK_ELEMENT = actionChild; expect(MOCK_ELEMENT.props(MOCK_PROP_NAME)).toBe(MOCK_PROP_VALUE); }); diff --git a/packages/dialtone-vue2/components/toast/toast.vue b/packages/dialtone-vue2/components/toast/toast.vue index 36b559f3ec..43a11107b0 100644 --- a/packages/dialtone-vue2/components/toast/toast.vue +++ b/packages/dialtone-vue2/components/toast/toast.vue @@ -1,60 +1,46 @@ diff --git a/packages/dialtone-vue2/components/toast/toast_constants.js b/packages/dialtone-vue2/components/toast/toast_constants.js index 5ab15dc8ca..8e3d47452e 100644 --- a/packages/dialtone-vue2/components/toast/toast_constants.js +++ b/packages/dialtone-vue2/components/toast/toast_constants.js @@ -1,7 +1,9 @@ export const TOAST_ROLES = ['status', 'alert']; export const TOAST_MIN_DURATION = 6000; +export const TOAST_LAYOUTS = ['default', 'alternate']; export default { TOAST_ROLES, TOAST_MIN_DURATION, + TOAST_LAYOUTS, }; diff --git a/packages/dialtone-vue2/components/toast/toast_default.story.vue b/packages/dialtone-vue2/components/toast/toast_default.story.vue index b844adfe4f..ab0c291214 100644 --- a/packages/dialtone-vue2/components/toast/toast_default.story.vue +++ b/packages/dialtone-vue2/components/toast/toast_default.story.vue @@ -18,10 +18,11 @@ :hide-action="$attrs.hideAction" :hide-icon="$attrs.hideIcon" :duration="$attrs.duration" + :layout="$attrs.layout" :close-button-props="buttonCloseProps" :visually-hidden-close="$attrs.visuallyHiddenClose" :visually-hidden-close-label="$attrs.visuallyHiddenCloseLabel" - @close="$attrs.onClose($event)" + @close="$attrs.onClose" > + + + + diff --git a/packages/dialtone-vue3/components/toast/layouts/toast_layout_default.test.js b/packages/dialtone-vue3/components/toast/layouts/toast_layout_default.test.js new file mode 100644 index 0000000000..ec447f682c --- /dev/null +++ b/packages/dialtone-vue3/components/toast/layouts/toast_layout_default.test.js @@ -0,0 +1,37 @@ +import ToastLayoutDefault from './toast_layout_default.vue'; + +describe('Toast Default Layout Tests', () => { + describe('Validation Tests', () => { + describe('Role Validator', () => { + const MOCK_PROP = ToastLayoutDefault.props.role; + + describe('When provided role is in TOAST_ROLES', () => { + it('passes custom prop validation', () => { + expect(MOCK_PROP.validator(MOCK_PROP.default)).toBe(true); + }); + }); + + describe('When provided role is not in TOAST_ROLES', () => { + it('fails custom prop validation', () => { + expect(MOCK_PROP.validator(`INVALID_ROLE`)).toBe(false); + }); + }); + }); + + describe('Kind Validator', () => { + const MOCK_PROP = ToastLayoutDefault.props.kind; + + describe('When provided kind is in NOTICE_KINDS', () => { + it('passes custom prop validation', () => { + expect(MOCK_PROP.validator(MOCK_PROP.default)).toBe(true); + }); + }); + + describe('When provided kind is not in NOTICE_KINDS', () => { + it('fails custom prop validation', () => { + expect(MOCK_PROP.validator(`INVALID_KIND`)).toBe(false); + }); + }); + }); + }); +}); diff --git a/packages/dialtone-vue3/components/toast/layouts/toast_layout_default.vue b/packages/dialtone-vue3/components/toast/layouts/toast_layout_default.vue new file mode 100644 index 0000000000..ab46fc093a --- /dev/null +++ b/packages/dialtone-vue3/components/toast/layouts/toast_layout_default.vue @@ -0,0 +1,198 @@ + + + diff --git a/packages/dialtone-vue3/components/toast/toast.mdx b/packages/dialtone-vue3/components/toast/toast.mdx index 209775d154..e678f3e30a 100644 --- a/packages/dialtone-vue3/components/toast/toast.mdx +++ b/packages/dialtone-vue3/components/toast/toast.mdx @@ -1,5 +1,4 @@ import { Meta } from '@storybook/blocks'; - import * as ToastStories from './toast.stories'; import RedirectToDocs from "@/common/snippets/redirect-to-docs.mdx" diff --git a/packages/dialtone-vue3/components/toast/toast.stories.js b/packages/dialtone-vue3/components/toast/toast.stories.js index 0017626c1e..23550a229c 100644 --- a/packages/dialtone-vue3/components/toast/toast.stories.js +++ b/packages/dialtone-vue3/components/toast/toast.stories.js @@ -4,6 +4,7 @@ import DtToast from './toast.vue'; import DtToastDefaultTemplate from './toast_default.story.vue'; import { NOTICE_KINDS } from '../notice'; +import { TOAST_LAYOUTS } from './toast_constants.js'; const iconsList = getIconNames(); @@ -75,6 +76,12 @@ export const argTypesData = { type: 'select', }, }, + layout: { + options: TOAST_LAYOUTS, + control: { + type: 'select', + }, + }, show: { table: { defaultValue: { diff --git a/packages/dialtone-vue3/components/toast/toast.test.js b/packages/dialtone-vue3/components/toast/toast.test.js index 5cf709641a..a59f885e5a 100644 --- a/packages/dialtone-vue3/components/toast/toast.test.js +++ b/packages/dialtone-vue3/components/toast/toast.test.js @@ -100,7 +100,7 @@ describe('DtToast Tests', () => { }); it('message prop is passed down correctly', () => { - expect(contentChild.text()).toBe(mockProps.message); + expect(contentChild.find('[data-qa="notice-content-message"]').text()).toBe(mockProps.message); }); it('hideClose prop is passed down correctly', () => { @@ -204,38 +204,6 @@ describe('DtToast Tests', () => { }); describe('Validation Tests', () => { - describe('Role Validator', () => { - const MOCK_PROP = DtToast.props.role; - - describe('When provided role is in TOAST_ROLES', () => { - it('passes custom prop validation', () => { - expect(MOCK_PROP.validator(MOCK_PROP.default)).toBe(true); - }); - }); - - describe('When provided role is not in TOAST_ROLES', () => { - it('fails custom prop validation', () => { - expect(MOCK_PROP.validator(`INVALID_ROLE`)).toBe(false); - }); - }); - }); - - describe('Kind Validator', () => { - const MOCK_PROP = DtToast.props.kind; - - describe('When provided kind is in NOTICE_KINDS', () => { - it('passes custom prop validation', () => { - expect(MOCK_PROP.validator(MOCK_PROP.default)).toBe(true); - }); - }); - - describe('When provided kind is not in NOTICE_KINDS', () => { - it('fails custom prop validation', () => { - expect(MOCK_PROP.validator(`INVALID_KIND`)).toBe(false); - }); - }); - }); - describe('Duration Validator', () => { const MOCK_PROP = DtToast.props.duration; const MOCK_DURATION = TOAST_MIN_DURATION; diff --git a/packages/dialtone-vue3/components/toast/toast.vue b/packages/dialtone-vue3/components/toast/toast.vue index f9c8295a62..cf30bb305f 100644 --- a/packages/dialtone-vue3/components/toast/toast.vue +++ b/packages/dialtone-vue3/components/toast/toast.vue @@ -1,58 +1,46 @@ diff --git a/packages/dialtone-vue3/components/toast/toast_constants.js b/packages/dialtone-vue3/components/toast/toast_constants.js index 5ab15dc8ca..8e3d47452e 100644 --- a/packages/dialtone-vue3/components/toast/toast_constants.js +++ b/packages/dialtone-vue3/components/toast/toast_constants.js @@ -1,7 +1,9 @@ export const TOAST_ROLES = ['status', 'alert']; export const TOAST_MIN_DURATION = 6000; +export const TOAST_LAYOUTS = ['default', 'alternate']; export default { TOAST_ROLES, TOAST_MIN_DURATION, + TOAST_LAYOUTS, }; diff --git a/packages/dialtone-vue3/components/toast/toast_default.story.vue b/packages/dialtone-vue3/components/toast/toast_default.story.vue index 6552f09bff..f1b1fd6014 100644 --- a/packages/dialtone-vue3/components/toast/toast_default.story.vue +++ b/packages/dialtone-vue3/components/toast/toast_default.story.vue @@ -18,10 +18,11 @@ :hide-action="$attrs.hideAction" :hide-icon="$attrs.hideIcon" :duration="$attrs.duration" + :layout="$attrs.layout" :close-button-props="buttonCloseProps" :visually-hidden-close="$attrs.visuallyHiddenClose" :visually-hidden-close-label="$attrs.visuallyHiddenCloseLabel" - @close="$attrs.onClose($event)" + @close="$attrs.onClose" >