@beydesign/storybook 0.2.23 → 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.
@@ -4,7 +4,9 @@ import styled from '@emotion/styled';
4
4
  import { DrawerSize, DrawerNavItemState, DrawerNavItemHierarchy, } from './types';
5
5
  import { DrawerNavItem } from './DrawerNavItem';
6
6
  import { DrawerCreditsCard } from './DrawerCreditsCard';
7
+ import { DrawerCreditsCardSkeleton } from './DrawerCreditsCardSkeleton';
7
8
  import { DrawerProfile } from './DrawerProfile';
9
+ import { DrawerProfileSkeleton } from './DrawerProfileSkeleton';
8
10
  import { Logo, LogoVariant } from '../UI';
9
11
  import { getIconComponent } from '../../utils';
10
12
  const EXPANDED_WIDTH = '238px';
@@ -136,7 +138,7 @@ const resolveState = (item, activeKey) => {
136
138
  * uncontrolled (`defaultCollapsed`). Every colour, space and radius resolves to
137
139
  * `@beydesign/tokens` CSS variables, so it renders correctly in light and dark.
138
140
  */
139
- export const Drawer = ({ navItems, activeKey, collapsed, defaultCollapsed = false, onToggle, showToggle = true, credits, 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, }) => {
140
142
  const isControlled = collapsed !== undefined;
141
143
  const [internalCollapsed, setInternalCollapsed] = useState(defaultCollapsed);
142
144
  const isCollapsed = isControlled ? collapsed : internalCollapsed;
@@ -157,5 +159,5 @@ export const Drawer = ({ navItems, activeKey, collapsed, defaultCollapsed = fals
157
159
  item.onClick?.();
158
160
  }
159
161
  };
160
- 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 || profile) && (_jsxs(Footer, { children: [credits && (_jsx(FooterSlot, { "$collapsed": isCollapsed, children: _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' }) }) }))] }));
161
163
  };
@@ -6,4 +6,9 @@ type Story = StoryObj<typeof Drawer>;
6
6
  export declare const Expanded: Story;
7
7
  export declare const Collapsed: Story;
8
8
  export declare const WithSubItems: Story;
9
+ export declare const CreditsLoading: Story;
10
+ export declare const CreditsLoadingCollapsed: Story;
11
+ export declare const ProfileLoading: Story;
12
+ export declare const ProfileLoadingCollapsed: Story;
13
+ export declare const FooterLoading: Story;
9
14
  export declare const NoFooter: Story;
@@ -42,6 +42,16 @@ const meta = {
42
42
  description: 'Show the floating collapse/expand toggle',
43
43
  table: { defaultValue: { summary: 'true' } },
44
44
  },
45
+ creditsLoading: {
46
+ control: 'boolean',
47
+ description: 'Render the credits card as a loading skeleton',
48
+ table: { defaultValue: { summary: 'false' } },
49
+ },
50
+ profileLoading: {
51
+ control: 'boolean',
52
+ description: 'Render the profile footer as a loading skeleton',
53
+ table: { defaultValue: { summary: 'false' } },
54
+ },
45
55
  navItems: { control: false },
46
56
  credits: { control: false },
47
57
  profile: { control: false },
@@ -102,6 +112,21 @@ export const WithSubItems = {
102
112
  ],
103
113
  },
104
114
  };
115
+ export const CreditsLoading = {
116
+ args: { creditsLoading: true },
117
+ };
118
+ export const CreditsLoadingCollapsed = {
119
+ args: { creditsLoading: true, defaultCollapsed: true },
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
+ };
105
130
  export const NoFooter = {
106
131
  args: { credits: undefined, profile: undefined },
107
132
  };
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { DrawerCreditsCardSkeletonProps } from './types';
3
+ /**
4
+ * Loading placeholder for {@link DrawerCreditsCard}. Reproduces the card's
5
+ * gem box, title, info icon, progress bar and credits caption (Expanded), or
6
+ * the single gem box (Collapsed), so it occupies the same footprint while the
7
+ * credits data is being fetched. Fully theme-aware.
8
+ */
9
+ export declare const DrawerCreditsCardSkeleton: React.FC<DrawerCreditsCardSkeletonProps>;
@@ -0,0 +1,49 @@
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
+ /** Mirrors {@link DrawerCreditsCard}'s Card so the skeleton snaps in place. */
6
+ const Card = styled.div `
7
+ box-sizing: border-box;
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: var(--spacing-tokens-size-in-px-spacing-spacing-xs, 4px);
11
+ width: 100%;
12
+ padding: var(--spacing-tokens-size-in-px-spacing-spacing-md, 8px)
13
+ var(--spacing-tokens-size-in-px-spacing-spacing-lg, 12px);
14
+ background-color: var(--color-background-bg-card-highlight);
15
+ border: 1px solid var(--color-border-border-divider);
16
+ border-radius: var(--spacing-tokens-size-in-px-spacing-spacing-md, 8px);
17
+ `;
18
+ const HeaderRow = styled.div `
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: space-between;
22
+ width: 100%;
23
+ `;
24
+ const HeaderLeft = styled.div `
25
+ display: flex;
26
+ align-items: center;
27
+ gap: var(--spacing-tokens-size-in-px-spacing-spacing-md, 8px);
28
+ `;
29
+ const ProgressRow = styled.div `
30
+ display: flex;
31
+ align-items: center;
32
+ height: 20px;
33
+ width: 100%;
34
+ `;
35
+ const RADIUS_XS = 'var(--spacing-tokens-size-in-px-radius-radius-xs, 4px)';
36
+ const RADIUS_FULL = 'var(--spacing-tokens-size-in-px-radius-radius-full, 999px)';
37
+ const RADIUS_BOX = 'var(--spacing-tokens-size-in-px-spacing-spacing-md, 8px)';
38
+ /**
39
+ * Loading placeholder for {@link DrawerCreditsCard}. Reproduces the card's
40
+ * gem box, title, info icon, progress bar and credits caption (Expanded), or
41
+ * the single gem box (Collapsed), so it occupies the same footprint while the
42
+ * credits data is being fetched. Fully theme-aware.
43
+ */
44
+ export const DrawerCreditsCardSkeleton = ({ size = DrawerSize.Expanded, className, style, testId }) => {
45
+ if (size === DrawerSize.Collapsed) {
46
+ return (_jsx(SkeletonLoader, { width: "36px", height: "36px", borderRadius: RADIUS_BOX, style: style }));
47
+ }
48
+ return (_jsxs(Card, { className: className, style: style, "data-testid": testId, children: [_jsxs(HeaderRow, { children: [_jsxs(HeaderLeft, { children: [_jsx(SkeletonLoader, { width: "36px", height: "36px", borderRadius: RADIUS_BOX }), _jsx(SkeletonLoader, { width: "96px", height: "20px", borderRadius: RADIUS_XS })] }), _jsx(SkeletonLoader, { width: "20px", height: "20px", borderRadius: RADIUS_FULL })] }), _jsx(ProgressRow, { children: _jsx(SkeletonLoader, { width: "100%", height: "4px", borderRadius: RADIUS_XS }) }), _jsx(SkeletonLoader, { width: "60%", height: "16px", borderRadius: RADIUS_XS })] }));
49
+ };
@@ -0,0 +1,7 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { DrawerCreditsCardSkeleton } from './DrawerCreditsCardSkeleton';
3
+ declare const meta: Meta<typeof DrawerCreditsCardSkeleton>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof DrawerCreditsCardSkeleton>;
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 { DrawerCreditsCardSkeleton } from './DrawerCreditsCardSkeleton';
3
+ import { DrawerSize } from './types';
4
+ const meta = {
5
+ title: 'Design System/Components/Drawer/DrawerCreditsCardSkeleton',
6
+ component: DrawerCreditsCardSkeleton,
7
+ parameters: {
8
+ layout: 'centered',
9
+ design: {
10
+ type: 'figspec',
11
+ url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=6940-29126',
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
+ };
@@ -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
+ };
@@ -1,6 +1,8 @@
1
1
  export * from './Drawer';
2
2
  export * from './DrawerNavItem';
3
3
  export * from './DrawerCreditsCard';
4
+ export * from './DrawerCreditsCardSkeleton';
4
5
  export * from './DrawerProfile';
6
+ export * from './DrawerProfileSkeleton';
5
7
  export * from './DrawerIcons';
6
8
  export * from './types';
@@ -1,6 +1,8 @@
1
1
  export * from './Drawer';
2
2
  export * from './DrawerNavItem';
3
3
  export * from './DrawerCreditsCard';
4
+ export * from './DrawerCreditsCardSkeleton';
4
5
  export * from './DrawerProfile';
6
+ export * from './DrawerProfileSkeleton';
5
7
  export * from './DrawerIcons';
6
8
  export * from './types';
@@ -77,6 +77,16 @@ export interface DrawerCreditsCardProps {
77
77
  style?: CSSProperties;
78
78
  testId?: string;
79
79
  }
80
+ /**
81
+ * Props for the {@link DrawerCreditsCardSkeleton} loading placeholder.
82
+ */
83
+ export interface DrawerCreditsCardSkeletonProps {
84
+ /** Expanded (full card) or Collapsed (icon-only box). */
85
+ size?: DrawerSize;
86
+ className?: string;
87
+ style?: CSSProperties;
88
+ testId?: string;
89
+ }
80
90
  /**
81
91
  * An item in the {@link DrawerProfile} click menu.
82
92
  */
@@ -114,6 +124,16 @@ export interface DrawerProfileProps {
114
124
  style?: CSSProperties;
115
125
  testId?: string;
116
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
+ }
117
137
  /**
118
138
  * A single entry in the {@link Drawer}'s `navItems` list.
119
139
  */
@@ -150,8 +170,12 @@ export interface DrawerProps {
150
170
  showToggle?: boolean;
151
171
  /** Credits card configuration. Omit to hide the card. */
152
172
  credits?: Omit<DrawerCreditsCardProps, 'size' | 'className' | 'style'>;
173
+ /** Render the credits card as a loading skeleton instead of its content. */
174
+ creditsLoading?: boolean;
153
175
  /** User profile footer configuration. Omit to hide the footer. */
154
176
  profile?: Omit<DrawerProfileProps, 'size' | 'className' | 'style'>;
177
+ /** Render the profile footer as a loading skeleton instead of its content. */
178
+ profileLoading?: boolean;
155
179
  /** Logo appearance overrides. */
156
180
  logo?: {
157
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.23",
4
+ "version": "0.2.25",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",