-
Notifications
You must be signed in to change notification settings - Fork 0
Shared Component Library Plan
This document outlines the strategy for consolidating duplicate React components across multiple NCI applications into a unified shared component library. .
This repository serves as the canonical source for all shared React components. Some components will integrate with NCIDS styling while others may be framework-agnostic with custom styling.
| Component | clinical-trials-search-app | cgov-react-app-playground | r4r-app | glossary-app |
|---|---|---|---|---|
| Accordion | ✓ (class-based) | ✓ (hooks) | - | - |
| Autocomplete | ✓ | ✓ | - | - |
| Checkbox | ✓ | ✓ | - | - |
| Dropdown | ✓ | ✓ | - | - |
| Radio | ✓ | ✓ | - | - |
| TextInput | ✓ | ✓ | - | - |
| Modal | ✓ | - | - | - |
| Pager | ✓ | ✓ | - | - |
| Spinner | ✓ | - | ✓ | - |
| ErrorBoundary | ✓ | ✓ | ✓ | - |
| Table | ✓ | - | - | - |
| Toggle | ✓ | - | - | - |
| SearchBar | - | - | ✓ | - |
| Filters | - | - | ✓ | - |
- Implementation Divergence: Same components use different patterns (class vs. functional, different prop APIs)
- Style Inconsistencies: SCSS varies between implementations
- Test Coverage Gaps: Test quality varies significantly
- Version Drift: React and dependency versions differ across projects
- No Single Source of Truth: No canonical implementation exists
Create a new independent repository (nci-react-components) with its own versioning, release cycle, and governance. This approach provides:
Rationale:
- Independence from NCIDS release cycle
- Flexibility to include non-NCIDS components
- Clear ownership and governance
- Dedicated documentation and Storybook
- Simpler adoption path for consuming applications
Components will be organized into two categories based on their styling approach:
| Category | Description | Styling Approach | Examples |
|---|---|---|---|
| NCIDS Components | Components that use NCIDS design system | Peer dependency on @nciocpl/ncids-css
|
Button, Accordion, Modal |
| Core Components | Framework-agnostic components | Self-contained CSS/SCSS | Spinner, ErrorBoundary, Autocomplete |
nci-react-components/
├── .github/
│ ├── workflows/
│ │ ├── ci.yml # Test, lint, build on PR
│ │ ├── release.yml # Publish to npm
│ │ └── storybook-deploy.yml # Deploy docs
│ ├── ISSUE_TEMPLATE/
│ └── PULL_REQUEST_TEMPLATE.md
├── .storybook/
│ ├── main.js
│ ├── preview.js
│ └── manager.js
├── src/
│ ├── components/
│ │ ├── core/ # Non-NCIDS components
│ │ │ ├── Spinner/
│ │ │ │ ├── Spinner.jsx
│ │ │ │ ├── Spinner.module.scss
│ │ │ │ ├── Spinner.test.jsx
│ │ │ │ ├── Spinner.stories.jsx
│ │ │ │ └── index.js
│ │ │ ├── ErrorBoundary/
│ │ │ ├── Autocomplete/
│ │ │ └── RemovableTag/
│ │ └── ncids/ # NCIDS-styled components
│ │ ├── Button/
│ │ │ ├── Button.jsx
│ │ │ ├── Button.test.jsx
│ │ │ ├── Button.stories.jsx
│ │ │ └── index.js
│ │ ├── Accordion/
│ │ ├── Checkbox/
│ │ ├── Radio/
│ │ ├── TextInput/
│ │ ├── Dropdown/
│ │ ├── Modal/
│ │ ├── Pager/
│ │ ├── Table/
│ │ └── Toggle/
│ ├── hooks/
│ │ ├── useClickOutside.js
│ │ ├── useDebounce.js
│ │ ├── useFocusTrap.js
│ │ ├── useAnalytics.js
│ │ └── index.js
│ ├── utils/
│ │ ├── classNames.js
│ │ ├── a11y.js
│ │ └── index.js
│ └── index.js # Main entry point
├── docs/
│ ├── getting-started.md
│ ├── contributing.md
│ ├── migration-guide.md
│ └── theming.md
├── scripts/
│ └── generate-component.js # Component scaffolding
├── package.json
├── babel.config.js
├── rollup.config.js
├── jest.config.js
├── .eslintrc.js
├── .prettierrc
├── CHANGELOG.md
├── LICENSE
└── README.md
| Decision | Choice | Rationale |
|---|---|---|
| Language | JavaScript (ES6+) | Consistency with existing apps, lower barrier to contribution |
| Type Hints | JSDoc annotations | IDE support without build step, optional type checking |
| Component Style | Functional + Hooks | Modern React patterns, easier testing |
| Styling (Core) | CSS Modules + SCSS | Scoped styles, no conflicts |
| Styling (NCIDS) | NCIDS CSS classes | Consistency with design system |
| Build | Rollup | Tree-shaking, ESM + CJS outputs |
| Documentation | Storybook 8.x | Interactive docs, accessibility testing |
| Testing | Jest + Testing Library | Industry standard |
| Linting | ESLint + Prettier | Code consistency |
| Versioning | Changesets | Semantic versioning with changelogs |
{
"name": "@nciocpl/react-components",
"version": "1.0.0",
"description": "Shared React component library for NCI applications",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"sideEffects": ["**/*.css", "**/*.scss"],
"exports": {
".": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js"
},
"./core": {
"import": "./dist/esm/components/core/index.js",
"require": "./dist/cjs/components/core/index.js"
},
"./ncids": {
"import": "./dist/esm/components/ncids/index.js",
"require": "./dist/cjs/components/ncids/index.js"
},
"./styles": "./dist/styles/index.css"
},
"peerDependencies": {
"react": ">=16.14.0",
"react-dom": ">=16.14.0"
},
"peerDependenciesMeta": {
"@nciocpl/ncids-css": {
"optional": true
}
},
"optionalDependencies": {
"@nciocpl/ncids-css": "^3.0.0"
}
}Set up the new repository with all infrastructure, tooling, and first batch of core components.
Implement components that integrate with NCIDS design system.
Extract and standardize shared hooks and utility functions
Implement non-NCIDS components that have custom styling.
Finalize documentation.
Update applications to use the shared library.
- Props should be intuitive and self-documenting
- Support controlled and uncontrolled modes where applicable
- Follow ARIA authoring practices for accessibility
- Expose
refforwarding viaforwardRef - Use composition over configuration
- Avoid breaking changes; use deprecation warnings
For Core Components:
// CSS Modules for scoped styling
import styles from './Spinner.module.scss';
export const Spinner = ({ size = 'medium' }) => (
<div className={styles[`spinner--${size}`]} />
);For NCIDS Components:
// Use NCIDS class names, styles come from ncids-css
import { classNames } from '../../utils';
export const Button = ({ variant = 'primary', children }) => (
<button className={classNames('usa-button', `usa-button--${variant}`)}>
{children}
</button>
);All components must:
- Support keyboard navigation
- Include proper ARIA attributes
- Maintain focus management
- Support screen readers