=
- withITwinIdOverride(
- withAccessTokenOverride((args) => {
- const [filter, setFilter] = React.useState("");
- const filterOrAddStartTile = React.useCallback(
- (iModels: IModelFull[], status?: DataStatus) => {
- if (status !== DataStatus.Complete) {
- return iModels;
- }
- const filterText = filter.toLocaleLowerCase().trim();
- if (filterText) {
- return iModels.filter((iModel) =>
- iModel.displayName?.toLocaleLowerCase().includes(filterText)
- );
- }
- iModels.unshift({
- id: "newiModel",
- displayName: "New iModel",
- description: "Click on this tile to create a new iModel",
- thumbnail:
- "https://unpkg.com/@bentley/icons-generic@1.0.34/icons/add.svg",
- });
- return iModels;
- },
- [filter]
- );
- return (
-
- Description
-
- Property postProcessCallback allows modification of the
- data that is sent to the grid, here, we either apply a filter, or
- add a new tile at the start of the list for a 'New iModel' when
- there is no filter defined.
-
- {
- const {
- target: { value },
- } = event;
- setFilter(value);
- }}
- />
-
-
- );
- })
+const WithPostProcessCallbackRender = (args: IModelGridProps) => {
+ const [filter, setFilter] = React.useState("");
+ const filterOrAddStartTile = React.useCallback(
+ (iModels: IModelFull[], status?: DataStatus) => {
+ if (status !== DataStatus.Complete) {
+ return iModels;
+ }
+ const filterText = filter.toLocaleLowerCase().trim();
+ if (filterText) {
+ return iModels.filter((iModel) =>
+ iModel.displayName?.toLocaleLowerCase().includes(filterText)
+ );
+ }
+ iModels.unshift({
+ id: "newiModel",
+ displayName: "New iModel",
+ description: "Click on this tile to create a new iModel",
+ thumbnail:
+ "https://unpkg.com/@bentley/icons-generic@1.0.34/icons/add.svg",
+ });
+ return iModels;
+ },
+ [filter]
);
-WithPostProcessCallback.args = {
- apiOverrides: { serverEnvironmentPrefix: "qa" },
-};
-
-export const DefaultNoStateComponentOverride = Template.bind({});
-DefaultNoStateComponentOverride.args = {
- apiOverrides: { serverEnvironmentPrefix: "qa" },
- emptyStateComponent: (
+ return (
- There are no iModels to show.
+ Description
+
+ Property postProcessCallback allows modification of the
+ data that is sent to the grid, here, we either apply a filter, or add a
+ new tile at the start of the list for a 'New iModel' when there is no
+ filter defined.
+
+ {
+ const {
+ target: { value },
+ } = event;
+ setFilter(value);
+ }}
+ />
+
- ),
+ );
};
-export const DisableAddToRecents = Template.bind({});
-DisableAddToRecents.args = {
- apiOverrides: { serverEnvironmentPrefix: "qa" },
- disableAddToRecents: true,
+export const WithPostProcessCallback: StoryObj = {
+ render: (args) => ,
+};
+
+export const DefaultNoStateComponentOverride: StoryObj = {
+ args: {
+ emptyStateComponent: (
+
+ There are no iModels to show.
+
+ ),
+ },
};
-DisableAddToRecents.argTypes = {
- accessToken: { table: { disable: true } },
- onThumbnailClick: { table: { disable: true } },
- sortOptions: { table: { disable: true } },
- iModelActions: { table: { disable: true } },
- useIndividualState: { table: { disable: true } },
- tileOverrides: { table: { disable: true } },
- stringsOverrides: { table: { disable: true } },
- apiOverrides: { table: { disable: true } },
- postProcessCallback: { table: { disable: true } },
- emptyStateComponent: { table: { disable: true } },
- searchText: { table: { disable: true } },
- viewMode: { table: { disable: true } },
- pageSize: { table: { disable: true } },
- maxCount: { table: { disable: true } },
- cellOverrides: { table: { disable: true } },
- className: { table: { disable: true } },
+
+export const DisableAddToRecents: StoryObj = {
+ args: {
+ disableAddToRecents: true,
+ },
+ argTypes: {
+ accessToken: { table: { disable: true } },
+ onThumbnailClick: { table: { disable: true } },
+ sortOptions: { table: { disable: true } },
+ iModelActions: { table: { disable: true } },
+ useIndividualState: { table: { disable: true } },
+ tileOverrides: { table: { disable: true } },
+ stringsOverrides: { table: { disable: true } },
+ apiOverrides: { table: { disable: true } },
+ postProcessCallback: { table: { disable: true } },
+ emptyStateComponent: { table: { disable: true } },
+ searchText: { table: { disable: true } },
+ viewMode: { table: { disable: true } },
+ pageSize: { table: { disable: true } },
+ maxCount: { table: { disable: true } },
+ cellOverrides: { table: { disable: true } },
+ className: { table: { disable: true } },
+ },
};
-export const Recents = Template.bind({});
-Recents.args = {
- apiOverrides: { serverEnvironmentPrefix: "qa" },
- requestType: "recents",
+export const Recents: StoryObj = {
+ args: {
+ requestType: "recents",
+ },
};
-export const RecentsWithCustomIcon = Template.bind({});
-RecentsWithCustomIcon.args = {
- apiOverrides: { serverEnvironmentPrefix: "qa" },
- requestType: "recents",
- removeFromRecentsIcon: ,
+export const RecentsWithCustomIcon: StoryObj = {
+ args: {
+ requestType: "recents",
+ removeFromRecentsIcon: ,
+ },
};
-export const RecentsWithCloseIcon = Template.bind({});
-RecentsWithCloseIcon.args = {
- apiOverrides: { serverEnvironmentPrefix: "qa" },
- requestType: "recents",
- removeFromRecentsIcon: ,
+export const RecentsWithCloseIcon: StoryObj = {
+ args: {
+ requestType: "recents",
+ removeFromRecentsIcon: ,
+ },
};
diff --git a/packages/apps/storybook/src/imodel-browser/IModelTile.stories.tsx b/packages/apps/storybook/src/imodel-browser/IModelTile.stories.tsx
index fdec087b..2a3a4868 100644
--- a/packages/apps/storybook/src/imodel-browser/IModelTile.stories.tsx
+++ b/packages/apps/storybook/src/imodel-browser/IModelTile.stories.tsx
@@ -5,7 +5,7 @@
import { IModelTile as C, IModelTileProps } from "@itwin/imodel-browser-react";
import { SvgImodel, SvgPlaceholder } from "@itwin/itwinui-icons-react";
import { Badge, Button } from "@itwin/itwinui-react";
-import { Meta, Story } from "@storybook/react/types-6-0";
+import type { Meta, StoryObj } from "@storybook/react-webpack5";
import React from "react";
export const IModelTile = (props: IModelTileProps) => ;
@@ -16,32 +16,31 @@ export default {
excludeStories: ["IModelTile"],
} as Meta;
-const Template: Story = (args) => ;
-
-export const Primary = Template.bind({});
-Primary.args = {
- iModel: {
- id: "1",
- displayName: "iModel Name",
- description: "iModel Description",
- },
- iModelOptions: [
- { key: "option-1", children: "Option 1" },
- { key: "option-2", children: "Option 2" },
- ],
- tileProps: {
- status: "positive",
- isDisabled: false,
- isLoading: false,
- isSelected: false,
- isNew: false,
- badge: Badge ,
- leftIcon: ,
- rightIcon: ,
- buttons: [
- Button 1 ,
- Button 2 ,
+export const Primary: StoryObj = {
+ args: {
+ iModel: {
+ id: "1",
+ displayName: "iModel Name",
+ description: "iModel Description",
+ },
+ iModelOptions: [
+ { key: "option-1", children: "Option 1" },
+ { key: "option-2", children: "Option 2" },
],
- thumbnail: ,
+ tileProps: {
+ status: "positive",
+ isDisabled: false,
+ isLoading: false,
+ isSelected: false,
+ isNew: false,
+ badge: Badge ,
+ leftIcon: ,
+ rightIcon: ,
+ buttons: [
+ Button 1 ,
+ Button 2 ,
+ ],
+ thumbnail: ,
+ },
},
};
diff --git a/packages/apps/storybook/src/imodel-browser/ITwinGrid.stories.tsx b/packages/apps/storybook/src/imodel-browser/ITwinGrid.stories.tsx
index 89ec6864..fbef3928 100644
--- a/packages/apps/storybook/src/imodel-browser/ITwinGrid.stories.tsx
+++ b/packages/apps/storybook/src/imodel-browser/ITwinGrid.stories.tsx
@@ -21,13 +21,10 @@ import {
Text,
Tile,
} from "@itwin/itwinui-react";
-import { Meta, Story } from "@storybook/react/types-6-0";
+import type { Meta, StoryObj } from "@storybook/react-webpack5";
import React, { PropsWithChildren } from "react";
-import {
- accessTokenArgTypes,
- withAccessTokenOverride,
-} from "../utils/storyHelp";
+import { accessTokenArgTypes } from "../utils/storyHelp";
type TileProps = React.ComponentPropsWithoutRef;
@@ -35,12 +32,11 @@ export const ITwinGrid = (props: ITwinGridProps) => (
);
-const accessToken = accessTokenArgTypes.accessToken;
export default {
title: "imodel-browser/ITwinGrid",
component: ITwinGrid,
argTypes: {
- accessToken,
+ ...accessTokenArgTypes,
requestType: {
options: ["all", "recents", "favorites"],
mapping: {
@@ -60,92 +56,91 @@ export default {
},
},
args: {
+ apiOverrides: { serverEnvironmentPrefix: "qa" },
requestType: "all",
},
+
excludeStories: ["ITwinGrid"],
} as Meta;
-const Template: Story = withAccessTokenOverride((args) => (
-
-));
-export const Primary = Template.bind({});
-Primary.args = {
- apiOverrides: { serverEnvironmentPrefix: "qa" },
+export const Primary: StoryObj = {};
+
+export const OverrideCellData: StoryObj = {
+ args: {
+ viewMode: "cells",
+ cellOverrides: {
+ ITwinNumber: (props) => (
+
+ {
+ e.stopPropagation();
+ console.log("Icon Clicked");
+ }}
+ >
+
+ {" "}
+ {props.value}
+
+ ),
+ ITwinName: (props) => {props.value} ,
+ hideColumns: [ITwinCellColumn.LastModified],
+ },
+ },
};
-export const OverrideCellData = Template.bind({});
-OverrideCellData.args = {
- apiOverrides: { serverEnvironmentPrefix: "qa" },
- viewMode: "cells",
- cellOverrides: {
- ITwinNumber: (props) => (
-
- {
- e.stopPropagation();
- console.log("Icon Clicked");
- }}
- >
-
- {" "}
- {props.value}
-
- ),
- ITwinName: (props) => {props.value} ,
- hideColumns: [ITwinCellColumn.LastModified],
+export const OverrideApiData: StoryObj = {
+ args: {
+ apiOverrides: {
+ data: [
+ {
+ id: "1",
+ displayName: "Provided iTwin",
+ number: "No Network Calls",
+ },
+ {
+ id: "2",
+ displayName: "Useful iTwin",
+ number:
+ "Use if the data comes from a different API or needs to be tweaked",
+ },
+ ],
+ },
},
};
-export const OverrideApiData = Template.bind({});
-OverrideApiData.args = {
- apiOverrides: {
- data: [
+export const IndividualContextMenu: StoryObj = {
+ args: {
+ iTwinActions: [
{
- id: "1",
- displayName: "Provided iTwin",
- number: "No Network Calls",
+ children: "displayName contains 'R'",
+ visible: (iTwin) => iTwin.displayName?.includes("R") ?? false,
+ key: "withR",
+ onClick: (iTwin) => alert("Contains R" + iTwin?.displayName),
},
{
- id: "2",
- displayName: "Useful iTwin",
- number:
- "Use if the data comes from a different API or needs to be tweaked",
+ children: "Add iTwinNumber",
+ visible: (iTwin) => !iTwin.number,
+ key: "addD",
+ onClick: (iTwin) => alert("Add iTwinNumber to " + iTwin?.displayName),
+ },
+ {
+ children: "Edit iTwinNumber",
+ visible: (iTwin) => !!iTwin.number,
+ key: "editD",
+ onClick: (iTwin) => alert("Edit iTwinNumber: " + iTwin?.number),
},
],
},
};
-export const IndividualContextMenu = Template.bind({});
-IndividualContextMenu.args = {
- apiOverrides: { serverEnvironmentPrefix: "qa" },
- iTwinActions: [
- {
- children: "displayName contains 'R'",
- visible: (iTwin) => iTwin.displayName?.includes("R") ?? false,
- key: "withR",
- onClick: (iTwin) => alert("Contains R" + iTwin?.displayName),
- },
- {
- children: "Add iTwinNumber",
- visible: (iTwin) => !iTwin.number,
- key: "addD",
- onClick: (iTwin) => alert("Add iTwinNumber to " + iTwin?.displayName),
- },
- {
- children: "Edit iTwinNumber",
- visible: (iTwin) => !!iTwin.number,
- key: "editD",
- onClick: (iTwin) => alert("Edit iTwinNumber: " + iTwin?.number),
+export const SimpleTilePropsOverrides: StoryObj = {
+ args: {
+ tileOverrides: {
+ tileProps: { style: { width: "100%" }, variant: "folder" },
},
- ],
-};
-
-export const SimpleTilePropsOverrides = Template.bind({});
-SimpleTilePropsOverrides.args = {
- apiOverrides: { serverEnvironmentPrefix: "qa" },
- tileOverrides: { tileProps: { style: { width: "100%" }, variant: "folder" } },
+ },
};
interface IModelMinimal {
@@ -278,7 +273,7 @@ const useIndividualState: IndividualITwinStateHook = (iTwin, props) => {
metadata: (
{
- imodels === undefined && fetchIModelList();
+ imodels === undefined && void fetchIModelList();
}}
>
{
};
};
-export const StatefulPropsOverrides = Template.bind({});
-StatefulPropsOverrides.args = {
- apiOverrides: { serverEnvironmentPrefix: "qa" },
- useIndividualState,
+export const StatefulPropsOverrides: StoryObj = {
+ args: {
+ useIndividualState,
+ },
};
-export const WithPostProcessCallback: Story =
- withAccessTokenOverride((args) => {
- const addStartTile = React.useCallback(
- (iTwins: ITwinFull[], status: DataStatus | undefined) => {
- if (status !== DataStatus.Complete) {
- return iTwins;
- }
- iTwins.unshift({
- id: "newProject",
- displayName: "New Project",
- number: "Click on this tile to create a new ITwin",
- });
+const WithPostProcessCallbackRender = (args: ITwinGridProps) => {
+ const addStartTile = React.useCallback(
+ (iTwins: ITwinFull[], status: DataStatus | undefined) => {
+ if (status !== DataStatus.Complete) {
return iTwins;
- },
- []
- );
- return (
-
- Description
-
- Property postProcessCallback allows modification of the
- data that is sent to the grid, here, we add a new tile at the start of
- the list for a 'New Project'.
-
-
-
- );
- });
-WithPostProcessCallback.args = {
- apiOverrides: { serverEnvironmentPrefix: "qa" },
+ }
+ iTwins.unshift({
+ id: "newProject",
+ displayName: "New Project",
+ number: "Click on this tile to create a new ITwin",
+ });
+ return iTwins;
+ },
+ []
+ );
+ return (
+
+ Description
+
+ Property postProcessCallback allows modification of the
+ data that is sent to the grid, here, we add a new tile at the start of
+ the list for a 'New Project'.
+
+
+
+ );
};
-export const FetchAllSubclasses = Template.bind({});
-FetchAllSubclasses.args = {
- apiOverrides: { serverEnvironmentPrefix: "qa" },
- iTwinSubClass: "All",
+export const WithPostProcessCallback: StoryObj = {
+ render: (args) => ,
+};
+
+export const FetchAllSubclasses: StoryObj = {
+ args: {
+ iTwinSubClass: "All",
+ },
};
diff --git a/packages/apps/storybook/src/imodel-browser/ITwinTile.stories.tsx b/packages/apps/storybook/src/imodel-browser/ITwinTile.stories.tsx
index 42965b88..ff44c825 100644
--- a/packages/apps/storybook/src/imodel-browser/ITwinTile.stories.tsx
+++ b/packages/apps/storybook/src/imodel-browser/ITwinTile.stories.tsx
@@ -3,7 +3,7 @@
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { ITwinTile as C, ITwinTileProps } from "@itwin/imodel-browser-react";
-import { Meta, Story } from "@storybook/react/types-6-0";
+import type { Meta, StoryObj } from "@storybook/react-webpack5";
import React from "react";
export const ITwinTile = (props: ITwinTileProps) => ;
@@ -14,12 +14,11 @@ export default {
excludeStories: ["ITwinTile"],
} as Meta;
-const Template: Story = (args) => ;
-
-export const Primary = Template.bind({});
-Primary.args = {
- iTwin: {
- id: "1",
- displayName: "iTwin Name",
+export const Primary: StoryObj = {
+ args: {
+ iTwin: {
+ id: "1",
+ displayName: "iTwin Name",
+ },
},
};
diff --git a/packages/apps/storybook/src/imodel-browser/NoResults.stories.tsx b/packages/apps/storybook/src/imodel-browser/NoResults.stories.tsx
index ea9a48de..32e9de63 100644
--- a/packages/apps/storybook/src/imodel-browser/NoResults.stories.tsx
+++ b/packages/apps/storybook/src/imodel-browser/NoResults.stories.tsx
@@ -6,7 +6,7 @@ import {
NoResults as ExternalComponent,
NoResultsProps,
} from "@itwin/imodel-browser-react";
-import { Meta, Story } from "@storybook/react/types-6-0";
+import type { Meta, StoryObj } from "@storybook/react-webpack5";
import React from "react";
export const NoResults = (props: NoResultsProps) => (
@@ -19,7 +19,4 @@ export default {
excludeStories: ["NoResults"],
} as Meta;
-const Template: Story = (args) => ;
-
-export const Primary = Template.bind({});
-Primary.args = {};
+export const Primary: StoryObj = {};
diff --git a/packages/apps/storybook/src/imodel-browser/mui/BaseCard.stories.tsx b/packages/apps/storybook/src/imodel-browser/mui/BaseCard.stories.tsx
index 17983ca1..5207a482 100644
--- a/packages/apps/storybook/src/imodel-browser/mui/BaseCard.stories.tsx
+++ b/packages/apps/storybook/src/imodel-browser/mui/BaseCard.stories.tsx
@@ -7,8 +7,8 @@ import Avatar from "@mui/material/Avatar";
import AvatarGroup from "@mui/material/AvatarGroup";
import Box from "@mui/material/Box";
import Chip from "@mui/material/Chip";
-import { action } from "@storybook/addon-actions";
-import { Meta, Story } from "@storybook/react/types-6-0";
+import { action } from "storybook/actions";
+import type { Meta, StoryObj } from "@storybook/react-webpack5";
import svgGeo from "@stratakit/icons/geospatial-features.svg";
import svgPin from "@stratakit/icons/pin.svg";
import svgSave from "@stratakit/icons/save.svg";
@@ -65,8 +65,6 @@ export default {
},
} as Meta;
-const Template: Story = (args) => ;
-
const baseArgs: BaseCardProps = {
title: "Main Street Bridge",
description: "3D model of the Main Street bridge structure and components.",
@@ -79,8 +77,9 @@ const baseArgs: BaseCardProps = {
// ── Stories ──────────────────────────────────────────────────────────────────
-export const Default = Template.bind({});
-Default.args = { ...baseArgs };
+export const Default: StoryObj = {
+ args: { ...baseArgs },
+};
const everythingArgs: BaseCardProps = {
...baseArgs,
@@ -146,133 +145,143 @@ const everythingArgs: BaseCardProps = {
thumbnailBottomRight: ,
};
-export const Everything = Template.bind({});
-Everything.storyName = "Everything";
-Everything.args = { ...everythingArgs };
-
-export const WithoutThumbnail = Template.bind({});
-WithoutThumbnail.storyName = "Without thumbnail";
-WithoutThumbnail.args = { ...everythingArgs, thumbnail: undefined };
+export const Everything: StoryObj = {
+ name: "Everything",
+ args: { ...everythingArgs },
+};
-export const WithDarkThumbnail = Template.bind({});
-WithDarkThumbnail.storyName = "With dark thumbnail";
-WithDarkThumbnail.args = {
- ...everythingArgs,
- thumbnail: nightThumbnail,
+export const WithoutThumbnail: StoryObj = {
+ name: "Without thumbnail",
+ args: { ...everythingArgs, thumbnail: undefined },
};
-export const WithoutContent = Template.bind({});
-WithoutContent.storyName = "Without content";
-WithoutContent.args = {
- ...everythingArgs,
- title: "Main Street Bridge",
- description: undefined,
- subheader: undefined,
- thumbnail: bridgeThumbnail,
+export const WithDarkThumbnail: StoryObj = {
+ name: "With dark thumbnail",
+ args: {
+ ...everythingArgs,
+ thumbnail: nightThumbnail,
+ },
};
-export const Statuses = () => (
-
- {(
- [
- { label: "Positive", icon: svgStatusSuccess, status: "positive" },
- { label: "Warning", icon: svgStatusWarning, status: "warning" },
- { label: "Negative", icon: svgStatusError, status: "negative" },
- ] as const
- ).map(({ label, icon, status }) => (
-
- ))}
-
-);
+export const WithoutContent: StoryObj = {
+ name: "Without content",
+ args: {
+ ...everythingArgs,
+ title: "Main Street Bridge",
+ description: undefined,
+ subheader: undefined,
+ thumbnail: bridgeThumbnail,
+ },
+};
-export const WithSvgThumbnail = Template.bind({});
-WithSvgThumbnail.storyName = "With SVG thumbnail";
-WithSvgThumbnail.args = {
- ...baseArgs,
- thumbnail: ,
+export const Statuses: StoryObj = {
+ render: () => (
+
+ {(
+ [
+ { label: "Positive", icon: svgStatusSuccess, status: "positive" },
+ { label: "Warning", icon: svgStatusWarning, status: "warning" },
+ { label: "Negative", icon: svgStatusError, status: "negative" },
+ ] as const
+ ).map(({ label, icon, status }) => (
+
+ ))}
+
+ ),
};
-export const Loading = Template.bind({});
-Loading.args = { ...baseArgs, loading: true };
+export const WithSvgThumbnail: StoryObj = {
+ name: "With SVG thumbnail",
+ args: {
+ ...baseArgs,
+ thumbnail: ,
+ },
+};
-export const Disabled = Template.bind({});
-Disabled.storyName = "Disabled state";
-Disabled.args = { ...baseArgs, disabled: true };
+export const Loading: StoryObj = {
+ args: { ...baseArgs, loading: true },
+};
-export const LongTitle = Template.bind({});
-LongTitle.storyName = "Long title";
-LongTitle.args = {
- ...baseArgs,
- title:
- "This is a very long title that should truncate automagically with an ellipsis at the end",
- moreActions: [
- {
- key: "open",
- label: "Open with",
- onClick: action("menu: open clicked"),
- icon: svgGeo,
- },
- ],
+export const Disabled: StoryObj = {
+ name: "Disabled state",
+ args: { ...baseArgs, disabled: true },
};
-export const OpenSitePlus = Template.bind({});
-OpenSitePlus.storyName = "OpenSite+ iModel style";
-OpenSitePlus.args = {
- ...baseArgs,
- title: "OpenSite+ iModel",
- subheader: "Modified 2026-01-01",
- description:
- "This card mimics the design of OpenSite+ iModel cards with an AvatarGroup in the top left, a pin/favorite button in the top right, and no dedicated action buttons below the description.",
- onContextMenu: action("context-menu opened"),
- moreActions: [
- {
- key: "open",
- label: "Open with",
- onClick: action("menu: open with clicked"),
- icon: svgGeo,
- },
- { key: "share", label: "Share", onClick: action("menu: share clicked") },
- { key: "delete", label: "Delete", onClick: action("menu: delete clicked") },
- ],
- actions: [
- {
- key: "open",
- label: "Open",
- onClick: action("default open action clicked"),
- },
- ],
- statusIconHref: undefined,
- thumbnailTopLeft: (
-
-
-
-
-
-
-
- ),
+export const LongTitle: StoryObj = {
+ name: "Long title",
+ args: {
+ ...baseArgs,
+ title:
+ "This is a very long title that should truncate automagically with an ellipsis at the end",
+ moreActions: [
+ {
+ key: "open",
+ label: "Open with",
+ onClick: action("menu: open clicked"),
+ icon: svgGeo,
+ },
+ ],
+ },
+};
- thumbnailTopRight: (
- <>
-
- >
- ),
+export const OpenSitePlus: StoryObj = {
+ name: "OpenSite+ iModel style",
+ args: {
+ ...baseArgs,
+ title: "OpenSite+ iModel",
+ subheader: "Modified 2026-01-01",
+ description:
+ "This card mimics the design of OpenSite+ iModel cards with an AvatarGroup in the top left, a pin/favorite button in the top right, and no dedicated action buttons below the description.",
+ onContextMenu: action("context-menu opened"),
+ moreActions: [
+ {
+ key: "open",
+ label: "Open with",
+ onClick: action("menu: open with clicked"),
+ icon: svgGeo,
+ },
+ { key: "share", label: "Share", onClick: action("menu: share clicked") },
+ { key: "delete", label: "Delete", onClick: action("menu: delete clicked") },
+ ],
+ actions: [
+ {
+ key: "open",
+ label: "Open",
+ onClick: action("default open action clicked"),
+ },
+ ],
+ statusIconHref: undefined,
+ thumbnailTopLeft: (
+
+
+
+
+
+
+
+ ),
+ thumbnailTopRight: (
+ <>
+
+ >
+ ),
+ },
};
diff --git a/packages/apps/storybook/src/imodel-browser/mui/IModelGridMUI.stories.tsx b/packages/apps/storybook/src/imodel-browser/mui/IModelGridMUI.stories.tsx
index ff857376..7596d7bf 100644
--- a/packages/apps/storybook/src/imodel-browser/mui/IModelGridMUI.stories.tsx
+++ b/packages/apps/storybook/src/imodel-browser/mui/IModelGridMUI.stories.tsx
@@ -13,18 +13,14 @@ import Avatar from "@mui/material/Avatar";
import AvatarGroup from "@mui/material/AvatarGroup";
import Chip from "@mui/material/Chip";
import Typography from "@mui/material/Typography";
-import { action } from "@storybook/addon-actions";
-import { Meta, Story } from "@storybook/react/types-6-0";
+import type { Meta, StoryObj } from "@storybook/react-webpack5";
import SvgDelete from "@stratakit/icons/delete.svg";
import React from "react";
+import { action } from "storybook/actions";
import bridgeThumbnail from "../../utils/bridge.jpg";
import nightThumbnail from "../../utils/night.jpg";
-import {
- accessTokenArgTypes,
- withAccessTokenOverride,
- withITwinIdOverride,
-} from "../../utils/storyHelp";
+import { accessTokenArgTypes } from "../../utils/storyHelp";
import {
additionalData,
initialData,
@@ -71,10 +67,6 @@ export default {
excludeStories: ["IModelGridMUI"],
} as Meta;
-const Template: Story = withITwinIdOverride(
- withAccessTokenOverride((args) => )
-);
-
const baseArgs: IModelGridMUIProps = {
apiOverrides: { serverEnvironmentPrefix: "qa" },
sortOptions: { sortType: "name", descending: false },
@@ -101,172 +93,180 @@ const baseArgs: IModelGridMUIProps = {
],
};
-export const Primary = Template.bind({});
-Primary.args = { ...baseArgs };
+export const Primary: StoryObj = {
+ args: { ...baseArgs },
+};
-export const TableView = Template.bind({});
-TableView.args = {
- ...baseArgs,
- viewMode: "cells",
+export const TableView: StoryObj = {
+ args: {
+ ...baseArgs,
+ viewMode: "cells",
+ },
};
-export const TableViewWithOverrides = Template.bind({});
-TableViewWithOverrides.args = {
- ...baseArgs,
- actions: [
- {
- key: "open",
- label: (iModel) => iModel.displayName ?? "",
- onClick: (iModel) => action("iModel opened")(iModel),
- disabled: (iModel) =>
- iModel.displayName?.toLowerCase().includes("t") ?? false,
- },
- ],
- viewMode: "cells",
- tableOverrides: {
- columnOverrides: {
- [IModelCellColumn.Name]: {
- renderCell: (params) =>
- params.formattedValue?.includes("*") ? (
-
- {params.formattedValue}{" "}
-
- (redacted number in name)
-
-
- ) : (
-
- {params.formattedValue}{" "}
- (no redactions)
-
+export const TableViewWithOverrides: StoryObj = {
+ args: {
+ ...baseArgs,
+ actions: [
+ {
+ key: "open",
+ label: (iModel) => iModel.displayName ?? "",
+ onClick: (iModel) => action("iModel opened")(iModel),
+ disabled: (iModel) =>
+ iModel.displayName?.toLowerCase().includes("t") ?? false,
+ },
+ ],
+ viewMode: "cells",
+ tableOverrides: {
+ columnOverrides: {
+ [IModelCellColumn.Name]: {
+ renderCell: (params) =>
+ params.formattedValue?.includes("*") ? (
+
+ {params.formattedValue}{" "}
+
+ (redacted number in name)
+
+
+ ) : (
+
+ {params.formattedValue}{" "}
+ (no redactions)
+
+ ),
+ valueFormatter: (value, iModel) => {
+ // replace any numbers with *
+ return iModel.displayName?.replace(/[0-9]/g, "*");
+ },
+ },
+ [IModelCellColumn.Description]: {
+ renderCell: (params) => (
+
+ Add random number {Math.floor(Math.random() * 100)} to description
+ "{params.value}"
+
),
- valueFormatter: (value, iModel) => {
- // replace any numbers with *
- return iModel.displayName?.replace(/[0-9]/g, "*");
},
},
- [IModelCellColumn.Description]: {
- renderCell: (params) => (
-
- Add random number {Math.floor(Math.random() * 100)} to description
- "{params.value}"
-
- ),
- },
+ hideColumns: [IModelCellColumn.LastModified],
},
- hideColumns: [IModelCellColumn.LastModified],
},
};
-export const OverrideApiDataWithLoadMore: Story =
- withITwinIdOverride(
- withAccessTokenOverride((args) => {
- const [data, setData] = React.useState(initialData);
- const [isLoading, setIsLoading] = React.useState(false);
- const [hasMore, setHasMore] = React.useState(true);
+const OverrideApiDataWithLoadMoreRender = (args: IModelGridMUIProps) => {
+ const [data, setData] = React.useState(initialData);
+ const [isLoading, setIsLoading] = React.useState(false);
+ const [hasMore, setHasMore] = React.useState(true);
- const handleLoadMore = React.useCallback(async () => {
- setIsLoading(true);
- // Simulate network delay
- await new Promise((resolve) => setTimeout(resolve, 2000));
- setData((prev) => [...prev, ...additionalData]);
- setHasMore(false);
- setIsLoading(false);
- }, []);
+ const handleLoadMore = React.useCallback(async () => {
+ setIsLoading(true);
+ // Simulate network delay
+ await new Promise((resolve) => setTimeout(resolve, 2000));
+ setData((prev) => [...prev, ...additionalData]);
+ setHasMore(false);
+ setIsLoading(false);
+ }, []);
- return (
- iModel.displayName ?? "",
- onClick: (iModel) => action("open action clicked")(iModel),
- },
- {
- key: "somethingElse",
- label: "Something else",
- onClick: (iModel) =>
- action("something else clicked " + iModel?.displayName)(iModel),
- },
- ]}
- dataMode="external"
- apiOverrides={{
- data,
- isLoading,
- hasMoreData: hasMore,
- }}
- onLoadMore={handleLoadMore}
- />
- );
- })
+ return (
+ iModel.displayName ?? "",
+ onClick: (iModel) => action("open action clicked")(iModel),
+ },
+ {
+ key: "somethingElse",
+ label: "Something else",
+ onClick: (iModel) =>
+ action("something else clicked " + iModel?.displayName)(iModel),
+ },
+ ]}
+ dataMode="external"
+ apiOverrides={{
+ data,
+ isLoading,
+ hasMoreData: hasMore,
+ }}
+ onLoadMore={handleLoadMore}
+ />
);
+};
-export const ContextualActions = Template.bind({});
-ContextualActions.args = {
- ...baseArgs,
- actions: [
- {
- key: "withR",
- label: "displayName contains 'R'",
- visible: (iModel) =>
- iModel.displayName?.toLowerCase().includes("r") ?? false,
- },
- ],
- moreActions: [
- {
- label: "displayName contains 'R'",
- visible: (iModel: IModelFull) =>
- iModel.displayName?.toLowerCase().includes("r") ?? false,
- key: "withR",
- onClick: (iModel: IModelFull | undefined) =>
- action("Contains R: " + iModel?.displayName)(iModel),
- },
- {
- label: "Disabled if name contains 'T'",
- disabled: (iModel: IModelFull) =>
- iModel.displayName?.toLowerCase().includes("t") ?? false,
- key: "withT",
- onClick: (iModel: IModelFull | undefined) =>
- action("Does not contain T: " + iModel?.displayName)(iModel),
- },
- {
- label: "Add description",
- key: "addD",
- onClick: (iModel: IModelFull | undefined) =>
- action("Add description: " + iModel?.displayName)(iModel),
- },
- {
- label: "Edit description",
- visible: (iModel: IModelFull) => !!iModel.description,
- key: "editD",
- onClick: (iModel: IModelFull | undefined) =>
- action("Edit description: " + iModel?.displayName)(iModel),
- },
- ],
+export const OverrideApiDataWithLoadMore: StoryObj = {
+ render: (args) => ,
+ args: { ...baseArgs },
};
-export const SimpleTilePropsOverrides = Template.bind({});
-SimpleTilePropsOverrides.args = {
- ...baseArgs,
- tileOverrides: {
- thumbnail: bridgeThumbnail,
- thumbnailBottomRight: ,
- thumbnailBottomLeft: ,
- thumbnailTopLeft: (
-
-
-
-
-
- ),
+export const ContextualActions: StoryObj = {
+ args: {
+ ...baseArgs,
+ actions: [
+ {
+ key: "withR",
+ label: "displayName contains 'R'",
+ visible: (iModel) =>
+ iModel.displayName?.toLowerCase().includes("r") ?? false,
+ },
+ ],
+ moreActions: [
+ {
+ label: "displayName contains 'R'",
+ visible: (iModel: IModelFull) =>
+ iModel.displayName?.toLowerCase().includes("r") ?? false,
+ key: "withR",
+ onClick: (iModel: IModelFull | undefined) =>
+ action("Contains R: " + iModel?.displayName)(iModel),
+ },
+ {
+ label: "Disabled if name contains 'T'",
+ disabled: (iModel: IModelFull) =>
+ iModel.displayName?.toLowerCase().includes("t") ?? false,
+ key: "withT",
+ onClick: (iModel: IModelFull | undefined) =>
+ action("Does not contain T: " + iModel?.displayName)(iModel),
+ },
+ {
+ label: "Add description",
+ key: "addD",
+ onClick: (iModel: IModelFull | undefined) =>
+ action("Add description: " + iModel?.displayName)(iModel),
+ },
+ {
+ label: "Edit description",
+ visible: (iModel: IModelFull) => !!iModel.description,
+ key: "editD",
+ onClick: (iModel: IModelFull | undefined) =>
+ action("Edit description: " + iModel?.displayName)(iModel),
+ },
+ ],
},
};
-export const StatefulPropsOverrides = Template.bind({});
-StatefulPropsOverrides.args = {
- ...baseArgs,
- useIndividualState,
+export const SimpleTilePropsOverrides: StoryObj = {
+ args: {
+ ...baseArgs,
+ tileOverrides: {
+ thumbnail: bridgeThumbnail,
+ thumbnailBottomRight: ,
+ thumbnailBottomLeft: ,
+ thumbnailTopLeft: (
+
+
+
+
+
+ ),
+ },
+ },
+};
+
+export const StatefulPropsOverrides: StoryObj = {
+ args: {
+ ...baseArgs,
+ useIndividualState,
+ },
};
function addStartTileCallback(iModels: IModelFull[], status?: DataStatus) {
@@ -283,127 +283,134 @@ function addStartTileCallback(iModels: IModelFull[], status?: DataStatus) {
return iModels;
}
-export const WithPostProcessCallback = Template.bind({});
-WithPostProcessCallback.args = {
- ...baseArgs,
- postProcessCallback: addStartTileCallback,
+export const WithPostProcessCallback: StoryObj = {
+ args: {
+ ...baseArgs,
+ postProcessCallback: addStartTileCallback,
+ },
};
-export const DefaultNoStateComponentOverride = Template.bind({});
-DefaultNoStateComponentOverride.args = {
- ...baseArgs,
- emptyStateComponent: (
-
- {/* eslint-disable-next-line jsx-a11y/heading-has-content */}
- }>
- There are no iModels to show.
-
-
- ),
+export const DefaultNoStateComponentOverride: StoryObj = {
+ args: {
+ ...baseArgs,
+ emptyStateComponent: (
+
+ {/* eslint-disable-next-line jsx-a11y/heading-has-content */}
+ }>
+ There are no iModels to show.
+
+
+ ),
+ },
};
-export const DisableAddToRecents = Template.bind({});
-DisableAddToRecents.args = {
- ...baseArgs,
- disableAddToRecents: true,
-};
-DisableAddToRecents.argTypes = {
- accessToken: { table: { disable: true } },
- actions: { table: { disable: true } },
- sortOptions: { table: { disable: true } },
- moreActions: { table: { disable: true } },
- useIndividualState: { table: { disable: true } },
- tileOverrides: { table: { disable: true } },
- stringsOverrides: { table: { disable: true } },
- apiOverrides: { table: { disable: true } },
- postProcessCallback: { table: { disable: true } },
- emptyStateComponent: { table: { disable: true } },
- searchText: { table: { disable: true } },
- viewMode: { table: { disable: true } },
- pageSize: { table: { disable: true } },
- maxCount: { table: { disable: true } },
- tableOverrides: { table: { disable: true } },
- className: { table: { disable: true } },
+export const DisableAddToRecents: StoryObj = {
+ args: {
+ ...baseArgs,
+ disableAddToRecents: true,
+ },
+ argTypes: {
+ accessToken: { table: { disable: true } },
+ actions: { table: { disable: true } },
+ sortOptions: { table: { disable: true } },
+ moreActions: { table: { disable: true } },
+ useIndividualState: { table: { disable: true } },
+ tileOverrides: { table: { disable: true } },
+ stringsOverrides: { table: { disable: true } },
+ apiOverrides: { table: { disable: true } },
+ postProcessCallback: { table: { disable: true } },
+ emptyStateComponent: { table: { disable: true } },
+ searchText: { table: { disable: true } },
+ viewMode: { table: { disable: true } },
+ pageSize: { table: { disable: true } },
+ maxCount: { table: { disable: true } },
+ tableOverrides: { table: { disable: true } },
+ className: { table: { disable: true } },
+ },
};
-export const Recents = Template.bind({});
-Recents.args = {
- ...baseArgs,
- requestType: "recents",
+export const Recents: StoryObj = {
+ args: {
+ ...baseArgs,
+ requestType: "recents",
+ },
};
-export const RecentsWithCustomIcon = Template.bind({});
-RecentsWithCustomIcon.args = {
- ...baseArgs,
- requestType: "recents",
- removeFromRecentsIcon: SvgDelete,
+export const RecentsWithCustomIcon: StoryObj = {
+ args: {
+ ...baseArgs,
+ requestType: "recents",
+ removeFromRecentsIcon: SvgDelete,
+ },
};
-export const NoResultsWithDefaultEmptyState = Template.bind({});
-NoResultsWithDefaultEmptyState.args = {
- ...baseArgs,
- apiOverrides: { serverEnvironmentPrefix: "qa" },
- postProcessCallback: (iModels, status) => {
- return [];
+export const NoResultsWithDefaultEmptyState: StoryObj = {
+ args: {
+ ...baseArgs,
+ apiOverrides: { serverEnvironmentPrefix: "qa" },
+ postProcessCallback: (iModels, status) => {
+ return [];
+ },
},
};
-export const StringsOverrideGrid = Template.bind({});
-StringsOverrideGrid.args = {
- ...baseArgs,
- moreActions: [
- {
- label: "Some action",
- key: "something",
- onClick: (iModel) => action("clicked " + iModel?.displayName)(iModel),
+export const StringsOverrideGrid: StoryObj = {
+ args: {
+ ...baseArgs,
+ moreActions: [
+ {
+ label: "Some action",
+ key: "something",
+ onClick: (iModel) => action("clicked " + iModel?.displayName)(iModel),
+ },
+ ],
+ stringsOverrides: {
+ moreOptions: "Fleiri valkostir",
+ addToFavorites: "Bæta við eftirlæti",
+ removeFromFavorites: "Fjarlægja úr eftirlætum",
+ tableColumnName: "Heiti iModel",
+ tableColumnDescription: "Lýsing iModel",
+ tableColumnLastModified: "Síðast breytt",
+ noRowsLabel: "Engar raðir",
+ noResultsOverlayLabel: "Engar niðurstöður fundust.",
+ paginationRowsPerPage: "Raðir á síðu:",
+ footerRowSelected: (count: number) =>
+ count !== 1
+ ? `${count.toLocaleString()} raðir valdar`
+ : `${count.toLocaleString()} röð valin`,
+ footerTotalVisibleRows: (visibleCount: number, totalCount: number) =>
+ `${visibleCount.toLocaleString()} af ${totalCount.toLocaleString()}`,
},
- ],
- stringsOverrides: {
- moreOptions: "Fleiri valkostir",
- addToFavorites: "Bæta við eftirlæti",
- removeFromFavorites: "Fjarlægja úr eftirlætum",
- tableColumnName: "Heiti iModel",
- tableColumnDescription: "Lýsing iModel",
- tableColumnLastModified: "Síðast breytt",
- noRowsLabel: "Engar raðir",
- noResultsOverlayLabel: "Engar niðurstöður fundust.",
- paginationRowsPerPage: "Raðir á síðu:",
- footerRowSelected: (count: number) =>
- count !== 1
- ? `${count.toLocaleString()} raðir valdar`
- : `${count.toLocaleString()} röð valin`,
- footerTotalVisibleRows: (visibleCount: number, totalCount: number) =>
- `${visibleCount.toLocaleString()} af ${totalCount.toLocaleString()}`,
},
};
-export const StringsOverrideTable = Template.bind({});
-StringsOverrideTable.args = {
- ...baseArgs,
- viewMode: "cells",
-
- moreActions: [
- {
- label: "Some action",
- key: "something",
- onClick: (iModel) => action("clicked " + iModel?.displayName)(iModel),
+export const StringsOverrideTable: StoryObj = {
+ args: {
+ ...baseArgs,
+ viewMode: "cells",
+ moreActions: [
+ {
+ label: "Some action",
+ key: "something",
+ onClick: (iModel) => action("clicked " + iModel?.displayName)(iModel),
+ },
+ ],
+ stringsOverrides: {
+ moreOptions: "Fleiri valkostir",
+ addToFavorites: "Bæta við eftirlæti",
+ removeFromFavorites: "Fjarlægja úr eftirlætum",
+ tableColumnName: "Heiti iModel",
+ tableColumnDescription: "Lýsing iModel",
+ tableColumnLastModified: "Síðast breytt",
+ noRowsLabel: "Engar raðir",
+ noResultsOverlayLabel: "Engar niðurstöður fundust.",
+ paginationRowsPerPage: "Raðir á síðu:",
+ footerRowSelected: (count: number) =>
+ count !== 1
+ ? `${count.toLocaleString()} raðir valdar`
+ : `${count.toLocaleString()} röð valin`,
+ footerTotalVisibleRows: (visibleCount: number, totalCount: number) =>
+ `${visibleCount.toLocaleString()} af ${totalCount.toLocaleString()}`,
},
- ],
- stringsOverrides: {
- moreOptions: "Fleiri valkostir",
- addToFavorites: "Bæta við eftirlæti",
- removeFromFavorites: "Fjarlægja úr eftirlætum",
- tableColumnName: "Heiti iModel",
- tableColumnDescription: "Lýsing iModel",
- tableColumnLastModified: "Síðast breytt",
- noRowsLabel: "Engar raðir",
- noResultsOverlayLabel: "Engar niðurstöður fundust.",
- paginationRowsPerPage: "Raðir á síðu:",
- footerRowSelected: (count: number) =>
- count !== 1
- ? `${count.toLocaleString()} raðir valdar`
- : `${count.toLocaleString()} röð valin`,
- footerTotalVisibleRows: (visibleCount: number, totalCount: number) =>
- `${visibleCount.toLocaleString()} af ${totalCount.toLocaleString()}`,
},
};
diff --git a/packages/apps/storybook/src/imodel-browser/mui/IModelTileMUI.stories.tsx b/packages/apps/storybook/src/imodel-browser/mui/IModelTileMUI.stories.tsx
index 1d36775a..bb8bedd5 100644
--- a/packages/apps/storybook/src/imodel-browser/mui/IModelTileMUI.stories.tsx
+++ b/packages/apps/storybook/src/imodel-browser/mui/IModelTileMUI.stories.tsx
@@ -11,8 +11,8 @@ import Avatar from "@mui/material/Avatar";
import AvatarGroup from "@mui/material/AvatarGroup";
import Box from "@mui/material/Box";
import Chip from "@mui/material/Chip";
-import { action } from "@storybook/addon-actions";
-import { Meta, Story } from "@storybook/react/types-6-0";
+import { action } from "storybook/actions";
+import type { Meta, StoryObj } from "@storybook/react-webpack5";
import SvgDelete from "@stratakit/icons/delete.svg";
import svgRoad from "@stratakit/icons/road.svg";
import SvgShare from "@stratakit/icons/share.svg";
@@ -61,10 +61,6 @@ export default {
},
} as Meta;
-const Template: Story = (args) => (
-
-);
-
const baseArgs: IModelTileMUIProps = {
iModel: {
id: "1",
@@ -99,48 +95,52 @@ const baseArgs: IModelTileMUIProps = {
],
};
-export const Default = Template.bind({});
-Default.args = {
- ...baseArgs,
+export const Default: StoryObj = {
+ args: {
+ ...baseArgs,
+ },
};
-export const Extensive = Template.bind({});
-Extensive.args = {
- ...baseArgs,
- title: "Overridden Title",
- description: "Overriden description",
- subheader: "Additional description",
- thumbnail: overpassThumbnail,
- getBadge: () => ,
- thumbnailTopLeft: (
-
-
-
-
-
- ),
- actions: [
- {
- key: "open",
- label: "Open in App",
- onClick: action("iModel opened"),
- },
- {
- key: "vr",
- label: "Open in VR Headset",
- onClick: action("VR Headset clicked"),
- },
- ],
+export const Extensive: StoryObj = {
+ args: {
+ ...baseArgs,
+ title: "Overridden Title",
+ description: "Overriden description",
+ subheader: "Additional description",
+ thumbnail: overpassThumbnail,
+ getBadge: () => ,
+ thumbnailTopLeft: (
+
+
+
+
+
+ ),
+ actions: [
+ {
+ key: "open",
+ label: "Open in App",
+ onClick: action("iModel opened"),
+ },
+ {
+ key: "vr",
+ label: "Open in VR Headset",
+ onClick: action("VR Headset clicked"),
+ },
+ ],
+ },
};
-export const NoThumbnail = Template.bind({});
-NoThumbnail.args = {
- ...baseArgs,
- thumbnail: undefined,
+export const NoThumbnail: StoryObj = {
+ args: {
+ ...baseArgs,
+ thumbnail: undefined,
+ },
};
-export const CustomSvgThumbnail = Template.bind({});
-CustomSvgThumbnail.args = {
- ...baseArgs,
- thumbnail: ,
+export const CustomSvgThumbnail: StoryObj = {
+ args: {
+ ...baseArgs,
+ thumbnail: ,
+ },
};
diff --git a/packages/apps/storybook/src/imodel-browser/mui/ITwinGridMUI.stories.tsx b/packages/apps/storybook/src/imodel-browser/mui/ITwinGridMUI.stories.tsx
index 6c1006f7..022b3272 100644
--- a/packages/apps/storybook/src/imodel-browser/mui/ITwinGridMUI.stories.tsx
+++ b/packages/apps/storybook/src/imodel-browser/mui/ITwinGridMUI.stories.tsx
@@ -18,18 +18,15 @@ import AvatarGroup from "@mui/material/AvatarGroup";
import Box from "@mui/material/Box";
import Chip from "@mui/material/Chip";
import Typography from "@mui/material/Typography";
-import { action } from "@storybook/addon-actions";
-import { Meta, Story } from "@storybook/react/types-6-0";
+import type { Meta, StoryObj } from "@storybook/react-webpack5";
import React from "react";
+import { action } from "storybook/actions";
import bridgeThumbnail from "../../utils/bridge.jpg";
import nightThumbnail from "../../utils/night.jpg";
import overpassThumbnail from "../../utils/overpass.jpg";
import powerThumbnail from "../../utils/power.jpg";
-import {
- accessTokenArgTypes,
- withAccessTokenOverride,
-} from "../../utils/storyHelp";
+import { accessTokenArgTypes } from "../../utils/storyHelp";
type ITwinTileType = React.ComponentPropsWithoutRef;
@@ -37,12 +34,6 @@ export const ITwinGrid = (props: ITwinGridProps) => (
);
-const accessToken = accessTokenArgTypes.accessToken;
-
-const Template: Story = withAccessTokenOverride((args) => (
-
-));
-
const baseArgs: ITwinGridProps = {
apiOverrides: { serverEnvironmentPrefix: "qa" },
viewMode: "tile",
@@ -55,149 +46,155 @@ const baseArgs: ITwinGridProps = {
],
};
-export const Primary = Template.bind({});
-Primary.args = {
- ...baseArgs,
+export const Primary: StoryObj = {
+ args: {
+ ...baseArgs,
+ },
};
-export const TableView = Template.bind({});
-TableView.args = {
- ...baseArgs,
- viewMode: "cells",
- moreActions: [
- {
- label: "Some action",
- key: "something",
- onClick: (iTwin) => action("clicked " + iTwin?.displayName)(iTwin),
- },
- {
- label: "Some other action",
- key: "something-else",
- onClick: (iTwin) =>
- action("clicked something else " + iTwin?.displayName)(iTwin),
- },
- ],
+export const TableView: StoryObj = {
+ args: {
+ ...baseArgs,
+ viewMode: "cells",
+ moreActions: [
+ {
+ label: "Some action",
+ key: "something",
+ onClick: (iTwin) => action("clicked " + iTwin?.displayName)(iTwin),
+ },
+ {
+ label: "Some other action",
+ key: "something-else",
+ onClick: (iTwin) =>
+ action("clicked something else " + iTwin?.displayName)(iTwin),
+ },
+ ],
+ },
};
-export const TableViewWithOverrides = Template.bind({});
-TableViewWithOverrides.args = {
- ...baseArgs,
- viewMode: "cells",
- actions: [
- {
- key: "open",
- label: (iTwin) => iTwin.displayName ?? "",
- onClick: (iTwin) => action("Open " + iTwin.displayName)(iTwin),
- disabled: (iTwin) =>
- iTwin.displayName?.toLowerCase().includes("t") ?? false,
- },
- ],
- tableOverrides: {
- columnOverrides: {
- [ITwinCellColumn.Number]: {
- renderCell: (params) => (
-
- {
- e.stopPropagation();
- action("Icon Clicked")();
- }}
- >
-
- {" "}
- {params.formattedValue}
-
- ),
+export const TableViewWithOverrides: StoryObj = {
+ args: {
+ ...baseArgs,
+ viewMode: "cells",
+ actions: [
+ {
+ key: "open",
+ label: (iTwin) => iTwin.displayName ?? "",
+ onClick: (iTwin) => action("Open " + iTwin.displayName)(iTwin),
+ disabled: (iTwin) =>
+ iTwin.displayName?.toLowerCase().includes("t") ?? false,
},
- [ITwinCellColumn.Name]: {
- renderCell: (params) => (
-
- {params.value}
-
- ),
+ ],
+ tableOverrides: {
+ columnOverrides: {
+ [ITwinCellColumn.Number]: {
+ renderCell: (params) => (
+
+ {
+ e.stopPropagation();
+ action("Icon Clicked")();
+ }}
+ >
+
+ {" "}
+ {params.formattedValue}
+
+ ),
+ },
+ [ITwinCellColumn.Name]: {
+ renderCell: (params) => (
+
+ {params.value}
+
+ ),
+ },
},
+ hideColumns: [ITwinCellColumn.LastModified],
},
- hideColumns: [ITwinCellColumn.LastModified],
},
};
-export const OverrideApiData = Template.bind({});
-OverrideApiData.args = {
- ...baseArgs,
- apiOverrides: {
- data: [
- {
- id: "1",
- displayName: "Bridge iTwin",
- number: "No Network Calls",
- image: bridgeThumbnail,
- },
+export const OverrideApiData: StoryObj = {
+ args: {
+ ...baseArgs,
+ apiOverrides: {
+ data: [
+ {
+ id: "1",
+ displayName: "Bridge iTwin",
+ number: "No Network Calls",
+ image: bridgeThumbnail,
+ },
+ {
+ id: "2",
+ displayName: "Power iTwin",
+ number: "aaa-bbb-ccc",
+ image: powerThumbnail,
+ },
+ {
+ id: "3",
+ displayName: "Overpass iTwin",
+ number: "No Network Calls",
+ image: overpassThumbnail,
+ },
+
+ {
+ id: "4",
+ displayName: "Highway iTwin",
+ number: "No Network Calls",
+ image: nightThumbnail,
+ },
+ ],
+ },
+ },
+};
+
+export const IndividualContextMenu: StoryObj = {
+ args: {
+ ...baseArgs,
+ moreActions: [
{
- id: "2",
- displayName: "Power iTwin",
- number: "aaa-bbb-ccc",
- image: powerThumbnail,
+ label: "displayName contains 'R'",
+ visible: (iTwin) => iTwin.displayName?.includes("R") ?? false,
+ key: "withR",
+ onClick: (iTwin) => action("Contains R" + iTwin?.displayName)(iTwin),
},
{
- id: "3",
- displayName: "Overpass iTwin",
- number: "No Network Calls",
- image: overpassThumbnail,
+ label: "Add iTwinNumber",
+ visible: (iTwin) => !iTwin.number,
+ key: "addD",
+ onClick: (iTwin) =>
+ action("Add iTwinNumber to " + iTwin?.displayName)(iTwin),
},
-
{
- id: "4",
- displayName: "Highway iTwin",
- number: "No Network Calls",
- image: nightThumbnail,
+ label: (iTwin) => `Edit iTwin ${iTwin.displayName}`,
+ visible: (iTwin) => !!iTwin.number,
+ key: "editD",
+ onClick: (iTwin) => action("Edit iTwinNumber: " + iTwin?.number)(iTwin),
},
],
},
};
-export const IndividualContextMenu = Template.bind({});
-IndividualContextMenu.args = {
- ...baseArgs,
- moreActions: [
- {
- label: "displayName contains 'R'",
- visible: (iTwin) => iTwin.displayName?.includes("R") ?? false,
- key: "withR",
- onClick: (iTwin) => action("Contains R" + iTwin?.displayName)(iTwin),
- },
- {
- label: "Add iTwinNumber",
- visible: (iTwin) => !iTwin.number,
- key: "addD",
- onClick: (iTwin) =>
- action("Add iTwinNumber to " + iTwin?.displayName)(iTwin),
- },
- {
- label: (iTwin) => `Edit iTwin ${iTwin.displayName}`,
- visible: (iTwin) => !!iTwin.number,
- key: "editD",
- onClick: (iTwin) => action("Edit iTwinNumber: " + iTwin?.number)(iTwin),
+export const SimpleTilePropsOverrides: StoryObj = {
+ args: {
+ ...baseArgs,
+ tileOverrides: {
+ status: "negative",
+ thumbnail: bridgeThumbnail,
+ thumbnailBottomLeft: ,
+ thumbnailBottomRight: ,
+ thumbnailTopLeft: (
+
+
+
+
+
+ ),
},
- ],
-};
-
-export const SimpleTilePropsOverrides = Template.bind({});
-SimpleTilePropsOverrides.args = {
- ...baseArgs,
- tileOverrides: {
- status: "negative",
- thumbnail: bridgeThumbnail,
- thumbnailBottomLeft: ,
- thumbnailBottomRight: ,
- thumbnailTopLeft: (
-
-
-
-
-
- ),
},
};
@@ -231,172 +228,178 @@ const useIndividualState: IndividualITwinStateHook = (iTwin, props) => {
};
};
-export const UseIndividualState = Template.bind({});
-UseIndividualState.args = {
- apiOverrides: { serverEnvironmentPrefix: "qa" },
- useIndividualState,
+export const UseIndividualState: StoryObj = {
+ args: {
+ apiOverrides: { serverEnvironmentPrefix: "qa" },
+ useIndividualState,
+ },
};
-export const WithPostProcessCallback: Story =
- withAccessTokenOverride((args) => {
- const addStartTile = React.useCallback(
- (iTwins: ITwinFull[], status: any) => {
- if (status !== (DataStatus as any).Complete) {
- return iTwins;
- }
- iTwins.unshift({
- id: "newProject",
- displayName: "New Project",
- number: "Click on this tile to create a new ITwin",
- });
- return iTwins;
- },
- []
- );
- return (
-
-
- Property postProcessCallback allows modification of the
- data that is sent to the grid, here, we add a new tile at the start of
- the list for a 'New Project'.
-
-
-
- );
- });
-WithPostProcessCallback.args = {
- apiOverrides: { serverEnvironmentPrefix: "qa" },
+const WithPostProcessCallbackRender = (args: ITwinGridProps) => {
+ const addStartTile = React.useCallback((iTwins: ITwinFull[], status: any) => {
+ if (status !== (DataStatus as any).Complete) {
+ return iTwins;
+ }
+ iTwins.unshift({
+ id: "newProject",
+ displayName: "New Project",
+ number: "Click on this tile to create a new ITwin",
+ });
+ return iTwins;
+ }, []);
+ return (
+
+
+ Property postProcessCallback allows modification of the
+ data that is sent to the grid, here, we add a new tile at the start of
+ the list for a 'New Project'.
+
+
+
+ );
};
-export const FetchAllSubclasses = Template.bind({});
-FetchAllSubclasses.args = {
- apiOverrides: { serverEnvironmentPrefix: "qa" },
- iTwinSubClass: "All",
+export const WithPostProcessCallback: StoryObj = {
+ render: (args) => ,
+ args: {
+ apiOverrides: { serverEnvironmentPrefix: "qa" },
+ },
};
-export const NoResultsWithDefaultEmptyState = Template.bind({});
-NoResultsWithDefaultEmptyState.args = {
- ...baseArgs,
- apiOverrides: { serverEnvironmentPrefix: "qa" },
- postProcessCallback: (iModels, status) => {
- return [];
+export const FetchAllSubclasses: StoryObj = {
+ args: {
+ apiOverrides: { serverEnvironmentPrefix: "qa" },
+ iTwinSubClass: "All",
},
};
-export const TableViewWithNoResults = Template.bind({});
-TableViewWithNoResults.args = {
- ...baseArgs,
- viewMode: "cells",
- postProcessCallback: (iModels, status) => {
- return [];
+export const NoResultsWithDefaultEmptyState: StoryObj = {
+ args: {
+ ...baseArgs,
+ apiOverrides: { serverEnvironmentPrefix: "qa" },
+ postProcessCallback: (iModels, status) => {
+ return [];
+ },
},
};
-export const StringsOverrideGrid = Template.bind({});
-StringsOverrideGrid.args = {
- ...baseArgs,
- apiOverrides: {
- data: [
- {
- id: "1",
- displayName: "Bridge iTwin",
- number: "1111-2222-3333-4444",
- image: bridgeThumbnail,
- status: "Trial",
- },
- {
- id: "2",
- displayName: "Power iTwin",
- number: "2222-3333-4444-5555",
- image: powerThumbnail,
- status: "Inactive",
- },
+export const TableViewWithNoResults: StoryObj = {
+ args: {
+ ...baseArgs,
+ viewMode: "cells",
+ postProcessCallback: (iModels, status) => {
+ return [];
+ },
+ },
+};
+
+export const StringsOverrideGrid: StoryObj = {
+ args: {
+ ...baseArgs,
+ apiOverrides: {
+ data: [
+ {
+ id: "1",
+ displayName: "Bridge iTwin",
+ number: "1111-2222-3333-4444",
+ image: bridgeThumbnail,
+ status: "Trial",
+ },
+ {
+ id: "2",
+ displayName: "Power iTwin",
+ number: "2222-3333-4444-5555",
+ image: powerThumbnail,
+ status: "Inactive",
+ },
+ {
+ id: "3",
+ displayName: "Highway iTwin",
+ number: "3333-4444-5555-6666",
+ image: nightThumbnail,
+ },
+ ],
+ },
+ moreActions: [
{
- id: "3",
- displayName: "Highway iTwin",
- number: "3333-4444-5555-6666",
- image: nightThumbnail,
+ label: "Some action",
+ key: "something",
+ onClick: (iTwin) => action("clicked " + iTwin?.displayName)(iTwin),
},
],
- },
- moreActions: [
- {
- label: "Some action",
- key: "something",
- onClick: (iTwin) => action("clicked " + iTwin?.displayName)(iTwin),
+ stringsOverrides: {
+ moreOptions: "Flere muligheder",
+ trialBadge: "Prøveversion",
+ inactiveBadge: "Inaktiv",
+ addToFavorites: "Føj til favoritter",
+ removeFromFavorites: "Fjern fra favoritter",
+ noRowsLabel: "Ingen rækker",
+ noResultsOverlayLabel: "Ingen resultater fundet.",
+ footerRowSelected: (count: number) =>
+ count !== 1
+ ? `${count.toLocaleString()} rækker valgt`
+ : `${count.toLocaleString()} række valgt`,
+ footerTotalVisibleRows: (visibleCount: number, totalCount: number) =>
+ `${visibleCount.toLocaleString()} af ${totalCount.toLocaleString()}`,
+ paginationRowsPerPage: "Rækker per side:",
},
- ],
- stringsOverrides: {
- moreOptions: "Flere muligheder",
- trialBadge: "Prøveversion",
- inactiveBadge: "Inaktiv",
- addToFavorites: "Føj til favoritter",
- removeFromFavorites: "Fjern fra favoritter",
- noRowsLabel: "Ingen rækker",
- noResultsOverlayLabel: "Ingen resultater fundet.",
- footerRowSelected: (count: number) =>
- count !== 1
- ? `${count.toLocaleString()} rækker valgt`
- : `${count.toLocaleString()} række valgt`,
- footerTotalVisibleRows: (visibleCount: number, totalCount: number) =>
- `${visibleCount.toLocaleString()} af ${totalCount.toLocaleString()}`,
- paginationRowsPerPage: "Rækker per side:",
},
};
-export const StringsOverrideTable = Template.bind({});
-StringsOverrideTable.args = {
- ...baseArgs,
- viewMode: "cells",
- apiOverrides: {
- data: [
- {
- id: "1",
- displayName: "Bridge iTwin",
- number: "1111-2222-3333-4444",
- image: bridgeThumbnail,
- status: "Trial",
- },
- {
- id: "2",
- displayName: "Power iTwin",
- number: "2222-3333-4444-5555",
- image: powerThumbnail,
- status: "Inactive",
- },
+export const StringsOverrideTable: StoryObj = {
+ args: {
+ ...baseArgs,
+ viewMode: "cells",
+ apiOverrides: {
+ data: [
+ {
+ id: "1",
+ displayName: "Bridge iTwin",
+ number: "1111-2222-3333-4444",
+ image: bridgeThumbnail,
+ status: "Trial",
+ },
+ {
+ id: "2",
+ displayName: "Power iTwin",
+ number: "2222-3333-4444-5555",
+ image: powerThumbnail,
+ status: "Inactive",
+ },
+ {
+ id: "3",
+ displayName: "Highway iTwin",
+ number: "3333-4444-5555-6666",
+ image: nightThumbnail,
+ },
+ ],
+ },
+ moreActions: [
{
- id: "3",
- displayName: "Highway iTwin",
- number: "3333-4444-5555-6666",
- image: nightThumbnail,
+ label: "Some action",
+ key: "something",
+ onClick: (iTwin) => action("clicked " + iTwin?.displayName)(iTwin),
},
],
- },
- moreActions: [
- {
- label: "Some action",
- key: "something",
- onClick: (iTwin) => action("clicked " + iTwin?.displayName)(iTwin),
+ stringsOverrides: {
+ moreOptions: "Flere muligheder",
+ trialBadge: "Prøveversion",
+ inactiveBadge: "Inaktiv",
+ addToFavorites: "Føj til favoritter",
+ removeFromFavorites: "Fjern fra favoritter",
+ tableColumnName: "iTwin Navn",
+ tableColumnDescription: "iTwin Beskrivelse",
+ tableColumnLastModified: "Sidst ændret",
+ noRowsLabel: "Ingen rækker",
+ noResultsOverlayLabel: "Ingen resultater fundet.",
+ footerRowSelected: (count: number) =>
+ count !== 1
+ ? `${count.toLocaleString()} rækker valgt`
+ : `${count.toLocaleString()} række valgt`,
+ footerTotalVisibleRows: (visibleCount: number, totalCount: number) =>
+ `${visibleCount.toLocaleString()} af ${totalCount.toLocaleString()}`,
+ paginationRowsPerPage: "Rækker per side:",
},
- ],
- stringsOverrides: {
- moreOptions: "Flere muligheder",
- trialBadge: "Prøveversion",
- inactiveBadge: "Inaktiv",
- addToFavorites: "Føj til favoritter",
- removeFromFavorites: "Fjern fra favoritter",
- tableColumnName: "iTwin Navn",
- tableColumnDescription: "iTwin Beskrivelse",
- tableColumnLastModified: "Sidst ændret",
- noRowsLabel: "Ingen rækker",
- noResultsOverlayLabel: "Ingen resultater fundet.",
- footerRowSelected: (count: number) =>
- count !== 1
- ? `${count.toLocaleString()} rækker valgt`
- : `${count.toLocaleString()} række valgt`,
- footerTotalVisibleRows: (visibleCount: number, totalCount: number) =>
- `${visibleCount.toLocaleString()} af ${totalCount.toLocaleString()}`,
- paginationRowsPerPage: "Rækker per side:",
},
};
@@ -404,7 +407,7 @@ export default {
title: "imodel-browser/ITwinGridMUI",
component: ITwinGrid,
argTypes: {
- accessToken,
+ ...accessTokenArgTypes,
viewMode: {
options: ["tile", "cells"],
control: {
diff --git a/packages/apps/storybook/src/imodel-browser/mui/ITwinTileMUI.stories.tsx b/packages/apps/storybook/src/imodel-browser/mui/ITwinTileMUI.stories.tsx
index 5fe99b5c..dba2defc 100644
--- a/packages/apps/storybook/src/imodel-browser/mui/ITwinTileMUI.stories.tsx
+++ b/packages/apps/storybook/src/imodel-browser/mui/ITwinTileMUI.stories.tsx
@@ -10,8 +10,8 @@ import {
import { SvgThumbnail } from "@itwin/imodel-browser-react/mui";
import Box from "@mui/material/Box";
import Chip from "@mui/material/Chip";
-import { action } from "@storybook/addon-actions";
-import { Meta, Story } from "@storybook/react/types-6-0";
+import { action } from "storybook/actions";
+import type { Meta, StoryObj } from "@storybook/react-webpack5";
import svgMagnet from "@stratakit/icons/magnet.svg";
import React from "react";
@@ -117,46 +117,46 @@ export default {
},
} as Meta;
-const Template: Story = (args) => (
-
-);
-
-export const Default = Template.bind({});
-Default.args = {
- ...baseArgs,
- isFavorite: false,
- disabled: false,
- loading: false,
+export const Default: StoryObj = {
+ args: {
+ ...baseArgs,
+ isFavorite: false,
+ disabled: false,
+ loading: false,
+ },
};
-export const Extensive = Template.bind({});
-Extensive.args = {
- ...baseArgs,
- status: "warning",
- isFavorite: true,
- title: "Overridden Title",
- description: "Overriden description",
- disabled: false,
- loading: false,
- thumbnail: powerThumbnail,
- thumbnailBottomRight: ,
- thumbnailTopLeft: ,
- thumbnailBottomLeft: ,
- actions: [
- { key: "open", label: "Open", onClick: action("iTwin open clicked") },
- { key: "share", label: "Share", onClick: action("iTwin share clicked") },
- ],
+export const Extensive: StoryObj = {
+ args: {
+ ...baseArgs,
+ status: "warning",
+ isFavorite: true,
+ title: "Overridden Title",
+ description: "Overriden description",
+ disabled: false,
+ loading: false,
+ thumbnail: powerThumbnail,
+ thumbnailBottomRight: ,
+ thumbnailTopLeft: ,
+ thumbnailBottomLeft: ,
+ actions: [
+ { key: "open", label: "Open", onClick: action("iTwin open clicked") },
+ { key: "share", label: "Share", onClick: action("iTwin share clicked") },
+ ],
+ },
};
-export const DefaultThumbnailStory = Template.bind({});
-DefaultThumbnailStory.args = {
- ...baseArgs,
- thumbnail: ,
+export const DefaultThumbnailStory: StoryObj = {
+ name: "Default Thumbnail",
+ args: {
+ ...baseArgs,
+ thumbnail: ,
+ },
};
-DefaultThumbnailStory.storyName = "Default Thumbnail";
-export const CustomSvgThumbnail = Template.bind({});
-CustomSvgThumbnail.args = {
- ...baseArgs,
- thumbnail: ,
+export const CustomSvgThumbnail: StoryObj = {
+ args: {
+ ...baseArgs,
+ thumbnail: ,
+ },
};
diff --git a/packages/apps/storybook/src/imodel-browser/mui/NoResultsMUI.stories.tsx b/packages/apps/storybook/src/imodel-browser/mui/NoResultsMUI.stories.tsx
index 3276aee6..1e08f9f8 100644
--- a/packages/apps/storybook/src/imodel-browser/mui/NoResultsMUI.stories.tsx
+++ b/packages/apps/storybook/src/imodel-browser/mui/NoResultsMUI.stories.tsx
@@ -6,7 +6,7 @@ import {
type NoResultsProps as NoResultsMUIProps,
NoResults as ExternalComponent,
} from "@itwin/imodel-browser-react/mui";
-import { Meta, Story } from "@storybook/react/types-6-0";
+import type { Meta, StoryObj } from "@storybook/react-webpack5";
import React from "react";
export const NoResults = (props: NoResultsMUIProps) => (
@@ -19,22 +19,23 @@ export default {
excludeStories: ["NoResults"],
} as Meta;
-const Template: Story = (args) => ;
-
-export const Primary = Template.bind({});
-Primary.args = {
- text: "No iModels available",
+export const Primary: StoryObj = {
+ args: {
+ text: "No iModels available",
+ },
};
-export const SearchResults = Template.bind({});
-SearchResults.args = {
- text: "No search results",
- subtext: "Try adjusting your search criteria.",
- isSearchResult: true,
+export const SearchResults: StoryObj = {
+ args: {
+ text: "No search results",
+ subtext: "Try adjusting your search criteria.",
+ isSearchResult: true,
+ },
};
-export const WithSubtext = Template.bind({});
-WithSubtext.args = {
- text: "No iModels available",
- subtext: "Please check back later.",
+export const WithSubtext: StoryObj = {
+ args: {
+ text: "No iModels available",
+ subtext: "Please check back later.",
+ },
};
diff --git a/packages/apps/storybook/src/manage-versions/ManageVersions.stories.tsx b/packages/apps/storybook/src/manage-versions/ManageVersions.stories.tsx
index 17b76fa5..f26e77ac 100644
--- a/packages/apps/storybook/src/manage-versions/ManageVersions.stories.tsx
+++ b/packages/apps/storybook/src/manage-versions/ManageVersions.stories.tsx
@@ -7,14 +7,10 @@ import {
ManageVersions as ExternalComponent,
ManageVersionsProps,
} from "@itwin/manage-versions-react";
-import { action } from "@storybook/addon-actions";
-import { Meta, Story } from "@storybook/react/types-6-0";
+import { action } from "storybook/actions";
+import type { Meta, StoryObj } from "@storybook/react-webpack5";
import React from "react";
-
-import {
- accessTokenArgTypes,
- withAccessTokenOverride,
-} from "../utils/storyHelp";
+import { accessTokenArgTypes } from "../utils/storyHelp";
export const ManageVersions = (props: ManageVersionsProps) => (
@@ -26,21 +22,19 @@ export default {
excludeStories: ["ManageVersions"],
argTypes: {
...accessTokenArgTypes,
- log: { defaultValue: action("Error logged. "), control: { disable: true } },
- onViewClick: {
- defaultValue: action("View Named Version clicked"),
- control: { disable: true },
- },
+ log: { control: { disable: true } },
+ onViewClick: { control: { disable: true } },
+ },
+ args: {
+ log: action("Error logged."),
+ onViewClick: action("View Named Version clicked"),
},
} as Meta;
-const Template: Story = withAccessTokenOverride((args) => (
-
-));
-
-export const Primary = Template.bind({});
-Primary.args = {
- apiOverrides: {
- serverEnvironmentPrefix: "qa",
+export const Primary: StoryObj = {
+ args: {
+ apiOverrides: {
+ serverEnvironmentPrefix: "qa",
+ },
},
};
diff --git a/packages/apps/storybook/src/utils/storyHelp.ts b/packages/apps/storybook/src/utils/storyHelp.ts
index 82a583b9..ca1c378f 100644
--- a/packages/apps/storybook/src/utils/storyHelp.ts
+++ b/packages/apps/storybook/src/utils/storyHelp.ts
@@ -2,35 +2,29 @@
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
-import { Story } from "@storybook/react/types-6-0";
const ACCESS_TOKEN_DEFAULT_VALUE =
"In this storybook, this is provided by clicking on the key in the toolbar";
export const accessTokenArgTypes = {
accessToken: {
- defaultValue: ACCESS_TOKEN_DEFAULT_VALUE,
+ control: { type: "text" },
+ table: { defaultValue: { summary: ACCESS_TOKEN_DEFAULT_VALUE } },
},
};
-/** HOC that will override the "accessToken" prop with the Addon token */
-export const withAccessTokenOverride: <
- T extends { accessToken?: string | (() => Promise) }
->(
- story: Story
-) => Story = (Story) => (args, context) =>
- Story({ ...args, accessToken: context.globals.accessToken }, context);
+const ITWIN_ID_DEFAULT_VALUE =
+ "In this storybook, this is provided by selecting an iTwin in the toolbar";
+export const iTwinIdArgTypes = {
+ iTwinId: {
+ description: "iTwin ID to load data from",
+ control: { type: "text" },
+ table: { defaultValue: { summary: ITWIN_ID_DEFAULT_VALUE } },
+ },
+};
-/** HOC that will override the "iTwinId" prop with the Addon iTwinId */
-export const withITwinIdOverride: (
- story: Story
-) => Story = (Story) => (args, context) =>
- Story(
- {
- ...args,
- iTwinId:
- args.iTwinId ??
- context.globals.iTwinId ??
- process.env.STORYBOOK_ITWIN_ID,
- },
- context
- );
+/** Combined helper for stories that need both access token (auth toolbar)
+ * and iTwin ID (project selector toolbar). */
+export const iTwinAndAccessTokenArgTypes = {
+ ...accessTokenArgTypes,
+ ...iTwinIdArgTypes,
+};
diff --git a/packages/apps/storybook/tsconfig.eslint.json b/packages/apps/storybook/tsconfig.eslint.json
index 06164757..e9f33bb5 100644
--- a/packages/apps/storybook/tsconfig.eslint.json
+++ b/packages/apps/storybook/tsconfig.eslint.json
@@ -1,11 +1,18 @@
{
"compilerOptions": {
"strictNullChecks": true,
+ "allowJs": true,
"paths": {
"@itwin/imodel-browser-react/mui": [
"../../modules/imodel-browser/src/mui/index.ts"
]
}
},
- "include": ["**/*.ts*", "**/*.js*", "**/.*.js*"]
+ "include": [
+ ".storybook/**/*.ts*",
+ ".storybook/**/*.js*",
+ "**/*.ts*",
+ "**/*.js*",
+ "**/.*.js*"
+ ]
}
diff --git a/packages/modules/storybook-auth-addon/register.js b/packages/modules/storybook-auth-addon/manager.jsx
similarity index 75%
rename from packages/modules/storybook-auth-addon/register.js
rename to packages/modules/storybook-auth-addon/manager.jsx
index d369ef4f..343db429 100644
--- a/packages/modules/storybook-auth-addon/register.js
+++ b/packages/modules/storybook-auth-addon/manager.jsx
@@ -5,24 +5,30 @@
/* eslint-disable react-hooks/rules-of-hooks */
import { ClientRequestContext } from "@bentley/bentleyjs-core";
import { BrowserAuthorizationClient } from "@bentley/frontend-authorization-client";
-import addons, { types } from "@storybook/addons";
-import { useAddonState, useGlobals, useParameter } from "@storybook/api";
-import { IconButton, Icons, Loader, WithTooltip } from "@storybook/components";
+import { AlertIcon, KeyIcon, LockIcon } from "@storybook/icons";
import React, { useRef, useState } from "react";
+import { IconButton, WithTooltip } from "storybook/internal/components";
+import {
+ addons,
+ types,
+ useAddonState,
+ useParameter,
+} from "storybook/manager-api";
+
+const ACCESS_TOKEN_EVENT = "auth/toolbar/set-access-token";
addons.register("auth/toolbar", () => {
addons.add("auth-toolbar-addon/toolbar", {
title: "OIDC Authentication toolbar",
- //👇 Sets the type of UI element in Storybook
type: types.TOOL,
- //👇 Shows the Toolbar UI element if either the Canvas or Docs tab is active
match: ({ viewMode }) => !!viewMode?.match(/^(story|docs)$/),
render: () => {
- const [globals, updateGlobals] = useGlobals();
+ const channel = addons.getChannel();
const redirectURI = `${window.location.origin}${window.location.pathname}signin-oidc.html`;
const [state, setState] = useAddonState("auth/toolbar", {
loading: false,
email: "",
+ accessToken: "",
});
const authClientConfig = useParameter("authClientConfig", {});
const client = useRef(null);
@@ -35,7 +41,10 @@ addons.register("auth/toolbar", () => {
return;
}
- setState({ loading: true });
+ setState((previousState) => ({
+ ...previousState,
+ loading: true,
+ }));
try {
if (!authClientConfig.clientId) {
setClientIdMissing(true);
@@ -55,8 +64,8 @@ addons.register("auth/toolbar", () => {
});
client.current.onUserStateChanged.addListener((accessToken) => {
if (!accessToken) {
- updateGlobals({ accessToken: "" });
- setState({ loading: false });
+ setState({ loading: false, email: "", accessToken: "" });
+ channel.emit(ACCESS_TOKEN_EVENT, "");
return;
}
let tokenString = accessToken.toTokenString();
@@ -65,23 +74,26 @@ addons.register("auth/toolbar", () => {
email = JSON.parse(
atob(tokenString.split(" ")[1]?.split(".")[1])
).email;
- } catch (e) {
+ } catch {
email = "Email parsing failed";
}
- updateGlobals({ accessToken: tokenString });
- setState({ loading: false, email });
+ setState({ loading: false, email, accessToken: tokenString });
+ channel.emit(ACCESS_TOKEN_EVENT, tokenString);
});
}
const context = new ClientRequestContext();
- if (!globals.accessToken) {
+ if (!state.accessToken) {
await client.current.signInPopup(context);
} else {
await client.current.signOutPopup(context).catch(() => {
// Intentionally a noop, user closing the window is not an issue.
});
}
- } catch (e) {
- setState({ loading: false });
+ } catch {
+ setState((previousState) => ({
+ ...previousState,
+ loading: false,
+ }));
}
};
@@ -105,7 +117,7 @@ addons.register("auth/toolbar", () => {
? `${redirectURI} not found: "storybook-auth-addon" is likely not built, run "rush build"`
: state.loading
? "Authenticating..."
- : globals.accessToken
+ : state.accessToken
? `Authenticated: ${state.email}, click to sign off`
: `Authenticate`}
@@ -113,25 +125,20 @@ addons.register("auth/toolbar", () => {
}}
>
authenticate()}
>
{buildMissing || clientIdMissing ? (
-
+
) : state.loading ? (
-
+ ...
+ ) : state.accessToken ? (
+
) : (
-
+
)}
diff --git a/packages/modules/storybook-auth-addon/package.json b/packages/modules/storybook-auth-addon/package.json
index 7a9c87ea..b370c1b6 100644
--- a/packages/modules/storybook-auth-addon/package.json
+++ b/packages/modules/storybook-auth-addon/package.json
@@ -4,9 +4,8 @@
"private": true,
"homepage": ".",
"dependencies": {
- "@storybook/addons": "^6.5.16",
- "@storybook/api": "^6.5.16",
- "@storybook/components": "^6.5.16"
+ "@storybook/icons": "^2.0.2",
+ "storybook": "^10.4.0"
},
"devDependencies": {
"@bentley/bentleyjs-core": "2.11.0",
diff --git a/packages/modules/storybook-auth-addon/preset.js b/packages/modules/storybook-auth-addon/preset.js
new file mode 100644
index 00000000..68ef12e3
--- /dev/null
+++ b/packages/modules/storybook-auth-addon/preset.js
@@ -0,0 +1,3 @@
+module.exports = {
+ managerEntries: [require.resolve("./manager.jsx")],
+};