@beydesign/storybook 0.2.24 → 0.2.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,7 @@ import { DrawerNavItem } from './DrawerNavItem';
6
6
  import { DrawerCreditsCard } from './DrawerCreditsCard';
7
7
  import { DrawerCreditsCardSkeleton } from './DrawerCreditsCardSkeleton';
8
8
  import { DrawerProfile } from './DrawerProfile';
9
+ import { DrawerProfileSkeleton } from './DrawerProfileSkeleton';
9
10
  import { Logo, LogoVariant } from '../UI';
10
11
  import { getIconComponent } from '../../utils';
11
12
  const EXPANDED_WIDTH = '238px';
@@ -137,7 +138,7 @@ const resolveState = (item, activeKey) => {
137
138
  * uncontrolled (`defaultCollapsed`). Every colour, space and radius resolves to
138
139
  * `@beydesign/tokens` CSS variables, so it renders correctly in light and dark.
139
140
  */
140
- export const Drawer = ({ navItems, activeKey, collapsed, defaultCollapsed = false, onToggle, showToggle = true, credits, creditsLoading = false, profile, logo, className, style, testId, }) => {
141
+ export const Drawer = ({ navItems, activeKey, collapsed, defaultCollapsed = false, onToggle, showToggle = true, credits, creditsLoading = false, profile, profileLoading = false, logo, className, style, testId, }) => {
141
142
  const isControlled = collapsed !== undefined;
142
143
  const [internalCollapsed, setInternalCollapsed] = useState(defaultCollapsed);
143
144
  const isCollapsed = isControlled ? collapsed : internalCollapsed;
@@ -158,5 +159,5 @@ export const Drawer = ({ navItems, activeKey, collapsed, defaultCollapsed = fals
158
159
  item.onClick?.();
159
160
  }
160
161
  };
161
- return (_jsxs(Shell, { "$collapsed": isCollapsed, className: className, style: style, "data-testid": testId, children: [_jsxs(Scroll, { children: [_jsx(LogoHeader, { "$collapsed": isCollapsed, children: _jsx(Logo, { variant: isCollapsed ? LogoVariant.Icon : LogoVariant.Horizontal, color: logo?.color ?? 'var(--color-foreground-fg-brand-logo)', gradient: logo?.gradient, svgUrl: logo?.svgUrl }) }), _jsxs(Nav, { children: [_jsx(NavTop, { children: navItems.map((item) => (_jsx(DrawerNavItem, { text: item.text, icon: item.icon, state: resolveState(item, activeKey), size: drawerSize, hierarchy: item.hierarchy ?? DrawerNavItemHierarchy.First, showBadge: item.showBadge, badgeText: item.badgeText, onClick: () => handleItemClick(item) }, item.key ?? item.text))) }), (credits || creditsLoading || profile) && (_jsxs(Footer, { children: [(credits || creditsLoading) && (_jsx(FooterSlot, { "$collapsed": isCollapsed, children: creditsLoading ? (_jsx(DrawerCreditsCardSkeleton, { size: drawerSize })) : (_jsx(DrawerCreditsCard, { ...credits, size: drawerSize })) })), profile && (_jsx(FooterSlot, { "$collapsed": isCollapsed, children: _jsx(DrawerProfile, { ...profile, size: drawerSize }) }))] }))] })] }), showToggle && (_jsx(ToggleButton, { type: "button", onClick: handleToggle, "aria-label": isCollapsed ? 'Expand sidebar' : 'Collapse sidebar', children: _jsx(ToggleIcon, { "$collapsed": isCollapsed, children: getIconComponent('CaretLeft', { size: 16, weight: 'bold' }) }) }))] }));
162
+ return (_jsxs(Shell, { "$collapsed": isCollapsed, className: className, style: style, "data-testid": testId, children: [_jsxs(Scroll, { children: [_jsx(LogoHeader, { "$collapsed": isCollapsed, children: _jsx(Logo, { variant: isCollapsed ? LogoVariant.Icon : LogoVariant.Horizontal, color: logo?.color ?? 'var(--color-foreground-fg-brand-logo)', gradient: logo?.gradient, svgUrl: logo?.svgUrl }) }), _jsxs(Nav, { children: [_jsx(NavTop, { children: navItems.map((item) => (_jsx(DrawerNavItem, { text: item.text, icon: item.icon, state: resolveState(item, activeKey), size: drawerSize, hierarchy: item.hierarchy ?? DrawerNavItemHierarchy.First, showBadge: item.showBadge, badgeText: item.badgeText, onClick: () => handleItemClick(item) }, item.key ?? item.text))) }), (credits || creditsLoading || profile || profileLoading) && (_jsxs(Footer, { children: [(credits || creditsLoading) && (_jsx(FooterSlot, { "$collapsed": isCollapsed, children: creditsLoading ? (_jsx(DrawerCreditsCardSkeleton, { size: drawerSize })) : (_jsx(DrawerCreditsCard, { ...credits, size: drawerSize })) })), (profile || profileLoading) && (_jsx(FooterSlot, { "$collapsed": isCollapsed, children: profileLoading ? (_jsx(DrawerProfileSkeleton, { size: drawerSize })) : (profile && _jsx(DrawerProfile, { ...profile, size: drawerSize })) }))] }))] })] }), showToggle && (_jsx(ToggleButton, { type: "button", onClick: handleToggle, "aria-label": isCollapsed ? 'Expand sidebar' : 'Collapse sidebar', children: _jsx(ToggleIcon, { "$collapsed": isCollapsed, children: getIconComponent('CaretLeft', { size: 16, weight: 'bold' }) }) }))] }));
162
163
  };
@@ -8,4 +8,7 @@ export declare const Collapsed: Story;
8
8
  export declare const WithSubItems: Story;
9
9
  export declare const CreditsLoading: Story;
10
10
  export declare const CreditsLoadingCollapsed: Story;
11
+ export declare const ProfileLoading: Story;
12
+ export declare const ProfileLoadingCollapsed: Story;
13
+ export declare const FooterLoading: Story;
11
14
  export declare const NoFooter: Story;
@@ -47,6 +47,11 @@ const meta = {
47
47
  description: 'Render the credits card as a loading skeleton',
48
48
  table: { defaultValue: { summary: 'false' } },
49
49
  },
50
+ profileLoading: {
51
+ control: 'boolean',
52
+ description: 'Render the profile footer as a loading skeleton',
53
+ table: { defaultValue: { summary: 'false' } },
54
+ },
50
55
  navItems: { control: false },
51
56
  credits: { control: false },
52
57
  profile: { control: false },
@@ -113,6 +118,15 @@ export const CreditsLoading = {
113
118
  export const CreditsLoadingCollapsed = {
114
119
  args: { creditsLoading: true, defaultCollapsed: true },
115
120
  };
121
+ export const ProfileLoading = {
122
+ args: { profileLoading: true },
123
+ };
124
+ export const ProfileLoadingCollapsed = {
125
+ args: { profileLoading: true, defaultCollapsed: true },
126
+ };
127
+ export const FooterLoading = {
128
+ args: { creditsLoading: true, profileLoading: true },
129
+ };
116
130
  export const NoFooter = {
117
131
  args: { credits: undefined, profile: undefined },
118
132
  };
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { DrawerProfileSkeletonProps } from './types';
3
+ /**
4
+ * Loading placeholder for {@link DrawerProfile}. Reproduces the footer's avatar
5
+ * plus name and email lines (Expanded), or just the avatar circle (Collapsed),
6
+ * so it occupies the same footprint while the user data is being fetched. Fully
7
+ * theme-aware.
8
+ */
9
+ export declare const DrawerProfileSkeleton: React.FC<DrawerProfileSkeletonProps>;
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import styled from '@emotion/styled';
3
+ import { SkeletonLoader } from '../UI';
4
+ import { DrawerSize } from './types';
5
+ /**
6
+ * Mirrors {@link DrawerProfile}'s clickable Row so the skeleton snaps in place:
7
+ * same 8px padding, gap and alignment as the profile footer's default (menu)
8
+ * usage.
9
+ */
10
+ const Row = styled.div `
11
+ box-sizing: border-box;
12
+ display: flex;
13
+ align-items: center;
14
+ gap: var(--spacing-tokens-size-in-px-spacing-spacing-md, 8px);
15
+ width: 100%;
16
+ padding: var(--spacing-tokens-size-in-px-spacing-spacing-md, 8px);
17
+ `;
18
+ const TextColumn = styled.div `
19
+ display: flex;
20
+ flex-direction: column;
21
+ gap: var(--spacing-tokens-size-in-px-spacing-spacing-xs, 4px);
22
+ min-width: 0;
23
+ `;
24
+ const RADIUS_XS = 'var(--spacing-tokens-size-in-px-radius-radius-xs, 4px)';
25
+ const RADIUS_FULL = 'var(--spacing-tokens-size-in-px-radius-radius-full, 999px)';
26
+ /**
27
+ * Loading placeholder for {@link DrawerProfile}. Reproduces the footer's avatar
28
+ * plus name and email lines (Expanded), or just the avatar circle (Collapsed),
29
+ * so it occupies the same footprint while the user data is being fetched. Fully
30
+ * theme-aware.
31
+ */
32
+ export const DrawerProfileSkeleton = ({ size = DrawerSize.Expanded, className, style, testId, }) => {
33
+ if (size === DrawerSize.Collapsed) {
34
+ return (_jsx(SkeletonLoader, { width: "32px", height: "32px", borderRadius: RADIUS_FULL, style: style }));
35
+ }
36
+ return (_jsxs(Row, { className: className, style: style, "data-testid": testId, children: [_jsx(SkeletonLoader, { width: "32px", height: "32px", borderRadius: RADIUS_FULL }), _jsxs(TextColumn, { children: [_jsx(SkeletonLoader, { width: "96px", height: "14px", borderRadius: RADIUS_XS }), _jsx(SkeletonLoader, { width: "128px", height: "12px", borderRadius: RADIUS_XS })] })] }));
37
+ };
@@ -0,0 +1,7 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { DrawerProfileSkeleton } from './DrawerProfileSkeleton';
3
+ declare const meta: Meta<typeof DrawerProfileSkeleton>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof DrawerProfileSkeleton>;
6
+ export declare const Default: Story;
7
+ export declare const Collapsed: Story;
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { DrawerProfileSkeleton } from './DrawerProfileSkeleton';
3
+ import { DrawerSize } from './types';
4
+ const meta = {
5
+ title: 'Design System/Components/Drawer/DrawerProfileSkeleton',
6
+ component: DrawerProfileSkeleton,
7
+ parameters: {
8
+ layout: 'centered',
9
+ design: {
10
+ type: 'figspec',
11
+ url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=6940-28398',
12
+ },
13
+ },
14
+ tags: ['autodocs'],
15
+ argTypes: {
16
+ size: {
17
+ control: 'inline-radio',
18
+ options: Object.values(DrawerSize),
19
+ table: { defaultValue: { summary: 'Expanded' } },
20
+ },
21
+ },
22
+ args: {
23
+ size: DrawerSize.Expanded,
24
+ },
25
+ decorators: [
26
+ (Story) => (_jsx("div", { style: {
27
+ width: 230,
28
+ padding: 16,
29
+ backgroundColor: 'var(--color-background-bg-page-contrast)',
30
+ }, children: _jsx(Story, {}) })),
31
+ ],
32
+ };
33
+ export default meta;
34
+ export const Default = {};
35
+ export const Collapsed = {
36
+ args: { size: DrawerSize.Collapsed },
37
+ };
@@ -3,5 +3,6 @@ export * from './DrawerNavItem';
3
3
  export * from './DrawerCreditsCard';
4
4
  export * from './DrawerCreditsCardSkeleton';
5
5
  export * from './DrawerProfile';
6
+ export * from './DrawerProfileSkeleton';
6
7
  export * from './DrawerIcons';
7
8
  export * from './types';
@@ -3,5 +3,6 @@ export * from './DrawerNavItem';
3
3
  export * from './DrawerCreditsCard';
4
4
  export * from './DrawerCreditsCardSkeleton';
5
5
  export * from './DrawerProfile';
6
+ export * from './DrawerProfileSkeleton';
6
7
  export * from './DrawerIcons';
7
8
  export * from './types';
@@ -124,6 +124,16 @@ export interface DrawerProfileProps {
124
124
  style?: CSSProperties;
125
125
  testId?: string;
126
126
  }
127
+ /**
128
+ * Props for the {@link DrawerProfileSkeleton} loading placeholder.
129
+ */
130
+ export interface DrawerProfileSkeletonProps {
131
+ /** Expanded (avatar + text lines) or Collapsed (avatar circle only). */
132
+ size?: DrawerSize;
133
+ className?: string;
134
+ style?: CSSProperties;
135
+ testId?: string;
136
+ }
127
137
  /**
128
138
  * A single entry in the {@link Drawer}'s `navItems` list.
129
139
  */
@@ -164,6 +174,8 @@ export interface DrawerProps {
164
174
  creditsLoading?: boolean;
165
175
  /** User profile footer configuration. Omit to hide the footer. */
166
176
  profile?: Omit<DrawerProfileProps, 'size' | 'className' | 'style'>;
177
+ /** Render the profile footer as a loading skeleton instead of its content. */
178
+ profileLoading?: boolean;
167
179
  /** Logo appearance overrides. */
168
180
  logo?: {
169
181
  color?: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@beydesign/storybook",
3
3
  "private": false,
4
- "version": "0.2.24",
4
+ "version": "0.2.25",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",