Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions .storybook/decorators.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import type { Decorator } from '@storybook/react-vite';
import { MemoryRouter } from 'react-router-dom';
import DataTableContext, {
DataTableContextType,
} from '../src/templates/Dataset/DataTableContext';
import {
DataTableActionsContext,
DataTableActionsContextProps,
} from '../src/components/DatasetTableTab/DataTableActionsContext';
import { mockResource } from '../__mocks__/mockResource';
import { mockDistribution } from '../__mocks__/mockDistribution';

export const defaultDataTableContext: DataTableContextType = {
id: 'wb6u-x2ny',
resource: mockResource,
distribution: mockDistribution,
rootUrl: '/api/1',
customColumns: [],
dataDictionaryBanner: false,
datasetTableControls: false,
enableEmptyFilters: false,
relativeHomeUrlPrepend: '',
};

export const defaultDataTableActionsContext: DataTableActionsContextProps = {
columnOrder: mockResource.columns ?? [],
setColumnOrder: () => {},
columnVisibility: {},
setColumnVisibility: () => {},
page: 1,
setPage: () => {},
tableDensity: 'normal',
setTableDensity: () => {},
};

/**
* Wraps a story in MemoryRouter + DataTableContext + DataTableActionsContext.
* Overrides are shallow-merged on top of the defaults.
*/
export const withDataTableContexts =
(
contextOverrides: Partial<DataTableContextType> = {},
actionsOverrides: Partial<DataTableActionsContextProps> = {}
): Decorator =>
(Story) => (
<MemoryRouter>
<DataTableContext.Provider value={{ ...defaultDataTableContext, ...contextOverrides }}>
<DataTableActionsContext.Provider
value={{ ...defaultDataTableActionsContext, ...actionsOverrides }}
>
<Story />
</DataTableActionsContext.Provider>
</DataTableContext.Provider>
</MemoryRouter>
);

/**
* Same as `withDataTableContexts` but pulls per-story overrides from `args.contextOverride`
* and `args.actionsOverride`. Use this when a story needs to vary the contexts per-instance
* (e.g. Loading / Empty / Error variants). Define the args as `control: false` so they
* don't pollute the docs table.
*/
export const withDataTableContextsFromArgs: Decorator = (Story, context) => {
const args = context.args as {
contextOverride?: Partial<DataTableContextType>;
actionsOverride?: Partial<DataTableActionsContextProps>;
};
return (
<MemoryRouter>
<DataTableContext.Provider value={{ ...defaultDataTableContext, ...args.contextOverride }}>
<DataTableActionsContext.Provider
value={{ ...defaultDataTableActionsContext, ...args.actionsOverride }}
>
<Story />
</DataTableActionsContext.Provider>
</DataTableContext.Provider>
</MemoryRouter>
);
};
73 changes: 73 additions & 0 deletions .storybook/fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import type { NavLinkArray, OrgType, FAQItemType } from '../src/types/misc';
import cmsLogo from '../src/assets/images/CMSgov@2x-white-O.png';

export const cmsOrg: OrgType = {
url: 'https://www.cms.gov',
tagline: 'The Centers for Medicare & Medicaid Services',
urlTitle: 'CMS Open Data',
logoAltText: 'CMS Logo',
};

export const cmsOrgWithLogo: OrgType = {
...cmsOrg,
logoFilePath: cmsLogo,
};

export const mainNavLinks: NavLinkArray[] = [
{ id: 'home', label: 'Home', url: '/' },
{ id: 'datasets', label: 'Datasets', url: '/datasets' },
{ id: 'api', label: 'API Documentation', url: '/api-docs' },
{ id: 'about', label: 'About', url: '/about' },
];

export const navLinksWithSubmenus: NavLinkArray[] = [
{ id: 'home', label: 'Home', url: '/' },
{ id: 'datasets', label: 'Datasets', url: '/datasets' },
{
id: 'resources',
label: 'Resources',
url: '/resources',
submenu: [
{ id: 'api', label: 'API Documentation', url: '/api-docs' },
{ id: 'dictionary', label: 'Data Dictionary', url: '/data-dictionary' },
{ id: 'guides', label: 'User Guides', url: '/guides' },
],
},
{
id: 'about',
label: 'About',
url: '/about',
submenu: [
{ id: 'mission', label: 'Our Mission', url: '/about/mission' },
{ id: 'team', label: 'Team', url: '/about/team' },
{ id: 'contact', label: 'Contact Us', url: '/about/contact' },
],
},
];

export const topNavLinks: NavLinkArray[] = [
{ id: 'cms-main', label: 'CMS.gov', url: 'https://www.cms.gov', target: '_blank' },
{ id: 'medicare', label: 'Medicare.gov', url: 'https://www.medicare.gov', target: '_blank' },
{ id: 'medicaid', label: 'Medicaid.gov', url: 'https://www.medicaid.gov', target: '_blank' },
];

export const sampleFaqs: FAQItemType[] = [
{
id: 'faq1',
title: 'What is Open Data?',
body: 'Open data is data that can be freely used, re-used, and redistributed by anyone.',
open: false,
},
{
id: 'faq2',
title: 'How do I access datasets?',
body: 'You can access datasets via the search or browse features on our site.',
open: false,
},
{
id: 'faq3',
title: 'Who maintains the data?',
body: 'The Open Data team maintains and updates the datasets regularly.',
open: false,
},
];
33 changes: 33 additions & 0 deletions .storybook/global.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
declare module '*.css' {
const css: string;
export default css;
}

declare module '*.css?inline' {
const css: string;
export default css;
}

declare module '@cmsgov/design-system/css/index.css';
declare module '@fortawesome/fontawesome-free/css/all.css';
declare module './font-awesome-overrides.css';

declare module '@cmsgov/design-system/css/core-theme.css?inline' {
const css: string;
export default css;
}

declare module '@cmsgov/ds-healthcare-gov/css/healthcare-theme.css?inline' {
const css: string;
export default css;
}

declare module '@cmsgov/ds-medicare-gov/css/medicare-theme.css?inline' {
const css: string;
export default css;
}

declare module '@cmsgov/ds-cms-gov/css/cmsgov-theme.css?inline' {
const css: string;
export default css;
}
16 changes: 16 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@ const config: StorybookConfig = {
jsxRuntime: 'automatic',
}),
],
build: {
...config.build,
rollupOptions: {
...config.build?.rollupOptions,
output: {
...(Array.isArray(config.build?.rollupOptions?.output)
? {}
: config.build?.rollupOptions?.output),
manualChunks: (id: string) => {
if (id.includes('node_modules/swagger-ui') || id.includes('node_modules/swagger-client') || id.includes('@civicactions/swagger-ui-layout')) {
return 'swagger-ui';
}
},
},
},
},
};
}
};
Expand Down
20 changes: 17 additions & 3 deletions .storybook/mswHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,26 @@ export const createStoredQueryPageHandlers = (

/**
* Creates MSW handlers for DatasetList stories.
* Mocks the search API endpoint.
* Mocks the search API endpoint. Respects `page-size` and `page` query params
* so the DatasetListSubmenu (page-size=4) and paginated DatasetList views
* render only the slice they asked for; `total` is preserved so "View all N"
* links and result counts still reflect the full dataset.
*/
export const createDatasetListHandlers = (searchResults: SearchResults) => [
http.get('**/search/*', async () => {
http.get('**/search/*', async ({ request }) => {
await delay(500);
return HttpResponse.json(searchResults);
const url = new URL(request.url);
const pageSize = parseInt(url.searchParams.get('page-size') ?? '', 10);
const page = parseInt(url.searchParams.get('page') ?? '', 10);
const entries = Object.entries(searchResults.results);

if (!Number.isFinite(pageSize) || pageSize <= 0 || entries.length === 0) {
return HttpResponse.json(searchResults);
}

const offset = Number.isFinite(page) && page > 1 ? (page - 1) * pageSize : 0;
const sliced = Object.fromEntries(entries.slice(offset, offset + pageSize));
return HttpResponse.json({ ...searchResults, results: sliced });
}),
];

Expand Down
Loading