diff --git a/code/ui/.storybook/main.ts b/code/ui/.storybook/main.ts
index 23a70dc16661..541b0f2c69e1 100644
--- a/code/ui/.storybook/main.ts
+++ b/code/ui/.storybook/main.ts
@@ -50,6 +50,7 @@ const config: StorybookConfig = {
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-storysource',
+ '@storybook/addon-designs',
],
framework: {
name: '@storybook/react-vite',
diff --git a/code/ui/components/src/clipboard/ClipboardCode.tsx b/code/ui/components/src/clipboard/ClipboardCode.tsx
new file mode 100644
index 000000000000..577a6674f5d4
--- /dev/null
+++ b/code/ui/components/src/clipboard/ClipboardCode.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import { color, styled, typography } from '@storybook/theming';
+
+const Code = styled.pre`
+ line-height: 18px;
+ padding: 11px 1rem;
+ white-space: pre-wrap;
+ background: rgba(0, 0, 0, 0.05);
+ color: ${color.darkest};
+ border-radius: 3px;
+ margin: 1rem 0;
+ width: 100%;
+ display: block;
+ overflow: hidden;
+ font-family: ${typography.fonts.mono};
+ font-size: ${typography.size.s2 - 1}px;
+`;
+
+interface ClipboardCodeProps {
+ code: string;
+}
+
+export const ClipboardCode = ({ code, ...props }: ClipboardCodeProps) => (
+
+ {code}
+
+);
diff --git a/code/ui/components/src/index.ts b/code/ui/components/src/index.ts
index c654094f1b61..292a9c7067dd 100644
--- a/code/ui/components/src/index.ts
+++ b/code/ui/components/src/index.ts
@@ -26,7 +26,6 @@ export { Span } from './typography/elements/Span';
export { Table } from './typography/elements/Table';
export { TT } from './typography/elements/TT';
export { UL } from './typography/elements/UL';
-
export { Badge } from './Badge/Badge';
// Typography
@@ -85,6 +84,8 @@ export * from './typography/ResetWrapper';
export { withReset, codeCommon } from './typography/lib/common';
+export { ClipboardCode } from './clipboard/ClipboardCode';
+
// eslint-disable-next-line prefer-destructuring
export const components = typography.components;
diff --git a/code/ui/manager/package.json b/code/ui/manager/package.json
index 1b4dd1152305..cb86ca81fd9f 100644
--- a/code/ui/manager/package.json
+++ b/code/ui/manager/package.json
@@ -51,6 +51,7 @@
},
"devDependencies": {
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
+ "@storybook/addon-designs": "^7.0.0",
"@storybook/addons": "7.1.0-beta.1",
"@storybook/api": "7.1.0-beta.1",
"@storybook/channel-postmessage": "7.1.0-beta.1",
diff --git a/code/ui/manager/src/settings/about.stories.jsx b/code/ui/manager/src/settings/about.stories.jsx
deleted file mode 100644
index 7bb06a45ad0b..000000000000
--- a/code/ui/manager/src/settings/about.stories.jsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import React from 'react';
-import { actions as createActions } from '@storybook/addon-actions';
-
-import { AboutScreen } from './about';
-
-const info = {
- plain: `- upgrade webpack & babel to latest\n- new addParameters and third argument to .add to pass data to addons\n- added the ability to theme storybook\n- improved ui for mobile devices\n- improved performance of addon-knobs`,
-};
-
-export default {
- component: AboutScreen,
- title: 'Settings/AboutScreen',
- decorators: [
- (storyFn) => (
-
- {storyFn()}
-
- ),
- ],
-};
-
-const actions = createActions('onClose');
-
-export const UpToDate = () => (
-
-);
-
-export const OldVersionRaceCondition = () => (
-
-);
-
-export const NewVersionRequired = () => (
-
-);
-
-export const FailedToFetchNewVersion = () => (
-
-);
diff --git a/code/ui/manager/src/settings/about.stories.tsx b/code/ui/manager/src/settings/about.stories.tsx
new file mode 100644
index 000000000000..67fcabcf6667
--- /dev/null
+++ b/code/ui/manager/src/settings/about.stories.tsx
@@ -0,0 +1,39 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import React from 'react';
+import { AboutScreen } from './about';
+
+const meta = {
+ component: AboutScreen,
+ title: 'Settings/AboutScreen',
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {
+ current: {
+ version: '7.0.1',
+ },
+ },
+ parameters: {
+ design: {
+ type: 'figma',
+ url: 'https://www.figma.com/file/ur4kydUbRqdDyfoZWzdiIw/Storybook-app?type=design&node-id=9564-120444&mode=design&t=0TPINZFpwgFQFQeX-4',
+ },
+ },
+};
diff --git a/code/ui/manager/src/settings/about.tsx b/code/ui/manager/src/settings/about.tsx
index fccd4ee15111..c6f3552d6117 100644
--- a/code/ui/manager/src/settings/about.tsx
+++ b/code/ui/manager/src/settings/about.tsx
@@ -1,114 +1,172 @@
+/* eslint-disable no-nested-ternary */
import type { FC } from 'react';
-import React from 'react';
-import semver from 'semver';
-import { styled } from '@storybook/theming';
+import React, { useState } from 'react';
+import { styled, typography } from '@storybook/theming';
import type { State } from '@storybook/manager-api';
-import { StorybookIcon, SyntaxHighlighter, DocumentWrapper } from '@storybook/components';
-
-import SettingsFooter from './SettingsFooter';
+import { Button as BaseButton, Icons, StorybookIcon } from '@storybook/components';
const Header = styled.header(({ theme }) => ({
- marginBottom: 20,
- fontSize: theme.typography.size.m3,
+ marginBottom: 32,
+ fontSize: theme.typography.size.l2,
color: theme.base === 'light' ? theme.color.darkest : theme.color.lightest,
fontWeight: theme.typography.weight.bold,
alignItems: 'center',
display: 'flex',
'> svg': {
- height: 32,
+ height: 48,
width: 'auto',
marginRight: 8,
},
}));
-const UpdateMessage = styled.div<{ status: 'positive' | 'negative' | string }>(
- ({ status, theme }) => {
- if (status === 'positive') {
- return { background: theme.background.positive, color: theme.color.positiveText };
- }
- if (status === 'negative') {
- return { background: theme.background.negative, color: theme.color.negativeText };
- }
- return {
- background: theme.base === 'light' ? '#EAF3FC' : theme.color.darkest,
- color: theme.base === 'light' ? theme.color.darkest : theme.defaultText,
- };
+const Container = styled.div({
+ display: `flex`,
+ alignItems: 'center',
+ justifyContent: 'center',
+ height: 'calc(100% - 40px)',
+ flexDirection: 'column',
+});
+
+export const UpgradeBlock = styled.div(({ theme }) => {
+ return {
+ border: '1px solid',
+ borderRadius: 5,
+ padding: 20,
+ margin: 20,
+ maxWidth: 400,
+ borderColor: theme.color.border,
+ fontSize: theme.typography.size.s2,
+ };
+});
+
+const Code = styled.pre`
+ background: rgba(0, 0, 0, 0.05);
+ font-size: ${typography.size.s2 - 1}px;
+ margin: 4px 0 16px;
+`;
+
+const Footer = styled.div(({ theme }) => ({
+ marginBottom: 24,
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ color: theme.base === 'light' ? theme.color.dark : theme.color.lightest,
+ fontWeight: theme.typography.weight.regular,
+ fontSize: theme.typography.size.s2,
+}));
+
+export const Button = styled(BaseButton)(({ theme }) => ({
+ '&&': {
+ borderRadius: 4,
+ fontSize: '13px',
+ lineHeight: '14px',
+ color: theme.base === 'light' ? theme.color.darker : theme.color.lightest,
+ padding: '9px 12px',
+ svg: {
+ marginRight: 6,
+ },
+ },
+}));
+
+const Tab = styled(Button)<{ active: boolean }>(({ theme, active }) => ({
+ '&&': {
+ padding: 2,
+ paddingRight: 8,
+ margin: 0,
+ color: active
+ ? theme.color.secondary
+ : theme.base === 'light'
+ ? theme.color.dark
+ : theme.color.lightest,
},
+}));
- ({ theme }) => ({
- fontWeight: theme.typography.weight.bold,
+const BlueLinkButton = styled(Button)(({ theme }) => ({
+ '&&': {
+ padding: 0,
+ paddingRight: 8,
+ margin: 0,
+ color: theme.color.secondary,
fontSize: theme.typography.size.s2,
- padding: '10px 20px',
- marginBottom: 24,
- borderRadius: theme.appBorderRadius,
- border: `1px solid ${theme.appBorderColor}`,
- textAlign: 'center',
- })
-);
-
-const Upgrade = styled.div(({ theme }) => ({
- marginTop: 20,
- borderTop: `1px solid ${theme.appBorderColor}`,
+ fontWeight: theme.typography.weight.regular,
+ },
}));
-const Container = styled.div({
- padding: `3rem 20px`,
- maxWidth: 600,
- margin: '0 auto',
-});
+export const StyledA = styled.a(({ theme }) => ({
+ '&&': {
+ textDecoration: 'none',
+ fontWeight: theme.typography.weight.bold,
+ color: theme.base === 'light' ? theme.color.dark : theme.color.light,
+ },
+ '&:hover': {
+ color: theme.base === 'light' ? theme.color.darkest : theme.color.lightest,
+ },
+}));
const AboutScreen: FC<{
- latest: State['versions']['latest'];
current: State['versions']['current'];
-}> = ({ latest = null, current }) => {
- const canUpdate = latest && semver.gt(latest.version, current.version);
-
- let updateMessage;
- if (latest) {
- if (canUpdate) {
- updateMessage = (
-
- Storybook {latest.version} is available. Upgrade from {current.version} now.
-
- );
- } else {
- updateMessage = (
- Looking good! You're up to date.
- );
- }
- } else {
- updateMessage = (
-
- Oops! The latest version of Storybook couldn't be fetched.
-
- );
- }
-
+ onNavigateToWhatsNew?: () => void;
+}> = ({ current, onNavigateToWhatsNew }) => {
+ const [activeTab, setActiveTab] = useState<'npm' | 'pnpm'>('npm');
return (
+
-
- Storybook {current.version}
+ Storybook
+
+ You are on Storybook {current.version}
+ Run the following script to check for updates and upgrade to the latest version.
+
+ setActiveTab('npm')}>
+ npm
+
+ setActiveTab('pnpm')}>
+ pnpm
+
+
+
+
+ {activeTab === 'npm'
+ ? 'npx storybook@latest upgrade'
+ : 'pnpm dlx storybook@latest upgrade'}
+
+ {onNavigateToWhatsNew && (
+
+ See what's new in Storybook
+
+ )}
+
+
+
+
);
};
diff --git a/code/ui/manager/src/settings/about_page.tsx b/code/ui/manager/src/settings/about_page.tsx
index 34640957ad18..e20d89d5362a 100644
--- a/code/ui/manager/src/settings/about_page.tsx
+++ b/code/ui/manager/src/settings/about_page.tsx
@@ -1,7 +1,7 @@
import type { FC } from 'react';
-import React, { Component } from 'react';
+import React, { Component, useCallback } from 'react';
-import { type API, useStorybookApi } from '@storybook/manager-api';
+import { type API, useStorybookApi, useStorybookState } from '@storybook/manager-api';
import { AboutScreen } from './about';
@@ -20,10 +20,19 @@ class NotificationClearer extends Component<{ api: API; notificationId: string }
const AboutPage: FC = () => {
const api = useStorybookApi();
+ const state = useStorybookState();
+ const onNavigateToWhatsNew = useCallback(() => {
+ api.changeSettingsTab('whats-new');
+ }, [api]);
return (
-
+
);
};