diff --git a/README.md b/README.md index 0fe70792..885eaca0 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,33 @@ The Storybook documentation covers: --- +## AI Agent Skills + +This repository ships an **integration skill** for AI coding agents that consume `@tedi-design-system/react` in a downstream application. The skill lives at [`skills/tedi-react/`](./skills/tedi-react) and conforms to the [skill.sh](https://skill.sh) standard, so it works with any modern AI tool that supports skills. + +It teaches an agent: + +- The canonical import paths (`/tedi` vs `/community`), required providers, and setup snippet +- Component APIs, props, polymorphic and breakpoint patterns +- Form control conventions (controlled/uncontrolled, helpers, validation) +- Theming with design tokens from `@tedi-design-system/core` +- Common pitfalls to avoid (deprecated Community components, hardcoded colors, `var()` fallbacks, etc.) +- Pointers back to this repo and the [live Storybook](https://storybook.tedi.ee/react/main/?path=/docs/documentation-get-started--get-started) as authoritative sources + +### Install + +Use the [skills.sh](https://skills.sh) CLI from your project root: + +```bash +npx skills add TEDI-Design-System/react +``` + +The CLI auto-discovers the `tedi-react` skill under [`skills/`](./skills) and registers it for any compatible agent. Once installed, the agent will trigger the skill whenever you work with TEDI React components. + +> Skills for **contributing to** the TEDI Design System (contributor skills, standards validation, etc.) live in a separate repo: [TEDI-Design-System/ai-skills](https://github.com/TEDI-Design-System/ai-skills). + +--- + ## Repository Development Guide (Contributors) The following instructions apply only if you are working on this repository itself diff --git a/skills/tedi-react/SKILL.md b/skills/tedi-react/SKILL.md index e6ca75ed..b54d29d1 100644 --- a/skills/tedi-react/SKILL.md +++ b/skills/tedi-react/SKILL.md @@ -1,15 +1,49 @@ --- name: tedi-react description: > - Build UIs with @tedi-design-system/react — 50+ accessible React components with design token - theming. Use when creating interfaces, integrating form controls, customizing themes, or working - with TEDI components in a React application. + Build UIs with @tedi-design-system/react — the official Estonian government React component + library (`@tedi-design-system/react`). Use whenever the user is integrating, importing, or + composing TEDI components in a downstream React app: `Button`, `Alert`, `TextField`, `Select`, + `Card`, `Tooltip`, `Dropdown`, `Tabs`, `Toggle`, `Pagination`, `EmptyState`, `Table`, `Modal`, + etc. Triggers on theming with TEDI design tokens, switching dark/light theme via + `ThemeProvider`, wiring `LabelProvider` / `StyleProvider` / `AccessibilityProvider`, form + validation with the `helper` prop, responsive `xs` / `md` / `lg` breakpoint props, and + polymorphic `as`-prop usage. Do NOT use when contributing to the TEDI library repo itself — + use `tedi-react-contributing` for that. --- # TEDI Design System — React React component library with 50+ accessible components. Built on React 18/19 with TypeScript, CSS Modules, and design tokens from `@tedi-design-system/core`. +## Authoritative Sources + +This skill bundles a snapshot of the API and patterns, but the library is public and ships fast. When a prop, default, or component listed below feels stale or absent, treat these as the source of truth and fetch from them. + +### Pin to the consumer's installed version + +Before fetching source, **determine which version of `@tedi-design-system/react` the project actually has installed** and browse the matching git tag — not `main`. The repo's release tags follow the pattern `react-` (e.g. `react-17.0.0-rc.8`, `react-17.1.0-rc.4`). + +1. Read the resolved version from the project — `package.json`'s `dependencies."@tedi-design-system/react"`, or `npm ls @tedi-design-system/react`, or the lockfile entry. Strip any range prefix (`^`, `~`). +2. Construct the tag URL: `https://github.com/TEDI-Design-System/react/tree/react-/...` +3. If the resolved version is a pre-release or the tag doesn't exist (rare), fall back to `main` and note the version mismatch when answering. + +**Example** for a project on `17.0.0-rc.8`: +- TEDI-Ready components: `https://github.com/TEDI-Design-System/react/tree/react-17.0.0-rc.8/src/tedi/components` +- Barrel export: `https://github.com/TEDI-Design-System/react/blob/react-17.0.0-rc.8/src/tedi/index.ts` +- Specific component: `https://github.com/TEDI-Design-System/react/blob/react-17.0.0-rc.8/src/tedi/components/buttons/button/button.tsx` + +### Canonical references + +- **Source code & releases**: [github.com/TEDI-Design-System/react](https://github.com/TEDI-Design-System/react) — TEDI-Ready components live under `src/tedi/components/`, community under `src/community/components/`. The barrel export `src/tedi/index.ts` is the canonical list of TEDI-Ready exports. Always prefer the version-pinned tag URLs (see above) over `main` when consulting source. +- **Live Storybook (interactive docs + prop tables)**: [storybook.tedi.ee/react/main](https://storybook.tedi.ee/react/main/?path=/docs/documentation-get-started--get-started) — has every component's args table, default values, and runnable examples. Note that the public Storybook tracks `main`; if it disagrees with the consumer's installed tag, the tag wins. +- **Design system wiki** (cross-framework guidelines): [github.com/TEDI-Design-System/general/wiki](https://github.com/TEDI-Design-System/general/wiki) +- **Releases & changelog**: [github.com/TEDI-Design-System/react/releases](https://github.com/TEDI-Design-System/react/releases), [CHANGELOG.md](https://github.com/TEDI-Design-System/react/blob/main/CHANGELOG.md), [Issues](https://github.com/TEDI-Design-System/react/issues) +- **npm**: [@tedi-design-system/react](https://www.npmjs.com/package/@tedi-design-system/react) +- **Sibling packages**: [@tedi-design-system/core](https://www.npmjs.com/package/@tedi-design-system/core) (tokens, SCSS, icons), [@tedi-design-system/angular](https://www.npmjs.com/package/@tedi-design-system/angular) (Angular counterpart — useful for behavioral parity questions) + +**Verification tip**: if the user asks about a recently added component or a prop you're unsure of, fetch the relevant `.tsx` file from the version-pinned tag (e.g. `src/tedi/components///.tsx`) — the JSDoc on `interface ...Props` is the canonical spec. + ## Installation ```bash @@ -29,14 +63,21 @@ dayjs: ^1.11.10 ### 1. Wrap your app with providers ```tsx -import { ThemeProvider, LabelProvider, StyleProvider } from '@tedi-design-system/react/tedi'; +import { + ThemeProvider, + LabelProvider, + StyleProvider, + AccessibilityProvider, +} from '@tedi-design-system/react/tedi'; function App() { return ( - + + + @@ -44,6 +85,8 @@ function App() { } ``` +`AccessibilityProvider` exposes `useDeclareLoader` and other a11y hooks; omit it only if you have no loaders/announcements. `PrintingProvider` is also available — wrap it inside `AccessibilityProvider` when you need the `usePrint` context. + ### 2. Import core styles ```tsx @@ -186,6 +229,18 @@ import { Alert, sendNotification, ToastContainer } from '@tedi-design-system/rea sendNotification({ type: 'success', title: 'Done', children: 'Task completed' }); ``` +## Common Pitfalls + +A handful of mistakes account for most TEDI integration issues. Avoid them up front: + +- **Import from `/tedi` or `/community`, never the package root.** `@tedi-design-system/react` is not a valid import path — the package has explicit entry points (`@tedi-design-system/react/tedi`, `@tedi-design-system/react/community`, `@tedi-design-system/react/index.css`). Importing from the root will fail or silently miss types. +- **Prefer TEDI-Ready over Community whenever possible.** Several Community components (`Button`, `Anchor`, `Check`, `Radio`, `Tabs`, `Toggle`, `Tooltip`, `Dropdown`, `Tag`) are deprecated in favor of TEDI-Ready equivalents. Reach into Community only when no TEDI-Ready alternative exists (e.g. `Modal`, `Stepper`, `Table`, `DateTimePicker`). +- **Always pass `id` to form controls.** `TextField`, `Select`, `Checkbox`, `Radio`, etc. require it — it's how the label/helper/aria wiring works. There is no auto-generated fallback. +- **Use design tokens, not hardcoded colors.** Reach for `var(--tedi-color-*)`, `var(--tedi-spacing-*)`, etc. from `@tedi-design-system/core` instead of hex codes. This is what makes theme switching and brand overrides work. +- **Do not add CSS `var()` fallbacks.** Write `var(--tedi-spacing-4)`, not `var(--tedi-spacing-4, 16px)` — fallbacks defeat token-driven theming. +- **Support both controlled and uncontrolled.** When wrapping a TEDI form control with your own, accept `value`/`defaultValue` and forward both — don't force consumers into one mode. +- **Mock `useBreakpointProps` in tests** for any component you wrote that uses breakpoint support; jsdom won't respond to media queries. + ## Additional References Load based on your task — **do not load all at once**: diff --git a/skills/tedi-react/references/components.md b/skills/tedi-react/references/components.md index 7cbe6c3e..b4ad6416 100644 --- a/skills/tedi-react/references/components.md +++ b/skills/tedi-react/references/components.md @@ -265,6 +265,40 @@ Same as Checkbox (without indeterminate) - `label: string` (required) - `accept?: string`, `multiple?: boolean`, `maxSize?: number` +### Toggle +**Props:** `ToggleProps` | fRef, form +- `id: string` (required) +- `label: ReactNode` (required), `hideLabel?: boolean`, `labelPosition?: 'left' | 'right' = 'right'` +- `checked?: boolean`, `defaultChecked?: boolean` +- `onChange?: (value: boolean) => void` +- `size?: 'default' | 'large'` +- `color?: 'primary' | 'colored'` +- `helper?: FeedbackTextProps` +- `disabled?: boolean`, `isLoading?: boolean` + +```tsx + +``` + +### InputGroup +Compose a labeled input with prefix/suffix slots (e.g. currency symbol, unit, button addon). + +**Props:** `InputGroupProps` extends `FormLabelProps` +- `children: ReactNode` (required) — use `InputGroup.Input` plus optional `InputGroup.Prefix` / `InputGroup.Suffix` +- `addons?: boolean = true` — merge borders/radius into a single visual control +- `helper?: FeedbackTextProps | FeedbackTextProps[]` +- `disabled?: boolean`, plus all `FormLabel` props (`label`, `id`, `required`, etc.) + +Sub-components: `InputGroup.Input`, `InputGroup.Prefix`, `InputGroup.Suffix` + +```tsx + + + + / month + +``` + ## Layout ### Row / Col (Grid) @@ -327,6 +361,61 @@ Sub-component: `Skeleton.Block` Profile ``` +### Tabs +**Props:** `TabsProps` +- `children: ReactNode` (required) — use `Tabs.List` + `Tabs.Trigger` and `Tabs.Content` +- `value?: string` (controlled), `defaultValue?: string` +- `onChange?: (tabId: string) => void` + +Sub-components: `Tabs.List`, `Tabs.Trigger` (props: `id` required, `icon?`, `disabled?`), `Tabs.Content` (props: `id` to scope content to a tab) + +```tsx + + + Overview + Settings + + Overview panel + Settings panel + +``` + +### Pagination +**Props:** `PaginationProps` +- `pageCount: number` (required) +- `page?: number` (controlled, 1-based), `defaultPage?: number = 1` +- `onPageChange?: (page: number) => void` +- `totalItems?: number` — renders a "{count} results" label when set +- `pageSize?: number`, `pageSizeOptions?: number[]`, `onPageSizeChange?: (size: number) => void` +- `labels?: Partial` — override default English labels (`ariaLabel`, `previous`, `next`, `pageAriaLabel`, `currentPageAriaLabel`, `results`, `pageSize`) + +```tsx + +``` + +### HashTrigger +Wraps an element and fires a callback (and optionally scrolls to it) when the URL hash matches. The `id` is injected onto the first child element so the browser can resolve it. Handy for opening modals or scrolling to sections from external deep links. + +**Props:** `HashTriggerProps` +- `children: ReactNode` (required) — receives `id` injected onto the first child element; if `children` isn't a valid element, `HashTrigger` wraps them in a `
` +- `id: string` (required) — hash value to match (without the leading `#`) +- `onMatch?: (id: string) => void` — fired when the hash matches; receives the matched id +- `scrollOnMatch?: boolean = true` — scrolls the element into view if it's off-screen (instant on initial load, smooth otherwise) + +```tsx + console.log('matched', id)}> +
Section 2 content
+
+``` + ## Notifications ### Alert @@ -413,6 +502,20 @@ Sub-components: `Popover.Trigger`, `Popover.Content` Active ``` +### StatusIndicator +Small colored dot for status — pair with a label or position over another element. + +**Props:** `StatusIndicatorProps` +- `type?: 'success' | 'danger' | 'warning' | 'inactive' = 'success'` +- `size?: 'sm' | 'lg' = 'sm'` +- `hasBorder?: boolean` — white ring (use over avatars/icons) +- `position?: 'default' | 'top-right'` — absolute-positioned at parent's corner + +```tsx + + +``` + ## Misc ### Separator @@ -423,6 +526,36 @@ Sub-components: `Popover.Trigger`, `Popover.Content` - `thickness?: 1 | 2` - `spacing?: SeparatorSpacing` +### EmptyState +"Nothing here yet" placeholder with icon, copy, and a CTA slot. + +**Props:** `EmptyStateProps` +- `type?: 'separate' | 'attached' | 'inside' = 'separate'` — `attached` removes top border (sits under a card/table); `inside` removes border + radius (lives inside another container) +- `size?: 'default' | 'small' = 'default'` +- `icon?: string | IconWithoutBackgroundProps | null = 'spa'` +- `heading?: ReactNode` +- `children?: ReactNode` — body text +- `actions?: ReactNode` — CTA slot, usually a `}> + Try a different search term. + +``` + +### Utility Components + +Niche helpers exported from `@tedi-design-system/react/tedi` — load on demand: + +- **Affix** — sticky-position wrapper (top/bottom offset) +- **Ellipsis** — single-line truncation with tooltip on overflow +- **Print** — show/hide subtree based on `usePrint()` context (paired with `PrintingProvider`) +- **ScrollFade** — fade edges of a scrollable container as content runs off +- **ScrollVisibility** — show/hide an element based on scroll position +- **StretchContent** — fill available space inside flex/grid parents +- **HashTrigger** — react to URL hash changes (see Navigation) +- **FeedbackText**, **FormLabel**, **Field** — primitives for composing custom form controls + --- # Community Components @@ -448,19 +581,13 @@ Import from `@tedi-design-system/react/community`. These are community-contribut ### Check (Checkbox) — **DEPRECATED** (use TEDI-Ready Checkbox) ### Radio — **DEPRECATED** (use TEDI-Ready Radio via ChoiceGroup) +### Toggle — **DEPRECATED** (use TEDI-Ready Toggle) +### ChoiceGroup — **DEPRECATED** (use TEDI-Ready ChoiceGroup) ### Select - `id: string`, `options`, `value?`, `defaultValue?`, `onChange?` - `multiple?: boolean`, `async?: boolean`, `isSearchable?: boolean`, `isClearable?: boolean` -### Toggle -- `ariaLabel: string`, `label?`, `checked?`, `defaultChecked?`, `onChange?` -- `size?: 'medium' | 'large'`, `color?: 'default' | 'alternative'`, `icon?`, `disabled?` - -### ChoiceGroup -- `id: string`, `items: ChoiceGroupItemProps[]`, `inputType?: 'radio' | 'checkbox'` -- `type?: 'light' | 'selector' | 'filter' | 'default'`, `value?`, `onChange?` - ### FileUpload - `id: string`, `name: string`, `accept?`, `multiple?`, `maxSize?` - `files?`, `defaultFiles?`, `onChange?`, `onDelete?` @@ -483,9 +610,8 @@ Import from `@tedi-design-system/react/community`. These are community-contribut - `activeStep?`, `defaultActiveStep?: number`, `onActiveStepChange?` - `allowStepLabelClick?: boolean`, `ariaLabel: string`, `card?: CardProps | boolean` -### Tabs -- `currentTab?: string`, `defaultCurrentTab?`, `onTabChange?` -- Sub-components: Tabs.Nav, Tabs.NavItem, Tabs.Item +### Tabs — **DEPRECATED** (use TEDI-Ready Tabs) +Legacy variant with `Tabs.Nav`, `Tabs.NavItem`, `Tabs.Item`. New code should use the TEDI-Ready `Tabs` component (different API: `Tabs.List` / `Tabs.Trigger` / `Tabs.Content`). ### TableOfContents - `items: TableOfContentsItemProps[]`, `heading?`, `open?`, `defaultOpen?` diff --git a/skills/tedi-react/references/forms.md b/skills/tedi-react/references/forms.md index cedd7c86..1272785c 100644 --- a/skills/tedi-react/references/forms.md +++ b/skills/tedi-react/references/forms.md @@ -140,17 +140,28 @@ import { Checkbox, Radio, ChoiceGroup } from '@tedi-design-system/react/tedi'; onChange={setSize} /> -// Segmented choice group +// Segmented choice group (single visually-merged control) + +// Card-style choices (each item rendered as a selectable card) + ``` ## Validation & Helper Text