From ce8df685dd26f7939b8a028ef632231752bf382a Mon Sep 17 00:00:00 2001 From: adriancofie <38888889+adriancofie@users.noreply.github.com> Date: Mon, 27 Apr 2026 09:51:02 -0400 Subject: [PATCH 1/3] (#67) Fix package distribution issues from integration review Address findings from downstream integration of v0.1.0: - Subpath exports (./core, ./ncids): switch rollup to multi-entry input so the documented subpath bundles and matching .d.ts files are actually emitted. - Bundled stylesheet (./styles): the export now resolves to a real compiled CSS file (uswds-global, usa-pagination, usa-icon, usa-collection). README documents the import and the uswds-img sprite setup for Vite and Webpack consumers. - React peer raised to >=17.0.0. The build emits react/jsx-runtime, which only exists on React >=16.14, and React 16 has been EOL since 2024. - @nciocpl/ncids-css declared as an optional peer (>=3.0.0) so the existing peerDependenciesMeta.optional entry has effect. - Build hygiene: eliminate TS5069 declarationMap warning by overriding declarationMap:false alongside declaration:false in rollup-plugin- typescript options. Closes #67 --- .changeset/fix-package-exports-and-styles.md | 10 ++++ README.md | 48 +++++++++++++++----- package.json | 9 ++-- rollup.config.js | 37 +++++---------- src/styles/index.scss | 19 ++++++++ 5 files changed, 83 insertions(+), 40 deletions(-) create mode 100644 .changeset/fix-package-exports-and-styles.md create mode 100644 src/styles/index.scss diff --git a/.changeset/fix-package-exports-and-styles.md b/.changeset/fix-package-exports-and-styles.md new file mode 100644 index 0000000..3d91509 --- /dev/null +++ b/.changeset/fix-package-exports-and-styles.md @@ -0,0 +1,10 @@ +--- +'@nciocpl/react-components': minor +--- + +Fix package distribution issues surfaced during downstream integration: + +- **Subpath exports** (`@nciocpl/react-components/core`, `@nciocpl/react-components/ncids`): rollup now uses multi-entry input so the documented subpath bundles (and matching type declarations) are actually emitted. +- **Bundled stylesheet** (`@nciocpl/react-components/styles`): the export now resolves to a real compiled CSS file (USWDS global, `usa-pagination`, `usa-icon`, `usa-collection`). Consumers that don't already compile NCIDS SCSS can `import '@nciocpl/react-components/styles'` and serve `node_modules/@nciocpl/ncids-css/uswds-img` at `/img`. README updated with Vite/Webpack recipes. +- **React peer dependency** raised to `>=17.0.0`. The build emits `react/jsx-runtime` imports (modern JSX transform), which only exist on React ≥ 16.14 — and React 16 has been EOL since 2024. Setting the floor at 17 prevents cryptic `Can't resolve 'react/jsx-runtime'` errors on outdated React installations. +- **`@nciocpl/ncids-css` peer dependency** is now declared (`>=3.0.0`, optional) so the existing `peerDependenciesMeta.optional` entry has effect. diff --git a/README.md b/README.md index 8096c5a..58b3c5b 100644 --- a/README.md +++ b/README.md @@ -13,29 +13,55 @@ This library provides a canonical set of reusable React components used across N - **Core Components** -- Framework-agnostic components with self-contained styling (Spinner, ErrorBoundary, Autocomplete, etc.) - **NCIDS Components** -- Components that integrate with the [NCI Design System](https://designsystem.cancer.gov/) (Button, Accordion, Modal, etc.) +## Requirements + +- React `>=17.0.0` and React DOM `>=17.0.0` (the build uses the modern `react/jsx-runtime` transform). +- `@nciocpl/ncids-css` `>=3.0.0` is required to use any NCIDS component (Pager, Collection, etc.). It is declared as an optional peer so apps that only use core components are not forced to install it. + ## Installation ```bash -pnpm add @nciocpl/react-components +pnpm add @nciocpl/react-components @nciocpl/ncids-css ``` -For NCIDS-styled components, also install the NCIDS CSS package: +## Setup -```bash -pnpm add @nciocpl/ncids-css +NCIDS components rely on USWDS CSS classes. The library ships a pre-bundled stylesheet that compiles the relevant NCIDS/USWDS modules. Import it once at your app entry: + +```ts +// e.g. src/main.tsx +import '@nciocpl/react-components/styles'; +``` + +The bundled CSS resolves USWDS sprite/font URLs against `/img`. Configure your app to serve `node_modules/@nciocpl/ncids-css/uswds-img` at that path. + +**Vite (vite.config.ts):** + +```ts +import { defineConfig } from 'vite'; +import path from 'node:path'; + +export default defineConfig({ + publicDir: path.resolve( + __dirname, + 'node_modules/@nciocpl/ncids-css/uswds-img' + ), + // ...or copy the directory to your existing public/ as `public/img` +}); ``` +**Webpack / Create React App:** copy `node_modules/@nciocpl/ncids-css/uswds-img` into your `public/img` folder (e.g. via `copy-webpack-plugin` or a postinstall script). + +If your app already compiles NCIDS SCSS for non-React UI, skip the bundled stylesheet — the components render with whatever NCIDS rules you have loaded. + ## Usage ```tsx -// Import all components -import { Spinner, ErrorBoundary } from '@nciocpl/react-components'; - -// Import only core components (smaller bundle) -import { Spinner } from '@nciocpl/react-components/core'; +// Import from the package root +import { Pager, Collection, CollectionItem } from '@nciocpl/react-components'; -// Import only NCIDS components -import { Accordion } from '@nciocpl/react-components/ncids'; +// Or scope imports to a category +import { Pager } from '@nciocpl/react-components/ncids'; ``` ## Development diff --git a/package.json b/package.json index 53ba2b9..345dbf0 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,9 @@ "registry": "https://npm.pkg.github.com" }, "scripts": { - "build": "rollup -c", + "build": "pnpm build:js && pnpm build:styles", + "build:js": "rollup -c", + "build:styles": "sass src/styles/index.scss dist/styles/index.css --load-path=node_modules/@nciocpl/ncids-css/packages --load-path=node_modules/@nciocpl/ncids-css/uswds-packages --no-source-map --style=compressed", "dev": "storybook dev -p 6006", "test": "vitest run", "test:watch": "vitest", @@ -70,8 +72,9 @@ }, "homepage": "https://github.com/NCIOCPL/react-app-shared#readme", "peerDependencies": { - "react": ">=16.14.0", - "react-dom": ">=16.14.0" + "@nciocpl/ncids-css": ">=3.0.0", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" }, "peerDependenciesMeta": { "@nciocpl/ncids-css": { diff --git a/rollup.config.js b/rollup.config.js index 1eb8339..a30918e 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -7,6 +7,12 @@ import postcssModules from 'postcss-modules'; const external = ['react', 'react-dom', 'react/jsx-runtime']; +const entries = { + index: 'src/index.ts', + 'components/core/index': 'src/components/core/index.ts', + 'components/ncids/index': 'src/components/ncids/index.ts', +}; + function scssModules() { const cssModulesExports = {}; return scss({ @@ -27,7 +33,7 @@ function scssModules() { export default [ // ESM build { - input: 'src/index.ts', + input: entries, output: { dir: 'dist/esm', format: 'esm', @@ -42,6 +48,7 @@ export default [ tsconfig: './tsconfig.build.json', outDir: 'dist/esm', declaration: false, + declarationMap: false, declarationDir: undefined, }), scssModules(), @@ -50,7 +57,7 @@ export default [ }, // CJS build { - input: 'src/index.ts', + input: entries, output: { dir: 'dist/cjs', format: 'cjs', @@ -65,6 +72,7 @@ export default [ tsconfig: './tsconfig.build.json', outDir: 'dist/cjs', declaration: false, + declarationMap: false, declarationDir: undefined, }), scssModules(), @@ -73,7 +81,7 @@ export default [ }, // Type declarations { - input: 'src/index.ts', + input: entries, output: { dir: 'dist/types', format: 'esm', @@ -83,27 +91,4 @@ export default [ plugins: [dts()], external: [/\.scss$/, /\.css$/], }, - // Standalone CSS bundle - { - input: 'src/index.ts', - output: { - dir: 'dist/styles', - format: 'esm', - assetFileNames: '[name][extname]', - }, - plugins: [ - resolve(), - commonjs(), - typescript({ - tsconfig: './tsconfig.build.json', - outDir: 'dist/styles', - declaration: false, - declarationDir: undefined, - }), - scss({ - output: 'dist/styles/index.css', - }), - ], - external, - }, ]; diff --git a/src/styles/index.scss b/src/styles/index.scss new file mode 100644 index 0000000..3082717 --- /dev/null +++ b/src/styles/index.scss @@ -0,0 +1,19 @@ +// Bundled NCIDS/USWDS styles for components shipped by this library. +// +// Consumers that don't already compile NCIDS SCSS can: +// import '@nciocpl/react-components/styles'; +// +// The bundle assumes the USWDS image sprite is served at /img. Map +// node_modules/@nciocpl/ncids-css/uswds-img to /img in your app's static +// asset pipeline (see README "Setup" for the standard recipes). + +@use 'uswds-core' with ( + $theme-image-path: '/img', + $theme-show-notifications: false, + $theme-show-compile-warnings: false +); + +@forward 'uswds-global'; +@forward 'usa-pagination'; +@forward 'usa-icon'; +@forward 'usa-collection'; From 4c6026079216af739a89bd54a1b6dec4178e09fa Mon Sep 17 00:00:00 2001 From: Olson Date: Mon, 13 Apr 2026 17:00:07 -0400 Subject: [PATCH 2/3] (#28) Implement Dropdown/Select component * Adds Dropdown and Dropdown Option components with NCIDS usa-select classes * Support configurable options * Includes unit tests * Adds Storybook documentation Closes #28 --- .storybook/preview.scss | 2 + .../ncids/Dropdown/Dropdown.stories.tsx | 57 ++++++++++++ .../ncids/Dropdown/Dropdown.test.tsx | 91 +++++++++++++++++++ src/components/ncids/Dropdown/Dropdown.tsx | 67 ++++++++++++++ src/components/ncids/Dropdown/index.ts | 2 + src/components/ncids/index.ts | 2 + 6 files changed, 221 insertions(+) create mode 100644 src/components/ncids/Dropdown/Dropdown.stories.tsx create mode 100644 src/components/ncids/Dropdown/Dropdown.test.tsx create mode 100644 src/components/ncids/Dropdown/Dropdown.tsx create mode 100644 src/components/ncids/Dropdown/index.ts diff --git a/.storybook/preview.scss b/.storybook/preview.scss index 92e0b24..af60bd1 100644 --- a/.storybook/preview.scss +++ b/.storybook/preview.scss @@ -8,3 +8,5 @@ @forward 'usa-pagination'; @forward 'usa-icon'; @forward 'usa-collection'; +@forward 'usa-select'; +@forward 'usa-combo-box'; diff --git a/src/components/ncids/Dropdown/Dropdown.stories.tsx b/src/components/ncids/Dropdown/Dropdown.stories.tsx new file mode 100644 index 0000000..30d0a2f --- /dev/null +++ b/src/components/ncids/Dropdown/Dropdown.stories.tsx @@ -0,0 +1,57 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { fn } from '@storybook/test'; +import React from 'react'; + +import { Dropdown } from './Dropdown'; + +const meta: Meta = { + title: 'NCIDS/Dropdown', + component: Dropdown, + tags: ['autodocs'], + argTypes: { + className: { + control: 'text', + description: 'Additional CSS classes on the list', + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + id: 'default-dropdown', + name: 'default-dropdown', + options: [ + { label: '20', value: 20 }, + { label: '50', value: 50 }, + { label: '100', value: 100 }, + ], + onChange: fn(), + }, +}; + +export const WithResultsPerPageText: Story = { + name: 'With Results Per Page Text', + render: (args) => { + return ( +
+ Show + + results per page +
+ ); + }, + args: { + id: 'results-per-page', + name: 'results-per-page', + ariaLabel: 'Select option', + options: [ + { label: '20', value: 20 }, + { label: '50', value: 50 }, + { label: '100', value: 100 }, + ], + onChange: fn(), + }, +}; diff --git a/src/components/ncids/Dropdown/Dropdown.test.tsx b/src/components/ncids/Dropdown/Dropdown.test.tsx new file mode 100644 index 0000000..8c122f7 --- /dev/null +++ b/src/components/ncids/Dropdown/Dropdown.test.tsx @@ -0,0 +1,91 @@ +import React from 'react'; +import { cleanup, render, screen } from '@testing-library/react'; +import { axe } from 'vitest-axe'; +import { afterEach, describe, expect, it, vi } from 'vitest'; +import userEvent from '@testing-library/user-event'; + +import { Dropdown } from './Dropdown'; + +describe('', () => { + afterEach(() => { + cleanup(); + }); + + it('should render options', () => { + render( + + ); + + expect(screen.getByRole('option', { name: '20' })).toBeInTheDocument(); + expect(screen.getByRole('option', { name: '30' })).toBeInTheDocument(); + expect(screen.getByRole('option', { name: '50' })).toBeInTheDocument(); + expect(screen.getByRole('option', { name: '100' })).toBeInTheDocument(); + }); + + it('should render a select with usa-select class', () => { + const { container } = render( + + ); + const select = container.querySelector('select'); + expect(select).toHaveClass('usa-select'); + }); + + it('should call onChange when selection changes', async () => { + const user = userEvent.setup(); + const handleChange = vi.fn(); + + render( + + ); + + const select = screen.getByRole('combobox'); + await user.selectOptions(select, '20'); + + expect(handleChange).toHaveBeenCalledTimes(1); + }); + + it('should have no accessibility violations', async () => { + const handleChange = vi.fn(); + const { container } = render( + + ); + const results = await axe(container); + expect(results).toHaveNoViolations(); + }); +}); diff --git a/src/components/ncids/Dropdown/Dropdown.tsx b/src/components/ncids/Dropdown/Dropdown.tsx new file mode 100644 index 0000000..1764223 --- /dev/null +++ b/src/components/ncids/Dropdown/Dropdown.tsx @@ -0,0 +1,67 @@ +import React from 'react'; + +export interface DropdownOptionProps { + /** Option label */ + label: string; + /** Additional CSS classes on the