Skip to content

Commit

Permalink
fix: improve Storybook code snippets (#1134)
Browse files Browse the repository at this point in the history
Relates to #1078, #414

- simplified some Stories
- refactor oynx icon import code transformer to work globally so we
don't need to add it to every component that uses icons
- temporarily copy over improved source code generation until it is
released in Storybook itself (see
storybookjs/storybook#27194)
  • Loading branch information
larsrickert committed May 23, 2024
1 parent f373212 commit 99b2089
Show file tree
Hide file tree
Showing 28 changed files with 684 additions and 350 deletions.
5 changes: 5 additions & 0 deletions .changeset/serious-comics-fly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@sit-onyx/icons": minor
---

feat: add `getIconImportName` utility
8 changes: 8 additions & 0 deletions .changeset/seven-carpets-exist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@sit-onyx/storybook-utils": major
---

use experimental source code generator

- port the improved source code generator from [this Storybook PR](https://github.com/storybookjs/storybook/pull/27194).
- globally replace onyx icons with their corresponding imports from `@sit-onyx/icons`
9 changes: 2 additions & 7 deletions apps/docs/src/.vitepress/utils-icons.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ICON_CATEGORIES } from "@sit-onyx/icons";
import { ICON_CATEGORIES, getIconImportName } from "@sit-onyx/icons";
import { capitalize } from "vue";

export type EnrichedIcon = {
Expand All @@ -23,12 +23,7 @@ const getIconContextData = (iconName: string, allIconContents: Record<string, st
// bell-disabled => `Bell Disabled`
const tooltipName = parts.map((word) => capitalize(word)).join(" ");
// bell-disabled => `bellDisabled`
const importName = parts
.map((word, index) => {
if (index === 0) return word;
return capitalize(word);
})
.join("");
const importName = getIconImportName(iconName);
// svg content for OnyxIcon `icon` prop
const content =
allIconContents[`../../../node_modules/@sit-onyx/icons/src/assets/${iconName}.svg`];
Expand Down
28 changes: 27 additions & 1 deletion packages/icons/src/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IconMetadata, groupIconsByCategory } from "./utils.js";
import { groupIconsByCategory, type IconMetadata } from "./utils.js";

/**
* Metadata for all available onyx icons.
Expand Down Expand Up @@ -1787,3 +1787,29 @@ export const ICON_METADATA = {
* Categories and icons will be sorted alphabetically.
*/
export const ICON_CATEGORIES = groupIconsByCategory(ICON_METADATA);

/**
* Transform an icon name to its corresponding JavaScript import name.
*
* @example
* ```ts
* "bell-disabled" => "bellDisabled"
* // e.g. used as 'import bellDisabled from "@sit-onyx/icons/bell-disabled.svg?raw"'
* ```
*/
export const getIconImportName = (iconName: string) => {
return iconName
.split("-")
.map((word, index) => {
if (index === 0) return word;
return capitalize(word);
})
.join("");
};

/**
* Capitalizes the first character of the given string.
*/
const capitalize = (value: string) => {
return value.charAt(0).toUpperCase() + value.slice(1);
};
3 changes: 2 additions & 1 deletion packages/icons/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"compilerOptions": {
"rootDir": "./src",
"baseUrl": ".",
"types": ["node"]
"types": ["node"],
"verbatimModuleSyntax": true
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { defineStorybookActionsAndVModels } from "@sit-onyx/storybook-utils";
import type { Meta, StoryObj } from "@storybook/vue3";
import { h } from "vue";
import OnyxAppLayout from "./OnyxAppLayout.vue";

/**
Expand Down Expand Up @@ -37,10 +38,6 @@ const meta: Meta<typeof OnyxAppLayout> = {
</div>`,
}),
],
render: (args) => ({
setup: () => ({ args }),
...getAppTemplate(args.navBarAlignment === "left"),
}),
}),
};

Expand All @@ -49,75 +46,75 @@ type Story = StoryObj<typeof OnyxAppLayout>;

/** Standard app layout with a nav bar and some content. */
export const Default = {
args: {},
args: {
default: () => h("div", "This is the page content."),
navBar: () =>
h(
"header",
{
style: "border-bottom: var(--onyx-1px-in-rem) solid var(--onyx-color-base-neutral-300);",
},
"Nav bar",
),
},
} satisfies Story;

/** App layout where the nav bar is left aligned. */
export const LeftNav = {
args: {
...Default.args,
navBarAlignment: "left",
default: () => h("div", "This is the page content."),
navBar: () =>
h(
"header",
{
style:
"border-right: var(--onyx-1px-in-rem) solid var(--onyx-color-base-neutral-300); height: 100%;",
},
"Nav bar",
),
},
} satisfies Story;

/** Example of an overlay that covers the whole application. */
export const AppOverlay = {
args: {
...Default.args,
appOverlay: () =>
h(
"div",
{
style: `background-color: var(--onyx-color-base-background-tinted);
position: absolute;
inset: 10rem;
min-width: 5rem;
min-height: 1rem;
`,
},
"This is an overlay that covers the whole app.",
),
},
render: (args) => ({
setup: () => ({ args }),
...getAppTemplate(
args.navBarAlignment === "left",
`<template #appOverlay>
<div style="background-color: var(--onyx-color-base-background-tinted);
position: absolute;
inset: 10rem;
min-width: 5rem;
min-height: 1rem;">
This is an overlay that covers the whole app.
</div>
</template>`,
),
}),
} satisfies Story;

/** Example of an overlay that covers the whole page section of an application. */
export const PageOverlay = {
args: {
...Default.args,
pageOverlay: () =>
h(
"div",
{
style: `backdrop-filter: blur(4px);
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
align-items: center;`,
},
[
h("div", "This is an overlay that covers the page content."),
h("div", "The nav bar is excluded."),
],
),
},
render: (args) => ({
setup: () => ({ args }),
...getAppTemplate(
args.navBarAlignment === "left",
`<template #pageOverlay>
<div style="backdrop-filter: blur(4px);
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
align-items: center;">
<div>This is an overlay that covers the page content.</div>
<div>The nav bar is excluded.</div>
</div>
</template>`,
),
}),
} satisfies Story;

const getAppTemplate = (alignNavLeft: boolean, otherSlotContent?: string) => ({
components: { OnyxAppLayout },
template: `
<OnyxAppLayout v-bind="args">
<template #navBar>
<div style="border-${alignNavLeft ? "right" : "bottom"}: var(--onyx-1px-in-rem) solid var(--onyx-color-base-neutral-300);
${alignNavLeft ? "height: 100%" : ""}">
Nav bar
</div>
</template>
<div>This is the page content.</div>
${otherSlotContent ?? ""}
</OnyxAppLayout>
`,
});
14 changes: 1 addition & 13 deletions packages/sit-onyx/src/components/OnyxBadge/OnyxBadge.stories.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import placeholder from "@sit-onyx/icons/placeholder.svg?raw";
import { defineStorybookActionsAndVModels } from "@sit-onyx/storybook-utils";
import type { Meta, StoryObj } from "@storybook/vue3";
import {
createIconSourceCodeTransformer,
createTruncationDecorator,
defineIconSelectArgType,
} from "../../utils/storybook";
import { createTruncationDecorator, defineIconSelectArgType } from "../../utils/storybook";
import OnyxBadge from "./OnyxBadge.vue";

/**
Expand All @@ -29,14 +25,6 @@ const meta: Meta<typeof OnyxBadge> = {
default: { control: { type: "text" } },
},
}),
parameters: {
docs: {
source: {
// improve code snippet by adding the icon import
transform: createIconSourceCodeTransformer("icon"),
},
},
},
};

export default meta;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import checkSmall from "@sit-onyx/icons/check-small.svg?raw";
import { defineStorybookActionsAndVModels } from "@sit-onyx/storybook-utils";
import type { Meta, StoryObj } from "@storybook/vue3";
import {
createIconSourceCodeTransformer,
createTruncationDecorator,
defineIconSelectArgType,
} from "../../utils/storybook";
import { createTruncationDecorator, defineIconSelectArgType } from "../../utils/storybook";
import OnyxButton from "./OnyxButton.vue";

/**
Expand All @@ -26,14 +22,6 @@ const meta: Meta<typeof OnyxButton> = {
icon: defineIconSelectArgType(),
},
}),
parameters: {
docs: {
source: {
// improve code snippet by adding the icon import
transform: createIconSourceCodeTransformer("icon"),
},
},
},
};

export default meta;
Expand Down
11 changes: 5 additions & 6 deletions packages/sit-onyx/src/components/OnyxEmpty/OnyxEmpty.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,11 @@ export const Default = {
export const CustomContent = {
args: {
icon: () => h(OnyxIcon, { icon: emojiSad, size: "32px", color: "warning" }),
default: () =>
h("div", [
"No data found. Go to ",
h(OnyxLink, { href: "#" }, () => "this page"),
" to add some data.",
]),
default: () => [
"No data found. Go to ",
h(OnyxLink, { href: "#" }, () => "this page"),
" to add some data.",
],
},
} satisfies Story;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
import { defineStorybookActionsAndVModels } from "@sit-onyx/storybook-utils";
import type { Meta, StoryObj } from "@storybook/vue3";
import OnyxFlyoutMenu from "./OnyxFlyoutMenu.vue";
import { h } from "vue";
import OnyxListItem from "../OnyxListItem/OnyxListItem.vue";
import OnyxFlyoutMenu from "./OnyxFlyoutMenu.vue";

const meta: Meta<typeof OnyxFlyoutMenu> = {
title: "support/FlyoutMenu",
...defineStorybookActionsAndVModels({
component: OnyxFlyoutMenu,
events: [],
argTypes: {
default: { control: { disable: true } },
header: { control: { disable: true } },
footer: { control: { disable: true } },
},
}),
};

export default meta;
type Story = StoryObj<typeof OnyxFlyoutMenu>;

const listAnimals = [
{ label: "Cat" },
{ label: "Dog" },
Expand All @@ -25,19 +33,11 @@ const listAnimals = [
{ label: "Owl" },
];

export default meta;
type Story = StoryObj<typeof OnyxFlyoutMenu>;

/**
* This example shows a basic OnyxFlyoutMenu
*/
export const Default = {
args: {
default: () =>
h(
"div",
{ style: { display: "contents" } },
listAnimals.map(({ label }) => h(OnyxListItem, label)),
),
default: () => listAnimals.map(({ label }) => h(OnyxListItem, label)),
},
} satisfies Story;
15 changes: 2 additions & 13 deletions packages/sit-onyx/src/components/OnyxIcon/OnyxIcon.stories.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import happyIcon from "@sit-onyx/icons/emoji-happy-2.svg?raw";
import { defineStorybookActionsAndVModels } from "@sit-onyx/storybook-utils";
import type { Meta, StoryObj } from "@storybook/vue3";
import {
createIconSourceCodeTransformer,
defineIconSelectArgType,
textColorDecorator,
} from "../../utils/storybook";
import { defineIconSelectArgType, textColorDecorator } from "../../utils/storybook";
import OnyxIcon from "./OnyxIcon.vue";

/**
Expand All @@ -24,14 +20,7 @@ const meta: Meta<typeof OnyxIcon> = {
icon: defineIconSelectArgType(),
},
}),
parameters: {
docs: {
source: {
// improve code snippet by adding the icon import
transform: createIconSourceCodeTransformer("icon"),
},
},
},

decorators: [textColorDecorator],
};

Expand Down
Loading

0 comments on commit 99b2089

Please sign in to comment.