Skip to content

Shared Component Library Plan

adriancofie edited this page Jan 21, 2026 · 1 revision

Shared Component Library Plan

Summary

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.


Current State Analysis

Identified Duplicate Components

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 - - -

Key Issues

  1. Implementation Divergence: Same components use different patterns (class vs. functional, different prop APIs)
  2. Style Inconsistencies: SCSS varies between implementations
  3. Test Coverage Gaps: Test quality varies significantly
  4. Version Drift: React and dependency versions differ across projects
  5. No Single Source of Truth: No canonical implementation exists

Recommended Approach

Strategy: New Standalone Repository

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

Component Categories

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

Repository Architecture

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

Technology Decisions

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

Package.json Structure

{
  "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"
  }
}

Implementation Phases

Phase 1: Repository Setup & Foundation

Set up the new repository with all infrastructure, tooling, and first batch of core components.

Phase 2: NCIDS Components

Implement components that integrate with NCIDS design system.

Phase 3: Hooks & Utilities

Extract and standardize shared hooks and utility functions

Phase 4: Non-NCIDS Components

Implement non-NCIDS components that have custom styling.

Phase 5: Documentation

Finalize documentation.

Phase 2-5: Application Migration

Update applications to use the shared library.


Component Design Principles

API Design

  • Props should be intuitive and self-documenting
  • Support controlled and uncontrolled modes where applicable
  • Follow ARIA authoring practices for accessibility
  • Expose ref forwarding via forwardRef
  • Use composition over configuration
  • Avoid breaking changes; use deprecation warnings

Styling Strategy

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>
);

Accessibility Requirements

All components must:

  • Support keyboard navigation
  • Include proper ARIA attributes
  • Maintain focus management
  • Support screen readers