diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..537a3fc27c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,27 @@ +React component library for [Cloudscape Design System](https://cloudscape.design/) — an open source design system for building accessible, inclusive web experiences at scale. + +## Setup + +``` +npm install +``` + +## Building + +- `npm run quick-build` — fast dev build (compiles TS, SCSS, generates icons/i18n/test-utils). Use this for local development. +- `npm run build` — full production build (quick-build + dev pages, theming, docs, size-limit). Use this before publishing or to verify everything works end-to-end. +- `npm run build:react18` — production build targeting React 18. + +## Running Locally + +``` +npm run start # starts watcher + dev server for development pages +npm run start:react18 # starts watcher + dev server (React 18) +``` + +The dev server runs at `http://localhost:8080`. Pages are served from `pages//`. + +## Docs Index + +See [docs/CLOUDSCAPE_COMPONENTS_GUIDE.md](docs/CLOUDSCAPE_COMPONENTS_GUIDE.md) for guides on component conventions, styling, writing tests, and more. +For running tests and configs, see docs/RUNNING_TESTS.md. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 01f9daa99f..e5086486a4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,38 +4,9 @@ Use this repository to [create bug reports or feature requests](https://github.com/cloudscape-design/components/issues/new/choose). You can also [start a discussion](https://github.com/cloudscape-design/components/discussions) to ask a question. We will do our best to reply. To minimize duplicates, we recommend that you search for existing bug reports, feature requests, or discussions before initiating a new thread. -## Versioning +## Documentation -We release patch versions on a daily basis to release bug fixes and new features and components. Patch versions do not contain breaking changes. - -### Public APIs - -Our public API consists of - -- [Components APIs](https://cloudscape.design/components) (properties, slots, events, functions) -- [Test utilities](https://cloudscape.design/get-started/testing/introduction/) -- Typescript definitions -- [Design tokens](https://cloudscape.design/foundation/visual-foundation/design-tokens) - -The inner HTML structure and class names of our components are not part of our APIs. Modifications to those are not considered breaking changes. - -## Frameworks support - -We support - -- React 16.8+ -- Jest 25+ - -## Browsers support - -We support the latest 3 *major* versions of these browsers for desktop: -- Google Chrome -- Mozilla Firefox -- Microsoft Edge - -and the latest three *minor* versions of Apple Safari for macOS for desktop. - -We do not support Microsoft Internet Explorer or mobile browsers. We support all viewport sizes across desktop browsers. +For detailed guides on component conventions, styling, writing tests, supported platforms, and more, see the [General Guide](docs/GENERAL_GUIDE.md). ## How to contribute code @@ -82,163 +53,26 @@ Then, start the dev-server by running: npm start ``` -This will open a webpack-dev-server at http://localhost:8080 and watch -for file changes. +This will open a webpack-dev-server at http://localhost:8080 and watch for file changes. ### Quick rebuild -To quickly rebuild this package, run `npm run quick-build`. This runs only the source code build, skipping -documentation and screenshot testing builds. +To quickly rebuild this package, run `npm run quick-build`. This runs only the source code build, skipping documentation and screenshot testing builds. ### Running tests -The package contains three types of tests: - -- **Build-tool tests** test the build-tools code in a NodeJS context. -- **Unit tests** emulate a browser environment using JSDOM. -- **Integration tests** test against real browser behavior on Chrome, with motion disabled. -- **Motion tests** run a specific set of tests on Chrome, with motion enabled. - -#### Run all tests: - -``` -npm test -``` - -#### Run build tool and unit tests: - ``` -npm run test:unit +npm test # all tests +npm run test:unit # unit + build-tool tests +npm run test:integ # integration tests +npm run test:motion # motion tests ``` -#### Run integration tests: - -If you're running integration tests on a Mac, make sure you have ChromeDriver: - -``` -npm i -g chromedriver -``` - -Then, run the dev server and integration tests in separate terminals: - -``` -npm start -``` - -``` -npm run test:integ -``` - -#### Run motion tests: - -As in integration tests, make sure you have ChromeDriver installed and start the dev server: - -``` -npm i -g chromedriver -npm start -``` - -Then, run the motion tests in a separate terminal: - -``` -npm test:motion -``` - -#### Run a single test suite - -To run a single test suite, you can call Jest directly using the appropriate config: - -``` -# Run a single button unit test suite -npx jest -c jest.unit.config.js src/button/__tests__/button.test.tsx - -# Run all input integration tests -npx jest -c jest.integ.config.js src/input - -# Run motion tests for the flashbar component -npx jest -c jest.motion.config.js src/flashbar - -# Test all stylelint rules -npx jest -c jest.build-tools.config.js build-tools/stylelint -``` - -Note: when running jest directly you may see errors about `--experimental-vm-modules`, to fix this you can set this NodeJS flag as follows: - -``` -export NODE_OPTIONS="$NODE_OPTIONS --experimental-vm-modules" -``` - -Alternatively, you can set the flag inline with the command: - -``` -# Run a single integration test file -NODE_OPTIONS='--experimental-vm-modules' npx jest -c jest.integ.config.js src/input/__integ__/input.test.ts -``` - -#### Updating all snapshots - -When component APIs change, you may need to update test snapshots. Use the `-u` flag to update snapshots: - -``` -npx jest -u snapshot -c jest.unit.config.js src/ -``` - -### Run visual regression tests - -Visual regression tests for the permutation pages are automatically run when opening a pull request in GitHub. - -#### Checking results in a pull requests - -To look at the results of the tests, check the details of the "Visual Regression Tests" action in the pull request. -The logs of the "Test for regressions" step should indicate what pages failed the regression tests. - -To check the full report in a browser, go to the action summary and download the `visual-regression-results` artifacts. -Unzip the downloaded archive and open the `html_report/index.html` file in your browser. - -If there are unexpected regressions, fix your pull request. -If the changes are expected, call this out in your pull request comments. +For targeting specific files, updating snapshots, ChromeDriver setup, and visual regression tests, see [docs/RUNNING_TESTS.md](docs/RUNNING_TESTS.md). ### Directory layout -``` -├── __mocks__ - jest mocks for external dependencies -│ -├── build-tools - build tasks and configuration for gulp -│ -├── pages - react pages for development, scenario and permutation testing -│ └── .page.tsx -│ -├── src -│ ├── __a11y__ - global a11y tests for all components -│ ├── __integ__ - global integ tests for all components -│ ├── __tests__ - global unit tests for all components -| | -│ ├── -│ │ ├── __tests__ - jest unit tests -│ │ ├── __integ__ - jest integration tests -│ │ ├── __motion__ - jest motion tests -│ │ ├── index.tsx - main component file (imports other files and styles) -│ │ └── styles.scss - main SCSS file -| │ -| ├── test-utils - test utils for components -│ │ ├── dom - main source code for test utils -│ │ └── selectors - utils for integration testing, generated from the code in `dom` folder -| | -| └── internal - library internals -| ├── base-component - necessary declarations for every public component -| ├── components - internal utility components -| ├── events - utilities for firing public events -| ├── hooks - internal utility hooks -| └── generated - generated code from style-dictionary -| └── styles - base styles and SCSS-mixins -│ -├── lib - build output -| ├── components - the primary components package -| ├── components-definitions – generated metadata for components -| └── design-tokens - exported design tokens -| -└── style-dictionary - style dictionary tokens and roles -``` +See [docs/DIRECTORY_LAYOUT.md](docs/DIRECTORY_LAYOUT.md) for the full repo structure. ## Code of Conduct diff --git a/docs/API_DOCS.md b/docs/API_DOCS.md new file mode 100644 index 0000000000..d6bf5184c2 --- /dev/null +++ b/docs/API_DOCS.md @@ -0,0 +1,14 @@ +# API Documentation Comments + +This project uses `@cloudscape-design/documenter` to generate API docs from JSDoc comments in component interface files (`src//interfaces.ts`). + +## Special Tags + +- `@i18n` — marks internationalization properties +- `@analytics` — marks analytics metadata properties +- `@deprecated` — marks deprecated properties (include replacement info) +- `@displayname` — overrides the display name (e.g. `children` → `text`) + +## Sub-types + +Define union types as named type aliases in the component's namespace, not as inline unions. Then reference them in the interface: `variant?: ButtonProps.Variant`. diff --git a/docs/CLOUDSCAPE_COMPONENTS_GUIDE.md b/docs/CLOUDSCAPE_COMPONENTS_GUIDE.md new file mode 100644 index 0000000000..34c4edd5c3 --- /dev/null +++ b/docs/CLOUDSCAPE_COMPONENTS_GUIDE.md @@ -0,0 +1,13 @@ +# Cloudscape Components Documentation + +Reference docs for contributing to [cloudscape-design/components](https://github.com/cloudscape-design/components). + +- [General Guide](GENERAL_GUIDE.md) — browsers, frameworks, public APIs, versioning +- [Component Conventions](COMPONENT_CONVENTIONS.md) — component structure, props, events, refs +- [Styling](STYLING.md) — design tokens, CSS rules, RTL support +- [Writing Tests](WRITING_TESTS.md) — test utils, unit and integration tests +- [Running Tests](RUNNING_TESTS.md) — commands, configs, visual regression tests +- [Code Style](CODE_STYLE.md) — prettier, stylelint, eslint +- [Dev Pages](DEV_PAGES.md) — dev/test pages and running in the browser +- [API Docs](API_DOCS.md) — API documentation comments +- [Internals](INTERNALS.md) — internal shared utilities diff --git a/docs/CODE_STYLE.md b/docs/CODE_STYLE.md new file mode 100644 index 0000000000..a4cbaef445 --- /dev/null +++ b/docs/CODE_STYLE.md @@ -0,0 +1,9 @@ +# Code Style + +Run `npm run lint` to check formatting and linting. The project uses: + +- **Prettier** for code formatting +- **Stylelint** for CSS/SCSS linting +- **ESLint** for JavaScript/TypeScript linting + +See the respective config files in the project root for rules. diff --git a/docs/COMPONENT_CONVENTIONS.md b/docs/COMPONENT_CONVENTIONS.md new file mode 100644 index 0000000000..c6ca94b5a9 --- /dev/null +++ b/docs/COMPONENT_CONVENTIONS.md @@ -0,0 +1,52 @@ +# Component Conventions + +## Component Structure + +Each component is exposed from `src//index.tsx`. + +- `applyDisplayName` — readable name in consumers' devtools + +### Internal Component (`internal.tsx`) + +For components used in composition, a private counterpart lives at `internal.tsx`. The public component must not add behavior beyond what the internal component provides. + +## Props & Interfaces + +- Props interface: `${ComponentName}Props`, namespace sub-types under it (e.g. `${ComponentName}Props.Variant`) +- Union types must be type aliases (no inline unions) +- Array types must use `ReadonlyArray` +- Cast string props to string at runtime if rendered in JSX — React accepts JSX content there +- Component return type must be exactly `JSX.Element` — `null` or arrays break the doc generator + +For how to document props, see [API_DOCS.md](API_DOCS.md). + +## Events + +Use `CancelableEventHandler` or `NonCancelableEventHandler`. All `on*` props must use these interfaces (build fails otherwise). + +Events are similar to native events with `event.preventDefault()` for cancelable events, but are not dispatched via DOM. + +## Refs + +- Use `useForwardFocus(ref, elementRef)` for simple focus delegation +- For `React.forwardRef` generics, create a `${ComponentName}ForwardRefType` interface + +## Controllable Components + +Use the same behavior as built-in React components: + +1. If only `onChange` is provided → uncontrolled (component manages its own state, `onChange` fires for side effects) +2. If `value` is provided → controlled (with or without `onChange`; without `onChange` it's read-only) + +Implementation: +1. Create a controlled component first +2. Use `useControllable` to wrap customer-provided properties — gives you a `[value, setValue]` pair +3. If `value` is provided without `onChange`, `useControllable` emits a console warning + +## I18n + +Centralize all translatable strings under a skippable property (e.g. `i18nStrings`). + +## Dependencies + +Before adding any dependency: must support React 16.8+ and latest 3 major Chrome/Firefox/Edge, no global state, ESM preferred. diff --git a/docs/DEV_PAGES.md b/docs/DEV_PAGES.md new file mode 100644 index 0000000000..b730008858 --- /dev/null +++ b/docs/DEV_PAGES.md @@ -0,0 +1,7 @@ +# Dev Pages + +Dev and test pages live in `pages//`. They are used for local development, integration tests, and visual regression tests. + +## Rules +- For visual regression content, either use `SimplePage` (handles heading, screenshot area, i18n, and layout) or wrap content in `ScreenshotArea`. +- Use `createPermutations` and `PermutationsView` for permutation pages. \ No newline at end of file diff --git a/docs/DIRECTORY_LAYOUT.md b/docs/DIRECTORY_LAYOUT.md new file mode 100644 index 0000000000..d74fd6c439 --- /dev/null +++ b/docs/DIRECTORY_LAYOUT.md @@ -0,0 +1,41 @@ +# Directory Layout + +``` +├── __mocks__ - jest mocks for external dependencies +│ +├── build-tools - build tasks and configuration for gulp +│ +├── pages - react pages for development, scenario and permutation testing +│ └── .page.tsx +│ +├── src +│ ├── __a11y__ - global a11y tests for all components +│ ├── __integ__ - global integ tests for all components +│ ├── __tests__ - global unit tests for all components +│ │ +│ ├── +│ │ ├── __tests__ - jest unit tests +│ │ ├── __integ__ - jest integration tests +│ │ ├── __motion__ - jest motion tests +│ │ ├── index.tsx - main component file (imports other files and styles) +│ │ └── styles.scss - main SCSS file +│ │ +│ ├── test-utils - test utils for components +│ │ ├── dom - main source code for test utils +│ │ └── selectors - utils for integration testing, generated from the code in `dom` folder +│ │ +│ └── internal - library internals +│ ├── base-component - necessary declarations for every public component +│ ├── components - internal utility components +│ ├── events - utilities for firing public events +│ ├── hooks - internal utility hooks +│ ├── generated - generated code from style-dictionary +│ └── styles - base styles and SCSS-mixins +│ +├── lib - build output +│ ├── components - the primary components package +│ ├── components-definitions - generated metadata for components +│ └── design-tokens - exported design tokens +│ +└── style-dictionary - style dictionary tokens and roles +``` diff --git a/docs/GENERAL_GUIDE.md b/docs/GENERAL_GUIDE.md new file mode 100644 index 0000000000..d44691b85a --- /dev/null +++ b/docs/GENERAL_GUIDE.md @@ -0,0 +1,32 @@ +# General Guide + +## Frameworks + +- React 16.8+ +- Jest 25+ + +## Browsers + +Latest 3 major versions of these desktop browsers: +- Google Chrome +- Mozilla Firefox +- Microsoft Edge + +Latest 3 minor versions of Apple Safari for macOS. + +Mobile browsers and Microsoft Internet Explorer are not supported. All viewport sizes across desktop browsers are supported. + +## Public APIs + +Our public API consists of: + +- [Components APIs](https://cloudscape.design/components) (properties, slots, events, functions) +- [Test utilities](https://cloudscape.design/get-started/testing/introduction/) +- TypeScript definitions +- [Design tokens](https://cloudscape.design/foundation/visual-foundation/design-tokens) + +The inner HTML structure and class names of our components are not part of our APIs. Modifications to those are not considered breaking changes. + +## Versioning + +We release patch versions on a daily basis for bug fixes, new features, and components. Patch versions do not contain breaking changes. diff --git a/docs/INTERNALS.md b/docs/INTERNALS.md new file mode 100644 index 0000000000..899fef6022 --- /dev/null +++ b/docs/INTERNALS.md @@ -0,0 +1,3 @@ +# Internal Utilities + +Shared infrastructure lives in `src/internal/`. Always check there before introducing new utilities, hooks, or internal shared components. diff --git a/docs/RUNNING_TESTS.md b/docs/RUNNING_TESTS.md new file mode 100644 index 0000000000..38717d73e1 --- /dev/null +++ b/docs/RUNNING_TESTS.md @@ -0,0 +1,66 @@ +# Running Tests + +## Quick Reference + +``` +npm test # all tests +npm run test:unit # unit + build-tool tests +npm run test:integ # integration tests (starts dev server automatically) +npm run test:motion # motion tests (starts dev server automatically) +npm run test:a11y # accessibility tests +``` + +The npm scripts use gulp tasks that handle env vars (`TZ=UTC`, `NODE_OPTIONS=--experimental-vm-modules`) and dev server lifecycle automatically. + +## Test Types + +- **Build-tool tests** — test the build-tools code in a NodeJS context. +- **Unit tests** — emulate a browser environment using JSDOM. +- **Integration tests** — test against real browser behavior on Chrome, with motion disabled. +- **Motion tests** — run a specific set of tests on Chrome, with motion enabled. + +## Targeting Specific Files + +Call jest directly with the appropriate config: + +``` +# Unit +TZ=UTC node_modules/.bin/jest -c jest.unit.config.js src/button/__tests__/button.test.tsx + +# Integration (requires dev server running via `npm start`) +NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.integ.config.js src/input/__integ__/ + +# Motion (requires dev server running via `npm start`) +NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.motion.config.js src/flashbar/__motion__/ +``` + +> **Note:** When running jest directly you may see errors about `--experimental-vm-modules`. The npm scripts handle this automatically, but when calling jest directly you need to set the flag yourself (see examples above). + +## ChromeDriver (macOS) + +Integration and motion tests require ChromeDriver: + +``` +npm i -g chromedriver +``` + +#### Updating all snapshots + +When component APIs change, you may need to update test snapshots. Use the `-u` flag to update snapshots: + +``` +TZ=UTC npx jest -u snapshot -c jest.unit.config.js src/ +``` + +## Visual Regression Tests + +Visual regression tests for permutation pages run automatically when opening a pull request in GitHub. + +To check results: look at the "Visual Regression Tests" action in the PR. The "Test for regressions" step logs which pages failed. For a full report, download the `visual-regression-results` artifact from the action summary and open `html_report/index.html` in your browser. + +If changes are expected, call it out in your PR comments. + + +### Run visual regression tests + +Visual regression tests for the permutation pages are automatically run when opening a pull request in GitHub. \ No newline at end of file diff --git a/docs/STYLING.md b/docs/STYLING.md new file mode 100644 index 0000000000..fa1b1e54b8 --- /dev/null +++ b/docs/STYLING.md @@ -0,0 +1,22 @@ +# Styling + +Prefer design tokens and custom CSS properties over hardcoded values (colors, spacing, font sizes, etc.). This keeps styles consistent across themes and modes. + +## Rules + +- Root elements must not have outer margins — spacing is managed by parent components +- No descendant combinators (`.a .b` with a space) — breaks CSS scoping because it applies to all `.class-b` elements at unlimited depth +- Wrap animations in the `with-motion` mixin to ensure motion can be toggled on and off +- Use logical properties only — no `left`/`right`/`top`/`bottom`/`width`/`height` in CSS. Use `inline-start`/`inline-end`/`block-start`/`block-end`/`inline-size`/`block-size` instead. Required for RTL support. + +References: +- [Mappings for sizing](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values/Sizing#mappings_for_dimensions) +- [Mappings for margins, borders, and padding](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values/Margins_borders_padding#mappings_for_margins_borders_and_padding) +- [Mappings for floating and positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_logical_properties_and_values/Floating_and_positioning#mapped_properties_and_values) + +## RTL Support + +For cases that need direction-aware logic beyond CSS logical properties: + +- In SCSS, use the `with-direction` mixin +- In TypeScript, `@cloudscape-design/component-toolkit/internal` provides direction detection and logical geometry helpers that replace physical DOM properties diff --git a/docs/WRITING_TESTS.md b/docs/WRITING_TESTS.md new file mode 100644 index 0000000000..8e2a0971e6 --- /dev/null +++ b/docs/WRITING_TESTS.md @@ -0,0 +1,28 @@ +# Writing Tests + +## Test Utils + +Test-utils core is a separate package: https://github.com/cloudscape-design/test-utils + +- Test-utils should not have any dependencies — they can be used with any tech stack. +- Test-utils extend `ComponentWrapper`. `ElementWrapper` is only a return type when no more specific type is available. +- Methods must have explicitly declared return types (enforced via ESLint). +- Wrapper classes must have a static `rootSelector` property. +- For methods that always return a value, add a non-null assertion. +- Adding `null` as a return type is a breaking change. Removing `null` is not. + +## Unit Tests + +Location: `src//__tests__/` + +Use `react-testing-library` to render, combined with test-utils. Prefer test-utils for querying and interacting with components — use react-testing-library directly only for internal edge cases not covered by test-utils. + +### Snapshot Tests + +Snapshot tests guard generated artifacts (API definitions, test-util wrappers, design tokens, etc.) against unintended changes. Running the unit and integration tests will update the snapshots accordingly. + +## Integration Tests + +Location: `src//__integ__/` + +Integration tests run in a real browser against dev pages (see [DEV_PAGES.md](DEV_PAGES.md)). Use `createWrapper` from `test-utils/selectors` (not `test-utils/dom` — selectors generate CSS selectors for browser tests, while dom wrappers operate on DOM nodes for unit tests).