| null;
+ /**
+ * @unstable
+ */
paramKey?: string;
+ /**
+ * @unstable
+ */
disabled?: boolean;
+ /**
+ * @unstable
+ */
hidden?: boolean;
}
+/**
+ * This is a copy of FC from react/index.d.ts, but has the PropsWithChildren type removed
+ * this is correct and more type strict, and future compatible with React.FC in React 18+
+ *
+ * @deprecated This type is deprecated and will be removed in 8.0. (assuming the manager uses React 18 is out by then)
+ */
+interface FCWithoutChildren {
+ (props: P, context?: any): ReactElement | null;
+ propTypes?: WeakValidationMap | undefined;
+ contextTypes?: ValidationMap | undefined;
+ defaultProps?: Partial | undefined;
+ displayName?: string | undefined;
+}
+
+export interface Addon_PageType {
+ type: Addon_TypesEnum.experimental_PAGE;
+ /**
+ * The unique id of the page.
+ */
+ id: string;
+ /**
+ * The URL to navigate to when Storybook needs to navigate to this page.
+ */
+ url: string;
+ /**
+ * The title is used in mobile mode to represent the page in the navigation.
+ */
+ title: FCWithoutChildren | string | ReactElement | ReactNode;
+ /**
+ * The main content of the addon, a function component without any props.
+ * Storybook will render your component always.
+ *
+ * If you want to render your component only when the URL matches, use the `Route` component.
+ * @example
+ * import { Route } from '@storybook/router';
+ *
+ * render: () => {
+ * return (
+ *
+ *
+ *
+ * );
+ * };
+ */
+ render: FCWithoutChildren;
+}
+
+export interface Addon_WrapperType {
+ type: Addon_TypesEnum.PREVIEW;
+ /**
+ * The unique id of the page.
+ */
+ id: string;
+ /**
+ * A React.FunctionComponent that wraps the story.
+ *
+ * This component must accept a children prop, and render it.
+ */
+ render: FC<
+ PropsWithChildren<{
+ index: number;
+ children: ReactNode;
+ id: string;
+ storyId: StoryId;
+ active: boolean;
+ }>
+ >;
+}
+
+type Addon_TypeBaseNames = Exclude<
+ Addon_TypesEnum,
+ Addon_TypesEnum.PREVIEW | Addon_TypesEnum.experimental_PAGE
+>;
+
+export interface Addon_TypesMapping extends Record {
+ [Addon_TypesEnum.PREVIEW]: Addon_WrapperType;
+ [Addon_TypesEnum.experimental_PAGE]: Addon_PageType;
+}
+
export type Addon_Loader = (api: API) => void;
export interface Addon_Loaders {
[key: string]: Addon_Loader;
}
-export interface Addon_Collection {
- [key: string]: Addon_Type;
+export interface Addon_Collection {
+ [key: string]: T;
}
export interface Addon_Elements {
[key: string]: Addon_Collection;
@@ -342,10 +477,36 @@ export interface Addon_Config {
}
export enum Addon_TypesEnum {
+ /**
+ * This API is used to create a tab the toolbar above the canvas, This API might be removed in the future.
+ * @unstable
+ */
TAB = 'tab',
+ /**
+ * This adds panels to the addons side panel.
+ */
PANEL = 'panel',
+ /**
+ * This adds items in the toolbar above the canvas - on the left side.
+ */
TOOL = 'tool',
+ /**
+ * This adds items in the toolbar above the canvas - on the right side.
+ */
TOOLEXTRA = 'toolextra',
+ /**
+ * This adds wrapper components around the canvas/iframe component storybook renders.
+ * @unstable this API is not stable yet, and is likely to change in 8.0.
+ */
PREVIEW = 'preview',
+ /**
+ * This adds pages that render instead of the canvas.
+ * @unstable
+ */
+ experimental_PAGE = 'page',
+
+ /**
+ * @deprecated This property does nothing, and will be removed in Storybook 8.0.
+ */
NOTES_ELEMENT = 'notes-element',
}
diff --git a/code/lib/types/src/modules/api-stories.ts b/code/lib/types/src/modules/api-stories.ts
index 7cfd32b22284..4853aa9a39cb 100644
--- a/code/lib/types/src/modules/api-stories.ts
+++ b/code/lib/types/src/modules/api-stories.ts
@@ -172,3 +172,15 @@ export interface API_Versions {
next?: API_Version;
current?: API_Version;
}
+
+export type API_StatusValue = 'pending' | 'success' | 'error' | 'warn' | 'unknown';
+
+export interface API_StatusObject {
+ status: API_StatusValue;
+ title: string;
+ description: string;
+ data?: any;
+}
+
+export type API_StatusState = Record>;
+export type API_StatusUpdate = Record;
diff --git a/code/lib/types/src/modules/api.ts b/code/lib/types/src/modules/api.ts
index 7a043dfc7382..6bf5777ae825 100644
--- a/code/lib/types/src/modules/api.ts
+++ b/code/lib/types/src/modules/api.ts
@@ -3,19 +3,17 @@
import type { RenderData } from '../../../router/src/types';
import type { Channel } from '../../../channels/src';
import type { ThemeVars } from '../../../theming/src/types';
-import type { ViewMode } from './csf';
import type { DocsOptions } from './core-common';
import type { API_HashEntry, API_IndexHash } from './api-stories';
import type { SetStoriesStory, SetStoriesStoryData } from './channelApi';
-import type { Addon_Type } from './addons';
+import type { Addon_BaseType, Addon_Collection, Addon_RenderOptions, Addon_Type } from './addons';
import type { StoryIndex } from './storyIndex';
-export type API_ViewMode = 'story' | 'info' | 'settings' | 'page' | undefined | string;
+type OrString = T | (string & {});
-export interface API_RenderOptions {
- active: boolean;
- key: string;
-}
+export type API_ViewMode = OrString<'story' | 'docs' | 'settings'> | undefined;
+
+export type API_RenderOptions = Addon_RenderOptions;
export interface API_RouteOptions {
storyId: string;
@@ -30,13 +28,20 @@ export interface API_MatchOptions {
path: string;
}
+/**
+ * @deprecated this is synonymous with `Addon_Type`. This interface will be removed in 8.0
+ */
export type API_Addon = Addon_Type;
-export interface API_Collection {
- [key: string]: T;
-}
+/**
+ * @deprecated this is synonymous with `Addon_Collection`. This interface will be removed in 8.0
+ */
+export type API_Collection = Addon_Collection;
-export type API_Panels = API_Collection;
+/**
+ * @deprecated This interface will be removed in 8.0
+ */
+export type API_Panels = Addon_Collection;
export type API_StateMerger = (input: S) => S;
@@ -64,12 +69,12 @@ export interface API_Provider {
export type API_IframeRenderer = (
storyId: string,
- viewMode: ViewMode,
+ viewMode: API_ViewMode,
id: string,
baseUrl: string,
scale: number,
queryParams: Record
-) => any;
+) => React.ReactElement | null;
export interface API_UIOptions {
name?: string;
@@ -91,7 +96,7 @@ export interface API_Layout {
showTabs: boolean;
showToolbar: boolean;
/**
- * @deprecated
+ * @deprecated, will be removed in 8.0 - this API no longer works
*/
isToolshown?: boolean;
}
@@ -111,6 +116,13 @@ export interface API_SidebarOptions {
renderLabel?: (item: API_HashEntry) => any;
}
+interface OnClearOptions {
+ /**
+ * True when the user dismissed the notification.
+ */
+ dismissed: boolean;
+}
+
export interface API_Notification {
id: string;
link: string;
@@ -118,14 +130,11 @@ export interface API_Notification {
headline: string;
subHeadline?: string | any;
};
-
icon?: {
name: string;
color?: string;
};
- onClear?: (options: {
- /** True when the user dismissed the notification. */ dismissed: boolean;
- }) => void;
+ onClear?: (options: OnClearOptions) => void;
}
type API_Versions = Record;
diff --git a/code/lib/types/src/modules/channelApi.ts b/code/lib/types/src/modules/channelApi.ts
index 0fe35baf824a..b58129999e21 100644
--- a/code/lib/types/src/modules/channelApi.ts
+++ b/code/lib/types/src/modules/channelApi.ts
@@ -1,3 +1,4 @@
+import type { API_ViewMode } from './api';
import type {
Args,
ArgTypes,
@@ -5,7 +6,6 @@ import type {
Parameters,
StoryId,
StoryKind,
- ViewMode,
Globals,
GlobalTypes,
} from './csf';
@@ -23,7 +23,7 @@ export interface SetStoriesStory {
[optionName: string]: any;
};
docsOnly?: boolean;
- viewMode?: ViewMode;
+ viewMode?: API_ViewMode;
[parameterName: string]: any;
};
argTypes?: ArgTypes;
diff --git a/code/lib/types/src/modules/csf.ts b/code/lib/types/src/modules/csf.ts
index ea5c13668bfd..b72b0a42af5a 100644
--- a/code/lib/types/src/modules/csf.ts
+++ b/code/lib/types/src/modules/csf.ts
@@ -60,13 +60,19 @@ export type {
Tag,
} from '@storybook/csf';
-export type ViewMode = ViewModeBase | 'story' | 'info' | 'settings' | string | undefined;
+type OrString = T | (string & {});
+
+export type ViewMode = OrString | undefined;
type Layout = 'centered' | 'fullscreen' | 'padded' | 'none';
export interface StorybookParameters {
options?: Addon_OptionsParameter;
- /** The layout property defines basic styles added to the preview body where the story is rendered. If you pass 'none', no styles are applied. */
+ /**
+ * The layout property defines basic styles added to the preview body where the story is rendered.
+ *
+ * If you pass `none`, no styles are applied.
+ */
layout?: Layout;
}
diff --git a/code/presets/create-react-app/package.json b/code/presets/create-react-app/package.json
index aae62c08aecb..c8b67e478017 100644
--- a/code/presets/create-react-app/package.json
+++ b/code/presets/create-react-app/package.json
@@ -44,7 +44,7 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
diff --git a/code/presets/create-react-app/src/helpers/mergePlugins.ts b/code/presets/create-react-app/src/helpers/mergePlugins.ts
index d315a29f24cb..4c3ff84ead18 100644
--- a/code/presets/create-react-app/src/helpers/mergePlugins.ts
+++ b/code/presets/create-react-app/src/helpers/mergePlugins.ts
@@ -1,7 +1,7 @@
-import type { WebpackPluginInstance } from 'webpack';
+import type { Configuration } from 'webpack';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
-export const mergePlugins = (...args: WebpackPluginInstance[]): WebpackPluginInstance[] =>
+export const mergePlugins = (...args: Configuration['plugins']): Configuration['plugins'] =>
args.reduce((plugins, plugin) => {
if (
plugins.some(
@@ -23,4 +23,4 @@ export const mergePlugins = (...args: WebpackPluginInstance[]): WebpackPluginIns
});
}
return [...plugins, updatedPlugin];
- }, [] as WebpackPluginInstance[]);
+ }, [] as Configuration['plugins']);
diff --git a/code/presets/html-webpack/package.json b/code/presets/html-webpack/package.json
index 14472b729a52..e1035a00238a 100644
--- a/code/presets/html-webpack/package.json
+++ b/code/presets/html-webpack/package.json
@@ -44,7 +44,7 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
diff --git a/code/presets/preact-webpack/package.json b/code/presets/preact-webpack/package.json
index 766a0e3e80b7..bb4df40c8b46 100644
--- a/code/presets/preact-webpack/package.json
+++ b/code/presets/preact-webpack/package.json
@@ -44,7 +44,7 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
diff --git a/code/presets/react-webpack/package.json b/code/presets/react-webpack/package.json
index 780476514d89..e5d9162d2d97 100644
--- a/code/presets/react-webpack/package.json
+++ b/code/presets/react-webpack/package.json
@@ -59,7 +59,7 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
diff --git a/code/presets/server-webpack/package.json b/code/presets/server-webpack/package.json
index 5bb0e268cb3b..5756f411daf2 100644
--- a/code/presets/server-webpack/package.json
+++ b/code/presets/server-webpack/package.json
@@ -49,7 +49,7 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
diff --git a/code/presets/svelte-webpack/package.json b/code/presets/svelte-webpack/package.json
index 47431d89989e..45efb2ee01a2 100644
--- a/code/presets/svelte-webpack/package.json
+++ b/code/presets/svelte-webpack/package.json
@@ -59,7 +59,7 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
diff --git a/code/presets/vue-webpack/package.json b/code/presets/vue-webpack/package.json
index ea15ea1f1a49..7820cc57fe1f 100644
--- a/code/presets/vue-webpack/package.json
+++ b/code/presets/vue-webpack/package.json
@@ -54,7 +54,7 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
diff --git a/code/presets/vue3-webpack/package.json b/code/presets/vue3-webpack/package.json
index 5fec90da4ed2..0186a5b0def7 100644
--- a/code/presets/vue3-webpack/package.json
+++ b/code/presets/vue3-webpack/package.json
@@ -54,7 +54,7 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
diff --git a/code/presets/web-components-webpack/package.json b/code/presets/web-components-webpack/package.json
index a14670849765..f9f7c9e3392d 100644
--- a/code/presets/web-components-webpack/package.json
+++ b/code/presets/web-components-webpack/package.json
@@ -47,7 +47,7 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
diff --git a/code/renderers/html/package.json b/code/renderers/html/package.json
index d90c69dcbca7..a036ef947ce4 100644
--- a/code/renderers/html/package.json
+++ b/code/renderers/html/package.json
@@ -44,7 +44,7 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
diff --git a/code/renderers/preact/package.json b/code/renderers/preact/package.json
index b41abe56f30b..3c050167faca 100644
--- a/code/renderers/preact/package.json
+++ b/code/renderers/preact/package.json
@@ -44,7 +44,7 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
diff --git a/code/renderers/react/package.json b/code/renderers/react/package.json
index 36655e6229f0..3397766ee669 100644
--- a/code/renderers/react/package.json
+++ b/code/renderers/react/package.json
@@ -48,7 +48,7 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
diff --git a/code/renderers/server/package.json b/code/renderers/server/package.json
index f4b0bd928143..6a4c231a3543 100644
--- a/code/renderers/server/package.json
+++ b/code/renderers/server/package.json
@@ -49,7 +49,7 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
diff --git a/code/renderers/web-components/package.json b/code/renderers/web-components/package.json
index 2f2521fdb468..1f6e5bf245fa 100644
--- a/code/renderers/web-components/package.json
+++ b/code/renderers/web-components/package.json
@@ -47,7 +47,7 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
diff --git a/code/tsconfig.json b/code/tsconfig.json
index e28f13442121..26de1b5ba684 100644
--- a/code/tsconfig.json
+++ b/code/tsconfig.json
@@ -10,7 +10,7 @@
"moduleResolution": "Node",
"target": "ES2020",
"module": "CommonJS",
- "skipLibCheck": false,
+ "skipLibCheck": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"isolatedModules": true,
diff --git a/code/ui/.storybook/main.ts b/code/ui/.storybook/main.ts
index 541b0f2c69e1..b21c8b0f9413 100644
--- a/code/ui/.storybook/main.ts
+++ b/code/ui/.storybook/main.ts
@@ -6,17 +6,21 @@ import type { StorybookConfig } from '../../frameworks/react-vite';
const isBlocksOnly = process.env.STORYBOOK_BLOCKS_ONLY === 'true';
const allStories = [
+ {
+ directory: '../components/src/new',
+ titlePrefix: '@core-ui',
+ },
{
directory: '../manager/src',
- titlePrefix: '@storybook-manager',
+ titlePrefix: '@manager',
},
{
- directory: '../components/src',
- titlePrefix: '@storybook-components',
+ directory: '../components/src/legacy',
+ titlePrefix: '@components',
},
{
directory: '../blocks/src',
- titlePrefix: '@storybook-blocks',
+ titlePrefix: '@blocks',
},
];
diff --git a/code/ui/.storybook/manager.ts b/code/ui/.storybook/manager.tsx
similarity index 52%
rename from code/ui/.storybook/manager.ts
rename to code/ui/.storybook/manager.tsx
index 1ac61cf4d375..775a1f63c8ed 100644
--- a/code/ui/.storybook/manager.ts
+++ b/code/ui/.storybook/manager.tsx
@@ -1,5 +1,7 @@
-import { addons } from '@storybook/manager-api';
+import { addons, types } from '@storybook/manager-api';
+import { IconButton, Icons } from '@storybook/components';
import startCase from 'lodash/startCase.js';
+import React, { Fragment } from 'react';
addons.setConfig({
sidebar: {
diff --git a/code/ui/blocks/package.json b/code/ui/blocks/package.json
index c2d7bd274ea5..5f1326b58e35 100644
--- a/code/ui/blocks/package.json
+++ b/code/ui/blocks/package.json
@@ -39,7 +39,7 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
diff --git a/code/ui/blocks/tsconfig.json b/code/ui/blocks/tsconfig.json
index af6084057dc0..fcad33be23fa 100644
--- a/code/ui/blocks/tsconfig.json
+++ b/code/ui/blocks/tsconfig.json
@@ -2,6 +2,7 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"module": "esnext",
+ "skipLibCheck": true,
"rootDir": "./src",
"types": ["jest"],
"strict": false
diff --git a/code/ui/components/package.json b/code/ui/components/package.json
index f2cccd7acf9b..f02bd1d1b5cb 100644
--- a/code/ui/components/package.json
+++ b/code/ui/components/package.json
@@ -27,6 +27,12 @@
"require": "./dist/index.js",
"import": "./dist/index.mjs"
},
+ "./experimental": {
+ "types": "./dist/experimantal.d.ts",
+ "node": "./dist/experimantal.js",
+ "require": "./dist/experimantal.js",
+ "import": "./dist/experimantal.mjs"
+ },
"./html": {
"types": "./dist/html.d.ts",
"require": "./dist/html.js",
@@ -37,6 +43,19 @@
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
+ "typesVersions": {
+ "*": {
+ "*": [
+ "dist/index.d.ts"
+ ],
+ "html": [
+ "dist/html.d.ts"
+ ],
+ "experimental": [
+ "dist/experimantal.d.ts"
+ ]
+ }
+ },
"files": [
"dist/**/*",
"README.md",
@@ -44,10 +63,11 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
+ "@radix-ui/react-select": "^1.2.2",
"@storybook/client-logger": "7.1.0",
"@storybook/csf": "^0.1.0",
"@storybook/global": "^5.0.0",
@@ -81,7 +101,8 @@
},
"bundler": {
"entries": [
- "./src/index.ts"
+ "./src/index.ts",
+ "./src/experimantal.ts"
],
"platform": "neutral"
},
diff --git a/code/ui/components/src/experimantal.ts b/code/ui/components/src/experimantal.ts
new file mode 100644
index 000000000000..6682e0ada3c5
--- /dev/null
+++ b/code/ui/components/src/experimantal.ts
@@ -0,0 +1,10 @@
+// eslint-disable-next-line @typescript-eslint/triple-slash-reference
+///
+
+/* ABSOLUTELY DO NOT:
+ * - EXPORT PROPS INTERFACES OF COMPONENTS
+ * - EXPORT * FROM ANY FILES, EVERY EXPORT SHOULD BE DELIBERATE
+ * - EXPORT IMPORT / EXPORT ANYTHING FROM LEGACY
+ */
+
+export { Button } from './new/Button/Button';
diff --git a/code/ui/components/src/index.ts b/code/ui/components/src/index.ts
index 292a9c7067dd..f0a15962f3f1 100644
--- a/code/ui/components/src/index.ts
+++ b/code/ui/components/src/index.ts
@@ -3,88 +3,91 @@
import type { ElementType } from 'react';
import { createElement, forwardRef } from 'react';
-import * as typography from './typography/components';
-
-export { A } from './typography/elements/A';
-export { Blockquote } from './typography/elements/Blockquote';
-export { Code } from './typography/elements/Code';
-export { Div } from './typography/elements/Div';
-export { DL } from './typography/elements/DL';
-export { H1 } from './typography/elements/H1';
-export { H2 } from './typography/elements/H2';
-export { H3 } from './typography/elements/H3';
-export { H4 } from './typography/elements/H4';
-export { H5 } from './typography/elements/H5';
-export { H6 } from './typography/elements/H6';
-export { HR } from './typography/elements/HR';
-export { Img } from './typography/elements/Img';
-export { LI } from './typography/elements/LI';
-export { OL } from './typography/elements/OL';
-export { P } from './typography/elements/P';
-export { Pre } from './typography/elements/Pre';
-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';
+import * as typography from './legacy/typography/components';
+
+export { A } from './legacy/typography/elements/A';
+export { Blockquote } from './legacy/typography/elements/Blockquote';
+export { Code } from './legacy/typography/elements/Code';
+export { Div } from './legacy/typography/elements/Div';
+export { DL } from './legacy/typography/elements/DL';
+export { H1 } from './legacy/typography/elements/H1';
+export { H2 } from './legacy/typography/elements/H2';
+export { H3 } from './legacy/typography/elements/H3';
+export { H4 } from './legacy/typography/elements/H4';
+export { H5 } from './legacy/typography/elements/H5';
+export { H6 } from './legacy/typography/elements/H6';
+export { HR } from './legacy/typography/elements/HR';
+export { Img } from './legacy/typography/elements/Img';
+export { LI } from './legacy/typography/elements/LI';
+export { OL } from './legacy/typography/elements/OL';
+export { P } from './legacy/typography/elements/P';
+export { Pre } from './legacy/typography/elements/Pre';
+export { Span } from './legacy/typography/elements/Span';
+export { Table } from './legacy/typography/elements/Table';
+export { TT } from './legacy/typography/elements/TT';
+export { UL } from './legacy/typography/elements/UL';
+export { Badge } from './legacy/Badge/Badge';
// Typography
-export { Link } from './typography/link/link';
-export { DocumentWrapper } from './typography/DocumentWrapper';
+export { Link } from './legacy/typography/link/link';
+export { DocumentWrapper } from './legacy/typography/DocumentWrapper';
export type {
SyntaxHighlighterFormatTypes,
SyntaxHighlighterProps,
SyntaxHighlighterRendererProps,
-} from './syntaxhighlighter/syntaxhighlighter-types';
-export { SyntaxHighlighter } from './syntaxhighlighter/lazy-syntaxhighlighter';
-export { createCopyToClipboardFunction } from './syntaxhighlighter/syntaxhighlighter';
+} from './legacy/syntaxhighlighter/syntaxhighlighter-types';
+export { SyntaxHighlighter } from './legacy/syntaxhighlighter/lazy-syntaxhighlighter';
+export { createCopyToClipboardFunction } from './legacy/syntaxhighlighter/syntaxhighlighter';
// UI
-export { ActionBar } from './ActionBar/ActionBar';
-export { Spaced } from './spaced/Spaced';
-export { Placeholder } from './placeholder/placeholder';
-export { ScrollArea } from './ScrollArea/ScrollArea';
-export { Zoom } from './Zoom/Zoom';
-export type { ActionItem } from './ActionBar/ActionBar';
-export { ErrorFormatter } from './ErrorFormatter/ErrorFormatter';
+export { ActionBar } from './legacy/ActionBar/ActionBar';
+export { Spaced } from './legacy/spaced/Spaced';
+export { Placeholder } from './legacy/placeholder/placeholder';
+export { ScrollArea } from './legacy/ScrollArea/ScrollArea';
+export { Zoom } from './legacy/Zoom/Zoom';
+export type { ActionItem } from './legacy/ActionBar/ActionBar';
+export { ErrorFormatter } from './legacy/ErrorFormatter/ErrorFormatter';
// Forms
-export { Button } from './Button/Button';
-export { Form } from './form/index';
+export { Button } from './legacy/Button/Button';
+export { Form } from './legacy/form/index';
// Tooltips
-export { WithTooltip, WithTooltipPure } from './tooltip/lazy-WithTooltip';
-export { TooltipMessage } from './tooltip/TooltipMessage';
-export { TooltipNote } from './tooltip/TooltipNote';
-export { TooltipLinkList, type Link as TooltipLinkListLink } from './tooltip/TooltipLinkList';
-export { default as ListItem } from './tooltip/ListItem';
+export { WithTooltip, WithTooltipPure } from './legacy/tooltip/lazy-WithTooltip';
+export { TooltipMessage } from './legacy/tooltip/TooltipMessage';
+export { TooltipNote } from './legacy/tooltip/TooltipNote';
+export {
+ TooltipLinkList,
+ type Link as TooltipLinkListLink,
+} from './legacy/tooltip/TooltipLinkList';
+export { default as ListItem } from './legacy/tooltip/ListItem';
// Toolbar and subcomponents
-export { Tabs, TabsState, TabBar, TabWrapper } from './tabs/tabs';
-export { IconButton, IconButtonSkeleton, TabButton } from './bar/button';
-export { Separator, interleaveSeparators } from './bar/separator';
-export { Bar, FlexBar } from './bar/bar';
-export { AddonPanel } from './addon-panel/addon-panel';
+export { Tabs, TabsState, TabBar, TabWrapper } from './legacy/tabs/tabs';
+export { IconButton, IconButtonSkeleton, TabButton } from './legacy/bar/button';
+export { Separator, interleaveSeparators } from './legacy/bar/separator';
+export { Bar, FlexBar } from './legacy/bar/bar';
+export { AddonPanel } from './legacy/addon-panel/addon-panel';
// Graphics
-export type { IconsProps } from './icon/icon';
-export { Icons, Symbols } from './icon/icon';
-export { icons } from './icon/icons';
-export { StorybookLogo } from './brand/StorybookLogo';
-export { StorybookIcon } from './brand/StorybookIcon';
+export type { IconsProps } from './legacy/icon/icon';
+export { Icons, Symbols } from './legacy/icon/icon';
+export { icons } from './legacy/icon/icons';
+export { StorybookLogo } from './legacy/brand/StorybookLogo';
+export { StorybookIcon } from './legacy/brand/StorybookIcon';
// Loader
-export { Loader } from './Loader/Loader';
+export { Loader } from './legacy/Loader/Loader';
// Utils
-export { getStoryHref } from './utils/getStoryHref';
+export { getStoryHref } from './legacy/utils/getStoryHref';
-export * from './typography/DocumentFormatting';
-export * from './typography/ResetWrapper';
+export * from './legacy/typography/DocumentFormatting';
+export * from './legacy/typography/ResetWrapper';
-export { withReset, codeCommon } from './typography/lib/common';
+export { withReset, codeCommon } from './legacy/typography/lib/common';
-export { ClipboardCode } from './clipboard/ClipboardCode';
+export { ClipboardCode } from './legacy/clipboard/ClipboardCode';
// eslint-disable-next-line prefer-destructuring
export const components = typography.components;
diff --git a/code/ui/components/src/ActionBar/ActionBar.stories.tsx b/code/ui/components/src/legacy/ActionBar/ActionBar.stories.tsx
similarity index 100%
rename from code/ui/components/src/ActionBar/ActionBar.stories.tsx
rename to code/ui/components/src/legacy/ActionBar/ActionBar.stories.tsx
diff --git a/code/ui/components/src/ActionBar/ActionBar.tsx b/code/ui/components/src/legacy/ActionBar/ActionBar.tsx
similarity index 100%
rename from code/ui/components/src/ActionBar/ActionBar.tsx
rename to code/ui/components/src/legacy/ActionBar/ActionBar.tsx
diff --git a/code/ui/components/src/Badge/Badge.stories.tsx b/code/ui/components/src/legacy/Badge/Badge.stories.tsx
similarity index 100%
rename from code/ui/components/src/Badge/Badge.stories.tsx
rename to code/ui/components/src/legacy/Badge/Badge.stories.tsx
diff --git a/code/ui/components/src/Badge/Badge.tsx b/code/ui/components/src/legacy/Badge/Badge.tsx
similarity index 100%
rename from code/ui/components/src/Badge/Badge.tsx
rename to code/ui/components/src/legacy/Badge/Badge.tsx
diff --git a/code/ui/components/src/Button/Button.stories.tsx b/code/ui/components/src/legacy/Button/Button.stories.tsx
similarity index 100%
rename from code/ui/components/src/Button/Button.stories.tsx
rename to code/ui/components/src/legacy/Button/Button.stories.tsx
diff --git a/code/ui/components/src/Button/Button.tsx b/code/ui/components/src/legacy/Button/Button.tsx
similarity index 100%
rename from code/ui/components/src/Button/Button.tsx
rename to code/ui/components/src/legacy/Button/Button.tsx
diff --git a/code/ui/components/src/Colors/SideBySide.tsx b/code/ui/components/src/legacy/Colors/SideBySide.tsx
similarity index 100%
rename from code/ui/components/src/Colors/SideBySide.tsx
rename to code/ui/components/src/legacy/Colors/SideBySide.tsx
diff --git a/code/ui/components/src/Colors/colorpalette.mdx b/code/ui/components/src/legacy/Colors/colorpalette.mdx
similarity index 100%
rename from code/ui/components/src/Colors/colorpalette.mdx
rename to code/ui/components/src/legacy/Colors/colorpalette.mdx
diff --git a/code/ui/components/src/ErrorFormatter/ErrorFormatter.stories.tsx b/code/ui/components/src/legacy/ErrorFormatter/ErrorFormatter.stories.tsx
similarity index 100%
rename from code/ui/components/src/ErrorFormatter/ErrorFormatter.stories.tsx
rename to code/ui/components/src/legacy/ErrorFormatter/ErrorFormatter.stories.tsx
diff --git a/code/ui/components/src/ErrorFormatter/ErrorFormatter.tsx b/code/ui/components/src/legacy/ErrorFormatter/ErrorFormatter.tsx
similarity index 100%
rename from code/ui/components/src/ErrorFormatter/ErrorFormatter.tsx
rename to code/ui/components/src/legacy/ErrorFormatter/ErrorFormatter.tsx
diff --git a/code/ui/components/src/Loader/Loader.stories.tsx b/code/ui/components/src/legacy/Loader/Loader.stories.tsx
similarity index 100%
rename from code/ui/components/src/Loader/Loader.stories.tsx
rename to code/ui/components/src/legacy/Loader/Loader.stories.tsx
diff --git a/code/ui/components/src/Loader/Loader.tsx b/code/ui/components/src/legacy/Loader/Loader.tsx
similarity index 100%
rename from code/ui/components/src/Loader/Loader.tsx
rename to code/ui/components/src/legacy/Loader/Loader.tsx
diff --git a/code/ui/components/src/ScrollArea/GlobalScrollAreaStyles.tsx b/code/ui/components/src/legacy/ScrollArea/GlobalScrollAreaStyles.tsx
similarity index 100%
rename from code/ui/components/src/ScrollArea/GlobalScrollAreaStyles.tsx
rename to code/ui/components/src/legacy/ScrollArea/GlobalScrollAreaStyles.tsx
diff --git a/code/ui/components/src/ScrollArea/OverlayScrollbars.tsx b/code/ui/components/src/legacy/ScrollArea/OverlayScrollbars.tsx
similarity index 100%
rename from code/ui/components/src/ScrollArea/OverlayScrollbars.tsx
rename to code/ui/components/src/legacy/ScrollArea/OverlayScrollbars.tsx
diff --git a/code/ui/components/src/ScrollArea/ScrollArea.stories.tsx b/code/ui/components/src/legacy/ScrollArea/ScrollArea.stories.tsx
similarity index 100%
rename from code/ui/components/src/ScrollArea/ScrollArea.stories.tsx
rename to code/ui/components/src/legacy/ScrollArea/ScrollArea.stories.tsx
diff --git a/code/ui/components/src/ScrollArea/ScrollArea.tsx b/code/ui/components/src/legacy/ScrollArea/ScrollArea.tsx
similarity index 100%
rename from code/ui/components/src/ScrollArea/ScrollArea.tsx
rename to code/ui/components/src/legacy/ScrollArea/ScrollArea.tsx
diff --git a/code/ui/components/src/Zoom/Zoom.stories.tsx b/code/ui/components/src/legacy/Zoom/Zoom.stories.tsx
similarity index 100%
rename from code/ui/components/src/Zoom/Zoom.stories.tsx
rename to code/ui/components/src/legacy/Zoom/Zoom.stories.tsx
diff --git a/code/ui/components/src/Zoom/Zoom.tsx b/code/ui/components/src/legacy/Zoom/Zoom.tsx
similarity index 100%
rename from code/ui/components/src/Zoom/Zoom.tsx
rename to code/ui/components/src/legacy/Zoom/Zoom.tsx
diff --git a/code/ui/components/src/Zoom/ZoomElement.tsx b/code/ui/components/src/legacy/Zoom/ZoomElement.tsx
similarity index 100%
rename from code/ui/components/src/Zoom/ZoomElement.tsx
rename to code/ui/components/src/legacy/Zoom/ZoomElement.tsx
diff --git a/code/ui/components/src/Zoom/ZoomIFrame.tsx b/code/ui/components/src/legacy/Zoom/ZoomIFrame.tsx
similarity index 100%
rename from code/ui/components/src/Zoom/ZoomIFrame.tsx
rename to code/ui/components/src/legacy/Zoom/ZoomIFrame.tsx
diff --git a/code/ui/components/src/addon-panel/addon-panel.tsx b/code/ui/components/src/legacy/addon-panel/addon-panel.tsx
similarity index 100%
rename from code/ui/components/src/addon-panel/addon-panel.tsx
rename to code/ui/components/src/legacy/addon-panel/addon-panel.tsx
diff --git a/code/ui/components/src/bar/bar.tsx b/code/ui/components/src/legacy/bar/bar.tsx
similarity index 100%
rename from code/ui/components/src/bar/bar.tsx
rename to code/ui/components/src/legacy/bar/bar.tsx
diff --git a/code/ui/components/src/bar/button.stories.tsx b/code/ui/components/src/legacy/bar/button.stories.tsx
similarity index 100%
rename from code/ui/components/src/bar/button.stories.tsx
rename to code/ui/components/src/legacy/bar/button.stories.tsx
diff --git a/code/ui/components/src/bar/button.tsx b/code/ui/components/src/legacy/bar/button.tsx
similarity index 100%
rename from code/ui/components/src/bar/button.tsx
rename to code/ui/components/src/legacy/bar/button.tsx
diff --git a/code/ui/components/src/bar/separator.tsx b/code/ui/components/src/legacy/bar/separator.tsx
similarity index 100%
rename from code/ui/components/src/bar/separator.tsx
rename to code/ui/components/src/legacy/bar/separator.tsx
diff --git a/code/ui/components/src/brand/StorybookIcon.stories.tsx b/code/ui/components/src/legacy/brand/StorybookIcon.stories.tsx
similarity index 100%
rename from code/ui/components/src/brand/StorybookIcon.stories.tsx
rename to code/ui/components/src/legacy/brand/StorybookIcon.stories.tsx
diff --git a/code/ui/components/src/brand/StorybookIcon.tsx b/code/ui/components/src/legacy/brand/StorybookIcon.tsx
similarity index 100%
rename from code/ui/components/src/brand/StorybookIcon.tsx
rename to code/ui/components/src/legacy/brand/StorybookIcon.tsx
diff --git a/code/ui/components/src/brand/StorybookLogo.stories.tsx b/code/ui/components/src/legacy/brand/StorybookLogo.stories.tsx
similarity index 100%
rename from code/ui/components/src/brand/StorybookLogo.stories.tsx
rename to code/ui/components/src/legacy/brand/StorybookLogo.stories.tsx
diff --git a/code/ui/components/src/brand/StorybookLogo.tsx b/code/ui/components/src/legacy/brand/StorybookLogo.tsx
similarity index 100%
rename from code/ui/components/src/brand/StorybookLogo.tsx
rename to code/ui/components/src/legacy/brand/StorybookLogo.tsx
diff --git a/code/ui/components/src/clipboard/ClipboardCode.tsx b/code/ui/components/src/legacy/clipboard/ClipboardCode.tsx
similarity index 100%
rename from code/ui/components/src/clipboard/ClipboardCode.tsx
rename to code/ui/components/src/legacy/clipboard/ClipboardCode.tsx
diff --git a/code/ui/components/src/form/field/field.tsx b/code/ui/components/src/legacy/form/field/field.tsx
similarity index 100%
rename from code/ui/components/src/form/field/field.tsx
rename to code/ui/components/src/legacy/form/field/field.tsx
diff --git a/code/ui/components/src/form/form.stories.tsx b/code/ui/components/src/legacy/form/form.stories.tsx
similarity index 100%
rename from code/ui/components/src/form/form.stories.tsx
rename to code/ui/components/src/legacy/form/form.stories.tsx
diff --git a/code/ui/components/src/form/index.tsx b/code/ui/components/src/legacy/form/index.tsx
similarity index 100%
rename from code/ui/components/src/form/index.tsx
rename to code/ui/components/src/legacy/form/index.tsx
diff --git a/code/ui/components/src/form/input/input.tsx b/code/ui/components/src/legacy/form/input/input.tsx
similarity index 98%
rename from code/ui/components/src/form/input/input.tsx
rename to code/ui/components/src/legacy/form/input/input.tsx
index cc21c5447ad3..83d99e4d8a8d 100644
--- a/code/ui/components/src/form/input/input.tsx
+++ b/code/ui/components/src/legacy/form/input/input.tsx
@@ -32,6 +32,12 @@ const styles = ({ theme }: { theme: Theme }): CSSObject => ({
fontSize: theme.typography.size.s2 - 1,
lineHeight: '20px',
padding: '6px 10px', // 32
+ boxSizing: 'border-box',
+ height: 32,
+
+ '&[type="file"]': {
+ height: 'auto',
+ },
'&:focus': {
boxShadow: `${theme.color.secondary} 0 0 0 1px inset`,
diff --git a/code/ui/components/src/icon/icon.stories.tsx b/code/ui/components/src/legacy/icon/icon.stories.tsx
similarity index 100%
rename from code/ui/components/src/icon/icon.stories.tsx
rename to code/ui/components/src/legacy/icon/icon.stories.tsx
diff --git a/code/ui/components/src/icon/icon.tsx b/code/ui/components/src/legacy/icon/icon.tsx
similarity index 100%
rename from code/ui/components/src/icon/icon.tsx
rename to code/ui/components/src/legacy/icon/icon.tsx
diff --git a/code/ui/components/src/icon/icons.tsx b/code/ui/components/src/legacy/icon/icons.tsx
similarity index 100%
rename from code/ui/components/src/icon/icons.tsx
rename to code/ui/components/src/legacy/icon/icons.tsx
diff --git a/code/ui/components/src/icon/svg.tsx b/code/ui/components/src/legacy/icon/svg.tsx
similarity index 100%
rename from code/ui/components/src/icon/svg.tsx
rename to code/ui/components/src/legacy/icon/svg.tsx
diff --git a/code/ui/components/src/placeholder/placeholder.stories.tsx b/code/ui/components/src/legacy/placeholder/placeholder.stories.tsx
similarity index 100%
rename from code/ui/components/src/placeholder/placeholder.stories.tsx
rename to code/ui/components/src/legacy/placeholder/placeholder.stories.tsx
diff --git a/code/ui/components/src/placeholder/placeholder.tsx b/code/ui/components/src/legacy/placeholder/placeholder.tsx
similarity index 100%
rename from code/ui/components/src/placeholder/placeholder.tsx
rename to code/ui/components/src/legacy/placeholder/placeholder.tsx
diff --git a/code/ui/components/src/shared/animation.ts b/code/ui/components/src/legacy/shared/animation.ts
similarity index 100%
rename from code/ui/components/src/shared/animation.ts
rename to code/ui/components/src/legacy/shared/animation.ts
diff --git a/code/ui/components/src/spaced/Spaced.stories.tsx b/code/ui/components/src/legacy/spaced/Spaced.stories.tsx
similarity index 100%
rename from code/ui/components/src/spaced/Spaced.stories.tsx
rename to code/ui/components/src/legacy/spaced/Spaced.stories.tsx
diff --git a/code/ui/components/src/spaced/Spaced.tsx b/code/ui/components/src/legacy/spaced/Spaced.tsx
similarity index 100%
rename from code/ui/components/src/spaced/Spaced.tsx
rename to code/ui/components/src/legacy/spaced/Spaced.tsx
diff --git a/code/ui/components/src/syntaxhighlighter/formatter.test.ts b/code/ui/components/src/legacy/syntaxhighlighter/formatter.test.ts
similarity index 100%
rename from code/ui/components/src/syntaxhighlighter/formatter.test.ts
rename to code/ui/components/src/legacy/syntaxhighlighter/formatter.test.ts
diff --git a/code/ui/components/src/syntaxhighlighter/formatter.ts b/code/ui/components/src/legacy/syntaxhighlighter/formatter.ts
similarity index 100%
rename from code/ui/components/src/syntaxhighlighter/formatter.ts
rename to code/ui/components/src/legacy/syntaxhighlighter/formatter.ts
diff --git a/code/ui/components/src/syntaxhighlighter/lazy-syntaxhighlighter.tsx b/code/ui/components/src/legacy/syntaxhighlighter/lazy-syntaxhighlighter.tsx
similarity index 100%
rename from code/ui/components/src/syntaxhighlighter/lazy-syntaxhighlighter.tsx
rename to code/ui/components/src/legacy/syntaxhighlighter/lazy-syntaxhighlighter.tsx
diff --git a/code/ui/components/src/syntaxhighlighter/syntaxhighlighter-types.ts b/code/ui/components/src/legacy/syntaxhighlighter/syntaxhighlighter-types.ts
similarity index 100%
rename from code/ui/components/src/syntaxhighlighter/syntaxhighlighter-types.ts
rename to code/ui/components/src/legacy/syntaxhighlighter/syntaxhighlighter-types.ts
diff --git a/code/ui/components/src/syntaxhighlighter/syntaxhighlighter.stories.tsx b/code/ui/components/src/legacy/syntaxhighlighter/syntaxhighlighter.stories.tsx
similarity index 100%
rename from code/ui/components/src/syntaxhighlighter/syntaxhighlighter.stories.tsx
rename to code/ui/components/src/legacy/syntaxhighlighter/syntaxhighlighter.stories.tsx
diff --git a/code/ui/components/src/syntaxhighlighter/syntaxhighlighter.tsx b/code/ui/components/src/legacy/syntaxhighlighter/syntaxhighlighter.tsx
similarity index 100%
rename from code/ui/components/src/syntaxhighlighter/syntaxhighlighter.tsx
rename to code/ui/components/src/legacy/syntaxhighlighter/syntaxhighlighter.tsx
diff --git a/code/ui/components/src/legacy/tabs/tabs.helpers.tsx b/code/ui/components/src/legacy/tabs/tabs.helpers.tsx
new file mode 100644
index 000000000000..024765fd5cf3
--- /dev/null
+++ b/code/ui/components/src/legacy/tabs/tabs.helpers.tsx
@@ -0,0 +1,52 @@
+import { styled } from '@storybook/theming';
+import type { FC, ReactChild, ReactElement, ReactNode } from 'react';
+import React, { Children } from 'react';
+import type { Addon_RenderOptions } from '@storybook/types';
+import type { TabsProps } from './tabs';
+
+export interface VisuallyHiddenProps {
+ active?: boolean;
+}
+
+export const VisuallyHidden = styled.div(({ active }) =>
+ active ? { display: 'block' } : { display: 'none' }
+);
+
+export const childrenToList = (children: TabsProps['children']) =>
+ Children.toArray(children).map(
+ ({
+ props: { title, id, color, children: childrenOfChild },
+ }: ReactElement<{
+ children: FC | ReactChild | null;
+ title: ReactChild | null | FC;
+ id: string;
+ color?: string;
+ }>) => {
+ const content: FC | ReactNode = Array.isArray(childrenOfChild)
+ ? childrenOfChild[0]
+ : childrenOfChild;
+
+ const render: FC = (
+ typeof content === 'function'
+ ? content
+ : ({ active, key }: any) => (
+
+ {content}
+
+ )
+ ) as FC;
+ return {
+ title,
+ id,
+ ...(color ? { color } : {}),
+ render,
+ };
+ }
+ );
+
+export type ChildrenList = ReturnType;
+export type ChildrenListComplete = Array<
+ ReturnType[0] & {
+ active: boolean;
+ }
+>;
diff --git a/code/ui/components/src/tabs/tabs.hooks.tsx b/code/ui/components/src/legacy/tabs/tabs.hooks.tsx
similarity index 95%
rename from code/ui/components/src/tabs/tabs.hooks.tsx
rename to code/ui/components/src/legacy/tabs/tabs.hooks.tsx
index 41df6e93f44f..0afb81ea9186 100644
--- a/code/ui/components/src/tabs/tabs.hooks.tsx
+++ b/code/ui/components/src/legacy/tabs/tabs.hooks.tsx
@@ -5,7 +5,7 @@ import useResizeObserver from 'use-resize-observer';
import { TabButton } from '../bar/button';
import { TooltipLinkList } from '../tooltip/TooltipLinkList';
import { WithTooltip } from '../tooltip/WithTooltip';
-import type { ChildrenList } from './tabs.helpers';
+import type { ChildrenListComplete } from './tabs.helpers';
import type { Link } from '../tooltip/TooltipLinkList';
const CollapseIcon = styled.span<{ isActive: boolean }>(({ theme, isActive }) => ({
@@ -32,7 +32,7 @@ const AddonButton = styled(TabButton)<{ preActive: boolean }>(({ active, theme,
`;
});
-export function useList(list: ChildrenList) {
+export function useList(list: ChildrenListComplete) {
const tabBarRef = useRef();
const addonsRef = useRef();
const tabRefs = useRef(new Map());
@@ -41,8 +41,8 @@ export function useList(list: ChildrenList) {
});
const [visibleList, setVisibleList] = useState(list);
- const [invisibleList, setInvisibleList] = useState([]);
- const previousList = useRef(list);
+ const [invisibleList, setInvisibleList] = useState([]);
+ const previousList = useRef(list);
const AddonTab = useCallback(
({
@@ -134,7 +134,7 @@ export function useList(list: ChildrenList) {
const { width: widthAddonsTab } = addonsRef.current.getBoundingClientRect();
const rightBorder = invisibleList.length ? x + width - widthAddonsTab : x + width;
- const newVisibleList: ChildrenList = [];
+ const newVisibleList: ChildrenListComplete = [];
let widthSum = 0;
diff --git a/code/ui/components/src/tabs/tabs.stories.tsx b/code/ui/components/src/legacy/tabs/tabs.stories.tsx
similarity index 82%
rename from code/ui/components/src/tabs/tabs.stories.tsx
rename to code/ui/components/src/legacy/tabs/tabs.stories.tsx
index 7a9abdb24130..77d357acf0b9 100644
--- a/code/ui/components/src/tabs/tabs.stories.tsx
+++ b/code/ui/components/src/legacy/tabs/tabs.stories.tsx
@@ -1,18 +1,19 @@
import { expect } from '@storybook/jest';
-import type { Key } from 'react';
import React, { Fragment } from 'react';
import { action } from '@storybook/addon-actions';
-import { logger } from '@storybook/client-logger';
import type { Meta, StoryObj } from '@storybook/react';
import {
within,
fireEvent,
waitFor,
screen,
- getByText,
userEvent,
+ findByText,
} from '@storybook/testing-library';
import { Tabs, TabsState, TabWrapper } from './tabs';
+import type { ChildrenList } from './tabs.helpers';
+import { IconButton } from '../bar/button';
+import { Icons } from '../icon/icon';
const colours = Array.from(new Array(15), (val, index) => index).map((i) =>
Math.floor((1 / 15) * i * 16777215)
@@ -41,13 +42,7 @@ function fibonacci(num: number, memo?: FibonacciMap): number {
/* eslint-enable no-param-reassign */
}
-interface Panels {
- [key: string]: {
- title: string;
- color?: string;
- render: ({ active, key }: { active: boolean; key: Key }) => JSX.Element;
- };
-}
+type Panels = Record>;
const panels: Panels = {
test1: {
@@ -121,27 +116,13 @@ const panels: Panels = {
const onSelect = action('onSelect');
const content = Object.entries(panels).map(([k, v]) => (
-
+
{v.render}
));
export default {
title: 'Tabs',
- decorators: [
- (story) => (
-
- {story()}
-
- ),
- ],
args: {
menuName: 'Addons',
},
@@ -199,11 +180,11 @@ export const StatefulStaticWithSetBackgroundColor = {
} satisfies Story;
const customViewports = {
- chromatic: {
- name: 'Chromatic',
+ sized: {
+ name: 'Sized',
styles: {
width: '380px',
- height: '963px',
+ height: '500px',
},
},
};
@@ -211,9 +192,10 @@ const customViewports = {
export const StatefulDynamicWithOpenTooltip = {
parameters: {
viewport: {
- defaultViewport: 'chromatic',
+ defaultViewport: 'sized',
viewports: customViewports,
},
+ theme: 'light',
chromatic: { viewports: [380] },
},
play: async ({ canvasElement }) => {
@@ -224,9 +206,8 @@ export const StatefulDynamicWithOpenTooltip = {
await expect(canvas.getByRole('tab', { name: /Addons/ })).toBeInTheDocument();
});
- const addonsTab = await canvas.findByRole('tab', { name: /Addons/ });
-
await waitFor(async () => {
+ const addonsTab = await canvas.findByRole('tab', { name: /Addons/ });
const tooltip = await screen.queryByTestId('tooltip');
if (!tooltip) {
@@ -236,14 +217,14 @@ export const StatefulDynamicWithOpenTooltip = {
if (!tooltip) {
throw new Error('Tooltip not found');
}
- });
- expect(screen.queryByTestId('tooltip')).toBeInTheDocument();
+ await expect(screen.queryByTestId('tooltip')).toBeInTheDocument();
+ });
},
render: (args) => (
{Object.entries(panels).map(([k, v]) => (
-
+
{v.render}
))}
@@ -252,20 +233,18 @@ export const StatefulDynamicWithOpenTooltip = {
} satisfies Story;
export const StatefulDynamicWithSelectedAddon = {
- parameters: {
- viewport: {
- defaultViewport: 'chromatic',
- viewports: customViewports,
- },
- chromatic: { viewports: [380] },
- },
+ ...StatefulDynamicWithOpenTooltip,
play: async (context) => {
await StatefulDynamicWithOpenTooltip.play(context);
+ const canvas = within(context.canvasElement);
- const popperContainer = screen.getByTestId('tooltip');
- const tab4 = getByText(popperContainer, 'Tab title #4', {});
- fireEvent(tab4, new MouseEvent('click', { bubbles: true }));
- await waitFor(() => screen.getByText('CONTENT 4'));
+ await waitFor(async () => {
+ const popperContainer = await screen.findByTestId('tooltip');
+ const tab4 = await findByText(popperContainer, 'Tab title #4', {});
+ fireEvent(tab4, new MouseEvent('click', { bubbles: true }));
+ const content4 = await canvas.findByText('CONTENT 4');
+ await expect(content4).toBeVisible();
+ });
// reopen the tooltip
await StatefulDynamicWithOpenTooltip.play(context);
@@ -273,7 +252,7 @@ export const StatefulDynamicWithSelectedAddon = {
render: (args) => (
{Object.entries(panels).map(([k, v]) => (
-
+
{v.render}
))}
@@ -305,6 +284,7 @@ export const StatelessBordered = {
export const StatelessWithTools = {
render: (args) => (
- logger.log('1')}>
- 1
-
- logger.log('2')}>
- 2
-
+
+
+
+
+
+
}
{...args}
@@ -328,6 +308,9 @@ export const StatelessWithTools = {
} satisfies Story;
export const StatelessAbsolute = {
+ parameters: {
+ layout: 'fullscreen',
+ },
render: (args) => (
(
(
= ({ active, render, children }) =>
export const panelProps = {};
export interface TabsProps {
- children?: FuncChildren[] | ReactNode;
+ children?: ReactElement<{
+ children: FC;
+ title: ReactNode | FC;
+ }>[];
id?: string;
tools?: ReactNode;
selected?: string;
@@ -138,9 +141,14 @@ export const Tabs: FC = memo(
id: htmlId,
menuName,
}) => {
- const list = useMemo(
- () => childrenToList(children, selected),
- [children, selected]
+ const idList = childrenToList(children).map((i) => i.id);
+ const list = useMemo(
+ () =>
+ childrenToList(children).map((i, index) => ({
+ ...i,
+ active: selected ? i.id === selected : index === 0,
+ })),
+ [selected, ...idList]
);
const { visibleList, tabBarRef, tabRefs, AddonTab } = useList(list);
@@ -169,7 +177,7 @@ export const Tabs: FC = memo(
}}
role="tab"
>
- {title}
+ {typeof title === 'function' ? : title}
);
})}
@@ -178,7 +186,9 @@ export const Tabs: FC = memo(
{tools}
- {list.map(({ id, active, render }) => render({ key: id, active }))}
+ {list.map(({ id, active, render }) => {
+ return React.createElement(render, { key: id, active }, null);
+ })}
) : (
@@ -199,10 +209,8 @@ Tabs.defaultProps = {
menuName: 'Tabs',
};
-type FuncChildren = ({ active }: { active: boolean }) => JSX.Element;
-
export interface TabsStateProps {
- children: FuncChildren[] | ReactNode;
+ children: TabsProps['children'];
initial: string;
absolute: boolean;
bordered: boolean;
diff --git a/code/ui/components/src/tooltip/ListItem.stories.tsx b/code/ui/components/src/legacy/tooltip/ListItem.stories.tsx
similarity index 100%
rename from code/ui/components/src/tooltip/ListItem.stories.tsx
rename to code/ui/components/src/legacy/tooltip/ListItem.stories.tsx
diff --git a/code/ui/components/src/tooltip/ListItem.tsx b/code/ui/components/src/legacy/tooltip/ListItem.tsx
similarity index 100%
rename from code/ui/components/src/tooltip/ListItem.tsx
rename to code/ui/components/src/legacy/tooltip/ListItem.tsx
diff --git a/code/ui/components/src/tooltip/Tooltip.stories.tsx b/code/ui/components/src/legacy/tooltip/Tooltip.stories.tsx
similarity index 100%
rename from code/ui/components/src/tooltip/Tooltip.stories.tsx
rename to code/ui/components/src/legacy/tooltip/Tooltip.stories.tsx
diff --git a/code/ui/components/src/tooltip/Tooltip.tsx b/code/ui/components/src/legacy/tooltip/Tooltip.tsx
similarity index 100%
rename from code/ui/components/src/tooltip/Tooltip.tsx
rename to code/ui/components/src/legacy/tooltip/Tooltip.tsx
diff --git a/code/ui/components/src/tooltip/TooltipLinkList.stories.tsx b/code/ui/components/src/legacy/tooltip/TooltipLinkList.stories.tsx
similarity index 100%
rename from code/ui/components/src/tooltip/TooltipLinkList.stories.tsx
rename to code/ui/components/src/legacy/tooltip/TooltipLinkList.stories.tsx
diff --git a/code/ui/components/src/tooltip/TooltipLinkList.tsx b/code/ui/components/src/legacy/tooltip/TooltipLinkList.tsx
similarity index 100%
rename from code/ui/components/src/tooltip/TooltipLinkList.tsx
rename to code/ui/components/src/legacy/tooltip/TooltipLinkList.tsx
diff --git a/code/ui/components/src/tooltip/TooltipMessage.stories.tsx b/code/ui/components/src/legacy/tooltip/TooltipMessage.stories.tsx
similarity index 100%
rename from code/ui/components/src/tooltip/TooltipMessage.stories.tsx
rename to code/ui/components/src/legacy/tooltip/TooltipMessage.stories.tsx
diff --git a/code/ui/components/src/tooltip/TooltipMessage.tsx b/code/ui/components/src/legacy/tooltip/TooltipMessage.tsx
similarity index 100%
rename from code/ui/components/src/tooltip/TooltipMessage.tsx
rename to code/ui/components/src/legacy/tooltip/TooltipMessage.tsx
diff --git a/code/ui/components/src/tooltip/TooltipNote.stories.tsx b/code/ui/components/src/legacy/tooltip/TooltipNote.stories.tsx
similarity index 100%
rename from code/ui/components/src/tooltip/TooltipNote.stories.tsx
rename to code/ui/components/src/legacy/tooltip/TooltipNote.stories.tsx
diff --git a/code/ui/components/src/tooltip/TooltipNote.tsx b/code/ui/components/src/legacy/tooltip/TooltipNote.tsx
similarity index 100%
rename from code/ui/components/src/tooltip/TooltipNote.tsx
rename to code/ui/components/src/legacy/tooltip/TooltipNote.tsx
diff --git a/code/ui/components/src/tooltip/WithTooltip.stories.tsx b/code/ui/components/src/legacy/tooltip/WithTooltip.stories.tsx
similarity index 100%
rename from code/ui/components/src/tooltip/WithTooltip.stories.tsx
rename to code/ui/components/src/legacy/tooltip/WithTooltip.stories.tsx
diff --git a/code/ui/components/src/tooltip/WithTooltip.tsx b/code/ui/components/src/legacy/tooltip/WithTooltip.tsx
similarity index 100%
rename from code/ui/components/src/tooltip/WithTooltip.tsx
rename to code/ui/components/src/legacy/tooltip/WithTooltip.tsx
diff --git a/code/ui/components/src/tooltip/assets/ellipse.png b/code/ui/components/src/legacy/tooltip/assets/ellipse.png
similarity index 100%
rename from code/ui/components/src/tooltip/assets/ellipse.png
rename to code/ui/components/src/legacy/tooltip/assets/ellipse.png
diff --git a/code/ui/components/src/tooltip/lazy-WithTooltip.tsx b/code/ui/components/src/legacy/tooltip/lazy-WithTooltip.tsx
similarity index 100%
rename from code/ui/components/src/tooltip/lazy-WithTooltip.tsx
rename to code/ui/components/src/legacy/tooltip/lazy-WithTooltip.tsx
diff --git a/code/ui/components/src/typography/DocumentFormatting.tsx b/code/ui/components/src/legacy/typography/DocumentFormatting.tsx
similarity index 100%
rename from code/ui/components/src/typography/DocumentFormatting.tsx
rename to code/ui/components/src/legacy/typography/DocumentFormatting.tsx
diff --git a/code/ui/components/src/typography/DocumentFormattingSample.mdx b/code/ui/components/src/legacy/typography/DocumentFormattingSample.mdx
similarity index 100%
rename from code/ui/components/src/typography/DocumentFormattingSample.mdx
rename to code/ui/components/src/legacy/typography/DocumentFormattingSample.mdx
diff --git a/code/ui/components/src/typography/DocumentWrapper.stories.tsx b/code/ui/components/src/legacy/typography/DocumentWrapper.stories.tsx
similarity index 100%
rename from code/ui/components/src/typography/DocumentWrapper.stories.tsx
rename to code/ui/components/src/legacy/typography/DocumentWrapper.stories.tsx
diff --git a/code/ui/components/src/typography/DocumentWrapper.tsx b/code/ui/components/src/legacy/typography/DocumentWrapper.tsx
similarity index 100%
rename from code/ui/components/src/typography/DocumentWrapper.tsx
rename to code/ui/components/src/legacy/typography/DocumentWrapper.tsx
diff --git a/code/ui/components/src/typography/ResetWrapper.tsx b/code/ui/components/src/legacy/typography/ResetWrapper.tsx
similarity index 100%
rename from code/ui/components/src/typography/ResetWrapper.tsx
rename to code/ui/components/src/legacy/typography/ResetWrapper.tsx
diff --git a/code/ui/components/src/typography/components.tsx b/code/ui/components/src/legacy/typography/components.tsx
similarity index 100%
rename from code/ui/components/src/typography/components.tsx
rename to code/ui/components/src/legacy/typography/components.tsx
diff --git a/code/ui/components/src/typography/elements/A.tsx b/code/ui/components/src/legacy/typography/elements/A.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/A.tsx
rename to code/ui/components/src/legacy/typography/elements/A.tsx
diff --git a/code/ui/components/src/typography/elements/Blockquote.tsx b/code/ui/components/src/legacy/typography/elements/Blockquote.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/Blockquote.tsx
rename to code/ui/components/src/legacy/typography/elements/Blockquote.tsx
diff --git a/code/ui/components/src/typography/elements/Code.tsx b/code/ui/components/src/legacy/typography/elements/Code.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/Code.tsx
rename to code/ui/components/src/legacy/typography/elements/Code.tsx
diff --git a/code/ui/components/src/typography/elements/DL.tsx b/code/ui/components/src/legacy/typography/elements/DL.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/DL.tsx
rename to code/ui/components/src/legacy/typography/elements/DL.tsx
diff --git a/code/ui/components/src/typography/elements/Div.tsx b/code/ui/components/src/legacy/typography/elements/Div.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/Div.tsx
rename to code/ui/components/src/legacy/typography/elements/Div.tsx
diff --git a/code/ui/components/src/typography/elements/H1.tsx b/code/ui/components/src/legacy/typography/elements/H1.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/H1.tsx
rename to code/ui/components/src/legacy/typography/elements/H1.tsx
diff --git a/code/ui/components/src/typography/elements/H2.tsx b/code/ui/components/src/legacy/typography/elements/H2.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/H2.tsx
rename to code/ui/components/src/legacy/typography/elements/H2.tsx
diff --git a/code/ui/components/src/typography/elements/H3.tsx b/code/ui/components/src/legacy/typography/elements/H3.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/H3.tsx
rename to code/ui/components/src/legacy/typography/elements/H3.tsx
diff --git a/code/ui/components/src/typography/elements/H4.tsx b/code/ui/components/src/legacy/typography/elements/H4.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/H4.tsx
rename to code/ui/components/src/legacy/typography/elements/H4.tsx
diff --git a/code/ui/components/src/typography/elements/H5.tsx b/code/ui/components/src/legacy/typography/elements/H5.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/H5.tsx
rename to code/ui/components/src/legacy/typography/elements/H5.tsx
diff --git a/code/ui/components/src/typography/elements/H6.tsx b/code/ui/components/src/legacy/typography/elements/H6.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/H6.tsx
rename to code/ui/components/src/legacy/typography/elements/H6.tsx
diff --git a/code/ui/components/src/typography/elements/HR.tsx b/code/ui/components/src/legacy/typography/elements/HR.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/HR.tsx
rename to code/ui/components/src/legacy/typography/elements/HR.tsx
diff --git a/code/ui/components/src/typography/elements/Img.tsx b/code/ui/components/src/legacy/typography/elements/Img.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/Img.tsx
rename to code/ui/components/src/legacy/typography/elements/Img.tsx
diff --git a/code/ui/components/src/typography/elements/LI.tsx b/code/ui/components/src/legacy/typography/elements/LI.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/LI.tsx
rename to code/ui/components/src/legacy/typography/elements/LI.tsx
diff --git a/code/ui/components/src/typography/elements/Link.tsx b/code/ui/components/src/legacy/typography/elements/Link.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/Link.tsx
rename to code/ui/components/src/legacy/typography/elements/Link.tsx
diff --git a/code/ui/components/src/typography/elements/OL.tsx b/code/ui/components/src/legacy/typography/elements/OL.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/OL.tsx
rename to code/ui/components/src/legacy/typography/elements/OL.tsx
diff --git a/code/ui/components/src/typography/elements/P.tsx b/code/ui/components/src/legacy/typography/elements/P.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/P.tsx
rename to code/ui/components/src/legacy/typography/elements/P.tsx
diff --git a/code/ui/components/src/typography/elements/Pre.tsx b/code/ui/components/src/legacy/typography/elements/Pre.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/Pre.tsx
rename to code/ui/components/src/legacy/typography/elements/Pre.tsx
diff --git a/code/ui/components/src/typography/elements/Span.tsx b/code/ui/components/src/legacy/typography/elements/Span.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/Span.tsx
rename to code/ui/components/src/legacy/typography/elements/Span.tsx
diff --git a/code/ui/components/src/typography/elements/TT.tsx b/code/ui/components/src/legacy/typography/elements/TT.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/TT.tsx
rename to code/ui/components/src/legacy/typography/elements/TT.tsx
diff --git a/code/ui/components/src/typography/elements/Table.tsx b/code/ui/components/src/legacy/typography/elements/Table.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/Table.tsx
rename to code/ui/components/src/legacy/typography/elements/Table.tsx
diff --git a/code/ui/components/src/typography/elements/UL.tsx b/code/ui/components/src/legacy/typography/elements/UL.tsx
similarity index 100%
rename from code/ui/components/src/typography/elements/UL.tsx
rename to code/ui/components/src/legacy/typography/elements/UL.tsx
diff --git a/code/ui/components/src/typography/lib/common.tsx b/code/ui/components/src/legacy/typography/lib/common.tsx
similarity index 100%
rename from code/ui/components/src/typography/lib/common.tsx
rename to code/ui/components/src/legacy/typography/lib/common.tsx
diff --git a/code/ui/components/src/typography/lib/isReactChildString.tsx b/code/ui/components/src/legacy/typography/lib/isReactChildString.tsx
similarity index 100%
rename from code/ui/components/src/typography/lib/isReactChildString.tsx
rename to code/ui/components/src/legacy/typography/lib/isReactChildString.tsx
diff --git a/code/ui/components/src/typography/link/link.stories.tsx b/code/ui/components/src/legacy/typography/link/link.stories.tsx
similarity index 100%
rename from code/ui/components/src/typography/link/link.stories.tsx
rename to code/ui/components/src/legacy/typography/link/link.stories.tsx
diff --git a/code/ui/components/src/typography/link/link.test.tsx b/code/ui/components/src/legacy/typography/link/link.test.tsx
similarity index 100%
rename from code/ui/components/src/typography/link/link.test.tsx
rename to code/ui/components/src/legacy/typography/link/link.test.tsx
diff --git a/code/ui/components/src/typography/link/link.tsx b/code/ui/components/src/legacy/typography/link/link.tsx
similarity index 100%
rename from code/ui/components/src/typography/link/link.tsx
rename to code/ui/components/src/legacy/typography/link/link.tsx
diff --git a/code/ui/components/src/typography/typography.stories.mdx b/code/ui/components/src/legacy/typography/typography.stories.mdx
similarity index 100%
rename from code/ui/components/src/typography/typography.stories.mdx
rename to code/ui/components/src/legacy/typography/typography.stories.mdx
diff --git a/code/ui/components/src/utils/getStoryHref.ts b/code/ui/components/src/legacy/utils/getStoryHref.ts
similarity index 100%
rename from code/ui/components/src/utils/getStoryHref.ts
rename to code/ui/components/src/legacy/utils/getStoryHref.ts
diff --git a/code/ui/components/src/new/Button/Button.stories.tsx b/code/ui/components/src/new/Button/Button.stories.tsx
new file mode 100644
index 000000000000..9366f5af32fa
--- /dev/null
+++ b/code/ui/components/src/new/Button/Button.stories.tsx
@@ -0,0 +1,175 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import React from 'react';
+
+import { Button } from './Button';
+
+const meta: Meta = {
+ title: 'Button',
+ component: Button,
+ tags: ['autodocs'],
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Base = {
+ args: { children: 'Button' },
+};
+
+const Icon = () => (
+
+
+
+
+);
+
+export const Types: Story = {
+ render: () => (
+
+ Primary
+ Secondary
+ Tertiary
+
+ ),
+};
+
+export const Active: Story = {
+ render: () => (
+
+
+ Primary
+
+
+ Secondary
+
+
+ Tertiary
+
+
+ ),
+};
+
+export const WithIcon: Story = {
+ render: () => (
+
+ }>
+ Primary
+
+ }>
+ Secondary
+
+ }>
+ Tertiary
+
+
+ ),
+};
+
+export const Sizes: Story = {
+ render: () => (
+
+ }>
+ Small Button
+
+ } iconOnly>
+ Small Button
+
+ }>
+ Medium Button
+
+ } iconOnly>
+ Medium Button
+
+
+ ),
+};
+
+export const IconOnly: Story = {
+ parameters: {
+ docs: {
+ description: {
+ story: 'This is a story that shows how to use the `iconOnly` prop.',
+ },
+ source: {
+ type: 'dynamic',
+ },
+ },
+ },
+ render: () => (
+ <>
+ }>
+ Primary
+
+ }>
+ Secondary
+
+ }>
+ Tertiary
+
+ }>
+ Primary
+
+ }>
+ Secondary
+
+ }>
+ Tertiary
+
+ >
+ ),
+ decorators: [
+ (Story) => {Story()}
,
+ ],
+};
+
+export const IconOnlyActive: Story = {
+ render: () => (
+
+ } active>
+ Primary
+
+ } active>
+ Secondary
+
+ } active>
+ Tertiary
+
+ } active>
+ Primary
+
+ } active>
+ Secondary
+
+ } active>
+ Tertiary
+
+
+ ),
+};
+
+export const Disabled: Story = {
+ args: {
+ disabled: true,
+ children: 'Disabled Button',
+ },
+};
+
+export const WithHref: Story = {
+ render: () => (
+
+ console.log('Hello')}>I am a button using onClick
+
+ I am an anchor using Href
+
+
+ ),
+};
diff --git a/code/ui/components/src/new/Button/Button.tsx b/code/ui/components/src/new/Button/Button.tsx
new file mode 100644
index 000000000000..a769bdf079e2
--- /dev/null
+++ b/code/ui/components/src/new/Button/Button.tsx
@@ -0,0 +1,123 @@
+import type { ReactNode } from 'react';
+import React, { forwardRef } from 'react';
+import { styled } from '@storybook/theming';
+import { darken, lighten, rgba, transparentize } from 'polished';
+import type { PropsOf } from '../utils/types';
+
+export interface ButtonProps {
+ as?: T;
+ children: string;
+ size?: 'small' | 'medium';
+ variant?: 'primary' | 'secondary' | 'tertiary';
+ icon?: ReactNode;
+ iconOnly?: boolean;
+ onClick?: () => void;
+ disabled?: boolean;
+ active?: boolean;
+}
+
+export const Button: {
+ (
+ props: ButtonProps & Omit, keyof ButtonProps>
+ ): JSX.Element;
+ displayName?: string;
+} = forwardRef(
+ ({ as, children, icon, ...props }: ButtonProps, ref: React.Ref) => {
+ return (
+
+ {icon}
+ {!props.iconOnly && children}
+
+ );
+ }
+);
+
+Button.displayName = 'Button';
+
+const StyledButton = styled.button>(
+ ({
+ theme,
+ variant = 'primary',
+ size = 'medium',
+ disabled = false,
+ active = false,
+ iconOnly = false,
+ }) => ({
+ border: 0,
+ cursor: disabled ? 'not-allowed' : 'pointer',
+ display: 'inline-flex',
+ gap: '6px',
+ alignItems: 'center',
+ justifyContent: 'center',
+ overflow: 'hidden',
+ padding: `${(() => {
+ if (!iconOnly && size === 'small') return '0 10px';
+ if (!iconOnly && size === 'medium') return '0 12px';
+ return 0;
+ })()}`,
+ width: `${(() => {
+ if (iconOnly && size === 'small') return '28px';
+ if (iconOnly && size === 'medium') return '32px';
+ return 'auto';
+ })()}`,
+ height: size === 'small' ? '28px' : '32px',
+ position: 'relative',
+ textAlign: 'center',
+ textDecoration: 'none',
+ transitionProperty: 'background, box-shadow',
+ transitionDuration: '150ms',
+ transitionTimingFunction: 'ease-out',
+ verticalAlign: 'top',
+ whiteSpace: 'nowrap',
+ userSelect: 'none',
+ opacity: disabled ? 0.5 : 1,
+ margin: 0,
+ fontSize: `${theme.typography.size.s1}px`,
+ fontWeight: theme.typography.weight.bold,
+ lineHeight: '1',
+ background: `${(() => {
+ if (variant === 'primary') return theme.color.secondary;
+ if (variant === 'secondary') return theme.button.background;
+ if (variant === 'tertiary' && active) return theme.background.hoverable;
+ return 'transparent';
+ })()}`,
+ color: `${(() => {
+ if (variant === 'primary') return theme.color.lightest;
+ if (variant === 'secondary') return theme.input.color;
+ if (variant === 'tertiary' && active) return theme.color.secondary;
+ if (variant === 'tertiary') return theme.color.mediumdark;
+ return theme.input.color;
+ })()}`,
+ boxShadow: variant === 'secondary' ? `${theme.button.border} 0 0 0 1px inset` : 'none',
+ borderRadius: theme.input.borderRadius,
+
+ '&:hover': {
+ color: variant === 'tertiary' ? theme.color.secondary : null,
+ background: `${(() => {
+ let bgColor = theme.color.secondary;
+ if (variant === 'primary') bgColor = theme.color.secondary;
+ if (variant === 'secondary') bgColor = theme.button.background;
+
+ if (variant === 'tertiary') return transparentize(0.86, theme.color.secondary);
+ return theme.base === 'light' ? darken(0.02, bgColor) : lighten(0.03, bgColor);
+ })()}`,
+ },
+
+ '&:active': {
+ color: variant === 'tertiary' ? theme.color.secondary : null,
+ background: `${(() => {
+ let bgColor = theme.color.secondary;
+ if (variant === 'primary') bgColor = theme.color.secondary;
+ if (variant === 'secondary') bgColor = theme.button.background;
+
+ if (variant === 'tertiary') return theme.background.hoverable;
+ return theme.base === 'light' ? darken(0.02, bgColor) : lighten(0.03, bgColor);
+ })()}`,
+ },
+
+ '&:focus': {
+ boxShadow: `${rgba(theme.color.secondary, 1)} 0 0 0 1px inset`,
+ outline: 'none',
+ },
+ })
+);
diff --git a/code/ui/components/src/new/Input/Input.stories.tsx b/code/ui/components/src/new/Input/Input.stories.tsx
new file mode 100644
index 000000000000..8130f39aa72d
--- /dev/null
+++ b/code/ui/components/src/new/Input/Input.stories.tsx
@@ -0,0 +1,32 @@
+import type { Meta, StoryObj } from '@storybook/react';
+
+import { Input } from './Input';
+
+const meta: Meta = {
+ title: 'Input',
+ component: Input,
+ tags: ['autodocs'],
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Base: Story = {
+ args: {
+ placeholder: 'Hello World',
+ },
+};
+
+export const Filled: Story = {
+ args: {
+ ...Base.args,
+ value: 'Hello World',
+ },
+};
+
+export const Disabled: Story = {
+ args: {
+ ...Base.args,
+ disabled: true,
+ },
+};
diff --git a/code/ui/components/src/new/Input/Input.tsx b/code/ui/components/src/new/Input/Input.tsx
new file mode 100644
index 000000000000..da10006c960e
--- /dev/null
+++ b/code/ui/components/src/new/Input/Input.tsx
@@ -0,0 +1,55 @@
+import React, { forwardRef } from 'react';
+import { styled } from '@storybook/theming';
+
+interface InputProps {
+ disabled?: boolean;
+ placeholder?: string;
+ value?: string;
+}
+
+export const Input = forwardRef(({ ...props }, ref) => {
+ return ;
+});
+
+Input.displayName = 'Input';
+
+const StyledInput = styled.input(({ theme }) => ({
+ // resets
+ appearance: 'none',
+ border: '0 none',
+ display: 'block',
+ margin: ' 0',
+ position: 'relative',
+
+ // styles
+ width: '100%',
+ height: '32px',
+ transition: 'box-shadow 200ms ease-out, opacity 200ms ease-out',
+ color: theme.input.color,
+ background: theme.input.background,
+ boxShadow: `${theme.input.border} 0 0 0 1px inset`,
+ borderRadius: theme.input.borderRadius,
+ fontSize: theme.typography.size.s2 - 1,
+ padding: '6px 10px',
+ boxSizing: 'border-box',
+ lineHeight: '20px',
+
+ '&:focus': {
+ boxShadow: `${theme.color.secondary} 0 0 0 1px inset`,
+ outline: 'none',
+ },
+
+ '&[disabled]': {
+ cursor: 'not-allowed',
+ opacity: 0.5,
+ },
+
+ '&:-webkit-autofill': {
+ WebkitBoxShadow: `0 0 0 3em ${theme.color.lightest} inset`,
+ },
+
+ '&::placeholder': {
+ color: theme.textMutedColor,
+ opacity: 1,
+ },
+}));
diff --git a/code/ui/components/src/new/Select/Select.stories.tsx b/code/ui/components/src/new/Select/Select.stories.tsx
new file mode 100644
index 000000000000..452fe113fba4
--- /dev/null
+++ b/code/ui/components/src/new/Select/Select.stories.tsx
@@ -0,0 +1,91 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import React from 'react';
+
+import { Select } from './Select';
+
+const meta: Meta = {
+ title: 'Select',
+ component: Select.Root,
+ tags: ['autodocs'],
+ parameters: {
+ layout: 'centered',
+ },
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Base: Story = {
+ args: {},
+ render: (_, { args }) => (
+
+
+
+
+
+
+ Avocado
+ Banana
+ Bilberry
+ Blackberry
+ Blackcurrant
+ Black sapote
+ Blueberry
+ Boysenberry
+ Breadfruit
+ Cacao
+ Cactus pear
+ Canistel
+ Catmon
+ Cempedak
+ Cherimoya
+ Cherry
+ Chico fruit
+ Cloudberry
+ Coco de mer
+ Coconut
+ Crab apple
+ Cranberry
+ Currant
+ Damson
+ Date
+ Dragonfruit
+ Durian
+ Elderberry
+ Feijoa
+ Fig
+ Finger Lime
+ Gac Fruit
+ Goji berry
+ Gooseberry
+ Grape
+ Raisin
+ Grapefruit
+ Grewia asiatica
+ Guava
+ Guyabano
+ Hala Fruit
+ Honeyberry
+ Huckleberry
+ Jabuticaba
+ Jackfruit
+ Jambul
+ Japanese plum
+ Jostaberry
+ Jujube
+ Juniper berry
+ Kaffir Lime
+ Kiwano
+ Kiwifruit
+ Kumquat
+ Lanzones
+ Lemon
+ Lime
+ Loganberry
+ Longan
+ Loquat
+
+
+
+ ),
+};
diff --git a/code/ui/components/src/new/Select/Select.tsx b/code/ui/components/src/new/Select/Select.tsx
new file mode 100644
index 000000000000..0ae606877434
--- /dev/null
+++ b/code/ui/components/src/new/Select/Select.tsx
@@ -0,0 +1,180 @@
+import * as React from 'react';
+import * as SelectPrimitive from '@radix-ui/react-select';
+import { styled } from '@storybook/theming';
+import { ExpandAlt } from './icons/ExpandAlt';
+import { Arrowup } from './icons/Arrowup';
+import { Arrowdown } from './icons/Arrowdown';
+import { Check } from './icons/Check';
+
+const SelectTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+ {children}
+
+
+
+
+));
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
+
+const SelectContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+ {children}
+
+
+
+
+
+));
+SelectContent.displayName = SelectPrimitive.Content.displayName;
+
+const SelectLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => );
+SelectLabel.displayName = SelectPrimitive.Label.displayName;
+
+const SelectItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+ {children}
+
+));
+SelectItem.displayName = SelectPrimitive.Item.displayName;
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => );
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
+
+export const Select = {
+ Root: SelectPrimitive.Root,
+ Group: SelectPrimitive.Group,
+ Value: SelectPrimitive.Value,
+ Trigger: SelectTrigger,
+ Content: SelectContent,
+ Label: SelectLabel,
+ Item: SelectItem,
+ Separator: SelectSeparator,
+};
+
+const StyledTrigger = styled(SelectPrimitive.Trigger)(({ theme }) => ({
+ all: 'unset',
+ display: 'flex',
+ width: '100%',
+ height: '32px',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ transition: 'box-shadow 200ms ease-out, opacity 200ms ease-out',
+ color: theme.input.color,
+ background: theme.input.background,
+ boxShadow: `${theme.input.border} 0 0 0 1px inset`,
+ borderRadius: theme.input.borderRadius,
+ fontSize: theme.typography.size.s2 - 1,
+ padding: '6px 10px',
+ boxSizing: 'border-box',
+ lineHeight: '20px',
+
+ '&:focus': {
+ boxShadow: `${theme.color.secondary} 0 0 0 1px inset`,
+ outline: 'none',
+ },
+
+ '&[disabled]': {
+ cursor: 'not-allowed',
+ opacity: 0.5,
+ },
+
+ '&[data-placeholder]': {
+ color: theme.textMutedColor,
+ },
+
+ '&:-webkit-autofill': {
+ WebkitBoxShadow: `0 0 0 3em ${theme.color.lightest} inset`,
+ },
+}));
+
+const StyledContent = styled(SelectPrimitive.Content)(({ theme }) => ({
+ boxSizing: 'border-box',
+ overflow: 'hidden',
+ backgroundColor: theme.input.background,
+ borderRadius: '6px',
+ border: theme.base === 'dark' ? `1px solid ${theme.input.border}` : '1px solid transparent',
+ width: '100%',
+ boxShadow:
+ '0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2)',
+}));
+
+const StyledViewport = styled(SelectPrimitive.Viewport)(() => ({
+ boxSizing: 'border-box',
+ width: '100%',
+ padding: '5px',
+}));
+
+const StyledScrollUpButton = styled(SelectPrimitive.ScrollUpButton)(({ theme }) => ({
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ height: '25px',
+ backgroundColor: theme.input.background,
+ color: theme.input.color,
+ cursor: 'default',
+}));
+
+const StyledScrollDownButton = styled(SelectPrimitive.ScrollDownButton)(({ theme }) => ({
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ height: '25px',
+ backgroundColor: theme.input.background,
+ color: theme.input.color,
+ cursor: 'default',
+}));
+
+const StyledItem = styled(SelectPrimitive.Item)(({ theme }) => ({
+ fontSize: '13px',
+ lineHeight: 1,
+ color: theme.input.color,
+ borderRadius: '3px',
+ display: 'flex',
+ alignItems: 'center',
+ height: '25px',
+ padding: '0 35px 0 25px',
+ position: 'relative',
+ userSelect: 'none',
+
+ '&[data-disabled]': {
+ color: 'red',
+ pointerEvents: 'none',
+ },
+
+ '&[data-highlighted]': {
+ outline: 'none',
+ backgroundColor: theme.barSelectedColor,
+ color: theme.barBg,
+ },
+}));
+
+const StyledItemIndicator = styled(SelectPrimitive.ItemIndicator)(() => ({
+ position: 'absolute',
+ left: 0,
+ width: '25px',
+ display: 'inline-flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+}));
diff --git a/code/ui/components/src/new/Select/SelectItem.tsx b/code/ui/components/src/new/Select/SelectItem.tsx
new file mode 100644
index 000000000000..6206e76095ca
--- /dev/null
+++ b/code/ui/components/src/new/Select/SelectItem.tsx
@@ -0,0 +1,62 @@
+import * as RadixSelect from '@radix-ui/react-select';
+import React, { forwardRef } from 'react';
+import { styled } from '@storybook/theming';
+
+interface SelectItemProps {
+ children: React.ReactNode;
+ value: string;
+}
+
+const FakeIcon = styled.div(({ theme }) => ({
+ width: 12,
+ height: 12,
+ backgroundColor: theme.color.mediumdark,
+}));
+
+export const SelectItem = forwardRef(
+ ({ children, ...props }, forwardedRef) => {
+ return (
+
+ {children}
+
+
+
+
+ );
+ }
+);
+
+SelectItem.displayName = 'SelectItem';
+
+const StyledItem = styled(RadixSelect.Item)(() => ({
+ fontSize: '13px',
+ lineHeight: 1,
+ color: 'blue',
+ borderRadius: '3px',
+ display: 'flex',
+ alignItems: 'center',
+ height: '25px',
+ padding: '0 35px 0 25px',
+ position: 'relative',
+ userSelect: 'none',
+
+ '&[data-disabled]': {
+ color: 'red',
+ pointerEvents: 'none',
+ },
+
+ '&[data-highlighted]': {
+ outline: 'none',
+ backgroundColor: 'green',
+ color: 'white',
+ },
+}));
+
+const StyledItemIndicator = styled(RadixSelect.ItemIndicator)(() => ({
+ position: 'absolute',
+ left: 0,
+ width: '25px',
+ display: 'inline-flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+}));
diff --git a/code/ui/components/src/new/Select/icons/Arrowdown.tsx b/code/ui/components/src/new/Select/icons/Arrowdown.tsx
new file mode 100644
index 000000000000..d8437ca5d122
--- /dev/null
+++ b/code/ui/components/src/new/Select/icons/Arrowdown.tsx
@@ -0,0 +1,29 @@
+import * as React from 'react';
+import type { IconProps } from './types';
+import { IconWrapper } from './IconWrapper';
+
+export const Arrowdown = (allProps: IconProps) => {
+ const { svgProps: props, ...restProps } = allProps;
+ return (
+
+
+
+ }
+ {...restProps}
+ />
+ );
+};
+
+export default Arrowdown;
diff --git a/code/ui/components/src/new/Select/icons/Arrowup.tsx b/code/ui/components/src/new/Select/icons/Arrowup.tsx
new file mode 100644
index 000000000000..24f722e410a6
--- /dev/null
+++ b/code/ui/components/src/new/Select/icons/Arrowup.tsx
@@ -0,0 +1,29 @@
+import * as React from 'react';
+import type { IconProps } from './types';
+import { IconWrapper } from './IconWrapper';
+
+export const Arrowup = (allProps: IconProps) => {
+ const { svgProps: props, ...restProps } = allProps;
+ return (
+
+
+
+ }
+ {...restProps}
+ />
+ );
+};
+
+export default Arrowup;
diff --git a/code/ui/components/src/new/Select/icons/Check.tsx b/code/ui/components/src/new/Select/icons/Check.tsx
new file mode 100644
index 000000000000..4d5beb1531a3
--- /dev/null
+++ b/code/ui/components/src/new/Select/icons/Check.tsx
@@ -0,0 +1,29 @@
+import * as React from 'react';
+import type { IconProps } from './types';
+import { IconWrapper } from './IconWrapper';
+
+export const Check = (allProps: IconProps) => {
+ const { svgProps: props, ...restProps } = allProps;
+ return (
+
+
+
+ }
+ {...restProps}
+ />
+ );
+};
+
+export default Check;
diff --git a/code/ui/components/src/new/Select/icons/ExpandAlt.tsx b/code/ui/components/src/new/Select/icons/ExpandAlt.tsx
new file mode 100644
index 000000000000..7f0626adcbea
--- /dev/null
+++ b/code/ui/components/src/new/Select/icons/ExpandAlt.tsx
@@ -0,0 +1,29 @@
+import * as React from 'react';
+import type { IconProps } from './types';
+import { IconWrapper } from './IconWrapper';
+
+export const ExpandAlt = (allProps: IconProps) => {
+ const { svgProps: props, ...restProps } = allProps;
+ return (
+
+
+
+ }
+ {...restProps}
+ />
+ );
+};
+
+export default ExpandAlt;
diff --git a/code/ui/components/src/new/Select/icons/IconWrapper.tsx b/code/ui/components/src/new/Select/icons/IconWrapper.tsx
new file mode 100644
index 000000000000..a4c60d6709f8
--- /dev/null
+++ b/code/ui/components/src/new/Select/icons/IconWrapper.tsx
@@ -0,0 +1,29 @@
+import * as React from 'react';
+import type { IconProps } from './types';
+
+export const IconWrapper: React.FC<{ icon: React.ReactNode } & IconProps> = ({
+ icon,
+ color: colorProp,
+ size: sizeProp,
+ ...restProps
+}) => {
+ const color = colorProp || 'currentColor';
+ const size = sizeProp || '14px';
+
+ return (
+
+ {icon}
+
+ );
+};
diff --git a/code/ui/components/src/new/Select/icons/types.ts b/code/ui/components/src/new/Select/icons/types.ts
new file mode 100644
index 000000000000..142493c5ee59
--- /dev/null
+++ b/code/ui/components/src/new/Select/icons/types.ts
@@ -0,0 +1,10 @@
+import type React from 'react';
+
+export type IconProps = {
+ /** Set icon fill color from design system */
+ color?: string;
+ /** Set width and height of icon in pixels */
+ size?: number;
+ /** Props to pass directly to svg element */
+ svgProps?: React.SVGProps;
+} & Omit, 'color' | 'size'>;
diff --git a/code/ui/components/src/new/Textarea/Textarea.stories.tsx b/code/ui/components/src/new/Textarea/Textarea.stories.tsx
new file mode 100644
index 000000000000..b23516d8f2bd
--- /dev/null
+++ b/code/ui/components/src/new/Textarea/Textarea.stories.tsx
@@ -0,0 +1,33 @@
+import type { Meta, StoryObj } from '@storybook/react';
+
+import { Textarea } from './Textarea';
+
+const meta: Meta = {
+ title: 'Textarea',
+ component: Textarea,
+ tags: ['autodocs'],
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Base: Story = {
+ args: {
+ placeholder: 'Hello World',
+ },
+};
+
+export const Filled: Story = {
+ args: {
+ ...Base.args,
+ value:
+ 'Self ocean ultimate reason faith virtues evil eternal-return moral strong superiority. Society will christian god holiest evil virtues ultimate salvation aversion victorious strong eternal-return. Ascetic pious hope selfish battle pinnacle revaluation passion ocean passion chaos reason intentions. Hope hatred pious superiority ascetic chaos ultimate mountains ideal. Superiority good abstract hatred holiest passion ultimate evil inexpedient joy. Salvation war salvation ideal decieve good law ascetic hatred transvaluation horror good. Zarathustra aversion pious truth burying evil inexpedient spirit virtues virtues hope salvation transvaluation. Enlightenment chaos ascetic salvation god holiest play marvelous oneself ocean. Enlightenment faithful dead truth insofar fearful madness love.Inexpedient war hatred superiority disgust justice superiority. Chaos justice contradict christian decieve god. Revaluation suicide hope enlightenment decrepit truth hatred insofar gains sexuality merciful ocean revaluation depths. Revaluation ocean superiority endless of evil horror. Ultimate salvation joy good good endless will horror aversion superiority depths. Evil hatred ideal pious joy reason.',
+ },
+};
+
+export const Disabled: Story = {
+ args: {
+ ...Base.args,
+ disabled: true,
+ },
+};
diff --git a/code/ui/components/src/new/Textarea/Textarea.tsx b/code/ui/components/src/new/Textarea/Textarea.tsx
new file mode 100644
index 000000000000..15a156bf8b1f
--- /dev/null
+++ b/code/ui/components/src/new/Textarea/Textarea.tsx
@@ -0,0 +1,58 @@
+import React, { forwardRef } from 'react';
+import { styled } from '@storybook/theming';
+import TextareaAutoResize from 'react-textarea-autosize';
+
+interface TextareaProps {
+ disabled?: boolean;
+ placeholder?: string;
+ value?: string;
+}
+
+export const Textarea = forwardRef(({ ...props }, ref) => {
+ return ;
+});
+
+Textarea.displayName = 'Textarea';
+
+const StyledTextarea = styled(TextareaAutoResize)(({ theme }) => ({
+ // resets
+ appearance: 'none',
+ border: '0 none',
+ margin: ' 0',
+ position: 'relative',
+
+ // styles
+ display: 'flex',
+ alignItems: 'center',
+ width: '100%',
+ height: '32px',
+ transition: 'box-shadow 200ms ease-out, opacity 200ms ease-out',
+ color: theme.input.color,
+ background: theme.input.background,
+ boxShadow: `${theme.input.border} 0 0 0 1px inset`,
+ borderRadius: theme.input.borderRadius,
+ fontSize: theme.typography.size.s2 - 1,
+ padding: '6px 10px',
+ boxSizing: 'border-box',
+ minHeight: 32,
+ lineHeight: '20px',
+
+ '&:focus': {
+ boxShadow: `${theme.color.secondary} 0 0 0 1px inset`,
+ outline: 'none',
+ },
+
+ '&[disabled]': {
+ cursor: 'not-allowed',
+ opacity: 0.5,
+ },
+
+ '&:-webkit-autofill': {
+ WebkitBoxShadow: `0 0 0 3em ${theme.color.lightest} inset`,
+ },
+
+ '&::placeholder': {
+ color: theme.textMutedColor,
+ opacity: 1,
+ },
+}));
diff --git a/code/ui/components/src/new/utils/types.ts b/code/ui/components/src/new/utils/types.ts
new file mode 100644
index 000000000000..f5713d0b3479
--- /dev/null
+++ b/code/ui/components/src/new/utils/types.ts
@@ -0,0 +1,4 @@
+import type React from 'react';
+
+export type PropsOf> =
+ JSX.LibraryManagedAttributes>;
diff --git a/code/ui/components/src/tabs/tabs.helpers.tsx b/code/ui/components/src/tabs/tabs.helpers.tsx
deleted file mode 100644
index b92d38733077..000000000000
--- a/code/ui/components/src/tabs/tabs.helpers.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { styled } from '@storybook/theming';
-import type { ReactElement } from 'react';
-import React, { Children } from 'react';
-
-export interface VisuallyHiddenProps {
- active?: boolean;
-}
-
-export const VisuallyHidden = styled.div(({ active }) =>
- active ? { display: 'block' } : { display: 'none' }
-);
-
-export const childrenToList = (children: any, selected: string) =>
- Children.toArray(children).map(
- ({ props: { title, id, color, children: childrenOfChild } }: ReactElement, index) => {
- const content = Array.isArray(childrenOfChild) ? childrenOfChild[0] : childrenOfChild;
- return {
- active: selected ? id === selected : index === 0,
- title,
- id,
- color,
- render:
- typeof content === 'function'
- ? content
- : ({ active, key }: any) => (
-
- {content}
-
- ),
- };
- }
- );
-
-export type ChildrenList = ReturnType;
diff --git a/code/ui/components/tsconfig.json b/code/ui/components/tsconfig.json
index 8ee47dbfcd6e..a2bea817d943 100644
--- a/code/ui/components/tsconfig.json
+++ b/code/ui/components/tsconfig.json
@@ -2,6 +2,7 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"types": ["react-syntax-highlighter", "jest", "testing-library__jest-dom"],
+ "skipLibCheck": true,
"strict": false
},
"include": ["src/**/*"]
diff --git a/code/ui/manager/package.json b/code/ui/manager/package.json
index dacb0055889d..45ac1d41e38c 100644
--- a/code/ui/manager/package.json
+++ b/code/ui/manager/package.json
@@ -46,15 +46,13 @@
"*.d.ts"
],
"scripts": {
- "check": "../../../scripts/node_modules/.bin/tsc --noEmit",
+ "check": "../../../scripts/prepare/check.ts",
"prep": "../../../scripts/prepare/esm-bundle.ts"
},
"devDependencies": {
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
"@storybook/addon-designs": "^7.0.0",
"@storybook/addons": "7.1.0",
- "@storybook/api": "7.1.0",
- "@storybook/channel-postmessage": "7.1.0",
"@storybook/channels": "7.1.0",
"@storybook/client-logger": "7.1.0",
"@storybook/components": "7.1.0",
diff --git a/code/ui/manager/src/app.stories.tsx b/code/ui/manager/src/app.stories.tsx
index 8709f9c60038..aef6a9133aa0 100644
--- a/code/ui/manager/src/app.stories.tsx
+++ b/code/ui/manager/src/app.stories.tsx
@@ -1,7 +1,7 @@
import React, { useEffect } from 'react';
-import { Provider as ManagerProvider, useStorybookApi } from '@storybook/manager-api';
-import { LocationProvider } from '@storybook/router';
+import { useStorybookApi, Provider as ManagerProvider } from '@storybook/manager-api';
+import { BaseLocationProvider } from '@storybook/router';
import { HelmetProvider } from 'react-helmet-async';
import { styled } from '@storybook/theming';
import App from './app';
@@ -11,25 +11,41 @@ export default {
component: App,
parameters: {
layout: 'fullscreen',
+ theme: 'light',
+ viewport: {
+ viewports: {
+ tablet: {
+ name: 'Tablet',
+ styles: {
+ height: '1112px',
+ width: '834px',
+ },
+ type: 'tablet',
+ },
+ },
+ defaultViewport: 'tablet',
+ defaultOrientation: 'landscape',
+ },
+ chromatic: { viewports: [1112] },
},
decorators: [
(StoryFn: any) => (
-
-
+
+
-
-
+
+
),
],
};
-const ThemeStack = styled.div(
+const Background = styled.div(
{
position: 'relative',
- minHeight: '50vh',
- height: '100%',
+ minHeight: '100vh',
+ height: '100vw',
},
({ theme }) => ({
background: theme.background.app,
@@ -53,6 +69,7 @@ export const Default = () => {
key="manager"
provider={provider}
path="/story/ui-app--loading-state"
+ viewMode="story"
storyId="ui-app--loading-state"
location={{ search: '' }}
navigate={() => {}}
@@ -63,7 +80,7 @@ export const Default = () => {
key="app"
viewMode="story"
layout={{
- initialActive: 'addons',
+ initialActive: 'sidebar',
isFullscreen: false,
showToolbar: true,
panelPosition: 'right',
@@ -71,6 +88,7 @@ export const Default = () => {
showPanel: true,
showTabs: true,
}}
+ pages={[]}
panelCount={0}
/>
@@ -91,7 +109,7 @@ export const LoadingState = () => (
key="app"
viewMode="story"
layout={{
- initialActive: 'addons',
+ initialActive: 'sidebar',
isFullscreen: false,
showToolbar: true,
panelPosition: 'right',
@@ -99,7 +117,32 @@ export const LoadingState = () => (
showPanel: true,
showTabs: true,
}}
+ pages={[]}
panelCount={0}
/>
);
+
+export const Dark = () => LoadingState();
+Dark.parameters = {
+ theme: 'dark',
+};
+export const Mobile = () => LoadingState();
+Mobile.parameters = {
+ theme: 'light',
+ viewport: {
+ viewports: {
+ mobile1: {
+ name: 'Small mobile',
+ styles: {
+ height: '568px',
+ width: '320px',
+ },
+ type: 'mobile',
+ },
+ },
+ defaultViewport: 'mobile1',
+ defaultOrientation: 'portrait',
+ },
+ chromatic: { viewports: [320] },
+};
diff --git a/code/ui/manager/src/app.tsx b/code/ui/manager/src/app.tsx
index 3935126f4674..607661b56a28 100644
--- a/code/ui/manager/src/app.tsx
+++ b/code/ui/manager/src/app.tsx
@@ -1,12 +1,11 @@
-import type { FC } from 'react';
import React, { useMemo } from 'react';
import { useResizeDetector } from 'react-resize-detector';
import { type State } from '@storybook/manager-api';
import { Symbols } from '@storybook/components';
-import { Route } from '@storybook/router';
import { Global, createGlobal, styled } from '@storybook/theming';
+import type { Addon_PageType } from '@storybook/types';
import { Mobile } from './components/layout/mobile';
import { Desktop } from './components/layout/desktop';
import Sidebar from './containers/sidebar';
@@ -14,8 +13,6 @@ import Preview from './containers/preview';
import Panel from './containers/panel';
import Notifications from './containers/notifications';
-import SettingsPages from './settings';
-
const View = styled.div({
position: 'fixed',
overflow: 'hidden',
@@ -27,9 +24,10 @@ export interface AppProps {
viewMode: State['viewMode'];
layout: State['layout'];
panelCount: number;
+ pages: Addon_PageType[];
}
-const App: React.FC = ({ viewMode, layout, panelCount }) => {
+const App: React.FC = ({ viewMode, layout, panelCount, pages }) => {
const { width, height, ref } = useResizeDetector();
let content;
@@ -39,17 +37,6 @@ const App: React.FC = ({ viewMode, layout, panelCount }) => {
Preview,
Panel,
Notifications,
- pages: [
- {
- key: 'settings',
- render: () => ,
- route: (({ children }) => (
-
- {children}
-
- )) as FC,
- },
- ],
}),
[]
);
@@ -57,7 +44,7 @@ const App: React.FC = ({ viewMode, layout, panelCount }) => {
if (!width || !height) {
content =
;
} else if (width < 600) {
- content = ;
+ content = ;
} else {
content = (
= ({ viewMode, layout, panelCount }) => {
width={width}
height={height}
panelCount={panelCount}
+ pages={pages}
/>
);
}
diff --git a/code/ui/manager/src/components/layout/app.mockdata.tsx b/code/ui/manager/src/components/layout/app.mockdata.tsx
index 48e2696d3306..32b22148adfb 100644
--- a/code/ui/manager/src/components/layout/app.mockdata.tsx
+++ b/code/ui/manager/src/components/layout/app.mockdata.tsx
@@ -2,7 +2,8 @@ import { global } from '@storybook/global';
import type { FC } from 'react';
import React, { Component } from 'react';
import { styled } from '@storybook/theming';
-import type { Addon_Collection } from '@storybook/types';
+import type { Addon_BaseType, Addon_Collection } from '@storybook/types';
+import { Addon_TypesEnum } from '@storybook/types';
import type { State } from '@storybook/manager-api';
import type { SidebarProps } from '../sidebar/Sidebar';
import { Sidebar } from '../sidebar/Sidebar';
@@ -37,9 +38,10 @@ export const shortcuts: State['shortcuts'] = {
remount: ['alt', 'R'],
};
-export const panels: Addon_Collection = {
+export const panels: Addon_Collection = {
test1: {
title: 'Test 1',
+ type: Addon_TypesEnum.PANEL,
render: ({ active, key }) =>
active ? (
@@ -49,6 +51,7 @@ export const panels: Addon_Collection = {
},
test2: {
title: 'Test 2',
+ type: Addon_TypesEnum.PANEL,
render: ({ active, key }) =>
active ? (
@@ -62,6 +65,7 @@ const realSidebarProps: SidebarProps = {
index: mockDataset.withRoot as SidebarProps['index'],
menu: [],
refs: {},
+ status: {},
previewInitialized: true,
};
@@ -162,8 +166,8 @@ export const mockProps: DesktopProps = {
},
viewMode: 'story',
panelCount: 2,
- width: 900,
- height: 600,
+ width: 1112,
+ height: 834,
};
export const realProps: DesktopProps = {
diff --git a/code/ui/manager/src/components/layout/desktop.stories.tsx b/code/ui/manager/src/components/layout/desktop.stories.tsx
index ae70cbc0da1a..780a844d6a31 100644
--- a/code/ui/manager/src/components/layout/desktop.stories.tsx
+++ b/code/ui/manager/src/components/layout/desktop.stories.tsx
@@ -1,8 +1,10 @@
-import React, { Fragment } from 'react';
+import React from 'react';
import type { DecoratorFn } from '@storybook/react';
import isChromatic from 'chromatic/isChromatic';
+import { BaseLocationProvider } from '@storybook/router';
+import { types } from '@storybook/manager-api';
import type { DesktopProps } from './desktop';
import { Desktop } from './desktop';
@@ -12,7 +14,27 @@ import { mockProps, realProps, MockPage } from './app.mockdata';
export default {
title: 'Layout/Desktop',
component: Desktop,
- parameters: { passArgsFirst: false },
+ parameters: {
+ passArgsFirst: false,
+ path: 'story/my-id',
+ layout: 'fullscreen',
+ viewport: {
+ viewports: {
+ tablet: {
+ name: 'Tablet',
+ styles: {
+ height: '1112px',
+ width: '834px',
+ },
+ type: 'tablet',
+ },
+ },
+ defaultViewport: 'tablet',
+ defaultOrientation: 'landscape',
+ },
+ theme: 'light',
+ chromatic: { viewports: [1112] },
+ },
decorators: [
((StoryFn, c) => {
const mocked = true;
@@ -24,9 +46,11 @@ export default {
const props = mocked ? mockProps : realProps;
return (
-
- ;
-
+
+
+
+
+
);
}) as DecoratorFn,
],
@@ -56,11 +80,13 @@ export const Page = ({ props }: { props: DesktopProps }) => (
{...props}
pages={[
{
- key: 'settings',
- route: ({ children }) =>
{children} ,
+ id: '/settings/',
+ title: 'Settings',
+ url: '/settings/',
+ type: types.experimental_PAGE,
render: () =>
,
},
]}
- viewMode="settings"
/>
);
+Page.parameters = { path: '/settings/' };
diff --git a/code/ui/manager/src/components/layout/desktop.tsx b/code/ui/manager/src/components/layout/desktop.tsx
index 2ff78c25d149..2004f3817ef5 100644
--- a/code/ui/manager/src/components/layout/desktop.tsx
+++ b/code/ui/manager/src/components/layout/desktop.tsx
@@ -1,7 +1,9 @@
-import type { ComponentType, FC } from 'react';
+import type { ComponentType } from 'react';
import React, { Fragment } from 'react';
import type { State } from '@storybook/manager-api';
+import { Route } from '@storybook/router';
+import type { Addon_PageType } from '@storybook/types';
import * as S from './container';
export interface DesktopProps {
@@ -12,11 +14,7 @@ export interface DesktopProps {
Preview: ComponentType
;
Panel: ComponentType;
Notifications: ComponentType;
- pages: {
- key: string;
- route: FC;
- render: ComponentType;
- }[];
+ pages: Addon_PageType[];
options: State['layout'];
viewMode: string;
}
@@ -56,18 +54,22 @@ const Desktop = Object.assign(
-
-
-
+
+
+
+
-
-
-
+
+
+
+
+
+
- {pages.map(({ key, route: Route, render: Content }) => (
-
+ {pages.map(({ id, render: Content }) => (
+
-
+
))}
diff --git a/code/ui/manager/src/components/layout/mobile.stories.tsx b/code/ui/manager/src/components/layout/mobile.stories.tsx
index e1a1942a7c62..2e9966d6e8fc 100644
--- a/code/ui/manager/src/components/layout/mobile.stories.tsx
+++ b/code/ui/manager/src/components/layout/mobile.stories.tsx
@@ -1,7 +1,9 @@
-import React, { Fragment } from 'react';
+import React from 'react';
import { ActiveTabs } from '@storybook/manager-api';
import type { DecoratorFn } from '@storybook/react';
+import { BaseLocationProvider } from '@storybook/router';
+import { Addon_TypesEnum } from '@storybook/types';
import type { MobileProps } from './mobile';
import { Mobile } from './mobile';
@@ -10,16 +12,40 @@ import { mockProps, realProps, MockPage } from './app.mockdata';
export default {
title: 'Layout/Mobile',
component: Mobile,
- parameters: { passArgsFirst: false },
+ parameters: {
+ passArgsFirst: false,
+ path: 'story/my-id',
+ layout: 'fullscreen',
+ viewport: {
+ viewports: {
+ mobile1: {
+ name: 'Small mobile',
+ styles: {
+ height: '568px',
+ width: '320px',
+ },
+ type: 'mobile',
+ },
+ },
+ defaultViewport: 'mobile1',
+ defaultOrientation: 'portrait',
+ },
+ theme: 'light',
+ chromatic: { viewports: [320] },
+ },
decorators: [
- ((storyFn, c) => {
+ ((StoryFn, c) => {
const mocked = true;
- const props = {
- ...(mocked ? mockProps : realProps),
- };
+ const props = mocked ? mockProps : realProps;
- return storyFn({ props, ...c });
+ return (
+
+
+
+
+
+ );
}) as DecoratorFn,
],
};
@@ -50,11 +76,16 @@ export const Page = ({ props }: { props: MobileProps }) => (
options={{ ...props.options, initialActive: ActiveTabs.CANVAS }}
pages={[
{
- key: 'settings',
- route: ({ children }) => {children} ,
+ id: 'settings',
+ url: '/settings',
+ title: 'Settings',
+ type: Addon_TypesEnum.experimental_PAGE,
render: () => ,
},
]}
viewMode="settings"
/>
);
+Page.parameters = {
+ path: '/settings/',
+};
diff --git a/code/ui/manager/src/components/layout/mobile.tsx b/code/ui/manager/src/components/layout/mobile.tsx
index 6b14ebad793b..2f2ef7d1e31c 100644
--- a/code/ui/manager/src/components/layout/mobile.tsx
+++ b/code/ui/manager/src/components/layout/mobile.tsx
@@ -1,9 +1,11 @@
import type { ComponentType, FC, ReactNode } from 'react';
-import React, { Component, Children } from 'react';
-import { type State, ActiveTabs } from '@storybook/manager-api';
+import React, { Fragment, Children, useCallback, useState } from 'react';
+import { type State, ActiveTabs, useStorybookApi } from '@storybook/manager-api';
import { styled } from '@storybook/theming';
import { TabButton } from '@storybook/components';
+import { Location } from '@storybook/router';
+import type { Addon_PageType } from '@storybook/types';
import { Root } from './Root';
export type ActiveTabsType = 'sidebar' | 'canvas' | 'addons';
@@ -127,12 +129,6 @@ const Bar = styled.nav(
})
);
-export interface Page {
- key: string;
- route: FC;
- render: FC;
-}
-
export interface MobileProps {
options: Pick;
Sidebar: ComponentType;
@@ -140,79 +136,107 @@ export interface MobileProps {
Panel: ComponentType;
Notifications: ComponentType;
viewMode: State['viewMode'];
- pages: Page[];
+ pages: Addon_PageType[];
}
export interface MobileState {
active: ActiveTabsType;
}
-class Mobile extends Component {
- constructor(props: MobileProps) {
- super(props);
-
- const { options } = props;
- this.state = {
- active: options.isFullscreen ? CANVAS : options.initialActive || SIDEBAR,
- };
- }
-
- render() {
- const { Sidebar, Preview, Panel, Notifications, pages, viewMode, options } = this.props;
-
- const { active } = this.state;
- return (
-
-
-
-
-
-
-
- {pages.map(({ key, route: Route, render: Content }) => (
-
-
-
- ))}
+export const Mobile = ({
+ Sidebar,
+ Preview,
+ Panel,
+ Notifications,
+ pages,
+ viewMode,
+ options,
+}: MobileProps) => {
+ const [{ active }, setState] = useState({
+ active: options.isFullscreen ? CANVAS : options.initialActive || SIDEBAR,
+ });
+ const api = useStorybookApi();
+ const handleCanvasClick = useCallback(() => {
+ setState({ active: CANVAS });
+ const id = api.retrieveSelection();
+ if (id) {
+ api.selectStory(id);
+ } else {
+ api.selectFirstStory();
+ }
+ }, []);
+
+ const handleSideBarClick = useCallback(() => {
+ setState({ active: SIDEBAR });
+ const id = api.retrieveSelection();
+ if (id) {
+ api.selectStory(id);
+ } else {
+ api.selectFirstStory();
+ }
+ }, []);
+
+ return (
+
+
+
+
+
+
+
-
-
- {!options.isFullscreen && (
-
- this.setState({ active: SIDEBAR })}
- active={active === SIDEBAR}
- >
- Sidebar
-
- this.setState({ active: CANVAS })} active={active === CANVAS}>
- {viewMode ? 'Canvas' : null}
- {pages.map(({ key, route: Route }) => (
- {key}
- ))}
+ {pages.map(({ id, render: Content }) => (
+
+
+
+ ))}
+
+
+
+ {!options.isFullscreen && (
+
+
+ Sidebar
+
+
+ Canvas
+
+
+ {({ path }) => (
+ <>
+ {pages.map(({ id, title, url }) => (
+ {
+ setState({ active: CANVAS });
+ api.navigateUrl(url, { plain: false });
+ }}
+ active={active === CANVAS && path.startsWith(url)}
+ >
+ {title}
+
+ ))}
+ >
+ )}
+
+ {viewMode === 'story' && options.showPanel ? (
+ setState({ active: ADDONS })} active={active === ADDONS}>
+ Addons
- {viewMode && options.showPanel ? (
- this.setState({ active: ADDONS })}
- active={active === ADDONS}
- >
- Addons
-
- ) : null}
-
- )}
-
- );
- }
-}
-
-export { Mobile };
+ ) : null}
+
+ )}
+
+ );
+};
diff --git a/code/ui/manager/src/components/panel/panel.stories.tsx b/code/ui/manager/src/components/panel/panel.stories.tsx
index 75a66c60942d..1b31eacd21e7 100644
--- a/code/ui/manager/src/components/panel/panel.stories.tsx
+++ b/code/ui/manager/src/components/panel/panel.stories.tsx
@@ -1,5 +1,7 @@
-import React, { useState } from 'react';
+import React, { useCallback, useRef, useState } from 'react';
import { action } from '@storybook/addon-actions';
+import { Badge, Icons, Spaced } from '@storybook/components';
+import { Addon_TypesEnum } from '@storybook/types';
import Panel from './panel';
import { panels, shortcuts } from '../layout/app.mockdata';
@@ -25,6 +27,119 @@ export const Default = () => {
);
};
+export const JSXTitles = () => {
+ const [selectedPanel, setSelectedPanel] = useState('function-string');
+ return (
+
'Test 1',
+ render: ({ active, key }) =>
+ active ? (
+
+ TEST as string
+
+ ) : null,
+ },
+ 'function-jsx': {
+ type: Addon_TypesEnum.PANEL,
+ title: () => (
+
+ ),
+ render: ({ active, key }) =>
+ active ? (
+
+ TEST with label
+
+ ) : null,
+ },
+ 'function-jsx-icon': {
+ type: Addon_TypesEnum.PANEL,
+ title: () => (
+
+ ),
+ render: ({ active, key }) =>
+ active ? (
+
+ TEST with label
+
+ ) : null,
+ },
+ 'function-jsx-state': {
+ type: Addon_TypesEnum.PANEL,
+ title: () => {
+ const MAX = 10;
+ const [count, setCount] = useState(0);
+ const timer = useRef(null);
+
+ const startTimer = useCallback((event) => {
+ event.stopPropagation();
+ if (timer.current) {
+ return;
+ }
+ timer.current = setInterval(() => {
+ setCount((c) => {
+ if (c === MAX) {
+ clearInterval(timer.current);
+ timer.current = null;
+ return c;
+ }
+ return c + 1;
+ });
+ }, 1000);
+ }, []);
+ const stopTimer = useCallback((event) => {
+ event.stopPropagation();
+ if (timer.current) {
+ clearInterval(timer.current);
+ timer.current = null;
+ }
+ }, []);
+
+ return (
+
+
+ Hover over me!
+ {count ? (
+ 8 ? 'critical' : 'warning'}>{count}
+ ) : null}
+
+
+ );
+ },
+ render: ({ active, key }) => {
+ return active ? (
+
+ TEST with timer
+
+ ) : null;
+ },
+ },
+ }}
+ actions={{ onSelect: setSelectedPanel, toggleVisibility, togglePosition }}
+ selectedPanel={selectedPanel}
+ shortcuts={shortcuts}
+ />
+ );
+};
+
export const NoPanels = () => (
string) | string;
+ title: Addon_BaseType['title'];
id: string;
- children: ReactElement;
+ children: Addon_BaseType['render'];
}
-const SafeTabContent = React.memo(function SafeTabContent({ children }) {
- return children;
-});
-
class SafeTab extends Component {
constructor(props: SafeTabProps) {
super(props);
@@ -29,22 +25,18 @@ class SafeTab extends Component {
render() {
const { hasError } = this.state;
- const { children, title, id } = this.props;
+ const { children } = this.props;
if (hasError) {
return Something went wrong. ;
}
- return (
-
- {children}
-
- );
+ return children;
}
}
const AddonPanel = React.memo<{
selectedPanel?: string;
actions: { onSelect: (id: string) => void } & Record;
- panels: Record;
+ panels: Record;
shortcuts: State['shortcuts'];
panelPosition?: 'bottom' | 'right';
absolute?: boolean;
@@ -89,7 +81,7 @@ const AddonPanel = React.memo<{
id="storybook-panel-root"
>
{Object.entries(panels).map(([k, v]) => (
-
+ : v.title}>
{v.render}
))}
diff --git a/code/ui/manager/src/components/preview/iframe.stories.tsx b/code/ui/manager/src/components/preview/iframe.stories.tsx
index 4c4efe5853fb..5b9e9f65946c 100644
--- a/code/ui/manager/src/components/preview/iframe.stories.tsx
+++ b/code/ui/manager/src/components/preview/iframe.stories.tsx
@@ -6,13 +6,28 @@ import { IFrame } from './iframe';
export default {
component: IFrame,
title: 'Iframe',
+ parameters: {
+ layout: 'fullscreen',
+ viewport: {
+ defaultViewport: 'sized',
+ viewports: {
+ sized: {
+ name: 'Sized',
+ styles: {
+ width: '700px',
+ height: '700px',
+ },
+ },
+ },
+ },
+ theme: 'light',
+ chromatic: { viewports: [700] },
+ },
};
const style: CSSProperties = {
maxWidth: '700px',
- height: '500px',
- border: '2px solid hotpink',
- position: 'relative',
+ height: '700px',
};
export const WorkingStory = () => (
@@ -20,7 +35,7 @@ export const WorkingStory = () => (
active
id="iframe"
title="Missing"
- src="/iframe.html?id=ui-panel--default"
+ src="/iframe.html?id=storybook-components-loader--infinite-state"
allowFullScreen
style={style}
scale={1.0}
diff --git a/code/ui/manager/src/components/preview/iframe.tsx b/code/ui/manager/src/components/preview/iframe.tsx
index 65e84ae2ee7e..58b0c8c0dc8f 100644
--- a/code/ui/manager/src/components/preview/iframe.tsx
+++ b/code/ui/manager/src/components/preview/iframe.tsx
@@ -11,7 +11,7 @@ const StyledIframe = styled.iframe({
height: '100%',
width: '100%',
border: '0 none',
- transition: 'all .3s, background-position 0s, visibility 0s',
+ transition: 'background-position 0s, visibility 0s',
backgroundPosition: '-1px -1px, -1px -1px, -1px -1px, -1px -1px',
});
diff --git a/code/ui/manager/src/components/preview/preview.mockdata.tsx b/code/ui/manager/src/components/preview/preview.mockdata.tsx
index f466b170deec..3129c92bbc72 100644
--- a/code/ui/manager/src/components/preview/preview.mockdata.tsx
+++ b/code/ui/manager/src/components/preview/preview.mockdata.tsx
@@ -1,28 +1,29 @@
import { types } from '@storybook/manager-api';
-import type { API, State, Addon } from '@storybook/manager-api';
+import type { API, State } from '@storybook/manager-api';
+import type { Addon_BaseType, Addon_Collection } from '@storybook/types';
import type { PreviewProps } from './utils/types';
+const addonNotes: Addon_BaseType = {
+ id: 'notes',
+ type: types.TAB,
+ title: 'Notes',
+ route: ({ storyId }) => `/info/${storyId}`,
+ match: ({ viewMode }) => viewMode === 'info',
+ render: () => null,
+};
+
+const mockAPI: Partial = {
+ on: (a, b) => () => {},
+ emit: () => {},
+ off: () => {},
+ getElements: (type) =>
+ type === types.TAB ? ({ notes: addonNotes } as Addon_Collection) : {},
+};
+
export const previewProps: PreviewProps = {
id: 'string',
storyId: 'story--id',
- api: {
- on: () => {},
- emit: () => {},
- off: () => {},
- getElements: ((type) =>
- type === types.TAB
- ? [
- {
- id: 'notes',
- type: types.TAB,
- title: 'Notes',
- route: ({ storyId }) => `/info/${storyId}`,
- match: ({ viewMode }) => viewMode === 'info',
- render: () => null,
- } as Addon,
- ]
- : []) as API['getElements'],
- } as any as API,
+ api: mockAPI as API,
entry: {
tags: [],
type: 'story',
diff --git a/code/ui/manager/src/components/preview/preview.tsx b/code/ui/manager/src/components/preview/preview.tsx
index 9d01ed28eaf5..57572a3949dd 100644
--- a/code/ui/manager/src/components/preview/preview.tsx
+++ b/code/ui/manager/src/components/preview/preview.tsx
@@ -1,16 +1,10 @@
+import type { FC } from 'react';
import React, { Fragment, useMemo, useEffect, useRef, useState } from 'react';
import { Helmet } from 'react-helmet-async';
import { global } from '@storybook/global';
-import {
- type API,
- Consumer,
- type Combo,
- merge,
- addons,
- types,
- type Addon,
-} from '@storybook/manager-api';
+import { type API, Consumer, type Combo, merge, addons, types } from '@storybook/manager-api';
+import type { Addon_BaseType } from '@storybook/types';
import { PREVIEW_BUILDER_PROGRESS, SET_CURRENT_STORY } from '@storybook/core-events';
import { Loader } from '@storybook/components';
@@ -26,8 +20,8 @@ import type { PreviewProps } from './utils/types';
const { FEATURES } = global;
-const getWrappers = (getFn: API['getElements']) => Object.values(getFn(types.PREVIEW));
-const getTabs = (getFn: API['getElements']) => Object.values(getFn(types.TAB));
+const getWrappers = (getFn: API['getElements']) => Object.values(getFn(types.PREVIEW));
+const getTabs = (getFn: API['getElements']) => Object.values(getFn(types.TAB));
const canvasMapper = ({ state, api }: Combo) => ({
storyId: state.storyId,
@@ -42,117 +36,26 @@ const canvasMapper = ({ state, api }: Combo) => ({
active: !!(state.viewMode && state.viewMode.match(/^(story|docs)$/)),
});
-const createCanvas = (id: string, baseUrl = 'iframe.html', withLoader = true): Addon => ({
+const createCanvasTab = (): Addon_BaseType => ({
id: 'canvas',
+ type: types.TAB,
title: 'Canvas',
route: ({ storyId, refId }) => (refId ? `/story/${refId}_${storyId}` : `/story/${storyId}`),
match: ({ viewMode }) => !!(viewMode && viewMode.match(/^(story|docs)$/)),
- render: () => {
- return (
-
- {({
- entry,
- refs,
- customCanvas,
- storyId,
- refId,
- viewMode,
- queryParams,
- getElements,
- previewInitialized,
- active,
- }) => {
- const wrappers = useMemo(
- () => [...defaultWrappers, ...getWrappers(getElements)],
- [getElements, ...defaultWrappers]
- );
-
- const [progress, setProgress] = useState(undefined);
- useEffect(() => {
- if (FEATURES?.storyStoreV7 && global.CONFIG_TYPE === 'DEVELOPMENT') {
- try {
- const channel = addons.getServerChannel();
-
- channel.on(PREVIEW_BUILDER_PROGRESS, (options) => {
- setProgress(options);
- });
- } catch {
- //
- }
- }
- }, []);
- // A ref simply depends on its readiness
- const refLoading = !!refs[refId] && !refs[refId].previewInitialized;
- // The root also might need to wait on webpack
- const isBuilding = !(progress?.value === 1 || progress === undefined);
- const rootLoading = !refId && (!previewInitialized || isBuilding);
- const isLoading = entry ? refLoading || rootLoading : rootLoading;
-
- return (
-
- {({ value: scale }) => {
- return (
- <>
- {withLoader && isLoading && (
-
-
-
- )}
-
- {customCanvas ? (
- customCanvas(storyId, viewMode, id, baseUrl, scale, queryParams)
- ) : (
-
- )}
-
- >
- );
- }}
-
- );
- }}
-
- );
- },
+ render: () => null,
});
-const useTabs = (
- id: PreviewProps['id'],
- baseUrl: PreviewProps['baseUrl'],
- withLoader: PreviewProps['withLoader'],
- getElements: API['getElements'],
- entry: PreviewProps['entry']
-) => {
- const canvas = useMemo(() => {
- return createCanvas(id, baseUrl, withLoader);
- }, [id, baseUrl, withLoader]);
-
- const tabsFromConfig = useMemo(() => {
- return getTabs(getElements);
- }, [getElements]);
+const useTabs = (getElements: API['getElements'], entry: PreviewProps['entry']) => {
+ const canvasTab = useMemo(() => createCanvasTab(), []);
+ const tabsFromConfig = useMemo(() => getTabs(getElements), [getElements]);
return useMemo(() => {
if (entry?.type === 'story' && entry.parameters) {
- return filterTabs([canvas, ...tabsFromConfig], entry.parameters);
+ return filterTabs([canvasTab, ...tabsFromConfig], entry.parameters);
}
- return [canvas, ...tabsFromConfig];
- }, [entry, canvas, ...tabsFromConfig]);
+ return [canvasTab, ...tabsFromConfig];
+ }, [entry, ...tabsFromConfig]);
};
const Preview = React.memo(function Preview(props) {
@@ -169,7 +72,7 @@ const Preview = React.memo(function Preview(props) {
} = props;
const { getElements } = api;
- const tabs = useTabs(previewId, baseUrl, withLoader, getElements, entry);
+ const tabs = useTabs(getElements, entry);
const shouldScale = viewMode === 'story';
const { showToolbar, showTabs = true } = options;
@@ -211,6 +114,7 @@ const Preview = React.memo(function Preview(props) {
tabs={visibleTabsInToolbar}
/>
+
{tabs.map(({ render: Render, match, ...t }, i) => {
// @ts-expect-error (Converted from ts-ignore)
const key = t.id || t.key || i;
@@ -228,7 +132,94 @@ const Preview = React.memo(function Preview(props) {
export { Preview };
-function filterTabs(panels: Addon[], parameters: Record) {
+const Canvas: FC<{ withLoader: boolean; baseUrl: string; children?: never }> = ({
+ baseUrl,
+ withLoader,
+}) => {
+ return (
+
+ {({
+ entry,
+ refs,
+ customCanvas,
+ storyId,
+ refId,
+ viewMode,
+ queryParams,
+ getElements,
+ previewInitialized,
+ active,
+ }) => {
+ const id = 'canvas';
+ const wrappers = useMemo(
+ () => [...defaultWrappers, ...getWrappers(getElements)],
+ [getElements, ...defaultWrappers]
+ );
+
+ const [progress, setProgress] = useState(undefined);
+ useEffect(() => {
+ if (FEATURES?.storyStoreV7 && global.CONFIG_TYPE === 'DEVELOPMENT') {
+ try {
+ const channel = addons.getServerChannel();
+
+ channel.on(PREVIEW_BUILDER_PROGRESS, (options) => {
+ setProgress(options);
+ });
+ } catch {
+ //
+ }
+ }
+ }, []);
+ // A ref simply depends on its readiness
+ const refLoading = !!refs[refId] && !refs[refId].previewInitialized;
+ // The root also might need to wait on webpack
+ const isBuilding = !(progress?.value === 1 || progress === undefined);
+ const rootLoading = !refId && (!previewInitialized || isBuilding);
+ const isLoading = entry ? refLoading || rootLoading : rootLoading;
+
+ return (
+
+ {({ value: scale }) => {
+ return (
+ <>
+ {withLoader && isLoading && (
+
+
+
+ )}
+
+ {customCanvas ? (
+ customCanvas(storyId, viewMode, id, baseUrl, scale, queryParams)
+ ) : (
+
+ )}
+
+ >
+ );
+ }}
+
+ );
+ }}
+
+ );
+};
+
+function filterTabs(panels: Addon_BaseType[], parameters: Record) {
const { previewTabs } = addons.getConfig();
const parametersTabs = parameters ? parameters.previewTabs : undefined;
@@ -245,7 +236,7 @@ function filterTabs(panels: Addon[], parameters: Record) {
const t = arrTabs.find((tab) => tab.id === panel.id);
return t === undefined || t.id === 'canvas' || !t.hidden;
})
- .map((panel, index) => ({ ...panel, index } as Addon))
+ .map((panel, index) => ({ ...panel, index } as Addon_BaseType))
.sort((p1, p2) => {
/* eslint-disable @typescript-eslint/naming-convention */
const tab_1 = arrTabs.find((tab) => tab.id === p1.id);
@@ -265,7 +256,7 @@ function filterTabs(panels: Addon[], parameters: Record) {
title: t.title || panel.title,
disabled: t.disabled,
hidden: t.hidden,
- } as Addon;
+ } as Addon_BaseType;
}
return panel;
});
diff --git a/code/ui/manager/src/components/preview/toolbar.tsx b/code/ui/manager/src/components/preview/toolbar.tsx
index 454ee5d1f4f0..b82e5cb2eb38 100644
--- a/code/ui/manager/src/components/preview/toolbar.tsx
+++ b/code/ui/manager/src/components/preview/toolbar.tsx
@@ -13,11 +13,11 @@ import {
merge,
type LeafEntry,
addons,
- type Addon,
types,
} from '@storybook/manager-api';
import { Location, type RenderData } from '@storybook/router';
+import type { Addon_BaseType } from '@storybook/types';
import { zoomTool } from './tools/zoom';
import * as S from './utils/components';
@@ -29,10 +29,8 @@ import { menuTool } from './tools/menu';
import { addonsTool } from './tools/addons';
import { remountTool } from './tools/remount';
-export const getTools = (getFn: API['getElements']) => Object.values(getFn(types.TOOL));
-
-export const getToolsExtra = (getFn: API['getElements']) =>
- Object.values(getFn(types.TOOLEXTRA));
+export const getTools = (getFn: API['getElements']) => Object.values(getFn(types.TOOL));
+export const getToolsExtra = (getFn: API['getElements']) => Object.values(getFn(types.TOOLEXTRA));
const Bar: FunctionComponent<{ shown: boolean } & Record> = ({ shown, ...props }) => (
@@ -59,9 +57,10 @@ const fullScreenMapper = ({ api, state }: Combo) => ({
singleStory: state.singleStory,
});
-export const fullScreenTool: Addon = {
+export const fullScreenTool: Addon_BaseType = {
title: 'fullscreen',
id: 'fullscreen',
+ type: types.TOOL,
match: (p) => ['story', 'docs'].includes(p.viewMode),
render: () => (
@@ -88,9 +87,10 @@ const tabsMapper = ({ state }: Combo) => ({
refId: state.refId,
});
-export const createTabsTool = (tabs: Addon[]): Addon => ({
+export const createTabsTool = (tabs: Addon_BaseType[]): Addon_BaseType => ({
title: 'title',
id: 'title',
+ type: types.TOOL,
render: () => (
{(rp) => (
@@ -117,12 +117,17 @@ export const createTabsTool = (tabs: Addon[]): Addon => ({
),
});
-export const defaultTools: Addon[] = [remountTool, zoomTool];
-export const defaultToolsExtra: Addon[] = [addonsTool, fullScreenTool, ejectTool, copyTool];
+export const defaultTools: Addon_BaseType[] = [remountTool, zoomTool];
+export const defaultToolsExtra: Addon_BaseType[] = [
+ addonsTool,
+ fullScreenTool,
+ ejectTool,
+ copyTool,
+];
const useTools = (
getElements: API['getElements'],
- tabs: Addon[],
+ tabs: Addon_BaseType[],
viewMode: PreviewProps['viewMode'],
entry: PreviewProps['entry'],
location: PreviewProps['location'],
@@ -154,7 +159,7 @@ const useTools = (
export interface ToolData {
isShown: boolean;
- tabs: Addon[];
+ tabs: Addon_BaseType[];
api: API;
entry: LeafEntry;
}
@@ -180,7 +185,7 @@ export const ToolbarComp = React.memo(function ToolbarComp(props) {
);
});
-export const Tools = React.memo<{ list: Addon[] }>(function Tools({ list }) {
+export const Tools = React.memo<{ list: Addon_BaseType[] }>(function Tools({ list }) {
return (
<>
{list.filter(Boolean).map(({ render: Render, id, ...t }, index) => (
@@ -191,7 +196,7 @@ export const Tools = React.memo<{ list: Addon[] }>(function Tools({ list }) {
);
});
-function toolbarItemHasBeenExcluded(item: Partial, entry: LeafEntry) {
+function toolbarItemHasBeenExcluded(item: Partial, entry: LeafEntry) {
const parameters = entry.type === 'story' && entry.prepared ? entry.parameters : {};
const toolbarItemsFromStoryParameters = 'toolbar' in parameters ? parameters.toolbar : undefined;
const { toolbar: toolbarItemsFromAddonsConfig } = addons.getConfig();
@@ -202,9 +207,9 @@ function toolbarItemHasBeenExcluded(item: Partial, entry: LeafEntry) {
}
export function filterTools(
- tools: Addon[],
- toolsExtra: Addon[],
- tabs: Addon[],
+ tools: Addon_BaseType[],
+ toolsExtra: Addon_BaseType[],
+ tabs: Addon_BaseType[],
{
viewMode,
entry,
@@ -224,7 +229,7 @@ export function filterTools(
];
const toolsRight = [...toolsExtra];
- const filter = (item: Partial) =>
+ const filter = (item: Partial) =>
item &&
(!item.match ||
item.match({
diff --git a/code/ui/manager/src/components/preview/tools/addons.tsx b/code/ui/manager/src/components/preview/tools/addons.tsx
index 9fc35f6de659..53fe821ae4af 100644
--- a/code/ui/manager/src/components/preview/tools/addons.tsx
+++ b/code/ui/manager/src/components/preview/tools/addons.tsx
@@ -1,7 +1,8 @@
import React from 'react';
import { IconButton, Icons } from '@storybook/components';
-import { Consumer } from '@storybook/manager-api';
-import type { Addon, Combo } from '@storybook/manager-api';
+import { Consumer, types } from '@storybook/manager-api';
+import type { Combo } from '@storybook/manager-api';
+import type { Addon_BaseType } from '@storybook/types';
const menuMapper = ({ api, state }: Combo) => ({
isVisible: state.layout.showPanel,
@@ -10,9 +11,10 @@ const menuMapper = ({ api, state }: Combo) => ({
toggle: () => api.togglePanel(),
});
-export const addonsTool: Addon = {
+export const addonsTool: Addon_BaseType = {
title: 'addons',
id: 'addons',
+ type: types.TOOL,
match: ({ viewMode }) => viewMode === 'story',
render: () => (
diff --git a/code/ui/manager/src/components/preview/tools/copy.tsx b/code/ui/manager/src/components/preview/tools/copy.tsx
index 857f8a82761e..0b0084aae857 100644
--- a/code/ui/manager/src/components/preview/tools/copy.tsx
+++ b/code/ui/manager/src/components/preview/tools/copy.tsx
@@ -2,8 +2,9 @@ import { global } from '@storybook/global';
import React from 'react';
import copy from 'copy-to-clipboard';
import { getStoryHref, IconButton, Icons } from '@storybook/components';
-import { Consumer } from '@storybook/manager-api';
-import type { Addon, Combo } from '@storybook/manager-api';
+import { Consumer, types } from '@storybook/manager-api';
+import type { Combo } from '@storybook/manager-api';
+import type { Addon_BaseType } from '@storybook/types';
const { PREVIEW_URL, document } = global;
@@ -22,9 +23,10 @@ const copyMapper = ({ state }: Combo) => {
};
};
-export const copyTool: Addon = {
+export const copyTool: Addon_BaseType = {
title: 'copy',
id: 'copy',
+ type: types.TOOL,
match: ({ viewMode }) => viewMode === 'story',
render: () => (
diff --git a/code/ui/manager/src/components/preview/tools/eject.tsx b/code/ui/manager/src/components/preview/tools/eject.tsx
index 6a08419f3077..65688038b859 100644
--- a/code/ui/manager/src/components/preview/tools/eject.tsx
+++ b/code/ui/manager/src/components/preview/tools/eject.tsx
@@ -1,8 +1,9 @@
import { global } from '@storybook/global';
import React from 'react';
import { getStoryHref, IconButton, Icons } from '@storybook/components';
-import { Consumer } from '@storybook/manager-api';
-import type { Addon, Combo } from '@storybook/manager-api';
+import { Consumer, types } from '@storybook/manager-api';
+import type { Combo } from '@storybook/manager-api';
+import type { Addon_BaseType } from '@storybook/types';
const { PREVIEW_URL } = global;
@@ -18,9 +19,10 @@ const ejectMapper = ({ state }: Combo) => {
};
};
-export const ejectTool: Addon = {
+export const ejectTool: Addon_BaseType = {
title: 'eject',
id: 'eject',
+ type: types.TOOL,
match: ({ viewMode }) => viewMode === 'story',
render: () => (
diff --git a/code/ui/manager/src/components/preview/tools/menu.tsx b/code/ui/manager/src/components/preview/tools/menu.tsx
index 6edb1366f77d..8512e9e16abc 100644
--- a/code/ui/manager/src/components/preview/tools/menu.tsx
+++ b/code/ui/manager/src/components/preview/tools/menu.tsx
@@ -1,7 +1,8 @@
import React from 'react';
import { IconButton, Icons, Separator } from '@storybook/components';
-import { Consumer } from '@storybook/manager-api';
-import type { Addon, Combo } from '@storybook/manager-api';
+import { Consumer, types } from '@storybook/manager-api';
+import type { Combo } from '@storybook/manager-api';
+import type { Addon_BaseType } from '@storybook/types';
const menuMapper = ({ api, state }: Combo) => ({
isVisible: state.layout.showNav,
@@ -9,9 +10,10 @@ const menuMapper = ({ api, state }: Combo) => ({
toggle: () => api.toggleNav(),
});
-export const menuTool: Addon = {
+export const menuTool: Addon_BaseType = {
title: 'menu',
id: 'menu',
+ type: types.TOOL,
match: ({ viewMode }) => ['story', 'docs'].includes(viewMode),
render: () => (
diff --git a/code/ui/manager/src/components/preview/tools/remount.tsx b/code/ui/manager/src/components/preview/tools/remount.tsx
index b095c6f431c1..6bf4c1f7553e 100644
--- a/code/ui/manager/src/components/preview/tools/remount.tsx
+++ b/code/ui/manager/src/components/preview/tools/remount.tsx
@@ -1,10 +1,11 @@
import type { ComponentProps } from 'react';
import React, { useState } from 'react';
import { IconButton, Icons } from '@storybook/components';
-import { Consumer } from '@storybook/manager-api';
-import type { Addon, Combo } from '@storybook/manager-api';
+import { Consumer, types } from '@storybook/manager-api';
+import type { Combo } from '@storybook/manager-api';
import { styled } from '@storybook/theming';
import { FORCE_REMOUNT } from '@storybook/core-events';
+import type { Addon_BaseType } from '@storybook/types';
interface AnimatedButtonProps {
animating?: boolean;
@@ -28,9 +29,10 @@ const menuMapper = ({ api, state }: Combo) => {
};
};
-export const remountTool: Addon = {
+export const remountTool: Addon_BaseType = {
title: 'remount',
id: 'remount',
+ type: types.TOOL,
match: ({ viewMode }) => viewMode === 'story',
render: () => (
diff --git a/code/ui/manager/src/components/preview/tools/zoom.tsx b/code/ui/manager/src/components/preview/tools/zoom.tsx
index fe6530b96d17..84e0f72bafae 100644
--- a/code/ui/manager/src/components/preview/tools/zoom.tsx
+++ b/code/ui/manager/src/components/preview/tools/zoom.tsx
@@ -2,7 +2,8 @@ import type { SyntheticEvent, MouseEventHandler } from 'react';
import React, { Component, useCallback } from 'react';
import { Icons, IconButton, Separator } from '@storybook/components';
-import type { Addon } from '@storybook/manager-api';
+import type { Addon_BaseType } from '@storybook/types';
+import { types } from '@storybook/manager-api';
const initialZoom = 1 as const;
@@ -78,9 +79,10 @@ const ZoomWrapper = React.memo<{ set: (zoomLevel: number) => void; value: number
}
);
-export const zoomTool: Addon = {
+export const zoomTool: Addon_BaseType = {
title: 'zoom',
id: 'zoom',
+ type: types.TOOL,
match: ({ viewMode }) => viewMode === 'story',
render: React.memo(function ZoomToolRenderer() {
return (
diff --git a/code/ui/manager/src/components/preview/utils/types.tsx b/code/ui/manager/src/components/preview/utils/types.tsx
index efe259cc61b1..9d8158514864 100644
--- a/code/ui/manager/src/components/preview/utils/types.tsx
+++ b/code/ui/manager/src/components/preview/utils/types.tsx
@@ -1,12 +1,10 @@
-import type { FunctionComponent, ReactNode } from 'react';
+import type { ReactElement } from 'react';
import type { State, API, LeafEntry } from '@storybook/manager-api';
-import type { StoryId } from '@storybook/types';
-
-export type ViewMode = State['viewMode'];
+import type { Addon_WrapperType, API_ViewMode, StoryId } from '@storybook/types';
export interface PreviewProps {
api: API;
- viewMode: ViewMode;
+ viewMode: API_ViewMode;
refs: State['refs'];
storyId: StoryId;
entry: LeafEntry;
@@ -25,20 +23,8 @@ export interface PreviewProps {
withLoader: boolean;
}
-export interface WrapperProps {
- index: number;
- children: ReactNode;
- id: string;
- storyId: StoryId;
- active: boolean;
-}
-
-export interface Wrapper {
- render: FunctionComponent;
-}
-
export interface ApplyWrappersProps {
- wrappers: Wrapper[];
+ wrappers: Addon_WrapperType[];
viewMode: State['viewMode'];
id: string;
storyId: StoryId;
@@ -52,7 +38,7 @@ export type CustomCanvasRenderer = (
baseUrl: string,
scale: number,
queryParams: Record
-) => ReactNode;
+) => ReactElement | null;
export interface FramesRendererProps {
entry: LeafEntry;
@@ -60,7 +46,7 @@ export interface FramesRendererProps {
refId: string;
baseUrl: string;
scale: number;
- viewMode: ViewMode;
+ viewMode: API_ViewMode;
queryParams: State['customQueryParams'];
refs: State['refs'];
}
diff --git a/code/ui/manager/src/components/preview/wrappers.tsx b/code/ui/manager/src/components/preview/wrappers.tsx
index 52da844cd01a..a88404e6d515 100644
--- a/code/ui/manager/src/components/preview/wrappers.tsx
+++ b/code/ui/manager/src/components/preview/wrappers.tsx
@@ -1,6 +1,8 @@
import type { FC } from 'react';
import React, { Fragment } from 'react';
-import type { ApplyWrappersProps, Wrapper } from './utils/types';
+import type { Addon_WrapperType } from '@storybook/types';
+import { Addon_TypesEnum } from '@storybook/types';
+import type { ApplyWrappersProps } from './utils/types';
import { IframeWrapper } from './utils/components';
export const ApplyWrappers: FC = ({
@@ -13,19 +15,23 @@ export const ApplyWrappers: FC = ({
return (
{wrappers.reduceRight(
- (acc, wrapper, index) => wrapper.render({ index, children: acc, id, storyId, active }),
+ (acc, wrapper, index) => (
+
+ ),
children
)}
);
};
-export const defaultWrappers = [
+export const defaultWrappers: Addon_WrapperType[] = [
{
+ id: 'iframe-wrapper',
+ type: Addon_TypesEnum.PREVIEW,
render: (p) => (
{p.children}
),
- } as Wrapper,
+ },
];
diff --git a/code/ui/manager/src/components/sidebar/RefIndicator.tsx b/code/ui/manager/src/components/sidebar/RefIndicator.tsx
index d9c81f6398c4..a46ba2c89642 100644
--- a/code/ui/manager/src/components/sidebar/RefIndicator.tsx
+++ b/code/ui/manager/src/components/sidebar/RefIndicator.tsx
@@ -10,7 +10,7 @@ import { useStorybookApi } from '@storybook/manager-api';
import type { RefType } from './types';
-import type { getStateType } from './utils';
+import type { getStateType } from '../../utils/tree';
const { document, window: globalWindow } = global;
diff --git a/code/ui/manager/src/components/sidebar/Refs.tsx b/code/ui/manager/src/components/sidebar/Refs.tsx
index b2c69b27f99c..a7696ee696ca 100644
--- a/code/ui/manager/src/components/sidebar/Refs.tsx
+++ b/code/ui/manager/src/components/sidebar/Refs.tsx
@@ -1,5 +1,6 @@
import type { FC, MutableRefObject } from 'react';
import React, { useMemo, useState, useRef, useCallback } from 'react';
+import type { State } from '@storybook/manager-api';
import { useStorybookApi, useStorybookState } from '@storybook/manager-api';
import { styled } from '@storybook/theming';
import { transparentize } from 'polished';
@@ -7,6 +8,7 @@ import { transparentize } from 'polished';
import { AuthBlock, ErrorBlock, LoaderBlock, EmptyBlock } from './RefBlocks';
import { RefIndicator } from './RefIndicator';
+
// eslint-disable-next-line import/no-cycle
import { Tree } from './Tree';
import { CollapseIcon } from './TreeNode';
@@ -14,7 +16,7 @@ import { CollapseIcon } from './TreeNode';
import { DEFAULT_REF_ID } from './Sidebar';
import type { Highlight, RefType } from './types';
-import { getStateType } from './utils';
+import { getStateType } from '../../utils/tree';
export interface RefProps {
isLoading: boolean;
@@ -95,7 +97,9 @@ const CollapseButton = styled.button(({ theme }) => ({
},
}));
-export const Ref: FC = React.memo(function Ref(props) {
+export const Ref: FC = React.memo(function Ref(
+ props
+) {
const { docsOptions } = useStorybookState();
const api = useStorybookApi();
const {
@@ -161,6 +165,7 @@ export const Ref: FC = React.memo(function Ref(props) {
{state === 'empty' && }
{state === 'ready' && (
{
- if (api) api.selectStory(id, undefined, { ref: refId !== DEFAULT_REF_ID && refId });
+ if (api) {
+ api.selectStory(id, undefined, { ref: refId !== DEFAULT_REF_ID && refId });
+ }
inputRef.current.blur();
showAllComponents(false);
},
@@ -177,9 +180,22 @@ export const Search = React.memo<{
);
const list: SearchItem[] = useMemo(() => {
- return dataset.entries.reduce((acc: SearchItem[], [refId, { index }]) => {
+ return dataset.entries.reduce((acc, [refId, { index, status }]) => {
+ const groupStatus = getGroupStatus(index || {}, status);
+
if (index) {
- acc.push(...Object.values(index).map((item) => searchItem(item, dataset.hash[refId])));
+ acc.push(
+ ...Object.values(index).map((item) => {
+ const statusValue =
+ status && status[item.id]
+ ? getHighestStatus(Object.values(status[item.id] || {}).map((s) => s.status))
+ : null;
+ return {
+ ...searchItem(item, dataset.hash[refId]),
+ status: statusValue || groupStatus[item.id] || null,
+ };
+ })
+ );
}
return acc;
}, []);
diff --git a/code/ui/manager/src/components/sidebar/SearchResults.stories.tsx b/code/ui/manager/src/components/sidebar/SearchResults.stories.tsx
index d2c72e0c721f..c92ef644cc48 100644
--- a/code/ui/manager/src/components/sidebar/SearchResults.stories.tsx
+++ b/code/ui/manager/src/components/sidebar/SearchResults.stories.tsx
@@ -4,7 +4,7 @@ import type { StoriesHash } from '@storybook/manager-api';
import { mockDataset } from './mockdata';
import { SearchResults } from './SearchResults';
import type { CombinedDataset, Refs, SearchItem } from './types';
-import { searchItem } from './utils';
+import { searchItem } from '../../utils/tree';
export default {
component: SearchResults,
diff --git a/code/ui/manager/src/components/sidebar/SearchResults.tsx b/code/ui/manager/src/components/sidebar/SearchResults.tsx
index 882d77a6f1f5..8cca81f84cb1 100644
--- a/code/ui/manager/src/components/sidebar/SearchResults.tsx
+++ b/code/ui/manager/src/components/sidebar/SearchResults.tsx
@@ -11,8 +11,9 @@ import { ComponentNode, DocumentNode, Path, RootNode, StoryNode } from './TreeNo
import type { Match, DownshiftItem, SearchResult } from './types';
import { isCloseType, isClearType, isExpandType } from './types';
// eslint-disable-next-line import/no-cycle
-import { getLink } from './utils';
+import { getLink } from '../../utils/tree';
import { matchesKeyCode, matchesModifiers } from '../../keybinding';
+import { statusMapping } from '../../utils/status';
const { document } = global;
@@ -25,14 +26,18 @@ const ResultsList = styled.ol({
});
const ResultRow = styled.li<{ isHighlighted: boolean }>(({ theme, isHighlighted }) => ({
- display: 'block',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'space-between',
margin: 0,
padding: 0,
+ paddingRight: 20,
background: isHighlighted ? theme.background.hoverable : 'transparent',
cursor: 'pointer',
'a:hover, button:hover': {
background: 'transparent',
},
+ gap: 10,
}));
const NoResults = styled.div(({ theme }) => ({
@@ -109,7 +114,11 @@ const Highlight: FC<{ match?: Match }> = React.memo(function Highlight({ childre
});
const Result: FC<
- SearchResult & { icon: string; isHighlighted: boolean; onClick: MouseEventHandler }
+ SearchResult & {
+ icon: string;
+ isHighlighted: boolean;
+ onClick: MouseEventHandler;
+ }
> = React.memo(function Result({ item, matches, icon, onClick, ...props }) {
const click: MouseEventHandler = useCallback(
(event) => {
@@ -163,7 +172,16 @@ const Result: FC<
node =