From 6326939384ad8c1e27326df566e3ac4c6045ecdd Mon Sep 17 00:00:00 2001 From: aldbr Date: Tue, 29 Oct 2024 10:29:21 +0100 Subject: [PATCH] fix(storybook): correct the doc --- .../components/BaseApp/BaseApp.stories.tsx | 19 +-- .../ApplicationDialog.stories.tsx | 6 +- .../DashboardDrawer.stories.tsx | 8 +- .../DashboardLayout/DrawerItem.stories.tsx | 8 +- .../components/DashboardLayout/DrawerItem.tsx | 17 +- .../DrawerItemGroup.stories.tsx | 8 +- .../DashboardLayout/ProfileButton.stories.tsx | 8 +- .../ThemeToggleButton.stories.tsx | 8 +- .../JobMonitor/JobDataTable.stories.tsx | 8 +- .../JobMonitor/JobHistoryDialog.stories.tsx | 7 +- .../JobMonitor/JobMonitor.stories.tsx | 10 +- .../components/Login/LoginForm.stories.tsx | 7 +- .../components/shared/DataTable.stories.tsx | 149 +++++++---------- .../components/shared/DataTable.tsx | 3 +- .../components/shared/FilterForm.stories.tsx | 93 ++++++----- .../components/shared/FilterForm.tsx | 2 +- .../shared/FilterToolbar.stories.tsx | 98 +++++++----- .../components/shared/FilterToolbar.tsx | 7 +- .../test/unit-tests/FilterForm.test.tsx | 150 +++++------------- .../test/unit-tests/FilterToolbar.test.tsx | 86 +++------- .../unit-tests/ThemeToggleButton.test.tsx | 53 +++++-- 21 files changed, 325 insertions(+), 430 deletions(-) diff --git a/packages/diracx-web-components/components/BaseApp/BaseApp.stories.tsx b/packages/diracx-web-components/components/BaseApp/BaseApp.stories.tsx index ff68e576..62582a64 100644 --- a/packages/diracx-web-components/components/BaseApp/BaseApp.stories.tsx +++ b/packages/diracx-web-components/components/BaseApp/BaseApp.stories.tsx @@ -1,12 +1,11 @@ import React from "react"; import { StoryObj, Meta } from "@storybook/react"; -import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; import { Paper } from "@mui/material"; import { Apps } from "@mui/icons-material"; import { useOidcAccessToken } from "../../mocks/react-oidc.mock"; -import { useMUITheme } from "../../hooks/theme"; import { ApplicationsContext } from "../../contexts/ApplicationsProvider"; import { NavigationProvider } from "../../contexts/NavigationProvider"; +import { ThemeProvider } from "../../contexts/ThemeProvider"; import BaseApp from "./BaseApp"; const meta = { @@ -16,18 +15,14 @@ const meta = { layout: "centered", }, tags: ["autodocs"], - argTypes: { - headerSize: { control: "radio" }, - }, decorators: [ (Story) => { - const theme = useMUITheme(); return ( - + - + ); }, (Story) => ( @@ -76,9 +71,7 @@ export default meta; type Story = StoryObj; export const LoggedIn: Story = { - args: { - headerSize: undefined, - }, + args: {}, render: (props) => { useOidcAccessToken.mockReturnValue({ accessTokenPayload: { preferred_username: "John Doe" }, @@ -88,9 +81,7 @@ export const LoggedIn: Story = { }; export const LoggedOff: Story = { - args: { - headerSize: undefined, - }, + args: {}, render: (props) => { useOidcAccessToken.mockReturnValue({ accessTokenPayload: null, diff --git a/packages/diracx-web-components/components/DashboardLayout/ApplicationDialog.stories.tsx b/packages/diracx-web-components/components/DashboardLayout/ApplicationDialog.stories.tsx index f1a6320e..9931a9b6 100644 --- a/packages/diracx-web-components/components/DashboardLayout/ApplicationDialog.stories.tsx +++ b/packages/diracx-web-components/components/DashboardLayout/ApplicationDialog.stories.tsx @@ -1,13 +1,12 @@ import React from "react"; import type { Meta, StoryObj } from "@storybook/react"; -import { ThemeProvider } from "@mui/material"; import { useArgs } from "@storybook/core/preview-api"; import { ApplicationsContext } from "../../contexts/ApplicationsProvider"; import { useOidcAccessToken } from "../../mocks/react-oidc.mock"; import { applicationList } from "../ApplicationList"; -import { useMUITheme } from "../../hooks/theme"; +import { ThemeProvider } from "../../contexts/ThemeProvider"; import ApplicationDialog from "./ApplicationDialog"; const meta = { @@ -31,9 +30,8 @@ const meta = { ); }, (Story) => { - const theme = useMUITheme(); return ( - + ); diff --git a/packages/diracx-web-components/components/DashboardLayout/DashboardDrawer.stories.tsx b/packages/diracx-web-components/components/DashboardLayout/DashboardDrawer.stories.tsx index 7d1f633d..f9f02092 100644 --- a/packages/diracx-web-components/components/DashboardLayout/DashboardDrawer.stories.tsx +++ b/packages/diracx-web-components/components/DashboardLayout/DashboardDrawer.stories.tsx @@ -3,12 +3,11 @@ import type { Meta, StoryObj } from "@storybook/react"; import { Box } from "@mui/material"; import { Dashboard } from "@mui/icons-material"; -import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; -import { useMUITheme } from "../../hooks/theme"; import { useOidc, useOidcAccessToken } from "../../mocks/react-oidc.mock"; import { ApplicationsContext } from "../../contexts/ApplicationsProvider"; import { applicationList } from "../ApplicationList"; import { DashboardGroup } from "../../types"; +import { ThemeProvider } from "../../contexts/ThemeProvider"; import DashboardDrawer from "./DashboardDrawer"; const meta = { @@ -20,7 +19,6 @@ const meta = { tags: ["autodocs"], decorators: [ (Story) => { - const theme = useMUITheme(); const [userDashboard, setUserDashboard] = React.useState< DashboardGroup[] >([ @@ -41,11 +39,11 @@ const meta = { - + - + ); }, diff --git a/packages/diracx-web-components/components/DashboardLayout/DrawerItem.stories.tsx b/packages/diracx-web-components/components/DashboardLayout/DrawerItem.stories.tsx index 70e1f356..1b8713aa 100644 --- a/packages/diracx-web-components/components/DashboardLayout/DrawerItem.stories.tsx +++ b/packages/diracx-web-components/components/DashboardLayout/DrawerItem.stories.tsx @@ -2,10 +2,9 @@ import React from "react"; import type { Meta, StoryObj } from "@storybook/react"; import { Paper } from "@mui/material"; -import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; import { Dashboard } from "@mui/icons-material"; -import { useMUITheme } from "../../hooks/theme"; import { useOidc, useOidcAccessToken } from "../../mocks/react-oidc.mock"; +import { ThemeProvider } from "../../contexts/ThemeProvider"; import DrawerItem from "./DrawerItem"; const meta = { @@ -17,13 +16,12 @@ const meta = { tags: ["autodocs"], decorators: [ (Story) => { - const theme = useMUITheme(); return ( - + - + ); }, ], diff --git a/packages/diracx-web-components/components/DashboardLayout/DrawerItem.tsx b/packages/diracx-web-components/components/DashboardLayout/DrawerItem.tsx index f4d46977..0d797d9b 100644 --- a/packages/diracx-web-components/components/DashboardLayout/DrawerItem.tsx +++ b/packages/diracx-web-components/components/DashboardLayout/DrawerItem.tsx @@ -20,7 +20,6 @@ import { extractClosestEdge, } from "@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge"; import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/element/set-custom-native-drag-preview"; -import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; import { ThemeProvider } from "@/contexts/ThemeProvider"; import { useSearchParamsUtils } from "@/hooks/searchParamsUtils"; import { useApplicationId } from "@/hooks/application"; @@ -75,15 +74,13 @@ export default function DrawerItem({ // Wraps the preview in the theme provider to ensure the correct theme is applied // This is necessary because the preview is rendered outside the main app - -
- -
-
+
+ +
, ); return () => root.unmount(); diff --git a/packages/diracx-web-components/components/DashboardLayout/DrawerItemGroup.stories.tsx b/packages/diracx-web-components/components/DashboardLayout/DrawerItemGroup.stories.tsx index 798d2fbf..112fe598 100644 --- a/packages/diracx-web-components/components/DashboardLayout/DrawerItemGroup.stories.tsx +++ b/packages/diracx-web-components/components/DashboardLayout/DrawerItemGroup.stories.tsx @@ -2,11 +2,10 @@ import React from "react"; import type { Meta, StoryObj } from "@storybook/react"; import { useArgs } from "@storybook/core/preview-api"; import { Paper } from "@mui/material"; -import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; import { Dashboard } from "@mui/icons-material"; -import { useMUITheme } from "../../hooks/theme"; import { useOidc, useOidcAccessToken } from "../../mocks/react-oidc.mock"; import { DashboardGroup } from "../../types/DashboardGroup"; +import { ThemeProvider } from "../../contexts/ThemeProvider"; import DrawerItemGroup from "./DrawerItemGroup"; const meta = { @@ -18,13 +17,12 @@ const meta = { tags: ["autodocs"], decorators: [ (Story) => { - const theme = useMUITheme(); return ( - + - + ); }, ], diff --git a/packages/diracx-web-components/components/DashboardLayout/ProfileButton.stories.tsx b/packages/diracx-web-components/components/DashboardLayout/ProfileButton.stories.tsx index 31f54922..8b919929 100644 --- a/packages/diracx-web-components/components/DashboardLayout/ProfileButton.stories.tsx +++ b/packages/diracx-web-components/components/DashboardLayout/ProfileButton.stories.tsx @@ -2,9 +2,8 @@ import React from "react"; import type { Meta, StoryObj } from "@storybook/react"; import { Paper } from "@mui/material"; -import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; -import { useMUITheme } from "../../hooks/theme"; import { useOidc, useOidcAccessToken } from "../../mocks/react-oidc.mock"; +import { ThemeProvider } from "../../contexts/ThemeProvider"; import { ProfileButton } from "./ProfileButton"; const meta = { @@ -16,13 +15,12 @@ const meta = { tags: ["autodocs"], decorators: [ (Story) => { - const theme = useMUITheme(); return ( - + - + ); }, ], diff --git a/packages/diracx-web-components/components/DashboardLayout/ThemeToggleButton.stories.tsx b/packages/diracx-web-components/components/DashboardLayout/ThemeToggleButton.stories.tsx index 74b59232..c49e98c9 100644 --- a/packages/diracx-web-components/components/DashboardLayout/ThemeToggleButton.stories.tsx +++ b/packages/diracx-web-components/components/DashboardLayout/ThemeToggleButton.stories.tsx @@ -2,8 +2,7 @@ import React from "react"; import type { Meta, StoryObj } from "@storybook/react"; import { Paper } from "@mui/material"; -import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; -import { useMUITheme } from "../../hooks/theme"; +import { ThemeProvider } from "../../contexts/ThemeProvider"; import { ThemeToggleButton } from "./ThemeToggleButton"; const meta = { @@ -15,13 +14,12 @@ const meta = { tags: ["autodocs"], decorators: [ (Story) => { - const theme = useMUITheme(); return ( - + - + ); }, ], diff --git a/packages/diracx-web-components/components/JobMonitor/JobDataTable.stories.tsx b/packages/diracx-web-components/components/JobMonitor/JobDataTable.stories.tsx index 870dc92d..b85e4466 100644 --- a/packages/diracx-web-components/components/JobMonitor/JobDataTable.stories.tsx +++ b/packages/diracx-web-components/components/JobMonitor/JobDataTable.stories.tsx @@ -1,9 +1,8 @@ import React from "react"; import { StoryObj, Meta } from "@storybook/react"; -import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; -import { useMUITheme } from "../../hooks/theme"; import { useJobs } from "../../mocks/JobDataService.mock"; import { useOidcAccessToken } from "../../mocks/react-oidc.mock"; +import { ThemeProvider } from "../../contexts/ThemeProvider"; import { JobDataTable } from "./JobDataTable"; const meta = { @@ -25,11 +24,10 @@ const meta = { }, decorators: [ (Story) => { - const theme = useMUITheme(); return ( - + - + ); }, ], diff --git a/packages/diracx-web-components/components/JobMonitor/JobHistoryDialog.stories.tsx b/packages/diracx-web-components/components/JobMonitor/JobHistoryDialog.stories.tsx index 3b162dc0..d3c08c6f 100644 --- a/packages/diracx-web-components/components/JobMonitor/JobHistoryDialog.stories.tsx +++ b/packages/diracx-web-components/components/JobMonitor/JobHistoryDialog.stories.tsx @@ -1,8 +1,7 @@ import React from "react"; import { StoryObj, Meta } from "@storybook/react"; import { useArgs } from "@storybook/core/preview-api"; -import { ThemeProvider } from "@mui/material"; -import { useMUITheme } from "../../hooks/theme"; +import { ThemeProvider } from "../../contexts/ThemeProvider"; import { JobHistoryDialog } from "./JobHistoryDialog"; const meta = { @@ -19,9 +18,8 @@ const meta = { }, decorators: [ (Story) => { - const theme = useMUITheme(); return ( - + ); @@ -45,6 +43,7 @@ export const Default: Story = { ], open: true, onClose: () => {}, + jobId: 1234, }, render: (props) => { const [, updateArgs] = useArgs(); diff --git a/packages/diracx-web-components/components/JobMonitor/JobMonitor.stories.tsx b/packages/diracx-web-components/components/JobMonitor/JobMonitor.stories.tsx index 1920bbf0..16a91474 100644 --- a/packages/diracx-web-components/components/JobMonitor/JobMonitor.stories.tsx +++ b/packages/diracx-web-components/components/JobMonitor/JobMonitor.stories.tsx @@ -1,11 +1,11 @@ import React from "react"; import { StoryObj, Meta } from "@storybook/react"; -import { Paper, ThemeProvider } from "@mui/material"; +import { Paper } from "@mui/material"; import { useJobs } from "../../mocks/JobDataService.mock"; import { useOidcAccessToken } from "../../mocks/react-oidc.mock"; import { ApplicationsContext } from "../../contexts/ApplicationsProvider"; import { NavigationProvider } from "../../contexts/NavigationProvider"; -import { useMUITheme } from "../../hooks/theme"; +import { ThemeProvider } from "../../contexts/ThemeProvider"; import JobMonitor from "./JobMonitor"; const meta = { @@ -15,14 +15,10 @@ const meta = { layout: "centered", }, tags: ["autodocs"], - argTypes: { - headerSize: { control: "select" }, - }, decorators: [ (Story) => { - const theme = useMUITheme(); return ( - + diff --git a/packages/diracx-web-components/components/Login/LoginForm.stories.tsx b/packages/diracx-web-components/components/Login/LoginForm.stories.tsx index 5ceb9ad0..d593efe5 100644 --- a/packages/diracx-web-components/components/Login/LoginForm.stories.tsx +++ b/packages/diracx-web-components/components/Login/LoginForm.stories.tsx @@ -1,9 +1,9 @@ import React from "react"; import { StoryObj, Meta } from "@storybook/react"; -import { Paper, ThemeProvider } from "@mui/material"; +import { Paper } from "@mui/material"; import { useMetadata } from "../../mocks/metadata.mock"; import { useOidc } from "../../mocks/react-oidc.mock"; -import { useMUITheme } from "../../hooks/theme"; +import { ThemeProvider } from "../../contexts/ThemeProvider"; import { LoginForm } from "./LoginForm"; const meta = { @@ -24,9 +24,8 @@ const meta = { }, decorators: [ (Story) => { - const theme = useMUITheme(); return ( - + diff --git a/packages/diracx-web-components/components/shared/DataTable.stories.tsx b/packages/diracx-web-components/components/shared/DataTable.stories.tsx index e6cf7cc8..207b03a8 100644 --- a/packages/diracx-web-components/components/shared/DataTable.stories.tsx +++ b/packages/diracx-web-components/components/shared/DataTable.stories.tsx @@ -1,131 +1,96 @@ import React from "react"; -import { StoryObj, Meta } from "@storybook/react"; -import { useArgs } from "@storybook/core/preview-api"; -import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; +import { Meta, StoryObj } from "@storybook/react"; import { - AccessorKeyColumnDef, createColumnHelper, + getCoreRowModel, + useReactTable, } from "@tanstack/react-table"; -import { useMUITheme } from "../../hooks/theme"; -import { DataTable } from "./DataTable"; - -const meta = { - title: "shared/DataTable", - component: DataTable, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], - argTypes: { - title: { control: "text" }, - page: { control: "number" }, - setPage: { control: false }, - rowsPerPage: { control: "number" }, - setRowsPerPage: { control: false }, - totalRows: { control: "number" }, - selected: { control: "object" }, - setSelected: { control: false }, - searchBody: { control: false }, - setSearchBody: { control: false }, - columns: { control: "object" }, - rows: { control: "object" }, - error: { control: "text" }, - isValidating: { control: "boolean" }, - isLoading: { control: "boolean" }, - rowIdentifier: { control: "text" }, - isMobile: { control: "boolean" }, - toolbarComponents: { control: false }, - menuItems: { control: "object" }, - }, - args: {}, - decorators: [ - (Story) => { - const theme = useMUITheme(); - return ( - - - - ); - }, - ], -} satisfies Meta; +import { ThemeProvider } from "../../contexts/ThemeProvider"; +import { DataTable, DataTableProps } from "./DataTable"; interface SimpleItem extends Record { id: number; name: string; email: string; - [key: string]: unknown; } const columnHelper = createColumnHelper(); -const columns: Array< - | AccessorKeyColumnDef, number> - | AccessorKeyColumnDef, string> - | AccessorKeyColumnDef, Date> -> = [ +const columnDefs = [ columnHelper.accessor("id", { header: "ID", meta: { type: "number" }, - }) as AccessorKeyColumnDef, number>, + }), columnHelper.accessor("name", { header: "Name", meta: { type: "string" }, - }) as AccessorKeyColumnDef, string>, + }), columnHelper.accessor("email", { header: "Email", meta: { type: "string" }, - }) as AccessorKeyColumnDef, string>, + }), ]; -export default meta; -type Story = StoryObj; +const data: SimpleItem[] = [ + { id: 1, name: "John Doe", email: "john@example.com" }, +]; -export const Default: Story = { +// Wrapper component to initialize the table +const DataTableWrapper: React.FC, "table">> = ( + props, +) => { + const table = useReactTable({ + data, + columns: columnDefs, + getCoreRowModel: getCoreRowModel(), + }); + + return {...props} table={table} />; +}; + +const meta: Meta = { + title: "shared/DataTable", + component: DataTableWrapper, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + argTypes: { + title: { control: "text" }, + table: { control: false, description: "tan stack `Table`", required: true }, + totalRows: { control: "number" }, + searchBody: { control: false }, + setSearchBody: { control: false }, + error: { control: "text" }, + isValidating: { control: "boolean" }, + isLoading: { control: "boolean" }, + toolbarComponents: { control: false }, + menuItems: { control: "object" }, + }, decorators: [ (Story) => ( -
- -
+ +
+ +
+
), ], +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { args: { title: "Data Table", - page: 0, - setPage: () => {}, - rowsPerPage: 25, - setRowsPerPage: () => {}, - totalRows: 1, - selected: [], - setSelected: () => {}, + totalRows: data.length, searchBody: { sort: [{ parameter: "id", direction: "asc" }] }, setSearchBody: () => {}, - columns: columns, - rows: [{ id: 1, name: "John Doe", email: "john@example.com" }], - error: "", + error: null, isValidating: false, isLoading: false, - rowIdentifier: "id", - isMobile: false, toolbarComponents: <>, menuItems: [{ label: "Edit", onClick: () => {} }], }, - render: (props) => { - const [, updateArgs] = useArgs(); - props.setPage = (newPage) => { - if (typeof newPage === "function") newPage = newPage(props.page); - updateArgs({ page: newPage }); - }; - props.setRowsPerPage = (newRowsPerPage) => { - if (typeof newRowsPerPage === "function") - newRowsPerPage = newRowsPerPage(props.rowsPerPage); - updateArgs({ rowsPerPage: newRowsPerPage }); - }; - props.setSelected = (newSelected) => { - if (typeof newSelected === "function") - newSelected = newSelected(props.selected); - updateArgs({ selected: newSelected }); - }; - return ; - }, }; diff --git a/packages/diracx-web-components/components/shared/DataTable.tsx b/packages/diracx-web-components/components/shared/DataTable.tsx index 1846c53a..0200cae8 100644 --- a/packages/diracx-web-components/components/shared/DataTable.tsx +++ b/packages/diracx-web-components/components/shared/DataTable.tsx @@ -201,7 +201,7 @@ function DataTableToolbar>( * @property {JSX.Element} toolbarComponents - the components to display in the toolbar * @property {MenuItem[]} menuItems - the menu items */ -interface DataTableProps> { +export interface DataTableProps> { /** The title of the table */ title: string; /** The table */ @@ -525,6 +525,7 @@ export function DataTable>( /> ( <> diff --git a/packages/diracx-web-components/components/shared/FilterForm.stories.tsx b/packages/diracx-web-components/components/shared/FilterForm.stories.tsx index 80a31b32..529d774d 100644 --- a/packages/diracx-web-components/components/shared/FilterForm.stories.tsx +++ b/packages/diracx-web-components/components/shared/FilterForm.stories.tsx @@ -1,23 +1,67 @@ import React from "react"; -import { StoryObj, Meta } from "@storybook/react"; -import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; +import { StoryObj } from "@storybook/react"; import { Paper } from "@mui/material"; import { - AccessorKeyColumnDef, createColumnHelper, + getCoreRowModel, + useReactTable, } from "@tanstack/react-table"; -import { useMUITheme } from "../../hooks/theme"; -import { FilterForm } from "./FilterForm"; +import { ThemeProvider } from "../../contexts/ThemeProvider"; +import { FilterForm, FilterFormProps } from "./FilterForm"; + +interface SimpleItem extends Record { + id: number; + name: string; + email: string; +} + +const columnHelper = createColumnHelper(); + +const columnDefs = [ + columnHelper.accessor("id", { + header: "ID", + meta: { type: "number" }, + }), + columnHelper.accessor("name", { + header: "Name", + meta: { type: "string" }, + }), + columnHelper.accessor("email", { + header: "Email", + meta: { type: "string" }, + }), +]; + +const data: SimpleItem[] = [ + { id: 1, name: "John Doe", email: "john@example.com" }, +]; + +// Wrapper component to initialize the table +const FilterFormWrapper: React.FC< + Omit, "columns"> +> = (props) => { + const table = useReactTable({ + data, + columns: columnDefs, + getCoreRowModel: getCoreRowModel(), + }); + + return {...props} columns={table.getAllColumns()} />; +}; const meta = { title: "shared/FilterForm", - component: FilterForm, + component: FilterFormWrapper, parameters: { layout: "centered", }, tags: ["autodocs"], argTypes: { - columns: { control: "object" }, + columns: { + control: false, + description: "`array` of tan stack `Column`", + required: true, + }, filters: { control: "object" }, setFilters: { control: "object" }, handleFilterChange: { control: "object" }, @@ -26,51 +70,22 @@ const meta = { }, decorators: [ (Story) => { - const theme = useMUITheme(); return ( - + - +
); }, ], -} satisfies Meta; +}; export default meta; type Story = StoryObj; -interface SimpleItem extends Record { - id: number; - name: string; - email: string; -} - -const columnHelper = createColumnHelper(); - -const columns: Array< - | AccessorKeyColumnDef, number> - | AccessorKeyColumnDef, string> - | AccessorKeyColumnDef, Date> -> = [ - columnHelper.accessor("id", { - header: "ID", - meta: { type: "number" }, - }) as AccessorKeyColumnDef, number>, - columnHelper.accessor("name", { - header: "Name", - meta: { type: "string" }, - }) as AccessorKeyColumnDef, string>, - columnHelper.accessor("email", { - header: "Email", - meta: { type: "string" }, - }) as AccessorKeyColumnDef, string>, -]; - export const Default: Story = { args: { - columns: columns, filters: [{ id: 0, parameter: "id", operator: "eq", value: "1" }], setFilters: () => {}, handleFilterChange: () => {}, diff --git a/packages/diracx-web-components/components/shared/FilterForm.tsx b/packages/diracx-web-components/components/shared/FilterForm.tsx index bc65c4dc..55bdfb3d 100644 --- a/packages/diracx-web-components/components/shared/FilterForm.tsx +++ b/packages/diracx-web-components/components/shared/FilterForm.tsx @@ -24,7 +24,7 @@ import "dayjs/locale/en-gb"; // needed by LocalizationProvider to format Dates t * @property {InternalFilter[]} filters - the filters for the table * @property {number} selectedFilterId - the id of the selected filter */ -interface FilterFormProps> { +export interface FilterFormProps> { /** The columns of the data table */ columns: Column[]; /** The function to call when a filter is changed */ diff --git a/packages/diracx-web-components/components/shared/FilterToolbar.stories.tsx b/packages/diracx-web-components/components/shared/FilterToolbar.stories.tsx index bf38860b..3d551f14 100644 --- a/packages/diracx-web-components/components/shared/FilterToolbar.stories.tsx +++ b/packages/diracx-web-components/components/shared/FilterToolbar.stories.tsx @@ -1,86 +1,106 @@ import React from "react"; -import { StoryObj, Meta } from "@storybook/react"; +import { StoryObj } from "@storybook/react"; import { useArgs } from "@storybook/core/preview-api"; -import { ThemeProvider as MUIThemeProvider } from "@mui/material/styles"; import { Paper } from "@mui/material"; import { - AccessorKeyColumnDef, createColumnHelper, + getCoreRowModel, + useReactTable, } from "@tanstack/react-table"; -import { useMUITheme } from "../../hooks/theme"; -import { FilterToolbar } from "./FilterToolbar"; +import { ThemeProvider } from "../../contexts/ThemeProvider"; +import { FilterToolbar, FilterToolbarProps } from "./FilterToolbar"; + +interface SimpleItem extends Record { + id: number; + name: string; + email: string; +} + +const columnHelper = createColumnHelper(); + +const columnDefs = [ + columnHelper.accessor("id", { + header: "ID", + meta: { type: "number" }, + }), + columnHelper.accessor("name", { + header: "Name", + meta: { type: "string" }, + }), + columnHelper.accessor("email", { + header: "Email", + meta: { type: "string" }, + }), +]; + +const data: SimpleItem[] = [ + { id: 1, name: "John Doe", email: "john@example.com" }, +]; + +// Wrapper component to initialize the table +const FilterToolbarWrapper: React.FC< + Omit, "columns"> +> = (props) => { + const table = useReactTable({ + data, + columns: columnDefs, + getCoreRowModel: getCoreRowModel(), + }); + + return ( + {...props} columns={table.getAllColumns()} /> + ); +}; const meta = { title: "shared/FilterToolbar", - component: FilterToolbar, + component: FilterToolbarWrapper, parameters: { layout: "centered", }, tags: ["autodocs"], argTypes: { - columns: { control: "object" }, + columns: { + control: false, + description: "`array` of tan stack `Column`", + required: true, + }, filters: { control: "object" }, setFilters: { control: "object" }, handleApplyFilters: { control: "object" }, + handleClearFilters: { control: "object" }, }, decorators: [ (Story) => { - const theme = useMUITheme(); return ( - + - +
); }, ], -} satisfies Meta; - -interface SimpleItem extends Record { - id: number; - name: string; - email: string; -} - -const columnHelper = createColumnHelper(); -const columns: Array< - | AccessorKeyColumnDef, number> - | AccessorKeyColumnDef, string> - | AccessorKeyColumnDef, Date> -> = [ - columnHelper.accessor("id", { - header: "ID", - meta: { type: "number" }, - }) as AccessorKeyColumnDef, number>, - columnHelper.accessor("name", { - header: "Name", - meta: { type: "string" }, - }) as AccessorKeyColumnDef, string>, - columnHelper.accessor("email", { - header: "Email", - meta: { type: "string" }, - }) as AccessorKeyColumnDef, string>, -]; +}; export default meta; type Story = StoryObj; export const Default: Story = { args: { - columns: columns, filters: [ { id: 0, parameter: "id", operator: "eq", value: "1" }, { id: 1, parameter: "id", operator: "neq", value: "2" }, ], setFilters: () => {}, handleApplyFilters: () => {}, + handleClearFilters: () => {}, appliedFilters: [{ id: 0, parameter: "id", operator: "eq", value: "1" }], }, render: (props) => { const [{ filters }, updateArgs] = useArgs(); props.setFilters = (filters) => updateArgs({ filters }); props.handleApplyFilters = () => updateArgs({ appliedFilters: filters }); - return ; + return ; }, }; diff --git a/packages/diracx-web-components/components/shared/FilterToolbar.tsx b/packages/diracx-web-components/components/shared/FilterToolbar.tsx index f0e81209..076c705e 100644 --- a/packages/diracx-web-components/components/shared/FilterToolbar.tsx +++ b/packages/diracx-web-components/components/shared/FilterToolbar.tsx @@ -13,7 +13,7 @@ import "@/hooks/theme"; * Filter toolbar component * @param {FilterToolbarProps} props - the props for the component */ -interface FilterToolbarProps> { +export interface FilterToolbarProps> { /** The columns of the data table */ columns: Column[]; /** The filters to apply */ @@ -216,6 +216,11 @@ export function FilterToolbar>( m: 0.5, backgroundColor: isApplied(filter) ? "primary.main" : grey[500], }} + className={ + isApplied(filter) + ? "chip-filter-applied" + : "chip-filter-unapplied" + } /> ))} diff --git a/packages/diracx-web-components/test/unit-tests/FilterForm.test.tsx b/packages/diracx-web-components/test/unit-tests/FilterForm.test.tsx index 04f50541..49246068 100644 --- a/packages/diracx-web-components/test/unit-tests/FilterForm.test.tsx +++ b/packages/diracx-web-components/test/unit-tests/FilterForm.test.tsx @@ -1,8 +1,6 @@ import React from "react"; import { render, screen, fireEvent, within } from "@testing-library/react"; import { - AccessorKeyColumnDef, - Column, createColumnHelper, getCoreRowModel, useReactTable, @@ -18,32 +16,6 @@ interface SimpleItem extends Record { category: string; } -// Define the props for the mock table component -interface MockTableComponentProps { - columns: AccessorKeyColumnDef[]; - data: SimpleItem[]; - setColumns: (columns: Column[]) => void; -} - -// Mock table component to initialize columns -const MockTableComponent = ({ - columns, - data, - setColumns, -}: MockTableComponentProps) => { - const tableInstance = useReactTable({ - data, - columns, - getCoreRowModel: getCoreRowModel(), - }); - - React.useEffect(() => { - setColumns(tableInstance.getAllColumns() as Column[]); - }, [tableInstance, setColumns]); - - return null; -}; - describe("FilterForm", () => { // Define the columns for the table const columnHelper = createColumnHelper(); @@ -73,22 +45,7 @@ describe("FilterForm", () => { { id: 2, name: "Item 2", category: "B", date: new Date() }, ]; - // Define state for the columns - let columns: Column[] = []; - - // Render the mock component to initialize columns - beforeAll(() => { - render( - - (columns = cols)} - /> - , - ); - }); - + // Mock filters const filters = [ { id: 1, parameter: "id", operator: "eq", value: "4" }, { id: 2, parameter: "name", operator: "neq", value: "value2" }, @@ -97,17 +54,36 @@ describe("FilterForm", () => { const handleFilterChange = jest.fn(); const handleFilterMenuClose = jest.fn(); - it("renders the filter form with correct initial values", () => { - render( - - columns={columns} - filters={filters} - setFilters={setFilters} - handleFilterChange={handleFilterChange} - handleFilterMenuClose={handleFilterMenuClose} - selectedFilterId={undefined} - />, + // Wrapper component to initialize the table + interface FilterFormWrapperProps { + selectedFilterId: number | undefined; + } + + const FilterFormWrapper: React.FC = ({ + selectedFilterId, + }) => { + const table = useReactTable({ + data, + columns: columnDefs, + getCoreRowModel: getCoreRowModel(), + }); + + return ( + + + columns={table.getAllColumns()} + filters={filters} + setFilters={setFilters} + handleFilterChange={handleFilterChange} + handleFilterMenuClose={handleFilterMenuClose} + selectedFilterId={selectedFilterId} + /> + ); + }; + + it("renders the filter form with correct initial values", () => { + render(); const columnSelect = screen.getByTestId("filter-form-select-parameter"); const operatorSelect = screen.getByTestId("filter-form-select-operator"); @@ -119,16 +95,7 @@ describe("FilterForm", () => { }); it("renders the filter form with correct initial values when a filter is selected", () => { - render( - - columns={columns} - filters={filters} - setFilters={setFilters} - handleFilterChange={handleFilterChange} - handleFilterMenuClose={handleFilterMenuClose} - selectedFilterId={1} - />, - ); + render(); const columnSelect = screen.getByTestId("filter-form-select-parameter"); const operatorSelect = screen.getByTestId("filter-form-select-operator"); @@ -140,16 +107,7 @@ describe("FilterForm", () => { }); it("updates the selected filter when fields are changed", () => { - render( - - columns={columns} - filters={filters} - setFilters={setFilters} - handleFilterChange={handleFilterChange} - handleFilterMenuClose={handleFilterMenuClose} - selectedFilterId={2} - />, - ); + render(); const columnSelect = screen.getByTestId("filter-form-select-parameter"); const operatorSelect = screen.getByTestId("filter-form-select-operator"); @@ -184,16 +142,7 @@ describe("FilterForm", () => { }); it("calls setFilters when applyChanges is clicked with a new filter", () => { - render( - , - ); + render(); const applyChangesButton = screen.getByText("Add"); @@ -208,16 +157,7 @@ describe("FilterForm", () => { }); it("calls handleFilterChange when applyChanges is clicked with an existing filter", () => { - render( - , - ); + render(); const applyChangesButton = screen.getByText("Add"); @@ -243,16 +183,7 @@ describe("FilterForm", () => { }); it("renders the correct input for DateTime column type", () => { - render( - , - ); + render(); const columnSelect = screen.getByTestId("filter-form-select-parameter"); const columnButton = within(columnSelect).getByRole("combobox"); @@ -279,16 +210,7 @@ describe("FilterForm", () => { }); it("handles 'in' and 'not in' operators for category columns", () => { - render( - , - ); + render(); const columnSelect = screen.getByTestId("filter-form-select-parameter"); const columnButton = within(columnSelect).getByRole("combobox"); diff --git a/packages/diracx-web-components/test/unit-tests/FilterToolbar.test.tsx b/packages/diracx-web-components/test/unit-tests/FilterToolbar.test.tsx index e9466ebc..f0f39b76 100644 --- a/packages/diracx-web-components/test/unit-tests/FilterToolbar.test.tsx +++ b/packages/diracx-web-components/test/unit-tests/FilterToolbar.test.tsx @@ -1,8 +1,6 @@ import React from "react"; import { render, screen, fireEvent, cleanup } from "@testing-library/react"; import { - AccessorKeyColumnDef, - Column, createColumnHelper, getCoreRowModel, useReactTable, @@ -17,32 +15,6 @@ interface SimpleItem extends Record { description: string; } -// Define the props for the mock table component -interface MockTableComponentProps { - columns: AccessorKeyColumnDef[]; - data: SimpleItem[]; - setColumns: (columns: Column[]) => void; -} - -// Mock table component to initialize columns -const MockTableComponent = ({ - columns, - data, - setColumns, -}: MockTableComponentProps) => { - const tableInstance = useReactTable({ - data, - columns, - getCoreRowModel: getCoreRowModel(), - }); - - React.useEffect(() => { - setColumns(tableInstance.getAllColumns() as Column[]); - }, [tableInstance, setColumns]); - - return null; -}; - describe("FilterToolbar", () => { // Define the columns for the table const columnHelper = createColumnHelper(); @@ -68,22 +40,7 @@ describe("FilterToolbar", () => { { id: 2, name: "Item 2", description: "Description 2" }, ]; - // Define state for the columns - let columns: Column[] = []; - - // Render the mock component to initialize columns - beforeAll(() => { - render( - - (columns = cols)} - /> - , - ); - }); - + // Create mock filters const filters = [ { id: 1, parameter: "id", operator: "eq", value: "value1" }, { id: 2, parameter: "name", operator: "neq", value: "value2" }, @@ -95,19 +52,30 @@ describe("FilterToolbar", () => { const handleApplyFilters = jest.fn(); const handleClearFilters = jest.fn(); - beforeEach(() => { - render( + // Wrapper component to initialize the table + const FilterToolbarWrapper: React.FC = () => { + const table = useReactTable({ + data, + columns: columnDefs, + getCoreRowModel: getCoreRowModel(), + }); + + return ( - columns={columns} + columns={table.getAllColumns()} filters={filters} setFilters={setFilters} handleApplyFilters={handleApplyFilters} handleClearFilters={handleClearFilters} appliedFilters={appliedFilters} /> - , +
); + }; + + beforeEach(() => { + render(); }); it("renders the filter toolbar with correct buttons", () => { @@ -118,6 +86,15 @@ describe("FilterToolbar", () => { expect(addFilterButton).toBeInTheDocument(); expect(applyFiltersButton).toBeInTheDocument(); expect(clearAllFiltersButton).toBeInTheDocument(); + + const idFilter = screen.getByText("id eq value1").closest("div"); + const nameFilter = screen.getByText("name neq value2").closest("div"); + + expect(idFilter).toBeInTheDocument(); + expect(nameFilter).toBeInTheDocument(); + + expect(idFilter).toHaveClass("chip-filter-applied"); + expect(nameFilter).toHaveClass("chip-filter-unapplied"); }); it("renders the warning when there are unapplied filters", () => { @@ -136,18 +113,7 @@ describe("FilterToolbar", () => { cleanup(); - render( - - - , - ); + render(); expect(warningMessage).not.toBeInTheDocument(); appliedFilters.pop(); diff --git a/packages/diracx-web-components/test/unit-tests/ThemeToggleButton.test.tsx b/packages/diracx-web-components/test/unit-tests/ThemeToggleButton.test.tsx index 1575849b..de014582 100644 --- a/packages/diracx-web-components/test/unit-tests/ThemeToggleButton.test.tsx +++ b/packages/diracx-web-components/test/unit-tests/ThemeToggleButton.test.tsx @@ -1,10 +1,11 @@ +// ThemeToggleButton.test.tsx import React from "react"; import { render, fireEvent } from "@testing-library/react"; import { ThemeToggleButton } from "@/components/DashboardLayout/ThemeToggleButton"; import { useTheme } from "@/hooks/theme"; -// Mocking the useTheme hook -jest.mock("../../hooks/theme", () => ({ +// Mock the useTheme hook +jest.mock("@/hooks/theme", () => ({ useTheme: jest.fn(), })); @@ -13,6 +14,10 @@ describe("", () => { beforeEach(() => { mockToggleTheme = jest.fn(); + (useTheme as jest.Mock).mockReturnValue({ + theme: "light", + toggleTheme: mockToggleTheme, + }); }); afterEach(() => { @@ -20,11 +25,6 @@ describe("", () => { }); it('renders the DarkModeIcon when theme is "light"', () => { - (useTheme as jest.Mock).mockReturnValue({ - theme: "light", - toggleTheme: mockToggleTheme, - }); - const { getByTestId, queryByTestId } = render(); expect(getByTestId("dark-mode")).toBeInTheDocument(); expect(queryByTestId("light-mode")).not.toBeInTheDocument(); @@ -42,15 +42,48 @@ describe("", () => { }); it("calls toggleTheme function when button is clicked", () => { + const { getByRole } = render(); + const button = getByRole("button"); + + fireEvent.click(button); + expect(mockToggleTheme).toHaveBeenCalledTimes(1); + }); + + it("renders the correct icon based on theme from localStorage", () => { + // Simulate theme stored in localStorage + localStorage.setItem("theme", "dark"); + + // Mock useTheme to read from localStorage + (useTheme as jest.Mock).mockReturnValue({ + theme: "dark", + toggleTheme: mockToggleTheme, + }); + + const { getByTestId } = render(); + expect(getByTestId("light-mode")).toBeInTheDocument(); + }); + + it("toggles theme and updates the icon accordingly", () => { (useTheme as jest.Mock).mockReturnValue({ theme: "light", toggleTheme: mockToggleTheme, }); - const { getByRole } = render(); - const button = getByRole("button"); + const { getByTestId, queryByTestId, rerender } = render( + , + ); + expect(getByTestId("dark-mode")).toBeInTheDocument(); - fireEvent.click(button); + fireEvent.click(getByTestId("dark-mode")); expect(mockToggleTheme).toHaveBeenCalledTimes(1); + + (useTheme as jest.Mock).mockReturnValue({ + theme: "dark", + toggleTheme: mockToggleTheme, + }); + + rerender(); + expect(getByTestId("light-mode")).toBeInTheDocument(); + expect(queryByTestId("dark-mode")).not.toBeInTheDocument(); }); });