From 701e0a7b8e09b2a1391d25321d98400da3b62de7 Mon Sep 17 00:00:00 2001 From: Henry Hein Date: Fri, 5 Apr 2024 10:03:36 +0800 Subject: [PATCH 01/15] feat: snackbar ui initial commit --- lib/components/Snackbar/SnackbarWrapper.tsx | 43 ++++++ lib/components/Snackbar/index.tsx | 91 +++++++++++++ lib/components/Snackbar/snackbar.scss | 97 ++++++++++++++ lib/components/Snackbar/snackbar.stories.tsx | 133 +++++++++++++++++++ package-lock.json | 11 ++ package.json | 1 + src/main.tsx | 11 +- 7 files changed, 379 insertions(+), 8 deletions(-) create mode 100644 lib/components/Snackbar/SnackbarWrapper.tsx create mode 100644 lib/components/Snackbar/index.tsx create mode 100644 lib/components/Snackbar/snackbar.scss create mode 100644 lib/components/Snackbar/snackbar.stories.tsx diff --git a/lib/components/Snackbar/SnackbarWrapper.tsx b/lib/components/Snackbar/SnackbarWrapper.tsx new file mode 100644 index 00000000..1b342ee9 --- /dev/null +++ b/lib/components/Snackbar/SnackbarWrapper.tsx @@ -0,0 +1,43 @@ +import { StandalonePlaceholderRegularIcon } from "@deriv/quill-icons/Standalone"; +import { Snackbar } from "."; +import React, { useState } from "react"; + +export const SnackbarWrapper = () => { + const [isOpen, setIsOpen] = useState(false); + + const handleActionClick = () => { + console.log("clicked"); + handleClose(); + }; + + const handleOpen = () => { + setIsOpen(true); + }; + + const handleClose = () => { + setTimeout(() => { + setIsOpen(false); + }, 1000); + }; + + return ( +
+ + + } + message="Unable to upload selected photos. \n The app will retry in 5 seconds." + actionText="Action" + onActionClick={handleActionClick} + isOpen={isOpen} + onClose={handleClose} + /> +
+ ); +}; diff --git a/lib/components/Snackbar/index.tsx b/lib/components/Snackbar/index.tsx new file mode 100644 index 00000000..85583a9b --- /dev/null +++ b/lib/components/Snackbar/index.tsx @@ -0,0 +1,91 @@ +import React, { ReactNode, useEffect, useState, HTMLAttributes } from "react"; +import { Text } from "../Typography"; +import "./snackbar.scss"; +import clsx from "clsx"; + +interface SnackbarProps extends HTMLAttributes { + icon?: ReactNode; + message: string; + actionText?: string; + hasCloseButton?: boolean; + onClose: () => void; + onActionClick?: () => void; + isOpen: boolean; +} + +export const Snackbar = ({ + icon: Icon = "", + message, + actionText = "", + onActionClick, + hasCloseButton = true, + isOpen = false, + onClose, + ...rest +}: SnackbarProps) => { + const [animationSpeed, setAnimationSpeed] = useState("slow"); + useEffect(() => { + if (isOpen) { + setAnimationSpeed("slow"); + const timer = setTimeout(() => { + onClose?.(); + }, 3000); + + return () => { + clearTimeout(timer); + }; + } + }, [isOpen]); + + const handleClose = () => { + onClose?.(); + setAnimationSpeed("fast"); + }; + + const handleActionClick = () => { + onActionClick?.(); + setAnimationSpeed("fast"); + }; + return ( + <> + {isOpen && ( +
+ {Icon && ( +
{Icon}
+ )} +
+ + {message} + +
+ {actionText && ( + + )} + {hasCloseButton && ( + + )} +
+ )} + + ); +}; diff --git a/lib/components/Snackbar/snackbar.scss b/lib/components/Snackbar/snackbar.scss new file mode 100644 index 00000000..be5e1358 --- /dev/null +++ b/lib/components/Snackbar/snackbar.scss @@ -0,0 +1,97 @@ +@import "@quill/spacing.scss"; +@import "@quill/border.scss"; +@import "@quill/breakpoints.scss"; +@import "@quill/motion.scss"; +@import "@quill/elevation.scss"; + +.snackbar { + display: flex; + width: 340px; + max-width: 560px; + min-height: 48px; + padding: var(--semantic-spacing-general-sm) + var(--semantic-spacing-general-md) var(--semantic-spacing-general-sm); + align-items: center; + gap: var(--semantic-spacing-gap-md); + border-radius: var(--semantic-borderRadius-md); + border: var(--borderWidth-xs) solid + var(--core-color-opacity-white-100, rgba(255, 255, 255, 0.08)); + background: var(--core-color-solid-slate-1400, #000); + box-shadow: var(--core-elevation-shadow-330); + position: absolute; + z-index: 1; + bottom: -100%; + transition: bottom var(--motion-duration-snappy) + var(--motion-easing-inandout); + + &.slow-animation { + animation: + slide-up var(--motion-duration-snappy) var(--motion-easing-inandout) + forwards, + slide-down var(--motion-duration-snappy) 3.5s + var(--motion-easing-inandout) forwards; + } + &.fast-animation { + animation: + slide-up var(--motion-duration-snappy) var(--motion-easing-inandout) + forwards, + slide-down var(--motion-duration-snappy) 1.5s + var(--motion-easing-inandout) forwards; + } + + @include breakpoint("sm") { + display: inline-flex; + width: 560px; + } + + &__icon--container { + display: flex; + width: 24px; + height: 24px; + flex-direction: column; + justify-content: center; + align-items: center; + flex-shrink: 0; + } + &__message { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + flex: 1 0 0; + + text-overflow: ellipsis; + overflow: hidden; + word-wrap: break-word; /* Text gets broken up into another line for long words */ + color: var(--core-color-solid-slate-50, #fff); + + @include breakpoint("sm") { + -webkit-line-clamp: 1; + } + + &--container { + display: flex; + padding-right: var(--semantic-spacing-general-sm); + align-items: flex-start; + flex: 1 0 0; + min-width: 0; /* Text broken up into another line for long words */ + } + } +} + +@keyframes slide-up { + 0% { + bottom: -100%; + } + 100% { + bottom: 32px; + } +} + +@keyframes slide-down { + 0% { + bottom: 32px; + } + 100% { + bottom: -100%; + } +} diff --git a/lib/components/Snackbar/snackbar.stories.tsx b/lib/components/Snackbar/snackbar.stories.tsx new file mode 100644 index 00000000..e98f7471 --- /dev/null +++ b/lib/components/Snackbar/snackbar.stories.tsx @@ -0,0 +1,133 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { StandalonePlaceholderRegularIcon } from "@deriv/quill-icons/Standalone"; +import { fn } from "@storybook/test"; +import { Snackbar } from "."; + +/* */ + +const meta = { + title: "Components/Snackbar/Snackbar", + component: Snackbar, + // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs + tags: ["autodocs"], + args: { + icon: , + message: "Message goes here", + actionText: "Action", + isOpen: false, + onActionClick: fn(), + onClose: fn(), + hasCloseButton: true, + }, + argTypes: { + icon: { + description: + "Optional. There will be no icon if icon is not provided.", + table: { type: { summary: "Reactnode | undefined" } }, + }, + message: { + control: "text", + description: "Required. Enter any message.", + }, + actionText: { + control: "text", + description: + "Optional. Action button will not be included if actionText is null or an empty string.", + table: { type: { summary: "string | undefined" } }, + }, + onActionClick: { + description: + "Optional. Must be included if we have actionText to trigger action and close snackbar.", + }, + isOpen: { + description: + "Required. Controls opening and closing of snackbar. Should be set to false by default.", + control: "boolean", + }, + onClose: { + description: + "Required. Needed to close snackbar after a certain time.", + }, + hasCloseButton: { + control: "boolean", + description: "Optional. Set to true by default.", + }, + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const defaultSnackbar: Story = {}; + +export const SnackbarWithMessageOnly: Story = { + args: { + isOpen: true, + icon: "", + hasCloseButton: false, + actionText: "", + }, +}; + +export const SnackbarWithIcon: Story = { + args: { + isOpen: true, + icon: , + hasCloseButton: false, + actionText: "", + }, +}; + +export const SnackbarWithAction: Story = { + args: { + isOpen: true, + icon: "", + hasCloseButton: false, + actionText: "Action", + onActionClick: fn(), + }, +}; + +export const SnackbarWithCloseButton: Story = { + args: { + isOpen: true, + icon: "", + actionText: "", + onClose: fn(), + }, +}; + +export const SnackbarWithTwoLinesMessage: Story = { + args: { + isOpen: true, + icon: "", + message: + "This is an extremely long text that goes on another line. Lorem ipsum lorem lorem. Lorem ipsum lorem lorem. Lorem ipsum lorem lorem.", + hasCloseButton: false, + actionText: "", + }, +}; + +export const SnackbarWithTwoLinesMessageWithCloseButton: Story = { + args: { + isOpen: true, + icon: "", + message: + "This is an extremely long text that goes on another line. Lorem ipsum lorem lorem.", + onClose: fn(), + actionText: "", + }, +}; + +export const SnackbarWithTwoLinesMessageWithActionButton: Story = { + args: { + isOpen: true, + icon: "", + message: + "This is an extremely long text that goes on another line. Lorem ipsum lorem lorem.", + actionText: "Action", + hasCloseButton: false, + onActionClick: fn(), + }, +}; diff --git a/package-lock.json b/package-lock.json index 1a746f80..42919e30 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@babel/preset-typescript": "^7.23.3", "@commitlint/cli": "^19.1.0", "@commitlint/config-conventional": "^19.1.0", + "@deriv/quill-icons": "^1.19.8", "@semantic-release/changelog": "^6.0.3", "@semantic-release/github": "^9.2.6", "@semantic-release/npm": "^11.0.2", @@ -2488,6 +2489,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/@deriv/quill-icons": { + "version": "1.19.19", + "resolved": "https://registry.npmjs.org/@deriv/quill-icons/-/quill-icons-1.19.19.tgz", + "integrity": "sha512-l1TU0eMl9rpns5+aDrjW+8JRT+ngTW11vgXAaQ51XL2U2AOQUebgKQZM0DQVJkfja9Su8jPrgGRTukumAU+4OA==", + "dev": true, + "peerDependencies": { + "react": ">= 16", + "react-dom": ">= 16" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", diff --git a/package.json b/package.json index 5782a835..f7c065cd 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", "@commitlint/cli": "^19.1.0", + "@deriv/quill-icons": "^1.19.8", "@commitlint/config-conventional": "^19.1.0", "@semantic-release/changelog": "^6.0.3", "@semantic-release/github": "^9.2.6", diff --git a/src/main.tsx b/src/main.tsx index 1b92b552..b8f328f0 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,18 +1,13 @@ import React from "react"; import ReactDOM from "react-dom/client"; -import { Text, Heading, ThemeProvider } from "../lib/main"; -import Button from "./button"; +import { ThemeProvider } from "../lib/main"; +import { SnackbarWrapper } from "../lib/components/Snackbar/SnackbarWrapper"; ReactDOM.createRoot(document.getElementById("root")!).render(
-
, From 31f678c2904652694019c508c5a9ae69bbf0294b Mon Sep 17 00:00:00 2001 From: Henry Hein Date: Fri, 5 Apr 2024 11:55:19 +0800 Subject: [PATCH 02/15] fix: change height of canvas for better visibility --- lib/components/Snackbar/snackbar.stories.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/components/Snackbar/snackbar.stories.tsx b/lib/components/Snackbar/snackbar.stories.tsx index e98f7471..a5a931b7 100644 --- a/lib/components/Snackbar/snackbar.stories.tsx +++ b/lib/components/Snackbar/snackbar.stories.tsx @@ -59,7 +59,15 @@ export default meta; type Story = StoryObj; -export const defaultSnackbar: Story = {}; +export const defaultSnackbar: Story = { + parameters: { + docs: { + story: { + height: '100px', + } + } + } +}; export const SnackbarWithMessageOnly: Story = { args: { From 0aaaca6f6357972c760c0278e1ca9c52a232376f Mon Sep 17 00:00:00 2001 From: Henry Hein Date: Fri, 5 Apr 2024 12:00:51 +0800 Subject: [PATCH 03/15] fix: revert package.json and package-lock.json --- package-lock.json | 11 ----------- package.json | 1 - 2 files changed, 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 42919e30..1a746f80 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,6 @@ "@babel/preset-typescript": "^7.23.3", "@commitlint/cli": "^19.1.0", "@commitlint/config-conventional": "^19.1.0", - "@deriv/quill-icons": "^1.19.8", "@semantic-release/changelog": "^6.0.3", "@semantic-release/github": "^9.2.6", "@semantic-release/npm": "^11.0.2", @@ -2489,16 +2488,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@deriv/quill-icons": { - "version": "1.19.19", - "resolved": "https://registry.npmjs.org/@deriv/quill-icons/-/quill-icons-1.19.19.tgz", - "integrity": "sha512-l1TU0eMl9rpns5+aDrjW+8JRT+ngTW11vgXAaQ51XL2U2AOQUebgKQZM0DQVJkfja9Su8jPrgGRTukumAU+4OA==", - "dev": true, - "peerDependencies": { - "react": ">= 16", - "react-dom": ">= 16" - } - }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", diff --git a/package.json b/package.json index f7c065cd..5782a835 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,6 @@ "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", "@commitlint/cli": "^19.1.0", - "@deriv/quill-icons": "^1.19.8", "@commitlint/config-conventional": "^19.1.0", "@semantic-release/changelog": "^6.0.3", "@semantic-release/github": "^9.2.6", From 8ae61550dffa5ae2313ec2271a7d0246e5d2aac5 Mon Sep 17 00:00:00 2001 From: Henry Hein Date: Fri, 5 Apr 2024 12:02:43 +0800 Subject: [PATCH 04/15] fix: revert main.tsx to head of main branch --- src/main.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main.tsx b/src/main.tsx index b8f328f0..40673064 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,13 +1,18 @@ import React from "react"; import ReactDOM from "react-dom/client"; -import { ThemeProvider } from "../lib/main"; -import { SnackbarWrapper } from "../lib/components/Snackbar/SnackbarWrapper"; +import { Text, Heading, ThemeProvider } from "../lib/main"; +import Button from "./button"; ReactDOM.createRoot(document.getElementById("root")!).render(
- +
, From caa3c5338bb56ec95a143c0257a4e9358fb5d984 Mon Sep 17 00:00:00 2001 From: Henry Hein Date: Fri, 5 Apr 2024 12:03:05 +0800 Subject: [PATCH 05/15] fix: revert main.tsx to head of main branch --- src/main.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.tsx b/src/main.tsx index 40673064..1b92b552 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -7,7 +7,7 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
- + +
+ +`; + +exports[`Snackbar calls onClose after a certain duration when Snackbar is open 1`] = ` +
+
+
+

+ Test message +

+
+ +
+
+`; + +exports[`Snackbar calls onClose when close button is clicked 1`] = ` +
+
+
+

+ Test message +

+
+ +
+
+`; + +exports[`Snackbar renders correctly with custom icon 1`] = ` +
+
+
+ Custom Icon +
+
+

+ Test message +

+
+ +
+
+`; + +exports[`Snackbar renders correctly without action button 1`] = ` +
+
+
+

+ Test message +

+
+ +
+
+`; + +exports[`Snackbar renders correctly without close button 1`] = ` +
+
+
+

+ Test message +

+
+
+
+`; + +exports[`Snackbar renders with default props 1`] = ` +
+
+
+

+ Test message +

+
+ +
+
+`; diff --git a/lib/components/Snackbar/__tests__/snackbar.test.tsx b/lib/components/Snackbar/__tests__/snackbar.test.tsx new file mode 100644 index 00000000..554c19df --- /dev/null +++ b/lib/components/Snackbar/__tests__/snackbar.test.tsx @@ -0,0 +1,109 @@ +import { render, screen, fireEvent } from "@testing-library/react"; +import { Snackbar } from ".."; + +describe("Snackbar", () => { + it("renders with default props", () => { + const { container } = render( + , + ); + expect(screen.getByText("Test message")).toBeInTheDocument(); + expect(container).toMatchSnapshot(); + }); + it("calls onActionClick when action button is clicked", () => { + const onActionClickMock = jest.fn(); + const { container } = render( + , + ); + + fireEvent.click(screen.getByText("Action")); + expect(onActionClickMock).toHaveBeenCalled(); + expect(container).toMatchSnapshot(); + }); + it("calls onClose when close button is clicked", () => { + const onCloseMock = jest.fn(); + const { container } = render( + , + ); + + fireEvent.click(screen.getByText("x")); + expect(onCloseMock).toHaveBeenCalled(); + expect(container).toMatchSnapshot(); + }); + it("calls onClose after a certain duration when Snackbar is open", async () => { + jest.useFakeTimers(); + + const onCloseMock = jest.fn(); + const { container } = render( + , + ); + + jest.advanceTimersByTime(3000); + expect(onCloseMock).toHaveBeenCalled(); + expect(container).toMatchSnapshot(); + + jest.useRealTimers(); + }); + it("renders correctly with custom icon", () => { + const customIcon = ( + Custom Icon + ); + const { container } = render( + , + ); + + expect(screen.getByTestId("custom-icon")).toBeInTheDocument(); + expect(container).toMatchSnapshot(); + }); + it("renders correctly without action button", () => { + const { container } = render( + {}} + />, + ); + + expect(screen.queryByText("Action")).toBeNull(); + expect(container).toMatchSnapshot(); + }); + it("renders correctly without close button", () => { + const { container } = render( + , + ); + + expect(screen.queryByText("x")).toBeNull(); + expect(container).toMatchSnapshot(); + }); +}); From 1daa5699784a01c6b427d321d28b838958a8d8a9 Mon Sep 17 00:00:00 2001 From: Henry Hein Date: Fri, 5 Apr 2024 12:39:50 +0800 Subject: [PATCH 07/15] fix: re-add package and package-lock --- package-lock.json | 11 +++++++++++ package.json | 1 + src/main.tsx | 16 ++++------------ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1a746f80..fa0f983e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@babel/preset-typescript": "^7.23.3", "@commitlint/cli": "^19.1.0", "@commitlint/config-conventional": "^19.1.0", + "@deriv/quill-icons": "^1.19.8", "@semantic-release/changelog": "^6.0.3", "@semantic-release/github": "^9.2.6", "@semantic-release/npm": "^11.0.2", @@ -2488,6 +2489,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/@deriv/quill-icons": { + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/@deriv/quill-icons/-/quill-icons-1.21.1.tgz", + "integrity": "sha512-aDag0De2cP5zzjl1bk7duX6PTeNh9ZGeI+f5/iAo3BVTTUXIGxcDcOpvuZ9MGB3TOjk2dpaw3NCJG8uqLxXtZw==", + "dev": true, + "peerDependencies": { + "react": ">= 16", + "react-dom": ">= 16" + } + }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", diff --git a/package.json b/package.json index 5782a835..f7c065cd 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", "@commitlint/cli": "^19.1.0", + "@deriv/quill-icons": "^1.19.8", "@commitlint/config-conventional": "^19.1.0", "@semantic-release/changelog": "^6.0.3", "@semantic-release/github": "^9.2.6", diff --git a/src/main.tsx b/src/main.tsx index 1b92b552..9a7ea41c 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,19 +1,11 @@ import React from "react"; import ReactDOM from "react-dom/client"; -import { Text, Heading, ThemeProvider } from "../lib/main"; -import Button from "./button"; +import { SnackbarWrapper } from "../lib/components/Snackbar/SnackbarWrapper"; ReactDOM.createRoot(document.getElementById("root")!).render( - -
-
-
+
+ +
, ); From 01e37d6d432f923bbd4245a756dd3a63a6680c1c Mon Sep 17 00:00:00 2001 From: Henry Hein Date: Fri, 5 Apr 2024 12:41:11 +0800 Subject: [PATCH 08/15] fix: revert main.tsx --- src/main.tsx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main.tsx b/src/main.tsx index 9a7ea41c..d32198e6 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,11 +1,19 @@ import React from "react"; import ReactDOM from "react-dom/client"; -import { SnackbarWrapper } from "../lib/components/Snackbar/SnackbarWrapper"; +import { Text, Heading, ThemeProvider } from "../lib/main"; +import Button from "./button"; ReactDOM.createRoot(document.getElementById("root")!).render( -
- -
+ +
+
+
, -); +); \ No newline at end of file From 575b92da72f712162cdcafebc7d10688bb5ed212 Mon Sep 17 00:00:00 2001 From: Henry Hein Date: Fri, 5 Apr 2024 12:42:09 +0800 Subject: [PATCH 09/15] fix: revert main.tsx --- src/main.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.tsx b/src/main.tsx index d32198e6..1b92b552 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -16,4 +16,4 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
, -); \ No newline at end of file +); From 9c55468c53df043feb731cceba1bae3a500ebc9a Mon Sep 17 00:00:00 2001 From: Henry Hein Date: Mon, 8 Apr 2024 14:05:54 +0800 Subject: [PATCH 10/15] fix: comments --- lib/components/Snackbar/SnackbarWrapper.tsx | 3 +- .../__snapshots__/snackbar.test.tsx.snap | 178 ------------------ .../Snackbar/__tests__/snackbar.test.tsx | 115 +++-------- lib/components/Snackbar/index.tsx | 37 ++-- lib/components/Snackbar/snackbar.scss | 27 +-- lib/components/Snackbar/snackbar.stories.tsx | 2 - package-lock.json | 2 - package.json | 1 - 8 files changed, 63 insertions(+), 302 deletions(-) delete mode 100644 lib/components/Snackbar/__tests__/__snapshots__/snackbar.test.tsx.snap diff --git a/lib/components/Snackbar/SnackbarWrapper.tsx b/lib/components/Snackbar/SnackbarWrapper.tsx index 1b342ee9..b9364a42 100644 --- a/lib/components/Snackbar/SnackbarWrapper.tsx +++ b/lib/components/Snackbar/SnackbarWrapper.tsx @@ -6,7 +6,6 @@ export const SnackbarWrapper = () => { const [isOpen, setIsOpen] = useState(false); const handleActionClick = () => { - console.log("clicked"); handleClose(); }; @@ -32,7 +31,7 @@ export const SnackbarWrapper = () => { iconSize="sm" /> } - message="Unable to upload selected photos. \n The app will retry in 5 seconds." + message="Enter message here" actionText="Action" onActionClick={handleActionClick} isOpen={isOpen} diff --git a/lib/components/Snackbar/__tests__/__snapshots__/snackbar.test.tsx.snap b/lib/components/Snackbar/__tests__/__snapshots__/snackbar.test.tsx.snap deleted file mode 100644 index fbf12d12..00000000 --- a/lib/components/Snackbar/__tests__/__snapshots__/snackbar.test.tsx.snap +++ /dev/null @@ -1,178 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Snackbar calls onActionClick when action button is clicked 1`] = ` -
-
-
-

- Test message -

-
- - -
-
-`; - -exports[`Snackbar calls onClose after a certain duration when Snackbar is open 1`] = ` -
-
-
-

- Test message -

-
- -
-
-`; - -exports[`Snackbar calls onClose when close button is clicked 1`] = ` -
-
-
-

- Test message -

-
- -
-
-`; - -exports[`Snackbar renders correctly with custom icon 1`] = ` -
-
-
- Custom Icon -
-
-

- Test message -

-
- -
-
-`; - -exports[`Snackbar renders correctly without action button 1`] = ` -
-
-
-

- Test message -

-
- -
-
-`; - -exports[`Snackbar renders correctly without close button 1`] = ` -
-
-
-

- Test message -

-
-
-
-`; - -exports[`Snackbar renders with default props 1`] = ` -
-
-
-

- Test message -

-
- -
-
-`; diff --git a/lib/components/Snackbar/__tests__/snackbar.test.tsx b/lib/components/Snackbar/__tests__/snackbar.test.tsx index 554c19df..5831f35c 100644 --- a/lib/components/Snackbar/__tests__/snackbar.test.tsx +++ b/lib/components/Snackbar/__tests__/snackbar.test.tsx @@ -2,108 +2,53 @@ import { render, screen, fireEvent } from "@testing-library/react"; import { Snackbar } from ".."; describe("Snackbar", () => { + const testMessage = "test message" + const defaultProps = { + message: testMessage, + isOpen: true, + onClose: jest.fn(), + }; + + const renderComponent = (props = {}) => + render(); + it("renders with default props", () => { - const { container } = render( - , - ); - expect(screen.getByText("Test message")).toBeInTheDocument(); - expect(container).toMatchSnapshot(); + renderComponent(); + expect(screen.getByText(testMessage)).toBeInTheDocument(); }); it("calls onActionClick when action button is clicked", () => { const onActionClickMock = jest.fn(); - const { container } = render( - , - ); - + renderComponent({ + actionText: "Action", + onActionClick: onActionClickMock, + }); fireEvent.click(screen.getByText("Action")); expect(onActionClickMock).toHaveBeenCalled(); - expect(container).toMatchSnapshot(); }); it("calls onClose when close button is clicked", () => { - const onCloseMock = jest.fn(); - const { container } = render( - , - ); - - fireEvent.click(screen.getByText("x")); - expect(onCloseMock).toHaveBeenCalled(); - expect(container).toMatchSnapshot(); + renderComponent(); + fireEvent.click(screen.getByTestId('close-button')); + expect(defaultProps.onClose).toHaveBeenCalled(); }); - it("calls onClose after a certain duration when Snackbar is open", async () => { + it("calls onClose after a certain duration when Snackbar is open", () => { jest.useFakeTimers(); - - const onCloseMock = jest.fn(); - const { container } = render( - , - ); - + renderComponent(); jest.advanceTimersByTime(3000); - expect(onCloseMock).toHaveBeenCalled(); - expect(container).toMatchSnapshot(); - + expect(defaultProps.onClose).toHaveBeenCalled(); jest.useRealTimers(); }); it("renders correctly with custom icon", () => { - const customIcon = ( - Custom Icon - ); - const { container } = render( - , - ); - - expect(screen.getByTestId("custom-icon")).toBeInTheDocument(); - expect(container).toMatchSnapshot(); + renderComponent({ + icon: Custom Icon, + }); + expect(screen.getByAltText("Custom Icon")).toBeInTheDocument(); }); it("renders correctly without action button", () => { - const { container } = render( - {}} - />, - ); - - expect(screen.queryByText("Action")).toBeNull(); - expect(container).toMatchSnapshot(); + renderComponent(); + expect(screen.queryByText("Action")).not.toBeInTheDocument(); }); it("renders correctly without close button", () => { - const { container } = render( - , - ); - - expect(screen.queryByText("x")).toBeNull(); - expect(container).toMatchSnapshot(); + renderComponent({ hasCloseButton: false }); + expect(screen.queryByText("x")).not.toBeInTheDocument(); }); }); diff --git a/lib/components/Snackbar/index.tsx b/lib/components/Snackbar/index.tsx index 85583a9b..e625daed 100644 --- a/lib/components/Snackbar/index.tsx +++ b/lib/components/Snackbar/index.tsx @@ -2,6 +2,8 @@ import React, { ReactNode, useEffect, useState, HTMLAttributes } from "react"; import { Text } from "../Typography"; import "./snackbar.scss"; import clsx from "clsx"; +import { Button } from "../Button"; +import { LabelPairedXmarkSmBoldIcon } from "@deriv/quill-icons"; interface SnackbarProps extends HTMLAttributes { icon?: ReactNode; @@ -14,19 +16,24 @@ interface SnackbarProps extends HTMLAttributes { } export const Snackbar = ({ - icon: Icon = "", + icon: Icon, message, - actionText = "", + actionText, onActionClick, hasCloseButton = true, isOpen = false, onClose, ...rest }: SnackbarProps) => { - const [animationSpeed, setAnimationSpeed] = useState("slow"); + const animationSpeedObj = Object.freeze({ + fast: 'fast', + slow: 'slow' + }); + + const [animationSpeed, setAnimationSpeed] = useState(animationSpeedObj.slow); useEffect(() => { if (isOpen) { - setAnimationSpeed("slow"); + setAnimationSpeed(animationSpeedObj.slow); const timer = setTimeout(() => { onClose?.(); }, 3000); @@ -39,12 +46,12 @@ export const Snackbar = ({ const handleClose = () => { onClose?.(); - setAnimationSpeed("fast"); + setAnimationSpeed(animationSpeedObj.fast); }; const handleActionClick = () => { onActionClick?.(); - setAnimationSpeed("fast"); + setAnimationSpeed(animationSpeedObj.fast); }; return ( <> @@ -52,7 +59,7 @@ export const Snackbar = ({
{actionText && ( - + + + + + +`; + +exports[`Snackbar calls onClose after a certain duration when Snackbar is open 1`] = ` +
+
+
+

+ test message +

+
+ +
+
+`; + +exports[`Snackbar calls onClose when close button is clicked 1`] = ` +
+
+
+

+ test message +

+
+ +
+
+`; + +exports[`Snackbar renders correctly with custom icon 1`] = ` +
+
+
+ Custom Icon +
+
+

+ test message +

+
+ +
+
+`; + +exports[`Snackbar renders correctly without action button 1`] = ` +
+
+
+

+ test message +

+
+ +
+
+`; + +exports[`Snackbar renders correctly without close button 1`] = ` +
+
+
+

+ test message +

+
+
+
+`; + +exports[`Snackbar renders with default props 1`] = ` +
+
+
+

+ test message +

+
+ +
+
+`; diff --git a/lib/components/Snackbar/__tests__/snackbar.test.tsx b/lib/components/Snackbar/__tests__/snackbar.test.tsx index 5831f35c..1cd906c8 100644 --- a/lib/components/Snackbar/__tests__/snackbar.test.tsx +++ b/lib/components/Snackbar/__tests__/snackbar.test.tsx @@ -13,42 +13,49 @@ describe("Snackbar", () => { render(); it("renders with default props", () => { - renderComponent(); + const { container } = renderComponent(); expect(screen.getByText(testMessage)).toBeInTheDocument(); + expect(container).toMatchSnapshot(); }); it("calls onActionClick when action button is clicked", () => { const onActionClickMock = jest.fn(); - renderComponent({ + const { container } = renderComponent({ actionText: "Action", onActionClick: onActionClickMock, }); fireEvent.click(screen.getByText("Action")); expect(onActionClickMock).toHaveBeenCalled(); + expect(container).toMatchSnapshot(); }); it("calls onClose when close button is clicked", () => { - renderComponent(); + const { container } = renderComponent(); fireEvent.click(screen.getByTestId('close-button')); expect(defaultProps.onClose).toHaveBeenCalled(); + expect(container).toMatchSnapshot(); }); it("calls onClose after a certain duration when Snackbar is open", () => { jest.useFakeTimers(); - renderComponent(); + const { container } = renderComponent(); jest.advanceTimersByTime(3000); expect(defaultProps.onClose).toHaveBeenCalled(); jest.useRealTimers(); + expect(container).toMatchSnapshot(); }); it("renders correctly with custom icon", () => { - renderComponent({ + const { container } = renderComponent({ icon: Custom Icon, }); expect(screen.getByAltText("Custom Icon")).toBeInTheDocument(); + expect(container).toMatchSnapshot(); }); it("renders correctly without action button", () => { - renderComponent(); + const { container } = renderComponent(); expect(screen.queryByText("Action")).not.toBeInTheDocument(); + expect(container).toMatchSnapshot(); }); it("renders correctly without close button", () => { - renderComponent({ hasCloseButton: false }); + const { container } = renderComponent({ hasCloseButton: false }); expect(screen.queryByText("x")).not.toBeInTheDocument(); + expect(container).toMatchSnapshot(); }); }); From 7b5432db0eee0a2bd78c2993e375b82fcde54c73 Mon Sep 17 00:00:00 2001 From: Henry Hein Date: Mon, 8 Apr 2024 14:44:12 +0800 Subject: [PATCH 12/15] fix: remove package-lock --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 08d484b6..0331e01d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2491,9 +2491,9 @@ } }, "node_modules/@deriv/quill-icons": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/@deriv/quill-icons/-/quill-icons-1.21.1.tgz", - "integrity": "sha512-aDag0De2cP5zzjl1bk7duX6PTeNh9ZGeI+f5/iAo3BVTTUXIGxcDcOpvuZ9MGB3TOjk2dpaw3NCJG8uqLxXtZw==", + "version": "1.19.8", + "resolved": "https://registry.npmjs.org/@deriv/quill-icons/-/quill-icons-1.19.8.tgz", + "integrity": "sha512-yCgYBhJIJSuhRmA7+o3a+wGNnwkRSkboYNms4KlHSDqy8faQrn+q98XqzC/s1ZhnodsiJxnVOuJFNtjKnQgT/g==", "peerDependencies": { "react": ">= 16", "react-dom": ">= 16" From 7adc49486c9785787a3f1064b048b6e9ffe1d6a2 Mon Sep 17 00:00:00 2001 From: Henry Hein Date: Mon, 8 Apr 2024 16:39:07 +0800 Subject: [PATCH 13/15] fix: make snackbar compatible with small screen size --- lib/components/Snackbar/snackbar.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/components/Snackbar/snackbar.scss b/lib/components/Snackbar/snackbar.scss index 735ea6c1..2551a785 100644 --- a/lib/components/Snackbar/snackbar.scss +++ b/lib/components/Snackbar/snackbar.scss @@ -7,7 +7,7 @@ .snackbar { display: flex; - width: 340px; + width: calc(100vw - var(--core-spacing-1600)); max-width: 560px; min-height: var(--core-size-2400); padding: var(--semantic-spacing-general-sm) From b6135ce50142cf5bcad63a13faf723fb3733312d Mon Sep 17 00:00:00 2001 From: Henry Hein Date: Tue, 9 Apr 2024 10:18:37 +0800 Subject: [PATCH 14/15] fix: empty commit From 7b04ad137ba1c70506785fc8f8195285766133ad Mon Sep 17 00:00:00 2001 From: Henry Hein Date: Mon, 15 Apr 2024 13:42:57 +0800 Subject: [PATCH 15/15] fix: breakpoints mixin bug --- lib/styles/quill/breakpoints.scss | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/styles/quill/breakpoints.scss b/lib/styles/quill/breakpoints.scss index 0477dcb3..2dc658bf 100644 --- a/lib/styles/quill/breakpoints.scss +++ b/lib/styles/quill/breakpoints.scss @@ -4,26 +4,27 @@ @content; } } - @if $breakpoint == "md" { + @else if $breakpoint == "md" { @media (min-width: 768px) { @content; } } - @if $breakpoint == "lg" { + @else if $breakpoint == "lg" { @media (min-width: 1024px) { @content; } } - @if $breakpoint == "xl" { + @else if $breakpoint == "xl" { @media (min-width: 1280px) { @content; } } - @if $breakpoint == "2xl" { + @else if $breakpoint == "2xl" { @media (min-width: 1440px) { @content; } - } @else { + } + @else { @warn "Unknown breakpoint: #{$breakpoint}."; } }