codex/design-system-foundation #33
643
docs/features/design-system-foundation/plan.md
Normal file
643
docs/features/design-system-foundation/plan.md
Normal file
@ -0,0 +1,643 @@
|
|||||||
|
# Design System Foundation Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** Создать внутренний пакет `@moex-vibe/design-system` с трёхуровневыми токенами, MUI light theme, согласованным UI-каталогом, Storybook и опубликованными правилами.
|
||||||
|
|
||||||
|
**Architecture:** Platform-neutral токены хранятся как JSON-compatible TypeScript records и разрешаются собственным проверяемым resolver. MUI-адаптер переводит semantic/component tokens в CSS-variable theme; React-компоненты доступны только через public exports пакета. Storybook служит локальным/CI workbench, Docusaurus — единственной опубликованной документацией.
|
||||||
|
|
||||||
|
**Tech Stack:** TypeScript 5, React 18, MUI 6.5, TanStack Table 8, Vitest 4 browser mode, Storybook 10.2 (`@storybook/react-vite`), Playwright Chromium, Docusaurus 3.7.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Зафиксированные интерфейсы
|
||||||
|
|
||||||
|
Пакет предоставляет subpath exports:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
".": "./dist/index.js",
|
||||||
|
"./tokens": "./dist/tokens/index.js",
|
||||||
|
"./theme": "./dist/theme/index.js"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Token source использует плоские DTCG-подобные записи:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type TokenType = 'color' | 'dimension' | 'fontFamily' | 'fontWeight' | 'duration' | 'cubicBezier' | 'shadow';
|
||||||
|
type TokenValue = string | number | readonly number[] | readonly string[];
|
||||||
|
type TokenDefinition = { readonly $type: TokenType; readonly $value: TokenValue };
|
||||||
|
type TokenCollection = Readonly<Record<string, TokenDefinition>>;
|
||||||
|
|
||||||
|
// Primitive содержит абсолютное значение.
|
||||||
|
'color.green.700': { $type: 'color', $value: '#176747' }
|
||||||
|
// Semantic/component содержит alias и никогда не дублирует primitive.
|
||||||
|
'color.action.primary': { $type: 'color', $value: '{color.green.700}' }
|
||||||
|
```
|
||||||
|
|
||||||
|
Публичный React API:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type Tone = 'default' | 'secondary' | 'positive' | 'negative' | 'muted';
|
||||||
|
type Density = 'balanced' | 'compact';
|
||||||
|
type ActionVariant = 'primary' | 'secondary' | 'tertiary' | 'danger';
|
||||||
|
|
||||||
|
type TextProps = { variant?: 'body' | 'caption' | 'label' | 'numeric'; tone?: Tone };
|
||||||
|
type HeadingProps = { level: 1 | 2 | 3 | 4 | 5 | 6; size?: 'display' | 'title' | 'section' | 'subsection' };
|
||||||
|
type ButtonProps = { variant?: ActionVariant; size?: 'small' | 'medium'; loading?: boolean };
|
||||||
|
type IconButtonProps = { label: string; size?: 'small' | 'medium' };
|
||||||
|
type SurfaceProps = { padding?: 'none' | 'sm' | 'md' | 'lg'; elevation?: 'none' | 'sm' | 'md' };
|
||||||
|
type DataTableProps<T> = { table: Table<T>; density?: Density; loading?: boolean; empty?: ReactNode; caption: string };
|
||||||
|
type MoneyProps = { value: number; currency?: string; locale?: string; signDisplay?: 'auto' | 'always' | 'never' };
|
||||||
|
type PriceChangeProps = { value: number; format?: 'percent' | 'money'; currency?: string; locale?: string };
|
||||||
|
type MetricProps = { label: string; value: ReactNode; supportingText?: ReactNode; trend?: ReactNode };
|
||||||
|
```
|
||||||
|
|
||||||
|
Остальные контракты фиксированы так:
|
||||||
|
|
||||||
|
- `Link`: MUI Link без `color`, `variant`, `sx`; добавляет `tone` и сохраняет polymorphic `component`.
|
||||||
|
- `TextField`: MUI TextField без `color`, `variant`, `size`, `sx`; всегда `variant="outlined"` и
|
||||||
|
`size="medium"`.
|
||||||
|
- `Select`: `label`, `value`, `onChange(value: string)`, `options: { value; label; disabled? }[]`,
|
||||||
|
`error?`, `helperText?`, `disabled?`.
|
||||||
|
- `Checkbox`: `label`, `checked`, `onChange(checked: boolean)`, `disabled?`, `error?`.
|
||||||
|
- `Card`: Surface props плюс `header?`, `actions?`, `children`; не знает маршруты и домен.
|
||||||
|
- `Chip`: `label`, `tone: neutral|info|success|warning|error`, `onDelete?`.
|
||||||
|
- `Badge`: `value`, `max?`, `label`; decorative badge запрещён без accessible label.
|
||||||
|
- `Alert`: `severity: info|success|warning|error`, `title?`, `children`, `action?`.
|
||||||
|
- `Dialog`: `open`, `onClose`, обязательные `title`, `children`, `actions?`.
|
||||||
|
- `Skeleton`: `width?`, `height?`, `shape: text|rectangular|rounded|circular`.
|
||||||
|
- `Progress`: `label`, `value?`; отсутствие value означает indeterminate.
|
||||||
|
- `FormField`: `label`, `htmlFor`, `helperText?`, `error?`, `required?`, `children`.
|
||||||
|
- `FilterBar`: `children`, `actions?`; отвечает только за responsive layout.
|
||||||
|
- Page states: обязательный `title`, optional `description/action`; `LoadingState` дополнительно имеет
|
||||||
|
`label` и `size: section|page`.
|
||||||
|
|
||||||
|
Все wrappers запрещают произвольные `color`, `variant`, `size` и `sx`, где они обходят контракт.
|
||||||
|
`Box`, `Stack`, `Grid` остаются полным и единственным прямым MUI allowlist во frontend.
|
||||||
|
|
||||||
|
## Карта файлов
|
||||||
|
|
||||||
|
- `packages/design-system/src/tokens/` — schema, три token collections, resolver и guards.
|
||||||
|
- `packages/design-system/src/theme/` — MUI augmentation, theme factory, provider, component overrides.
|
||||||
|
- `packages/design-system/src/components/` — Core UI и product patterns; один каталог на компонент.
|
||||||
|
- `packages/design-system/.storybook/` — единый provider, a11y и browser-test annotations.
|
||||||
|
- `apps/frontend/src/app/providers/AppProviders.tsx` — подключение provider пакета без миграции страниц.
|
||||||
|
- `apps/docs/docs/design-system/` — канонические правила; `apps/docs/docs/adr/ADR-016-design-system.md` — архитектурное решение.
|
||||||
|
|
||||||
|
### Task 1: Pre-flight и workspace shell
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `package.json`
|
||||||
|
- Create: `packages/design-system/package.json`
|
||||||
|
- Create: `packages/design-system/tsconfig.json`
|
||||||
|
- Create: `packages/design-system/vitest.config.ts`
|
||||||
|
- Create: `packages/design-system/.eslintrc.cjs`
|
||||||
|
- Create: `packages/design-system/src/test/setup.ts`
|
||||||
|
- Create: `packages/design-system/src/index.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Проверить pre-flight до любых implementation edits**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git branch --show-current
|
||||||
|
git status --short
|
||||||
|
npm run test:backend
|
||||||
|
npm run test:frontend
|
||||||
|
npm run lint
|
||||||
|
npm run build:backend
|
||||||
|
npm run build:frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: ветка `codex/design-system-foundation`, чистый worktree и все команды PASS. При сбое
|
||||||
|
остановиться и классифицировать его до изменения кода.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Добавить workspace и root scripts**
|
||||||
|
|
||||||
|
В `package.json` добавить `packages/design-system` в `workspaces` и scripts:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"build:design-system": "npm run build -w packages/design-system",
|
||||||
|
"test:design-system": "npm run test -w packages/design-system",
|
||||||
|
"lint:design-system": "npm run lint -w packages/design-system",
|
||||||
|
"storybook": "npm run storybook -w packages/design-system",
|
||||||
|
"build:storybook": "npm run build-storybook -w packages/design-system",
|
||||||
|
"test:storybook": "npm run test:storybook -w packages/design-system"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: Создать package manifest и TypeScript build**
|
||||||
|
|
||||||
|
`packages/design-system/package.json` должен быть private ESM package версии `0.1.0`, иметь `files:
|
||||||
|
["dist"]`, exports из раздела выше, peer dependencies на React 18, MUI 6 и TanStack Table 8, dev
|
||||||
|
dependencies Storybook `^10.2.9`, `@vitest/browser-playwright`/Vitest `^4.1.8` и Playwright, scripts
|
||||||
|
`build: tsc -p tsconfig.json`, `test: vitest run --project unit`, `storybook: storybook dev -p 6006
|
||||||
|
--no-open`, `build-storybook: storybook build`, `test:storybook: vitest run --project storybook`,
|
||||||
|
`lint: eslint "src/**/*.{ts,tsx}"`.
|
||||||
|
|
||||||
|
`tsconfig.json` компилирует `src` в `dist`, включает declarations, `jsx: react-jsx`, strict и
|
||||||
|
`moduleResolution: bundler`. `vitest.config.ts` сначала содержит unit project с jsdom и
|
||||||
|
`passWithNoTests: true`; флаг удаляется после Task 2. Test setup подключает jest-dom и matchMedia mock.
|
||||||
|
Package ESLint config проверяет TypeScript/React hooks, но не включает frontend FSD zones. Root `lint`
|
||||||
|
дополняется `lint:design-system`.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Установить зависимости и проверить пустой пакет**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run build:design-system
|
||||||
|
npm run test:design-system
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: package-lock обновлён, build PASS, Vitest PASS с `passWithNoTests: true`.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add package.json package-lock.json packages/design-system
|
||||||
|
git commit -m "build(design-system): add workspace package"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 2: Token schema и resolver (TDD)
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/design-system/src/tokens/types.ts`
|
||||||
|
- Create: `packages/design-system/src/tokens/resolveToken.ts`
|
||||||
|
- Test: `packages/design-system/src/tokens/resolveToken.test.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Написать failing tests**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
it('resolves an alias chain and preserves the declared type', () => {
|
||||||
|
const tokens = {
|
||||||
|
base: { $type: 'color', $value: '#176747' },
|
||||||
|
semantic: { $type: 'color', $value: '{base}' },
|
||||||
|
component: { $type: 'color', $value: '{semantic}' },
|
||||||
|
} satisfies TokenCollection;
|
||||||
|
expect(resolveToken(tokens, 'component')).toEqual({ type: 'color', value: '#176747' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
['missing alias', { a: { $type: 'color', $value: '{missing}' } }, /Unknown token/],
|
||||||
|
['cycle', { a: { $type: 'color', $value: '{b}' }, b: { $type: 'color', $value: '{a}' } }, /cycle/i],
|
||||||
|
['type mismatch', { a: { $type: 'color', $value: '#fff' }, b: { $type: 'dimension', $value: '{a}' } }, /type/i],
|
||||||
|
])('rejects %s', (_name, tokens, error) => expect(() => resolveToken(tokens, 'a')).toThrow(error));
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Запустить тест и подтвердить RED**
|
||||||
|
|
||||||
|
Run: `npm run test:design-system -- resolveToken.test.ts`
|
||||||
|
|
||||||
|
Expected: FAIL, module/functions отсутствуют.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Реализовать schema и resolver**
|
||||||
|
|
||||||
|
Resolver распознаёт только полную alias-строку `/^\{([^}]+)\}$/`, хранит visited path, проверяет
|
||||||
|
существование target и равенство `$type`, возвращает `{ type, value }`. Смешанные строки вроде
|
||||||
|
`calc({space.2} * 2)` запрещены.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Запустить тест и commit**
|
||||||
|
|
||||||
|
Run: `npm run test:design-system -- resolveToken.test.ts`
|
||||||
|
|
||||||
|
Expected: PASS.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add packages/design-system/src/tokens
|
||||||
|
git commit -m "feat(design-system): add token contracts"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 3: Три уровня токенов
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/design-system/src/tokens/primitives.ts`
|
||||||
|
- Create: `packages/design-system/src/tokens/semantic.light.ts`
|
||||||
|
- Create: `packages/design-system/src/tokens/components.ts`
|
||||||
|
- Create: `packages/design-system/src/tokens/index.ts`
|
||||||
|
- Test: `packages/design-system/src/tokens/tokens.test.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Написать token integrity tests**
|
||||||
|
|
||||||
|
Тест объединяет три collections, резолвит каждый token, проверяет уникальные имена и инварианты:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
expect(Object.keys(primitiveTokens).every((name) => !isAlias(primitiveTokens[name].$value))).toBe(true);
|
||||||
|
expect(Object.keys(semanticLightTokens).every((name) => isAlias(semanticLightTokens[name].$value))).toBe(true);
|
||||||
|
expect(Object.keys(componentTokens).every((name) => isAlias(componentTokens[name].$value))).toBe(true);
|
||||||
|
for (const name of Object.keys(allTokens)) expect(() => resolveToken(allTokens, name)).not.toThrow();
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Запустить тест и подтвердить RED**
|
||||||
|
|
||||||
|
Run: `npm run test:design-system -- tokens.test.ts`
|
||||||
|
|
||||||
|
Expected: FAIL, collections отсутствуют.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Добавить минимальный полный набор foundations**
|
||||||
|
|
||||||
|
Primitive groups: `color.neutral.{0,50,100,200,400,600,800,900}`, `color.green.{50,100,600,700,800}`,
|
||||||
|
`color.red.{50,600,700}`, `color.amber.{50,600}`, `color.blue.{50,600}`, `space.{0,1,2,3,4,5,6,8,10,12}`
|
||||||
|
на 4px grid, `radius.{none,sm,md,lg,pill}`, `font.family.{sans,mono}`, `font.size.{100..700}`,
|
||||||
|
`font.weight.{regular,medium,semibold,bold}`, `lineHeight.{tight,normal,relaxed}`, `shadow.{none,sm,md}`,
|
||||||
|
`duration.{instant,fast,normal}`, `easing.standard`, `size.control.{sm,md}`.
|
||||||
|
|
||||||
|
`font.family.sans` использует Inter с system-ui fallback. Webfont weights 400/500/600/700 подключаются
|
||||||
|
один раз во frontend shell в Task 10; Roboto не становится частью нового foundation.
|
||||||
|
|
||||||
|
Semantic groups: `color.canvas`, `color.surface.{default,subtle,raised}`, `color.text.{primary,secondary,
|
||||||
|
disabled,inverse}`, `color.border.{subtle,default,strong,focus}`, `color.action.{primary,primaryHover,
|
||||||
|
secondary,danger}`, `color.feedback.{info,success,warning,error}`, `color.finance.{positive,negative,
|
||||||
|
neutral}`, plus semantic typography, focus ring, spacing and control sizes.
|
||||||
|
|
||||||
|
Component groups cover только API v1: button, iconButton, field, checkbox, surface, card, chip, badge,
|
||||||
|
alert, dialog, skeleton, progress, table, metric и pageState.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Проверить GREEN и запрет hardcoded values вне primitives**
|
||||||
|
|
||||||
|
Run: `npm run test:design-system -- tokens.test.ts`
|
||||||
|
|
||||||
|
Expected: PASS. Дополнительный test scan подтверждает, что `semantic.light.ts` и `components.ts` не
|
||||||
|
содержат hex/rgb/px literals.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add packages/design-system/src/tokens
|
||||||
|
git commit -m "feat(design-system): define three-level tokens"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 4: MUI adapter и provider (TDD)
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/design-system/src/theme/createMoexVibeTheme.ts`
|
||||||
|
- Create: `packages/design-system/src/theme/MoexVibeThemeProvider.tsx`
|
||||||
|
- Create: `packages/design-system/src/theme/mui.d.ts`
|
||||||
|
- Create: `packages/design-system/src/theme/index.ts`
|
||||||
|
- Test: `packages/design-system/src/theme/createMoexVibeTheme.test.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Написать failing theme contract tests**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const theme = createMoexVibeTheme();
|
||||||
|
expect(theme.cssVarPrefix).toBe('mv');
|
||||||
|
expect(theme.colorSchemes.light.palette.primary.main).toBe(resolveValue('color.action.primary'));
|
||||||
|
expect(theme.typography.body1.fontFamily).toContain('Inter');
|
||||||
|
expect(theme.shape.borderRadius).toBe(resolveValue('radius.md'));
|
||||||
|
expect(theme.components?.MuiButton?.defaultProps).toMatchObject({ disableElevation: true });
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Подтвердить RED**
|
||||||
|
|
||||||
|
Run: `npm run test:design-system -- createMoexVibeTheme.test.ts`
|
||||||
|
|
||||||
|
Expected: FAIL, factory отсутствует.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Реализовать adapter**
|
||||||
|
|
||||||
|
Использовать `createTheme({ cssVariables: { cssVarPrefix: 'mv' }, colorSchemes: { light: ... } })`.
|
||||||
|
Theme получает palette, typography, spacing, shape, shadows, transitions и component overrides только
|
||||||
|
через `resolveValue`. `mui.d.ts` включает `themeCssVarsAugmentation` и добавляет `finance` palette roles.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Реализовать provider**
|
||||||
|
|
||||||
|
`MoexVibeThemeProvider` оборачивает MUI `ThemeProvider` и `CssBaseline`, принимает только `children`;
|
||||||
|
mode API в v1 не экспортируется.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Проверить и commit**
|
||||||
|
|
||||||
|
Run: `npm run test:design-system -- createMoexVibeTheme.test.ts && npm run build:design-system`
|
||||||
|
|
||||||
|
Expected: PASS.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add packages/design-system/src/theme packages/design-system/src/tokens
|
||||||
|
git commit -m "feat(design-system): add MUI theme adapter"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 5: Storybook и automated story checks
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/design-system/.storybook/main.ts`
|
||||||
|
- Create: `packages/design-system/.storybook/preview.tsx`
|
||||||
|
- Create: `packages/design-system/.storybook/vitest.setup.ts`
|
||||||
|
- Modify: `packages/design-system/vitest.config.ts`
|
||||||
|
- Modify: `packages/design-system/package.json`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Настроить Storybook 10.2**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const config: StorybookConfig = {
|
||||||
|
framework: '@storybook/react-vite',
|
||||||
|
stories: ['../src/**/*.stories.@(ts|tsx)'],
|
||||||
|
addons: ['@storybook/addon-a11y', '@storybook/addon-vitest'],
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
`preview.tsx` добавляет decorator `MoexVibeThemeProvider`, background `color.canvas` и параметры
|
||||||
|
`a11y.test = 'error'`, layout `centered` по умолчанию.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Добавить Storybook Vitest browser project**
|
||||||
|
|
||||||
|
Использовать `storybookTest`, `@vitest/browser-playwright` и headless Chromium; setup регистрирует
|
||||||
|
`@storybook/addon-a11y/preview` и project annotations через `setProjectAnnotations`.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Проверить стенд**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx playwright install chromium
|
||||||
|
npm run build:storybook
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: static Storybook build PASS. Первый `test:storybook` запускается после появления stories в
|
||||||
|
Task 6.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add package.json package-lock.json packages/design-system
|
||||||
|
git commit -m "build(design-system): configure Storybook workbench"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 6: Typography и actions (TDD)
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/design-system/src/components/{Text,Heading,Link,Button,IconButton}/`
|
||||||
|
- Test: colocated `*.test.tsx`
|
||||||
|
- Stories: colocated `*.stories.tsx`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Написать failing interaction/type tests**
|
||||||
|
|
||||||
|
Проверить semantic heading level, tone mapping, external link rel, loading button disabled state и
|
||||||
|
progress label, обязательный accessible label IconButton. Type tests отклоняют `color`, произвольный
|
||||||
|
`variant` и `sx`.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Подтвердить RED**
|
||||||
|
|
||||||
|
Run: `npm run test:design-system -- Text Heading Link Button IconButton`
|
||||||
|
|
||||||
|
Expected: FAIL, exports отсутствуют.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Реализовать компоненты по public contracts**
|
||||||
|
|
||||||
|
Каждый каталог содержит component, props и index. `Button loading` сохраняет ширину, блокирует повторный
|
||||||
|
click и имеет `aria-busy`; `IconButton` всегда устанавливает `aria-label={label}`.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Добавить stories и проверить GREEN**
|
||||||
|
|
||||||
|
Stories: все variants/sizes/tones, disabled, loading, long Russian label, keyboard focus reference.
|
||||||
|
|
||||||
|
Run: `npm run test:design-system && npm run test:storybook && npm run build:storybook`
|
||||||
|
|
||||||
|
Expected: PASS без a11y violations.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add packages/design-system/src
|
||||||
|
git commit -m "feat(design-system): add typography and actions"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 7: Inputs, surfaces и feedback (TDD)
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/design-system/src/components/{TextField,Select,Checkbox,Surface,Card,Chip,Badge,Alert,Dialog,Skeleton,Progress}/`
|
||||||
|
- Test/Stories: colocated `*.test.tsx`, `*.stories.tsx`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Написать failing contract tests**
|
||||||
|
|
||||||
|
Проверить label/helper/error association, Select options и keyboard opening, Checkbox label click,
|
||||||
|
Surface padding/elevation mapping, Dialog focus/escape/label, Alert role, determinate/indeterminate
|
||||||
|
Progress accessible name и Skeleton `aria-hidden`.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Подтвердить RED**
|
||||||
|
|
||||||
|
Run: `npm run test:design-system -- TextField Select Checkbox Surface Card Chip Badge Alert Dialog Skeleton Progress`
|
||||||
|
|
||||||
|
Expected: FAIL, components отсутствуют.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Реализовать ограниченные wrappers**
|
||||||
|
|
||||||
|
`Select` принимает `{ value: string; label: string; disabled?: boolean }[]`; `Checkbox` требует `label`;
|
||||||
|
`Dialog` требует `title` и управляется через `open/onClose`; Surface/Card не принимают raw elevation.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Stories, GREEN и commit**
|
||||||
|
|
||||||
|
Stories покрывают default/focus/disabled/error/loading, длинный русский текст и narrow viewport.
|
||||||
|
|
||||||
|
Run: `npm run test:design-system && npm run test:storybook && npm run build:storybook`
|
||||||
|
|
||||||
|
Expected: PASS.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add packages/design-system/src
|
||||||
|
git commit -m "feat(design-system): add controls and feedback"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 8: Финансовые data patterns (TDD)
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/design-system/src/components/{DataTable,Metric,Money,PriceChange}/`
|
||||||
|
- Test/Stories: colocated `*.test.tsx`, `*.stories.tsx`
|
||||||
|
- Test: `packages/design-system/src/visual/financial-patterns.visual.test.ts`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Написать failing tests**
|
||||||
|
|
||||||
|
Проверить table caption, semantic headers, sortable button `aria-sort`, balanced/compact density, loading
|
||||||
|
и empty states; `Money` через `Intl.NumberFormat`; `PriceChange` знак, текстовое направление и не только
|
||||||
|
цвет; `Metric` composition.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Подтвердить RED**
|
||||||
|
|
||||||
|
Run: `npm run test:design-system -- DataTable Metric Money PriceChange`
|
||||||
|
|
||||||
|
Expected: FAIL.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Реализовать patterns**
|
||||||
|
|
||||||
|
`DataTable` принимает готовый TanStack `Table<T>` и не владеет server pagination/sorting. Defaults:
|
||||||
|
`density="balanced"`, locale `ru-RU`, currency `RUB`. Negative/positive output включает видимый знак и
|
||||||
|
screen-reader label.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Stories, visual baselines и commit**
|
||||||
|
|
||||||
|
Canonical screenshot cases: balanced table, compact table, positive/negative metrics, empty/loading.
|
||||||
|
Browser test запускает portable stories через `composeStories`, затем делает
|
||||||
|
`expect(page.getByTestId('visual-root')).toMatchScreenshot('<stable-name>.png')`; baselines коммитятся
|
||||||
|
рядом с visual test. Обновление baseline допустимо только вместе с объяснением визуального изменения.
|
||||||
|
|
||||||
|
Run: `npm run test:design-system && npm run test:storybook`
|
||||||
|
|
||||||
|
Expected: interaction, a11y и screenshot checks PASS.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add packages/design-system/src
|
||||||
|
git commit -m "feat(design-system): add financial data patterns"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 9: Form и page-state patterns (TDD)
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `packages/design-system/src/components/{FormField,FilterBar,EmptyState,ErrorState,LoadingState}/`
|
||||||
|
- Test/Stories: colocated `*.test.tsx`, `*.stories.tsx`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Написать failing tests**
|
||||||
|
|
||||||
|
Проверить `htmlFor`/description/error association FormField, wrapping and action placement FilterBar,
|
||||||
|
semantic heading/action для Empty/Error, `aria-live="polite"` LoadingState и reduced-motion rendering.
|
||||||
|
|
||||||
|
- [ ] **Step 2: RED, implementation, GREEN**
|
||||||
|
|
||||||
|
Run before: `npm run test:design-system -- FormField FilterBar EmptyState ErrorState LoadingState`
|
||||||
|
|
||||||
|
Expected before: FAIL. Реализовать только layout/semantics без API/domain knowledge.
|
||||||
|
|
||||||
|
Run after: `npm run test:design-system && npm run test:storybook`
|
||||||
|
|
||||||
|
Expected after: PASS.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add packages/design-system/src
|
||||||
|
git commit -m "feat(design-system): add form and page-state patterns"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 10: Frontend integration и MUI boundary
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `apps/frontend/package.json`
|
||||||
|
- Modify: `apps/frontend/tsconfig.json`
|
||||||
|
- Modify: `apps/frontend/vite.config.ts`
|
||||||
|
- Modify: `apps/frontend/src/app/providers/AppProviders.tsx`
|
||||||
|
- Modify: `apps/frontend/.eslintrc.cjs`
|
||||||
|
- Delete: `apps/frontend/src/app/styles/theme.ts`
|
||||||
|
- Test: `apps/frontend/src/app/providers/AppProviders.test.tsx`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Написать failing provider test**
|
||||||
|
|
||||||
|
Render `AppProviders`, assert generated `--mv-palette-primary-main` exists and Query/Session providers
|
||||||
|
по-прежнему доступны. Test не проверяет редизайн страниц.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Подключить workspace package**
|
||||||
|
|
||||||
|
Добавить dependency, `@fontsource/inter` и aliases к source для dev/test; заменить MUI
|
||||||
|
ThemeProvider/CssBaseline на `MoexVibeThemeProvider`. Удалить старый локальный theme и импортировать
|
||||||
|
Inter 400/500/600/700 в provider entry ровно один раз.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Зафиксировать ESLint allowlist**
|
||||||
|
|
||||||
|
Во frontend запретить root import `@mui/material` и subpaths через `no-restricted-imports`; исключить
|
||||||
|
только `@mui/material/Box`, `Stack`, `Grid`. Сам пакет design-system использует отдельный lint config.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Проверить отсутствие визуальной миграции**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run test:frontend
|
||||||
|
npm run lint -w apps/frontend
|
||||||
|
npm run build:design-system
|
||||||
|
npm run build:frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: PASS; legacy `styles.css` и product component markup не изменены.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add apps/frontend packages/design-system package.json package-lock.json
|
||||||
|
git commit -m "feat(frontend): connect design system foundation"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 11: Docusaurus и ADR
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `apps/docs/docs/design-system/{overview,foundations,tokens,components,patterns,accessibility,governance}.md`
|
||||||
|
- Create: `apps/docs/docs/adr/ADR-016-design-system.md`
|
||||||
|
- Create: `packages/design-system/CHANGELOG.md`
|
||||||
|
- Modify: `apps/docs/sidebars.ts`
|
||||||
|
- Modify: `apps/docs/docs/adr/index.md`
|
||||||
|
- Modify: `apps/docs/docs/frontend/styling.md`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Написать канонические правила**
|
||||||
|
|
||||||
|
Документы фиксируют light-only v1, balanced density, три token levels, MUI allowlist, каталог,
|
||||||
|
component-card template, WCAG 2.2 AA и promotion/change process. Components page содержит матрицу
|
||||||
|
«задача → компонент → не использовать» и полную карточку обязательного шаблона для каждого public
|
||||||
|
export. `CHANGELOG.md` начинается с `0.1.0` и документирует public API foundation; governance описывает
|
||||||
|
обязательную migration note для будущих breaking changes.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Добавить ADR-016**
|
||||||
|
|
||||||
|
ADR содержит Context, варианты theme-only/hybrid/full-wrapper, решение hybrid, последствия, Storybook
|
||||||
|
роль и будущие DTCG/Android adapters.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Обновить sidebar и проверить docs**
|
||||||
|
|
||||||
|
Run: `npm run build:docs`
|
||||||
|
|
||||||
|
Expected: PASS без broken links; новый раздел виден как отдельная категория «Дизайн-система».
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add apps/docs packages/design-system/CHANGELOG.md
|
||||||
|
git commit -m "docs(design-system): publish usage guidelines"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 12: CI, visual checks и финальная верификация
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `.gitea/workflows/ci.yml`
|
||||||
|
- Modify: `README.md`
|
||||||
|
- Modify: `docs/features/design-system-foundation/tasks.md`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Добавить CI gates**
|
||||||
|
|
||||||
|
После `npm ci` установить Chromium `npx playwright install --with-deps chromium`; добавить design-system
|
||||||
|
unit/story tests, Storybook build, design-system build и docs build. Сохранять Storybook static build и
|
||||||
|
visual diff через `actions/upload-artifact@v4` с `if: failure()`.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Обновить команды README**
|
||||||
|
|
||||||
|
Добавить `npm run storybook`, `build:storybook`, `test:design-system`, `test:storybook`,
|
||||||
|
`build:design-system` и пояснить, что Docusaurus — published docs, Storybook — engineering workbench.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Запустить полный DoD**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run format:check
|
||||||
|
npm run lint
|
||||||
|
npm run test:backend
|
||||||
|
npm run test:frontend
|
||||||
|
npm run test:design-system
|
||||||
|
npm run test:storybook
|
||||||
|
npm run build:backend
|
||||||
|
npm run build:design-system
|
||||||
|
npm run build:storybook
|
||||||
|
npm run build:frontend
|
||||||
|
npm run build:docs
|
||||||
|
git diff --check
|
||||||
|
git status --short
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: все команды PASS; status содержит только намеренные изменения до финального commit.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Обновить SDD tasks и запросить code review**
|
||||||
|
|
||||||
|
Отметить выполненные checkbox в `tasks.md`, применить `superpowers:requesting-code-review`, исправить
|
||||||
|
только подтверждённые scope issues и повторить полный DoD.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Final commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add .gitea/workflows/ci.yml README.md docs/features/design-system-foundation
|
||||||
|
git commit -m "ci(design-system): enforce foundation quality gates"
|
||||||
|
```
|
||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Статус
|
## Статус
|
||||||
|
|
||||||
Draft — ожидает ревью пользователя.
|
Approved — утверждена пользователем 2026-06-21.
|
||||||
|
|
||||||
## Цель
|
## Цель
|
||||||
|
|
||||||
|
|||||||
68
docs/features/design-system-foundation/tasks.md
Normal file
68
docs/features/design-system-foundation/tasks.md
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
# Design System Foundation — Tasks
|
||||||
|
|
||||||
|
Исполнять по `plan.md` последовательно, используя TDD и отмечая checkbox только после успешной
|
||||||
|
проверки соответствующего шага.
|
||||||
|
|
||||||
|
## 1. Pre-flight и workspace
|
||||||
|
|
||||||
|
- [ ] Подтвердить ветку, чистый worktree и зелёные baseline tests/lint/build.
|
||||||
|
- [ ] Добавить `packages/design-system` в npm workspaces и root scripts.
|
||||||
|
- [ ] Создать package manifest, TypeScript/Vitest/ESLint configs и test setup.
|
||||||
|
- [ ] Установить зависимости; проверить пустой package build/test/lint.
|
||||||
|
- [ ] Закоммитить workspace shell.
|
||||||
|
|
||||||
|
## 2. Tokens
|
||||||
|
|
||||||
|
- [ ] Написать RED-тесты alias resolver: chain, missing target, cycle, type mismatch.
|
||||||
|
- [ ] Реализовать token schema и resolver; получить GREEN.
|
||||||
|
- [ ] Написать RED-тесты целостности трёх token levels.
|
||||||
|
- [ ] Добавить primitive/reference tokens.
|
||||||
|
- [ ] Добавить light semantic tokens только через aliases.
|
||||||
|
- [ ] Добавить component tokens только через semantic aliases.
|
||||||
|
- [ ] Проверить guards, build и отсутствие hardcoded values вне primitives.
|
||||||
|
- [ ] Закоммитить token foundation.
|
||||||
|
|
||||||
|
## 3. MUI adapter и Storybook
|
||||||
|
|
||||||
|
- [ ] Написать RED-тесты MUI theme contract.
|
||||||
|
- [ ] Реализовать `createMoexVibeTheme`, type augmentation и provider; получить GREEN.
|
||||||
|
- [ ] Настроить Storybook React/Vite с общим theme decorator.
|
||||||
|
- [ ] Подключить addon-a11y и Vitest browser project с Playwright Chromium.
|
||||||
|
- [ ] Проверить package и static Storybook build.
|
||||||
|
- [ ] Закоммитить theme adapter и Storybook workbench.
|
||||||
|
|
||||||
|
## 4. Core UI
|
||||||
|
|
||||||
|
- [ ] Реализовать через TDD `Text`, `Heading`, `Link`, `Button`, `IconButton`.
|
||||||
|
- [ ] Добавить их stories, type checks и a11y checks.
|
||||||
|
- [ ] Реализовать через TDD `TextField`, `Select`, `Checkbox`.
|
||||||
|
- [ ] Реализовать через TDD `Surface`, `Card`, `Chip`, `Badge`.
|
||||||
|
- [ ] Реализовать через TDD `Alert`, `Dialog`, `Skeleton`, `Progress`.
|
||||||
|
- [ ] Добавить обязательные states, responsive stories и visual baselines.
|
||||||
|
- [ ] Проверить unit/story/a11y/build gates и закоммитить Core UI.
|
||||||
|
|
||||||
|
## 5. Product patterns
|
||||||
|
|
||||||
|
- [ ] Реализовать через TDD `DataTable`, включая caption, sorting semantics и density variants.
|
||||||
|
- [ ] Реализовать через TDD `Metric`, `Money`, `PriceChange` без color-only semantics.
|
||||||
|
- [ ] Добавить financial stories и стабильные screenshot baselines.
|
||||||
|
- [ ] Реализовать через TDD `FormField` и `FilterBar`.
|
||||||
|
- [ ] Реализовать через TDD `EmptyState`, `ErrorState`, `LoadingState`.
|
||||||
|
- [ ] Проверить unit/story/a11y/visual gates и закоммитить patterns.
|
||||||
|
|
||||||
|
## 6. Интеграция и governance
|
||||||
|
|
||||||
|
- [ ] Написать RED-тест frontend provider integration.
|
||||||
|
- [ ] Подключить workspace package и Inter во frontend shell; удалить локальную MUI theme.
|
||||||
|
- [ ] Enforce ESLint allowlist для `Box`, `Stack`, `Grid`.
|
||||||
|
- [ ] Подтвердить, что product pages и legacy CSS не мигрированы.
|
||||||
|
- [ ] Создать Docusaurus-раздел с foundations, catalog, rules и accessibility.
|
||||||
|
- [ ] Создать ADR-016 и changelog `0.1.0`.
|
||||||
|
- [ ] Обновить sidebar, styling docs и README; собрать docs.
|
||||||
|
|
||||||
|
## 7. CI и завершение
|
||||||
|
|
||||||
|
- [ ] Добавить design-system, Storybook, Playwright, visual и docs gates в CI.
|
||||||
|
- [ ] Запустить полный DoD из Task 12 `plan.md`.
|
||||||
|
- [ ] Запросить code review и устранить подтверждённые scope issues.
|
||||||
|
- [ ] Повторить полный DoD, отметить все tasks и сделать финальный commit.
|
||||||
Loading…
x
Reference in New Issue
Block a user