From c918e0572ef76e9a53683cdf85390817e29718a0 Mon Sep 17 00:00:00 2001 From: Arkadiusz Bachorski <60391032+arkadiuszbachorski@users.noreply.github.com> Date: Thu, 17 Oct 2024 20:26:55 +0200 Subject: [PATCH] Add tests and stories to Tabs component (#71) # Add tests and stories to Tabs component ## :gear: Release Notes * Add tests and stories to Tabs component ![image](https://github.com/user-attachments/assets/9e94c814-e8f5-472f-9f68-6072a7a173b5) Closes #23 ### Code of Conduct & Contributing Guidelines By submitting creating this pull request, you agree to follow our [Code of Conduct](https://github.com/StanfordBDHG/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/StanfordBDHG/.github/blob/main/CONTRIBUTING.md): - [x] I agree to follow the [Code of Conduct](https://github.com/StanfordBDHG/.github/blob/main/CODE_OF_CONDUCT.md) and [Contributing Guidelines](https://github.com/StanfordBDHG/.github/blob/main/CONTRIBUTING.md). --- package-lock.json | 50 +++++++-------- package.json | 8 +-- .../src/components/Tabs/Tabs.mocks.tsx | 46 +++++++++++++ .../src/components/Tabs/Tabs.stories.tsx | 60 +++++++++++++++++ .../src/components/Tabs/Tabs.test.tsx | 47 ++++++++++++++ .../src/components/Tabs/Tabs.tsx | 64 +++++++++++++------ routes/~_dashboard/~patients/~$id/~index.tsx | 18 ++---- 7 files changed, 233 insertions(+), 60 deletions(-) create mode 100644 packages/design-system/src/components/Tabs/Tabs.mocks.tsx create mode 100644 packages/design-system/src/components/Tabs/Tabs.stories.tsx create mode 100644 packages/design-system/src/components/Tabs/Tabs.test.tsx diff --git a/package-lock.json b/package-lock.json index b5567ce3..a02ba101 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,13 +26,13 @@ "@stanfordbdhg/engagehf-models": "^0.4.0", "@t3-oss/env-core": "^0.11.1", "@tanstack/match-sorter-utils": "^8.19.4", - "@tanstack/react-query": "^5.59.14", + "@tanstack/react-query": "^5.59.15", "@tanstack/react-table": "^8.20.5", "@vitejs/plugin-react": "^4.3.2", "class-variance-authority": "^0.7.0", "cmdk": "^1.0.0", "date-fns": "^3.6.0", - "es-toolkit": "^1.25.1", + "es-toolkit": "^1.25.2", "firebase": "^10.14.1", "jsdom": "^25.0.1", "lucide-react": "^0.453.0", @@ -59,8 +59,8 @@ "@storybook/react": "^8.3.5", "@storybook/react-vite": "^8.3.5", "@storybook/test": "^8.3.5", - "@tanstack/router-devtools": "^1.67.0", - "@tanstack/router-plugin": "^1.66.1", + "@tanstack/router-devtools": "^1.70.0", + "@tanstack/router-plugin": "^1.69.1", "@testing-library/jest-dom": "^6", "@testing-library/react": "^16", "@total-typescript/ts-reset": "^0.6.1", @@ -5229,9 +5229,9 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.59.14", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.14.tgz", - "integrity": "sha512-2cM4x3Ka4Thl7/wnjf++EMGA2Is/RgPynn83D4kfGiJOGSjb5T2D3EEOlC8Nt6U2htLS3imOXjOSMEjC3K7JNg==", + "version": "5.59.15", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.15.tgz", + "integrity": "sha512-QbVlAkTI78wB4Mqgf2RDmgC0AOiJqer2c5k9STOOSXGv1S6ZkY37r/6UpE8DbQ2Du0ohsdoXgFNEyv+4eDoPEw==", "dependencies": { "@tanstack/query-core": "5.59.13" }, @@ -5244,9 +5244,9 @@ } }, "node_modules/@tanstack/react-router": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.67.0.tgz", - "integrity": "sha512-8AmtDpJZpoC+TnHf9qS0FbRz7qnLkQ336Oi6fyhfbHH+BPvf27tlj+YxbVrt7sVrdvI8n3AZveUGx38/JUkLDQ==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.70.0.tgz", + "integrity": "sha512-PS5Y01n01B8LW6btdvphBhl7ChPo4p/sVi43Wcs7KjF7xJTSsVB0wo7l9yb1gfLIOpAhm4Gtiy+7YOd/oCXbWQ==", "dev": true, "peer": true, "dependencies": { @@ -5263,7 +5263,7 @@ "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "@tanstack/router-generator": "1.65.0", + "@tanstack/router-generator": "1.69.1", "react": ">=18", "react-dom": ">=18" }, @@ -5312,9 +5312,9 @@ } }, "node_modules/@tanstack/router-devtools": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@tanstack/router-devtools/-/router-devtools-1.67.0.tgz", - "integrity": "sha512-20mk4wGX7FAgJNnjsSb2RJH2Z5BMNrxdNX3CT41QMp83S/SXlnRaknxfbfWuoToQtQTHvyWRV0ZYpMIvG5Y6Zg==", + "version": "1.70.0", + "resolved": "https://registry.npmjs.org/@tanstack/router-devtools/-/router-devtools-1.70.0.tgz", + "integrity": "sha512-FZbntqx1Q3ccw9AYca2ahAZkkAj4f9akuw1B7bsgzH861KUwKLF6xMhIgHX9UUSShwDsVvEokd0KppMxG/eWLg==", "dev": true, "dependencies": { "clsx": "^2.1.1", @@ -5328,7 +5328,7 @@ "url": "https://github.com/sponsors/tannerlinsley" }, "peerDependencies": { - "@tanstack/react-router": "^1.67.0", + "@tanstack/react-router": "^1.70.0", "react": ">=18", "react-dom": ">=18" } @@ -5343,9 +5343,9 @@ } }, "node_modules/@tanstack/router-generator": { - "version": "1.65.0", - "resolved": "https://registry.npmjs.org/@tanstack/router-generator/-/router-generator-1.65.0.tgz", - "integrity": "sha512-N2H2cHBbUNLqx/ySd5g0R1GR8gGc4UagoBWmQKaTD5gHjFwWojJhigllZlQN4U1nxZLb4yJ3eJYuxRQZCoqaJw==", + "version": "1.69.1", + "resolved": "https://registry.npmjs.org/@tanstack/router-generator/-/router-generator-1.69.1.tgz", + "integrity": "sha512-llWfYf2oEgSC8QoO0uSov1SGC9/5r4oED7k07AAnfDMQ/jdXU4g0OdJlu2se7m5a17GgN2lEkaz/ZjmgM/tQrA==", "dev": true, "dependencies": { "@tanstack/virtual-file-routes": "^1.64.0", @@ -5362,9 +5362,9 @@ } }, "node_modules/@tanstack/router-plugin": { - "version": "1.66.1", - "resolved": "https://registry.npmjs.org/@tanstack/router-plugin/-/router-plugin-1.66.1.tgz", - "integrity": "sha512-0oeO4x1AKwL9lP9WMJ4ysVcNCUdXHffjFKBWYaEt1Cj2RkWgaWXtSzgg3dhFFemYgn/Zm1NOGQlA4f6/q/PPZw==", + "version": "1.69.1", + "resolved": "https://registry.npmjs.org/@tanstack/router-plugin/-/router-plugin-1.69.1.tgz", + "integrity": "sha512-GM7qUwtyUPpC1uP6bgIkK6IipKUfdahv7PUyhyK8JhCsgWiymf8X5LuMwgLIKa6G3qr9Hm0DZnbGaAlirFqYFQ==", "dev": true, "dependencies": { "@babel/core": "^7.25.8", @@ -5375,7 +5375,7 @@ "@babel/template": "^7.25.7", "@babel/traverse": "^7.25.7", "@babel/types": "^7.25.8", - "@tanstack/router-generator": "^1.65.0", + "@tanstack/router-generator": "^1.69.1", "@tanstack/virtual-file-routes": "^1.64.0", "@types/babel__core": "^7.20.5", "@types/babel__generator": "^7.6.8", @@ -8558,9 +8558,9 @@ } }, "node_modules/es-toolkit": { - "version": "1.25.1", - "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.25.1.tgz", - "integrity": "sha512-MVagOA/u9rgdFRfm6aQti7JmPhSDTYIBD3cm0P/l6us+LVf5XT9Drnv/woYXOtezKqwIGZWzb2FdUZyYo/eyug==" + "version": "1.25.2", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.25.2.tgz", + "integrity": "sha512-zEh2aJUwnlDwashas6JN+oFVN08F2s2qBaEwTo6EOACjO9PdPH4eGRBZC2JP/3SDLeANiMTEtVnOGhoG7GwZcA==" }, "node_modules/esbuild": { "version": "0.24.0", diff --git a/package.json b/package.json index 63fd5624..2abbee31 100644 --- a/package.json +++ b/package.json @@ -44,13 +44,13 @@ "@stanfordbdhg/engagehf-models": "^0.4.0", "@t3-oss/env-core": "^0.11.1", "@tanstack/match-sorter-utils": "^8.19.4", - "@tanstack/react-query": "^5.59.14", + "@tanstack/react-query": "^5.59.15", "@tanstack/react-table": "^8.20.5", "@vitejs/plugin-react": "^4.3.2", "class-variance-authority": "^0.7.0", "cmdk": "^1.0.0", "date-fns": "^3.6.0", - "es-toolkit": "^1.25.1", + "es-toolkit": "^1.25.2", "firebase": "^10.14.1", "jsdom": "^25.0.1", "lucide-react": "^0.453.0", @@ -77,8 +77,8 @@ "@storybook/react": "^8.3.5", "@storybook/react-vite": "^8.3.5", "@storybook/test": "^8.3.5", - "@tanstack/router-devtools": "^1.67.0", - "@tanstack/router-plugin": "^1.66.1", + "@tanstack/router-devtools": "^1.70.0", + "@tanstack/router-plugin": "^1.69.1", "@testing-library/jest-dom": "^6", "@testing-library/react": "^16", "@total-typescript/ts-reset": "^0.6.1", diff --git a/packages/design-system/src/components/Tabs/Tabs.mocks.tsx b/packages/design-system/src/components/Tabs/Tabs.mocks.tsx new file mode 100644 index 00000000..b77e8c78 --- /dev/null +++ b/packages/design-system/src/components/Tabs/Tabs.mocks.tsx @@ -0,0 +1,46 @@ +// +// This source file is part of the Stanford Biodesign Digital Health ENGAGE-HF open-source project +// +// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// +import { TabsContent, TabsList, TabsTrigger } from './Tabs' + +export enum Tab { + lorem = 'lorem', + ipsum = 'ipsum', + dolor = 'dolor', +} + +export const elements = { + triggers: ( + + Lorem + Ipsum + Dolor + + ), + content: ( + <> + +

Lorem content

+ Accusantium debitis dignissimos eaque explicabo impedit minima, modi + nisi pariatur praesentium voluptatem! Aliquam corporis enim error iste + pariatur sed vel, voluptas voluptates. +
+ +

Ipsum content

+ Alias animi corporis ea facilis magnam nulla obcaecati omnis possimus + quis voluptatum. Consectetur enim expedita facilis fugiat molestiae odio + officiis, placeat! Harum. +
+ +

Dolor content

+ Ab aperiam autem blanditiis culpa deleniti earum expedita, harum ipsa + ipsum itaque molestias neque nesciunt nisi odio pariatur quibusdam sequi + tempora vel? +
+ + ), +} diff --git a/packages/design-system/src/components/Tabs/Tabs.stories.tsx b/packages/design-system/src/components/Tabs/Tabs.stories.tsx new file mode 100644 index 00000000..b85fcab4 --- /dev/null +++ b/packages/design-system/src/components/Tabs/Tabs.stories.tsx @@ -0,0 +1,60 @@ +// +// This source file is part of the Stanford Biodesign Digital Health ENGAGE-HF open-source project +// +// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// +import { type Meta } from '@storybook/react' +import { useState } from 'react' +import { Tabs, TabsList, TabsTrigger } from './Tabs' +import { elements, Tab } from './Tabs.mocks' + +const meta: Meta = { + title: 'Components/Tabs', + component: Tabs, +} + +export default meta + +export const Default = () => ( + + {elements.triggers} + {elements.content} + +) + +export const Controlled = () => { + const [tab, setTab] = useState(Tab.lorem) + return ( + setTab(value as Tab)}> + {elements.triggers} +

active tab: {tab}

+ {elements.content} +
+ ) +} + +export const CustomPositioning = () => { + const [tab, setTab] = useState(Tab.lorem) + return ( + <> + setTab(value as Tab)}> + + {elements.content} + + + ) +} + +export const TriggerGrow = () => ( +
+ + + Lorem + Ipsum + Dolor + + +
+) diff --git a/packages/design-system/src/components/Tabs/Tabs.test.tsx b/packages/design-system/src/components/Tabs/Tabs.test.tsx new file mode 100644 index 00000000..85eb331a --- /dev/null +++ b/packages/design-system/src/components/Tabs/Tabs.test.tsx @@ -0,0 +1,47 @@ +// +// This source file is part of the Stanford Biodesign Digital Health ENGAGE-HF open-source project +// +// SPDX-FileCopyrightText: 2023 Stanford University and the project authors (see CONTRIBUTORS.md) +// +// SPDX-License-Identifier: MIT +// +import { render, screen } from '@testing-library/react' +import { userEvent } from '@testing-library/user-event' +import { elements, Tab } from './Tabs.mocks' +import { Tabs } from '.' + +describe('Tabs', () => { + it('renders no content if no default value provided', () => { + render( + + {elements.triggers} + {elements.content} + , + ) + + const contentElement = screen.queryByText(/content/) + expect(contentElement).not.toBeInTheDocument() + }) + + it('renders correct tab', () => { + render({elements.content}) + + const contentElement = screen.getByText(/Dolor content/) + expect(contentElement).toBeInTheDocument() + }) + + it('switches tabs', async () => { + render( + + {elements.triggers} + {elements.content} + , + ) + + const ipsumTab = screen.getByRole('tab', { name: /Ipsum/ }) + await userEvent.click(ipsumTab) + + const ipsumContent = screen.getByText(/Ipsum content/) + expect(ipsumContent).toBeInTheDocument() + }) +}) diff --git a/packages/design-system/src/components/Tabs/Tabs.tsx b/packages/design-system/src/components/Tabs/Tabs.tsx index 66517cd4..8bcc4525 100644 --- a/packages/design-system/src/components/Tabs/Tabs.tsx +++ b/packages/design-system/src/components/Tabs/Tabs.tsx @@ -8,41 +8,65 @@ import * as TabsPrimitive from '@radix-ui/react-tabs' import { type ComponentPropsWithoutRef, + createContext, type ElementRef, forwardRef, + useContext, } from 'react' import { cn } from '../../utils/className' export const Tabs = TabsPrimitive.Root +interface TabsListContextProps { + /** + * Expand tabs control to occupy available width + * */ + grow?: boolean +} + +interface TabsListProps + extends ComponentPropsWithoutRef, + TabsListContextProps {} + +const TabsListContext = createContext({ + grow: false, +}) + export const TabsList = forwardRef< ElementRef, - ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - + TabsListProps +>(({ className, grow, ...props }, ref) => ( + + + )) TabsList.displayName = TabsPrimitive.List.displayName export const TabsTrigger = forwardRef< ElementRef, ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) +>(({ className, ...props }, ref) => { + const { grow } = useContext(TabsListContext) + return ( + + ) +}) TabsTrigger.displayName = TabsPrimitive.Trigger.displayName export const TabsContent = forwardRef< diff --git a/routes/~_dashboard/~patients/~$id/~index.tsx b/routes/~_dashboard/~patients/~$id/~index.tsx index d066f95a..7ece3275 100644 --- a/routes/~_dashboard/~patients/~$id/~index.tsx +++ b/routes/~_dashboard/~patients/~$id/~index.tsx @@ -187,23 +187,19 @@ const PatientPage = () => { Edit {userName} - - + + Information - + Notifications - + Medications - - Allergies - - - Labs - - + Allergies + Labs + Appointments