From 62ac659e5d3c17f5b669f28580c83716340efba9 Mon Sep 17 00:00:00 2001 From: Matthew Warman Date: Thu, 19 Mar 2026 08:26:49 -0400 Subject: [PATCH 1/7] Refactor Alert component to use shadcn styles and update related tests and stories --- src/common/components/Alert/Alert.tsx | 83 ------------------ src/common/components/Alert/ErrorAlert.tsx | 25 +++--- .../Alert/__stories__/ErrorAlert.stories.tsx | 10 --- .../components/Alert/__tests__/Alert.test.tsx | 87 ------------------- .../__stories__/Alert.stories.tsx | 78 ++++++++++------- .../shadcn/__tests__/Alert.test.tsx | 78 +++++++++++++++++ src/common/components/shadcn/alert.tsx | 58 +++++++++++++ src/index.css | 18 ++++ .../Components/components/AlertComponents.tsx | 80 ++++++++--------- 9 files changed, 249 insertions(+), 268 deletions(-) delete mode 100644 src/common/components/Alert/Alert.tsx delete mode 100644 src/common/components/Alert/__tests__/Alert.test.tsx rename src/common/components/{Alert => shadcn}/__stories__/Alert.stories.tsx (65%) create mode 100644 src/common/components/shadcn/__tests__/Alert.test.tsx create mode 100644 src/common/components/shadcn/alert.tsx diff --git a/src/common/components/Alert/Alert.tsx b/src/common/components/Alert/Alert.tsx deleted file mode 100644 index 0e2f602..0000000 --- a/src/common/components/Alert/Alert.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { PropsWithChildren } from 'react'; -import { cva, VariantProps } from 'class-variance-authority'; - -import { BaseComponentProps } from 'common/utils/types'; -import { cn } from 'common/utils/css'; -import FAIcon, { FAIconProps } from '../Icon/FAIcon'; - -/** - * Define the `Alert` component base and variant styles. - */ -const alertVariants = cva('relative rounded-md p-3 *:data-icon:absolute [&>svg~*]:ps-8', { - variants: { - variant: { - danger: 'bg-red-800/90 text-white/80', - info: 'bg-neutral-200/90 text-slate-900', - success: 'bg-green-800/90 text-white/80', - warning: 'bg-amber-400/90 text-slate-900', - }, - }, - defaultVariants: { variant: 'info' }, -}); - -/** - * Properties for the `Alert` component. - */ -export interface AlertProps extends BaseComponentProps, PropsWithChildren, VariantProps {} - -/** - * The `Alert` component formats and renders a styled message. Use the - * `variant` property to apply predefined styles. - * - * Compose an Alert using of combinations of: `Icon`, `Title`, and `Description`. - * - * **Example:** - * ``` - - - Unable to create task - {error.message} - - * ``` - */ -const Alert = ({ children, className, variant = 'info', testId = 'alert' }: AlertProps) => { - return ( -
- {children} -
- ); -}; - -/** - * The `Title` component renders the styled title text for an `Alert`. - */ -const Title = ({ children, className, testId = 'alert-title' }: BaseComponentProps & PropsWithChildren) => { - return ( -
- {children} -
- ); -}; -Alert.Title = Title; - -/** - * The `Description` component renders the styled description text for an `Alert`. - */ -const Description = ({ children, className, testId = 'alert-description' }: BaseComponentProps & PropsWithChildren) => { - return ( -
- {children} -
- ); -}; -Alert.Description = Description; - -/** - * The `Icon` component renders the styled icon for an `Alert`. - */ -const Icon = ({ size = 'lg', testId = 'alert-icon', ...props }: FAIconProps) => { - return ; -}; -Alert.Icon = Icon; - -export default Alert; diff --git a/src/common/components/Alert/ErrorAlert.tsx b/src/common/components/Alert/ErrorAlert.tsx index 2d8b853..b06038c 100644 --- a/src/common/components/Alert/ErrorAlert.tsx +++ b/src/common/components/Alert/ErrorAlert.tsx @@ -1,11 +1,13 @@ +import { AlertCircleIcon } from 'lucide-react'; + import { cn } from 'common/utils/css'; -import { FAIconProps } from '../Icon/FAIcon'; -import Alert, { AlertProps } from './Alert'; +import { Alert, AlertDescription, AlertTitle } from '../shadcn/alert'; +import { BaseComponentProps } from 'common/utils/types'; /** * Properties for the `ErrorAlert` component. */ -export interface ErrorAlertProps extends Omit, Partial> { +export interface ErrorAlertProps extends BaseComponentProps { title?: string; description: string; } @@ -14,19 +16,12 @@ export interface ErrorAlertProps extends Omit, Partial { +const ErrorAlert = ({ className, description, testId = 'alert-error', title, ...props }: ErrorAlertProps) => { return ( - - - {title && {title}} - {description} + + + {title && {title}} + {description} ); }; diff --git a/src/common/components/Alert/__stories__/ErrorAlert.stories.tsx b/src/common/components/Alert/__stories__/ErrorAlert.stories.tsx index cadf6d8..5de36b5 100644 --- a/src/common/components/Alert/__stories__/ErrorAlert.stories.tsx +++ b/src/common/components/Alert/__stories__/ErrorAlert.stories.tsx @@ -7,10 +7,8 @@ const meta = { component: ErrorAlert, tags: ['autodocs'], argTypes: { - children: { description: 'The content.' }, className: { description: 'Additional CSS classes.' }, description: { description: 'The detailed description.' }, - icon: { description: 'The icon name.', type: 'string' }, testId: { description: 'The test identifier.', type: 'string' }, title: { description: 'The title.' }, }, @@ -33,11 +31,3 @@ export const DescriptionOnly: Story = { description: 'Some problem has occurred. Please check your work and try again.', }, }; - -export const WithAlternateIcon: Story = { - args: { - icon: 'phone', - title: 'This is bad!', - description: 'You probably need to call customer support.', - }, -}; diff --git a/src/common/components/Alert/__tests__/Alert.test.tsx b/src/common/components/Alert/__tests__/Alert.test.tsx deleted file mode 100644 index 56e5a88..0000000 --- a/src/common/components/Alert/__tests__/Alert.test.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { render, screen } from 'test/test-utils'; - -import Alert from '../Alert'; - -describe('Alert', () => { - it('should render successfully', async () => { - // ARRANGE - render(); - await screen.findByTestId('alert'); - - // ASSERT - expect(screen.getByTestId('alert')).toBeDefined(); - }); - - it('should use custom test ID', async () => { - // ARRANGE - render(); - await screen.findByTestId('custom-testid'); - - expect(screen.queryByTestId('alert')).toBeNull(); - expect(screen.getByTestId('custom-testid')).toBeDefined(); - }); - - it('should use classes from className property', async () => { - // ARRANGE - render(); - await screen.findByTestId('alert'); - - // ASSERT - expect(screen.getByTestId('alert').classList).toContain('custom-class'); - }); - - it('should render the info variant', async () => { - // ARRANGE - render(); - await screen.findByTestId('alert'); - - // ASSERT - expect(screen.getByTestId('alert').classList).toContain('bg-neutral-200/90'); - }); - - it('should render the warning variant', async () => { - // ARRANGE - render(); - await screen.findByTestId('alert'); - - // ASSERT - expect(screen.getByTestId('alert').classList).toContain('bg-amber-400/90'); - }); - - it('should render the danger variant', async () => { - // ARRANGE - render(); - await screen.findByTestId('alert'); - - // ASSERT - expect(screen.getByTestId('alert').classList).toContain('bg-red-800/90'); - }); - - it('should render the success variant', async () => { - // ARRANGE - render(); - await screen.findByTestId('alert'); - - // ASSERT - expect(screen.getByTestId('alert').classList).toContain('bg-green-800/90'); - }); - - it('should render the default variant when variant not specified', async () => { - // ARRANGE - render(); - await screen.findByTestId('alert'); - - // ASSERT - expect(screen.getByTestId('alert').classList).toContain('bg-neutral-200/90'); - }); - - it('should have role=alert', async () => { - // ARRANGE - render(); - await screen.findByTestId('alert'); - - // ASSERT - expect(screen.getByTestId('alert').getAttribute('role')).toBe('alert'); - }); -}); diff --git a/src/common/components/Alert/__stories__/Alert.stories.tsx b/src/common/components/shadcn/__stories__/Alert.stories.tsx similarity index 65% rename from src/common/components/Alert/__stories__/Alert.stories.tsx rename to src/common/components/shadcn/__stories__/Alert.stories.tsx index d82f921..140e67b 100644 --- a/src/common/components/Alert/__stories__/Alert.stories.tsx +++ b/src/common/components/shadcn/__stories__/Alert.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; +import { CircleAlertIcon, Info } from 'lucide-react'; -import Alert from '../Alert'; +import { Alert, AlertDescription, AlertTitle } from '../alert'; const meta = { title: 'Common/Alert/Alert', @@ -12,9 +13,8 @@ const meta = { variant: { description: 'Optional. The style variant.', control: { type: 'select' }, - options: ['danger', 'info', 'success', 'warning'], + options: ['default', 'destructive', 'info', 'success', 'warning'], }, - testId: { description: 'The test identifier.' }, }, } satisfies Meta; @@ -25,93 +25,109 @@ type Story = StoryObj; export const WithIcon: Story = { render: (args) => ( - - Something unexpected has happened! - + + Something unexpected has happened! + Mollit proident aliqua vel pariatur dolor cupidatat sunt. Tempus quis elit officia ero exercitation labore a. Nisi commodo nunc id et. Labore facilisis do nibh fermentum exercitation voluptate. Aute et ut est justo veniam. Ut do convallis reprehenderit qui. Consectetur nibh nibh est pariatur tempor. Qos laoreet qui labore a neque incididunt. - + ), args: { - variant: 'danger', + variant: 'destructive', }, }; export const NoIcon: Story = { render: (args) => ( - Something unexpected has happened! - + Something unexpected has happened! + Mollit proident aliqua vel pariatur dolor cupidatat sunt. Tempus quis elit officia ero exercitation labore a. Nisi commodo nunc id et. Labore facilisis do nibh fermentum exercitation voluptate. Aute et ut est justo veniam. Ut do convallis reprehenderit qui. Consectetur nibh nibh est pariatur tempor. Qos laoreet qui labore a neque incididunt. - + ), args: { - variant: 'danger', + variant: 'destructive', }, }; export const DescriptionOnly: Story = { render: (args) => ( - + Mollit proident aliqua vel pariatur dolor cupidatat sunt. Tempus quis elit officia ero exercitation labore a. Nisi commodo nunc id et. Labore facilisis do nibh fermentum exercitation voluptate. Aute et ut est justo veniam. Ut do convallis reprehenderit qui. Consectetur nibh nibh est pariatur tempor. Qos laoreet qui labore a neque incididunt. - + ), args: { - variant: 'danger', + variant: 'destructive', }, }; export const TitleOnly: Story = { render: (args) => ( - - Something unexpected has happened! + + Something unexpected has happened! ), args: { - variant: 'danger', + variant: 'destructive', }, }; -export const Info: Story = { +export const DefaultVariant: Story = { render: (args) => ( - Something you should know... - + + Something you should know... + Mollit proident aliqua vel pariatur dolor cupidatat sunt. Tempus quis elit officia ero exercitation labore a. Nisi commodo nunc id et. Labore facilisis do nibh fermentum exercitation voluptate. Aute et ut est justo veniam. Ut do convallis reprehenderit qui. Consectetur nibh nibh est pariatur tempor. Qos laoreet qui labore a neque incididunt. - + + + ), +}; + +export const DestructiveVariant: Story = { + render: (args) => ( + + + Something unexpected has happened! + + Mollit proident aliqua vel pariatur dolor cupidatat sunt. Tempus quis elit officia ero exercitation labore a. + Nisi commodo nunc id et. Labore facilisis do nibh fermentum exercitation voluptate. Aute et ut est justo veniam. + Ut do convallis reprehenderit qui. Consectetur nibh nibh est pariatur tempor. Qos laoreet qui labore a neque + incididunt. + ), args: { - variant: 'info', + variant: 'destructive', }, }; -export const Warning: Story = { +export const WarningVariant: Story = { render: (args) => ( - Proceed with caution! - + Proceed with caution! + Mollit proident aliqua vel pariatur dolor cupidatat sunt. Tempus quis elit officia ero exercitation labore a. Nisi commodo nunc id et. Labore facilisis do nibh fermentum exercitation voluptate. Aute et ut est justo veniam. Ut do convallis reprehenderit qui. Consectetur nibh nibh est pariatur tempor. Qos laoreet qui labore a neque incididunt. - + ), args: { @@ -119,16 +135,16 @@ export const Warning: Story = { }, }; -export const Success: Story = { +export const SuccessVariant: Story = { render: (args) => ( - You did it! - + You did it! + Mollit proident aliqua vel pariatur dolor cupidatat sunt. Tempus quis elit officia ero exercitation labore a. Nisi commodo nunc id et. Labore facilisis do nibh fermentum exercitation voluptate. Aute et ut est justo veniam. Ut do convallis reprehenderit qui. Consectetur nibh nibh est pariatur tempor. Qos laoreet qui labore a neque incididunt. - + ), args: { diff --git a/src/common/components/shadcn/__tests__/Alert.test.tsx b/src/common/components/shadcn/__tests__/Alert.test.tsx new file mode 100644 index 0000000..953b3d0 --- /dev/null +++ b/src/common/components/shadcn/__tests__/Alert.test.tsx @@ -0,0 +1,78 @@ +import { describe, expect, it } from 'vitest'; +import { render, screen } from 'test/test-utils'; + +import { Alert } from '../alert'; + +describe('Alert', () => { + it('should render successfully', async () => { + // ARRANGE + render(); + const alert = await screen.findByRole('alert'); + + // ASSERT + expect(alert).toBeDefined(); + }); + + it('should use custom test ID', async () => { + // ARRANGE + render(); + await screen.findByTestId('custom-testid'); + + expect(screen.queryByTestId('alert')).toBeNull(); + expect(screen.getByTestId('custom-testid')).toBeDefined(); + }); + + it('should use classes from className property', async () => { + // ARRANGE + render(); + const alert = await screen.findByRole('alert'); + + // ASSERT + expect(alert.classList).toContain('custom-class'); + }); + + it('should render the warning variant', async () => { + // ARRANGE + render(); + const alert = await screen.findByRole('alert'); + + // ASSERT + expect(alert.classList).toContain('text-warning'); + }); + + it('should render the destructive variant', async () => { + // ARRANGE + render(); + const alert = await screen.findByRole('alert'); + + // ASSERT + expect(alert.classList).toContain('text-destructive'); + }); + + it('should render the success variant', async () => { + // ARRANGE + render(); + const alert = await screen.findByRole('alert'); + + // ASSERT + expect(alert.classList).toContain('text-success'); + }); + + it('should render the default variant when variant not specified', async () => { + // ARRANGE + render(); + const alert = await screen.findByRole('alert'); + + // ASSERT + expect(alert.classList).toContain('bg-card'); + }); + + it('should have role=alert', async () => { + // ARRANGE + render(); + const alert = await screen.findByRole('alert'); + + // ASSERT + expect(alert.getAttribute('role')).toBe('alert'); + }); +}); diff --git a/src/common/components/shadcn/alert.tsx b/src/common/components/shadcn/alert.tsx new file mode 100644 index 0000000..de712c3 --- /dev/null +++ b/src/common/components/shadcn/alert.tsx @@ -0,0 +1,58 @@ +import * as React from 'react'; +import { cva, type VariantProps } from 'class-variance-authority'; + +import { cn } from 'common/utils/css'; + +const alertVariants = cva( + "group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4", + { + variants: { + variant: { + default: 'bg-card text-card-foreground', + destructive: + 'bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current', + success: 'bg-card text-success *:data-[slot=alert-description]:text-success/90 *:[svg]:text-current', + warning: 'bg-card text-warning *:data-[slot=alert-description]:text-warning/90 *:[svg]:text-current', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +); + +function Alert({ className, variant, ...props }: React.ComponentProps<'div'> & VariantProps) { + return
; +} + +function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) { + return ( +
svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3', + className, + )} + {...props} + /> + ); +} + +function AlertDescription({ className, ...props }: React.ComponentProps<'div'>) { + return ( +
+ ); +} + +function AlertAction({ className, ...props }: React.ComponentProps<'div'>) { + return
; +} + +export { Alert, AlertTitle, AlertDescription, AlertAction }; diff --git a/src/index.css b/src/index.css index ae3dfdd..4968bb1 100644 --- a/src/index.css +++ b/src/index.css @@ -21,6 +21,12 @@ --accent: oklch(0.97 0 0); --accent-foreground: oklch(0.205 0 0); --destructive: oklch(0.577 0.245 27.325); + --info: oklch(0.922 0 0 / 90%); + --info-foreground: oklch(0.205 0 0); + --success: oklch(0.448 0.119 151.328 / 90%); + --success-foreground: oklch(1 0 0); + --warning: oklch(0.666 0.179 58.318); + --warning-foreground: oklch(0.205 0 0); --border: oklch(0.922 0 0); --input: oklch(0.922 0 0); --ring: oklch(0.708 0 0); @@ -56,6 +62,12 @@ --accent: oklch(0.269 0 0); --accent-foreground: oklch(0.985 0 0); --destructive: oklch(0.704 0.191 22.216); + --info: oklch(0.922 0 0 / 90%); + --info-foreground: oklch(0.205 0 0); + --success: oklch(52.7% 0.154 150.069); + --success-foreground: oklch(1 0 0); + --warning: oklch(0.828 0.189 84.429 / 90%); + --warning-foreground: oklch(0.205 0 0); --border: oklch(1 0 0 / 10%); --input: oklch(1 0 0 / 15%); --ring: oklch(0.556 0 0); @@ -101,6 +113,12 @@ --color-secondary: var(--secondary); --color-primary-foreground: var(--primary-foreground); --color-primary: var(--primary); + --color-info: var(--info); + --color-info-foreground: var(--info-foreground); + --color-success: var(--success); + --color-success-foreground: var(--success-foreground); + --color-warning: var(--warning); + --color-warning-foreground: var(--warning-foreground); --color-popover-foreground: var(--popover-foreground); --color-popover: var(--popover); --color-card-foreground: var(--card-foreground); diff --git a/src/pages/Components/components/AlertComponents.tsx b/src/pages/Components/components/AlertComponents.tsx index 3bf5574..4c8cc70 100644 --- a/src/pages/Components/components/AlertComponents.tsx +++ b/src/pages/Components/components/AlertComponents.tsx @@ -1,11 +1,12 @@ import { ColumnDef, createColumnHelper } from '@tanstack/react-table'; +import { AlertCircleIcon, HeartHandshake } from 'lucide-react'; import { BaseComponentProps } from 'common/utils/types'; import { ComponentProperty } from '../model/components'; import Table from 'common/components/Table/Table'; import CodeSnippet from 'common/components/Text/CodeSnippet'; import Heading from 'common/components/Text/Heading'; -import Alert from 'common/components/Alert/Alert'; +import { Alert, AlertDescription, AlertTitle } from 'common/components/shadcn/alert'; /** * The `AlertComponents` React component renders a set of examples illustrating @@ -64,47 +65,42 @@ const AlertComponents = ({ className, testId = 'components-alert' }: BaseCompone Examples
- - - Uh oh! - Something unexpected has occurred. Please excuse our mess. + + + Tip + + Use a default alert for general information. This may be useful in various scenarios such as providing + helpful tips or guidance. +
- {` - - Uh oh! - - Something unexpected has occurred. Please excuse our mess. - + {` + + Tip + + Use a default alert for general information. This may be useful in various scenarios such as providing + helpful tips or guidance. + `}
- - - Something you should know... - - Mollit proident aliqua vel pariatur dolor cupidatat sunt. Tempus quis elit officia ero exercitation labore - a. Nisi commodo nunc id et. Labore facilisis do nibh fermentum exercitation voluptate. Aute et ut est - justo veniam. Ut do convallis reprehenderit qui. Consectetur nibh nibh est pariatur tempor. Qos laoreet - qui labore a neque incididunt. - + + + Uh oh! + Something unexpected has occurred. Please excuse our mess.
- {` - - Something you should know... - - Mollit proident aliqua vel pariatur dolor cupidatat sunt. Tempus quis elit officia ero - exercitation labore a. Nisi commodo nunc id et. Labore facilisis do nibh fermentum - exercitation voluptate. Aute et ut est justo veniam. Ut do convallis reprehenderit - qui. Consectetur nibh nibh est pariatur tempor. Qos laoreet qui labore a neque - incididunt. - + {` + + Uh oh! + + Something unexpected has occurred. Please excuse our mess. + `}
@@ -112,25 +108,25 @@ const AlertComponents = ({ className, testId = 'components-alert' }: BaseCompone
- Proceed with caution! - + Proceed with caution! + Mollit proident aliqua vel pariatur dolor cupidatat sunt. Tempus quis elit officia ero exercitation labore a. Nisi commodo nunc id et. Labore facilisis do nibh fermentum exercitation voluptate. Aute et ut est justo veniam. Ut do convallis reprehenderit qui. Consectetur nibh nibh est pariatur tempor. Qos laoreet qui labore a neque incididunt. - +
{` - Proceed with caution! - + Proceed with caution! + Mollit proident aliqua vel pariatur dolor cupidatat sunt. Tempus quis elit officia ero exercitation labore a. Nisi commodo nunc id et. Labore facilisis do nibh fermentum exercitation voluptate. Aute et ut est justo veniam. Ut do convallis reprehenderit qui. Consectetur nibh nibh est pariatur tempor. Qos laoreet qui labore a neque incididunt. - + `}
@@ -138,25 +134,25 @@ const AlertComponents = ({ className, testId = 'components-alert' }: BaseCompone
- You did it! - + You did it! + Mollit proident aliqua vel pariatur dolor cupidatat sunt. Tempus quis elit officia ero exercitation labore a. Nisi commodo nunc id et. Labore facilisis do nibh fermentum exercitation voluptate. Aute et ut est justo veniam. Ut do convallis reprehenderit qui. Consectetur nibh nibh est pariatur tempor. Qos laoreet qui labore a neque incididunt. - +
{` - You did it! - + You did it! + Mollit proident aliqua vel pariatur dolor cupidatat sunt. Tempus quis elit officia ero exercitation labore a. Nisi commodo nunc id et. Labore facilisis do nibh fermentum exercitation voluptate. Aute et ut est justo veniam. Ut do convallis reprehenderit qui. Consectetur nibh nibh est pariatur tempor. Qos laoreet qui labore a neque incididunt. - + `}
From c5a153edd916efefd93b3497e8ab3b0a5dd24985 Mon Sep 17 00:00:00 2001 From: Matthew Warman Date: Thu, 19 Mar 2026 08:37:26 -0400 Subject: [PATCH 2/7] Update test scripts in package.json for improved execution and coverage reporting --- README.md | 25 ++++++++++++++++++++++--- package.json | 7 ++++--- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b632000..281d3b1 100644 --- a/README.md +++ b/README.md @@ -169,14 +169,21 @@ The page will reload when source files are saved. ### `npm test` +Executes the unit tests once. See the Vitest documentation about [running tests](https://vitest.dev/guide/cli.html) for more information. + +### `npm run test:coverage` + +Executes the unit tests once, producing a code coverage report. + +### `npm run test:watch` + Launches the test runner in the interactive watch mode. -See the section about [running tests](https://vitest.dev/guide/cli.html) for more information. ### `npm run test:ci` -Executes the test runner in `CI` mode and produces a coverage report. With `CI` mode enabled, the test runner executes all tests one time and prints a summary report to the console. A code coverage report is printed to the console immediately following the test summary. +Executes the test runner in `CI` mode and produces a coverage report. With `CI` mode enabled, the test runner executes all tests one time silently and prints a summary report to the console. A code coverage report is printed to the console immediately following the test summary. -A detailed test coverage report is created in the `./coverage` directory. +A detailed test coverage report is created in the `./coverage` directory. Additional report formats, for example a JSON summary report, are produced which may be injested by external reporting tools. > **NOTE:** This is the command which should be utilized by CI/CD platforms. @@ -191,10 +198,22 @@ It correctly bundles React in production mode and optimizes the build for the be See the official guide for more information about [building for production](https://vitejs.dev/guide/build.html) and [deploying a static site](https://vitejs.dev/guide/static-deploy.html). +### `npm run format` + +Runs the Prettier static code analysis and fixes problems identified to comply with Prettier formatting rules. See `.prettierrc` and `.prettierignore`. + +### `npm run format:check` + +Runs the Prettier static code analysis and prints the results to the console. + ### `npm run lint` Runs the eslint static code analysis and prints the results to the console. +### `npm run lint:fix` + +Runs the eslint static code analysis and updates source code to fix problems. + ## `npm run storybook` Starts the [Storybook][storybook] UI. Open [http://localhost:6006](http://localhost:6006) to view it in the browser. diff --git a/package.json b/package.json index 4c45f97..ec38520 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,11 @@ "prepare": "husky", "preview": "vite preview", "storybook": "storybook dev -p 6006", - "test": "vitest", - "test:coverage": "vitest --coverage --coverage.all=false", + "test": "vitest run", + "test:coverage": "vitest run --coverage", "test:ci": "vitest run --coverage --silent", - "test:ui": "vitest --ui --coverage --silent" + "test:ui": "vitest --ui --coverage --silent", + "test:watch": "vitest" }, "dependencies": { "@fontsource-variable/noto-sans": "5.2.10", From ae390fab41fe0ceefa1d386a24bd9e96950c92f4 Mon Sep 17 00:00:00 2001 From: Matthew Warman Date: Thu, 19 Mar 2026 09:16:34 -0400 Subject: [PATCH 3/7] Update copilot instructions to clarify icon usage and component structure --- .github/copilot-instructions.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 7f0782c..45d18af 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -31,7 +31,7 @@ The React application leverages a modern technology stack to ensure optimal perf - **HTTP Client**: Axios - **Styling**: TailwindCSS - **Component Library**: shadcn/ui -- **Font Awesome**: icons +- **Icons**: Font Awesome and Lucide - **Utility Library**: Lodash - **Date Library**: date-fns - **Unit Testing**: Vitest @@ -53,7 +53,7 @@ src useGetCurrentUser.ts # API hook for fetching current user useGetCurrentUser.test.ts # Unit test for useGetCurrentUser /components - /ui # shadcn/ui components + /shadcn # shadcn/ui components button.tsx # Reusable button component from shadcn/ui input.tsx # Reusable input component from shadcn/ui label.tsx # Reusable label component from shadcn/ui @@ -142,8 +142,8 @@ package.json # Project dependencies and scripts - Use **functional components** with hooks. - Use **TypeScript** for type safety. -- Return **JSX.Element** or **false** from components. - Use arrow functions for components. +- Return JSX or `null` from components. - Use the `data-testid` attribute to assist with testing. - Use default exports for components. - Use a **testId** prop for components that need to be tested, defaulting to the component name in kebab-case. @@ -157,6 +157,7 @@ package.json # Project dependencies and scripts - Use **Tailwind CSS** for styling. - Apply base styles in `src/index.css` - Use CSS variables for theming (index.css). +- Use class-variance-authority (CVA) for reusable component styles and variants, see: `src/common/utils/css.ts`. ### Configuration @@ -195,8 +196,9 @@ package.json # Project dependencies and scripts After installing shadcn/ui: -- Reusable UI components like `