Merge pull request #33675 from backstage/fix/storybook-apptheme-api
fix(storybook): register appThemeApiRef in Storybook API providers
This commit is contained in:
+1
-1
@@ -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',
|
||||
|
||||
@@ -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' : '';
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user