From ff3c4ffe20721249f7a7b2e0912fca27e0f00b31 Mon Sep 17 00:00:00 2001 From: lukaspoloki Date: Wed, 15 May 2024 16:02:39 +0200 Subject: [PATCH 1/3] chore: Add BccDialog component to design library --- .../src/components/BccDialog/BccDialog.css | 83 +++++++++++ .../components/BccDialog/BccDialog.spec.ts | 85 +++++++++++ .../components/BccDialog/BccDialog.stories.ts | 132 ++++++++++++++++++ .../src/components/BccDialog/BccDialog.vue | 131 +++++++++++++++++ design-library/src/css/index.css | 1 + design-library/src/index.ts | 1 + 6 files changed, 433 insertions(+) create mode 100644 design-library/src/components/BccDialog/BccDialog.css create mode 100644 design-library/src/components/BccDialog/BccDialog.spec.ts create mode 100644 design-library/src/components/BccDialog/BccDialog.stories.ts create mode 100644 design-library/src/components/BccDialog/BccDialog.vue diff --git a/design-library/src/components/BccDialog/BccDialog.css b/design-library/src/components/BccDialog/BccDialog.css new file mode 100644 index 00000000..a5df9d91 --- /dev/null +++ b/design-library/src/components/BccDialog/BccDialog.css @@ -0,0 +1,83 @@ + +@layer components { + .bcc-dialog { + @apply relative flex flex-col gap-6 rounded-lg bg-primary p-6 text-primary shadow-xl sm:min-w-[28.5rem] sm:max-w-[28.5rem] xl:max-w-[32rem] max-h-[80vh]; + } + + /* Overlay/Wrapper */ + .bcc-dialog-overlay-wrapper { + @apply fixed inset-0 z-50; + } + + .bcc-dialog-container { + @apply flex h-[100dvh] w-[100dvw] items-center justify-center overflow-hidden; + } + + .bcc-dialog-overlay { + @apply fixed inset-0 bg-black/80 backdrop-blur-sm transition-opacity; + } + + .bcc-dialog-wrapper { + @apply flex h-full w-full transform flex-col justify-end transition-all sm:items-center sm:justify-center p-4; + } + + /* Header */ + .bcc-dialog-header + .bcc-dialog-body { + @apply border-t border-on-primary pt-5; + } + + /* Body */ + .bcc-dialog-body { + @apply flex flex-col gap-2 overflow-y-auto; + } + + /* Title */ + .bcc-dialog-title { + @apply text-heading-lg flex justify-between items-start; + } + .bcc-dialog-close-button { + @apply text-black; + } + .bcc-dialog-close-icon { + @apply h-6 w-6; + } + + .bcc-dialog-subtitle { + @apply text-caption-lg text-secondary; + } + + /* Content */ + .bcc-dialog-content { + @apply text-body overflow-y-auto py-6; + } + + /* Footer */ + .bcc-dialog-footer { + @apply sticky bottom-0 left-0 right-0 bg-primary; + } + + .bcc-dialog-footer-content { + @apply mb-5; + } + + /* Actions */ + .bcc-dialog-actions { + @apply flex gap-4; + } + .bcc-dialog-secondary-action { + @apply flex-1 w-full; + button { + @apply w-full; + } + } + .bcc-dialog-primary-action { + @apply flex-1 w-full; + button { + @apply w-full; + } + } + + .bcc-dialog-alert .bcc-dialog-actions { + @apply justify-center; + } +} diff --git a/design-library/src/components/BccDialog/BccDialog.spec.ts b/design-library/src/components/BccDialog/BccDialog.spec.ts new file mode 100644 index 00000000..21717e8c --- /dev/null +++ b/design-library/src/components/BccDialog/BccDialog.spec.ts @@ -0,0 +1,85 @@ +import { describe, it, expect } from "vitest"; +import { mount } from "@vue/test-utils"; +import BccDialog from "./BccDialog.vue"; +import BccButton from "../BccButton/BccButton.vue"; + +describe("BccDialog", () => { + it("renders with default props", () => { + const wrapper = mount(BccDialog, { + props: { + open: true, + title: "Test Title", + }, + }); + + expect(wrapper.find(".bcc-dialog-title h3").text()).toBe("Test Title"); + expect(wrapper.find(".bcc-dialog-close-button").exists()).toBe(true); + expect(wrapper.find(".bcc-dialog-subtitle").exists()).toBe(false); + }); + + it("renders the subtitle when provided", () => { + const wrapper = mount(BccDialog, { + props: { + open: true, + title: "Test Title", + subtitle: "Test Subtitle", + }, + }); + + expect(wrapper.find(".bcc-dialog-subtitle").text()).toBe("Test Subtitle"); + }); + + it("hides the close button for alert variant", () => { + const wrapper = mount(BccDialog, { + props: { + open: true, + title: "Alert Title", + variant: "alert", + }, + }); + + expect(wrapper.find(".bcc-dialog-close-button").exists()).toBe(false); + }); + + it("renders primary button with danger variant for destructive action", () => { + const wrapper = mount(BccDialog, { + props: { + open: true, + title: "Destructive Action", + destructive: true, + }, + slots: { + primaryAction: "Submit", + }, + global: { + components: { + BccButton, + }, + }, + }); + + const primaryButton = wrapper.findComponent(BccButton); + expect(primaryButton.props("context")).toBe("danger"); + }); + + it("renders primary button with danger variant for alert", () => { + const wrapper = mount(BccDialog, { + props: { + open: true, + title: "Alert Title", + variant: "alert", + }, + slots: { + primaryAction: "Submit", + }, + global: { + components: { + BccButton, + }, + }, + }); + + const primaryButton = wrapper.findComponent(BccButton); + expect(primaryButton.props("context")).toBe("danger"); + }); +}); diff --git a/design-library/src/components/BccDialog/BccDialog.stories.ts b/design-library/src/components/BccDialog/BccDialog.stories.ts new file mode 100644 index 00000000..506f8f67 --- /dev/null +++ b/design-library/src/components/BccDialog/BccDialog.stories.ts @@ -0,0 +1,132 @@ +import BccButton from "../BccButton/BccButton.vue"; +import BccDialog from "./BccDialog.vue"; + +import type { Meta, StoryFn } from "@storybook/vue3"; + +export default { + title: "Common/BccDialog", + component: BccDialog, + argTypes: { + variant: { + control: { type: "select" }, + options: ["action", "alert"], + }, + destructive: { + control: { type: "boolean" }, + }, + }, +} as Meta; + +const Template: StoryFn = (args) => ({ + components: { BccDialog, BccButton }, + setup() { + return { args }; + }, + template: ` +
+ + + + + + + + + Open dialog +
+ `, +}); + +// Default story +export const Default = Template.bind({}); +Default.args = { + open: false, + title: "Question?", + subtitle: "Subtitle if needed", + closeButton: true, + variant: "action", + destructive: false, + slotDefault: "", + slotSecondaryAction: true, + slotPrimaryAction: true, +}; +Default.parameters = { + docs: { + source: { + language: "html", + code: ` + + Do you want to update something? + + + + + + `, + }, + }, +}; + +export const ActionVariant = Template.bind({}); +ActionVariant.args = { + ...Default.args, + variant: "action", + destructive: false, +}; + +export const DestructiveAction = Template.bind({}); +DestructiveAction.args = { + ...Default.args, + variant: "action", + destructive: true, +}; + +export const DestructiveAlert = Template.bind({}); +DestructiveAlert.args = { + ...Default.args, + variant: "alert", + destructive: true, // Enforce destructive for alert +}; + +export const NoActions = Template.bind({}); +NoActions.args = { + open: false, + title: "Question?", + closeButton: true, + variant: "action", + destructive: false, + slotDefault: "", + slotSecondaryAction: false, + slotPrimaryAction: false, +}; +NoActions.parameters = { + docs: { + source: { + language: "html", + code: ` + + Do you want to update something? + + `, + }, + }, +}; diff --git a/design-library/src/components/BccDialog/BccDialog.vue b/design-library/src/components/BccDialog/BccDialog.vue new file mode 100644 index 00000000..a987e157 --- /dev/null +++ b/design-library/src/components/BccDialog/BccDialog.vue @@ -0,0 +1,131 @@ + + + diff --git a/design-library/src/css/index.css b/design-library/src/css/index.css index 429d60eb..3326f699 100644 --- a/design-library/src/css/index.css +++ b/design-library/src/css/index.css @@ -34,3 +34,4 @@ @import "../components/BccTooltip/BccTooltip.css"; @import "../components/BccAccordion/BccAccordion.css"; @import "../components/BccPagination/BccPagination.css"; +@import "../components/BccDialog/BccDialog.css"; diff --git a/design-library/src/index.ts b/design-library/src/index.ts index a7d22e6d..e9bf2260 100644 --- a/design-library/src/index.ts +++ b/design-library/src/index.ts @@ -41,3 +41,4 @@ export { default as BccStepper } from "./components/BccStepper/BccStepper.vue"; export { default as BccTooltip } from "./components/BccTooltip/BccTooltip.vue"; export { default as BccAccordion } from "./components/BccAccordion/BccAccordion.vue"; export { default as BccPagination } from "./components/BccPagination/BccPagination.vue"; +export { default as BccDialog } from "./components/BccDialog/BccDialog.vue"; From 954ae0d077630068792d82d63e74cb96d1e6a876 Mon Sep 17 00:00:00 2001 From: lukaspoloki Date: Mon, 27 May 2024 09:13:57 +0200 Subject: [PATCH 2/3] fix tests --- .../components/BccDialog/BccDialog.spec.ts | 93 +++++++++++-------- 1 file changed, 54 insertions(+), 39 deletions(-) diff --git a/design-library/src/components/BccDialog/BccDialog.spec.ts b/design-library/src/components/BccDialog/BccDialog.spec.ts index 21717e8c..63f2ce1e 100644 --- a/design-library/src/components/BccDialog/BccDialog.spec.ts +++ b/design-library/src/components/BccDialog/BccDialog.spec.ts @@ -1,85 +1,100 @@ import { describe, it, expect } from "vitest"; import { mount } from "@vue/test-utils"; -import BccDialog from "./BccDialog.vue"; +import BccDialog from "./BccDialog.vue"; // replace with the actual component name import BccButton from "../BccButton/BccButton.vue"; describe("BccDialog", () => { - it("renders with default props", () => { + it("renders a dialog", () => { const wrapper = mount(BccDialog, { - props: { - open: true, - title: "Test Title", + props: { open: true, title: "Dialog title", subtitle: "Dialog subtitle" }, + slots: { + default: "Dialog content", + header: "Header slot", + footer: "Footer slot", + primaryAction: "Primary action", + secondaryAction: "Secondary action", + }, + global: { + renderStubDefaultSlot: true, }, + shallow: true, }); - expect(wrapper.find(".bcc-dialog-title h3").text()).toBe("Test Title"); - expect(wrapper.find(".bcc-dialog-close-button").exists()).toBe(true); - expect(wrapper.find(".bcc-dialog-subtitle").exists()).toBe(false); + expect(wrapper.text()).toContain("Header slot"); + expect(wrapper.text()).toContain("Dialog title"); + expect(wrapper.text()).toContain("Dialog subtitle"); + expect(wrapper.text()).toContain("Dialog content"); + expect(wrapper.text()).toContain("Footer slot"); + expect(wrapper.text()).toContain("Primary action"); + expect(wrapper.text()).toContain("Secondary action"); + expect(wrapper.html()).toMatchSnapshot(); }); - it("renders the subtitle when provided", () => { + it("closes when the close button is clicked", async () => { const wrapper = mount(BccDialog, { - props: { - open: true, - title: "Test Title", - subtitle: "Test Subtitle", + props: { open: true }, + slots: { + default: "Dialog content", }, + global: { + renderStubDefaultSlot: true, + }, + shallow: true, }); - expect(wrapper.find(".bcc-dialog-subtitle").text()).toBe("Test Subtitle"); + await wrapper.find(".bcc-dialog-close-button").trigger("click"); + + expect(wrapper.emitted("close")?.length).toBe(1); }); - it("hides the close button for alert variant", () => { + it("doesn't show the close button when there is a header", () => { const wrapper = mount(BccDialog, { - props: { - open: true, - title: "Alert Title", - variant: "alert", + props: { open: true }, + slots: { + default: "Dialog content", + header: "Header content", }, + global: { + renderStubDefaultSlot: true, + }, + shallow: true, }); - expect(wrapper.find(".bcc-dialog-close-button").exists()).toBe(false); + expect(wrapper.html()).not.toContain("bcc-dialog-close-button"); }); - it("renders primary button with danger variant for destructive action", () => { + it("renders the correct variant class", () => { const wrapper = mount(BccDialog, { - props: { - open: true, - title: "Destructive Action", - destructive: true, - }, + props: { open: true, variant: "alert" }, slots: { - primaryAction: "Submit", + default: "Dialog content", }, global: { - components: { - BccButton, - }, + renderStubDefaultSlot: true, }, + shallow: true, }); - const primaryButton = wrapper.findComponent(BccButton); - expect(primaryButton.props("context")).toBe("danger"); + expect(wrapper.find(".bcc-dialog").classes()).toContain("bcc-dialog-alert"); }); - it("renders primary button with danger variant for alert", () => { + it("renders the correct button context for primary action", () => { const wrapper = mount(BccDialog, { - props: { - open: true, - title: "Alert Title", - variant: "alert", - }, + props: { open: true, destructive: true }, slots: { - primaryAction: "Submit", + primaryAction: "Primary action", }, global: { components: { BccButton, }, + renderStubDefaultSlot: true, }, + shallow: true, }); const primaryButton = wrapper.findComponent(BccButton); + expect(primaryButton.exists()).toBe(true); expect(primaryButton.props("context")).toBe("danger"); }); }); From 32139c5560c2cdb2fce6fd3800ffb54ea2b3f510 Mon Sep 17 00:00:00 2001 From: lukaspoloki Date: Mon, 27 May 2024 09:19:20 +0200 Subject: [PATCH 3/3] remove to match snapshot --- design-library/src/components/BccDialog/BccDialog.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/design-library/src/components/BccDialog/BccDialog.spec.ts b/design-library/src/components/BccDialog/BccDialog.spec.ts index 63f2ce1e..ded08921 100644 --- a/design-library/src/components/BccDialog/BccDialog.spec.ts +++ b/design-library/src/components/BccDialog/BccDialog.spec.ts @@ -27,7 +27,6 @@ describe("BccDialog", () => { expect(wrapper.text()).toContain("Footer slot"); expect(wrapper.text()).toContain("Primary action"); expect(wrapper.text()).toContain("Secondary action"); - expect(wrapper.html()).toMatchSnapshot(); }); it("closes when the close button is clicked", async () => {