Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
95d4160
fix(headless): stop menu Escape from bubbling to the parent floating …
alexcarpenter Jul 31, 2026
9653a8f
feat(headless): add alignOffset to Popover.Root
alexcarpenter Jul 31, 2026
bfe1013
feat(headless): hold Popover contents while it closes (#9310)
alexcarpenter Jul 31, 2026
3366a9f
feat(ui): add UserButton account/org switcher
alexcarpenter Jul 16, 2026
6a41d4b
feat(ui): give UserButton its own User section above Organization
alexcarpenter Jul 16, 2026
f2d5042
refactor(ui): move UserButton into its own user-button/ folder
alexcarpenter Jul 16, 2026
1de672e
fix(ui): resolve UserButton lint errors
alexcarpenter Jul 16, 2026
273c049
refactor(ui): compose UserButton view from Mosaic Popover, Card, Item…
alexcarpenter Jul 31, 2026
ff53246
fix(ui): show additional account emails on md rows in UserButton
alexcarpenter Jul 31, 2026
cac185d
fix(ui): drop the popover positioner z-index
alexcarpenter Jul 31, 2026
306b29b
feat(ui): render UserButton accounts as email rows with a menu
alexcarpenter Jul 31, 2026
c72393e
fix(ui): make the UserButton trigger follow the avatar's shape
alexcarpenter Jul 31, 2026
519c2f8
feat(ui): give the Mosaic Popover trigger a themeable slot
alexcarpenter Jul 31, 2026
13ad2f0
refactor(ui): name the Mosaic radius tokens by size
alexcarpenter Jul 31, 2026
33f1ed8
avatar disable dragging
alexcarpenter Jul 31, 2026
9be1134
default item color to neutral faded
alexcarpenter Jul 31, 2026
a120c43
Update item.styles.ts
alexcarpenter Jul 31, 2026
c54d913
feat(ui): add UserButton mode prop for combined, orgs, and user switc…
alexcarpenter Jul 31, 2026
f6b46f5
docs(ui): note UserButton mode in the changeset
alexcarpenter Jul 31, 2026
86722bb
docs(repo): make the swingset UserButton examples live prototypes
alexcarpenter Jul 31, 2026
bbcb512
fix(ui): stop rendering the active UserButton workspace as a button
alexcarpenter Jul 31, 2026
b6a15fc
fix(ui): drop the separator above additional UserButton accounts
alexcarpenter Jul 31, 2026
d2ab847
fix(ui): put every UserButton switch row in one group
alexcarpenter Jul 31, 2026
ff1f564
fix(ui): keep the account at the head of the combined UserButton surface
alexcarpenter Jul 31, 2026
17a3345
feat(ui): list every account's organizations in the UserButton
alexcarpenter Jul 31, 2026
4263f67
fix(ui): give the Avatar fallback the image's radius
alexcarpenter Jul 31, 2026
3010f2c
fix(ui): promote Item icons with the row, on interactive rows only
alexcarpenter Jul 31, 2026
8091fe0
docs(swingset): render Item docs icons with the Icon component
alexcarpenter Jul 31, 2026
4daf002
style(ui): weight the UserButton account row email
alexcarpenter Jul 31, 2026
b1115da
feat(ui): sign the UserButton footer with the Clerk logo
alexcarpenter Jul 31, 2026
d20a90b
style(ui): give the UserButton branding row more breathing room
alexcarpenter Jul 31, 2026
c0300c3
feat(headless): hold Popover contents while it closes (#9310)
alexcarpenter Jul 31, 2026
0b5bc52
refactor(ui): scope the UserButton switcher to fetchable data
alexcarpenter Aug 3, 2026
fd486c0
chore(ui): drop the UserButton changeset
alexcarpenter Aug 3, 2026
49e85f8
fix(ui): follow the StyleX Card migration
alexcarpenter Aug 3, 2026
813f3c3
docs(swingset): follow the Mosaic radius token rename
alexcarpenter Aug 3, 2026
84c37fe
refactor(ui): rename the presentational UserButton to UserButtonView
alexcarpenter Aug 3, 2026
13733ac
feat(ui): add Mosaic Spinner and a spin-delay hook
alexcarpenter Aug 3, 2026
cab33fb
feat(ui): add loading and busy states to UserButton
alexcarpenter Aug 3, 2026
0bbd958
fix(ui): keep pending workspaces reachable and avatars out of row names
alexcarpenter Aug 3, 2026
4ccfd5f
feat(ui): close the UserButton popover only when a workspace is picked
alexcarpenter Aug 3, 2026
be87de7
refactor(ui): migrate the Mosaic Spinner to StyleX and give it a sm size
alexcarpenter Aug 3, 2026
baf2970
feat(ui): rework the account-only UserButton surface
alexcarpenter Aug 3, 2026
0e21a01
refactor(ui): sign out from a labelled header button and check the ac…
alexcarpenter Aug 3, 2026
891c240
docs(swingset): trim the UserButton page
alexcarpenter Aug 3, 2026
b80985c
fix(ui): check the active account in the combined Accounts list, not …
alexcarpenter Aug 3, 2026
02b6b9a
feat(ui): name the active workspace in the UserButton trigger
alexcarpenter Aug 3, 2026
5f67627
refactor(ui): split the UserButton trigger label into two props
alexcarpenter Aug 3, 2026
8aa6218
feat(ui): let combined UserButton lead with the organization or the a…
alexcarpenter Aug 3, 2026
345d106
docs(swingset): drop the Styling section from the UserButton page
alexcarpenter Aug 3, 2026
c9ca7c9
feat(ui): name the active organization from itself, and list accepted…
alexcarpenter Aug 4, 2026
8fb642f
fix(ui): show the UserButton workspace list in one go rather than a l…
alexcarpenter Aug 4, 2026
ae87a55
fix(ui): take the user button trigger corner from its workspace
alexcarpenter Aug 4, 2026
4f72a22
fix(ui): drop the padding from the labelled UserButton trigger
alexcarpenter Aug 4, 2026
cf271c9
refactor(ui): render the UserButton trigger skeleton with StyleX
alexcarpenter Aug 5, 2026
3397233
fix(ui): keep a UserButton row's avatar through its busy state
alexcarpenter Aug 5, 2026
e39db40
feat(ui): scroll the UserButton workspace list through the shared scr…
alexcarpenter Aug 4, 2026
6001e9e
fix(ui): keep the UserButton account row on an account with no organi…
alexcarpenter Aug 4, 2026
debb6d7
fix(ui): keep the UserButton account menu mounted while another actio…
alexcarpenter Aug 4, 2026
461084d
feat(ui): render the UserButton personal workspace row
alexcarpenter Aug 5, 2026
d0bc588
feat(ui): name the UserButton personal row "Personal account"
alexcarpenter Aug 5, 2026
e710de0
fix(ui): withhold the UserButton personal row when handed none
alexcarpenter Aug 5, 2026
492e37f
test(ui): cover the UserButton rendering cases in the view test
alexcarpenter Aug 5, 2026
39d49df
fix(ui): shape the UserButton loading placeholder like the workspace …
alexcarpenter Aug 4, 2026
9fba035
fix(ui): apply the loading placeholder's shape to the block that pain…
alexcarpenter Aug 4, 2026
b7c7e9d
fix(ui): keep a scrolled-into-view workspace row clear of the list's …
alexcarpenter Aug 5, 2026
f00b17c
refactor(ui): name the UserButton account line by identifier, not email
alexcarpenter Aug 5, 2026
0ded411
fix(ui): drop the UserButton identifier line when it repeats the title
alexcarpenter Aug 5, 2026
cc3d79e
fix(repo): rename the swingset UserButton session fixture field to id…
alexcarpenter Aug 5, 2026
5c109ee
fix(ui): use the scroll-padding longhands stylex accepts
alexcarpenter Aug 6, 2026
d90577e
fix(ui): name the active UserButton row as the current one
alexcarpenter Aug 6, 2026
4fb6030
refactor(ui): follow the Mosaic radius token rename in Input
alexcarpenter Aug 6, 2026
836db8d
fix(ui): spin the UserButton join button in place
alexcarpenter Aug 6, 2026
fd14fec
feat(ui): add customMenuItems and menuItemOrder to the UserButton view
alexcarpenter Aug 6, 2026
5e86372
refactor(ui): move the UserButton contract into user-button.types
alexcarpenter Aug 6, 2026
8d1b2ef
refactor(ui): drop the UserButton trigger placeholder
alexcarpenter Aug 6, 2026
2d25475
refactor(ui): drop comments the test names already carry
alexcarpenter Aug 7, 2026
58cc1e7
refactor(ui): name the organization mode in full
alexcarpenter Aug 7, 2026
9f1148b
feat(ui): link the UserButton's Clerk logo and pull its strings into …
alexcarpenter Aug 7, 2026
fd4475a
refactor(ui): rename renderPlanBadge to renderTriggerBadge
alexcarpenter Aug 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/headless-popover-menu.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 2 additions & 0 deletions .changeset/mosaic-user-button-view.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 1 addition & 1 deletion packages/headless/src/primitives/menu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ Accepts all `FloatingArrow` props. `ref` and `context` are injected automaticall
- Nested menus open on hover (75ms delay) with a `safePolygon` safe zone.
- Only one sibling submenu can be open at a time.
- Clicking any item with `closeOnClick={true}` (default) closes the entire menu tree via a tree event.
- `Escape` closes the innermost menu first, bubbling up through the tree.
- `Escape` closes one level: the innermost open menu, leaving its parent — a parent menu, or a `Popover` the menu is rendered inside — open. Pressing it again closes the next level up. An outside press is the opposite: it dismisses the whole stack at once.

## Important Notes

Expand Down
4 changes: 3 additions & 1 deletion packages/headless/src/primitives/menu/menu-root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ function MenuInner(props: MenuProps) {
delete reference.role;
return { ...baseRole, reference };
}, [baseRole, isNested]);
const dismiss = useDismiss(floatingContext, { bubbles: true });
// Escape must not bubble: it closes this menu and leaves whatever it sits inside — a parent menu,
// or a popover — open. An outside press is the opposite, and dismisses the whole stack.
const dismiss = useDismiss(floatingContext, { bubbles: { escapeKey: false, outsidePress: true } });
const listNavigation = useListNavigation(floatingContext, {
listRef: elementsRef,
activeIndex,
Expand Down
74 changes: 74 additions & 0 deletions packages/headless/src/primitives/menu/menu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,80 @@ describe('Menu', () => {

expect(onClick).toHaveBeenCalledTimes(1);
});

it('Escape closes only the submenu', async () => {
const user = userEvent.setup();
render(
<Menu.Root>
<Menu.Trigger>Actions</Menu.Trigger>
<Menu.Positioner>
<Menu.Popup>
<Menu.Root>
<Menu.Trigger>Share</Menu.Trigger>
<Menu.Positioner>
<Menu.Popup>
<Menu.Item label='Email'>Email</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Root>
</Menu.Popup>
</Menu.Positioner>
</Menu.Root>,
);

await user.click(screen.getByText('Actions'));
await new Promise(r => requestAnimationFrame(r));
await user.keyboard('{ArrowDown}');
await user.keyboard('{ArrowRight}');
await user.keyboard('{Escape}');

expect(screen.getByText('Share')).toHaveAttribute('data-closed', '');
expect(screen.getByText('Actions')).toHaveAttribute('data-open', '');
});
});

describe('inside a popover', () => {
function renderMenuInPopover() {
return render(
<Popover.Root defaultOpen>
<Popover.Trigger>Open popover</Popover.Trigger>
<Popover.Positioner>
<Popover.Popup>
<Menu.Root>
<Menu.Trigger>Actions</Menu.Trigger>
<Menu.Positioner>
<Menu.Popup>
<Menu.Item label='Cut'>Cut</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Root>
</Popover.Popup>
</Popover.Positioner>
</Popover.Root>,
);
}

it('Escape closes only the menu', async () => {
const user = userEvent.setup();
renderMenuInPopover();

await user.click(screen.getByText('Actions'));
await user.keyboard('{Escape}');

expect(screen.getByText('Actions')).toHaveAttribute('data-closed', '');
expect(screen.getByText('Open popover')).toHaveAttribute('data-open', '');
});

it('Escape closes the popover once the menu is closed', async () => {
const user = userEvent.setup();
renderMenuInPopover();

await user.click(screen.getByText('Actions'));
await user.keyboard('{Escape}');
await user.keyboard('{Escape}');

expect(screen.getByText('Open popover')).toHaveAttribute('data-closed', '');
});
});

describe('positioner', () => {
Expand Down
18 changes: 10 additions & 8 deletions packages/headless/src/primitives/popover/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,15 @@ const [open, setOpen] = useState(false);

### `Popover.Root`

| Prop | Type | Default | Description |
| -------------- | ------------------------- | ---------- | ---------------------------------- |
| `open` | `boolean` | — | Controlled open state |
| `defaultOpen` | `boolean` | `false` | Initial open state (uncontrolled) |
| `onOpenChange` | `(open: boolean) => void` | — | Called when open state changes |
| `placement` | `Placement` | `"bottom"` | Floating UI placement |
| `sideOffset` | `number` | `4` | Gap between trigger and popup (px) |
| `modal` | `boolean` | `false` | Traps focus within the popover |
| Prop | Type | Default | Description |
| -------------- | ------------------------- | ---------- | ----------------------------------- |
| `open` | `boolean` | — | Controlled open state |
| `defaultOpen` | `boolean` | `false` | Initial open state (uncontrolled) |
| `onOpenChange` | `(open: boolean) => void` | — | Called when open state changes |
| `placement` | `Placement` | `"bottom"` | Floating UI placement |
| `sideOffset` | `number` | `4` | Gap between trigger and popup (px) |
| `alignOffset` | `number` | `0` | Nudge along the alignment axis (px) |
| `modal` | `boolean` | `false` | Traps focus within the popover |

### `Popover.Trigger`, `Popover.Positioner`, `Popover.Popup`, `Popover.Title`, `Popover.Description`, `Popover.Close`

Expand Down Expand Up @@ -104,6 +105,7 @@ Middleware stack: `offset` -> `flip` -> `shift` -> `arrow` -> CSS vars. The popu
- **Title and Description are optional but recommended.** They wire `aria-labelledby` and `aria-describedby` to the positioner. If omitted, those attributes are simply absent.
- **Non-modal by default.** Unlike Dialog, the page remains interactive behind the popover. Set `modal={true}` for a stricter focus trap.
- **Nested popovers are supported.** The `FloatingTree` pattern handles nesting automatically.
- **Popup contents freeze while closing.** The popup outlives `open` by its exit animation, so its children are wrapped in `Freeze` (`@clerk/headless/utils`) and hold their last frame instead of re-rendering under the animation. The popup element itself keeps updating, so `data-closed` / `data-ending-style` still land. Freezing wraps the children in a `display: contents` element and detaches refs inside them until the popup reopens.

## ARIA

Expand Down
11 changes: 8 additions & 3 deletions packages/headless/src/primitives/popover/popover-popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@

import React from 'react';

import { type ComponentProps, mergeProps, useRender } from '../../utils';
import { type ComponentProps, Freeze, mergeProps, useRender } from '../../utils';
import { usePopoverContext } from './popover-context';

export type PopoverPopupProps = ComponentProps<'div'>;

export const PopoverPopup = React.forwardRef<HTMLDivElement, PopoverPopupProps>(function PopoverPopup(props, ref) {
const { render, ...otherProps } = props;
const { popupRef, transitionProps } = usePopoverContext();
const { render, children, ...otherProps } = props;
const { open, popupRef, transitionProps } = usePopoverContext();

const defaultProps = {
...transitionProps,
// The popup outlives `open` by the length of its exit animation. Whatever closed it has
// usually changed the data behind it (switching account, picking an item), so the contents
// hold their last frame on the way out instead of swapping under the animation. The popup
// element itself stays live, so `data-closed` / `data-ending-style` still land.
children: <Freeze frozen={!open}>{children}</Freeze>,
};

return useRender({
Expand Down
12 changes: 10 additions & 2 deletions packages/headless/src/primitives/popover/popover-root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface PopoverProps {
onOpenChange?: (open: boolean) => void;
placement?: Placement;
sideOffset?: number;
alignOffset?: number;
modal?: boolean;
/**
* Where focus lands when the popup opens.
Expand All @@ -47,7 +48,14 @@ export interface PopoverProps {

function PopoverInner(props: PopoverProps) {
const nodeId = useFloatingNodeId();
const { placement: placementProp = 'bottom', sideOffset = 4, modal = false, initialFocus = 'auto', children } = props;
const {
placement: placementProp = 'bottom',
sideOffset = 4,
alignOffset = 0,
modal = false,
initialFocus = 'auto',
children,
} = props;

const [open, setOpen] = useControllableState(props.open, props.defaultOpen ?? false, props.onOpenChange);

Expand All @@ -71,7 +79,7 @@ function PopoverInner(props: PopoverProps) {
onOpenChange: setOpen,
placement: placementProp,
middleware: [
offset(sideOffset),
offset({ mainAxis: sideOffset, alignmentAxis: alignOffset }),
flip({
crossAxis: placementProp.includes('-'),
fallbackAxisSideDirection: 'end',
Expand Down
72 changes: 72 additions & 0 deletions packages/headless/src/utils/freeze.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { cleanup, render, screen } from '@testing-library/react';
import { afterEach, describe, expect, it } from 'vitest';

import { Freeze } from './freeze';

afterEach(() => {
cleanup();
});

describe('Freeze', () => {
it('renders children while not frozen', () => {
render(<Freeze frozen={false}>Acme</Freeze>);

expect(screen.getByText('Acme')).toBeInTheDocument();
});

it('holds the committed DOM when children change while frozen', () => {
const { rerender } = render(<Freeze frozen={false}>Acme</Freeze>);

rerender(<Freeze frozen>Globex</Freeze>);

expect(screen.getByText('Acme')).toBeInTheDocument();
expect(screen.queryByText('Globex')).toBeNull();
});

it('keeps the held DOM visible', () => {
const { rerender } = render(<Freeze frozen={false}>Acme</Freeze>);

rerender(<Freeze frozen>Globex</Freeze>);

expect(screen.getByText('Acme')).toBeVisible();
});

it('keeps the held DOM visible across further updates while frozen', () => {
const { rerender } = render(<Freeze frozen={false}>Acme</Freeze>);

rerender(<Freeze frozen>Globex</Freeze>);
rerender(<Freeze frozen>Initech</Freeze>);

expect(screen.getByText('Acme')).toBeVisible();
});

it('commits the pending children once unfrozen', () => {
const { rerender } = render(<Freeze frozen={false}>Acme</Freeze>);

rerender(<Freeze frozen>Globex</Freeze>);
rerender(<Freeze frozen={false}>Globex</Freeze>);

expect(screen.getByText('Globex')).toBeInTheDocument();
expect(screen.queryByText('Acme')).toBeNull();
});

it('holds state updates raised from inside the frozen subtree', () => {
function Counter({ count }: { count: number }) {
return <span>count: {count}</span>;
}

const { rerender } = render(
<Freeze frozen={false}>
<Counter count={0} />
</Freeze>,
);

rerender(
<Freeze frozen>
<Counter count={1} />
</Freeze>,
);

expect(screen.getByText('count: 0')).toBeInTheDocument();
});
});
64 changes: 64 additions & 0 deletions packages/headless/src/utils/freeze.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
'use client';

import * as React from 'react';

/**
* Never settles. Throwing it suspends the enclosing boundary indefinitely: React keeps
* rendering the subtree but holds the commit, so the DOM keeps painting its last frame.
*/
const never = new Promise<never>(() => {});

function Suspend(): null {
// eslint-disable-next-line @typescript-eslint/only-throw-error -- Suspending is React's thrown-thenable protocol, not an error. `React.use()` would say this more plainly but needs React 19.2; this package supports React 18.
throw never;
}

export interface FreezeProps {
/** While `true`, the DOM below holds whatever it last committed. */
frozen: boolean;
children?: React.ReactNode;
}

/**
* Holds its subtree's DOM at the last committed frame while `frozen`. Renders keep
* happening, they just don't reach the DOM; the pending one commits when `frozen` flips
* back to `false`.
*
* Use it to stop content from visibly changing under an exit animation — a popover that
* closes because the thing it was showing changed would otherwise swap its contents on the
* way out.
*/
export function Freeze({ frozen, children }: FreezeProps) {
const contentRef = React.useRef<HTMLDivElement | null>(null);

// Hold onto the node ourselves rather than reading a plain ref: hiding a boundary's children
// detaches their refs, so by the time the effect below runs a normal ref reads `null`.
const setContent = React.useCallback((node: HTMLDivElement | null) => {
if (node) {
contentRef.current = node;
}
}, []);

// React hides a suspended boundary's host children with `display: none !important`, which is
// the opposite of what this is for. Undo it on the commit that applies it: insertion effects
// run after the boundary's mutation and before paint, so the held frame never blinks out.
// `display: contents` is also what the wrapper renders with, so React puts it back on unfreeze
// and the wrapper stays out of the layout it is spliced into.
React.useInsertionEffect(() => {
if (frozen) {
contentRef.current?.style.setProperty('display', 'contents');
}
}, [frozen]);

return (
<React.Suspense fallback={null}>
{frozen ? <Suspend /> : null}
<div
ref={setContent}
style={{ display: 'contents' }}
>
{children}
</div>
</React.Suspense>
);
}
1 change: 1 addition & 0 deletions packages/headless/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { cssVars } from './css-vars';
export { Freeze, type FreezeProps } from './freeze';
export { isKeyboardEvent, isKeyboardOpen } from './interaction-modality';
export { resetLayoutStyles } from './reset-layout-styles';
export {
Expand Down
3 changes: 3 additions & 0 deletions packages/swingset/src/components/DocsViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { ViewSource } from './ViewSource';
// MDX docs keyed by `group` slug → `component` slug. Group-aware so identically-named
// entries (the headless `Dialog` primitive vs. the styled `Dialog` component) stay distinct.
const docModules: Record<string, Record<string, React.ComponentType>> = {
user: {
'user-button': dynamic(() => import('../stories/user-button.mdx')),
},
organization: {
'organization-profile': dynamic(() => import('../stories/organization-profile.mdx')),
'organization-profile-general-panel': dynamic(() => import('../stories/organization-profile-general-panel.mdx')),
Expand Down
15 changes: 15 additions & 0 deletions packages/swingset/src/lib/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ import {
} from '../stories/text.stories';
import { meta as tooltipMeta } from '../stories/tooltip.stories';
import { meta as useDataTableMeta } from '../stories/use-data-table.stories';
import {
Combined as UserButtonCombined,
meta as userButtonMeta,
Organizations as UserButtonOrganizations,
User as UserButtonUser,
} from '../stories/user-button.stories';
Comment thread
coderabbitai[bot] marked this conversation as resolved.
import { toSlug } from './slug';
import type { StoryModule } from './types';

Expand Down Expand Up @@ -187,6 +193,13 @@ const itemModule: StoryModule = {
Scrolling: ItemScrolling,
};

const userButtonModule: StoryModule = {
meta: userButtonMeta,
Combined: UserButtonCombined,
Organizations: UserButtonOrganizations,
User: UserButtonUser,
};

const headingModule: StoryModule = {
meta: headingMeta,
Default: HeadingDefault,
Expand Down Expand Up @@ -237,6 +250,8 @@ const scrollAreaModule: StoryModule = {
const useDataTableModule: StoryModule = { meta: useDataTableMeta };

export const registry: StoryModule[] = [
// User
userButtonModule,
// Organization
organizationProfileModule,
organizationProfileGeneralPanelModule,
Expand Down
Loading
Loading