From a10b27cdc3f48df3785389ad6ddf1b203af80808 Mon Sep 17 00:00:00 2001 From: Tom Hazledine Date: Thu, 19 Sep 2024 11:35:33 +0100 Subject: [PATCH 01/15] add scroll shadow to OverlayPanelContent --- .../core/src/overlay/OverlayPanelContent.css | 8 +++++- .../core/src/overlay/OverlayPanelContent.tsx | 26 ++++++++++++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/core/src/overlay/OverlayPanelContent.css b/packages/core/src/overlay/OverlayPanelContent.css index c71cdd07be5..298eda88ccd 100644 --- a/packages/core/src/overlay/OverlayPanelContent.css +++ b/packages/core/src/overlay/OverlayPanelContent.css @@ -1,6 +1,12 @@ .saltOverlayPanelContent { animation: var(--salt-animation-fade-in-center); position: relative; - overflow: auto; + overflow-y: auto; padding: var(--saltOverlay-content-padding, var(--salt-spacing-100)); } + +.saltOverlayPanelContent-scroll { + border-bottom: var(--salt-size-border) var(--salt-separable-borderStyle) var(--salt-separable-tertiary-borderColor); + box-shadow: var(--salt-overlayable-shadow-scroll); + margin-bottom: calc(0 - var(--salt-size-border)); +} diff --git a/packages/core/src/overlay/OverlayPanelContent.tsx b/packages/core/src/overlay/OverlayPanelContent.tsx index 971786804c5..bc833f277d7 100644 --- a/packages/core/src/overlay/OverlayPanelContent.tsx +++ b/packages/core/src/overlay/OverlayPanelContent.tsx @@ -4,15 +4,16 @@ import clsx from "clsx"; import { type ComponentPropsWithoutRef, type ReactNode, + type UIEvent, forwardRef, + useState, } from "react"; import { makePrefixer } from "../utils"; import overlayPanelContentCss from "./OverlayPanelContent.css"; const withBaseName = makePrefixer("saltOverlayPanelContent"); -export interface OverlayPanelContentProps - extends ComponentPropsWithoutRef<"div"> { +export interface OverlayHeaderProps extends ComponentPropsWithoutRef<"div"> { /** * The content of Overlay Panel Content */ @@ -21,7 +22,7 @@ export interface OverlayPanelContentProps export const OverlayPanelContent = forwardRef< HTMLDivElement, - OverlayPanelContentProps + OverlayHeaderProps >(function OverlayPanelContent(props, ref) { const targetWindow = useWindow(); useComponentCssInjection({ @@ -31,10 +32,23 @@ export const OverlayPanelContent = forwardRef< }); const { children, className, ...rest } = props; + const [scrollTop, setScrollTop] = useState(0); + + const handleScroll = (event: UIEvent) => { + setScrollTop(event.currentTarget.scrollTop); + }; return ( -
- {children} -
+ <> +
0 })} /> +
+ {children} +
+ ); }); From 170a98ff09c23ad4146bcb7d88b93144fb035692 Mon Sep 17 00:00:00 2001 From: Tom Hazledine Date: Thu, 19 Sep 2024 11:44:44 +0100 Subject: [PATCH 02/15] OverlayHeader component --- .../__tests__/__e2e__/overlay/Overlay.cy.tsx | 8 +- packages/core/src/overlay/OverlayHeader.css | 31 ++++ packages/core/src/overlay/OverlayHeader.tsx | 84 +++++++++ packages/core/src/overlay/index.ts | 1 + .../core/stories/overlay/overlay.stories.tsx | 173 ++++++++++++++---- 5 files changed, 254 insertions(+), 43 deletions(-) create mode 100644 packages/core/src/overlay/OverlayHeader.css create mode 100644 packages/core/src/overlay/OverlayHeader.tsx diff --git a/packages/core/src/__tests__/__e2e__/overlay/Overlay.cy.tsx b/packages/core/src/__tests__/__e2e__/overlay/Overlay.cy.tsx index ff0bf0c6607..52e9935edfd 100644 --- a/packages/core/src/__tests__/__e2e__/overlay/Overlay.cy.tsx +++ b/packages/core/src/__tests__/__e2e__/overlay/Overlay.cy.tsx @@ -3,7 +3,7 @@ import { composeStories } from "@storybook/react"; import { checkAccessibility } from "../../../../../../cypress/tests/checkAccessibility"; const composedStories = composeStories(overlayStories); -const { Default, Right, Bottom, Left, CloseButton } = composedStories; +const { Default, Right, Bottom, Left, HeaderWithCloseButton } = composedStories; describe("GIVEN an Overlay", () => { checkAccessibility(composedStories); @@ -30,7 +30,7 @@ describe("GIVEN an Overlay", () => { }); it("THEN it should focus into the overlay when opened", () => { - cy.mount(); + cy.mount(); cy.realPress("Tab"); cy.realPress("Enter"); @@ -41,7 +41,7 @@ describe("GIVEN an Overlay", () => { }); it("THEN it should trap focus within Overlay once opened", () => { - cy.mount(); + cy.mount(); cy.findByRole("button", { name: /Show Overlay/i }).realClick(); cy.findByRole("dialog").should("be.visible"); @@ -112,7 +112,7 @@ describe("GIVEN an Overlay", () => { describe("WHEN a Close Button is used", () => { it("THEN it should remain open until outside Overlay click or close button click", () => { const onOpenChangeSpy = cy.stub().as("onOpenChangeSpy"); - cy.mount(); + cy.mount(); cy.realPress("Tab"); cy.realPress("Enter"); diff --git a/packages/core/src/overlay/OverlayHeader.css b/packages/core/src/overlay/OverlayHeader.css new file mode 100644 index 00000000000..cfef1741b34 --- /dev/null +++ b/packages/core/src/overlay/OverlayHeader.css @@ -0,0 +1,31 @@ +.saltOverlayHeader { + padding: var(--salt-spacing-100); + width: 100%; + align-items: center; + display: flex; + flex-direction: row; + justify-content: stretch; + gap: var(--salt-spacing-100); + box-sizing: border-box; +} + +.saltOverlayHeader-body { + flex-grow: 1; + margin: 0; + display: flex; + flex-direction: column; + gap: var(--salt-spacing-50); +} + +.saltOverlayHeader-header { + margin: 0; +} + +.saltOverlayHeader-endAdornmentContainer { + align-self: flex-start; +} + +/* Overrides */ +.saltOverlayHeader ~ .saltOverlayPanelContent { + padding-top: 0; +} diff --git a/packages/core/src/overlay/OverlayHeader.tsx b/packages/core/src/overlay/OverlayHeader.tsx new file mode 100644 index 00000000000..8ca2958aaf7 --- /dev/null +++ b/packages/core/src/overlay/OverlayHeader.tsx @@ -0,0 +1,84 @@ +import { useComponentCssInjection } from "@salt-ds/styles"; +import { useWindow } from "@salt-ds/window"; +import clsx from "clsx"; +import { + type ComponentPropsWithoutRef, + type ReactNode, + forwardRef, +} from "react"; +import { H3, Text } from "../text"; +import { makePrefixer } from "../utils"; +import overlayHeaderCss from "./OverlayHeader.css"; + +const withBaseName = makePrefixer("saltOverlayHeader"); + +export interface OverlayPanelContentProps + extends ComponentPropsWithoutRef<"div"> { + /** + * Description text is displayed just below the header + **/ + description?: string; + /** + * End adornment component + */ + endAdornment?: ReactNode; + /** + * Header text + */ + header?: string; + /** + * The id of the Overlay Header, used for aria-labelledby on OverlayPanel + **/ + id?: string; + /** + * Preheader text is displayed just above the header + **/ + preheader?: string; +} + +export const OverlayHeader = forwardRef< + HTMLDivElement, + OverlayPanelContentProps +>(function OverlayPanelContent(props, ref) { + const targetWindow = useWindow(); + useComponentCssInjection({ + testId: "salt-overlay-panel-content", + css: overlayHeaderCss, + window: targetWindow, + }); + + const { + className, + description, + header, + endAdornment, + id, + preheader, + ...rest + } = props; + + return ( +
+
+ {preheader && ( + {preheader} + )} + {header && ( +

+ {header} +

+ )} + {description && ( + + {description} + + )} +
+ {endAdornment && ( +
+ {endAdornment} +
+ )} +
+ ); +}); diff --git a/packages/core/src/overlay/index.ts b/packages/core/src/overlay/index.ts index 78d3e49b9c1..fbfd67b5d82 100644 --- a/packages/core/src/overlay/index.ts +++ b/packages/core/src/overlay/index.ts @@ -1,4 +1,5 @@ export * from "./Overlay"; +export * from "./OverlayHeader"; export * from "./OverlayTrigger"; export * from "./OverlayPanel"; export * from "./OverlayPanelCloseButton"; diff --git a/packages/core/stories/overlay/overlay.stories.tsx b/packages/core/stories/overlay/overlay.stories.tsx index 53fe0939a3f..2b38f3b7d51 100644 --- a/packages/core/stories/overlay/overlay.stories.tsx +++ b/packages/core/stories/overlay/overlay.stories.tsx @@ -4,15 +4,17 @@ import { CheckboxGroup, Divider, Overlay, + OverlayHeader, OverlayPanel, - OverlayPanelCloseButton, OverlayPanelContent, type OverlayProps, OverlayTrigger, StackLayout, + Text, Tooltip, useId, } from "@salt-ds/core"; +import { CloseIcon } from "@salt-ds/icons"; import type { Meta, StoryFn } from "@storybook/react"; import { type ChangeEvent, useState } from "react"; @@ -58,8 +60,8 @@ Right.args = { placement: "right", }; -export const CloseButton = ({ onOpenChange }: OverlayProps) => { - const [open, setOpen] = useState(false); +export const Header = ({ onOpenChange }: OverlayProps) => { + const [open, setOpen] = useState(true); const id = useId(); const onChange = (newOpen: boolean) => { @@ -67,69 +69,87 @@ export const CloseButton = ({ onOpenChange }: OverlayProps) => { onOpenChange?.(newOpen); }; - const handleClose = () => setOpen(false); - return ( - - + + -

- Title -

-
- Content of Overlay -
-
- - - -
+ + + Content of Overlay. Lorem Ipsum is simply dummy text of the + printing and typesetting industry. Lorem Ipsum has been the + industry's standard dummy text ever since the 1500s. When an + unknown printer took a galley of type and scrambled it to make a + type specimen book. + +
+ + + +
+
); }; -export const LongContent = () => { - const [open, setOpen] = useState(false); +export const HeaderWithCloseButton = ({ onOpenChange }: OverlayProps) => { + const [open, setOpen] = useState(true); + const id = useId(); - const onOpenChange = (newOpen: boolean) => { + const onChange = (newOpen: boolean) => { setOpen(newOpen); + onOpenChange?.(newOpen); }; const handleClose = () => setOpen(false); + return ( - + - + + + + } + /> - + + + Content of Overlay. Lorem Ipsum is simply dummy text of the + printing and typesetting industry. Lorem Ipsum has been the + industry's standard dummy text ever since the 1500s. When an + unknown printer took a galley of type and scrambled it to make a + type specimen book. +
- Lorem Ipsum is simply dummy text of the printing and typesetting - industry. Lorem Ipsum has been the industry's standard dummy text - ever since the 1500s, when an unknown printer took a galley of - type and scrambled it to make a type specimen book. -
-
- It has survived not only five centuries, but also the leap into - electronic typesetting, remaining essentially unchanged. It was - popularised in the 1960s with the release of Letraset sheets - containing Lorem Ipsum passages, and more recently with desktop - publishing software like Aldus PageMaker including versions of - Lorem Ipsum. + + +
@@ -138,6 +158,81 @@ export const LongContent = () => { ); }; +export const LongContent = () => { + const [open, setOpen] = useState(true); + const id = useId(); + + const onOpenChange = (newOpen: boolean) => { + setOpen(newOpen); + }; + + const handleClose = () => setOpen(false); + + const CloseButton = () => ( + + ); + + return ( + + + + + + } + /> + + + + A global leader, we deliver strategic advice and solutions, + including capital raising, risk management, and trade finance to + corporations, institutions and governments. A global leader, we + deliver strategic advice and solutions, including capitai raising, + risk management, and trade finance to corporations, institutions + and governments. A global leader, we deliver strategic advice and + solutions, including capital raising, risk management, and trade + finance to corporations, institutions and governments. + + + A global leader, we deliver strategic advice and solutions, + including capital raising, risk management, and trade finance to + corporations, institutions and governments. A global leader, we + deliver strategic advice and solutions, including capital raising, + risk management, and trade finance to corporations, institutions + and governments. A global leader, we deliver strategic advice and + solutions, including capital raising, risk management, and trade + finance to corporations, institutions and governments. A global + leader, we deliver strategic advice and solutions, including + capital raising, risk management, and trade finance to + corporations, institutions and governments. + + Markets + + Serving the world's largest corporate clients and institutional + investors, we support the investment cycle with market-leading + research, analytics and trade execution across multiple asset + classes. + + + + + + ); +}; + const checkboxesData = [ { label: "Overlay", From 779afba1cbfcb05beb7503e3920aba3429be6771 Mon Sep 17 00:00:00 2001 From: Tom Hazledine Date: Thu, 19 Sep 2024 12:24:05 +0100 Subject: [PATCH 03/15] Update DialogHeader to incorporate description and end-adornment --- packages/core/src/dialog/DialogHeader.css | 16 +++++++- packages/core/src/dialog/DialogHeader.tsx | 30 +++++++++++--- .../core/stories/dialog/dialog.stories.tsx | 39 +++++++++++++++---- 3 files changed, 70 insertions(+), 15 deletions(-) diff --git a/packages/core/src/dialog/DialogHeader.css b/packages/core/src/dialog/DialogHeader.css index 0fa72081a0e..cfd2b45a8fb 100644 --- a/packages/core/src/dialog/DialogHeader.css +++ b/packages/core/src/dialog/DialogHeader.css @@ -1,6 +1,6 @@ /* Styles applied to the root element */ .saltDialogHeader { - padding-bottom: var(--salt-spacing-100); + padding-bottom: var(--salt-spacing-300); padding-left: var(--salt-spacing-300); padding-right: var(--salt-spacing-300); align-items: center; @@ -10,10 +10,22 @@ box-sizing: border-box; } +.saltDialogHeader-body { + flex-grow: 1; + margin: 0; + display: flex; + flex-direction: column; + gap: var(--salt-spacing-50); +} + .saltDialogHeader-header { margin: 0; } +.saltDialogHeader-endAdornmentContainer { + align-self: flex-start; +} + /* Styles applied to the status indicator icon overriding its default size */ .saltDialogHeader .saltStatusIndicator.saltIcon { --icon-size: var(--salt-text-h2-lineHeight); @@ -29,7 +41,7 @@ position: absolute; top: 0; left: 0; - bottom: var(--salt-spacing-100); + bottom: var(--salt-spacing-300); width: var(--salt-size-bar); background: var(--salt-accent-background); } diff --git a/packages/core/src/dialog/DialogHeader.tsx b/packages/core/src/dialog/DialogHeader.tsx index 7691c511e65..2a6871088ce 100644 --- a/packages/core/src/dialog/DialogHeader.tsx +++ b/packages/core/src/dialog/DialogHeader.tsx @@ -30,15 +30,25 @@ export interface DialogHeaderProps extends ComponentPropsWithoutRef<"div"> { * Displays the preheader just above the header **/ preheader?: ReactNode; + /** + * Description text is displayed just below the header + **/ + description?: string; + /** + * End adornment component + */ + endAdornment?: ReactNode; } export const DialogHeader = forwardRef( function DialogHeader(props, ref) { const { className, + description, + disableAccent, + endAdornment, header, preheader, - disableAccent, status: statusProp, ...rest } = props; @@ -68,14 +78,22 @@ export const DialogHeader = forwardRef( {...rest} > {status && } -

+
{preheader && ( - - {preheader} + {preheader} + )} +

{header}

+ {description && ( + + {description} )} -
{header}
-

+
+ {endAdornment && ( +
+ {endAdornment} +
+ )} ); }, diff --git a/packages/core/stories/dialog/dialog.stories.tsx b/packages/core/stories/dialog/dialog.stories.tsx index da387feddcf..3ef8686aa36 100644 --- a/packages/core/stories/dialog/dialog.stories.tsx +++ b/packages/core/stories/dialog/dialog.stories.tsx @@ -2,13 +2,13 @@ import { Button, Dialog, DialogActions, - DialogCloseButton, DialogContent, type DialogContentProps, DialogHeader, type DialogProps, StackLayout, } from "@salt-ds/core"; +import { CloseIcon } from "@salt-ds/icons"; import type { Meta, StoryFn } from "@storybook/react"; import { type ComponentProps, @@ -23,7 +23,10 @@ export default { title: "Core/Dialog", component: Dialog, args: { - header: "Congratulations! You have created a Dialog.", + open: true, + preheader: "Settlements", + header: "Terms and conditions", + description: "Effective date: August 29, 2024", content: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.", }, @@ -40,12 +43,16 @@ const UnmountLogger = () => { const DialogTemplate: StoryFn< Omit & - Pick, "header" | "preheader"> & { + Pick< + ComponentProps, + "header" | "preheader" | "description" + > & { content: DialogContentProps["children"]; } > = ({ header, preheader, + description, content, id, size, @@ -66,6 +73,12 @@ const DialogTemplate: StoryFn< setOpen(false); }; + const CloseButton = () => ( + + ); + return ( <> - ); @@ -349,13 +366,22 @@ export const StickyFooter: StoryFn = ({ setOpen(false); }; + const CloseButton = () => ( + + ); + return ( <> - + } + /> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever @@ -371,7 +397,6 @@ export const StickyFooter: StoryFn = ({ Next - ); From 1e5ac51aa53ab40f782c01e789db2b068cba69c0 Mon Sep 17 00:00:00 2001 From: Tom Hazledine Date: Thu, 19 Sep 2024 12:44:13 +0100 Subject: [PATCH 04/15] updating Dialog tests to account for header changes --- packages/core/src/__tests__/__e2e__/dialog/Dialog.cy.tsx | 8 ++++---- packages/core/stories/dialog/dialog.stories.tsx | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/core/src/__tests__/__e2e__/dialog/Dialog.cy.tsx b/packages/core/src/__tests__/__e2e__/dialog/Dialog.cy.tsx index fb5c71d98fb..a2c77ccff53 100644 --- a/packages/core/src/__tests__/__e2e__/dialog/Dialog.cy.tsx +++ b/packages/core/src/__tests__/__e2e__/dialog/Dialog.cy.tsx @@ -165,23 +165,23 @@ describe("GIVEN a Dialog", () => { cy.mount(); cy.findByRole("button", { name: "Open dialog" }).realClick(); cy.findByRole("dialog").should("be.visible"); + cy.findAllByRole("button", { name: "Close dialog" }).should("be.focused"); + cy.realPress("Tab"); cy.findAllByRole("button", { name: "Cancel" }).should("be.focused"); cy.realPress("Tab"); cy.findAllByRole("button", { name: "Previous" }).should("be.focused"); cy.realPress("Tab"); cy.findAllByRole("button", { name: "Next" }).should("be.focused"); cy.realPress("Tab"); - cy.findAllByRole("button", { name: "Close dialog" }).should("be.focused"); - cy.realPress("Tab"); //back to the first button - cy.findAllByRole("button", { name: "Cancel" }).should("be.focused"); + cy.findAllByRole("button", { name: "Close dialog" }).should("be.focused"); }); it("THEN should support initialFocus being set", () => { cy.mount(); cy.findByRole("button", { name: "Open dialog" }).realClick(); cy.findByRole("dialog").should("be.visible"); - cy.findByRole("button", { name: "Next" }).should("be.focused"); + cy.findByRole("button", { name: "Previous" }).should("be.focused"); }); }); }); diff --git a/packages/core/stories/dialog/dialog.stories.tsx b/packages/core/stories/dialog/dialog.stories.tsx index 3ef8686aa36..873cb8013f7 100644 --- a/packages/core/stories/dialog/dialog.stories.tsx +++ b/packages/core/stories/dialog/dialog.stories.tsx @@ -23,7 +23,6 @@ export default { title: "Core/Dialog", component: Dialog, args: { - open: true, preheader: "Settlements", header: "Terms and conditions", description: "Effective date: August 29, 2024", From f434a4ad390022f0f3028be3ce56dd66b29c2ea3 Mon Sep 17 00:00:00 2001 From: Tom Hazledine Date: Thu, 19 Sep 2024 12:55:07 +0100 Subject: [PATCH 05/15] updating Overlay tests to account for header changes --- .../__tests__/__e2e__/overlay/Overlay.cy.tsx | 11 +++++---- .../core/stories/overlay/overlay.stories.tsx | 24 ++++++++++--------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/packages/core/src/__tests__/__e2e__/overlay/Overlay.cy.tsx b/packages/core/src/__tests__/__e2e__/overlay/Overlay.cy.tsx index 52e9935edfd..74e22ad2bca 100644 --- a/packages/core/src/__tests__/__e2e__/overlay/Overlay.cy.tsx +++ b/packages/core/src/__tests__/__e2e__/overlay/Overlay.cy.tsx @@ -36,7 +36,9 @@ describe("GIVEN an Overlay", () => { cy.realPress("Enter"); cy.findByRole("dialog").should("be.visible"); //focus into overlay - cy.findByRole("button", { name: /Close Overlay/i }).should("be.focused"); + cy.findAllByRole("button", { name: "Close overlay" }).should( + "be.focused", + ); cy.realPress("Tab"); }); @@ -45,11 +47,11 @@ describe("GIVEN an Overlay", () => { cy.findByRole("button", { name: /Show Overlay/i }).realClick(); cy.findByRole("dialog").should("be.visible"); - cy.findByRole("button", { name: /Close Overlay/i }).should("be.focused"); + cy.findByRole("button", { name: /Close overlay/i }).should("be.focused"); cy.realPress("Tab"); cy.findByRole("button", { name: /Hover me/i }).should("be.focused"); cy.realPress("Tab"); - cy.findByRole("button", { name: /Close Overlay/i }).should("be.focused"); + cy.findByRole("button", { name: /Close overlay/i }).should("be.focused"); }); }); @@ -119,7 +121,8 @@ describe("GIVEN an Overlay", () => { cy.findByRole("dialog").should("be.visible"); cy.get("@onOpenChangeSpy").should("have.callCount", 1); - cy.findByRole("button", { name: /Close Overlay/i }).realClick(); + cy.findByRole("button", { name: /Close overlay/i }).realClick(); + cy.findByRole("dialog").should("not.exist"); cy.findByRole("button", { name: /Show Overlay/i }).realClick(); diff --git a/packages/core/stories/overlay/overlay.stories.tsx b/packages/core/stories/overlay/overlay.stories.tsx index 2b38f3b7d51..ecb7e25b7c2 100644 --- a/packages/core/stories/overlay/overlay.stories.tsx +++ b/packages/core/stories/overlay/overlay.stories.tsx @@ -61,7 +61,7 @@ Right.args = { }; export const Header = ({ onOpenChange }: OverlayProps) => { - const [open, setOpen] = useState(true); + const [open, setOpen] = useState(false); const id = useId(); const onChange = (newOpen: boolean) => { @@ -103,7 +103,7 @@ export const Header = ({ onOpenChange }: OverlayProps) => { }; export const HeaderWithCloseButton = ({ onOpenChange }: OverlayProps) => { - const [open, setOpen] = useState(true); + const [open, setOpen] = useState(false); const id = useId(); const onChange = (newOpen: boolean) => { @@ -113,6 +113,16 @@ export const HeaderWithCloseButton = ({ onOpenChange }: OverlayProps) => { const handleClose = () => setOpen(false); + const CloseButton = () => ( + + ); + return ( @@ -127,15 +137,7 @@ export const HeaderWithCloseButton = ({ onOpenChange }: OverlayProps) => { - - - } + endAdornment={} /> From 88f81a330147fb8901dcb78fd280f34e85cd7b7a Mon Sep 17 00:00:00 2001 From: Fernanda Castillo Date: Thu, 10 Oct 2024 14:31:01 +0100 Subject: [PATCH 06/15] add overlay header to lab --- .changeset/quiet-rice-prove.md | 17 + .../__tests__/__e2e__/dialog/Dialog.cy.tsx | 8 +- .../__tests__/__e2e__/overlay/Overlay.cy.tsx | 19 +- packages/core/src/dialog/DialogHeader.css | 16 +- packages/core/src/dialog/DialogHeader.tsx | 30 +- .../core/src/overlay/OverlayPanelContent.css | 8 +- .../core/src/overlay/OverlayPanelContent.tsx | 26 +- packages/core/src/overlay/index.ts | 1 - .../core/stories/dialog/dialog.stories.tsx | 38 +- .../core/stories/overlay/overlay.stories.tsx | 171 ++------- packages/lab/src/index.ts | 1 + .../src/overlay/OverlayHeader.css | 4 +- .../src/overlay/OverlayHeader.tsx | 37 +- packages/lab/src/overlay/index.ts | 1 + .../stories/overlay/overlay.qa.stories.tsx | 83 +++++ .../lab/stories/overlay/overlay.stories.tsx | 334 ++++++++++++++++++ 16 files changed, 523 insertions(+), 271 deletions(-) create mode 100644 .changeset/quiet-rice-prove.md rename packages/{core => lab}/src/overlay/OverlayHeader.css (87%) rename packages/{core => lab}/src/overlay/OverlayHeader.tsx (71%) create mode 100644 packages/lab/src/overlay/index.ts create mode 100644 packages/lab/stories/overlay/overlay.qa.stories.tsx create mode 100644 packages/lab/stories/overlay/overlay.stories.tsx diff --git a/.changeset/quiet-rice-prove.md b/.changeset/quiet-rice-prove.md new file mode 100644 index 00000000000..9b4cd234240 --- /dev/null +++ b/.changeset/quiet-rice-prove.md @@ -0,0 +1,17 @@ +--- +"@salt-ds/lab": minor +--- + +Added `OverlayHeader` component to lab. + +```typescript + + + + + + }/> + Content of Overlay + + +``` diff --git a/packages/core/src/__tests__/__e2e__/dialog/Dialog.cy.tsx b/packages/core/src/__tests__/__e2e__/dialog/Dialog.cy.tsx index a2c77ccff53..fb5c71d98fb 100644 --- a/packages/core/src/__tests__/__e2e__/dialog/Dialog.cy.tsx +++ b/packages/core/src/__tests__/__e2e__/dialog/Dialog.cy.tsx @@ -165,23 +165,23 @@ describe("GIVEN a Dialog", () => { cy.mount(); cy.findByRole("button", { name: "Open dialog" }).realClick(); cy.findByRole("dialog").should("be.visible"); - cy.findAllByRole("button", { name: "Close dialog" }).should("be.focused"); - cy.realPress("Tab"); cy.findAllByRole("button", { name: "Cancel" }).should("be.focused"); cy.realPress("Tab"); cy.findAllByRole("button", { name: "Previous" }).should("be.focused"); cy.realPress("Tab"); cy.findAllByRole("button", { name: "Next" }).should("be.focused"); cy.realPress("Tab"); - //back to the first button cy.findAllByRole("button", { name: "Close dialog" }).should("be.focused"); + cy.realPress("Tab"); + //back to the first button + cy.findAllByRole("button", { name: "Cancel" }).should("be.focused"); }); it("THEN should support initialFocus being set", () => { cy.mount(); cy.findByRole("button", { name: "Open dialog" }).realClick(); cy.findByRole("dialog").should("be.visible"); - cy.findByRole("button", { name: "Previous" }).should("be.focused"); + cy.findByRole("button", { name: "Next" }).should("be.focused"); }); }); }); diff --git a/packages/core/src/__tests__/__e2e__/overlay/Overlay.cy.tsx b/packages/core/src/__tests__/__e2e__/overlay/Overlay.cy.tsx index 74e22ad2bca..ff0bf0c6607 100644 --- a/packages/core/src/__tests__/__e2e__/overlay/Overlay.cy.tsx +++ b/packages/core/src/__tests__/__e2e__/overlay/Overlay.cy.tsx @@ -3,7 +3,7 @@ import { composeStories } from "@storybook/react"; import { checkAccessibility } from "../../../../../../cypress/tests/checkAccessibility"; const composedStories = composeStories(overlayStories); -const { Default, Right, Bottom, Left, HeaderWithCloseButton } = composedStories; +const { Default, Right, Bottom, Left, CloseButton } = composedStories; describe("GIVEN an Overlay", () => { checkAccessibility(composedStories); @@ -30,28 +30,26 @@ describe("GIVEN an Overlay", () => { }); it("THEN it should focus into the overlay when opened", () => { - cy.mount(); + cy.mount(); cy.realPress("Tab"); cy.realPress("Enter"); cy.findByRole("dialog").should("be.visible"); //focus into overlay - cy.findAllByRole("button", { name: "Close overlay" }).should( - "be.focused", - ); + cy.findByRole("button", { name: /Close Overlay/i }).should("be.focused"); cy.realPress("Tab"); }); it("THEN it should trap focus within Overlay once opened", () => { - cy.mount(); + cy.mount(); cy.findByRole("button", { name: /Show Overlay/i }).realClick(); cy.findByRole("dialog").should("be.visible"); - cy.findByRole("button", { name: /Close overlay/i }).should("be.focused"); + cy.findByRole("button", { name: /Close Overlay/i }).should("be.focused"); cy.realPress("Tab"); cy.findByRole("button", { name: /Hover me/i }).should("be.focused"); cy.realPress("Tab"); - cy.findByRole("button", { name: /Close overlay/i }).should("be.focused"); + cy.findByRole("button", { name: /Close Overlay/i }).should("be.focused"); }); }); @@ -114,15 +112,14 @@ describe("GIVEN an Overlay", () => { describe("WHEN a Close Button is used", () => { it("THEN it should remain open until outside Overlay click or close button click", () => { const onOpenChangeSpy = cy.stub().as("onOpenChangeSpy"); - cy.mount(); + cy.mount(); cy.realPress("Tab"); cy.realPress("Enter"); cy.findByRole("dialog").should("be.visible"); cy.get("@onOpenChangeSpy").should("have.callCount", 1); - cy.findByRole("button", { name: /Close overlay/i }).realClick(); - + cy.findByRole("button", { name: /Close Overlay/i }).realClick(); cy.findByRole("dialog").should("not.exist"); cy.findByRole("button", { name: /Show Overlay/i }).realClick(); diff --git a/packages/core/src/dialog/DialogHeader.css b/packages/core/src/dialog/DialogHeader.css index cfd2b45a8fb..0fa72081a0e 100644 --- a/packages/core/src/dialog/DialogHeader.css +++ b/packages/core/src/dialog/DialogHeader.css @@ -1,6 +1,6 @@ /* Styles applied to the root element */ .saltDialogHeader { - padding-bottom: var(--salt-spacing-300); + padding-bottom: var(--salt-spacing-100); padding-left: var(--salt-spacing-300); padding-right: var(--salt-spacing-300); align-items: center; @@ -10,22 +10,10 @@ box-sizing: border-box; } -.saltDialogHeader-body { - flex-grow: 1; - margin: 0; - display: flex; - flex-direction: column; - gap: var(--salt-spacing-50); -} - .saltDialogHeader-header { margin: 0; } -.saltDialogHeader-endAdornmentContainer { - align-self: flex-start; -} - /* Styles applied to the status indicator icon overriding its default size */ .saltDialogHeader .saltStatusIndicator.saltIcon { --icon-size: var(--salt-text-h2-lineHeight); @@ -41,7 +29,7 @@ position: absolute; top: 0; left: 0; - bottom: var(--salt-spacing-300); + bottom: var(--salt-spacing-100); width: var(--salt-size-bar); background: var(--salt-accent-background); } diff --git a/packages/core/src/dialog/DialogHeader.tsx b/packages/core/src/dialog/DialogHeader.tsx index 2a6871088ce..7691c511e65 100644 --- a/packages/core/src/dialog/DialogHeader.tsx +++ b/packages/core/src/dialog/DialogHeader.tsx @@ -30,25 +30,15 @@ export interface DialogHeaderProps extends ComponentPropsWithoutRef<"div"> { * Displays the preheader just above the header **/ preheader?: ReactNode; - /** - * Description text is displayed just below the header - **/ - description?: string; - /** - * End adornment component - */ - endAdornment?: ReactNode; } export const DialogHeader = forwardRef( function DialogHeader(props, ref) { const { className, - description, - disableAccent, - endAdornment, header, preheader, + disableAccent, status: statusProp, ...rest } = props; @@ -78,22 +68,14 @@ export const DialogHeader = forwardRef( {...rest} > {status && } -
+

{preheader && ( - {preheader} - )} -

{header}

- {description && ( - - {description} + + {preheader} )} -
- {endAdornment && ( -
- {endAdornment} -
- )} +
{header}
+ ); }, diff --git a/packages/core/src/overlay/OverlayPanelContent.css b/packages/core/src/overlay/OverlayPanelContent.css index 298eda88ccd..c71cdd07be5 100644 --- a/packages/core/src/overlay/OverlayPanelContent.css +++ b/packages/core/src/overlay/OverlayPanelContent.css @@ -1,12 +1,6 @@ .saltOverlayPanelContent { animation: var(--salt-animation-fade-in-center); position: relative; - overflow-y: auto; + overflow: auto; padding: var(--saltOverlay-content-padding, var(--salt-spacing-100)); } - -.saltOverlayPanelContent-scroll { - border-bottom: var(--salt-size-border) var(--salt-separable-borderStyle) var(--salt-separable-tertiary-borderColor); - box-shadow: var(--salt-overlayable-shadow-scroll); - margin-bottom: calc(0 - var(--salt-size-border)); -} diff --git a/packages/core/src/overlay/OverlayPanelContent.tsx b/packages/core/src/overlay/OverlayPanelContent.tsx index bc833f277d7..971786804c5 100644 --- a/packages/core/src/overlay/OverlayPanelContent.tsx +++ b/packages/core/src/overlay/OverlayPanelContent.tsx @@ -4,16 +4,15 @@ import clsx from "clsx"; import { type ComponentPropsWithoutRef, type ReactNode, - type UIEvent, forwardRef, - useState, } from "react"; import { makePrefixer } from "../utils"; import overlayPanelContentCss from "./OverlayPanelContent.css"; const withBaseName = makePrefixer("saltOverlayPanelContent"); -export interface OverlayHeaderProps extends ComponentPropsWithoutRef<"div"> { +export interface OverlayPanelContentProps + extends ComponentPropsWithoutRef<"div"> { /** * The content of Overlay Panel Content */ @@ -22,7 +21,7 @@ export interface OverlayHeaderProps extends ComponentPropsWithoutRef<"div"> { export const OverlayPanelContent = forwardRef< HTMLDivElement, - OverlayHeaderProps + OverlayPanelContentProps >(function OverlayPanelContent(props, ref) { const targetWindow = useWindow(); useComponentCssInjection({ @@ -32,23 +31,10 @@ export const OverlayPanelContent = forwardRef< }); const { children, className, ...rest } = props; - const [scrollTop, setScrollTop] = useState(0); - - const handleScroll = (event: UIEvent) => { - setScrollTop(event.currentTarget.scrollTop); - }; return ( - <> -
0 })} /> -
- {children} -
- +
+ {children} +
); }); diff --git a/packages/core/src/overlay/index.ts b/packages/core/src/overlay/index.ts index fbfd67b5d82..78d3e49b9c1 100644 --- a/packages/core/src/overlay/index.ts +++ b/packages/core/src/overlay/index.ts @@ -1,5 +1,4 @@ export * from "./Overlay"; -export * from "./OverlayHeader"; export * from "./OverlayTrigger"; export * from "./OverlayPanel"; export * from "./OverlayPanelCloseButton"; diff --git a/packages/core/stories/dialog/dialog.stories.tsx b/packages/core/stories/dialog/dialog.stories.tsx index 873cb8013f7..da387feddcf 100644 --- a/packages/core/stories/dialog/dialog.stories.tsx +++ b/packages/core/stories/dialog/dialog.stories.tsx @@ -2,13 +2,13 @@ import { Button, Dialog, DialogActions, + DialogCloseButton, DialogContent, type DialogContentProps, DialogHeader, type DialogProps, StackLayout, } from "@salt-ds/core"; -import { CloseIcon } from "@salt-ds/icons"; import type { Meta, StoryFn } from "@storybook/react"; import { type ComponentProps, @@ -23,9 +23,7 @@ export default { title: "Core/Dialog", component: Dialog, args: { - preheader: "Settlements", - header: "Terms and conditions", - description: "Effective date: August 29, 2024", + header: "Congratulations! You have created a Dialog.", content: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.", }, @@ -42,16 +40,12 @@ const UnmountLogger = () => { const DialogTemplate: StoryFn< Omit & - Pick< - ComponentProps, - "header" | "preheader" | "description" - > & { + Pick, "header" | "preheader"> & { content: DialogContentProps["children"]; } > = ({ header, preheader, - description, content, id, size, @@ -72,12 +66,6 @@ const DialogTemplate: StoryFn< setOpen(false); }; - const CloseButton = () => ( - - ); - return ( <> + ); @@ -365,22 +349,13 @@ export const StickyFooter: StoryFn = ({ setOpen(false); }; - const CloseButton = () => ( - - ); - return ( <> - } - /> + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever @@ -396,6 +371,7 @@ export const StickyFooter: StoryFn = ({ Next + ); diff --git a/packages/core/stories/overlay/overlay.stories.tsx b/packages/core/stories/overlay/overlay.stories.tsx index ecb7e25b7c2..53fe0939a3f 100644 --- a/packages/core/stories/overlay/overlay.stories.tsx +++ b/packages/core/stories/overlay/overlay.stories.tsx @@ -4,17 +4,15 @@ import { CheckboxGroup, Divider, Overlay, - OverlayHeader, OverlayPanel, + OverlayPanelCloseButton, OverlayPanelContent, type OverlayProps, OverlayTrigger, StackLayout, - Text, Tooltip, useId, } from "@salt-ds/core"; -import { CloseIcon } from "@salt-ds/icons"; import type { Meta, StoryFn } from "@storybook/react"; import { type ChangeEvent, useState } from "react"; @@ -60,7 +58,7 @@ Right.args = { placement: "right", }; -export const Header = ({ onOpenChange }: OverlayProps) => { +export const CloseButton = ({ onOpenChange }: OverlayProps) => { const [open, setOpen] = useState(false); const id = useId(); @@ -69,165 +67,70 @@ export const Header = ({ onOpenChange }: OverlayProps) => { onOpenChange?.(newOpen); }; + const handleClose = () => setOpen(false); + return ( - - + + - - - Content of Overlay. Lorem Ipsum is simply dummy text of the - printing and typesetting industry. Lorem Ipsum has been the - industry's standard dummy text ever since the 1500s. When an - unknown printer took a galley of type and scrambled it to make a - type specimen book. - -
- - - -
-
+

+ Title +

+
+ Content of Overlay +
+
+ + + +
); }; -export const HeaderWithCloseButton = ({ onOpenChange }: OverlayProps) => { +export const LongContent = () => { const [open, setOpen] = useState(false); - const id = useId(); - const onChange = (newOpen: boolean) => { + const onOpenChange = (newOpen: boolean) => { setOpen(newOpen); - onOpenChange?.(newOpen); }; const handleClose = () => setOpen(false); - - const CloseButton = () => ( - - ); - return ( - + - } - /> + - - - Content of Overlay. Lorem Ipsum is simply dummy text of the - printing and typesetting industry. Lorem Ipsum has been the - industry's standard dummy text ever since the 1500s. When an - unknown printer took a galley of type and scrambled it to make a - type specimen book. - +
- - - + Lorem Ipsum is simply dummy text of the printing and typesetting + industry. Lorem Ipsum has been the industry's standard dummy text + ever since the 1500s, when an unknown printer took a galley of + type and scrambled it to make a type specimen book. +
+
+ It has survived not only five centuries, but also the leap into + electronic typesetting, remaining essentially unchanged. It was + popularised in the 1960s with the release of Letraset sheets + containing Lorem Ipsum passages, and more recently with desktop + publishing software like Aldus PageMaker including versions of + Lorem Ipsum.
-
-
-
-
- ); -}; - -export const LongContent = () => { - const [open, setOpen] = useState(true); - const id = useId(); - - const onOpenChange = (newOpen: boolean) => { - setOpen(newOpen); - }; - - const handleClose = () => setOpen(false); - - const CloseButton = () => ( - - ); - - return ( - - - - - - } - /> - - - - A global leader, we deliver strategic advice and solutions, - including capital raising, risk management, and trade finance to - corporations, institutions and governments. A global leader, we - deliver strategic advice and solutions, including capitai raising, - risk management, and trade finance to corporations, institutions - and governments. A global leader, we deliver strategic advice and - solutions, including capital raising, risk management, and trade - finance to corporations, institutions and governments. - - - A global leader, we deliver strategic advice and solutions, - including capital raising, risk management, and trade finance to - corporations, institutions and governments. A global leader, we - deliver strategic advice and solutions, including capital raising, - risk management, and trade finance to corporations, institutions - and governments. A global leader, we deliver strategic advice and - solutions, including capital raising, risk management, and trade - finance to corporations, institutions and governments. A global - leader, we deliver strategic advice and solutions, including - capital raising, risk management, and trade finance to - corporations, institutions and governments. - - Markets - - Serving the world's largest corporate clients and institutional - investors, we support the investment cycle with market-leading - research, analytics and trade execution across multiple asset - classes. - diff --git a/packages/lab/src/index.ts b/packages/lab/src/index.ts index 0b511f8a3c1..655da87abe9 100644 --- a/packages/lab/src/index.ts +++ b/packages/lab/src/index.ts @@ -50,6 +50,7 @@ export * from "./list-next"; export * from "./logo"; export * from "./menu-button"; export * from "./metric"; +export * from "./overlay"; export * from "./portal"; export * from "./query-input"; export * from "./responsive"; diff --git a/packages/core/src/overlay/OverlayHeader.css b/packages/lab/src/overlay/OverlayHeader.css similarity index 87% rename from packages/core/src/overlay/OverlayHeader.css rename to packages/lab/src/overlay/OverlayHeader.css index cfef1741b34..e28a367f2a5 100644 --- a/packages/core/src/overlay/OverlayHeader.css +++ b/packages/lab/src/overlay/OverlayHeader.css @@ -9,7 +9,7 @@ box-sizing: border-box; } -.saltOverlayHeader-body { +.saltOverlayHeader-container { flex-grow: 1; margin: 0; display: flex; @@ -21,7 +21,7 @@ margin: 0; } -.saltOverlayHeader-endAdornmentContainer { +.saltOverlayHeader-actionsContainer { align-self: flex-start; } diff --git a/packages/core/src/overlay/OverlayHeader.tsx b/packages/lab/src/overlay/OverlayHeader.tsx similarity index 71% rename from packages/core/src/overlay/OverlayHeader.tsx rename to packages/lab/src/overlay/OverlayHeader.tsx index 8ca2958aaf7..69a8263fd80 100644 --- a/packages/core/src/overlay/OverlayHeader.tsx +++ b/packages/lab/src/overlay/OverlayHeader.tsx @@ -1,3 +1,5 @@ +import { H4, Text } from "@salt-ds/core/src/text"; +import { makePrefixer } from "@salt-ds/core/src/utils"; import { useComponentCssInjection } from "@salt-ds/styles"; import { useWindow } from "@salt-ds/window"; import clsx from "clsx"; @@ -6,8 +8,6 @@ import { type ReactNode, forwardRef, } from "react"; -import { H3, Text } from "../text"; -import { makePrefixer } from "../utils"; import overlayHeaderCss from "./OverlayHeader.css"; const withBaseName = makePrefixer("saltOverlayHeader"); @@ -17,15 +17,15 @@ export interface OverlayPanelContentProps /** * Description text is displayed just below the header **/ - description?: string; + description?: ReactNode; /** - * End adornment component + * Actions to be displayed in header */ - endAdornment?: ReactNode; + actions?: ReactNode; /** * Header text */ - header?: string; + header?: ReactNode; /** * The id of the Overlay Header, used for aria-labelledby on OverlayPanel **/ @@ -33,7 +33,7 @@ export interface OverlayPanelContentProps /** * Preheader text is displayed just above the header **/ - preheader?: string; + preheader?: ReactNode; } export const OverlayHeader = forwardRef< @@ -47,26 +47,19 @@ export const OverlayHeader = forwardRef< window: targetWindow, }); - const { - className, - description, - header, - endAdornment, - id, - preheader, - ...rest - } = props; + const { className, description, header, actions, id, preheader, ...rest } = + props; return (
-
+
{preheader && ( {preheader} )} {header && ( -

+

{header} -

+ )} {description && ( @@ -74,10 +67,8 @@ export const OverlayHeader = forwardRef< )}
- {endAdornment && ( -
- {endAdornment} -
+ {actions && ( +
{actions}
)}
); diff --git a/packages/lab/src/overlay/index.ts b/packages/lab/src/overlay/index.ts new file mode 100644 index 00000000000..f9c8c290a19 --- /dev/null +++ b/packages/lab/src/overlay/index.ts @@ -0,0 +1 @@ +export * from "./OverlayHeader"; diff --git a/packages/lab/stories/overlay/overlay.qa.stories.tsx b/packages/lab/stories/overlay/overlay.qa.stories.tsx new file mode 100644 index 00000000000..3dc37ca302a --- /dev/null +++ b/packages/lab/stories/overlay/overlay.qa.stories.tsx @@ -0,0 +1,83 @@ +import { + Button, + Overlay, + OverlayPanel, + OverlayPanelContent, + OverlayTrigger, +} from "@salt-ds/core"; +import { CloseIcon } from "@salt-ds/icons"; +import { OverlayHeader } from "@salt-ds/lab"; +import type { Meta, StoryFn } from "@storybook/react"; +import { QAContainer, type QAContainerProps } from "docs/components"; + +export default { + title: "Lab/Overlay Header/Overlay Header QA", + component: Overlay, +} as Meta; + +export const Default: StoryFn = (props) => { + return ( + + + + + + + + +
Content of Overlay
+
+
+
+
+ ); +}; + +Default.parameters = { + chromatic: { disableSnapshot: false }, +}; + +export const CloseButton: StoryFn = (props) => { + const CloseButton = () => ( + + ); + return ( + + + + + + + } /> + +
Content of Overlay
+
+
+
+
+ ); +}; + +CloseButton.parameters = { + chromatic: { disableSnapshot: false }, +}; diff --git a/packages/lab/stories/overlay/overlay.stories.tsx b/packages/lab/stories/overlay/overlay.stories.tsx new file mode 100644 index 00000000000..6b55c8040df --- /dev/null +++ b/packages/lab/stories/overlay/overlay.stories.tsx @@ -0,0 +1,334 @@ +import { + Button, + Checkbox, + CheckboxGroup, + Divider, + Overlay, + OverlayPanel, + OverlayPanelContent, + type OverlayProps, + OverlayTrigger, + StackLayout, + Text, + Tooltip, + useId, +} from "@salt-ds/core"; +import { CloseIcon } from "@salt-ds/icons"; +import { OverlayHeader } from "@salt-ds/lab"; +import type { Meta, StoryFn } from "@storybook/react"; +import { type ChangeEvent, useState } from "react"; + +export default { + title: "Lab/Overlay Header", +} as Meta; + +export const Default: StoryFn = ({ ...args }) => { + const id = useId(); + + return ( + + + + + + + + Content of Overlay + + + ); +}; + +export const Bottom = Default.bind({}); +Bottom.args = { + placement: "bottom", +}; + +export const Left = Default.bind({}); +Left.args = { + placement: "left", +}; + +export const Right = Default.bind({}); +Right.args = { + placement: "right", +}; + +export const Header = ({ onOpenChange }: OverlayProps) => { + const [open, setOpen] = useState(false); + const id = useId(); + + const onChange = (newOpen: boolean) => { + setOpen(newOpen); + onOpenChange?.(newOpen); + }; + + return ( + + + + + + + + + + Content of Overlay. Lorem Ipsum is simply dummy text of the + printing and typesetting industry. Lorem Ipsum has been the + industry's standard dummy text ever since the 1500s. When an + unknown printer took a galley of type and scrambled it to make a + type specimen book. + +
+ + + +
+
+
+
+
+ ); +}; + +export const HeaderWithCloseButton = ({ onOpenChange }: OverlayProps) => { + const [open, setOpen] = useState(false); + const id = useId(); + + const onChange = (newOpen: boolean) => { + setOpen(newOpen); + onOpenChange?.(newOpen); + }; + + const handleClose = () => setOpen(false); + + const CloseButton = () => ( + + ); + + return ( + + + + + + } + /> + + + + Content of Overlay. Lorem Ipsum is simply dummy text of the + printing and typesetting industry. Lorem Ipsum has been the + industry's standard dummy text ever since the 1500s. When an + unknown printer took a galley of type and scrambled it to make a + type specimen book. + +
+ + + +
+
+
+
+
+ ); +}; + +export const LongContent = () => { + const [open, setOpen] = useState(true); + const id = useId(); + + const onOpenChange = (newOpen: boolean) => { + setOpen(newOpen); + }; + + const handleClose = () => setOpen(false); + + const CloseButton = () => ( + + ); + + return ( + + + + + + } /> + + + + A global leader, we deliver strategic advice and solutions, + including capital raising, risk management, and trade finance to + corporations, institutions and governments. A global leader, we + deliver strategic advice and solutions, including capital raising, + risk management, and trade finance to corporations, institutions + and governments. A global leader, we deliver strategic advice and + solutions, including capital raising, risk management, and trade + finance to corporations, institutions and governments. + + + A global leader, we deliver strategic advice and solutions, + including capital raising, risk management, and trade finance to + corporations, institutions and governments. A global leader, we + deliver strategic advice and solutions, including capital raising, + risk management, and trade finance to corporations, institutions + and governments. A global leader, we deliver strategic advice and + solutions, including capital raising, risk management, and trade + finance to corporations, institutions and governments. A global + leader, we deliver strategic advice and solutions, including + capital raising, risk management, and trade finance to + corporations, institutions and governments. + + Markets + + Serving the world's largest corporate clients and institutional + investors, we support the investment cycle with market-leading + research, analytics and trade execution across multiple asset + classes. + + + + + + ); +}; + +const checkboxesData = [ + { + label: "Overlay", + value: "overlay", + }, + { + label: "Row", + value: "row", + }, +]; + +const WithActionsContent = ({ onClose }: { onClose: () => void }) => { + const [controlledValues, setControlledValues] = useState([ + checkboxesData[0].value, + ]); + + const [checkboxState, setCheckboxState] = useState({ + checked: false, + indeterminate: true, + }); + + const handleChange = (event: ChangeEvent) => { + const updatedChecked = event.target.checked; + setCheckboxState({ + indeterminate: !updatedChecked && checkboxState.checked, + checked: + checkboxState.indeterminate && updatedChecked ? false : updatedChecked, + }); + }; + + const handleGroupChange = (event: ChangeEvent) => { + const value = event.target.value; + if (controlledValues.indexOf(value) === -1) { + setControlledValues((prevControlledValues) => [ + ...prevControlledValues, + value, + ]); + } else { + setControlledValues((prevControlledValues) => + prevControlledValues.filter( + (controlledValue) => controlledValue !== value, + ), + ); + } + }; + + const indeterminate = controlledValues.length <= 1; + + const handleExport = () => { + console.log(`${controlledValues.length} file(s) exported`); + onClose(); + }; + + return ( + + + + + {checkboxesData.map((data) => ( + + ))} + + + + + ); +}; + +export const WithActions = ({ onOpenChange }: OverlayProps) => { + const [open, setOpen] = useState(false); + const id = useId(); + + const onChange = (newOpen: boolean) => { + setOpen(newOpen); + onOpenChange?.(newOpen); + }; + + return ( + + + + + + + + { + setOpen(false); + }} + /> + + + + ); +}; From b78a5594a94306024784ff3265b60566ccaf59f1 Mon Sep 17 00:00:00 2001 From: Fernanda Castillo Date: Mon, 14 Oct 2024 13:29:30 +0100 Subject: [PATCH 07/15] update imports --- packages/lab/src/overlay/OverlayHeader.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/lab/src/overlay/OverlayHeader.tsx b/packages/lab/src/overlay/OverlayHeader.tsx index 69a8263fd80..1a99fa74fcc 100644 --- a/packages/lab/src/overlay/OverlayHeader.tsx +++ b/packages/lab/src/overlay/OverlayHeader.tsx @@ -1,5 +1,4 @@ -import { H4, Text } from "@salt-ds/core/src/text"; -import { makePrefixer } from "@salt-ds/core/src/utils"; +import { H4, Text, makePrefixer } from "@salt-ds/core"; import { useComponentCssInjection } from "@salt-ds/styles"; import { useWindow } from "@salt-ds/window"; import clsx from "clsx"; From ed7d607fb53222892152ebbca3ee72ad36fc6ecc Mon Sep 17 00:00:00 2001 From: Fernanda Castillo Date: Tue, 15 Oct 2024 15:03:17 +0100 Subject: [PATCH 08/15] clean stories, pass header as node --- packages/lab/src/overlay/OverlayHeader.css | 2 +- packages/lab/src/overlay/OverlayHeader.tsx | 23 +- .../stories/overlay/overlay.qa.stories.tsx | 10 +- .../lab/stories/overlay/overlay.stories.tsx | 223 +----------------- 4 files changed, 23 insertions(+), 235 deletions(-) diff --git a/packages/lab/src/overlay/OverlayHeader.css b/packages/lab/src/overlay/OverlayHeader.css index e28a367f2a5..2a74f8fd5b0 100644 --- a/packages/lab/src/overlay/OverlayHeader.css +++ b/packages/lab/src/overlay/OverlayHeader.css @@ -17,7 +17,7 @@ gap: var(--salt-spacing-50); } -.saltOverlayHeader-header { +.saltOverlayHeader-header > .saltText { margin: 0; } diff --git a/packages/lab/src/overlay/OverlayHeader.tsx b/packages/lab/src/overlay/OverlayHeader.tsx index 1a99fa74fcc..2041b158569 100644 --- a/packages/lab/src/overlay/OverlayHeader.tsx +++ b/packages/lab/src/overlay/OverlayHeader.tsx @@ -1,4 +1,4 @@ -import { H4, Text, makePrefixer } from "@salt-ds/core"; +import { Text, makePrefixer } from "@salt-ds/core"; import { useComponentCssInjection } from "@salt-ds/styles"; import { useWindow } from "@salt-ds/window"; import clsx from "clsx"; @@ -25,10 +25,6 @@ export interface OverlayPanelContentProps * Header text */ header?: ReactNode; - /** - * The id of the Overlay Header, used for aria-labelledby on OverlayPanel - **/ - id?: string; /** * Preheader text is displayed just above the header **/ @@ -46,20 +42,17 @@ export const OverlayHeader = forwardRef< window: targetWindow, }); - const { className, description, header, actions, id, preheader, ...rest } = - props; + const { className, description, header, actions, preheader, ...rest } = props; return (
- {preheader && ( - {preheader} - )} - {header && ( -

- {header} -

- )} +
+ {preheader && ( + {preheader} + )} + {header} +
{description && ( {description} diff --git a/packages/lab/stories/overlay/overlay.qa.stories.tsx b/packages/lab/stories/overlay/overlay.qa.stories.tsx index 3dc37ca302a..898da3a9738 100644 --- a/packages/lab/stories/overlay/overlay.qa.stories.tsx +++ b/packages/lab/stories/overlay/overlay.qa.stories.tsx @@ -1,5 +1,6 @@ import { Button, + H4, Overlay, OverlayPanel, OverlayPanelContent, @@ -30,7 +31,7 @@ export const Default: StoryFn = (props) => { - + Header block} />
Content of Overlay
@@ -68,7 +69,12 @@ export const CloseButton: StoryFn = (props) => { - } /> + Header block} + actions={} + />
Content of Overlay
diff --git a/packages/lab/stories/overlay/overlay.stories.tsx b/packages/lab/stories/overlay/overlay.stories.tsx index 6b55c8040df..661dcfb2b59 100644 --- a/packages/lab/stories/overlay/overlay.stories.tsx +++ b/packages/lab/stories/overlay/overlay.stories.tsx @@ -1,8 +1,6 @@ import { Button, - Checkbox, - CheckboxGroup, - Divider, + H4, Overlay, OverlayPanel, OverlayPanelContent, @@ -16,44 +14,12 @@ import { import { CloseIcon } from "@salt-ds/icons"; import { OverlayHeader } from "@salt-ds/lab"; import type { Meta, StoryFn } from "@storybook/react"; -import { type ChangeEvent, useState } from "react"; +import { useState } from "react"; export default { title: "Lab/Overlay Header", } as Meta; -export const Default: StoryFn = ({ ...args }) => { - const id = useId(); - - return ( - - - - - - - - Content of Overlay - - - ); -}; - -export const Bottom = Default.bind({}); -Bottom.args = { - placement: "bottom", -}; - -export const Left = Default.bind({}); -Left.args = { - placement: "left", -}; - -export const Right = Default.bind({}); -Right.args = { - placement: "right", -}; - export const Header = ({ onOpenChange }: OverlayProps) => { const [open, setOpen] = useState(false); const id = useId(); @@ -74,7 +40,7 @@ export const Header = ({ onOpenChange }: OverlayProps) => { width: 500, }} > - + Header block} /> @@ -130,8 +96,9 @@ export const HeaderWithCloseButton = ({ onOpenChange }: OverlayProps) => { }} > Header block} actions={} /> @@ -154,181 +121,3 @@ export const HeaderWithCloseButton = ({ onOpenChange }: OverlayProps) => { ); }; - -export const LongContent = () => { - const [open, setOpen] = useState(true); - const id = useId(); - - const onOpenChange = (newOpen: boolean) => { - setOpen(newOpen); - }; - - const handleClose = () => setOpen(false); - - const CloseButton = () => ( - - ); - - return ( - - - - - - } /> - - - - A global leader, we deliver strategic advice and solutions, - including capital raising, risk management, and trade finance to - corporations, institutions and governments. A global leader, we - deliver strategic advice and solutions, including capital raising, - risk management, and trade finance to corporations, institutions - and governments. A global leader, we deliver strategic advice and - solutions, including capital raising, risk management, and trade - finance to corporations, institutions and governments. - - - A global leader, we deliver strategic advice and solutions, - including capital raising, risk management, and trade finance to - corporations, institutions and governments. A global leader, we - deliver strategic advice and solutions, including capital raising, - risk management, and trade finance to corporations, institutions - and governments. A global leader, we deliver strategic advice and - solutions, including capital raising, risk management, and trade - finance to corporations, institutions and governments. A global - leader, we deliver strategic advice and solutions, including - capital raising, risk management, and trade finance to - corporations, institutions and governments. - - Markets - - Serving the world's largest corporate clients and institutional - investors, we support the investment cycle with market-leading - research, analytics and trade execution across multiple asset - classes. - - - - - - ); -}; - -const checkboxesData = [ - { - label: "Overlay", - value: "overlay", - }, - { - label: "Row", - value: "row", - }, -]; - -const WithActionsContent = ({ onClose }: { onClose: () => void }) => { - const [controlledValues, setControlledValues] = useState([ - checkboxesData[0].value, - ]); - - const [checkboxState, setCheckboxState] = useState({ - checked: false, - indeterminate: true, - }); - - const handleChange = (event: ChangeEvent) => { - const updatedChecked = event.target.checked; - setCheckboxState({ - indeterminate: !updatedChecked && checkboxState.checked, - checked: - checkboxState.indeterminate && updatedChecked ? false : updatedChecked, - }); - }; - - const handleGroupChange = (event: ChangeEvent) => { - const value = event.target.value; - if (controlledValues.indexOf(value) === -1) { - setControlledValues((prevControlledValues) => [ - ...prevControlledValues, - value, - ]); - } else { - setControlledValues((prevControlledValues) => - prevControlledValues.filter( - (controlledValue) => controlledValue !== value, - ), - ); - } - }; - - const indeterminate = controlledValues.length <= 1; - - const handleExport = () => { - console.log(`${controlledValues.length} file(s) exported`); - onClose(); - }; - - return ( - - - - - {checkboxesData.map((data) => ( - - ))} - - - - - ); -}; - -export const WithActions = ({ onOpenChange }: OverlayProps) => { - const [open, setOpen] = useState(false); - const id = useId(); - - const onChange = (newOpen: boolean) => { - setOpen(newOpen); - onOpenChange?.(newOpen); - }; - - return ( - - - - - - - - { - setOpen(false); - }} - /> - - - - ); -}; From 0a8ed1fb41211534f810b613b29ab2725325efcd Mon Sep 17 00:00:00 2001 From: Fernanda Castillo Date: Tue, 15 Oct 2024 16:52:14 +0100 Subject: [PATCH 09/15] Update changesets button --- .changeset/quiet-rice-prove.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.changeset/quiet-rice-prove.md b/.changeset/quiet-rice-prove.md index 9b4cd234240..44b8408ae73 100644 --- a/.changeset/quiet-rice-prove.md +++ b/.changeset/quiet-rice-prove.md @@ -10,7 +10,13 @@ Added `OverlayHeader` component to lab. - }/> + + + }/> Content of Overlay From 92f945fa0f24ed20ea1c6ca05defe226e6b98f11 Mon Sep 17 00:00:00 2001 From: Fernanda Castillo Date: Thu, 17 Oct 2024 08:49:03 +0100 Subject: [PATCH 10/15] example in site --- site/docs/components/overlay/examples.mdx | 7 ++++ site/src/examples/overlay/WithHeader.tsx | 46 +++++++++++++++++++++++ site/src/examples/overlay/index.ts | 1 + 3 files changed, 54 insertions(+) create mode 100644 site/src/examples/overlay/WithHeader.tsx diff --git a/site/docs/components/overlay/examples.mdx b/site/docs/components/overlay/examples.mdx index e0c0bb316c7..1862f8dc4ab 100644 --- a/site/docs/components/overlay/examples.mdx +++ b/site/docs/components/overlay/examples.mdx @@ -48,5 +48,12 @@ When overlay content extends beyond the set height, the content defaults to scro With actions, users can decide if interacting with content in the overlay should close the overlay. This example shows how activating the **Export** button triggers the export function, closes the overlay, and returns focus to the trigger element. + + + ## 🚧 With header + + `OverlayHeader` can be added as a child of the `OverlayPanel` component to provide a structured header for overlay. The header includes a title and actions that follows our Header Block pattern. This approach is recommended over using the `OverlayPanelCloseButton` separately. + + diff --git a/site/src/examples/overlay/WithHeader.tsx b/site/src/examples/overlay/WithHeader.tsx new file mode 100644 index 00000000000..4a3b4099956 --- /dev/null +++ b/site/src/examples/overlay/WithHeader.tsx @@ -0,0 +1,46 @@ +import { + Button, + Overlay, + OverlayPanel, + OverlayPanelContent, + OverlayTrigger, + useId, +} from "@salt-ds/core"; +import { CloseIcon } from "@salt-ds/icons"; +import { OverlayHeader } from "@salt-ds/lab"; +import { type ReactElement, useState } from "react"; + +export const WithHeader = (): ReactElement => { + const [open, setOpen] = useState(false); + const id = useId(); + + const onOpenChange = (newOpen: boolean) => setOpen(newOpen); + + const handleClose = () => setOpen(false); + + const headerActions = ( + + ); + return ( + + + + + + Title} + actions={headerActions} + /> + +
Content of Overlay
+
+
+
+ ); +}; diff --git a/site/src/examples/overlay/index.ts b/site/src/examples/overlay/index.ts index 397ef08d1f7..4cfd414188f 100644 --- a/site/src/examples/overlay/index.ts +++ b/site/src/examples/overlay/index.ts @@ -2,4 +2,5 @@ export * from "./Default"; export * from "./Placement"; export * from "./LongContent"; export * from "./WithActions"; +export * from "./WithHeader"; export * from "./CloseButton"; From fee6f9eb58e38cdb88f2c64ec59ebf5676624287 Mon Sep 17 00:00:00 2001 From: Fernanda Castillo Date: Mon, 11 Nov 2024 10:21:37 +0000 Subject: [PATCH 11/15] update imports --- packages/lab/stories/overlay/overlay.stories.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/lab/stories/overlay/overlay.stories.tsx b/packages/lab/stories/overlay/overlay.stories.tsx index 661dcfb2b59..ef7610b0c8e 100644 --- a/packages/lab/stories/overlay/overlay.stories.tsx +++ b/packages/lab/stories/overlay/overlay.stories.tsx @@ -13,7 +13,7 @@ import { } from "@salt-ds/core"; import { CloseIcon } from "@salt-ds/icons"; import { OverlayHeader } from "@salt-ds/lab"; -import type { Meta, StoryFn } from "@storybook/react"; +import type { Meta } from "@storybook/react"; import { useState } from "react"; export default { From 42e0de3b87d9978919e4179970f10d9dd175b1f5 Mon Sep 17 00:00:00 2001 From: Fernanda Castillo Date: Wed, 13 Nov 2024 14:21:49 +0000 Subject: [PATCH 12/15] fix capitals --- packages/lab/stories/overlay/overlay.qa.stories.tsx | 4 ++-- packages/lab/stories/overlay/overlay.stories.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/lab/stories/overlay/overlay.qa.stories.tsx b/packages/lab/stories/overlay/overlay.qa.stories.tsx index 898da3a9738..09a58ba2de5 100644 --- a/packages/lab/stories/overlay/overlay.qa.stories.tsx +++ b/packages/lab/stories/overlay/overlay.qa.stories.tsx @@ -70,8 +70,8 @@ export const CloseButton: StoryFn = (props) => { Header block} actions={} /> diff --git a/packages/lab/stories/overlay/overlay.stories.tsx b/packages/lab/stories/overlay/overlay.stories.tsx index ef7610b0c8e..cf640781114 100644 --- a/packages/lab/stories/overlay/overlay.stories.tsx +++ b/packages/lab/stories/overlay/overlay.stories.tsx @@ -96,8 +96,8 @@ export const HeaderWithCloseButton = ({ onOpenChange }: OverlayProps) => { }} > Header block} actions={} /> From da1224d79c4e334e4f968d1342e1512c34111e93 Mon Sep 17 00:00:00 2001 From: Fernanda Castillo Date: Thu, 14 Nov 2024 10:11:30 +0000 Subject: [PATCH 13/15] updates from feedback --- packages/lab/src/overlay/OverlayHeader.tsx | 10 +++++----- site/docs/components/overlay/examples.mdx | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/lab/src/overlay/OverlayHeader.tsx b/packages/lab/src/overlay/OverlayHeader.tsx index 2041b158569..195008408d9 100644 --- a/packages/lab/src/overlay/OverlayHeader.tsx +++ b/packages/lab/src/overlay/OverlayHeader.tsx @@ -1,7 +1,7 @@ import { Text, makePrefixer } from "@salt-ds/core"; import { useComponentCssInjection } from "@salt-ds/styles"; import { useWindow } from "@salt-ds/window"; -import clsx from "clsx"; +import { clsx } from "clsx"; import { type ComponentPropsWithoutRef, type ReactNode, @@ -11,7 +11,7 @@ import overlayHeaderCss from "./OverlayHeader.css"; const withBaseName = makePrefixer("saltOverlayHeader"); -export interface OverlayPanelContentProps +export interface OverlayHeaderContentProps extends ComponentPropsWithoutRef<"div"> { /** * Description text is displayed just below the header @@ -33,11 +33,11 @@ export interface OverlayPanelContentProps export const OverlayHeader = forwardRef< HTMLDivElement, - OverlayPanelContentProps ->(function OverlayPanelContent(props, ref) { + OverlayHeaderContentProps +>(function OverlayHeaderContent(props, ref) { const targetWindow = useWindow(); useComponentCssInjection({ - testId: "salt-overlay-panel-content", + testId: "salt-overlay-header-content", css: overlayHeaderCss, window: targetWindow, }); diff --git a/site/docs/components/overlay/examples.mdx b/site/docs/components/overlay/examples.mdx index d0d8db85aba..baa2a3160e4 100644 --- a/site/docs/components/overlay/examples.mdx +++ b/site/docs/components/overlay/examples.mdx @@ -52,7 +52,9 @@ With actions, users can decide if interacting with content in the overlay should ## 🚧 With header - `OverlayHeader` can be added as a child of the `OverlayPanel` component to provide a structured header for overlay. The header includes a title and actions that follows our Header Block pattern. This approach is recommended over using the `OverlayPanelCloseButton` separately. + `OverlayHeader`'s update follows our standardized header for container components and app regions, and it can be added as a child of the `OverlayPanel` component to provide a structured header for overlay. The header includes a title and actions that follows our Header Block pattern. This approach is recommended over using the `OverlayPanelCloseButton` separately. + + **Note:** This change is currently in Lab. From 1e585d0f1c0d86fc2496a3b2fb7dbc567d36ff02 Mon Sep 17 00:00:00 2001 From: Fernanda Castillo Date: Mon, 18 Nov 2024 16:58:44 +0000 Subject: [PATCH 14/15] updates from feedback --- .changeset/quiet-rice-prove.md | 36 +++++++------ packages/lab/src/overlay/OverlayHeader.tsx | 59 +++++++++++----------- 2 files changed, 50 insertions(+), 45 deletions(-) diff --git a/.changeset/quiet-rice-prove.md b/.changeset/quiet-rice-prove.md index 44b8408ae73..83f3f042110 100644 --- a/.changeset/quiet-rice-prove.md +++ b/.changeset/quiet-rice-prove.md @@ -5,19 +5,25 @@ Added `OverlayHeader` component to lab. ```typescript - - - - - - - - }/> - Content of Overlay - - + + + + + + + + + } + /> + Content of Overlay + + ``` diff --git a/packages/lab/src/overlay/OverlayHeader.tsx b/packages/lab/src/overlay/OverlayHeader.tsx index 195008408d9..0375929a124 100644 --- a/packages/lab/src/overlay/OverlayHeader.tsx +++ b/packages/lab/src/overlay/OverlayHeader.tsx @@ -11,8 +11,7 @@ import overlayHeaderCss from "./OverlayHeader.css"; const withBaseName = makePrefixer("saltOverlayHeader"); -export interface OverlayHeaderContentProps - extends ComponentPropsWithoutRef<"div"> { +export interface OverlayHeaderProps extends ComponentPropsWithoutRef<"div"> { /** * Description text is displayed just below the header **/ @@ -31,37 +30,37 @@ export interface OverlayHeaderContentProps preheader?: ReactNode; } -export const OverlayHeader = forwardRef< - HTMLDivElement, - OverlayHeaderContentProps ->(function OverlayHeaderContent(props, ref) { - const targetWindow = useWindow(); - useComponentCssInjection({ - testId: "salt-overlay-header-content", - css: overlayHeaderCss, - window: targetWindow, - }); +export const OverlayHeader = forwardRef( + function OverlayHeader(props, ref) { + const targetWindow = useWindow(); + useComponentCssInjection({ + testId: "salt-overlay-header", + css: overlayHeaderCss, + window: targetWindow, + }); - const { className, description, header, actions, preheader, ...rest } = props; + const { className, description, header, actions, preheader, ...rest } = + props; - return ( -
-
-
- {preheader && ( - {preheader} + return ( +
+
+
+ {preheader && ( + {preheader} + )} + {header} +
+ {description && ( + + {description} + )} - {header}
- {description && ( - - {description} - + {actions && ( +
{actions}
)}
- {actions && ( -
{actions}
- )} -
- ); -}); + ); + }, +); From e63b1e346ed51522cc667e42faf5bb31704966d0 Mon Sep 17 00:00:00 2001 From: Fernanda Castillo Date: Tue, 19 Nov 2024 10:16:31 +0000 Subject: [PATCH 15/15] update qas and changeset --- .changeset/quiet-rice-prove.md | 4 +- .../stories/overlay/overlay.qa.stories.tsx | 46 ++++++------------- 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/.changeset/quiet-rice-prove.md b/.changeset/quiet-rice-prove.md index 83f3f042110..e84003a249d 100644 --- a/.changeset/quiet-rice-prove.md +++ b/.changeset/quiet-rice-prove.md @@ -4,7 +4,7 @@ Added `OverlayHeader` component to lab. -```typescript +```tsx @@ -19,7 +19,7 @@ Added `OverlayHeader` component to lab. appearance="transparent" sentiment="neutral" > - + } /> diff --git a/packages/lab/stories/overlay/overlay.qa.stories.tsx b/packages/lab/stories/overlay/overlay.qa.stories.tsx index 09a58ba2de5..1c7b5a5a0c4 100644 --- a/packages/lab/stories/overlay/overlay.qa.stories.tsx +++ b/packages/lab/stories/overlay/overlay.qa.stories.tsx @@ -17,35 +17,6 @@ export default { } as Meta; export const Default: StoryFn = (props) => { - return ( - - - - - - - Header block} /> - -
Content of Overlay
-
-
-
-
- ); -}; - -Default.parameters = { - chromatic: { disableSnapshot: false }, -}; - -export const CloseButton: StoryFn = (props) => { const CloseButton = () => (