From 7e0f961ba280f1617fd19259eeb13580534f27f6 Mon Sep 17 00:00:00 2001
From: sjoerdbeentjes <11621275+sjoerdbeentjes@users.noreply.github.com>
Date: Wed, 1 Jul 2026 16:05:37 +0200
Subject: [PATCH 1/2] chore(storybook): pin Foundations on top, brand per
framework, add logo
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Fixes the sidebar order so Foundations always sorts above Components and
stays alphabetically consistent between the React and Angular Storybooks
(storySort has to be a literal per preview.ts — Storybook's index generator
reads it via static AST analysis and never executes the shared config
module). Also brands each Storybook's title with its framework name and
adds the SURF logo, and links both deployed Storybooks from the README.
---
README.md | 8 ++++++--
packages/angular/.storybook/manager.ts | 2 +-
packages/angular/.storybook/preview.ts | 14 ++++++++++++-
packages/react/.storybook/manager.ts | 2 +-
packages/react/.storybook/preview.ts | 10 ++++++++++
packages/storybook-config/src/index.ts | 20 +++++++++++++++++++
packages/storybook-config/src/logo.ts | 13 ++++++++++++
packages/storybook-config/src/manager.ts | 25 ++++++++++++++++--------
8 files changed, 81 insertions(+), 13 deletions(-)
create mode 100644 packages/storybook-config/src/logo.ts
diff --git a/README.md b/README.md
index b4c2dfe..7e70a62 100644
--- a/README.md
+++ b/README.md
@@ -63,11 +63,15 @@ pnpm format # format everything with Prettier
# Storybook (run per package)
pnpm --filter @surfnet/react storybook # http://localhost:6006
-pnpm --filter @surfnet/angular storybook # http://localhost:6006
+pnpm --filter @surfnet/angular storybook # http://localhost:6007
```
Each component ships a Storybook story covering its full surface (variants, sizes,
-states). Start there to see what's available.
+states). Start there to see what's available. Both Storybooks are also deployed
+to GitHub Pages on every push to `main`:
+
+- **React** — https://surfnet.github.io/DesignSystem/react/
+- **Angular** — https://surfnet.github.io/DesignSystem/angular/
## Demo app
diff --git a/packages/angular/.storybook/manager.ts b/packages/angular/.storybook/manager.ts
index 662f673..4acf61d 100644
--- a/packages/angular/.storybook/manager.ts
+++ b/packages/angular/.storybook/manager.ts
@@ -1,3 +1,3 @@
import { registerManager } from '@surfnet/storybook-config/manager';
-registerManager();
+registerManager('angular');
diff --git a/packages/angular/.storybook/preview.ts b/packages/angular/.storybook/preview.ts
index 20f3f2b..e949568 100644
--- a/packages/angular/.storybook/preview.ts
+++ b/packages/angular/.storybook/preview.ts
@@ -14,5 +14,17 @@ export default {
initialGlobals: { framework: 'angular', ...themeInitialGlobals },
globalTypes: { ...frameworkGlobalTypes, ...themeGlobalTypes },
decorators: [frameworkSwitcher('angular'), themeSwitcher()],
- parameters: sharedParameters,
+ parameters: {
+ ...sharedParameters,
+ // Must be a literal here (not spread from @surfnet/storybook-config) —
+ // Storybook's index generator reads `options.storySort` via static AST
+ // analysis of this file, it never executes the module. Keep in sync with
+ // packages/react/.storybook/preview.ts.
+ options: {
+ storySort: {
+ method: 'alphabetical',
+ order: ['Foundations', 'Components'],
+ },
+ },
+ },
};
diff --git a/packages/react/.storybook/manager.ts b/packages/react/.storybook/manager.ts
index 662f673..63862db 100644
--- a/packages/react/.storybook/manager.ts
+++ b/packages/react/.storybook/manager.ts
@@ -1,3 +1,3 @@
import { registerManager } from '@surfnet/storybook-config/manager';
-registerManager();
+registerManager('react');
diff --git a/packages/react/.storybook/preview.ts b/packages/react/.storybook/preview.ts
index 0c09272..e2677a4 100644
--- a/packages/react/.storybook/preview.ts
+++ b/packages/react/.storybook/preview.ts
@@ -24,5 +24,15 @@ export default {
// `args` param) is treated as a non-args story, so Storybook prints the whole
// story object instead of the JSX. See @storybook/react's `skipJsxRender`.
docs: { source: { type: 'dynamic' } },
+ // Must be a literal here (not spread from @surfnet/storybook-config) —
+ // Storybook's index generator reads `options.storySort` via static AST
+ // analysis of this file, it never executes the module. Keep in sync with
+ // packages/angular/.storybook/preview.ts.
+ options: {
+ storySort: {
+ method: 'alphabetical',
+ order: ['Foundations', 'Components'],
+ },
+ },
},
};
diff --git a/packages/storybook-config/src/index.ts b/packages/storybook-config/src/index.ts
index 4793ab5..46ffa16 100644
--- a/packages/storybook-config/src/index.ts
+++ b/packages/storybook-config/src/index.ts
@@ -12,6 +12,12 @@ export type {
// Shared preview parameters so every framework's Storybook renders stories the
// same way.
+//
+// `options.storySort` deliberately isn't included here: Storybook's story
+// index generator reads it via static AST analysis of each package's own
+// preview.ts (it never executes the module), so a value reached through this
+// spread is invisible to it — it has to be a literal in every preview.ts.
+// Keep the `storySort` literal below in sync across packages.
export const sharedParameters = {
layout: 'centered',
controls: {
@@ -21,3 +27,17 @@ export const sharedParameters = {
},
},
};
+
+// Pins "Foundations" above "Components" (alphabetical order would otherwise put
+// Components first) and sorts alphabetically within each, so the sidebar reads
+// identically across both frameworks' Storybooks. Copy this literal into each
+// package's `.storybook/preview.ts` — see the comment on `sharedParameters`.
+//
+// parameters: {
+// options: {
+// storySort: {
+// method: 'alphabetical',
+// order: ['Foundations', 'Components'],
+// },
+// },
+// },
diff --git a/packages/storybook-config/src/logo.ts b/packages/storybook-config/src/logo.ts
new file mode 100644
index 0000000..fe7a522
--- /dev/null
+++ b/packages/storybook-config/src/logo.ts
@@ -0,0 +1,13 @@
+// Inlined as a string (rather than an asset import) so it works identically
+// under both the Vite and webpack manager builds without extra loader config.
+const LOGO_SVG = ``;
+
+export const LOGO_DATA_URI = `data:image/svg+xml,${encodeURIComponent(LOGO_SVG)}`;
diff --git a/packages/storybook-config/src/manager.ts b/packages/storybook-config/src/manager.ts
index ba448d3..3dc0052 100644
--- a/packages/storybook-config/src/manager.ts
+++ b/packages/storybook-config/src/manager.ts
@@ -1,14 +1,23 @@
import { addons } from 'storybook/manager-api';
import { create } from 'storybook/theming/create';
+import type { Framework } from './frameworks.js';
+import { LOGO_DATA_URI } from './logo.js';
+
+const FRAMEWORK_TITLES: Record = {
+ react: 'React',
+ angular: 'Angular',
+};
+
// Shared manager (chrome/sidebar) theme so every framework's Storybook is
-// branded identically.
-const theme = create({
- base: 'light',
- brandTitle: 'SURF Design System',
- brandUrl: 'https://www.surf.nl',
-});
-
-export function registerManager(): void {
+// branded identically, save for the framework name in the title.
+export function registerManager(framework: Framework): void {
+ const theme = create({
+ base: 'light',
+ brandTitle: `SURF Design System — ${FRAMEWORK_TITLES[framework]}`,
+ brandUrl: 'https://www.surf.nl',
+ brandImage: LOGO_DATA_URI,
+ });
+
addons.setConfig({ theme });
}
From 85cce7dcc91ae585b2e9eab89b8d0b2fcb60129b Mon Sep 17 00:00:00 2001
From: sjoerdbeentjes <11621275+sjoerdbeentjes@users.noreply.github.com>
Date: Wed, 1 Jul 2026 16:20:18 +0200
Subject: [PATCH 2/2] fix(storybook): shrink logo, show framework name next to
it
The logo was rendering at Storybook's brandImage cap (100px tall). Now
it's a small icon with "Design System - " text next to it,
which requires leaving brandImage unset so Storybook renders brandTitle
as raw HTML instead of plain text. Also trims some overly long comments
and drops a stale commented-out code example.
---
packages/angular/.storybook/preview.ts | 6 ++----
packages/react/.storybook/preview.ts | 6 ++----
packages/storybook-config/src/index.ts | 22 +++-------------------
packages/storybook-config/src/logo.ts | 8 +++-----
packages/storybook-config/src/manager.ts | 17 ++++++++++++++---
5 files changed, 24 insertions(+), 35 deletions(-)
diff --git a/packages/angular/.storybook/preview.ts b/packages/angular/.storybook/preview.ts
index e949568..6a99806 100644
--- a/packages/angular/.storybook/preview.ts
+++ b/packages/angular/.storybook/preview.ts
@@ -16,10 +16,8 @@ export default {
decorators: [frameworkSwitcher('angular'), themeSwitcher()],
parameters: {
...sharedParameters,
- // Must be a literal here (not spread from @surfnet/storybook-config) —
- // Storybook's index generator reads `options.storySort` via static AST
- // analysis of this file, it never executes the module. Keep in sync with
- // packages/react/.storybook/preview.ts.
+ // Must be a literal (Storybook reads it via static analysis, not
+ // execution). Keep in sync with packages/react/.storybook/preview.ts.
options: {
storySort: {
method: 'alphabetical',
diff --git a/packages/react/.storybook/preview.ts b/packages/react/.storybook/preview.ts
index e2677a4..b459c19 100644
--- a/packages/react/.storybook/preview.ts
+++ b/packages/react/.storybook/preview.ts
@@ -24,10 +24,8 @@ export default {
// `args` param) is treated as a non-args story, so Storybook prints the whole
// story object instead of the JSX. See @storybook/react's `skipJsxRender`.
docs: { source: { type: 'dynamic' } },
- // Must be a literal here (not spread from @surfnet/storybook-config) —
- // Storybook's index generator reads `options.storySort` via static AST
- // analysis of this file, it never executes the module. Keep in sync with
- // packages/angular/.storybook/preview.ts.
+ // Must be a literal (Storybook reads it via static analysis, not
+ // execution). Keep in sync with packages/angular/.storybook/preview.ts.
options: {
storySort: {
method: 'alphabetical',
diff --git a/packages/storybook-config/src/index.ts b/packages/storybook-config/src/index.ts
index 46ffa16..ad051ae 100644
--- a/packages/storybook-config/src/index.ts
+++ b/packages/storybook-config/src/index.ts
@@ -13,11 +13,9 @@ export type {
// Shared preview parameters so every framework's Storybook renders stories the
// same way.
//
-// `options.storySort` deliberately isn't included here: Storybook's story
-// index generator reads it via static AST analysis of each package's own
-// preview.ts (it never executes the module), so a value reached through this
-// spread is invisible to it — it has to be a literal in every preview.ts.
-// Keep the `storySort` literal below in sync across packages.
+// `options.storySort` isn't here: Storybook reads it via static analysis of
+// each preview.ts, not by executing this module, so it must be a literal in
+// every preview.ts instead (see those files for the actual value).
export const sharedParameters = {
layout: 'centered',
controls: {
@@ -27,17 +25,3 @@ export const sharedParameters = {
},
},
};
-
-// Pins "Foundations" above "Components" (alphabetical order would otherwise put
-// Components first) and sorts alphabetically within each, so the sidebar reads
-// identically across both frameworks' Storybooks. Copy this literal into each
-// package's `.storybook/preview.ts` — see the comment on `sharedParameters`.
-//
-// parameters: {
-// options: {
-// storySort: {
-// method: 'alphabetical',
-// order: ['Foundations', 'Components'],
-// },
-// },
-// },
diff --git a/packages/storybook-config/src/logo.ts b/packages/storybook-config/src/logo.ts
index fe7a522..be268ed 100644
--- a/packages/storybook-config/src/logo.ts
+++ b/packages/storybook-config/src/logo.ts
@@ -1,6 +1,6 @@
-// Inlined as a string (rather than an asset import) so it works identically
-// under both the Vite and webpack manager builds without extra loader config.
-const LOGO_SVG = `