Merge pull request #33675 from backstage/fix/storybook-apptheme-api

fix(storybook): register appThemeApiRef in Storybook API providers
This commit is contained in:
Charles de Dreuille
2026-03-30 13:55:46 +01:00
committed by GitHub
4 changed files with 45 additions and 112 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ const isChromatic = process.env.STORYBOOK_STORY_SET === 'chromatic';
// All stories for full development
const allStories = isChromatic
? ['packages/ui']
? ['packages/ui', 'plugins/app']
: [
'packages/ui',
'packages/core-components',
+5 -1
View File
@@ -6,7 +6,7 @@ import { definePreview } from '@storybook/react-vite';
import React, { useEffect } from 'react';
import { TestApiProvider } from '@backstage/test-utils';
import { AlertDisplay } from '@backstage/core-components';
import { apis } from './support/apis';
import { apis, appThemeApi } from './support/apis';
import { useGlobals } from 'storybook/preview-api';
import { UnifiedThemeProvider, themes } from '@backstage/theme';
import { allModes } from './modes';
@@ -157,6 +157,10 @@ export default definePreview({
};
}, [selectedTheme, selectedThemeName]);
useEffect(() => {
appThemeApi.setActiveThemeId(selectedThemeMode);
}, [selectedThemeMode]);
document.body.style.backgroundColor = 'var(--bui-bg-app)';
document.body.style.padding =
isFullscreen && selectedBackground !== 'app' ? '1rem' : '';
+9
View File
@@ -1,5 +1,6 @@
import {
AlertApiForwarder,
AppThemeSelector,
ErrorAlerter,
ErrorApiForwarder,
GithubAuth,
@@ -13,6 +14,7 @@ import {
import {
alertApiRef,
appThemeApiRef,
errorApiRef,
githubAuthApiRef,
gitlabAuthApiRef,
@@ -24,10 +26,16 @@ import {
featureFlagsApiRef,
} from '@backstage/core-plugin-api';
import { themes } from '@backstage/theme';
import { translationApiRef } from '@backstage/core-plugin-api/alpha';
import { MockTranslationApi } from '@backstage/test-utils/alpha';
const configApi = new ConfigReader({});
export const appThemeApi = AppThemeSelector.createWithStorage([
{ id: 'light', title: 'Light', variant: 'light', theme: themes.light },
{ id: 'dark', title: 'Dark', variant: 'dark', theme: themes.dark },
]);
const featureFlagsApi = new LocalStorageFeatureFlags();
const alertApi = new AlertApiForwarder();
const errorApi = new ErrorAlerter(alertApi, new ErrorApiForwarder());
@@ -62,6 +70,7 @@ const translationApi = MockTranslationApi.create();
export const apis = [
[configApiRef, configApi],
[appThemeApiRef, appThemeApi],
[featureFlagsApiRef, featureFlagsApi],
[alertApiRef, alertApi],
[errorApiRef, errorApi],
@@ -28,7 +28,7 @@ const toastQueue = new ToastQueue<ToastApiMessageContent>({
});
const meta = preview.meta({
title: 'Plugins/App/Toast',
title: 'App/Toast',
component: ToastContainer,
parameters: {
layout: 'centered',
@@ -157,6 +157,35 @@ export const Default = meta.story({
),
});
const previewQueue = new ToastQueue<ToastApiMessageContent>({
maxVisibleToasts: 4,
});
previewQueue.add({
title: 'Changes saved successfully',
description: 'Your changes have been saved.',
status: 'success',
});
export const PreviewToast = meta.story({
render: () => <ToastContainer queue={previewQueue} />,
});
const timerQueue = new ToastQueue<ToastApiMessageContent>({
maxVisibleToasts: 4,
});
timerQueue.add(
{
title: 'Auto-dismissing in 5 seconds',
description: 'This toast will disappear automatically.',
status: 'info',
},
{ timeout: 5000 },
);
export const PreviewToastWithTimer = meta.story({
render: () => <ToastContainer queue={timerQueue} />,
});
export const StatusVariants = meta.story({
render: () => (
<>
@@ -557,113 +586,4 @@ export const AlertApiIntegration = meta.story({
},
});
/**
* This story tests the real AlertApi integration.
* It uses the alertApi from the TestApiProvider (set up in storybook preview)
* and shows how alerts posted via alertApi.post() appear.
*
* Note: The storybook preview.tsx renders AlertDisplay from core-components,
* which still uses Material UI. To test the new ToastDisplay, run the actual
* Backstage app where the app plugin's elements.tsx is used.
*/
function RealAlertApiStory() {
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
const { useApi, alertApiRef } = require('@backstage/core-plugin-api');
const alertApi = useApi(alertApiRef);
return (
<Flex direction="column" gap="4">
<Flex direction="column" gap="2">
<Text variant="body-medium" weight="bold">
Real AlertApi Test
</Text>
<Text variant="body-small">
These buttons call alertApi.post() directly. Alerts appear in the OLD
AlertDisplay (top of screen) because Storybook uses core-components.
</Text>
<Text variant="body-small">
To test the NEW ToastDisplay, run: yarn start
</Text>
</Flex>
<Flex gap="3">
<Button
onPress={() =>
alertApi.post({
message: 'Entity saved successfully!',
severity: 'success',
display: 'transient',
})
}
>
Success (transient)
</Button>
<Button
onPress={() =>
alertApi.post({
message: 'Catalog refresh in progress',
severity: 'info',
display: 'transient',
})
}
>
Info (transient)
</Button>
<Button
onPress={() =>
alertApi.post({
message: 'Entity validation has warnings',
severity: 'warning',
display: 'transient',
})
}
>
Warning (transient)
</Button>
<Button
onPress={() =>
alertApi.post({
message: 'Failed to fetch entity from catalog',
severity: 'error',
display: 'transient',
})
}
>
Error (transient)
</Button>
</Flex>
<Flex gap="3">
<Button
variant="secondary"
onPress={() =>
alertApi.post({
message: 'This alert stays until dismissed',
severity: 'info',
display: 'permanent',
})
}
>
Permanent Alert
</Button>
<Button
variant="secondary"
onPress={() =>
alertApi.post({
message: 'Critical error - requires attention!',
severity: 'error',
display: 'permanent',
})
}
>
Permanent Error
</Button>
</Flex>
</Flex>
);
}
export const RealAlertApi = meta.story({
name: 'Real AlertApi Test',
render: () => <RealAlertApiStory />,
});
export default meta;