From f21f0b645e177adf9def33b54834948420ebdfbe Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Tue, 5 Mar 2024 12:21:33 -0800 Subject: [PATCH 01/10] components: Preflight --- packages/components/rollup.config.js | 9 + packages/components/src/main.css | 3 + packages/components/src/preflight.css | 409 +++++++++++++++++++++++++ packages/components/tailwind.config.ts | 3 + 4 files changed, 424 insertions(+) create mode 100644 packages/components/src/preflight.css diff --git a/packages/components/rollup.config.js b/packages/components/rollup.config.js index 3e081870..244a8095 100644 --- a/packages/components/rollup.config.js +++ b/packages/components/rollup.config.js @@ -41,6 +41,15 @@ export default [ insertAt: "top", }, }), + postcss({ + config: { + path: "./postcss.config.js", + }, + include: "./main.css", + extract: "main.css", + minimize: true, + sourceMap: true, + }), terser(), ], }, diff --git a/packages/components/src/main.css b/packages/components/src/main.css index f2e73947..54fde74c 100644 --- a/packages/components/src/main.css +++ b/packages/components/src/main.css @@ -1,6 +1,9 @@ /* Fonts import should come first */ @import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i,700,700i|Space+Mono:400,400i,700,700i&display=swap"); +/* Replaces tailwind preflight to fix button transparency in local development */ +@import "./preflight.css"; + @tailwind base; html, diff --git a/packages/components/src/preflight.css b/packages/components/src/preflight.css new file mode 100644 index 00000000..b4c5f5a6 --- /dev/null +++ b/packages/components/src/preflight.css @@ -0,0 +1,409 @@ +/* stylelint-disable function-no-unknown */ + +/* +Copied from https://github.com/tailwindlabs/tailwindcss/blob/master/src/css/preflight.css +Preflight is set to false and this css file is imported in global.css instead. +Need this until this transparent button issue is fixed https://github.com/tailwindlabs/tailwindcss/discussions/5969 +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; /* 1 */ + border-width: 0; /* 2 */ + border-style: solid; /* 2 */ + border-color: theme("borderColor.DEFAULT", currentColor); /* 2 */ +} + +::before, +::after { + --tw-content: ""; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +*/ + +html { + line-height: 1.5; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ + -moz-tab-size: 4; /* 3 */ + tab-size: 4; /* 3 */ + font-family: theme( + "fontFamily.sans", + ui-sans-serif, + system-ui, + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + Roboto, + "Helvetica Neue", + Arial, + "Noto Sans", + sans-serif, + "Apple Color Emoji", + "Segoe UI Emoji", + "Segoe UI Symbol", + "Noto Color Emoji" + ); /* 4 */ + font-feature-settings: theme( + "fontFamily.sans[1].fontFeatureSettings", + normal + ); /* 5 */ + font-variation-settings: theme( + "fontFamily.sans[1].fontVariationSettings", + normal + ); /* 6 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; /* 1 */ + line-height: inherit; /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; /* 1 */ + color: inherit; /* 2 */ + border-top-width: 1px; /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + /* font-size: inherit; + font-weight: inherit; */ +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font family by default. +2. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: theme( + "fontFamily.mono", + ui-monospace, + SFMono-Regular, + Menlo, + Monaco, + Consolas, + "Liberation Mono", + "Courier New", + monospace + ); /* 1 */ + font-size: 1em; /* 2 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; /* 1 */ + border-color: inherit; /* 2 */ + border-collapse: collapse; /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 1 */ + font-weight: inherit; /* 1 */ + line-height: inherit; /* 1 */ + color: inherit; /* 1 */ + margin: 0; /* 2 */ + padding: 0; /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; /* 1 */ + /* background-color: transparent; 2 */ + /* background-image: none; 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::placeholder, +textarea::placeholder { + opacity: 1; /* 1 */ + color: theme("colors.gray.400", #9ca3af); /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; /* 1 */ + vertical-align: middle; /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ +[hidden] { + display: none; +} diff --git a/packages/components/tailwind.config.ts b/packages/components/tailwind.config.ts index c3a89f86..26b22478 100644 --- a/packages/components/tailwind.config.ts +++ b/packages/components/tailwind.config.ts @@ -1,6 +1,9 @@ import { mergeConfig } from "./src/tailwind/mergeConfig"; const config = mergeConfig({ + corePlugins: { + preflight: false, + }, content: ["./src/**/*.tsx", "./src/**/*.css"], theme: { extend: { From c8db2a07172f48d31af305f94461010aec83ebc7 Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Tue, 5 Mar 2024 15:52:22 -0800 Subject: [PATCH 02/10] components: Fixes --- packages/components/.storybook/preview.tsx | 2 +- packages/components/rollup.config.js | 9 --------- packages/components/src/Markdown/index.tsx | 2 -- packages/components/src/__stories__/Markdown.stories.tsx | 7 ------- packages/components/src/{main.css => styles/global.css} | 0 packages/components/src/{ => styles}/preflight.css | 0 6 files changed, 1 insertion(+), 19 deletions(-) rename packages/components/src/{main.css => styles/global.css} (100%) rename packages/components/src/{ => styles}/preflight.css (100%) diff --git a/packages/components/.storybook/preview.tsx b/packages/components/.storybook/preview.tsx index baf21e15..973c10b3 100644 --- a/packages/components/.storybook/preview.tsx +++ b/packages/components/.storybook/preview.tsx @@ -1,6 +1,6 @@ import { Preview } from "@storybook/react"; import React from "react"; -import "../src/main.css"; +import "../src/styles/global.css"; import ThemeProvider from "../src/tailwind/context"; const preview: Preview = { diff --git a/packages/components/rollup.config.js b/packages/components/rollup.config.js index 244a8095..3e081870 100644 --- a/packages/components/rollup.config.js +++ b/packages/components/rollup.config.js @@ -41,15 +41,6 @@ export default [ insertAt: "top", }, }), - postcss({ - config: { - path: "./postcss.config.js", - }, - include: "./main.css", - extract: "main.css", - minimize: true, - sourceMap: true, - }), terser(), ], }, diff --git a/packages/components/src/Markdown/index.tsx b/packages/components/src/Markdown/index.tsx index fa8ced74..68a63e9d 100644 --- a/packages/components/src/Markdown/index.tsx +++ b/packages/components/src/Markdown/index.tsx @@ -11,7 +11,6 @@ type Props = { ["data-cy"]?: string; forModal?: boolean; baseTextSize?: boolean; - isDoc?: boolean; }; export default function Markdown({ @@ -26,7 +25,6 @@ export default function Markdown({ "markdown-body", css.preview, { - "markdown-doc": props.isDoc, [css.forModal]: forModal, [css.baseText]: baseTextSize, }, diff --git a/packages/components/src/__stories__/Markdown.stories.tsx b/packages/components/src/__stories__/Markdown.stories.tsx index 748ddddc..6b46f384 100644 --- a/packages/components/src/__stories__/Markdown.stories.tsx +++ b/packages/components/src/__stories__/Markdown.stories.tsx @@ -45,10 +45,3 @@ export const BaseText: Story = { baseTextSize: true, }, }; - -export const ForDoc: Story = { - args: { - value: markdown, - isDoc: true, - }, -}; diff --git a/packages/components/src/main.css b/packages/components/src/styles/global.css similarity index 100% rename from packages/components/src/main.css rename to packages/components/src/styles/global.css diff --git a/packages/components/src/preflight.css b/packages/components/src/styles/preflight.css similarity index 100% rename from packages/components/src/preflight.css rename to packages/components/src/styles/preflight.css From 8d6bd3ec228b5dd88e13fe50b4e348314b66a910 Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Tue, 5 Mar 2024 16:29:04 -0800 Subject: [PATCH 03/10] components: Add ButtonWithPopup --- .../src/ButtonWithPopup/index.module.css | 19 ++ .../components/src/ButtonWithPopup/index.tsx | 72 ++++++ .../src/__stories__/Button.stories.tsx | 3 + .../__stories__/ButtonWithPopup.stories.tsx | 29 +++ .../src/__stories__/Popup.stories.tsx | 13 +- .../src/__tests__/ButtonWithPopup.test.tsx | 229 ++++++++++++++++++ packages/components/src/index.ts | 1 + 7 files changed, 364 insertions(+), 2 deletions(-) create mode 100644 packages/components/src/ButtonWithPopup/index.module.css create mode 100644 packages/components/src/ButtonWithPopup/index.tsx create mode 100644 packages/components/src/__stories__/ButtonWithPopup.stories.tsx create mode 100644 packages/components/src/__tests__/ButtonWithPopup.test.tsx diff --git a/packages/components/src/ButtonWithPopup/index.module.css b/packages/components/src/ButtonWithPopup/index.module.css new file mode 100644 index 00000000..3102e1d7 --- /dev/null +++ b/packages/components/src/ButtonWithPopup/index.module.css @@ -0,0 +1,19 @@ +.triggerButton { + @apply rounded px-3 py-1 text-sm font-semibold border border-primary whitespace-nowrap bg-transparent flex items-center justify-center; + + &:focus { + @apply outline-none widget-shadow-lightblue; + } +} + +.withoutText { + @apply border-0 p-0 rounded-none inline-block; +} + +.caret { + @apply pl-2 text-lg; +} + +.caretWithoutText { + @apply pl-0 inline-block; +} diff --git a/packages/components/src/ButtonWithPopup/index.tsx b/packages/components/src/ButtonWithPopup/index.tsx new file mode 100644 index 00000000..43ba786f --- /dev/null +++ b/packages/components/src/ButtonWithPopup/index.tsx @@ -0,0 +1,72 @@ +import { FaCaretDown } from "@react-icons/all-files/fa/FaCaretDown"; +import { FaCaretUp } from "@react-icons/all-files/fa/FaCaretUp"; +import cx from "classnames"; +import React, { ReactNode } from "react"; +import Popup, { PopupProps } from "../Popup"; +import css from "./index.module.css"; + +type Props = Partial & { + children: ReactNode; + isOpen?: boolean; + setIsOpen?: (o: boolean) => void; + triggerText?: string; + buttonClassName?: string; + ["data-cy"]?: string; +}; + +export default function ButtonWithPopup({ + children, + isOpen, + setIsOpen, + triggerText, + ...props +}: Props) { + const openProps: Partial = + isOpen !== undefined && setIsOpen !== undefined + ? { + open: isOpen, + onOpen: () => setIsOpen(true), + onClose: () => setIsOpen(false), + } + : {}; + return ( + ( + + )} + // props must come last to override default props above + {...props} + > + {children} + + ); +} diff --git a/packages/components/src/__stories__/Button.stories.tsx b/packages/components/src/__stories__/Button.stories.tsx index 942603db..b312cab4 100644 --- a/packages/components/src/__stories__/Button.stories.tsx +++ b/packages/components/src/__stories__/Button.stories.tsx @@ -47,6 +47,9 @@ export const White: Story = { white: true, pill: true, }, + parameters: { + backgrounds: { default: "dark" }, + }, }; export const Gradient: Story = { diff --git a/packages/components/src/__stories__/ButtonWithPopup.stories.tsx b/packages/components/src/__stories__/ButtonWithPopup.stories.tsx new file mode 100644 index 00000000..ab709063 --- /dev/null +++ b/packages/components/src/__stories__/ButtonWithPopup.stories.tsx @@ -0,0 +1,29 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import React from "react"; +import ButtonWithPopup from "../ButtonWithPopup"; + +const meta: Meta = { + title: "ButtonWithPopup", + component: ButtonWithPopup, + tags: ["autodocs"], + parameters: { + layout: "centered", + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Basic: Story = { + args: { + children:
Items
, + triggerText: "Action", + }, +}; + +export const NoText: Story = { + args: { + children:
Items
, + }, +}; diff --git a/packages/components/src/__stories__/Popup.stories.tsx b/packages/components/src/__stories__/Popup.stories.tsx index c4c64be2..1a3eadbc 100644 --- a/packages/components/src/__stories__/Popup.stories.tsx +++ b/packages/components/src/__stories__/Popup.stories.tsx @@ -1,5 +1,6 @@ import type { Meta, StoryObj } from "@storybook/react"; import React from "react"; +import Button from "../Button"; import Popup from "../Popup"; const meta: Meta = { @@ -18,14 +19,22 @@ type Story = StoryObj; export const Basic: Story = { args: { children: Show me on click, - trigger: , + trigger: ( +
+ Click me +
+ ), }, }; export const Hover: Story = { args: { children: Show me on hover, - trigger: , + trigger: ( +
+ Hover over me +
+ ), on: "hover", }, }; diff --git a/packages/components/src/__tests__/ButtonWithPopup.test.tsx b/packages/components/src/__tests__/ButtonWithPopup.test.tsx new file mode 100644 index 00000000..5f6001ac --- /dev/null +++ b/packages/components/src/__tests__/ButtonWithPopup.test.tsx @@ -0,0 +1,229 @@ +import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import React from "react"; +import ButtonWithPopup from "../ButtonWithPopup"; +import { PopupProps } from "../Popup"; + +// Taken from https://github.com/yjose/reactjs-popup/blob/master/__test__/index.test.tsx + +const SimplePopup = ({ ...props }: Partial) => ( + + popup Content + +); +const popupContentShouldntExist = () => { + expect(screen.queryByText(/popup Content/)).toBeNull(); +}; +const popupContentShouldExist = () => { + expect(screen.getByText(/popup Content/)).toBeInTheDocument(); +}; + +describe("Popup Component Render ", () => { + test("should render trigger correctly", () => { + render(); + expect(screen.getByText(/trigger/)).toBeInTheDocument(); + }); + + test("should be a tooltip by default", () => { + render(); + fireEvent.click(screen.getByText("trigger")); + expect(screen.getByRole("tooltip")).toBeInTheDocument(); + }); + + test("no Arrow for modal", () => { + render(); + fireEvent.click(screen.getByText("trigger")); + expect(screen.getByRole("dialog")).toBeInTheDocument(); + expect(screen.queryByTestId("arrow")).toBeNull(); + }); + + test("no Arrow on arrow= false", () => { + render(); + fireEvent.click(screen.getByText("trigger")); + expect(screen.getByRole("tooltip")).toBeInTheDocument(); + expect(screen.queryByTestId("arrow")).toBeNull(); + }); + + test("should render a Modal on modal=true", () => { + render(); + fireEvent.click(screen.getByText("trigger")); + expect(screen.getByRole("dialog")).toBeInTheDocument(); + }); + + test("it should be closed on disabled = true ", () => { + render(); + popupContentShouldntExist(); + fireEvent.click(screen.getByText("trigger")); + popupContentShouldntExist(); + fireEvent.click(screen.getByText("trigger")); + popupContentShouldntExist(); + }); + test("should be open by default on defaultOpen= true ", () => { + render(); + popupContentShouldExist(); + }); + + test("should call onOpen & onClose functions ", async () => { + const onOpen = jest.fn(); + const onClose = jest.fn(); + render(); + + fireEvent.click(screen.getByText("trigger")); + await waitFor(() => { + expect(onOpen).toHaveBeenCalled(); + const [event] = onOpen.mock.calls[0]; + expect("target" in event).toBe(true); + }); + fireEvent.click(screen.getByText("trigger")); + await waitFor(() => { + expect(onClose).toHaveBeenCalled(); + const [event] = onClose.mock.calls[0]; + expect("target" in event).toBe(true); + }); + // expect(screen.getByRole('tooltip')).toBeInTheDocument(); + }); + + test("should be closed on Escape", async () => { + render(); + fireEvent.click(screen.getByText("trigger")); + popupContentShouldExist(); + fireEvent.keyUp(document, { key: "Escape", code: "Escape" }); + popupContentShouldntExist(); + }); + test("shouldnt close on Escape if closeOnEscape=false", async () => { + render(); + fireEvent.click(screen.getByText("trigger")); + popupContentShouldExist(); + fireEvent.keyUp(document, { key: "Escape", code: "Escape" }); + popupContentShouldExist(); + }); + + test("should be closed on ClickOutside ", async () => { + render(); + fireEvent.click(screen.getByText("trigger")); + popupContentShouldExist(); + fireEvent.mouseDown(document); + popupContentShouldntExist(); + }); + test("shouldnt close on ClickOutside if closeOnDocumentClick=false", async () => { + render(); + fireEvent.click(screen.getByText("trigger")); + popupContentShouldExist(); + fireEvent.mouseDown(document); + popupContentShouldExist(); + }); + + test("should lock Document Scroll on lockScroll=true", async () => { + render(); + fireEvent.click(screen.getByText("trigger")); + popupContentShouldExist(); + expect(document.body).toHaveStyle(`overflow: hidden`); + fireEvent.click(screen.getByText("trigger")); + popupContentShouldntExist(); + expect(document.body).toHaveStyle(`overflow: auto`); + }); +}); + +// test for "on" props status +describe('Popup Component with "on" Prop ', () => { + test("it should be opened only on Click as default value ", () => { + render(); + popupContentShouldntExist(); + fireEvent.click(screen.getByText("trigger")); + popupContentShouldExist(); + fireEvent.click(screen.getByText("trigger")); + popupContentShouldntExist(); + }); + + test('it should be opened only on Click where on="click" ', () => { + render(); + popupContentShouldntExist(); + fireEvent.click(screen.getByText("trigger")); + popupContentShouldExist(); + fireEvent.click(screen.getByText("trigger")); + popupContentShouldntExist(); + }); + test('it should be opened only on Right-Click where on="right-click" ', () => { + render(); + popupContentShouldntExist(); + fireEvent.contextMenu(screen.getByText("trigger")); + popupContentShouldExist(); + fireEvent.contextMenu(screen.getByText("trigger")); + popupContentShouldntExist(); + }); + test('it should be opened only on Hover where on="hover" ', async () => { + render(); + popupContentShouldntExist(); + fireEvent.mouseOver(screen.getByText("trigger")); + await waitFor( + () => popupContentShouldExist(), + { timeout: 120 }, // default delay = "100" + ); + fireEvent.mouseLeave(screen.getByText("trigger")); + + await waitFor( + () => expect(screen.queryByText(/popup Content/)).toBeNull(), + { timeout: 120 }, + ); + // should not show on click + fireEvent.click(screen.getByText("trigger")); + popupContentShouldntExist(); + }); + test('it should be opened only on Focus where on="focus" ', async () => { + render(); + popupContentShouldntExist(); + fireEvent.focus(screen.getByText("trigger")); + await waitFor( + () => popupContentShouldExist(), + { timeout: 120 }, // default delay = "100" + ); + fireEvent.blur(screen.getByText("trigger")); + await waitFor(() => popupContentShouldntExist(), { timeout: 120 }); + // should not show content on click + fireEvent.click(screen.getByText("trigger")); + popupContentShouldntExist(); + }); + test('it should be opened on Focus & click & focus where on=["focus","click","hover"] ', async () => { + render(); + popupContentShouldntExist(); + // on focus + fireEvent.focus(screen.getByText("trigger")); + await waitFor( + () => popupContentShouldExist(), + { timeout: 120 }, // default delay = "100" + ); + fireEvent.blur(screen.getByText("trigger")); + await waitFor(() => popupContentShouldntExist(), { timeout: 120 }); + // on click + fireEvent.click(screen.getByText("trigger")); + popupContentShouldExist(); + fireEvent.click(screen.getByText("trigger")); + popupContentShouldntExist(); + + // on Hover + fireEvent.mouseOver(screen.getByText("trigger")); + await waitFor( + () => popupContentShouldExist(), + { timeout: 120 }, // default delay = "100" + ); + fireEvent.mouseLeave(screen.getByText("trigger")); + + await waitFor( + () => expect(screen.queryByText(/popup Content/)).toBeNull(), + { timeout: 120 }, + ); + }); + + test("should respect mouseEnterDelay mouseLeaveDelay on Hover ", async () => { + render( + , + ); + popupContentShouldntExist(); + fireEvent.mouseOver(screen.getByText("trigger")); + await waitFor(() => popupContentShouldntExist(), { timeout: 120 }); + await waitFor(() => popupContentShouldExist(), { timeout: 1000 }); + fireEvent.mouseLeave(screen.getByText("trigger")); + + await waitFor(() => popupContentShouldExist(), { timeout: 120 }); + await waitFor(() => popupContentShouldntExist(), { timeout: 1000 }); + }); +}); diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index 5b86ab5a..0e8e39a4 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -1,5 +1,6 @@ export { default as Btn } from "./Btn"; export { default as Button } from "./Button"; +export { default as ButtonWithPopup } from "./ButtonWithPopup"; export { default as CharCount } from "./CharCount"; export { default as Checkbox } from "./Checkbox"; export { default as ExternalLink } from "./ExternalLink"; From 052c40ffeca5f054b940b689a8c3be6eeb0dff6a Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Wed, 6 Mar 2024 12:28:08 -0800 Subject: [PATCH 04/10] components: Add CellDropdown --- packages/components/package.json | 3 +- .../src/CellDropdown/index.module.css | 39 +++++ .../components/src/CellDropdown/index.tsx | 61 +++++++ .../src/__stories__/CellDropdown.stories.tsx | 154 ++++++++++++++++++ .../src/__tests__/CellDropdown.test.tsx | 83 ++++++++++ packages/components/tsconfig.json | 6 +- packages/contexts/package.json | 2 +- yarn.lock | 5 +- 8 files changed, 348 insertions(+), 5 deletions(-) create mode 100644 packages/components/src/CellDropdown/index.module.css create mode 100644 packages/components/src/CellDropdown/index.tsx create mode 100644 packages/components/src/__stories__/CellDropdown.stories.tsx create mode 100644 packages/components/src/__tests__/CellDropdown.test.tsx diff --git a/packages/components/package.json b/packages/components/package.json index 3d0645d9..fd0eba9b 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -30,13 +30,14 @@ "yalc:push": "yarn dbuild && yalc push", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", - "clean": "rm -rf dist && rm -rf .rollup.cache && rm -rf types && rm -rf tsconfig.tsbuildinfo" + "clean": "rm -rf dist && rm -rf .rollup.cache && rm -rf types && rm -rf tsconfig.tsbuildinfo && rm -rf .eslintcache" }, "peerDependencies": { "react": "^18", "react-dom": "^18" }, "dependencies": { + "@dolthub/react-hooks": "^0.1.7", "@dolthub/web-utils": "^0.1.3", "@react-icons/all-files": "^4.1.0", "classnames": "^2.5.1", diff --git a/packages/components/src/CellDropdown/index.module.css b/packages/components/src/CellDropdown/index.module.css new file mode 100644 index 00000000..709262a5 --- /dev/null +++ b/packages/components/src/CellDropdown/index.module.css @@ -0,0 +1,39 @@ +.cellDropdown { + @apply hidden; + + @screen md { + @apply block; + } +} + +.cellDropdownForRow { + @apply relative; +} + +.button { + @apply bg-white rounded-full; +} + +.rowButton { + @apply bg-ld-lightgrey; +} + +.icon { + @apply p-1 w-5 h-5 text-ld-darkgrey; + + &:hover { + @apply text-primary; + } +} + +.dropdown { + @apply absolute bg-white widget-shadow-hover z-100 rounded-b top-[1.95rem] px-3 py-1 border-b border-x border-ld-lightgrey max-w-[9rem] right-0 flex flex-col; +} + +.padding { + @apply -right-[0.85rem]; +} + +.rowDropdown { + @apply -right-20; +} diff --git a/packages/components/src/CellDropdown/index.tsx b/packages/components/src/CellDropdown/index.tsx new file mode 100644 index 00000000..636a7a63 --- /dev/null +++ b/packages/components/src/CellDropdown/index.tsx @@ -0,0 +1,61 @@ +import { useOnClickOutside } from "@dolthub/react-hooks"; +import { RiMenu5Line } from "@react-icons/all-files/ri/RiMenu5Line"; +import cx from "classnames"; +import React, { ReactNode, useRef } from "react"; +import Btn from "../Btn"; +import css from "./index.module.css"; + +type Props = { + children: ReactNode; + showDropdown: boolean; + setShowDropdown: (s: boolean) => void; + buttonClassName: string; + padding?: boolean; + forRow?: boolean; + ["data-cy"]?: string; +}; + +export default function CellDropdown({ + setShowDropdown, + forRow = false, + padding = false, + ...props +}: Props) { + const toggle = () => setShowDropdown(!props.showDropdown); + const dropdownRef = useRef(null); + useOnClickOutside(dropdownRef, () => setShowDropdown(false)); + + return ( +
+ + + + {props.showDropdown && ( +
+ {props.children} +
+ )} +
+ ); +} diff --git a/packages/components/src/__stories__/CellDropdown.stories.tsx b/packages/components/src/__stories__/CellDropdown.stories.tsx new file mode 100644 index 00000000..e7cb7700 --- /dev/null +++ b/packages/components/src/__stories__/CellDropdown.stories.tsx @@ -0,0 +1,154 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import cx from "classnames"; +import React from "react"; +import CellDropdown from "../CellDropdown"; + +const meta: Meta = { + title: "CellDropdown", + component: CellDropdown, + tags: ["autodocs"], +}; + +export default meta; + +type Story = StoryObj; + +const cellClassName = + "align-top relative min-w-[130px] pr-10 pl-2 border-b border-ld-lightgrey text-primary font-mono leading-8 text-sm"; +const buttonClassName = "absolute bg-white right-2 top-[0.35rem]"; + +const Cell = (props: { children: React.ReactNode; className?: string }) => ( + {props.children} +); + +const HeadCell = (props: { children: React.ReactNode; className?: string }) => ( + {props.children} +); + +const CellWrapper = ({ children }: { children: React.ReactNode }) => ( + + + + Header 1 + Header 2 + + + + + + Cell 1 + {children} + + Cell 2 + + + Cell 1 + Cell 2 + + +
+); + +const HeadCellWrapper = ({ children }: { children: React.ReactNode }) => ( + + + + Header 1{children} + Header 2 + + + + + Cell 1 + Cell 2 + + + Cell 1 + Cell 2 + + +
+); + +const RowWrapper = ({ children }: { children: React.ReactNode }) => ( + + + + + + + + + Cell 1 + Cell 2 + + + + +
+ Header 1 + Header 2 +
{children}
+ Cell 1 + Cell 2 +
+); + +const children = ( +
    +
  • First item
  • +
  • Second item
  • +
+); + +export const NoShow: Story = { + args: { + children, + showDropdown: false, + setShowDropdown: () => {}, + buttonClassName, + }, + decorators: [story => {story()}], +}; + +export const ShowCell: Story = { + args: { + children, + showDropdown: true, + setShowDropdown: () => {}, + buttonClassName, + }, + decorators: [story => {story()}], +}; + +export const ShowHeadCell: Story = { + args: { + children, + showDropdown: true, + setShowDropdown: () => {}, + buttonClassName, + }, + decorators: [story => {story()}], +}; + +export const ShowPadding: Story = { + args: { + children, + showDropdown: true, + setShowDropdown: () => {}, + padding: true, + buttonClassName, + }, + decorators: [story => {story()}], +}; + +export const ShowRow: Story = { + args: { + children, + showDropdown: true, + setShowDropdown: () => {}, + forRow: true, + buttonClassName: "text-ld-darkergrey mx-2 text-xl flex", + }, + decorators: [story => {story()}], +}; diff --git a/packages/components/src/__tests__/CellDropdown.test.tsx b/packages/components/src/__tests__/CellDropdown.test.tsx new file mode 100644 index 00000000..7fb7f77b --- /dev/null +++ b/packages/components/src/__tests__/CellDropdown.test.tsx @@ -0,0 +1,83 @@ +import { render, screen } from "@testing-library/react"; +import React from "react"; +import CellDropdown from "../CellDropdown"; + +describe("test CellDropdown", () => { + it("renders without crashing", () => { + render( + {}} + buttonClassName="" + > +
Dropdown Content
+
, + ); + expect(screen.getByRole("button")).toBeInTheDocument(); + }); + + // it("toggles dropdown visibility on button click", async () => { + // const setShowDropdown = jest.fn(); + // render( + // + //
Dropdown Content
+ //
, + // ); + + // const button = screen.getByRole("button"); + // await userEvent.click(button); + // expect(setShowDropdown).toHaveBeenCalledWith(true); + // }); + + // it("closes dropdown when clicking outside", async () => { + // const setShowDropdown = jest.fn(); + // render( + //
+ // + //
Dropdown Content
+ //
+ //
Outside Area
+ //
, + // ); + + // const outsideArea = screen.getByTestId("outside"); + // await userEvent.click(outsideArea); + // expect(setShowDropdown).toHaveBeenCalledWith(false); + // }); + + // it("applies correct class based on `forRow` prop", () => { + // const { rerender } = render( + // {}} + // buttonClassName="" + // forRow={false} + // > + //
Dropdown Content
+ //
, + // ); + + // expect(screen.getByRole("button")).not.toHaveClass("rowButton"); // Adapt class names as necessary + + // rerender( + // {}} + // buttonClassName="" + // forRow={true} + // > + //
Dropdown Content
+ //
, + // ); + + // expect(screen.getByRole("button")).toHaveClass("rowButton"); // Adapt class names as necessary + // }); +}); diff --git a/packages/components/tsconfig.json b/packages/components/tsconfig.json index 12ba1c2b..5c1b4f6c 100644 --- a/packages/components/tsconfig.json +++ b/packages/components/tsconfig.json @@ -20,5 +20,9 @@ "composite": true, "noImplicitAny": true, "types": ["node", "jsdom", "@testing-library/jest-dom"] - } + }, + "references": [ + { "path": "../hooks"}, + { "path": "../utils" } + ] } \ No newline at end of file diff --git a/packages/contexts/package.json b/packages/contexts/package.json index 73a77e2e..5e92b0df 100644 --- a/packages/contexts/package.json +++ b/packages/contexts/package.json @@ -34,7 +34,7 @@ "react-dom": "^18.2.0" }, "dependencies": { - "@dolthub/react-hooks": "^0.1.6" + "@dolthub/react-hooks": "^0.1.7" }, "devDependencies": { "@babel/core": "^7.24.0", diff --git a/yarn.lock b/yarn.lock index 7ba578f1..42cd9582 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2231,6 +2231,7 @@ __metadata: "@babel/preset-env": "npm:^7.24.0" "@babel/preset-react": "npm:^7.23.3" "@babel/preset-typescript": "npm:^7.23.3" + "@dolthub/react-hooks": "npm:^0.1.7" "@dolthub/web-utils": "npm:^0.1.3" "@react-icons/all-files": "npm:^4.1.0" "@rollup/plugin-commonjs": "npm:^25.0.7" @@ -2307,7 +2308,7 @@ __metadata: "@babel/preset-env": "npm:^7.24.0" "@babel/preset-react": "npm:^7.23.3" "@babel/preset-typescript": "npm:^7.23.3" - "@dolthub/react-hooks": "npm:^0.1.6" + "@dolthub/react-hooks": "npm:^0.1.7" "@rollup/plugin-commonjs": "npm:^25.0.7" "@rollup/plugin-node-resolve": "npm:^15.2.3" "@rollup/plugin-typescript": "npm:^11.1.5" @@ -2343,7 +2344,7 @@ __metadata: languageName: unknown linkType: soft -"@dolthub/react-hooks@npm:^0.1.6, @dolthub/react-hooks@workspace:packages/hooks": +"@dolthub/react-hooks@npm:^0.1.7, @dolthub/react-hooks@workspace:packages/hooks": version: 0.0.0-use.local resolution: "@dolthub/react-hooks@workspace:packages/hooks" dependencies: From 47d91cdf3601b1ce0d96a4a4d05c6da6a1b0dfde Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Wed, 6 Mar 2024 12:40:52 -0800 Subject: [PATCH 05/10] components: Fixes --- package.json | 2 +- .../src/__tests__/CellDropdown.test.tsx | 115 +++++++++--------- 2 files changed, 59 insertions(+), 58 deletions(-) diff --git a/package.json b/package.json index 0a6346bb..7e0c02a5 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "packages/*" ], "scripts": { - "ci": "yarn prettier && yarn lint && yarn sbuild && yarn dbuild && yarn test", + "ci": "yarn prettier && yarn lint && yarn dbuild && yarn sbuild && yarn test", "clean": "rimraf node_modules -g 'packages/*/.eslintcache' 'packages/*/*.tsbuildinfo' 'packages/*/dist' 'packages/*/.rollup.cache' 'packages/*/types' 'packages/*/coverage'", "dbuild": "npm-run-all 'dbuild:*'", "dbuild:utils": "yarn workspace @dolthub/web-utils dbuild", diff --git a/packages/components/src/__tests__/CellDropdown.test.tsx b/packages/components/src/__tests__/CellDropdown.test.tsx index 7fb7f77b..8bb687f5 100644 --- a/packages/components/src/__tests__/CellDropdown.test.tsx +++ b/packages/components/src/__tests__/CellDropdown.test.tsx @@ -1,6 +1,7 @@ import { render, screen } from "@testing-library/react"; import React from "react"; import CellDropdown from "../CellDropdown"; +import { setup } from "./testUtils.test"; describe("test CellDropdown", () => { it("renders without crashing", () => { @@ -16,68 +17,68 @@ describe("test CellDropdown", () => { expect(screen.getByRole("button")).toBeInTheDocument(); }); - // it("toggles dropdown visibility on button click", async () => { - // const setShowDropdown = jest.fn(); - // render( - // - //
Dropdown Content
- //
, - // ); + it("toggles dropdown visibility on button click", async () => { + const setShowDropdown = jest.fn(); + const { user } = setup( + +
Dropdown Content
+
, + ); - // const button = screen.getByRole("button"); - // await userEvent.click(button); - // expect(setShowDropdown).toHaveBeenCalledWith(true); - // }); + const button = screen.getByRole("button"); + await user.click(button); + expect(setShowDropdown).toHaveBeenCalledWith(true); + }); - // it("closes dropdown when clicking outside", async () => { - // const setShowDropdown = jest.fn(); - // render( - //
- // - //
Dropdown Content
- //
- //
Outside Area
- //
, - // ); + it("closes dropdown when clicking outside", async () => { + const setShowDropdown = jest.fn(); + const { user } = setup( +
+ +
Dropdown Content
+
+
Outside Area
+
, + ); - // const outsideArea = screen.getByTestId("outside"); - // await userEvent.click(outsideArea); - // expect(setShowDropdown).toHaveBeenCalledWith(false); - // }); + const outsideArea = screen.getByTestId("outside"); + await user.click(outsideArea); + expect(setShowDropdown).toHaveBeenCalledWith(false); + }); - // it("applies correct class based on `forRow` prop", () => { - // const { rerender } = render( - // {}} - // buttonClassName="" - // forRow={false} - // > - //
Dropdown Content
- //
, - // ); + it("applies correct class based on `forRow` prop", () => { + const { rerender } = render( + {}} + buttonClassName="" + forRow={false} + > +
Dropdown Content
+
, + ); - // expect(screen.getByRole("button")).not.toHaveClass("rowButton"); // Adapt class names as necessary + expect(screen.getByRole("button")).not.toHaveClass(/rowButton/); - // rerender( - // {}} - // buttonClassName="" - // forRow={true} - // > - //
Dropdown Content
- //
, - // ); + rerender( + {}} + buttonClassName="" + forRow + > +
Dropdown Content
+
, + ); - // expect(screen.getByRole("button")).toHaveClass("rowButton"); // Adapt class names as necessary - // }); + expect(screen.getByRole("button")).toHaveClass(/rowButton/); + }); }); From 94f0c942063497fa0625a6c5771cb6da003fc33f Mon Sep 17 00:00:00 2001 From: Taylor Bantle Date: Wed, 6 Mar 2024 13:12:09 -0800 Subject: [PATCH 06/10] components: Improve test coverage --- .../components/src/ButtonWithPopup/index.tsx | 2 +- .../components/src/__tests__/Button.test.tsx | 9 ++++++ .../src/__tests__/ButtonWithPopup.test.tsx | 21 +++++++++++--- .../src/__tests__/Checkbox.test.tsx | 7 ++++- .../src/__tests__/FormInput.test.tsx | 29 +++++++++++++++++-- .../components/src/__tests__/Radio.test.tsx | 9 +++++- .../src/__tests__/Textarea.test.tsx | 26 +++++++++++++++-- 7 files changed, 91 insertions(+), 12 deletions(-) diff --git a/packages/components/src/ButtonWithPopup/index.tsx b/packages/components/src/ButtonWithPopup/index.tsx index 43ba786f..c17eee65 100644 --- a/packages/components/src/ButtonWithPopup/index.tsx +++ b/packages/components/src/ButtonWithPopup/index.tsx @@ -5,7 +5,7 @@ import React, { ReactNode } from "react"; import Popup, { PopupProps } from "../Popup"; import css from "./index.module.css"; -type Props = Partial & { +export type Props = Partial & { children: ReactNode; isOpen?: boolean; setIsOpen?: (o: boolean) => void; diff --git a/packages/components/src/__tests__/Button.test.tsx b/packages/components/src/__tests__/Button.test.tsx index b19b537c..f3538677 100644 --- a/packages/components/src/__tests__/Button.test.tsx +++ b/packages/components/src/__tests__/Button.test.tsx @@ -116,6 +116,15 @@ describe("test Button", () => { expect(button).toHaveClass(/underlined/); }); + it("renders an outlined button", () => { + render(Button Name); + + const button = screen.getByText("Button Name"); + expect(button).toHaveTextContent("Button Name"); + expect(button).toHaveAttribute("type", "button"); + expect(button).toHaveClass(/outlined/); + }); + it("renders a Button Group", () => { render( diff --git a/packages/components/src/__tests__/ButtonWithPopup.test.tsx b/packages/components/src/__tests__/ButtonWithPopup.test.tsx index 5f6001ac..18bba0ac 100644 --- a/packages/components/src/__tests__/ButtonWithPopup.test.tsx +++ b/packages/components/src/__tests__/ButtonWithPopup.test.tsx @@ -1,11 +1,10 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import React from "react"; -import ButtonWithPopup from "../ButtonWithPopup"; -import { PopupProps } from "../Popup"; +import ButtonWithPopup, { Props } from "../ButtonWithPopup"; // Taken from https://github.com/yjose/reactjs-popup/blob/master/__test__/index.test.tsx -const SimplePopup = ({ ...props }: Partial) => ( +const SimplePopup = ({ ...props }: Partial) => ( popup Content @@ -17,7 +16,7 @@ const popupContentShouldExist = () => { expect(screen.getByText(/popup Content/)).toBeInTheDocument(); }; -describe("Popup Component Render ", () => { +describe("test ButtonWithPopup ", () => { test("should render trigger correctly", () => { render(); expect(screen.getByText(/trigger/)).toBeInTheDocument(); @@ -29,6 +28,20 @@ describe("Popup Component Render ", () => { expect(screen.getByRole("tooltip")).toBeInTheDocument(); }); + test("should use open props to open", async () => { + const setIsOpen = jest.fn(); + render(); + fireEvent.click(screen.getByText("trigger")); + await waitFor(() => expect(setIsOpen).toHaveBeenCalledWith(true)); + }); + + test("should use open props to close", async () => { + const setIsOpen = jest.fn(); + render(); + fireEvent.click(screen.getByText("trigger")); + await waitFor(() => expect(setIsOpen).toHaveBeenCalledWith(false)); + }); + test("no Arrow for modal", () => { render(); fireEvent.click(screen.getByText("trigger")); diff --git a/packages/components/src/__tests__/Checkbox.test.tsx b/packages/components/src/__tests__/Checkbox.test.tsx index c0e2e131..50a8bb2c 100644 --- a/packages/components/src/__tests__/Checkbox.test.tsx +++ b/packages/components/src/__tests__/Checkbox.test.tsx @@ -7,7 +7,7 @@ describe("test Checkbox", () => { const mocks = [ { name: "one", label: "one-label" }, { name: "two", label: "two-label" }, - { name: "three", label: "three-label" }, + { name: "three", label: "three-label", description: "description" }, ]; mocks.forEach((mock, ind) => { @@ -23,6 +23,7 @@ describe("test Checkbox", () => { checked={checked} className="classname" label={mock.label} + description={mock.description} />, ); @@ -36,6 +37,10 @@ describe("test Checkbox", () => { expect(input).not.toBeChecked(); } + if (mock.description) { + expect(screen.getByText(mock.description)).toBeVisible(); + } + await user.click(screen.getByLabelText(mock.label)); expect(onChangeValue).toHaveBeenCalled(); }); diff --git a/packages/components/src/__tests__/FormInput.test.tsx b/packages/components/src/__tests__/FormInput.test.tsx index 1c81817d..200be40d 100644 --- a/packages/components/src/__tests__/FormInput.test.tsx +++ b/packages/components/src/__tests__/FormInput.test.tsx @@ -29,20 +29,43 @@ describe("test FormInput", () => { expect(input).toHaveValue("new name"); }); - it("renders form input with value", () => { - render( + it("renders form input with onChangeString", async () => { + const onChangeString = jest.fn(); + const { user } = setup( , + ); + + const input = screen.getByPlaceholderText("Placeholder text"); + expect(input).toBeVisible(); + expect(input).toHaveAttribute("type", "text"); + + await user.type(input, "new name"); + expect(onChangeString).toHaveBeenCalledWith("new name"); + expect(input).toHaveValue("new name"); + }); + + it("renders form input with value and description", () => { + render( + , ); - const input = screen.getByPlaceholderText("Placeholder text"); + const input = screen.getByRole("textbox"); expect(input).toBeVisible(); expect(input).toHaveAttribute("type", "email"); expect(input).toHaveValue("email@email.com"); + expect(input).toHaveAttribute("placeholder", ""); + + expect(screen.getByText("description")).toBeVisible(); }); }); diff --git a/packages/components/src/__tests__/Radio.test.tsx b/packages/components/src/__tests__/Radio.test.tsx index b76c7f9f..e7116253 100644 --- a/packages/components/src/__tests__/Radio.test.tsx +++ b/packages/components/src/__tests__/Radio.test.tsx @@ -7,7 +7,7 @@ describe("test Radio", () => { const mocks = [ { name: "one", label: "one-label" }, { name: "two", label: "two-label" }, - { name: "three", label: "three-label" }, + { name: "three", label: "three-label", description: "description" }, ]; mocks.forEach((mock, ind) => { @@ -23,10 +23,17 @@ describe("test Radio", () => { checked={checked} className="classname" disabled={disabled} + description={mock.description} />, ); + const content = screen.getByLabelText(mock.label); expect(content).toBeVisible(); + + if (mock.description) { + expect(screen.getByText(mock.description)).toBeVisible(); + } + if (!disabled) { const input = screen.getByRole("radio"); if (checked) { diff --git a/packages/components/src/__tests__/Textarea.test.tsx b/packages/components/src/__tests__/Textarea.test.tsx index 5e0ee46c..fcb82537 100644 --- a/packages/components/src/__tests__/Textarea.test.tsx +++ b/packages/components/src/__tests__/Textarea.test.tsx @@ -29,20 +29,42 @@ describe("test Textarea", () => { expect(input).toHaveValue("new name"); }); + it("renders textarea with onChangeString", async () => { + const onChangeString = jest.fn(); + const { user } = setup( +