Merge pull request #30879 from backstage/storybook

Merge all Storybook instances at the root level
This commit is contained in:
Patrik Oldsberg
2025-08-14 14:19:38 +02:00
committed by GitHub
69 changed files with 1015 additions and 6337 deletions
+3 -9
View File
@@ -192,20 +192,14 @@ jobs:
path: .cache/package-docs
key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-${{ github.run_id }}
# Also build and upload storybook
- name: storybook yarn install
run: yarn install --immutable
working-directory: storybook
- name: storybook build
- name: Build Storybook
run: yarn build-storybook
working-directory: storybook
- name: storybook upload
- name: Upload Storybook
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: storybook
path: storybook/dist/
path: dist-storybook
if-no-files-found: error
retention-days: 1
+3 -9
View File
@@ -192,20 +192,14 @@ jobs:
path: .cache/package-docs
key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-next-${{ github.run_id }}
# Also build and upload storybook
- name: storybook yarn install
run: yarn install --immutable
working-directory: storybook
- name: storybook build
- name: Build Storybook
run: yarn build-storybook
working-directory: storybook
- name: storybook upload
- name: Upload Storybook
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: storybook
path: storybook/dist/
path: dist-storybook
if-no-files-found: error
retention-days: 1
+12 -14
View File
@@ -4,7 +4,8 @@ on:
pull_request:
paths:
- '.github/workflows/verify_storybook.yml'
- 'storybook/**'
- '.storybook/**'
- 'packages/ui/src/**'
- 'packages/config/src/**'
- 'packages/theme/src/**'
- 'packages/types/src/**'
@@ -32,33 +33,30 @@ jobs:
with:
egress-policy: audit
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0 # Required to retrieve git history
- name: use node.js ${{ matrix.node-version }}
- name: Use node.js ${{ matrix.node-version }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
- name: yarn install
- name: Install dependencies
uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17
with:
cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }}
- name: storybook yarn install
run: yarn install --immutable
working-directory: storybook
# Only for verification, the BUI one is what we upload to Chromatic
- run: yarn build-storybook
- name: Build Storybook
run: yarn build-storybook
- run: yarn --cwd packages/ui build-storybook
- uses: chromaui/action@1cfa065cbdab28f6ca3afaeb3d761383076a35aa # v11
- name: Deploy Storybook to Chromatic
uses: chromaui/action@1cfa065cbdab28f6ca3afaeb3d761383076a35aa # v11
with:
token: ${{ secrets.GITHUB_TOKEN }}
# projectToken intentionally shared to allow collaborators to run Chromatic on forks
# https://www.chromatic.com/docs/custom-ci-provider#run-chromatic-on-external-forks-of-open-source-projects
projectToken: chpt_dab72dc0f97d55b
workingDir: packages/ui
storybookBuildDir: storybook-static
storybookBuildDir: dist-storybook
+3
View File
@@ -184,3 +184,6 @@ knip.json
type-docs
docs.json
tsconfig.typedoc.tmp.json
# Storybook
dist-storybook/
@@ -2,10 +2,9 @@ import type { StorybookConfig } from '@storybook/react-vite';
import { join, dirname, posix } from 'path';
/**
* This set of stories are the ones that we publish to backstage.io.
*/
// This set of stories are the ones that we publish to backstage.io.
const backstageCoreStories = [
'packages/ui',
'packages/core-components',
'packages/app',
'plugins/org',
@@ -15,7 +14,7 @@ const backstageCoreStories = [
'plugins/catalog-react',
];
const rootPath = '../../';
const rootPath = '../';
const storiesSrcMdx = 'src/**/*.mdx';
const storiesSrcGlob = 'src/**/*.stories.@(js|jsx|mjs|ts|tsx)';
@@ -27,10 +26,7 @@ const stories = backstageCoreStories.flatMap(element => [
getStoriesPath(element, storiesSrcGlob),
]);
/**
* This function is used to resolve the absolute path of a package.
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
*/
// Resolve absolute path of a package. Needed in monorepos.
function getAbsolutePath(value: string): any {
return dirname(require.resolve(join(value, 'package.json')));
}
@@ -40,7 +36,6 @@ const config: StorybookConfig = {
addons: [
getAbsolutePath('@storybook/addon-links'),
getAbsolutePath('@storybook/addon-essentials'),
getAbsolutePath('@chromatic-com/storybook'),
getAbsolutePath('@storybook/addon-interactions'),
getAbsolutePath('@storybook/addon-themes'),
getAbsolutePath('@storybook/addon-storysource'),
@@ -50,4 +45,5 @@ const config: StorybookConfig = {
options: {},
},
};
export default config;
+126
View File
@@ -0,0 +1,126 @@
import React, { useEffect } from 'react';
import { TestApiProvider } from '@backstage/test-utils';
import { Content, AlertDisplay } from '@backstage/core-components';
import { apis } from './support/apis';
import type { Decorator, Preview } from '@storybook/react';
import { useGlobals } from '@storybook/preview-api';
import { UnifiedThemeProvider, themes } from '@backstage/theme';
// Default Backstage theme CSS (from packages/ui)
import '../packages/ui/src/css/styles.css';
// Custom Storybook chrome/styles
import './storybook.css';
// Custom themes
import './themes/spotify.css';
const preview: Preview = {
globalTypes: {
themeMode: {
name: 'Theme Mode',
description: 'Global theme mode for components',
defaultValue: 'light',
toolbar: {
icon: 'circlehollow',
items: [
{ value: 'light', icon: 'circlehollow', title: 'Light' },
{ value: 'dark', icon: 'circle', title: 'Dark' },
],
showName: true,
dynamicTitle: true,
},
},
themeName: {
name: 'Theme Name',
description: 'Global theme name for components',
defaultValue: 'backstage',
toolbar: {
icon: 'paintbrush',
items: [
{ value: 'backstage', title: 'Backstage' },
{ value: 'spotify', title: 'Spotify' },
],
showName: true,
dynamicTitle: true,
},
},
},
initialGlobals: {
themeMode: 'light',
themeName: 'backstage',
},
parameters: {
layout: 'fullscreen',
backgrounds: {
disable: true,
},
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
options: {
storySort: {
order: ['Backstage UI', 'Plugins', 'Layout', 'Navigation'],
},
},
viewport: {
viewports: {
initial: {
name: 'Initial',
styles: { width: '320px', height: '100%' },
},
xs: { name: 'Extra Small', styles: { width: '640px', height: '100%' } },
sm: { name: 'Small', styles: { width: '768px', height: '100%' } },
md: { name: 'Medium', styles: { width: '1024px', height: '100%' } },
lg: { name: 'Large', styles: { width: '1280px', height: '100%' } },
xl: {
name: 'Extra Large',
styles: { width: '1536px', height: '100%' },
},
},
},
},
decorators: [
Story => {
const [globals] = useGlobals();
const selectedTheme =
globals.themeMode === 'light' ? themes.light : themes.dark;
const selectedThemeMode = globals.themeMode || 'light';
const selectedThemeName = globals.themeName || 'backstage';
useEffect(() => {
document.body.removeAttribute('data-theme-mode');
document.body.removeAttribute('data-theme-name');
document.body.setAttribute('data-theme-mode', selectedThemeMode);
document.body.setAttribute('data-theme-name', selectedThemeName);
return () => {
document.body.removeAttribute('data-theme-mode');
document.body.removeAttribute('data-theme-name');
};
}, [selectedTheme, selectedThemeName]);
document.body.style.backgroundColor = 'var(--bui-bg)';
const docsStoryElements = document.getElementsByClassName('docs-story');
Array.from(docsStoryElements).forEach(element => {
(element as HTMLElement).style.backgroundColor = 'var(--bui-bg)';
});
return (
<UnifiedThemeProvider theme={selectedTheme}>
{/* @ts-ignore */}
<TestApiProvider apis={apis}>
<AlertDisplay />
<Content>
<Story />
</Content>
</TestApiProvider>
</UnifiedThemeProvider>
);
},
],
};
export default preview;
@@ -12,7 +12,7 @@
--sb-content-padding-inline: 250px;
}
[data-theme='dark'] {
[data-theme-mode='dark'] {
--sb-sidebar-bg: var(--bui-bg-surface-1);
}
@@ -30,7 +30,7 @@
--sb-content-padding-inline: 258px;
}
[data-theme-name='spotify'][data-theme='light'] {
[data-theme-name='spotify'][data-theme-mode='light'] {
--sb-sidebar-border: 1px solid var(--bui-border);
--sb-sidebar-border-right: 1px solid var(--bui-border);
--sb-options-border: 1px solid var(--bui-border);
@@ -211,7 +211,7 @@
}
}
[data-theme='light'][data-theme-name='spotify'] {
[data-theme-mode='light'][data-theme-name='spotify'] {
--bui-bg: var(--bui-gray-1);
--bui-bg-surface-1: var(--bui-white);
--bui-bg-surface-2: var(--bui-gray-2);
@@ -243,7 +243,7 @@
}
}
[data-theme='dark'][data-theme-name='spotify'] {
[data-theme-mode='dark'][data-theme-name='spotify'] {
--bui-bg: var(--bui-black);
--bui-bg-surface-1: var(--bui-gray-1);
--bui-bg-surface-2: var(--bui-gray-2);
+2 -2
View File
@@ -23,7 +23,7 @@
"@mdx-js/react": "^3.1.0",
"@next/mdx": "15.3.4",
"@remixicon/react": "^4.6.0",
"@storybook/react": "^8.6.8",
"@storybook/react": "^8.6.12",
"@uiw/codemirror-themes": "^4.23.7",
"@uiw/react-codemirror": "^4.23.7",
"html-react-parser": "^5.2.5",
@@ -34,7 +34,7 @@
"react": "19.1.1",
"react-dom": "19.1.1",
"shiki": "^1.26.1",
"storybook": "^8.6.8"
"storybook": "^8.6.12"
},
"devDependencies": {
"@types/mdx": "^2.0.13",
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -14,7 +14,7 @@ const config = {
name: 'Main Styles',
},
{
source: '.storybook/themes/spotify.css',
source: '../../.storybook/themes/spotify.css',
destination: 'theme-spotify.css',
name: 'Spotify Theme',
},
+2 -2
View File
@@ -44,8 +44,8 @@ export default async function RootLayout({
return (
<html
lang="en"
data-theme="light"
data-theme-name="default"
data-theme-mode="light"
data-theme-name="backstage"
suppressHydrationWarning
>
<body>
+2 -2
View File
@@ -13,8 +13,8 @@ even more straightforward.
## Light & Dark modes
By default, Backstage UI supports both light and dark modes using the `data-theme` attribute.
The light theme is applied by default if no `data-theme` attribute is specified. To create
By default, Backstage UI supports both light and dark modes using the `data-theme-mode` attribute.
The light theme is applied by default if no `data-theme-mode` attribute is specified. To create
a custom theme, you'll need to define both light and dark modes as outlined below. If
only one mode is defined, the other will fall back to the default theme.
@@ -16,11 +16,11 @@
color: #2563eb;
}
[data-theme='dark'] .chip {
[data-theme-mode='dark'] .chip {
background-color: #2c2c2c;
color: #fff;
}
[data-theme='dark'] .chip.head {
[data-theme-mode='dark'] .chip.head {
background-color: #33405b;
}
@@ -9,7 +9,7 @@
margin-bottom: 1rem;
}
[data-theme='dark'] .codeBlock {
[data-theme-mode='dark'] .codeBlock {
background-color: #121212;
}
@@ -16,7 +16,7 @@
text-decoration: underline;
}
[data-theme='dark'] .button {
[data-theme-mode='dark'] .button {
background-color: #2c2c2c;
color: #fff;
}
@@ -35,7 +35,7 @@
font-size: 14px;
}
[data-theme='dark'] .previewContent {
[data-theme-mode='dark'] .previewContent {
background-image: radial-gradient(
rgba(255, 255, 255, 0.18) 1px,
transparent 0
@@ -13,7 +13,7 @@ import { Icon } from '@backstage/ui';
import { usePlayground } from '@/utils/playground-context';
const themes = [
{ name: 'Backstage', value: 'default' },
{ name: 'Backstage', value: 'backstage' },
{ name: 'Spotify', value: 'spotify' },
{ name: 'Custom theme', value: 'custom' },
];
@@ -23,7 +23,7 @@ export const ThemeNameSelector = () => {
return (
<Select
selectedKey={selectedThemeName || 'default'}
selectedKey={selectedThemeName || 'backstage'}
onSelectionChange={setSelectedThemeName}
>
<Button className={styles.Select}>
+3 -3
View File
@@ -18,7 +18,7 @@
--surface-1: #f4f4f4;
}
[data-theme='dark'] {
[data-theme-mode='dark'] {
--bg: #000;
--panel: #181818;
--panel-hover: #202020;
@@ -54,8 +54,8 @@ iframe {
line-height: 1.7;
}
[data-theme='dark'] .shiki,
[data-theme='dark'] .shiki span {
[data-theme-mode='dark'] .shiki,
[data-theme-mode='dark'] .shiki span {
color: var(--shiki-dark) !important;
/* Optional, if you also want font styles */
font-style: var(--shiki-dark-font-style) !important;
+1 -1
View File
@@ -15,7 +15,7 @@ export const customTheme = `:root {
}
}
[data-theme='dark'] {
[data-theme-mode='dark'] {
--bui-font-regular: system-ui;
--bui-font-weight-regular: 400;
--bui-font-weight-bold: 600;
+8 -8
View File
@@ -8,7 +8,7 @@ import {
import { components } from './data';
type Theme = 'light' | 'dark';
type ThemeName = 'legacy' | 'default' | 'custom';
type ThemeName = 'backstage' | 'spotify';
// Create a context with an empty array as the default value
const PlaygroundContext = createContext<{
@@ -27,7 +27,7 @@ const PlaygroundContext = createContext<{
setSelectedComponents: () => {},
selectedTheme: 'light',
setSelectedTheme: () => {},
selectedThemeName: 'default',
selectedThemeName: 'backstage',
setSelectedThemeName: () => {},
});
@@ -42,12 +42,12 @@ export const PlaygroundProvider = ({ children }: { children: ReactNode }) => {
);
const [selectedTheme, setSelectedTheme] = useState<Theme>('light');
const [selectedThemeName, setSelectedThemeName] =
useState<ThemeName>('default');
useState<ThemeName>('backstage');
// Load saved theme from localStorage after hydration
useEffect(() => {
if (isBrowser) {
const savedTheme = localStorage.getItem('theme') as Theme;
const savedTheme = localStorage.getItem('theme-mode') as Theme;
if (savedTheme) {
setSelectedTheme(savedTheme);
}
@@ -67,10 +67,10 @@ export const PlaygroundProvider = ({ children }: { children: ReactNode }) => {
useEffect(() => {
if (isBrowser) {
document.documentElement.setAttribute(
'data-theme',
'data-theme-mode',
selectedTheme || 'light',
);
localStorage.setItem('theme', selectedTheme || 'light');
localStorage.setItem('theme-mode', selectedTheme || 'light');
}
}, [selectedTheme, isBrowser]);
@@ -78,9 +78,9 @@ export const PlaygroundProvider = ({ children }: { children: ReactNode }) => {
if (isBrowser) {
document.documentElement.setAttribute(
'data-theme-name',
selectedThemeName || 'default',
selectedThemeName || 'backstage',
);
localStorage.setItem('theme-name', selectedThemeName || 'default');
localStorage.setItem('theme-name', selectedThemeName || 'backstage');
}
}, [selectedThemeName, isBrowser]);
+3 -3
View File
@@ -9,19 +9,19 @@ export type ThemeName = keyof typeof availableThemes;
export function switchTheme(themeName: ThemeName) {
// Remove existing theme links
const existingLinks = document.querySelectorAll('link[data-theme]');
const existingLinks = document.querySelectorAll('link[data-theme-mode]');
existingLinks.forEach(link => link.remove());
// Add new theme link
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = availableThemes[themeName];
link.setAttribute('data-theme', themeName);
link.setAttribute('data-theme-mode', themeName);
document.head.appendChild(link);
}
export function getCurrentTheme(): ThemeName {
const existingLink = document.querySelector('link[data-theme]');
const existingLink = document.querySelector('link[data-theme-mode]');
if (existingLink) {
const href = existingLink.getAttribute('href');
for (const [name, path] of Object.entries(availableThemes)) {
+4 -4
View File
@@ -156,10 +156,10 @@ are separated out into their own folder, see further down.
- [`integration/`](https://github.com/backstage/backstage/tree/master/packages/integration) -
Common functionalities of integrations like GitHub, GitLab, etc.
- [`storybook/`](https://github.com/backstage/backstage/tree/master/storybook) -
This folder contains only the Storybook config which helps visualize our
reusable React components. Stories are within the core package, and are
published in the [Backstage Storybook](https://backstage.io/storybook).
- [`.storybook/`](https://github.com/backstage/backstage/tree/master/.storybook) -
This folder contains the Storybook configuration which helps visualize our
reusable React components. Stories are scanned from packages and plugins across
the monorepo, and are published in the [Backstage Storybook](https://backstage.io/storybook).
- [`techdocs-node/`](https://github.com/backstage/backstage/tree/master/plugins/techdocs-node) -
Common node.js functionalities for TechDocs, to be shared between
+12 -3
View File
@@ -23,7 +23,7 @@
]
},
"scripts": {
"build-storybook": "yarn ./storybook run build-storybook",
"build-storybook": "storybook build --output-dir dist-storybook",
"build:all": "backstage-cli repo build --all",
"build:api-docs": "LANG=en_EN yarn build:api-reports --docs --exclude 'plugins/@(adr|adr-backend|adr-common|airbrake|airbrake-backend|allure|analytics-module-ga|analytics-module-ga4|analytics-module-newrelic-browser|apache-airflow|api-docs|api-docs-module-protoc-gen-doc|apollo-explorer|app-visualizer|azure-devops|azure-devops-backend|azure-devops-common|azure-sites|azure-sites-backend|azure-sites-common|badges|badges-backend|bazaar|bazaar-backend|bitbucket-cloud-common|bitrise|catalog-graph|catalog-graphql|catalog-import|catalog-unprocessed-entities|cicd-statistics|cicd-statistics-module-gitlab|circleci|cloudbuild|code-climate|code-coverage|code-coverage-backend|codescene|config-schema|cost-insights|cost-insights-common|dynatrace|entity-feedback|entity-feedback-backend|entity-feedback-common|entity-validation|example-todo-list|example-todo-list-backend|example-todo-list-common|firehydrant|fossa|gcalendar|gcp-projects|git-release-manager|github-actions|github-deployments|github-issues|github-pull-requests-board|gitops-profiles|gocd|graphiql|graphql-backend|graphql-voyager|ilert|jenkins|jenkins-backend|jenkins-common|kafka|kafka-backend|lighthouse|lighthouse-backend|lighthouse-common|linguist|linguist-backend|linguist-common|microsoft-calendar|newrelic|newrelic-dashboard|nomad|nomad-backend|octopus-deploy|opencost|pagerduty|periskop|periskop-backend|playlist|playlist-backend|playlist-common|proxy-backend|puppetdb|rollbar|rollbar-backend|sentry|shortcuts|splunk-on-call|stack-overflow|stack-overflow-backend|stackstorm|tech-radar|tech-radar-2|todo|todo-backend|xcmetrics)'",
"build:api-reports": "yarn build:api-reports:only --tsc",
@@ -55,7 +55,7 @@
"start-backend": "echo \"Use 'yarn start example-backend' instead\"",
"start:microsite": "cd microsite/ && yarn start",
"start:next": "yarn start example-app-next example-backend",
"storybook": "yarn ./storybook run storybook",
"storybook": "storybook dev -p 6006",
"sync-issue-templates": "node ./.github/ISSUE_TEMPLATE/sync.js",
"techdocs-cli": "node scripts/techdocs-cli.js",
"techdocs-cli:dev": "cross-env TECHDOCS_CLI_DEV_MODE=true node scripts/techdocs-cli.js",
@@ -129,6 +129,13 @@
"@octokit/rest": "^19.0.3",
"@playwright/test": "^1.32.3",
"@spotify/eslint-plugin": "^15.0.0",
"@storybook/addon-essentials": "^8.6.12",
"@storybook/addon-interactions": "^8.6.12",
"@storybook/addon-links": "^8.6.12",
"@storybook/addon-storysource": "^8.6.12",
"@storybook/addon-themes": "^8.6.12",
"@storybook/react": "^8.6.12",
"@storybook/react-vite": "^8.6.12",
"@techdocs/cli": "workspace:*",
"@types/cacheable-request": "^8.3.6",
"@types/memjs": "^1.3.3",
@@ -153,8 +160,10 @@
"shx": "^0.4.0",
"sloc": "^0.3.1",
"sort-package-json": "^2.8.0",
"storybook": "^8.6.12",
"typedoc": "^0.28.0",
"typescript": "~5.7.0"
"typescript": "~5.7.0",
"vite": "^7.1.2"
},
"packageManager": "yarn@4.8.1",
"engines": {
-34
View File
@@ -1,34 +0,0 @@
import type { StorybookConfig } from '@storybook/react-webpack5';
import { join, dirname } from 'path';
/**
* This function is used to resolve the absolute path of a package.
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
*/
function getAbsolutePath(value: string): string {
return dirname(require.resolve(join(value, 'package.json')));
}
const config: StorybookConfig = {
stories: ['../src/components/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
staticDirs: ['../static'],
addons: [
getAbsolutePath('@storybook/addon-webpack5-compiler-swc'),
getAbsolutePath('@storybook/addon-essentials'),
getAbsolutePath('@storybook/addon-interactions'),
],
framework: {
name: getAbsolutePath('@storybook/react-webpack5'),
options: {},
},
// https://storybook.js.org/docs/configure/integration/compilers#the-swc-compiler-doesnt-work-with-react
swc: () => ({
jsc: {
transform: {
react: {
runtime: 'automatic',
},
},
},
}),
};
export default config;
-153
View File
@@ -1,153 +0,0 @@
import React, { useEffect } from 'react';
import type { Decorator, Preview, ReactRenderer } from '@storybook/react';
import { useGlobals } from '@storybook/preview-api';
// Default Backstage theme
import '../src/css/styles.css';
// Custom mocked styles
import '../src/css/storybook.css';
// Custom themes
import './themes/spotify.css';
export const withThemes: Decorator = StoryFn => {
const [globals] = useGlobals();
const selectedTheme = globals.themeMode || 'light';
const selectedThemeName = globals.themeName || 'default';
useEffect(() => {
const htmlElement = document.documentElement;
// Remove any existing theme data attributes
htmlElement.removeAttribute('data-theme');
htmlElement.removeAttribute('data-theme-name');
// Add the selected theme data attribute
htmlElement.setAttribute('data-theme', selectedTheme);
htmlElement.setAttribute('data-theme-name', selectedThemeName);
// Cleanup on unmount
return () => {
htmlElement.removeAttribute('data-theme');
htmlElement.removeAttribute('data-theme-name');
};
}, [selectedTheme, selectedThemeName]);
return <StoryFn />;
};
const preview: Preview = {
globalTypes: {
themeMode: {
name: 'Theme Mode',
description: 'Global theme mode for components',
defaultValue: 'light',
toolbar: {
icon: 'circlehollow',
items: [
{ value: 'light', icon: 'circlehollow', title: 'Light' },
{ value: 'dark', icon: 'circle', title: 'Dark' },
],
showName: true,
dynamicTitle: true,
},
},
themeName: {
name: 'Theme Name',
description: 'Global theme name for components',
defaultValue: 'default',
toolbar: {
icon: 'paintbrush',
items: [
{ value: 'default', title: 'Default (Backstage)' },
{ value: 'spotify', title: 'Spotify' },
],
showName: true,
dynamicTitle: true,
},
},
},
initialGlobals: {
themeMode: 'light',
themeName: 'default',
},
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
backgrounds: {
disable: true,
},
options: {
storySort: {
method: 'alphabetical',
order: ['Core Concepts', 'Components'],
},
},
viewport: {
viewports: {
initial: {
name: 'Initial',
styles: {
width: '320px',
height: '100%',
},
},
xs: {
name: 'Extra Small',
styles: {
width: '640px',
height: '100%',
},
},
sm: {
name: 'Small',
styles: {
width: '768px',
height: '100%',
},
},
md: {
name: 'Medium',
styles: {
width: '1024px',
height: '100%',
},
},
lg: {
name: 'Large',
styles: {
width: '1280px',
height: '100%',
},
},
xl: {
name: 'Extra Large',
styles: {
width: '1536px',
height: '100%',
},
},
},
},
},
decorators: [
withThemes,
Story => {
document.body.style.backgroundColor = 'var(--bui-bg)';
const docsStoryElements = document.getElementsByClassName('docs-story');
Array.from(docsStoryElements).forEach(element => {
(element as HTMLElement).style.backgroundColor = 'var(--bui-bg)';
});
return <Story />;
},
],
};
export default preview;
+1 -1
View File
@@ -9182,7 +9182,7 @@
--bui-animate-pulse: pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;
}
[data-theme="dark"] {
[data-theme-mode="dark"] {
--bui-gray-1: #191919;
--bui-gray-2: #242424;
--bui-gray-3: #373737;
+1 -11
View File
@@ -28,7 +28,6 @@
],
"scripts": {
"build": "yarn build:app && yarn build:css",
"build-storybook": "storybook build",
"build:app": "backstage-cli package build",
"build:css": "node scripts/build-css.mjs",
"build:css:watch": "node scripts/build-css.mjs --watch",
@@ -36,31 +35,22 @@
"lint": "backstage-cli package lint",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack",
"start": "concurrently \"yarn build:css:watch\" \"yarn storybook\"",
"storybook": "storybook dev -p 6006",
"test": "backstage-cli package test"
},
"dependencies": {
"@base-ui-components/react": "1.0.0-alpha.7",
"@remixicon/react": "^4.6.0",
"@storybook/test": "^8.6.12",
"@tanstack/react-table": "^8.21.3",
"clsx": "^2.1.1",
"react-aria-components": "^1.10.1"
},
"devDependencies": {
"@backstage/cli": "workspace:^",
"@storybook/addon-essentials": "^8.6.12",
"@storybook/addon-interactions": "^8.6.12",
"@storybook/addon-styling-webpack": "^1.0.1",
"@storybook/addon-webpack5-compiler-swc": "^3.0.0",
"@storybook/blocks": "^8.6.12",
"@storybook/react": "^8.6.12",
"@storybook/react-webpack5": "^8.6.12",
"@storybook/test": "^8.6.12",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"chalk": "^5.4.1",
"concurrently": "^9.2.0",
"eslint-plugin-storybook": "^0.12.0",
"glob": "^11.0.1",
"globals": "^15.11.0",
@@ -19,7 +19,7 @@ import { Avatar } from './index';
import { Flex } from '../..';
const meta = {
title: 'Components/Avatar',
title: 'Backstage UI/Avatar',
component: Avatar,
} satisfies Meta<typeof Avatar>;
@@ -20,7 +20,7 @@ import { Box } from './Box';
import { Flex } from '../Flex';
const meta = {
title: 'Layout/Box',
title: 'Backstage UI/Box',
component: Box,
argTypes: {
as: {
@@ -21,7 +21,7 @@ import { Text } from '../Text';
import { Icon } from '../Icon';
const meta = {
title: 'Components/Button',
title: 'Backstage UI/Button',
component: Button,
argTypes: {
size: {
@@ -21,7 +21,7 @@ import { Text } from '../Text';
import { Icon } from '../Icon';
const meta = {
title: 'Components/ButtonIcon',
title: 'Backstage UI/ButtonIcon',
component: ButtonIcon,
argTypes: {
size: {
@@ -21,7 +21,7 @@ import { Text } from '../Text';
import { Icon } from '../Icon';
const meta = {
title: 'Components/ButtonLink',
title: 'Backstage UI/ButtonLink',
component: ButtonLink,
argTypes: {
size: {
@@ -25,7 +25,7 @@ export interface ListItem {
}
const meta = {
title: 'Components/Card',
title: 'Backstage UI/Card',
component: Card,
subcomponents: { CardHeader, CardBody, CardFooter },
} satisfies Meta<typeof Card>;
@@ -20,7 +20,7 @@ import { Flex } from '../Flex';
import { Text } from '../Text';
const meta = {
title: 'Components/Checkbox',
title: 'Backstage UI/Checkbox',
component: Checkbox,
} satisfies Meta<typeof Checkbox>;
@@ -22,7 +22,7 @@ import { Text } from '../Text';
import { Icon } from '../Icon';
const meta = {
title: 'Components/Collapsible',
title: 'Backstage UI/Collapsible',
component: Collapsible.Root,
} satisfies Meta<typeof Collapsible.Root>;
@@ -19,7 +19,7 @@ import { Box } from '../Box/Box';
import { Container } from './Container';
const meta = {
title: 'Layout/Container',
title: 'Backstage UI/Container',
component: Container,
parameters: {
layout: 'fullscreen',
@@ -19,7 +19,7 @@ import { TextField, Input, Form } from 'react-aria-components';
import { FieldError } from './FieldError';
const meta = {
title: 'Forms/FieldError',
title: 'Backstage UI/FieldError',
component: FieldError,
} satisfies Meta<typeof FieldError>;
@@ -18,7 +18,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { FieldLabel } from './FieldLabel';
const meta = {
title: 'Forms/FieldLabel',
title: 'Backstage UI/FieldLabel',
component: FieldLabel,
argTypes: {
label: {
@@ -19,7 +19,7 @@ import { Flex } from './Flex';
import { Text } from '../Text';
const meta = {
title: 'Layout/Flex',
title: 'Backstage UI/Flex',
component: Flex,
argTypes: {
align: {
@@ -21,7 +21,7 @@ import { Box } from '../Box/Box';
import { Flex } from '../Flex';
const meta = {
title: 'Layout/Grid',
title: 'Backstage UI/Grid',
component: Grid.Root,
} satisfies Meta<typeof Grid>;
@@ -31,7 +31,7 @@ import {
import { HeaderPageBreadcrumb } from '../HeaderPage/types';
const meta = {
title: 'Components/Header',
title: 'Backstage UI/Header',
component: Header,
parameters: {
layout: 'fullscreen',
@@ -23,7 +23,7 @@ import { Container } from '../Container';
import { Text } from '../Text';
const meta = {
title: 'Components/HeaderPage',
title: 'Backstage UI/HeaderPage',
component: HeaderPage,
parameters: {
layout: 'fullscreen',
@@ -20,7 +20,7 @@ import { IconProvider } from './provider';
import { icons } from './icons';
const meta = {
title: 'Components/Icon',
title: 'Backstage UI/Icon',
component: Icon,
argTypes: {
name: {
@@ -21,7 +21,7 @@ import { Text } from '../Text';
import { MemoryRouter } from 'react-router-dom';
const meta = {
title: 'Components/Link',
title: 'Backstage UI/Link',
component: Link,
args: {
children: 'Link',
@@ -20,7 +20,7 @@ import { Text, Icon, Button, Flex } from '../../index';
import { useState } from 'react';
const meta = {
title: 'Components/Menu',
title: 'Backstage UI/Menu',
component: Menu.Root,
} satisfies Meta<typeof Menu.Root>;
@@ -18,7 +18,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { RadioGroup, Radio } from './RadioGroup';
const meta = {
title: 'Forms/RadioGroup',
title: 'Backstage UI/RadioGroup',
component: RadioGroup,
} satisfies Meta<typeof RadioGroup>;
@@ -19,7 +19,7 @@ import { ScrollArea } from './ScrollArea';
import { Text } from '../Text/Text';
const meta = {
title: 'Components/ScrollArea',
title: 'Backstage UI/ScrollArea',
component: ScrollArea.Root,
} satisfies Meta<typeof ScrollArea.Root>;
@@ -25,7 +25,7 @@ import { RiCactusLine } from '@remixicon/react';
import { Button } from '../Button';
const meta = {
title: 'Forms/SearchField',
title: 'Backstage UI/SearchField',
component: SearchField,
argTypes: {
isRequired: {
@@ -20,7 +20,7 @@ import { Form } from 'react-aria-components';
import { RiCloudLine } from '@remixicon/react';
const meta = {
title: 'Forms/Select',
title: 'Backstage UI/Select',
component: Select,
} satisfies Meta<typeof Select>;
@@ -19,7 +19,7 @@ import { Skeleton } from './Skeleton';
import { Flex } from '../Flex';
const meta = {
title: 'Components/Skeleton',
title: 'Backstage UI/Skeleton',
component: Skeleton,
argTypes: {
rounded: {
@@ -18,7 +18,7 @@ import type { Meta, StoryObj } from '@storybook/react';
import { Switch } from './Switch';
const meta = {
title: 'Components/Switch',
title: 'Backstage UI/Switch',
component: Switch,
} satisfies Meta<typeof Switch>;
@@ -35,7 +35,7 @@ import { RiCactusLine } from '@remixicon/react';
import { TablePagination } from '../TablePagination';
const meta = {
title: 'Components/Table',
title: 'Backstage UI/Table',
decorators: [
(Story: StoryFn) => (
<MemoryRouter>
@@ -47,7 +47,7 @@ const meta = {
// Added this fix to fix Chromatic timeout error. This bug is due to rerendering the table with too many rows.
// Work in progress to fix it here - https://github.com/backstage/backstage/pull/30687
const data1 = data1Raw.slice(0, 25);
const data1 = data1Raw.slice(0, 10);
export default meta;
type Story = StoryObj<typeof meta>;
@@ -14,12 +14,13 @@
* limitations under the License.
*/
import { useArgs } from '@storybook/preview-api';
// TODO: Bring useArgs() back when we update Storybook to 9
// import { useArgs } from 'storybook/preview-api';
import type { Meta, StoryObj } from '@storybook/react';
import { TablePagination } from './TablePagination';
const meta = {
title: 'Components/TablePagination',
title: 'Backstage UI/TablePagination',
component: TablePagination,
argTypes: {
offset: { control: 'number' },
@@ -41,17 +42,17 @@ export const Default: Story = {
rowCount: 100,
},
render: args => {
const [{}, updateArgs] = useArgs();
// const [{}, updateArgs] = useArgs();
return (
<TablePagination
{...args}
setOffset={value => {
updateArgs({ offset: value });
}}
setPageSize={value => {
updateArgs({ pageSize: value });
}}
// setOffset={value => {
// updateArgs({ offset: value });
// }}
// setPageSize={value => {
// updateArgs({ pageSize: value });
// }}
/>
);
},
@@ -21,7 +21,7 @@ import { Box } from '../Box';
import { Text } from '../Text';
const meta = {
title: 'Components/Tabs',
title: 'Backstage UI/Tabs',
component: Tabs,
} satisfies Meta<typeof Tabs>;
@@ -19,7 +19,7 @@ import { Text } from './Text';
import { Flex } from '../Flex';
const meta = {
title: 'Components/Text',
title: 'Backstage UI/Text',
component: Text,
args: {
children: 'Text',
@@ -22,7 +22,7 @@ import { Flex } from '../Flex';
import { FieldLabel } from '../FieldLabel';
const meta = {
title: 'Forms/TextField',
title: 'Backstage UI/TextField',
component: TextField,
argTypes: {
isRequired: {
@@ -20,7 +20,7 @@ import { TooltipTrigger, Tooltip } from './Tooltip';
import { Button } from '../Button/Button';
const meta = {
title: 'Components/Tooltip',
title: 'Backstage UI/Tooltip',
component: TooltipTrigger,
parameters: { layout: 'centered' },
argTypes: {
+1 -1
View File
@@ -143,7 +143,7 @@
}
/* Dark theme tokens */
[data-theme='dark'] {
[data-theme-mode='dark'] {
/* Gray scale */
--bui-gray-1: #191919;
--bui-gray-2: #242424;
-26
View File
@@ -1,26 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*storybook.log
-2
View File
@@ -1,2 +0,0 @@
registry=https://registry.npmjs.org/
engine-strict=true
-53
View File
@@ -1,53 +0,0 @@
import { TestApiProvider } from '@backstage/test-utils';
import { Content, AlertDisplay } from '@backstage/core-components';
import { lightTheme, darkTheme } from '@backstage/theme';
import { CssBaseline, ThemeProvider } from '@material-ui/core';
import { apis } from './apis';
import { withThemeFromJSXProvider } from '@storybook/addon-themes';
import type { Preview, ReactRenderer } from '@storybook/react';
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/i,
},
},
darkMode: {
current: 'light',
},
layout: 'fullscreen',
},
decorators: [
withThemeFromJSXProvider<ReactRenderer>({
themes: {
Light: lightTheme,
Dark: darkTheme,
},
defaultTheme: 'Light',
Provider: ThemeProvider,
GlobalStyles: CssBaseline,
}),
Story => (
// @ts-ignore - TODO: Fix this
<TestApiProvider apis={apis}>
<AlertDisplay />
<Content>
<Story />
</Content>
</TestApiProvider>
),
],
};
export default preview;
export const parameters = {
options: {
storySort: {
order: ['Plugins', 'Layout', 'Navigation'],
},
},
};
-36
View File
@@ -1,36 +0,0 @@
# storybook
## 0.2.2
### Patch Changes
- Updated dependencies
- @backstage/core-plugin-api@0.6.0
- @backstage/test-utils@0.2.3
- @backstage/core-app-api@0.5.0
## 0.2.2-next.0
### Patch Changes
- Updated dependencies
- @backstage/core-plugin-api@0.6.0-next.0
- @backstage/core-app-api@0.5.0-next.0
- @backstage/test-utils@0.2.3-next.0
## 0.2.1
### Patch Changes
- Updated dependencies
- @backstage/test-utils@0.2.2
- @backstage/core-plugin-api@0.5.0
- @backstage/core-app-api@0.4.0
## 0.2.0
### Patch Changes
- Updated dependencies [ae5983387]
- Updated dependencies [0d4459c08]
- @backstage/theme@0.2.0
-17
View File
@@ -1,17 +0,0 @@
# storybook
This package provides a Storybook build for Backstage. See https://backstage.io/storybook/.
## Usage
To run the storybook locally, call `yarn storybook` in repo root. This will show the default set of stories that we publish to https://backstage.io/storybook
If you want to show stories other than the default set, you can pass one or more package paths as an argument when calling storybook, for example:
```sh
yarn storybook plugins/search
```
## Why is this not part of `@backstage/core-components`?
This separate storybook package exists because of dependency conflicts with `@backstage/cli`. It uses `nohoist` to avoid the conflicts, and since you can only use that in private packages it has to be separated out of `@backstage/core-components`.
-45
View File
@@ -1,45 +0,0 @@
{
"name": "backstage-storybook",
"version": "0.2.2",
"description": "Storybook build for components package",
"private": true,
"type": "module",
"scripts": {
"build-storybook": "storybook build --output-dir dist",
"storybook": "storybook dev -p 6006"
},
"eslintConfig": {
"extends": [
"plugin:storybook/recommended"
]
},
"dependencies": {
"@storybook/addon-storysource": "^8.3.6",
"react": "^18.0.2",
"react-dom": "^18.0.2"
},
"devDependencies": {
"@chromatic-com/storybook": "^1.9.0",
"@storybook/addon-essentials": "^8.3.5",
"@storybook/addon-interactions": "^8.3.5",
"@storybook/addon-links": "^8.3.5",
"@storybook/addon-themes": "^8.3.6",
"@storybook/blocks": "^8.3.5",
"@storybook/react": "^8.3.5",
"@storybook/react-vite": "^8.3.5",
"@storybook/test": "^8.3.5",
"@storybook/types": "^8.3.5",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^9.11.1",
"eslint-plugin-storybook": "^0.12.0",
"globals": "^15.9.0",
"react-router-dom": "^6.3.0",
"storybook": "^8.3.5",
"typescript": "^5.5.3",
"typescript-eslint": "^8.7.0",
"vite": "^5.4.8"
}
}
-5010
View File
File diff suppressed because it is too large Load Diff
+769 -826
View File
File diff suppressed because it is too large Load Diff