Storybook Addon
Пакет @sxl-studio/storybook-addon добавляет в Storybook панель SXL Studio с Figma-embed, описанием, бейджами Tokens и Status, JSON композиции и файлами Code Connect для компонента текущей story.
@sxl-studio/storybook-addon добавляет в Storybook вкладку SXL Studio. Для компонента текущей story панель показывает Figma-embed, описание, бейджи Tokens и Status, API компонента, JSON композиции и файлы Code Connect.
Данные берутся из файла реестра diff-code-connect.<fileKey>.json. Плагин SXL Studio записывает его, когда вы подключаете компонент во вкладке Code Connect, вы коммитите файл в репозиторий, а Storybook его читает. Аддон только читает: он не меняет ни файл Figma, ни реестр.
| Требование | Значение |
|---|---|
| Storybook | 9 или 10 (^9 || ^10) |
| React | 18 или 19 (^18 || ^19): сама панель написана на React, stories могут быть на любом фреймворке из списка ниже |
| Фреймворки stories | React, Vue, Angular, Web Components, Svelte |
| Реестр | diff-code-connect.<fileKey>.json из плагина или экспортированный sxl-codeconnect.json |
| Текущая версия | 2.0.2 |
Установка
npm install @sxl-studio/storybook-addon --save-dev
Настройка
1. Зарегистрируйте аддон в main
Добавьте пакет в addons. Пресет поставляется вместе с пакетом и подключается автоматически.
// .storybook/main.ts
export default {
addons: [
"@storybook/addon-docs",
"@sxl-studio/storybook-addon",
],
};
Если конфигурация Storybook приходит из общего внутреннего пакета, дописывайте аддон к существующему списку, а не заменяйте его:
// .storybook/main.ts
import sharedMain from "@your-org/storybook-vue/main";
const config = {
...sharedMain,
addons: [...(sharedMain.addons ?? []), "@sxl-studio/storybook-addon"],
};
export default config;
Если пресет не подхватился, укажите в addons явно @sxl-studio/storybook-addon/preset.
2. Подключите реестр в preview
Импортируйте файл реестра и положите его в parameters.sxl.registry. Путь импорта зависит от вашего репозитория, например ../../tokens/tokens/diff-code-connect.<fileKey>.json.
// .storybook/preview.ts
import registry from "../path/to/diff-code-connect.<fileKey>.json";
export default {
parameters: {
sxl: { registry },
},
};
Для актуального файла из плагина достаточно прямого импорта. fromDiffCodeConnect(raw) нормализует файл явно; используйте её, если хотите единый путь кода или смешиваете форматы реестра:
// .storybook/preview.ts
import raw from "../path/to/diff-code-connect.<fileKey>.json";
import { fromDiffCodeConnect } from "@sxl-studio/storybook-addon";
export default {
parameters: {
sxl: { registry: fromDiffCodeConnect(raw) },
},
};
При общем preview смержите parameters, чтобы не потерять общие декораторы и globals:
// .storybook/preview.ts
import sharedPreview from "@your-org/storybook-vue/preview";
import registry from "../../tokens/tokens/diff-code-connect.<fileKey>.json";
export default {
...sharedPreview,
parameters: {
...sharedPreview.parameters,
sxl: { registry },
},
};
Имя файла содержит ключ файла Figma. Когда плагин начнёт записывать новое имя, обновите импорт или положитесь на alias пресета, описанный ниже.
3. Перезапустите Storybook
Перезапускайте dev-сервер после каждого изменения main или preview. Откройте story подключённого компонента: у вкладки SXL Studio появится зелёная точка, а в панели данные.
Что делает пресет
Пресет запускается, как только аддон указан в addons. Особая структура папок не нужна.
- Figma-embed. В Content-Security-Policy dev-сервера Vite добавляется
frame-srcдляhttps://www.figma.comиhttps://*.figma.com, чтобы iframe мог загрузиться. - JSON композиции. Если рядом с пакетом Storybook или на два уровня выше
.storybookесть папкаtokens/tokensс выгрузкой плагина, её файлы отдаются по адресу/sxl-tokens/…в dev и копируются в статику приstorybook build. ТогдаcompositionFilePathиз реестра разрешается без дополнительной настройки Vite. - Стабильное имя реестра. Если
previewимпортируетdiff-code-connect.SXL-Components.json, такого файла нет, но в той же папке есть другойdiff-code-connect.*.json, пресет добавляет на него alias Vite (при нескольких файлах берётся первый по сортировке имён).
Сопоставление story и компонентов
Аддон подбирает запись реестра для текущей story в таком порядке:
sxl.figmaNodeId: запись с этим ID ноды.sxl.component(или его синонимsxl.componentName): запись с такимdisplayName, без учёта регистра.- Контекст story: последний сегмент заголовка story, имя story, её ID и путь к файлу сравниваются с именами, import path и именами файлов записей. Слабое или неоднозначное совпадение отклоняется, и одна запись реестра никогда не применяется ко всем stories.
Обычно файлы stories менять не нужно: держите display name в плагине равным имени компонента в Storybook. Если имена различаются, привяжите story явно:
export const Default = {
parameters: {
sxl: { component: "WButton" },
},
};
Или по ID ноды Figma:
export const Default = {
parameters: {
sxl: { figmaNodeId: "1:23" },
},
};
Для прототипов данные можно передать напрямую, без реестра:
export const Default = {
parameters: {
sxl: {
figmaUrl: "https://www.figma.com/design/abc123?node-id=1-23",
description: "Primary action button",
tokensBool: "true",
readiness: "ready-for-dev",
},
},
};
Точка рядом с названием вкладки показывает состояние: зелёная означает, что story связана, жёлтая означает, что ни одна запись не подошла, серая означает, что parameters.sxl.registry не задан. У stories вообще без parameters.sxl вкладки нет: Storybook её скрывает.
Что показывает панель
Данные заполняются в плагине: откройте компонент во вкладке Code Connect и используйте блок Storybook Integration. У него свои кнопки Connect и Disconnect, он работает независимо от файлов Code Connect и не требует URL репозитория. В блоке также показывается готовый сниппет Story parameters для story. Подробнее: Code Connect & Storybook.
| В плагине | В панели Storybook |
|---|---|
| Display name | Заголовок панели |
| Дата последнего изменения привязки | Updated <дата> под заголовком |
| Tokens (Ready / Not ready) | Бейдж Tokens с текстом Have Tokens или No Tokens |
| Status | Бейдж Backlog, In Progress, Ready for Dev или Completed |
| Metadata с Description | Блок Description |
| Design Embed | Блок Embed с iframe Figma и ссылками Open embed in new tab и Open in Figma Dev Mode; при выключенном embed кнопка Open in Figma |
| Composition JSON и выбранный файл композиции | Блок Composition JSON с кнопкой Copy JSON |
| Свойства компонента Figma | Таблица API: имя, тип, значение по умолчанию и варианты |
| Файлы Code Connect, import path и сниппет | Блок Code Connect: Import, по строке на каждый файл с его фреймворком, шаблон сниппета |
| ID ноды | Node: <id> внизу панели |
Загрузка JSON композиции
Плагин записывает в реестр только compositionFilePath. Сам JSON композиции остаётся в репозитории, и Storybook должен загрузить его во время работы. Аддон перебирает источники в таком порядке:
sxl.compositionSources: карта из пути относительно репозитория в сырой текст JSON.sxl.resolveComposition: ваша собственная функция загрузки.sxl.compositionFetchBaseUrl:fetch()из статической папки.sxl.compositionDevProxyPrefix:fetch()через same-origin dev-прокси./sxl-tokens/…: папка, которую отдаёт пресет.- Сырой файл по
repository.urlиз реестра (GitLab), затем тот же путь относительно корня Storybook.
Если ничего не сработало, панель подсказывает, что настроить.
Вариант A: glob и raw-импорт (Vite)
// .storybook/preview.ts
import raw from "../path/to/diff-code-connect.<fileKey>.json";
import { fromDiffCodeConnect } from "@sxl-studio/storybook-addon";
const sources = import.meta.glob("../packages/ds/**/*.json", {
query: "?raw",
import: "default",
eager: true,
}) as Record<string, string>;
function indexByRepoPath(map: Record<string, string>): Record<string, string> {
const out: Record<string, string> = {};
for (const [key, value] of Object.entries(map)) {
// ключ должен совпадать с compositionFilePath из реестра
const rel = key.replace(/^.*?\/packages\//, "packages/");
out[rel] = value;
}
return out;
}
export default {
parameters: {
sxl: {
registry: fromDiffCodeConnect(raw),
compositionSources: indexByRepoPath(sources),
},
},
};
Ключи должны совпадать с compositionFilePath из реестра: тот же путь относительно репозитория, что и в монорепозитории.
Вариант B: статическая папка и fetch
// .storybook/main.ts
export default {
staticDirs: [{ from: "../path/to/compositions", to: "/sxl-compositions" }],
};
// .storybook/preview.ts
export default {
parameters: {
sxl: {
compositionFetchBaseUrl: `${import.meta.env.BASE_URL}sxl-compositions/`,
},
},
};
Вариант C: приватный GitLab и CORS
Аддон умеет строить raw-URL GitLab из repository.url реестра, но браузер не прочитает кросс-доменный ответ без Access-Control-Allow-Origin. Направьте запросы через same-origin dev-прокси и укажите его аддону:
// .storybook/preview.ts, префикс должен совпадать с путём прокси ниже
export default {
parameters: {
sxl: {
compositionDevProxyPrefix: `${import.meta.env.BASE_URL}__sxl_git_raw/`,
},
},
};
// .storybook/main.ts, подставьте свой хост и авторизацию
import { mergeSxlFigmaFrameSrcHeader } from "@sxl-studio/storybook-addon/preset";
export default {
async viteFinal(config) {
const base = await mergeSxlFigmaFrameSrcHeader(config);
return {
...base,
server: {
...base.server,
proxy: {
...base.server?.proxy,
"/__sxl_git_raw": {
target: "https://git.example.com",
changeOrigin: true,
secure: true,
rewrite: (path) => path.replace(/^\/__sxl_git_raw/, ""),
},
},
},
};
},
};
Figma-embed
Если iframe остаётся пустым:
- Включите
parameters.sxl.debugFigmaEmbed: trueвpreviewи найдите в консоли браузера сообщения[SXL Studio addon] Figma embed: в них итоговый URL embed и события загрузки и ошибки iframe. - Убедитесь, что dev-сервер отдаёт Content-Security-Policy, разрешающую встраивание Figma. Пресет делает это автоматически; в собственном
viteFinalсмержите заголовок сами (см. ниже). - Проверьте, что ссылка Open embed in new tab в панели открывает дизайн.
// .storybook/main.ts
import { mergeSxlFigmaFrameSrcHeader } from "@sxl-studio/storybook-addon/preset";
export default {
async viteFinal(config) {
return mergeSxlFigmaFrameSrcHeader(config);
},
};
Формат URL выбирает sxl.embedUrlMode. auto (по умолчанию) использует legacy-URL www.figma.com/embed для Safari на localhost и Embed Kit 2.0 (embed.figma.com) во всех остальных случаях; embed-kit-2 и legacy принудительно задают один формат. Аддон передаёт текущий хост Storybook как embed-host, например localhost:6006.
Если Figma просит войти после каждого обновления страницы, разрешите встроенный контент и сторонние cookies для figma.com в настройках браузера. Это политика приватности браузера, а не настройка аддона.
Справочник параметров
| Параметр | Тип | Назначение |
|---|---|---|
sxl.registry | SxlRegistry | Объект реестра, задаётся один раз в preview.ts |
sxl.component | string | Сопоставить запись по displayName |
sxl.componentName | string | Синоним component |
sxl.figmaNodeId | string | Сопоставить запись по ID ноды Figma |
sxl.figmaUrl | string | Прямой URL Figma, реестр не нужен |
sxl.description | string | Переопределить описание |
sxl.tokensBool | "true" | "false" | Переопределить бейдж Tokens |
sxl.tokenStatus | "assigned" | "partial" | "none" | Устарел; переводится в tokensBool |
sxl.readiness | "complete" | "ready-for-dev" | "in-progress" | "backlog" | Переопределить бейдж Status |
sxl.designEmbed | boolean | Переопределить флаг Design Embed записи |
sxl.compositionJson | boolean | Переопределить флаг Composition JSON записи |
sxl.metadata | boolean | Переопределить флаг Metadata записи |
sxl.compositionSources | Record<string, string> | Карта из путей относительно репозитория в сырой текст JSON |
sxl.compositionFetchBaseUrl | string | Базовый URL для загрузки композиций по пути |
sxl.compositionDevProxyPrefix | string | Same-origin префикс для загрузки из приватного Git через dev-прокси |
sxl.resolveComposition | (path) => Promise<string | undefined> | Собственный загрузчик JSON композиции |
sxl.debugFigmaEmbed | boolean | Писать диагностику embed в консоль |
sxl.embedUrlMode | "auto" | "embed-kit-2" | "legacy" | Стратегия URL embed |
Если что-то не работает
| Что случилось | Что делать |
|---|---|
| Нет вкладки SXL Studio | У story нет parameters.sxl. Задайте parameters.sxl.registry в preview.ts и проверьте, что аддон указан в addons |
Серая точка и SXL Studio not configured | parameters.sxl.registry пуст или путь импорта неверный |
Жёлтая точка и No Figma integration for this component | Ни одна запись реестра не подошла story. Сравните display name в плагине с заголовком story или задайте sxl.component либо sxl.figmaNodeId. Убедитесь, что компонент подключён в блоке Storybook Integration, а файл реестра актуален |
| Iframe Figma пустой | Content-Security-Policy запрещает встраивание. Смержите frame-src через mergeSxlFigmaFrameSrcHeader и включите debugFigmaEmbed |
Embed is enabled but no Figma URL is available | В реестре нет ключа файла Figma. Укажите URL файла Figma в Repository connection плагина и экспортируйте реестр заново |
| Figma просит войти при каждом обновлении | Разрешите встроенный контент и сторонние cookies для figma.com в браузере. В Safari на localhost аддон уже использует legacy-embed |
No composition data resolved | Включите Composition JSON в плагине, выберите файл композиции и отправьте реестр |
| Автозагрузка композиции не удалась | Приватные Git-хосты блокируют CORS. Используйте compositionSources, compositionFetchBaseUrl со staticDirs, resolveComposition() или dev-прокси с compositionDevProxyPrefix |
| Файл реестра получил новое имя | Имя содержит ключ файла Figma. Обновите импорт или положитесь на alias пресета для diff-code-connect.SXL-Components.json |