<!-- npm-public-section:start -->
# Reusable UI Library

This repository contains a collection of reusable React components that are purely visual. These components are designed to be used across multiple projects and should not handle data fetching, routing, or translations.

## Caution: This is an early-stage development version of the Shoptet UI kit package

Please be aware that this version may contain bugs, incomplete features, or undergo significant changes before reaching a stable release. Use it at your own risk and for testing and development purposes only. Your feedback and contributions are appreciated to help improve the stability and functionality of the package. Refer to the documentation and release notes for more information on the current state of development. Thank you for your understanding.

## Storybook

We have written a Storybook for the components to demonstrate how to use them.

The internal public version is currently available here:
[https://react.shoptet.cz/](https://react.shoptet.cz/)

## Localization

The components come with a built-in localization in 8 languages: `cs-CZ`, `de-DE`, `en-US`, `hu-HU`, `pl-PL`, `ro-RO`, `sk-SK`, and `vi-VN`. It is necessary to wrap your application with the `LocalizationProvider` component from the library and provide the `locale` prop with one of the supported locales.

## Bundling

This library is build on top of [@react-aria](https://react-spectrum.adobe.com/react-aria/) which includes localized messages for various languages. In order to avoid bundling all the languages some of which you might not need, you can define your own build plugin to strip away the translations. Here is an example of such a vite plugin:

```ts
export const uiTranslations = ({ locales }: Options): Plugin => {
  const intlLocales = locales.map(l => new Intl.Locale(l));

  return {
    name: 'vite-plugin-ui-translations',
    transform(_, id) {
      if (!id || !/[/\\](@react-stately|@react-aria|@react-spectrum|react-aria-components)[/\\]/.test(id)) {
        return;
      }

      const match = id.match(/[a-z]{2}-[A-Z]{2}/);

      if (match) {
        const locale = new Intl.Locale(match[0]);
        if (!intlLocales.some(l => localeMatches(locale, l))) {
          return {
            code: `export default {};`,
            map: null,
          };
        }
      }

      return;
    },
  };
};

function localeMatches(localeToMatch: Intl.Locale, includedLocale: Intl.Locale) {
  return (
    localeToMatch.language === includedLocale.language &&
    (!includedLocale.region || localeToMatch.region === includedLocale.region)
  );
}
```

And then you can use it in your `vite.config.ts` like this:

```ts
import { defineConfig } from "vite";
import { uiTranslations } from './vite-plugin-ui-translations';

export default defineConfig({
  plugins: [
    uiTranslations({
      locales: ["en-US", "cs-CZ"], // Specify the locales you want to bundle
    }),
  ],
});
```

Make sure not to set the library language to any of the locales you are stripping away, otherwise the library will not work properly.
<!-- npm-public-section:end -->

## Technology Stack

- React 19 with TypeScript
- Vite
- React Aria Components
- Vitest
- Cypress
- Storybook
- ESLint + Oxfmt

## Getting Started

### Prerequisites

This guide assumes you have already cloned this monorepo and set up your development environment using the [Development environment](/docker/README.md) guide.

### Quick Start

1. **Install dependencies**:

   ```bash
   pnpm install
   ```

### Development Workflow

1. **Start with Storybook** for component development:

   ```bash
   # Run from cms4 folder
   pnpm storybook
   ```

   Access Storybook at <http://localhost:6007> to develop and test components in isolation.

2. **Write tests** for new components:

   ```bash
   pnpm test              # Run tests
   pnpm test:update       # Update snapshots and fix formatting
   ```

3. **Lint your code** before committing:

   ```bash
   pnpm lint              # Check all linting
   pnpm lint:fix          # Fix formatting issues
   pnpm lint:eslint:fix   # Fix ESLint issues
   ```

4. **Run component tests** with Cypress:

   ```bash
   pnpm cy:run            # Run in command line
   pnpm cy:open           # Open Cypress UI
   ```

5. **(Optional) Build the library**:

   ```bash
   pnpm build
   ```

   Output location: `dist/`

## Testing

We use comprehensive testing strategies to ensure component quality:

### Vitest Unit Tests

- **HMTL Snapshot tests** for component consistency

```bash
pnpm test              # Run all tests with coverage
pnpm test:update       # Update snapshots after changes
```

We aim to maintain a high level of code coverage mainly to help with refactorings and related unintentional changes. The minimum coverage thresholds are defined in the Vitest configuration file (`vitest.config.ts`):

```json
{
  "branches": 80,
  "functions": 80,
  "lines": 80,
  "statements": 80
}
```

You can find the detailed coverage report in the `coverage` directory after running `pnpm test:report`.

Files or lines that are not relevant for coverage (e.g., re-exports, etc.) are marked with `/* istanbul ignore file */` or `/* istanbul ignore next */` comments to exclude them from the coverage report.

### Component Testing with Cypress

- **Visual regression testing**
- **Interaction testing** for complex components
- **Cross-browser compatibility** testing

Run the tests on your local environment (outside of dev container)

```bash
pnpm cy:run            # Run component tests in headless mode
pnpm cy:open           # Interactive test development
```

Tests are located in the `cypress/tests` directory.

## Linting and Code Quality

```bash
pnpm lint              # Run all linting checks
pnpm lint:eslint       # ESLint for code quality
pnpm lint:formatting   # Oxfmt for code formatting
pnpm lint:ts           # TypeScript compilation check

pnpm lint:fix          # Fix formatting issues
pnpm lint:eslint:fix   # Fix ESLint issues automatically
```

## Development with Storybook

### Production Storybook

**Live Storybook**: [https://react.shoptet.cz/](https://react.shoptet.cz/)

- **Component Documentation**: Complete API documentation for all components
- **Usage Examples**: Interactive examples and code snippets

### Writing Stories

Follow these patterns when creating component stories:

1. Create `.stories.tsx` files alongside components
2. Document all component props and variants
3. Provide usage guidelines
4. Include interactive examples for complex components

## Release Process

Release `@shoptet/ui` follows these steps:

1. **Create a PR** with:
   - Updated version in `package.json` ([Semantic Versioning](https://semver.org/))
   - New headline version in `CHANGELOG.md`
   - Changes copied from `CHANGELOG_UNRELEASED.md` to `CHANGELOG.md`

2. **(Recommended) Run the consumption test locally** to verify the packed tarball is importable and renders correctly before triggering publish:

   ```bash
   # From frontend/libs/design-system/ui/react/
   pnpm test:consumption
   ```

  This packs the package, installs it in an isolated sandbox outside the pnpm workspace, and renders a component with `react-dom/server`.

3. **Deploy to npm** by running the publish workflow from `master`:
  - Workflow: [publish-package-ui](https://github.com/shoptet/cms4/actions/workflows/publish-package-ui.yaml)
  - The workflow executes both consumption checks automatically:
    - pre-publish check from a packed tarball
    - post-publish check from npm registry

4. **Deploy Storybook** using [deploy-storybook](https://github.com/shoptet/cms4/actions/workflows/deploy-fe-storybook.yaml)

5. **Check documentation pages** for breakage:
   - [designsystem.shoptet.cz](https://designsystem.shoptet.cz)
   - [react.shoptet.cz](https://react.shoptet.cz) — verify embeds still work (folder structure or story name changes can break them)

6. **Notify the team** in the `#design_system` Slack channel with changelog details (Breaking changes, API changes, New components, Bug fixes)

7. **Create a duplicate of this ticket** and add it to the next Sprint!

## Publishing to NPM

> [!NOTE]
> Publishing is possible only from the `master` branch because of secrets check.
> Read more about publishing packages to npm registry in our [docs](https://github.com/shoptet/cms4/blob/master/docs/ci/npm-packages.md).

Publishing is done through the workflow:

- [publish-package-ui](https://github.com/shoptet/cms4/actions/workflows/publish-package-ui.yaml)

This workflow currently runs:

1. Build
2. Pre-publish consumption test (`consumption-test.sh --skip-build`)
3. Pack the package (`pnpm pack --pack-destination ./packed`)
4. Publish to npmjs.com
5. Post-publish consumption test (`consumption-test.sh --post-publish`)

You can still run the consumption test locally before triggering CI:

```bash
# From frontend/libs/design-system/ui/react/
pnpm test:consumption
```

This script builds the package, packs it into a tarball, installs it into the
isolated `consumption-test/` sandbox (i.e. outside the pnpm workspace), renders
a `Button` component via `react-dom/server`, and fails loudly if anything is
broken. See [`consumption-test/`](./consumption-test/) for implementation details.

Use the GitHub Actions workflow to publish from `master`:
<https://github.com/shoptet/cms4/actions/workflows/publish-package-ui.yaml>

## Stylesheets

The package includes a LESS implementation of the Shoptet Design System written in BEM, colocated with the React components (formerly the `@shoptet/css` package).

### Usage

- **This package / Storybook**: [`src/index.less`](./src/index.less) bundles all the CSS and assets; the Vite build emits it as `dist/index.css` (the `./index.css` export). Storybook imports `@shoptet/ui/index.less`.
- **Admin**: admin CSS relies on a certain order of imports, hence the DS CSS is imported in parts:
  - [`public/cms/css/v2/variables.less`](/public/cms/css/v2/variables.less) imports [`src/components/Icon/IconName.less`](./src/components/Icon/IconName.less) and [`src/tokens/dimensions.less`](./src/tokens/dimensions.less).
  - [`public/cms/css/main.less`](/public/cms/css/main.less) imports [`src/index.less`](./src/index.less).
- Stylesheets with no React counterpart (used by legacy admin markup only) live in [`src/styles/`](./src/styles/).

### Tokens

Tokens are generated by the scripts in [`scripts/`](./scripts/) from the `*.config.ts` source files under [`src/tokens/`](./src/tokens/). Each generator emits its artifacts next to the config that produced them, so configs and outputs sit side by side.

Generation pipeline (config → generator → artifact):

1. **Scales (design language)** — the raw vocabulary the configs draw from. Not intended for direct consumption.
   - [`scales/palette.ts`](./src/tokens/scales/palette.ts) maps intents to color values (system palette, neutral, controls, brand colors, etc.).
   - [`scales/index.ts`](./src/tokens/scales/index.ts) enumerates the full application design language (dimension scale, swatches, font families, font weights, etc.).
2. **Tokens (current and legacy)** — [`tokens.config.ts`](./src/tokens/tokens.config.ts) and [`legacyTokens.config.ts`](./src/tokens/legacyTokens.config.ts) select a curated subset of the scales and drive the generators (`pnpm generate:tokens`, `pnpm generate:legacyTokens`). Outputs are emitted alongside each config:
   - [`tokens.config.ts`](./src/tokens/tokens.config.ts) → [`tokens.ts`](./src/tokens/tokens.ts), [`tokens.css`](./src/tokens/tokens.css), [`tokens.json`](./src/tokens/tokens.json)
   - [`legacyTokens.config.ts`](./src/tokens/legacyTokens.config.ts) → [`legacyTokens.ts`](./src/tokens/legacyTokens.ts), [`legacyTokens.css`](./src/tokens/legacyTokens.css)

   The [`tokens.json`](./src/tokens/tokens.json) output maps almost directly onto Figma collections, enabling design–developer parity.

Auxiliary files:

- [`src/tokens/types.ts`](./src/tokens/types.ts) — type definitions describing the structure of the design tokens and the underlying design language.
- [`src/tokens/dimensions.less`](./src/tokens/dimensions.less) — exposes the media query breakpoints as LESS variables (breakpoints only — other token categories are consumed via the generated CSS/TS).

### Icon font

`pnpm generateIconFont` (requires [Deno](https://deno.com)) rebuilds the `shp` icon font in [`src/assets/fonts/shoptet/`](./src/assets/fonts/shoptet/) from the SVG sources in [`src/assets/icons/`](./src/assets/icons/) and regenerates [`src/components/Icon/IconName.ts`](./src/components/Icon/IconName.ts), [`IconName.less`](./src/components/Icon/IconName.less), and [`iconGlyphs.css`](./src/components/Icon/iconGlyphs.css).

### Stylesheet snapshot tests

Every stylesheet has a colocated `*.styleSheet.test.ts` snapshot test (`pnpm test:stylesheets`). After changing a stylesheet or tokens, refresh them with `pnpm test:stylesheets:update`.

## Getting Help

- **Slack channel**: `#design_system`
- **GitHub issues**: For bug reports and feature requests
- **Storybook documentation**: For usage examples and API reference
- **Team reviews**: Consult with the design system team for architectural decisions
