@beydesign/storybook 0.1.19 → 0.1.21

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.
Files changed (39) hide show
  1. package/dist/App.js +161 -5
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +2 -0
  4. package/dist/stories/EntityCard/EntityCard.d.ts +3 -0
  5. package/dist/stories/EntityCard/EntityCard.js +22 -0
  6. package/dist/stories/EntityCard/EntityCard.stories.d.ts +10 -0
  7. package/dist/stories/EntityCard/EntityCard.stories.js +187 -0
  8. package/dist/stories/EntityCard/EntityCardSkeleton.d.ts +3 -0
  9. package/dist/stories/EntityCard/EntityCardSkeleton.js +9 -0
  10. package/dist/stories/EntityCard/EntityCardSkeleton.stories.d.ts +10 -0
  11. package/dist/stories/EntityCard/EntityCardSkeleton.stories.js +73 -0
  12. package/dist/stories/EntityCard/index.d.ts +3 -0
  13. package/dist/stories/EntityCard/index.js +3 -0
  14. package/dist/stories/EntityCard/types.d.ts +54 -0
  15. package/dist/stories/EntityCard/types.js +1 -0
  16. package/dist/stories/PlanCard/PlanCard.d.ts +3 -0
  17. package/dist/stories/PlanCard/PlanCard.js +13 -0
  18. package/dist/stories/PlanCard/PlanCard.stories.d.ts +9 -0
  19. package/dist/stories/PlanCard/PlanCard.stories.js +125 -0
  20. package/dist/stories/PlanCard/PlanCardSkeleton.d.ts +3 -0
  21. package/dist/stories/PlanCard/PlanCardSkeleton.js +6 -0
  22. package/dist/stories/PlanCard/PlanCardSkeleton.stories.d.ts +7 -0
  23. package/dist/stories/PlanCard/PlanCardSkeleton.stories.js +40 -0
  24. package/dist/stories/PlanCard/index.d.ts +3 -0
  25. package/dist/stories/PlanCard/index.js +3 -0
  26. package/dist/stories/PlanCard/types.d.ts +61 -0
  27. package/dist/stories/PlanCard/types.js +1 -0
  28. package/dist/stories/UI/CheckIcon.d.ts +3 -0
  29. package/dist/stories/UI/CheckIcon.js +36 -0
  30. package/dist/stories/UI/CheckIcon.stories.d.ts +7 -0
  31. package/dist/stories/UI/CheckIcon.stories.js +48 -0
  32. package/dist/stories/UI/Menu.js +3 -3
  33. package/dist/stories/UI/Menu.stories.d.ts +1 -0
  34. package/dist/stories/UI/Menu.stories.js +32 -0
  35. package/dist/stories/UI/index.d.ts +1 -0
  36. package/dist/stories/UI/index.js +1 -0
  37. package/dist/stories/UI/types.d.ts +47 -0
  38. package/dist/stories/UI/types.js +30 -0
  39. package/package.json +1 -1
package/dist/App.js CHANGED
@@ -1,10 +1,166 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
- import reactLogo from './assets/react.svg';
4
- import viteLogo from '/vite.svg';
3
+ import { EntityCard, EntityCardSkeleton } from './stories/EntityCard';
4
+ import { PlanCard, PlanCardSkeleton } from './stories/PlanCard';
5
+ import { BadgeColor } from './stories/UI';
5
6
  import './App.css';
7
+ const plans = [
8
+ {
9
+ id: 'free',
10
+ title: 'Free',
11
+ description: 'Discover what Beyond Presence has to offer.',
12
+ price: '€0',
13
+ badgeText: 'Current Plan',
14
+ badgeColor: BadgeColor.Green,
15
+ buttonText: 'Current plan',
16
+ buttonDisabled: true,
17
+ features: [
18
+ '1 credit (1 min of Conversational Agent video)',
19
+ '5 stock Digital Twins',
20
+ ],
21
+ },
22
+ {
23
+ id: 'personal',
24
+ title: 'Personal',
25
+ description: 'Tailored for personal projects and independent creators.',
26
+ price: '€50',
27
+ features: [
28
+ '50 credits (50 min of Conversational Agent video)',
29
+ '5 stock Digital Twins',
30
+ '1 active Conversational Agent',
31
+ ],
32
+ },
33
+ {
34
+ id: 'pro',
35
+ title: 'Pro',
36
+ description: 'Designed for professionals looking to expand their reach.',
37
+ price: '€210',
38
+ badgeText: 'Most Popular',
39
+ badgeColor: BadgeColor.Lemon,
40
+ highlighted: true,
41
+ features: [
42
+ '300 credits (300 min of Conversational Agent video)',
43
+ '5 stock Digital Twins',
44
+ '3 active Conversational Agents',
45
+ 'Priority support',
46
+ ],
47
+ },
48
+ ];
49
+ const avatars = [
50
+ {
51
+ id: 'nelly',
52
+ name: 'Nelly',
53
+ image: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?fm=jpg&q=60&w=600&ixlib=rb-4.0.3',
54
+ },
55
+ {
56
+ id: 'ava',
57
+ name: 'Ava',
58
+ image: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?fm=jpg&q=60&w=600&ixlib=rb-4.0.3',
59
+ },
60
+ {
61
+ id: 'leo',
62
+ name: 'Leo',
63
+ image: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?fm=jpg&q=60&w=600&ixlib=rb-4.0.3',
64
+ },
65
+ {
66
+ id: 'maya',
67
+ name: 'Maya Customer Support',
68
+ image: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?fm=jpg&q=60&w=600&ixlib=rb-4.0.3',
69
+ },
70
+ {
71
+ id: 'no-image',
72
+ name: 'No Image',
73
+ image: undefined,
74
+ },
75
+ ];
76
+ const agentMenuItems = [
77
+ {
78
+ label: 'Conversation Link',
79
+ icon: 'ShareNetwork',
80
+ onClick: () => { },
81
+ },
82
+ { label: 'Copy Agent ID', icon: 'CopySimple', onClick: () => { } },
83
+ { label: 'Embed', icon: 'Code', onClick: () => { } },
84
+ {
85
+ label: 'Agent Details',
86
+ icon: 'IdentificationCard',
87
+ onClick: () => { },
88
+ },
89
+ { label: 'Duplicate', icon: 'Copy', onClick: () => { } },
90
+ {
91
+ label: 'Delete',
92
+ icon: 'Trash',
93
+ onClick: () => { },
94
+ destructive: true,
95
+ },
96
+ ];
6
97
  function App() {
7
- const [count, setCount] = useState(0);
8
- return (_jsxs(_Fragment, { children: [_jsxs("div", { children: [_jsx("a", { href: "https://vite.dev", target: "_blank", children: _jsx("img", { src: viteLogo, className: "logo", alt: "Vite logo" }) }), _jsx("a", { href: "https://react.dev", target: "_blank", children: _jsx("img", { src: reactLogo, className: "logo react", alt: "React logo" }) })] }), _jsx("h1", { children: "Vite + React" }), _jsxs("div", { className: "card", children: [_jsxs("button", { onClick: () => setCount((count) => count + 1), children: ["count is ", count] }), _jsxs("p", { children: ["Edit ", _jsx("code", { children: "src/App.tsx" }), " and save to test HMR"] })] }), _jsx("p", { className: "read-the-docs", children: "Click on the Vite and React logos to learn more" })] }));
98
+ const [loading, setLoading] = useState(false);
99
+ return (_jsxs("div", { style: { padding: '32px' }, children: [_jsx("button", { type: "button", onClick: () => setLoading((prev) => !prev), style: {
100
+ fontFamily: 'Figtree, sans-serif',
101
+ marginBottom: '24px',
102
+ padding: '8px 16px',
103
+ borderRadius: '6px',
104
+ border: '1px solid var(--colors-light-border-border-component)',
105
+ background: 'var(--colors-light-background-bg-white)',
106
+ color: 'var(--colors-light-text-text-title)',
107
+ cursor: 'pointer',
108
+ }, children: loading ? 'Show loaded cards' : 'Show loading skeletons' }), _jsx("h2", { style: {
109
+ fontFamily: 'Figtree, sans-serif',
110
+ color: 'var(--colors-light-text-text-title)',
111
+ marginBottom: '4px',
112
+ }, children: "Explore our Avatar Library" }), _jsx("p", { style: {
113
+ fontFamily: 'Figtree, sans-serif',
114
+ color: 'var(--colors-light-text-text-subtle)',
115
+ marginBottom: '24px',
116
+ }, children: "Select the perfect studio-quality digital human to make your interactions feel personal." }), _jsx("div", { style: {
117
+ display: 'flex',
118
+ flexWrap: 'wrap',
119
+ gap: '16px',
120
+ }, children: avatars.map((avatar) => loading ? (_jsx(EntityCardSkeleton, { view: "avatar", showDescription: true }, avatar.id)) : (_jsx(EntityCard, { name: avatar.name, image: avatar.image, onPreview: () => console.log('Preview', avatar.name), showMenu: true, menuItems: [
121
+ {
122
+ label: 'Copy Avatar ID',
123
+ icon: 'Copy',
124
+ onClick: () => console.log('Copy', avatar.id),
125
+ },
126
+ {
127
+ label: 'Create Agent',
128
+ icon: 'UserCirclePlus',
129
+ onClick: () => console.log('Create Agent', avatar.name),
130
+ },
131
+ {
132
+ label: 'Delete',
133
+ icon: 'Trash',
134
+ onClick: () => console.log('Delete', avatar.name),
135
+ destructive: true,
136
+ },
137
+ ], description: "daijdaij pa oapopao ao opaop op", showDescription: true }, avatar.id))) }), _jsx("h2", { style: {
138
+ fontFamily: 'Figtree, sans-serif',
139
+ color: 'var(--colors-light-text-text-title)',
140
+ margin: '40px 0 4px',
141
+ }, children: "Explore our Agent library" }), _jsx("p", { style: {
142
+ fontFamily: 'Figtree, sans-serif',
143
+ color: 'var(--colors-light-text-text-subtle)',
144
+ marginBottom: '24px',
145
+ }, children: "Your active agents \u2014 hover a card to join a live conversation." }), _jsx("div", { style: {
146
+ display: 'flex',
147
+ flexWrap: 'wrap',
148
+ gap: '16px',
149
+ }, children: avatars
150
+ .slice(0, 4)
151
+ .map((avatar) => loading ? (_jsx(EntityCardSkeleton, { view: "agent" }, `agent-${avatar.id}`)) : (_jsx(EntityCard, { view: "agent", name: `${avatar.name} Customer Support`, image: avatar.image, showStatusBadge: true, statusBadgeText: "Active", onPreview: () => console.log('Join', avatar.name), previewButtonText: "Join", previewIcon: "VideoCamera", showMenu: true, menuItems: agentMenuItems }, `agent-${avatar.id}`))) }), _jsx("h2", { style: {
152
+ fontFamily: 'Figtree, sans-serif',
153
+ color: 'var(--colors-light-text-text-title)',
154
+ margin: '40px 0 4px',
155
+ }, children: "Subscription plans" }), _jsx("p", { style: {
156
+ fontFamily: 'Figtree, sans-serif',
157
+ color: 'var(--colors-light-text-text-subtle)',
158
+ marginBottom: '24px',
159
+ }, children: "Choose the plan that's right for you." }), _jsx("div", { style: {
160
+ display: 'flex',
161
+ flexWrap: 'wrap',
162
+ alignItems: 'flex-start',
163
+ gap: '16px',
164
+ }, children: plans.map((plan) => loading ? (_jsx(PlanCardSkeleton, { featuresCount: plan.features.length }, plan.id)) : (_jsx(PlanCard, { title: plan.title, description: plan.description, price: plan.price, badgeText: plan.badgeText, badgeColor: plan.badgeColor, highlighted: plan.highlighted, buttonText: plan.buttonText, buttonDisabled: plan.buttonDisabled, features: plan.features, onButtonClick: () => console.log('Plan action', plan.title) }, plan.id))) })] }));
9
165
  }
10
166
  export default App;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export * from './stories/UI';
2
2
  export * from './stories/Agent';
3
+ export * from './stories/EntityCard';
4
+ export * from './stories/PlanCard';
3
5
  export * from './stories/Form';
4
6
  export * from './stories/Buttons';
5
7
  export * from './stories/Typography';
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  export * from './stories/UI';
2
2
  export * from './stories/Agent';
3
+ export * from './stories/EntityCard';
4
+ export * from './stories/PlanCard';
3
5
  export * from './stories/Form';
4
6
  export * from './stories/Buttons';
5
7
  export * from './stories/Typography';
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import { EntityCardProps } from './types';
3
+ export declare const EntityCard: React.FC<EntityCardProps>;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { Box } from '@mui/material';
4
+ import { Typography, TypographySize, TypographyWeight } from '../Typography';
5
+ import { Menu, Badge, BadgeColor, BadgeSize } from '../UI';
6
+ import { MainButton, ButtonHierarchy, ButtonSize, ButtonDisplayMode, } from '../Buttons';
7
+ import { UserCircle } from '@phosphor-icons/react';
8
+ export const EntityCard = ({ view = 'avatar', name, image, description = '', showDescription = false, onPreview, previewButtonText, previewIcon = 'Play', showStatusBadge = false, statusBadgeText = '', statusBadgeColor = BadgeColor.Green, menuItems = [], showMenu = false, width, }) => {
9
+ const [hovered, setHovered] = useState(false);
10
+ const [imageError, setImageError] = useState(false);
11
+ const isAgent = view === 'agent';
12
+ const cardWidth = width ?? (isAgent ? '280px' : '218px');
13
+ const imageAspectRatio = isAgent ? '1 / 1' : '203 / 264';
14
+ const entityKey = name?.toLowerCase()?.replace(/\s+/g, '-');
15
+ const hasMenu = showMenu && menuItems.length > 0;
16
+ const showActions = hovered && (!!onPreview || hasMenu);
17
+ const showBadge = showStatusBadge && !!statusBadgeText;
18
+ return (_jsxs(Box, { width: cardWidth, display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', bgcolor: 'var(--colors-light-background-bg-card-highlight)', border: '1px solid var(--colors-light-border-border-subtle)', boxSizing: 'border-box', overflow: 'hidden', "data-testid": `${entityKey}-entity-card`, children: [_jsxs(Box, { position: 'relative', width: '100%', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', overflow: 'hidden', sx: { aspectRatio: imageAspectRatio }, onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), children: [image && !imageError ? (_jsx("img", { src: image, alt: name, width: '100%', height: '100%', style: { objectFit: 'cover', height: '100%', width: '100%' }, onError: () => setImageError(true) })) : (_jsx(Box, { width: '100%', height: '100%', bgcolor: 'var(--colors-light-background-bg-element)', display: 'flex', justifyContent: 'center', alignItems: 'center', children: _jsx(UserCircle, { size: 74, weight: 'thin', color: 'var(--colors-light-text-text-subtle)' }) })), _jsx(Box, { position: 'absolute', left: 0, right: 0, bottom: 0, display: 'flex', alignItems: 'center', padding: '24px 12px 12px', sx: {
19
+ backgroundImage: 'linear-gradient(rgba(45, 38, 55, 0) 0%, rgba(45, 38, 55, 0.16) 34%, rgba(45, 38, 55, 0.64) 100%)',
20
+ }, children: _jsx(Typography, { size: TypographySize.HeadingXS, weight: TypographyWeight.SemiBold, color: 'var(--colors-light-text-text-white)', textStyle: { wordBreak: 'break-word' }, children: name }) }), showBadge && (_jsx(Box, { position: 'absolute', top: '8px', left: '8px', sx: { zIndex: 2 }, children: _jsx(Badge, { text: statusBadgeText, color: statusBadgeColor, size: BadgeSize.md, showIcon: true, icon: 'Circle', iconWeight: 'fill', iconSize: '8px', iconColor: 'var(--colors-light-background-bg-success)' }) })), showActions && (_jsxs(Box, { position: 'absolute', top: '8px', right: '8px', display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', sx: { zIndex: 2 }, children: [onPreview &&
21
+ (previewButtonText ? (_jsx(MainButton, { text: previewButtonText, showLeadingIcon: true, leadingIcon: previewIcon, hierarchy: ButtonHierarchy.Primary, size: ButtonSize.sm, onClick: onPreview, testId: `${entityKey}-preview-button` })) : (_jsx(MainButton, { displayIcon: previewIcon, displayIconMode: ButtonDisplayMode.Only, hierarchy: ButtonHierarchy.Primary, size: ButtonSize.sm, onClick: onPreview, testId: `${entityKey}-preview-button` }))), hasMenu && (_jsx(Menu, { items: menuItems, testKey: entityKey, size: ButtonSize.sm }))] }))] }), showDescription && description && (_jsx(Box, { display: 'flex', alignItems: 'center', padding: '0 12px', width: '100%', boxSizing: 'border-box', children: _jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: 'var(--colors-light-text-text-label)', textStyle: { wordBreak: 'break-word' }, children: description }) }))] }));
22
+ };
@@ -0,0 +1,10 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { EntityCard } from './EntityCard';
3
+ declare const meta: Meta<typeof EntityCard>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof EntityCard>;
6
+ export declare const Default: Story;
7
+ export declare const AgentView: Story;
8
+ export declare const WithDescription: Story;
9
+ export declare const NoActions: Story;
10
+ export declare const NoImage: Story;
@@ -0,0 +1,187 @@
1
+ import { EntityCard } from './EntityCard';
2
+ import { BadgeColor } from '../UI';
3
+ const meta = {
4
+ title: 'Design System/Components/EntityCard/EntityCard',
5
+ component: EntityCard,
6
+ parameters: {
7
+ layout: 'centered',
8
+ design: {
9
+ type: 'figspec',
10
+ url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=6916-20176',
11
+ },
12
+ },
13
+ tags: ['autodocs'],
14
+ argTypes: {
15
+ view: {
16
+ control: 'select',
17
+ options: ['avatar', 'agent'],
18
+ description: 'View variant — drives default width and image aspect ratio',
19
+ table: {
20
+ type: { summary: "'avatar' | 'agent'" },
21
+ defaultValue: { summary: 'avatar' },
22
+ },
23
+ },
24
+ name: {
25
+ control: 'text',
26
+ description: 'Display name shown over the image',
27
+ table: { type: { summary: 'string' } },
28
+ },
29
+ image: {
30
+ control: 'text',
31
+ description: 'Image url',
32
+ table: { type: { summary: 'string' } },
33
+ },
34
+ description: {
35
+ control: 'text',
36
+ description: 'Optional description shown below the image',
37
+ table: { type: { summary: 'string' } },
38
+ },
39
+ showDescription: {
40
+ control: 'boolean',
41
+ description: 'Whether to show the description',
42
+ table: { type: { summary: 'boolean' } },
43
+ },
44
+ showMenu: {
45
+ control: 'boolean',
46
+ description: 'Whether to show the menu on hover',
47
+ table: { type: { summary: 'boolean' } },
48
+ },
49
+ onPreview: {
50
+ description: 'Preview handler — renders the action button on hover',
51
+ table: { type: { summary: 'function' } },
52
+ },
53
+ previewButtonText: {
54
+ control: 'text',
55
+ description: 'Label for the action button; when set it renders text + icon, otherwise icon-only',
56
+ table: { type: { summary: 'string' } },
57
+ },
58
+ previewIcon: {
59
+ control: 'text',
60
+ description: 'Icon for the action button',
61
+ table: {
62
+ type: { summary: 'IconName' },
63
+ defaultValue: { summary: 'Play' },
64
+ },
65
+ },
66
+ showStatusBadge: {
67
+ control: 'boolean',
68
+ description: 'Whether to show the status badge (top-left)',
69
+ table: { type: { summary: 'boolean' } },
70
+ },
71
+ statusBadgeText: {
72
+ control: 'text',
73
+ description: 'Status badge text (e.g. "Active")',
74
+ table: { type: { summary: 'string' } },
75
+ },
76
+ statusBadgeColor: {
77
+ control: 'select',
78
+ options: Object.values(BadgeColor),
79
+ description: 'Status badge color',
80
+ table: {
81
+ type: { summary: 'BadgeColor' },
82
+ defaultValue: { summary: BadgeColor.Green },
83
+ },
84
+ },
85
+ menuItems: {
86
+ description: 'Menu items for the dropdown',
87
+ table: { type: { summary: 'array' } },
88
+ },
89
+ width: {
90
+ control: 'text',
91
+ description: 'Width of the card',
92
+ table: {
93
+ type: { summary: 'string | number' },
94
+ defaultValue: { summary: '218px' },
95
+ },
96
+ },
97
+ },
98
+ };
99
+ export default meta;
100
+ const avatarImage = 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?fm=jpg&q=60&w=600&ixlib=rb-4.0.3';
101
+ export const Default = {
102
+ args: {
103
+ name: 'Nelly',
104
+ image: avatarImage,
105
+ onPreview: () => { },
106
+ showMenu: true,
107
+ menuItems: [
108
+ { label: 'Copy Avatar ID', icon: 'Copy', onClick: () => { } },
109
+ { label: 'Create Agent', icon: 'UserCirclePlus', onClick: () => { } },
110
+ { label: 'Delete', icon: 'Trash', onClick: () => { }, destructive: true },
111
+ ],
112
+ },
113
+ };
114
+ export const AgentView = {
115
+ args: {
116
+ view: 'agent',
117
+ name: 'Nelly Customer Support',
118
+ image: avatarImage,
119
+ onPreview: () => { },
120
+ previewButtonText: 'Join',
121
+ previewIcon: 'VideoCamera',
122
+ showStatusBadge: true,
123
+ statusBadgeText: 'Active',
124
+ showMenu: true,
125
+ menuItems: [
126
+ { label: 'Conversation Link', icon: 'ShareNetwork', onClick: () => { } },
127
+ { label: 'Copy Agent ID', icon: 'CopySimple', onClick: () => { } },
128
+ { label: 'Embed', icon: 'Code', onClick: () => { } },
129
+ { label: 'Agent Details', icon: 'IdentificationCard', onClick: () => { } },
130
+ { label: 'Duplicate', icon: 'Copy', onClick: () => { } },
131
+ { label: 'Delete', icon: 'Trash', onClick: () => { }, destructive: true },
132
+ ],
133
+ },
134
+ parameters: {
135
+ design: {
136
+ type: 'figspec',
137
+ url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=6919-20599',
138
+ },
139
+ docs: {
140
+ description: {
141
+ story: 'The agent view — wider, square image, a persistent status badge, and a labelled "Join" action. Hover to reveal the action row.',
142
+ },
143
+ },
144
+ },
145
+ };
146
+ export const WithDescription = {
147
+ args: {
148
+ ...Default.args,
149
+ name: 'Nelly Customer Support',
150
+ showDescription: true,
151
+ description: 'Support description',
152
+ },
153
+ parameters: {
154
+ docs: {
155
+ description: {
156
+ story: 'Avatar card with an optional description below the image.',
157
+ },
158
+ },
159
+ },
160
+ };
161
+ export const NoActions = {
162
+ args: {
163
+ name: 'Nelly',
164
+ image: avatarImage,
165
+ },
166
+ parameters: {
167
+ docs: {
168
+ description: {
169
+ story: 'A read-only avatar card without preview or menu actions on hover.',
170
+ },
171
+ },
172
+ },
173
+ };
174
+ export const NoImage = {
175
+ args: {
176
+ ...Default.args,
177
+ name: 'No Image',
178
+ image: undefined,
179
+ },
180
+ parameters: {
181
+ docs: {
182
+ description: {
183
+ story: 'Fallback placeholder shown when no image is provided or it fails to load.',
184
+ },
185
+ },
186
+ },
187
+ };
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import { EntityCardSkeletonProps } from './types';
3
+ export declare const EntityCardSkeleton: React.FC<EntityCardSkeletonProps>;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box } from '@mui/material';
3
+ import { SkeletonLoader } from '../UI';
4
+ export const EntityCardSkeleton = ({ view = 'avatar', width, showDescription = false, style, }) => {
5
+ const isAgent = view === 'agent';
6
+ const cardWidth = width ?? (isAgent ? '280px' : '218px');
7
+ const imageAspectRatio = isAgent ? '1 / 1' : '203 / 264';
8
+ return (_jsxs(Box, { width: cardWidth, display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', bgcolor: 'var(--colors-light-background-bg-card-highlight)', border: '1px solid var(--colors-light-border-border-subtle)', boxSizing: 'border-box', overflow: 'hidden', style: style, children: [_jsx(Box, { width: '100%', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', overflow: 'hidden', sx: { aspectRatio: imageAspectRatio }, children: _jsx(SkeletonLoader, {}) }), showDescription && (_jsxs(Box, { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-sm)', padding: '0 12px', width: '100%', boxSizing: 'border-box', children: [_jsx(SkeletonLoader, { width: '100%', height: '16px', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)' }), _jsx(SkeletonLoader, { width: '70%', height: '16px', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)' })] }))] }));
9
+ };
@@ -0,0 +1,10 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { EntityCardSkeleton } from './EntityCardSkeleton';
3
+ declare const meta: Meta<typeof EntityCardSkeleton>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof EntityCardSkeleton>;
6
+ export declare const Avatar: Story;
7
+ export declare const AvatarWithDescription: Story;
8
+ export declare const Agent: Story;
9
+ export declare const Grid: Story;
10
+ export declare const LoadingState: Story;
@@ -0,0 +1,73 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box } from '@mui/material';
3
+ import { EntityCardSkeleton } from './EntityCardSkeleton';
4
+ const meta = {
5
+ title: 'Design System/Components/EntityCard/EntityCardSkeleton',
6
+ component: EntityCardSkeleton,
7
+ parameters: {
8
+ layout: 'centered',
9
+ },
10
+ tags: ['autodocs'],
11
+ argTypes: {
12
+ view: {
13
+ control: 'select',
14
+ options: ['avatar', 'agent'],
15
+ description: 'View variant — drives default width and image aspect ratio',
16
+ table: {
17
+ type: { summary: "'avatar' | 'agent'" },
18
+ defaultValue: { summary: 'avatar' },
19
+ },
20
+ },
21
+ width: {
22
+ control: 'text',
23
+ description: 'Width of the card (overrides the view default)',
24
+ table: {
25
+ type: { summary: 'string | number' },
26
+ defaultValue: { summary: '218px (avatar) / 280px (agent)' },
27
+ },
28
+ },
29
+ showDescription: {
30
+ control: 'boolean',
31
+ description: 'Show the description placeholder',
32
+ table: {
33
+ type: { summary: 'boolean' },
34
+ defaultValue: { summary: 'false' },
35
+ },
36
+ },
37
+ style: {
38
+ control: 'object',
39
+ description: 'Custom style for the skeleton',
40
+ table: {
41
+ type: { summary: 'React.CSSProperties' },
42
+ defaultValue: { summary: 'undefined' },
43
+ },
44
+ },
45
+ },
46
+ };
47
+ export default meta;
48
+ export const Avatar = {
49
+ args: {
50
+ view: 'avatar',
51
+ },
52
+ };
53
+ export const AvatarWithDescription = {
54
+ args: {
55
+ view: 'avatar',
56
+ showDescription: true,
57
+ },
58
+ };
59
+ export const Agent = {
60
+ args: {
61
+ view: 'agent',
62
+ },
63
+ };
64
+ export const Grid = {
65
+ render: () => (_jsxs(Box, { display: "flex", flexWrap: "wrap", gap: 2, children: [_jsx(EntityCardSkeleton, { view: "avatar" }), _jsx(EntityCardSkeleton, { view: "avatar", showDescription: true }), _jsx(EntityCardSkeleton, { view: "agent" })] })),
66
+ };
67
+ export const LoadingState = {
68
+ render: () => (_jsxs(Box, { display: "flex", flexDirection: "column", gap: 3, children: [_jsxs(Box, { children: [_jsx("h3", { children: "Loading state example" }), _jsx("p", { children: "How a row of entity cards appears while data is loading." })] }), _jsx(Box, { display: "flex", flexWrap: "wrap", gap: 2, children: Array(5)
69
+ .fill(0)
70
+ .map((_, index) => (_jsx(EntityCardSkeleton, { view: "avatar" }, index))) }), _jsx(Box, { display: "flex", flexWrap: "wrap", gap: 2, children: Array(4)
71
+ .fill(0)
72
+ .map((_, index) => (_jsx(EntityCardSkeleton, { view: "agent" }, index))) })] })),
73
+ };
@@ -0,0 +1,3 @@
1
+ export * from './EntityCard';
2
+ export * from './EntityCardSkeleton';
3
+ export * from './types';
@@ -0,0 +1,3 @@
1
+ export * from './EntityCard';
2
+ export * from './EntityCardSkeleton';
3
+ export * from './types';
@@ -0,0 +1,54 @@
1
+ import { MenuItemProps, BadgeColor } from '../UI';
2
+ import { IconName } from '../../utils';
3
+ /**
4
+ * Entity card view variants.
5
+ * - `avatar`: portrait avatar tile (icon-only preview action)
6
+ * - `agent`: wider, square agent tile (labelled action + status badge)
7
+ */
8
+ export type EntityCardView = 'avatar' | 'agent';
9
+ /**
10
+ * Entity card component props
11
+ */
12
+ export interface EntityCardProps {
13
+ /** View variant — drives default width and image aspect ratio */
14
+ view?: EntityCardView;
15
+ /** Display name shown over the image */
16
+ name: string;
17
+ /** Image url */
18
+ image?: string;
19
+ /** Optional description shown below the image */
20
+ description?: string;
21
+ /** Whether to show the description */
22
+ showDescription?: boolean;
23
+ /** Preview handler — renders the action button on hover */
24
+ onPreview?: () => void;
25
+ /** Label for the action button; when set it renders text + icon, otherwise icon-only */
26
+ previewButtonText?: string;
27
+ /** Icon for the action button (defaults to `Play`) */
28
+ previewIcon?: IconName;
29
+ /** Whether to show the status badge (top-left) */
30
+ showStatusBadge?: boolean;
31
+ /** Status badge text (e.g. "Active") */
32
+ statusBadgeText?: string;
33
+ /** Status badge color (defaults to Green) */
34
+ statusBadgeColor?: BadgeColor;
35
+ /** Menu items for the dropdown — renders the menu button on hover */
36
+ menuItems?: MenuItemProps[];
37
+ /** Whether to show the menu */
38
+ showMenu?: boolean;
39
+ /** Width of the card (overrides the view default) */
40
+ width?: string | number;
41
+ }
42
+ /**
43
+ * Entity card skeleton props
44
+ */
45
+ export interface EntityCardSkeletonProps {
46
+ /** View variant — drives default width and image aspect ratio */
47
+ view?: EntityCardView;
48
+ /** Width of the card (overrides the view default) */
49
+ width?: string | number;
50
+ /** Show the description placeholder */
51
+ showDescription?: boolean;
52
+ /** Custom style for the skeleton */
53
+ style?: React.CSSProperties;
54
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import { PlanCardProps } from './types';
3
+ export declare const PlanCard: React.FC<PlanCardProps>;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box } from '@mui/material';
3
+ import { Typography, TypographySize, TypographyWeight } from '../Typography';
4
+ import { MainButton, ButtonHierarchy, ButtonSize } from '../Buttons';
5
+ import { Badge, BadgeColor, BadgeSize, CheckIcon, CheckIconColor, CheckIconSize, CheckIconType, } from '../UI';
6
+ export const PlanCard = ({ title, description, price, priceSuffix = '/month', badgeText, badgeColor = BadgeColor.Lemon, highlighted = false, featuresTitle = 'This includes:', features = [], buttonText = 'Upgrade', onButtonClick, buttonDisabled = false, buttonHierarchy = ButtonHierarchy.Primary, buttonLoading = false, width = '286px', style, testId, }) => {
7
+ const planKey = title?.toLowerCase()?.replace(/\s+/g, '-');
8
+ return (_jsxs(Box, { width: width, display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xl)', paddingX: 'var(--spacing-tokens-size-in-px-spacing-spacing-xl)', paddingY: 'var(--spacing-tokens-size-in-px-spacing-spacing-2xl)', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg)', bgcolor: highlighted
9
+ ? 'var(--colors-light-background-bg-card-highlight)'
10
+ : 'var(--colors-light-background-bg-white)', border: `1px solid ${highlighted
11
+ ? 'var(--colors-light-border-border-brand)'
12
+ : 'var(--colors-light-border-border-subtle)'}`, boxSizing: 'border-box', style: style, "data-testid": testId ?? `${planKey}-plan-card`, children: [_jsxs(Box, { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', children: [_jsxs(Box, { display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', minHeight: '24px', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', children: [_jsx(Typography, { size: TypographySize.HeadingM, weight: TypographyWeight.Bold, color: 'var(--colors-light-text-text-title)', children: title }), badgeText && (_jsx(Badge, { text: badgeText, color: badgeColor, size: BadgeSize.xs, fill: true }))] }), description && (_jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Regular, color: 'var(--colors-light-text-text-paragraph)', textStyle: { textAlign: 'left' }, children: description })), price && (_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'baseline', gap: '2px', children: [_jsx(Typography, { size: TypographySize.HeadingL, weight: TypographyWeight.Bold, color: 'var(--colors-light-text-text-title)', children: price }), priceSuffix && (_jsx(Typography, { size: TypographySize.TextXS, weight: TypographyWeight.Regular, color: 'var(--colors-light-text-text-paragraph)', children: priceSuffix }))] }))] }), buttonText && (_jsx(MainButton, { text: buttonText, hierarchy: buttonHierarchy, size: ButtonSize.md, disabled: buttonDisabled, loading: buttonLoading, onClick: onButtonClick, style: { width: '100%' }, testId: `${planKey}-plan-button` })), features.length > 0 && (_jsxs(Box, { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', alignItems: 'flex-start', children: [_jsx(Typography, { size: TypographySize.TextS, weight: TypographyWeight.Medium, color: 'var(--colors-light-text-text-paragraph)', children: featuresTitle }), features.map((feature, index) => (_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'flex-start', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', width: '100%', children: [_jsx(CheckIcon, { color: CheckIconColor.Violet, size: CheckIconSize.xs, type: CheckIconType.Duotone }), _jsx(Typography, { size: TypographySize.TextM, weight: TypographyWeight.Regular, color: 'var(--colors-light-text-text-title)', textStyle: { textAlign: 'left' }, children: feature })] }, `${index}-${feature}`)))] }))] }));
13
+ };
@@ -0,0 +1,9 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { PlanCard } from './PlanCard';
3
+ declare const meta: Meta<typeof PlanCard>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof PlanCard>;
6
+ export declare const Pro: Story;
7
+ export declare const Free: Story;
8
+ export declare const Personal: Story;
9
+ export declare const Custom: Story;
@@ -0,0 +1,125 @@
1
+ import { PlanCard } from './PlanCard';
2
+ import { BadgeColor } from '../UI';
3
+ import { ButtonHierarchy } from '../Buttons';
4
+ const meta = {
5
+ title: 'Design System/Components/PlanCard/PlanCard',
6
+ component: PlanCard,
7
+ parameters: {
8
+ layout: 'centered',
9
+ design: {
10
+ type: 'figspec',
11
+ url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=7002-19848',
12
+ },
13
+ },
14
+ tags: ['autodocs'],
15
+ argTypes: {
16
+ title: { control: 'text', description: 'Plan name' },
17
+ description: { control: 'text', description: 'Short plan description' },
18
+ price: {
19
+ control: 'text',
20
+ description: 'Price text (e.g. "€210" or "Custom")',
21
+ },
22
+ priceSuffix: {
23
+ control: 'text',
24
+ description: 'Price suffix',
25
+ table: { defaultValue: { summary: '/month' } },
26
+ },
27
+ badgeText: { control: 'text', description: 'Badge text' },
28
+ badgeColor: {
29
+ control: 'select',
30
+ options: Object.values(BadgeColor),
31
+ description: 'Badge color',
32
+ table: { defaultValue: { summary: BadgeColor.Lemon } },
33
+ },
34
+ highlighted: {
35
+ control: 'boolean',
36
+ description: 'Brand background + border highlight',
37
+ },
38
+ featuresTitle: {
39
+ control: 'text',
40
+ table: { defaultValue: { summary: 'This includes:' } },
41
+ },
42
+ features: { control: 'object', description: 'Feature list' },
43
+ buttonText: {
44
+ control: 'text',
45
+ table: { defaultValue: { summary: 'Upgrade' } },
46
+ },
47
+ buttonDisabled: { control: 'boolean' },
48
+ buttonHierarchy: {
49
+ control: 'select',
50
+ options: Object.values(ButtonHierarchy),
51
+ table: { defaultValue: { summary: ButtonHierarchy.Primary } },
52
+ },
53
+ buttonLoading: { control: 'boolean' },
54
+ width: {
55
+ control: 'text',
56
+ table: { defaultValue: { summary: '286px' } },
57
+ },
58
+ },
59
+ };
60
+ export default meta;
61
+ export const Pro = {
62
+ args: {
63
+ title: 'Pro',
64
+ description: 'Designed for professionals looking to expand their reach.',
65
+ price: '€210',
66
+ priceSuffix: '/month',
67
+ badgeText: 'Most Popular',
68
+ badgeColor: BadgeColor.Lemon,
69
+ highlighted: true,
70
+ buttonText: 'Upgrade',
71
+ features: [
72
+ '300 credits (300 min of Conversational Agent video)',
73
+ '5 stock Digital Twins',
74
+ '3 active Conversational Agents',
75
+ 'Priority support',
76
+ ],
77
+ },
78
+ };
79
+ export const Free = {
80
+ args: {
81
+ title: 'Free',
82
+ description: 'Discover what Beyond Presence has to offer.',
83
+ price: '€0',
84
+ priceSuffix: '/month',
85
+ badgeText: 'Current Plan',
86
+ badgeColor: BadgeColor.Green,
87
+ buttonText: 'Current plan',
88
+ buttonDisabled: true,
89
+ features: [
90
+ '1 credit (1 min of Conversational Agent video)',
91
+ '5 stock Digital Twins',
92
+ ],
93
+ },
94
+ };
95
+ export const Personal = {
96
+ args: {
97
+ title: 'Personal',
98
+ description: 'Tailored for personal projects and independent creators.',
99
+ price: '€50',
100
+ priceSuffix: '/month',
101
+ buttonText: 'Upgrade',
102
+ features: [
103
+ '50 credits (50 min of Conversational Agent video)',
104
+ '5 stock Digital Twins',
105
+ '1 active Conversational Agent',
106
+ ],
107
+ },
108
+ };
109
+ export const Custom = {
110
+ args: {
111
+ title: 'Professional',
112
+ description: 'For businesses with high volume and custom needs.',
113
+ price: 'Custom',
114
+ priceSuffix: '',
115
+ buttonText: 'Upgrade',
116
+ features: ['5000+ credits (5000+ min of Conversational Agent video)'],
117
+ },
118
+ parameters: {
119
+ docs: {
120
+ description: {
121
+ story: 'A custom-priced plan — the price suffix is omitted.',
122
+ },
123
+ },
124
+ },
125
+ };
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import { PlanCardSkeletonProps } from './types';
3
+ export declare const PlanCardSkeleton: React.FC<PlanCardSkeletonProps>;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box } from '@mui/material';
3
+ import { SkeletonLoader } from '../UI';
4
+ export const PlanCardSkeleton = ({ showBadge = true, showButton = true, showFeatures = true, featuresCount = 4, width = '286px', style, testId, }) => {
5
+ return (_jsxs(Box, { width: width, display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-xl)', paddingX: 'var(--spacing-tokens-size-in-px-spacing-spacing-xl)', paddingY: 'var(--spacing-tokens-size-in-px-spacing-spacing-2xl)', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-lg)', bgcolor: 'var(--colors-light-background-bg-white)', border: '1px solid var(--colors-light-border-border-subtle)', boxSizing: 'border-box', style: style, "data-testid": testId, children: [_jsxs(Box, { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', children: [_jsxs(Box, { display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', children: [_jsx(SkeletonLoader, { width: '40%', height: '24px', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)' }), showBadge && (_jsx(SkeletonLoader, { width: '88px', height: '24px', borderRadius: 'var(--spacing-tokens-size-in-px-radius-radius-full)' }))] }), _jsx(SkeletonLoader, { width: '100%', height: '20px', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)' }), _jsx(SkeletonLoader, { width: '50%', height: '32px', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)' })] }), showButton && (_jsx(SkeletonLoader, { width: '100%', height: '36px', borderRadius: 'var(--spacing-tokens-size-in-px-radius-radius-xs)' })), showFeatures && (_jsxs(Box, { display: 'flex', flexDirection: 'column', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', alignItems: 'flex-start', children: [_jsx(SkeletonLoader, { width: '96px', height: '20px', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)' }), Array.from({ length: featuresCount }).map((_, index) => (_jsxs(Box, { display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 'var(--spacing-tokens-size-in-px-spacing-spacing-md)', width: '100%', children: [_jsx(SkeletonLoader, { width: '20px', height: '20px', borderRadius: 'var(--spacing-tokens-size-in-px-radius-radius-full)' }), _jsx(SkeletonLoader, { width: index % 2 === 0 ? '80%' : '60%', height: '18px', borderRadius: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)' })] }, index)))] }))] }));
6
+ };
@@ -0,0 +1,7 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { PlanCardSkeleton } from './PlanCardSkeleton';
3
+ declare const meta: Meta<typeof PlanCardSkeleton>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof PlanCardSkeleton>;
6
+ export declare const Default: Story;
7
+ export declare const Grid: Story;
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box } from '@mui/material';
3
+ import { PlanCardSkeleton } from './PlanCardSkeleton';
4
+ const meta = {
5
+ title: 'Design System/Components/PlanCard/PlanCardSkeleton',
6
+ component: PlanCardSkeleton,
7
+ parameters: {
8
+ layout: 'centered',
9
+ },
10
+ tags: ['autodocs'],
11
+ argTypes: {
12
+ showBadge: {
13
+ control: 'boolean',
14
+ table: { defaultValue: { summary: 'true' } },
15
+ },
16
+ showButton: {
17
+ control: 'boolean',
18
+ table: { defaultValue: { summary: 'true' } },
19
+ },
20
+ showFeatures: {
21
+ control: 'boolean',
22
+ table: { defaultValue: { summary: 'true' } },
23
+ },
24
+ featuresCount: {
25
+ control: 'number',
26
+ table: { defaultValue: { summary: '4' } },
27
+ },
28
+ width: {
29
+ control: 'text',
30
+ table: { defaultValue: { summary: '286px' } },
31
+ },
32
+ },
33
+ };
34
+ export default meta;
35
+ export const Default = {
36
+ args: {},
37
+ };
38
+ export const Grid = {
39
+ render: () => (_jsxs(Box, { display: "flex", flexWrap: "wrap", gap: 2, children: [_jsx(PlanCardSkeleton, {}), _jsx(PlanCardSkeleton, { featuresCount: 3 }), _jsx(PlanCardSkeleton, { featuresCount: 5 })] })),
40
+ };
@@ -0,0 +1,3 @@
1
+ export * from './PlanCard';
2
+ export * from './PlanCardSkeleton';
3
+ export * from './types';
@@ -0,0 +1,3 @@
1
+ export * from './PlanCard';
2
+ export * from './PlanCardSkeleton';
3
+ export * from './types';
@@ -0,0 +1,61 @@
1
+ import React from 'react';
2
+ import { BadgeColor } from '../UI';
3
+ import { ButtonHierarchy } from '../Buttons';
4
+ /**
5
+ * Plan card component props — a compact, in-app subscription plan card.
6
+ */
7
+ export interface PlanCardProps {
8
+ /** Plan name (e.g. "Pro") */
9
+ title: string;
10
+ /** Short plan description */
11
+ description?: string;
12
+ /** Price text (e.g. "€210" or "Custom") */
13
+ price?: string;
14
+ /** Price suffix shown next to the price (e.g. "/month") */
15
+ priceSuffix?: string;
16
+ /** Badge text (e.g. "Most Popular", "Current Plan") */
17
+ badgeText?: string;
18
+ /** Badge color (defaults to Lemon) */
19
+ badgeColor?: BadgeColor;
20
+ /** Highlights the card with a brand background + border */
21
+ highlighted?: boolean;
22
+ /** Heading above the feature list */
23
+ featuresTitle?: string;
24
+ /** Feature list */
25
+ features?: string[];
26
+ /** Action button label (defaults to "Upgrade"); hidden when omitted */
27
+ buttonText?: string;
28
+ /** Action button click handler */
29
+ onButtonClick?: () => void;
30
+ /** Disable the action button (e.g. for the current plan) */
31
+ buttonDisabled?: boolean;
32
+ /** Action button hierarchy (defaults to Primary) */
33
+ buttonHierarchy?: ButtonHierarchy;
34
+ /** Show a loading spinner on the action button */
35
+ buttonLoading?: boolean;
36
+ /** Width of the card */
37
+ width?: string | number;
38
+ /** Custom style */
39
+ style?: React.CSSProperties;
40
+ /** Test id */
41
+ testId?: string;
42
+ }
43
+ /**
44
+ * Plan card skeleton props
45
+ */
46
+ export interface PlanCardSkeletonProps {
47
+ /** Whether to show the badge placeholder */
48
+ showBadge?: boolean;
49
+ /** Whether to show the button placeholder */
50
+ showButton?: boolean;
51
+ /** Whether to show the features placeholder */
52
+ showFeatures?: boolean;
53
+ /** Number of feature placeholders */
54
+ featuresCount?: number;
55
+ /** Width of the card */
56
+ width?: string | number;
57
+ /** Custom style */
58
+ style?: React.CSSProperties;
59
+ /** Test id */
60
+ testId?: string;
61
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ import { CheckIconProps } from './types';
3
+ export declare const CheckIcon: React.FC<CheckIconProps>;
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box } from '@mui/material';
3
+ import { CheckIconColor, CheckIconSize, CheckIconType, } from './types';
4
+ import { getIconComponent } from '../../utils';
5
+ const WHITE = 'var(--semantic-colors-light-foreground-fg-on-primary-accent-color)';
6
+ const COLOR_MAP = {
7
+ [CheckIconColor.Violet]: {
8
+ accent: 'var(--semantic-colors-light-foreground-fg-accent-violet)',
9
+ subtle: 'var(--semantic-colors-light-background-bg-badge-accent-brand-violet)',
10
+ },
11
+ [CheckIconColor.Green]: {
12
+ accent: 'var(--colors-light-foreground-fg-success)',
13
+ subtle: 'var(--semantic-colors-light-background-bg-badge-accent-success)',
14
+ },
15
+ };
16
+ const SIZE_MAP = {
17
+ [CheckIconSize.xs]: { container: 16, check: 11, border: 1.5 },
18
+ [CheckIconSize.sm]: { container: 20, check: 13, border: 1.5 },
19
+ [CheckIconSize.md]: { container: 24, check: 16, border: 2 },
20
+ [CheckIconSize.lg]: { container: 28, check: 18, border: 2 },
21
+ };
22
+ export const CheckIcon = ({ color = CheckIconColor.Violet, size = CheckIconSize.md, type = CheckIconType.Duotone, style, testId, }) => {
23
+ const { accent, subtle } = COLOR_MAP[color];
24
+ const { container, check, border } = SIZE_MAP[size];
25
+ const background = type === CheckIconType.Filled
26
+ ? accent
27
+ : type === CheckIconType.Duotone
28
+ ? subtle
29
+ : 'transparent';
30
+ const checkColor = type === CheckIconType.Filled ? WHITE : accent;
31
+ return (_jsx(Box, { width: `${container}px`, height: `${container}px`, flexShrink: 0, boxSizing: 'border-box', display: 'flex', alignItems: 'center', justifyContent: 'center', borderRadius: 'var(--spacing-tokens-size-in-px-radius-radius-full)', bgcolor: background, border: type === CheckIconType.Line ? `${border}px solid ${accent}` : undefined, style: style, "data-testid": testId, children: getIconComponent('Check', {
32
+ size: `${check}px`,
33
+ weight: 'bold',
34
+ color: checkColor,
35
+ }) }));
36
+ };
@@ -0,0 +1,7 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { CheckIcon } from './CheckIcon';
3
+ declare const meta: Meta<typeof CheckIcon>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof CheckIcon>;
6
+ export declare const Default: Story;
7
+ export declare const AllVariants: Story;
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box } from '@mui/material';
3
+ import { CheckIcon } from './CheckIcon';
4
+ import { CheckIconColor, CheckIconSize, CheckIconType } from './types';
5
+ const meta = {
6
+ title: 'Design System/Components/UI/CheckIcon',
7
+ component: CheckIcon,
8
+ parameters: {
9
+ layout: 'centered',
10
+ design: {
11
+ type: 'figspec',
12
+ url: 'https://www.figma.com/design/mIhjz2yJjcpLlIqw6oUivt/Beyond-Presence?node-id=4781-5580',
13
+ },
14
+ },
15
+ tags: ['autodocs'],
16
+ argTypes: {
17
+ color: {
18
+ control: 'select',
19
+ options: Object.values(CheckIconColor),
20
+ table: { defaultValue: { summary: CheckIconColor.Violet } },
21
+ },
22
+ size: {
23
+ control: 'select',
24
+ options: Object.values(CheckIconSize),
25
+ table: { defaultValue: { summary: CheckIconSize.md } },
26
+ },
27
+ type: {
28
+ control: 'select',
29
+ options: Object.values(CheckIconType),
30
+ table: { defaultValue: { summary: CheckIconType.Duotone } },
31
+ },
32
+ },
33
+ };
34
+ export default meta;
35
+ export const Default = {
36
+ args: {
37
+ color: CheckIconColor.Violet,
38
+ size: CheckIconSize.md,
39
+ type: CheckIconType.Duotone,
40
+ },
41
+ };
42
+ export const AllVariants = {
43
+ render: () => (_jsx(Box, { display: "flex", flexDirection: "column", gap: 3, children: Object.values(CheckIconType).map((type) => (_jsxs(Box, { display: "flex", flexDirection: "column", gap: 1, children: [_jsx("span", { style: {
44
+ fontFamily: 'Figtree, sans-serif',
45
+ fontSize: 12,
46
+ color: 'var(--colors-light-text-text-subtle)',
47
+ }, children: type }), Object.values(CheckIconColor).map((color) => (_jsx(Box, { display: "flex", alignItems: "center", gap: 2, children: Object.values(CheckIconSize).map((size) => (_jsx(CheckIcon, { color: color, type: type, size: size }, `${color}-${type}-${size}`))) }, color)))] }, type))) })),
48
+ };
@@ -1,10 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { MenuItem as MuiMenuItem, Menu as MuiMenu, } from '@mui/material';
3
3
  import { Fragment, useState } from 'react';
4
- import { MainButton, ButtonDisplayMode, ButtonHierarchy, ButtonType, } from '../Buttons';
4
+ import { MainButton, ButtonDisplayMode, ButtonHierarchy, ButtonType, ButtonSize, } from '../Buttons';
5
5
  import { Typography, TypographySize, TypographyWeight } from '../Typography';
6
6
  import { getIconComponent } from '../../utils';
7
- export const Menu = ({ icon = 'DotsThree', items, style, testKey, }) => {
7
+ export const Menu = ({ icon = 'DotsThree', items, style, testKey, size = ButtonSize.xl, hierarchy = ButtonHierarchy.Secondary, }) => {
8
8
  const [anchorEl, setAnchorEl] = useState(null);
9
9
  const open = Boolean(anchorEl);
10
10
  const handleClick = (event) => {
@@ -24,7 +24,7 @@ export const Menu = ({ icon = 'DotsThree', items, style, testKey, }) => {
24
24
  return 'var(--colors-light-text-text-error)';
25
25
  return 'var(--colors-light-text-text-title)';
26
26
  };
27
- return (_jsxs(Fragment, { children: [_jsx(MainButton, { displayIcon: icon, onClick: handleClick, type: ButtonType.Button, displayIconMode: ButtonDisplayMode.Only, hierarchy: ButtonHierarchy.Secondary, style: style, testId: `${testKey}-menu-button` }), _jsx(MuiMenu, { id: "basic-menu", "data-testid": `${testKey}-menu`, anchorEl: anchorEl, open: open, onClose: handleClose, MenuListProps: {
27
+ return (_jsxs(Fragment, { children: [_jsx(MainButton, { displayIcon: icon, onClick: handleClick, type: ButtonType.Button, displayIconMode: ButtonDisplayMode.Only, hierarchy: hierarchy, size: size, style: style, testId: `${testKey}-menu-button` }), _jsx(MuiMenu, { id: "basic-menu", "data-testid": `${testKey}-menu`, anchorEl: anchorEl, open: open, onClose: handleClose, MenuListProps: {
28
28
  'aria-labelledby': 'basic-button',
29
29
  style: {
30
30
  padding: 'var(--spacing-tokens-size-in-px-spacing-spacing-xs)',
@@ -4,3 +4,4 @@ declare const meta: Meta<typeof Menu>;
4
4
  export default meta;
5
5
  type Story = StoryObj<typeof meta>;
6
6
  export declare const Default: Story;
7
+ export declare const Small: Story;
@@ -1,4 +1,5 @@
1
1
  import { Menu } from './Menu';
2
+ import { ButtonSize, ButtonHierarchy } from '../Buttons';
2
3
  const meta = {
3
4
  title: 'Design System/Components/UI/Menu',
4
5
  component: Menu,
@@ -34,6 +35,24 @@ const meta = {
34
35
  type: { summary: 'CSSProperties' },
35
36
  },
36
37
  },
38
+ size: {
39
+ control: 'select',
40
+ options: Object.values(ButtonSize),
41
+ description: 'Size of the trigger button',
42
+ table: {
43
+ type: { summary: 'ButtonSize' },
44
+ defaultValue: { summary: ButtonSize.xl },
45
+ },
46
+ },
47
+ hierarchy: {
48
+ control: 'select',
49
+ options: Object.values(ButtonHierarchy),
50
+ description: 'Hierarchy of the trigger button',
51
+ table: {
52
+ type: { summary: 'ButtonHierarchy' },
53
+ defaultValue: { summary: ButtonHierarchy.Secondary },
54
+ },
55
+ },
37
56
  },
38
57
  };
39
58
  export default meta;
@@ -46,3 +65,16 @@ export const Default = {
46
65
  ],
47
66
  },
48
67
  };
68
+ export const Small = {
69
+ args: {
70
+ ...Default.args,
71
+ size: ButtonSize.sm,
72
+ },
73
+ parameters: {
74
+ docs: {
75
+ description: {
76
+ story: 'The trigger button rendered at the small size, used in compact contexts such as the EntityCard action row.',
77
+ },
78
+ },
79
+ },
80
+ };
@@ -1,5 +1,6 @@
1
1
  export * from './Badge';
2
2
  export * from './Avatar';
3
+ export * from './CheckIcon';
3
4
  export * from './FeatureBanner';
4
5
  export * from './HintBubble';
5
6
  export * from './Logo';
@@ -1,5 +1,6 @@
1
1
  export * from './Badge';
2
2
  export * from './Avatar';
3
+ export * from './CheckIcon';
3
4
  export * from './FeatureBanner';
4
5
  export * from './HintBubble';
5
6
  export * from './Logo';
@@ -1,5 +1,6 @@
1
1
  import { Theme, SxProps } from '@mui/material';
2
2
  import { IconName } from '../../utils';
3
+ import { ButtonSize, ButtonHierarchy } from '../Buttons';
3
4
  /**
4
5
  * Badge size variants
5
6
  */
@@ -163,7 +164,53 @@ export type MenuProps = {
163
164
  style?: React.CSSProperties;
164
165
  /** Test key */
165
166
  testKey?: string;
167
+ /** Size of the trigger button */
168
+ size?: ButtonSize;
169
+ /** Hierarchy of the trigger button */
170
+ hierarchy?: ButtonHierarchy;
166
171
  };
172
+ /**
173
+ * Check icon color variants
174
+ */
175
+ export declare enum CheckIconColor {
176
+ Violet = "Violet",
177
+ Green = "Green"
178
+ }
179
+ /**
180
+ * Check icon size variants (xs = 16px, sm = 20px, md = 24px, lg = 28px)
181
+ */
182
+ export declare enum CheckIconSize {
183
+ xs = "xs",
184
+ sm = "sm",
185
+ md = "md",
186
+ lg = "lg"
187
+ }
188
+ /**
189
+ * Check icon type variants.
190
+ * - `Filled`: solid accent background + white check
191
+ * - `Line`: transparent background, accent ring + accent check
192
+ * - `Duotone`: light badge background + accent-colored check
193
+ */
194
+ export declare enum CheckIconType {
195
+ Filled = "Filled",
196
+ Line = "Line",
197
+ Duotone = "Duotone"
198
+ }
199
+ /**
200
+ * Check icon component props
201
+ */
202
+ export interface CheckIconProps {
203
+ /** Color variant */
204
+ color?: CheckIconColor;
205
+ /** Size variant (xs = 16px, md = 24px) */
206
+ size?: CheckIconSize;
207
+ /** Fill style */
208
+ type?: CheckIconType;
209
+ /** Custom style */
210
+ style?: React.CSSProperties;
211
+ /** Test id */
212
+ testId?: string;
213
+ }
167
214
  /**
168
215
  * Progress indicator mode variants
169
216
  */
@@ -61,6 +61,36 @@ export var LogoVariant;
61
61
  LogoVariant["Vertical"] = "vertical";
62
62
  LogoVariant["Icon"] = "icon";
63
63
  })(LogoVariant || (LogoVariant = {}));
64
+ /**
65
+ * Check icon color variants
66
+ */
67
+ export var CheckIconColor;
68
+ (function (CheckIconColor) {
69
+ CheckIconColor["Violet"] = "Violet";
70
+ CheckIconColor["Green"] = "Green";
71
+ })(CheckIconColor || (CheckIconColor = {}));
72
+ /**
73
+ * Check icon size variants (xs = 16px, sm = 20px, md = 24px, lg = 28px)
74
+ */
75
+ export var CheckIconSize;
76
+ (function (CheckIconSize) {
77
+ CheckIconSize["xs"] = "xs";
78
+ CheckIconSize["sm"] = "sm";
79
+ CheckIconSize["md"] = "md";
80
+ CheckIconSize["lg"] = "lg";
81
+ })(CheckIconSize || (CheckIconSize = {}));
82
+ /**
83
+ * Check icon type variants.
84
+ * - `Filled`: solid accent background + white check
85
+ * - `Line`: transparent background, accent ring + accent check
86
+ * - `Duotone`: light badge background + accent-colored check
87
+ */
88
+ export var CheckIconType;
89
+ (function (CheckIconType) {
90
+ CheckIconType["Filled"] = "Filled";
91
+ CheckIconType["Line"] = "Line";
92
+ CheckIconType["Duotone"] = "Duotone";
93
+ })(CheckIconType || (CheckIconType = {}));
64
94
  /**
65
95
  * Progress indicator mode variants
66
96
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@beydesign/storybook",
3
3
  "private": false,
4
- "version": "0.1.19",
4
+ "version": "0.1.21",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",