Files
tnsu-ui-kit/PROJECT.md
T
Тимур Городецкий d16e4a2216 feat:generic Badge(0.3.0)
2026-08-14 12:06:07 +05:00

15 KiB
Raw Blame History

@tnsu/ui-kit — память проекта

Единый пакет UI-компонентов для SPFx web part'ов ТНСУ + Storybook как витрина. Этот файл — источник правды по решениям, плану и прогрессу. Обновлять по ходу работы.

Зачем проект

У команды много SPFx web part'ов в разных репозиториях (request-portal, бывший 1c-ticket-form, pto-tracker и др.). В каждом дизайн разъезжается, компоненты копируются и пишутся заново. Цель — вынести компоненты в один npm-пакет, подключать его в любую web part через npm i, и держать единый вид через дизайн-токены.

Источник компонентов: request-portal/src/webparts/requestPortal/shared/ui/ — там уже ~30 чистых компонентов на SCSS-модулях с barrel-экспортом (ui/index.ts). Это база.

Ключевые архитектурные решения

  • Гибридный подход: отдельный репозиторий tnsu-ui-kit (пакет + Storybook), публикуется в GitLab Package Registry. Существующие web part'ы остаются в своих репах и просто ставят пакет. НЕ монорепо (не хотим переселять существующие проекты).
  • Сборка — Vite (library mode), не tsup. Причина: компоненты на .module.scss, Vite понимает SCSS-модули из коробки и он же движок Storybook → один инструмент.
  • React/Fluent/tslib — только peerDependencies (React 17, @fluentui/react 8, tslib). Защита от двух React в бандле («Invalid hook call»). Локально те же версии стоят как devDependencies для сборки и Storybook.
  • Дизайн-токены (tokens.scss: цвета, отступы, радиусы) — единый рычаг общего вида. Компоненты тянут значения оттуда, а не хардкодят.
  • Скоуп имени @tnsu — сверить с именем группы в GitLab на шаге публикации. Пока имя ни на что не влияет, переименование = find-replace.
  • Что переносим: только чистые UI-примитивы (Button, TextInput, FormCard, Select, ItemsTable, PeopleField...). НЕ переносим бизнес-логику: approval/, pnp/, dictionary/, сервисы SharePoint — они привязаны к спискам. Возможно позже отдельный пакет @tnsu/sp-core.
  • Расширение через пропсы, не форки. Если в старом проекте компонент выглядел иначе — добавляем variant/size в ui-kit, а не плодим копии.
  • Версии инструментов синхронны с SPFx: TypeScript пинить на ~5.8.0 (как в request-portal). Без пина npm i typescript ставит TS 7 (нативный компилятор без JS Compiler API) — vite-plugin-dts падает. То же правило для React/Fluent.

План (маршрут)

  1. Скелет репо — папка, git, package.json, зависимости
  2. Сборка (Vite) — vite.config.ts, пакет собирается в dist/
  3. Первый компонент — Button перенесён, сборка чистая
  4. Дизайн-токены — src/tokens/_tokens.scss перенесён из request-portal
  5. Storybook — витрина работает (SB 10.4 + Vite), Button виден со всеми вариантами
  6. Публикация — @tnsu/ui-kit@0.0.1 опубликован в Gitea (это Gitea 1.26.4, НЕ GitLab!)
  7. [~] Потребление проверено ПОЛНОСТЬЮ: request-portal собрался (heft webpack ок) + кнопки из пакета отрисовались в SharePoint workbench со стилями (accent-золото, secondary-рамки). CSS из пакета применяется в рантайме. Осталась публикация в registry.

ВАЖНО про потребление (проверено 2026-08-13):

  • Тест через локальный тарбол (npm pack → npm i ../tnsu-ui-kit/*.tgz), НЕ npm link (link тащит второй React → Invalid hook call).
  • CSS подключается в SPFx через import '@tnsu/ui-kit/styles.css' в корневом компоненте веб-парта (RequestPortal.tsx). SPFx-webpack (5.95) обрабатывает .css из node_modules — ОК.
  • Заменён Button в SubmitActions.tsx (../Button/Button → @tnsu/ui-kit). Правки НЕ коммитить, пока пакет не опубликован в registry (сейчас ссылается на file:-тарбол).

Известный tech-debt: Sass @import устарел (Dart Sass 3.0). Мигрировать токены и модули на @use/@forward — отдельной задачей, сейчас работает с варнингом.

Прогресс / журнал

  • Шаг 1 готов: создан репо C:\Users\gorodetskiy.timur\Documents\Projects\tnsu-ui-kit, git init, болванка package.json.
  • Шаг 2 в работе: переписан package.json (@tnsu/ui-kit, exports, peerDeps, Vite-скрипты), ставятся devDependencies, добавлен .gitignore.

План переноса компонентов (инвентаризация + решения, 2026-08-13)

Источник — request-portal shared/ui/, shared/approval/ui/, shared/dictionary/ui/, и (фаза 2) components/.

Группа 1 — чистая презентация, переносим КАК ЕСТЬ

Alert, FormCard, FormGrid, FormField, TextInput, TextArea, PhoneInput, MoneyInput, Select, Toggle, CollapseSection, FormActions, Icon (+ IconSprite/iconNames), RequestNumber, StatusBadge, ErrorOverlay, FormMode (FormModeProvider + useFormReadOnly).

Группа 2 — презентация с нюансами (решения приняты)

  • ConfirmDialog (+ confirmModal), FileDropzone, useFlipRows → переносим КАК ЕСТЬ (проверено: чисты).
  • AttachmentList → переносим, но РАЗВЯЗАТЬ тип: вместо import { IAttachmentInfo } из attachments.service объявить локальный { url: string; fileName: string }.
  • PrintButton → переносим КАК ЕСТЬ; сохраняем соглашение классов .rp-print-root / .rp-no-print — задокументировать в README, чтобы потребители их соблюдали.
  • ItemsTable → ОБОБЩАЕМ в generic DataTable (колонки через проп: {key,title,width?, align?,editable?,render?}, value/onChange/readOnly). Excel-вставка — ОПЦИОНАЛЬНАЯ/ подключаемая (не хардкодить parseExcelRows под ТМЦ; проект даёт свой маппинг/парсер). ТМЦ-таблица = конфиг колонок поверх DataTable. Это ОТДЕЛЬНАЯ design-задача (не copy-paste), делать позже, сверяя абстракцию со 2-м реальным примером таблицы.

Группа 3 — бизнес-логика / SharePoint, НЕ в ui-kit (как есть)

PeopleField/PeopleFieldMulti, UserPicker, InitiatorLine, SubmitActions, useRequestSubmit, DecisionPanel, ApprovalMatrix, DepartmentPicker, ProjectPicker.

  • SubmitActions — на обсуждении: можно «очистить» от useRequestSubmit → презентационный → в ui-kit.
  • Пикеры — возможно позже РАЗДЕЛИТЬ: презентационный UI (ui-kit) + data-обёртка (проект).
  • Логику согласования — возможно в отдельный пакет @tnsu/approval (или @tnsu/sp-core). На будущее.

Адаптивность (жёсткое требование) — базовый проход СДЕЛАН

Часть DoD каждого компонента. Основа: в tokens.scss брейкпоинты $bp-xs:350 / $bp-sm:640 / $bp-md:768 / $bp-lg:1024 / $bp-xl:1280 + миксины below()/above(). Проход по компонентам (в 0.2.0):

  • FormGrid / CollapseSection — 2 колонки → 1 на < sm (через below($bp-sm)).
  • TextInput/TextArea/Select — сделаны резиновыми (width:100% + box-sizing) — раньше зависели от глобального стиля портала.
  • FormCard — padding 32→18 на < sm.
  • FormActions — кнопки в столбец на всю ширину на < sm (поля согласованы с FormCard 18px).
  • ConfirmDialog — уже адаптивна (overlay padding + max-width).
  • Sidebar — рейл ↔ выезжающий drawer + SidebarToggle (бургер, виден только < md).
  • Мелкие (Alert, Icon, Toggle, Button, AttachmentList, FileDropzone, PrintButton) — уже резиновые. ItemsTable→скролл сделаем вместе с обобщением в DataTable.

Группа 4 — навигация / app shell (ФАЗА 2)

  • [СДЕЛАНО, не опубликовано] Sidebar — обобщён (items/brand пропсами, id=string) + адаптивный: десктоп-рейл со сворачиванием, < 768px — выезжающий drawer + затемнение (mobileOpen/onMobileClose, бургер рисует родитель). Собирается, в index экспортирован. Tabs (из CategoryTabs), Breadcrumbs (новый), Sidebar (из SideNav), AppLayout (из RequestPortal), Drawer(?). Сначала «расцепить» от роутинга портала → сделать controlled (items + activeId + onSelect), потом переносить. Даёт новым приложениям готовую оболочку.

Миграция request-portal (в процессе)

  • [СДЕЛАНО] Формы переведены на пакет через переключение barrel shared/ui/index.ts (перенесённые компоненты реэкспортятся из @tnsu/ui-kit). FormMode объединён на пакетный.
  • [СДЕЛАНО] Чистка: 12 прямых импортеров (SideNav, табы, Drawer, RequestGrid, пикеры, unsavedChanges, RequestPortal→IconSprite, ItemsTable, SubmitActions) → @tnsu/ui-kit; удалены 20 локальных папок-дублей. В shared/ui осталось: ItemsTable, PeopleField, SubmitActions, ErrorOverlay, InitiatorLine (+useRequestSubmit, index). Heft собирается.
  • [СДЕЛАНО] B: локальный components/SideNav заменён на пакетный Sidebar + SidebarToggle. В RequestPortal: navItems (с counts), состояние mobileNavOpen, бургер в шапке main (.mobileNav, виден < 768px). Shell CSS: убран старый перелом 900px; на < 768 .side схлопывается (drawer position:fixed), main на всю ширину. SideNav удалён. Heft ок.

Badge (0.3.0) — паттерн «презентация в kit, смысл в проекте»

Generic Badge в kit (variant: neutral/muted/info/success/warning/danger/primary). Портальный StatusBadge переписан в тонкий маппер REQUEST_STATUS→variant поверх Badge; StatusBadge.module.scss удалён. Это шаблон для будущего разделения пикеров (group 3): вид в kit, привязка к данным в проекте.

Порядок работ

  1. [СДЕЛАНО в 0.1.0] Быстрая пачка (20 компонентов): вся Группа 1 (кроме ErrorOverlay/ StatusBadge — оказались связаны с diagnostics/statuses, отложены) + ConfirmDialog/ FileDropzone/useFlipRows + AttachmentList (тип развязан на локальный {url,fileName}) + PrintButton. Сборка чистая, типы ок.
  2. DataTable — отдельной design-задачей.
  3. Группа 3/4 — по мере надобности.

Как мигрировать старый проект (памятка на будущее)

  1. npm i @tnsu/ui-kit — ничего не ломается, пакет просто лежит рядом.
  2. Заменяем импорты по одному компоненту/форме: ../shared/ui → @tnsu/ui-kit. Пропсы те же, JSX не меняется. Старый и новый компонент могут сосуществовать.
  3. Проверили форму → удалили старый компонент из shared/ui.
  4. Повторяем, пока shared/ui не опустеет.
  5. Перед миграцией проекта проверить: его версии React/Fluent совместимы с peerDeps ui-kit.

Публикация (Gitea, НЕ GitLab!)

  • Хостинг git — Gitea 1.26.4 на http://git.tnsu.kz (не GitLab). Registry npm привязан к ВЛАДЕЛЬЦУ, не к проекту. Никакого "project ID" не нужно.
  • Адрес registry: http://git.tnsu.kz/api/packages/timur.gorodetskiy/npm/
  • .npmrc в проекте (можно коммитить, НЕ секрет): @tnsu:registry=http://git.tnsu.kz/api/packages/timur.gorodetskiy/npm/
  • Токен (СЕКРЕТ) — в ~/.npmrc (C:\Users\gorodetskiy.timur.npmrc), в git НЕ попадает: //git.tnsu.kz/api/packages/timur.gorodetskiy/npm/:_authToken=<TOKEN>
  • Токен создаётся в Gitea: аватар → Настройки → Приложения → права package = Чтение и запись.
  • Публикация: npm run build && npm publish. Бамп версии в package.json перед каждой публикацией (npm не даёт перезаписать уже опубликованную версию).
  • Потребителю (request-portal и др.) нужен такой же @tnsu:registry в его .npmrc + свой токен на чтение пакетов.
  • ПРОВЕРЕНО: коллега (другой пользователь Gitea) успешно установил @tnsu/ui-kit со своим read-токеном — кросс-доступ к личным пакетам работает, переносить в организацию пока НЕ нужно.

Окружение