Utilities

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, ни реестр.

ТребованиеЗначение
Storybook9 или 10 (^9 || ^10)
React18 или 19 (^18 || ^19): сама панель написана на React, stories могут быть на любом фреймворке из списка ниже
Фреймворки storiesReact, Vue, Angular, Web Components, Svelte
Реестрdiff-code-connect.<fileKey>.json из плагина или экспортированный sxl-codeconnect.json
Текущая версия2.0.2

Установка

BASH
npm install @sxl-studio/storybook-addon --save-dev

Настройка

1. Зарегистрируйте аддон в main

Добавьте пакет в addons. Пресет поставляется вместе с пакетом и подключается автоматически.

TS
// .storybook/main.ts
export default {
  addons: [
    "@storybook/addon-docs",
    "@sxl-studio/storybook-addon",
  ],
};

Если конфигурация Storybook приходит из общего внутреннего пакета, дописывайте аддон к существующему списку, а не заменяйте его:

TS
// .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.

TS
// .storybook/preview.ts
import registry from "../path/to/diff-code-connect.<fileKey>.json";

export default {
  parameters: {
    sxl: { registry },
  },
};

Для актуального файла из плагина достаточно прямого импорта. fromDiffCodeConnect(raw) нормализует файл явно; используйте её, если хотите единый путь кода или смешиваете форматы реестра:

TS
// .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:

TS
// .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 в таком порядке:

  1. sxl.figmaNodeId: запись с этим ID ноды.
  2. sxl.component (или его синоним sxl.componentName): запись с таким displayName, без учёта регистра.
  3. Контекст story: последний сегмент заголовка story, имя story, её ID и путь к файлу сравниваются с именами, import path и именами файлов записей. Слабое или неоднозначное совпадение отклоняется, и одна запись реестра никогда не применяется ко всем stories.

Обычно файлы stories менять не нужно: держите display name в плагине равным имени компонента в Storybook. Если имена различаются, привяжите story явно:

TS
export const Default = {
  parameters: {
    sxl: { component: "WButton" },
  },
};

Или по ID ноды Figma:

TS
export const Default = {
  parameters: {
    sxl: { figmaNodeId: "1:23" },
  },
};

Для прототипов данные можно передать напрямую, без реестра:

TS
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 должен загрузить его во время работы. Аддон перебирает источники в таком порядке:

  1. sxl.compositionSources: карта из пути относительно репозитория в сырой текст JSON.
  2. sxl.resolveComposition: ваша собственная функция загрузки.
  3. sxl.compositionFetchBaseUrl: fetch() из статической папки.
  4. sxl.compositionDevProxyPrefix: fetch() через same-origin dev-прокси.
  5. /sxl-tokens/…: папка, которую отдаёт пресет.
  6. Сырой файл по repository.url из реестра (GitLab), затем тот же путь относительно корня Storybook.

Если ничего не сработало, панель подсказывает, что настроить.

Вариант A: glob и raw-импорт (Vite)

TS
// .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

TS
// .storybook/main.ts
export default {
  staticDirs: [{ from: "../path/to/compositions", to: "/sxl-compositions" }],
};
TS
// .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-прокси и укажите его аддону:

TS
// .storybook/preview.ts, префикс должен совпадать с путём прокси ниже
export default {
  parameters: {
    sxl: {
      compositionDevProxyPrefix: `${import.meta.env.BASE_URL}__sxl_git_raw/`,
    },
  },
};
TS
// .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 остаётся пустым:

  1. Включите parameters.sxl.debugFigmaEmbed: true в preview и найдите в консоли браузера сообщения [SXL Studio addon] Figma embed: в них итоговый URL embed и события загрузки и ошибки iframe.
  2. Убедитесь, что dev-сервер отдаёт Content-Security-Policy, разрешающую встраивание Figma. Пресет делает это автоматически; в собственном viteFinal смержите заголовок сами (см. ниже).
  3. Проверьте, что ссылка Open embed in new tab в панели открывает дизайн.
TS
// .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.registrySxlRegistryОбъект реестра, задаётся один раз в preview.ts
sxl.componentstringСопоставить запись по displayName
sxl.componentNamestringСиноним component
sxl.figmaNodeIdstringСопоставить запись по ID ноды Figma
sxl.figmaUrlstringПрямой URL Figma, реестр не нужен
sxl.descriptionstringПереопределить описание
sxl.tokensBool"true" | "false"Переопределить бейдж Tokens
sxl.tokenStatus"assigned" | "partial" | "none"Устарел; переводится в tokensBool
sxl.readiness"complete" | "ready-for-dev" | "in-progress" | "backlog"Переопределить бейдж Status
sxl.designEmbedbooleanПереопределить флаг Design Embed записи
sxl.compositionJsonbooleanПереопределить флаг Composition JSON записи
sxl.metadatabooleanПереопределить флаг Metadata записи
sxl.compositionSourcesRecord<string, string>Карта из путей относительно репозитория в сырой текст JSON
sxl.compositionFetchBaseUrlstringБазовый URL для загрузки композиций по пути
sxl.compositionDevProxyPrefixstringSame-origin префикс для загрузки из приватного Git через dev-прокси
sxl.resolveComposition(path) => Promise<string | undefined>Собственный загрузчик JSON композиции
sxl.debugFigmaEmbedbooleanПисать диагностику embed в консоль
sxl.embedUrlMode"auto" | "embed-kit-2" | "legacy"Стратегия URL embed

Если что-то не работает

Что случилосьЧто делать
Нет вкладки SXL StudioУ story нет parameters.sxl. Задайте parameters.sxl.registry в preview.ts и проверьте, что аддон указан в addons
Серая точка и SXL Studio not configuredparameters.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

Связанные страницы