@syscore/ui-library 1.27.3 → 2.0.0-beta-publish-workflow-beta.2

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 (169) hide show
  1. package/client/App.tsx +0 -2
  2. package/client/assets/alert/icon-clear.svg +4 -0
  3. package/client/assets/alert/icon-done.svg +3 -0
  4. package/client/assets/avatar-identity/photo.png +0 -0
  5. package/client/assets/avatar-identity/placeholder-icon.svg +8 -0
  6. package/client/assets/breadcrumb/icon-ellipsis.svg +5 -0
  7. package/client/assets/breadcrumb/icon-home.svg +3 -0
  8. package/client/assets/breadcrumb/icon-separator.svg +3 -0
  9. package/client/assets/button-v01/icon-back.svg +4 -0
  10. package/client/assets/button-v01/icon-check.svg +3 -0
  11. package/client/assets/button-v01/icon-close.svg +4 -0
  12. package/client/assets/button-v01/icon-delete.svg +3 -0
  13. package/client/assets/button-v01/icon-device.svg +3 -0
  14. package/client/assets/dropdown/icon-carrot-contract.svg +3 -0
  15. package/client/assets/dropdown/icon-carrot-expand.svg +3 -0
  16. package/client/assets/dropdown/icon-clear.svg +4 -0
  17. package/client/assets/dropdown/icon-enter.svg +3 -0
  18. package/client/components/icons/UtilityChangeType.tsx +21 -0
  19. package/client/components/ui/alert-dialog-panel.tsx +246 -0
  20. package/client/components/ui/alert-v02.tsx +214 -0
  21. package/client/components/ui/app-bar.tsx +299 -0
  22. package/client/components/ui/avatar-identity.tsx +122 -0
  23. package/client/components/ui/banner.tsx +108 -0
  24. package/client/components/ui/bottom-navigation.tsx +223 -0
  25. package/client/components/ui/breadcrumb-trail.tsx +170 -0
  26. package/client/components/ui/button-v01.tsx +238 -0
  27. package/client/components/ui/button.tsx +2 -0
  28. package/client/components/ui/card.tsx +108 -26
  29. package/client/components/ui/code-badge.tsx +62 -0
  30. package/client/components/ui/date-picker.tsx +188 -0
  31. package/client/components/ui/dropdown.tsx +448 -0
  32. package/client/components/ui/empty-state.tsx +197 -0
  33. package/client/components/ui/file-upload.tsx +214 -0
  34. package/client/components/ui/footer.tsx +179 -0
  35. package/client/components/ui/layout.tsx +1182 -0
  36. package/client/components/ui/pagination-v01.tsx +157 -0
  37. package/client/components/ui/password-field-v01.tsx +67 -0
  38. package/client/components/ui/search-field-v01.tsx +192 -0
  39. package/client/components/ui/section-heading.tsx +49 -0
  40. package/client/components/ui/sidebar.tsx +2 -3
  41. package/client/components/ui/standard-navigation.tsx +8 -2
  42. package/client/components/ui/stepper.tsx +148 -0
  43. package/client/components/ui/switch-v01.tsx +71 -0
  44. package/client/components/ui/system-bar.tsx +119 -0
  45. package/client/components/ui/table-v01.tsx +230 -0
  46. package/client/components/ui/tabs-v01.tsx +99 -0
  47. package/client/components/ui/tag-v02.tsx +113 -0
  48. package/client/components/ui/text-area-v01.tsx +33 -0
  49. package/client/components/ui/text-field-v01.tsx +108 -0
  50. package/client/components/ui/timeline.tsx +181 -0
  51. package/client/components/ui/toast-v01.tsx +206 -0
  52. package/client/components/ui/tooltip.tsx +264 -71
  53. package/client/global.css +9565 -4378
  54. package/client/storybook.css +125 -0
  55. package/client/ui/Accordion.stories.tsx +1 -1
  56. package/client/ui/AlertDialogPanel/AlertDialogPanel.stories.tsx +150 -0
  57. package/client/ui/AlertV02/AlertV02.stories.tsx +188 -0
  58. package/client/ui/AppBar/app-bar.stories.tsx +280 -0
  59. package/client/ui/AspectRatio.stories.tsx +1 -1
  60. package/client/ui/AvatarIdentity/AvatarIdentity.stories.tsx +77 -0
  61. package/client/ui/Banner/banner.stories.tsx +238 -0
  62. package/client/ui/BottomNavigation/bottom-navigation.stories.tsx +285 -0
  63. package/client/ui/BreadcrumbTrail/BreadcrumbTrail.stories.tsx +232 -0
  64. package/client/ui/Button.stories.tsx +1 -1
  65. package/client/ui/ButtonV01/ButtonV01.stories.tsx +417 -0
  66. package/client/ui/Calendar/Calendar.stories.tsx +1 -1
  67. package/client/ui/Card.stories.tsx +285 -168
  68. package/client/ui/Carousel/Carousel.stories.tsx +1 -1
  69. package/client/ui/Chart/Chart.stories.tsx +1 -1
  70. package/client/ui/Checkbox.stories.tsx +1 -1
  71. package/client/ui/Collapsible/Collapsible.stories.tsx +1 -1
  72. package/client/ui/Colors.stories.tsx +1 -1
  73. package/client/ui/Command/Command.stories.tsx +1 -1
  74. package/client/ui/ContextMenu/ContextMenu.stories.tsx +1 -1
  75. package/client/ui/DatePicker/DatePicker.stories.tsx +293 -0
  76. package/client/ui/Dialog.stories.tsx +1 -1
  77. package/client/ui/Drawer/Drawer.stories.tsx +1 -1
  78. package/client/ui/Dropdown/Dropdown.stories.tsx +205 -0
  79. package/client/ui/EmptyState/empty-state.stories.tsx +251 -0
  80. package/client/ui/FileUpload/FileUpload.stories.tsx +218 -0
  81. package/client/ui/Footer/footer.stories.tsx +194 -0
  82. package/client/ui/Form/Form.stories.tsx +1 -1
  83. package/client/ui/Hero.stories.tsx +1 -1
  84. package/client/ui/HoverCard/HoverCard.stories.tsx +1 -1
  85. package/client/ui/Icons.stories.tsx +1 -1
  86. package/client/ui/Input.stories.tsx +1 -1
  87. package/client/ui/InputOTP/InputOTP.stories.tsx +1 -1
  88. package/client/ui/Label.stories.tsx +1 -1
  89. package/client/ui/Layout/Center.stories.tsx +100 -0
  90. package/client/ui/Layout/Columns.stories.tsx +136 -0
  91. package/client/ui/Layout/Container.stories.tsx +104 -0
  92. package/client/ui/Layout/FormLayout.stories.tsx +144 -0
  93. package/client/ui/Layout/Grid.stories.tsx +158 -0
  94. package/client/ui/Layout/GridSpan.stories.tsx +75 -0
  95. package/client/ui/Layout/HStack.stories.tsx +128 -0
  96. package/client/ui/Layout/Layout.stories.tsx +287 -0
  97. package/client/ui/Layout/LayoutContent.stories.tsx +91 -0
  98. package/client/ui/Layout/LayoutFooter.stories.tsx +77 -0
  99. package/client/ui/Layout/LayoutHeader.stories.tsx +104 -0
  100. package/client/ui/Layout/LayoutPanel.stories.tsx +143 -0
  101. package/client/ui/Layout/Positioned.stories.tsx +113 -0
  102. package/client/ui/Layout/Section.stories.tsx +84 -0
  103. package/client/ui/Layout/ShowHide.stories.tsx +99 -0
  104. package/client/ui/Layout/Spacer.stories.tsx +149 -0
  105. package/client/ui/Layout/Stack.stories.tsx +69 -0
  106. package/client/ui/Layout/StackItem.stories.tsx +124 -0
  107. package/client/ui/Layout/VStack.stories.tsx +101 -0
  108. package/client/ui/Layout/controls.ts +33 -0
  109. package/client/ui/Layout/demo.tsx +37 -0
  110. package/client/ui/Menubar/Menubar.stories.tsx +1 -1
  111. package/client/ui/MobileNav.stories.tsx +1 -1
  112. package/client/ui/Navigation.stories.tsx +1 -1
  113. package/client/ui/NavigationMenu/NavigationMenu.stories.tsx +1 -1
  114. package/client/ui/PageHeader.stories.tsx +13 -13
  115. package/client/ui/Pagination/Pagination.stories.tsx +178 -98
  116. package/client/ui/Pagination/PaginationLegacy.stories.tsx +121 -0
  117. package/client/ui/Panel.stories.tsx +1 -1
  118. package/client/ui/Popover/Popover.stories.tsx +1 -1
  119. package/client/ui/Progress/Progress.stories.tsx +1 -1
  120. package/client/ui/RadioGroup/RadioGroup.stories.tsx +1 -1
  121. package/client/ui/Resizable/Resizable.stories.tsx +1 -1
  122. package/client/ui/ScrollArea/ScrollArea.stories.tsx +1 -1
  123. package/client/ui/SearchField/SearchField.stories.tsx +153 -0
  124. package/client/ui/{SearchField.stories.tsx → SearchField/SearchFieldLegacy.stories.tsx} +2 -2
  125. package/client/ui/SectionBadge.stories.tsx +1 -1
  126. package/client/ui/Select.stories.tsx +1 -1
  127. package/client/ui/Separator/Separator.stories.tsx +1 -1
  128. package/client/ui/Sheet/Sheet.stories.tsx +1 -1
  129. package/client/ui/Sidebar/Sidebar.stories.tsx +57 -8
  130. package/client/ui/Skeleton/Skeleton.stories.tsx +1 -1
  131. package/client/ui/Slider/Slider.stories.tsx +1 -1
  132. package/client/ui/StandardNavigation.stories.tsx +1 -1
  133. package/client/ui/Stepper/Stepper.stories.tsx +344 -0
  134. package/client/ui/Switch.stories.tsx +1 -1
  135. package/client/ui/SwitchV01/SwitchV01.stories.tsx +152 -0
  136. package/client/ui/SystemBar/system-bar.stories.tsx +179 -0
  137. package/client/ui/Table/TableV01.stories.tsx +256 -0
  138. package/client/ui/Tabs.stories.tsx +1 -1
  139. package/client/ui/TabsV01/TabsV01.stories.tsx +144 -0
  140. package/client/ui/Tag.stories.tsx +1 -1
  141. package/client/ui/TagV02/TagStatusV02.stories.tsx +132 -0
  142. package/client/ui/TagV02/TagV02.stories.tsx +142 -0
  143. package/client/ui/TextAreaV01/TextAreaV01.stories.tsx +146 -0
  144. package/client/ui/TextFieldV01/PasswordFieldV01.stories.tsx +161 -0
  145. package/client/ui/TextFieldV01/TextFieldV01.stories.tsx +160 -0
  146. package/client/ui/Textarea.stories.tsx +1 -1
  147. package/client/ui/Timeline/timeline.stories.tsx +404 -0
  148. package/client/ui/Toast/Toast.stories.tsx +1 -1
  149. package/client/ui/ToastV01/ToastV01.stories.tsx +201 -0
  150. package/client/ui/Toaster/Toaster.stories.tsx +1 -1
  151. package/client/ui/Toggle.stories.tsx +1 -1
  152. package/client/ui/Tooltip.stories.tsx +287 -114
  153. package/client/ui/Typography.stories.tsx +1 -1
  154. package/dist/index.cjs.js +1 -1
  155. package/dist/index.d.ts +649 -13
  156. package/dist/index.es.js +4405 -653
  157. package/package.json +1 -1
  158. package/client/components/ui/badge.tsx +0 -33
  159. package/client/components/ui/sonner.tsx +0 -26
  160. package/client/components/ui/toggle-group.tsx +0 -76
  161. package/client/ui/Alert/Alert.stories.tsx +0 -82
  162. package/client/ui/AlertDialog/AlertDialog.stories.tsx +0 -106
  163. package/client/ui/Avatar/Avatar.stories.tsx +0 -94
  164. package/client/ui/Badge/Badge.stories.tsx +0 -60
  165. package/client/ui/Breadcrumb/Breadcrumb.stories.tsx +0 -97
  166. package/client/ui/DropdownMenu/DropdownMenu.stories.tsx +0 -139
  167. package/client/ui/Sonner/Sonner.stories.tsx +0 -48
  168. package/client/ui/Table/Table.stories.tsx +0 -135
  169. package/client/ui/ToggleGroup/ToggleGroup.stories.tsx +0 -88
@@ -0,0 +1,201 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { ToastV01 } from "../../components/ui/toast-v01";
3
+ import samplePhoto from "../../assets/avatar-identity/photo.png";
4
+
5
+ const LOREM =
6
+ "Lorem ipsum dolor sit amet consectetur adipiscing elit. Sit amet consectetur adipiscing elit quisque faucibus ex. Adipiscing elit quisque faucibus ex sapien vitae pellentesque.";
7
+
8
+ const appearances = ["solid", "subtle", "outline"] as const;
9
+ const colors = ["default", "success", "error"] as const;
10
+ const sizes = ["large", "small"] as const;
11
+ const indicators = ["icon", "avatar"] as const;
12
+
13
+ const meta = {
14
+ title: "Components/Toast",
15
+ component: ToastV01,
16
+ tags: ["autodocs"],
17
+ parameters: {
18
+ layout: "centered",
19
+ docs: {
20
+ description: {
21
+ component: [
22
+ "Transient notification. Existing `Components/ToastLegacy` (Radix `Toast`) is unchanged.",
23
+ "",
24
+ "Stories are the semantic states: **Default**, **Success**, **Error**.",
25
+ "Use **Controls** for appearance (solid / subtle / outline), size, and indicator.",
26
+ "All Variants shows the full set of **36**.",
27
+ "",
28
+ "---",
29
+ "",
30
+ "## Import",
31
+ "",
32
+ "```tsx",
33
+ `import { ToastV01 } from "@syscore/ui-library"`,
34
+ "```",
35
+ "",
36
+ "## How to use",
37
+ "",
38
+ "Pick `color` for meaning. Use `variant`, `size`, and `indicator` for presentation.",
39
+ 'A toast is announced to screen readers on appearance (`role="status"`), so keep the',
40
+ "copy short and put anything the user must act on behind Confirm.",
41
+ "",
42
+ "```tsx",
43
+ "const [visible, setVisible] = useState(true)",
44
+ "",
45
+ "visible ? (",
46
+ " <ToastV01",
47
+ ` color="success"`,
48
+ ` variant="solid"`,
49
+ ` size="large"`,
50
+ ` indicator="icon"`,
51
+ ` title="Changes saved"`,
52
+ ` description="Your updates are live for everyone on the project."`,
53
+ ` confirmLabel="View"`,
54
+ ` cancelLabel="Dismiss"`,
55
+ " onConfirm={handleView}",
56
+ " onCancel={() => setVisible(false)}",
57
+ " onDismiss={() => setVisible(false)}",
58
+ " />",
59
+ ") : null",
60
+ "```",
61
+ "",
62
+ "| Situation | Use |",
63
+ "|-----------|-----|",
64
+ '| Neutral notice | `color="default"` |',
65
+ '| Completed action | `color="success"` |',
66
+ '| Failure / blocking problem | `color="error"` |',
67
+ '| Single line, one action | `size="small"` (heading + Confirm only) |',
68
+ '| Person-specific message | `indicator="avatar"` and `avatarSrc` |',
69
+ "",
70
+ "## Props",
71
+ "",
72
+ "| Prop | Values | Default | Effect |",
73
+ "|------|--------|---------|--------|",
74
+ '| `color` | `"default"` \\| `"success"` \\| `"error"` | `"default"` | Semantic state (stories) |',
75
+ '| `variant` | `"solid"` \\| `"subtle"` \\| `"outline"` | `"solid"` | Surface treatment |',
76
+ '| `size` | `"large"` \\| `"small"` | `"large"` | Stacked copy + two actions vs single row |',
77
+ '| `indicator` | `"icon"` \\| `"avatar"` | `"icon"` | Leading checkmark or 32px avatar |',
78
+ "| `action` | `boolean` | `true` | Confirm (and Cancel on large) |",
79
+ "| `title` | `ReactNode` | — | Heading |",
80
+ "| `description` | `ReactNode` | — | Body copy (large only) |",
81
+ '| `confirmLabel` | `ReactNode` | `"Confirm"` | Primary pill |',
82
+ '| `cancelLabel` | `ReactNode` | `"Cancel"` | Secondary pill (large only) |',
83
+ "| `onConfirm` / `onCancel` / `onDismiss` | `() => void` | — | Action and close handlers |",
84
+ ].join("\n"),
85
+ },
86
+ },
87
+ },
88
+ argTypes: {
89
+ variant: {
90
+ name: "Appearance",
91
+ control: "select",
92
+ options: [...appearances],
93
+ },
94
+ size: { control: "select", options: [...sizes] },
95
+ indicator: { control: "select", options: [...indicators] },
96
+ action: { control: "boolean" },
97
+ title: { control: "text" },
98
+ description: { control: "text" },
99
+ confirmLabel: { control: "text" },
100
+ cancelLabel: { control: "text" },
101
+ avatarSrc: { control: "text" },
102
+ color: { table: { disable: true } },
103
+ },
104
+ args: {
105
+ variant: "solid",
106
+ size: "large",
107
+ indicator: "icon",
108
+ action: true,
109
+ title: "Alert Heading",
110
+ description: LOREM,
111
+ confirmLabel: "Confirm",
112
+ cancelLabel: "Cancel",
113
+ avatarSrc: samplePhoto,
114
+ },
115
+ } satisfies Meta<typeof ToastV01>;
116
+
117
+ export default meta;
118
+ type Story = StoryObj<typeof meta>;
119
+
120
+ export const Default: Story = {
121
+ args: { color: "default" },
122
+ };
123
+
124
+ export const Success: Story = {
125
+ args: { color: "success" },
126
+ };
127
+
128
+ export const Error: Story = {
129
+ args: { color: "error" },
130
+ };
131
+
132
+ export const AllVariants: Story = {
133
+ name: "All Variants",
134
+ render: (args) => (
135
+ <div
136
+ style={{
137
+ display: "grid",
138
+ // minmax(0, 1fr) so the track can shrink below its max-content width.
139
+ gridTemplateColumns: "minmax(0, 1fr)",
140
+ justifyItems: "center",
141
+ gap: 40,
142
+ width: "100%",
143
+ }}
144
+ >
145
+ {sizes.flatMap((size) =>
146
+ indicators.map((indicator) => (
147
+ <section
148
+ key={`${size}-${indicator}`}
149
+ style={{ width: "100%", maxWidth: 1112 }}
150
+ >
151
+ <h3
152
+ style={{
153
+ margin: "0 0 16px",
154
+ fontSize: 14,
155
+ fontWeight: 600,
156
+ textTransform: "uppercase",
157
+ letterSpacing: 0.5,
158
+ color: "#52545d",
159
+ textAlign: "center",
160
+ }}
161
+ >
162
+ {size} · {indicator}
163
+ </h3>
164
+ <div
165
+ style={{
166
+ display: "grid",
167
+ gridTemplateColumns: "repeat(auto-fill, minmax(360px, 1fr))",
168
+ maxWidth: 1112,
169
+ margin: "0 auto",
170
+ gap: 16,
171
+ justifyItems: "center",
172
+ }}
173
+ >
174
+ {appearances.flatMap((variant) =>
175
+ colors.map((color) => (
176
+ <ToastV01
177
+ key={`${size}-${indicator}-${variant}-${color}`}
178
+ {...args}
179
+ size={size}
180
+ indicator={indicator}
181
+ variant={variant}
182
+ color={color}
183
+ title="Alert Heading"
184
+ description={LOREM}
185
+ avatarSrc={samplePhoto}
186
+ />
187
+ )),
188
+ )}
189
+ </div>
190
+ </section>
191
+ )),
192
+ )}
193
+ </div>
194
+ ),
195
+ argTypes: {
196
+ variant: { table: { disable: true } },
197
+ size: { table: { disable: true } },
198
+ indicator: { table: { disable: true } },
199
+ },
200
+ parameters: { layout: "padded" },
201
+ };
@@ -4,7 +4,7 @@ import { useToast } from "../../hooks/use-toast";
4
4
  import { Button } from "../../components/ui/button";
5
5
 
6
6
  const meta = {
7
- title: "UI/Toaster",
7
+ title: "Components/Toaster",
8
8
  tags: ["autodocs"],
9
9
  parameters: {
10
10
  layout: "centered",
@@ -6,7 +6,7 @@ import { UtilityAccordion } from "../components/icons/UtilityAccordion";
6
6
  import { UtilityText } from "../components/icons/UtilityText";
7
7
 
8
8
  const meta = {
9
- title: "Review/Toggle",
9
+ title: "Components/Toggle",
10
10
  component: SegmentedControl,
11
11
  tags: ["autodocs"],
12
12
  parameters: {
@@ -1,139 +1,312 @@
1
+ import * as React from "react";
1
2
  import type { Meta, StoryObj } from "@storybook/react";
2
3
  import {
3
4
  Tooltip,
4
5
  TooltipContent,
6
+ TooltipPanel,
5
7
  TooltipTrigger,
6
8
  } from "../components/ui/tooltip";
7
- import { Separator } from "../components/ui/separator";
8
- import { Button } from "../components/ui/button";
9
- import { Text } from "../components/ui/typography";
10
9
 
11
- const meta = {
12
- title: "Review/Tooltip",
13
- component: Tooltip,
14
- tags: ["autodocs"],
15
- parameters: {
16
- layout: "centered",
17
- },
18
- } satisfies Meta<typeof Tooltip>;
10
+ const tones = ["dark", "light"] as const;
11
+ const directions = ["top", "bottom", "left", "right"] as const;
19
12
 
20
- export default meta;
13
+ // The panel is portaled, so centring it means boxing the trigger inside the
14
+ // group's footprint and insetting it by the panel's own alignOffset shift.
15
+ const PANEL_W = 400;
16
+ const PANEL_H = { vertical: 234, horizontal: 226 } as const;
17
+ const SIDE_OFFSET = 12;
18
+ const TRIGGER = { w: 65, h: 24 };
19
+ const MARKER_CENTRE = { inline: 54 + 5, block: 53 + 5 } as const;
21
20
 
22
- type Story = StoryObj<typeof meta>;
21
+ const shift = {
22
+ inline: MARKER_CENTRE.inline - TRIGGER.w / 2,
23
+ block: MARKER_CENTRE.block - TRIGGER.h / 2,
24
+ };
23
25
 
24
- export const Default: Story = {
25
- render: () => (
26
- <div className="p-16">
27
- <Tooltip>
28
- <TooltipTrigger>Hover</TooltipTrigger>
29
- <TooltipContent className="w-[400px]">
30
- <div className="space-y-3">
31
- <Text as="h1" variant="body-base" className="text-white">
32
- Strategy Details
33
- </Text>
34
- <div className="space-y-1">
35
- <Text as="p" variant="body-small" className="text-white/90">
36
- Status: Active
37
- </Text>
38
- <Text as="p" variant="body-small" className="text-white/90">
39
- Score: 12 PTS
40
- </Text>
41
- <Text as="p" variant="body-small" className="text-white/90">
42
- Last updated: 2 days ago
43
- </Text>
44
- </div>
45
- </div>
46
- </TooltipContent>
47
- </Tooltip>
48
- </div>
49
- ),
26
+ const group: Record<
27
+ (typeof directions)[number],
28
+ { width: number; height: number; trigger: React.CSSProperties }
29
+ > = {
30
+ top: {
31
+ width: PANEL_W,
32
+ height: TRIGGER.h + SIDE_OFFSET + PANEL_H.vertical,
33
+ trigger: { left: shift.inline, top: 0 },
34
+ },
35
+ bottom: {
36
+ width: PANEL_W,
37
+ height: TRIGGER.h + SIDE_OFFSET + PANEL_H.vertical,
38
+ trigger: { left: shift.inline, bottom: 0 },
39
+ },
40
+ left: {
41
+ width: TRIGGER.w + SIDE_OFFSET + PANEL_W,
42
+ height: PANEL_H.horizontal,
43
+ trigger: { left: 0, top: shift.block },
44
+ },
45
+ right: {
46
+ width: TRIGGER.w + SIDE_OFFSET + PANEL_W,
47
+ height: PANEL_H.horizontal,
48
+ trigger: { right: 0, top: shift.block },
49
+ },
50
50
  };
51
51
 
52
- export const Click: Story = {
53
- render: () => (
54
- <div className="p-16">
55
- <Tooltip trigger="click">
56
- <TooltipTrigger>Click me</TooltipTrigger>
57
- <TooltipContent className="w-[400px]">
58
- <div className="space-y-3">
59
- <Text as="h1" variant="body-base" className="text-white">
60
- Click Tooltip
61
- </Text>
62
- <div className="space-y-1">
63
- <Text as="p" variant="body-small" className="text-white/90">
64
- This tooltip opens on click and closes when clicking outside.
65
- </Text>
66
- </div>
67
- </div>
68
- </TooltipContent>
69
- </Tooltip>
70
- </div>
71
- ),
52
+ type StoryArgs = React.ComponentProps<typeof TooltipContent> & {
53
+ /** `trigger` on the `Tooltip` root. */
54
+ trigger?: "hover" | "click";
72
55
  };
73
56
 
74
- export const Simple: Story = {
75
- render: () => (
76
- <div className="p-16">
77
- <Tooltip>
78
- <TooltipTrigger>Hover (Simple)</TooltipTrigger>
79
- <TooltipContent variant="simple">
80
- <Text as="p" variant="body-small">
81
- <span className="font-semibold">Required</span> for certification
82
- </Text>
83
- </TooltipContent>
84
- </Tooltip>
85
- </div>
86
- ),
57
+ const copy = {
58
+ heading: "Heading",
59
+ body: "Body text",
60
+ conditionalText: "Conditional body text",
61
+ primaryLabel: "Label",
62
+ secondaryLabel: "Label",
87
63
  };
88
64
 
89
- export const LongText: Story = {
90
- render: () => (
91
- <div className="p-16 h-screen">
92
- <Tooltip trigger="click">
93
- <TooltipTrigger>Click for details</TooltipTrigger>
94
- <TooltipContent className="w-[400px]">
95
- <div className="space-y-2">
96
- <Text as="h1" variant="body-base" className="text-white">
97
- WELL Feature Details
98
- </Text>
99
- <Text as="p" variant="body-small" className="text-white/90">
100
- This feature requires documentation of at least two instances of
101
- biophilic design elements incorporated into the space, including
102
- natural materials, plants, water features, or views to the
103
- outside. All elements must be verified by a WELL Assessor during
104
- the on-site performance verification visit.
105
- </Text>
106
- </div>
107
- </TooltipContent>
108
- </Tooltip>
65
+ const meta = {
66
+ title: "Components/Tooltip",
67
+ component: TooltipContent,
68
+ tags: ["autodocs"],
69
+ args: {
70
+ tone: "dark",
71
+ direction: "top",
72
+ trigger: "click",
73
+ ...copy,
74
+ },
75
+ argTypes: {
76
+ direction: {
77
+ control: "inline-radio",
78
+ options: [...directions],
79
+ description:
80
+ "Which edge the marker sits on. That edge also carries the extra 8px of padding, and the panel opens on the opposite side so the marker points back at the trigger.",
81
+ table: { defaultValue: { summary: "top" } },
82
+ },
83
+ trigger: {
84
+ control: "inline-radio",
85
+ options: ["hover", "click"],
86
+ description:
87
+ "How the panel opens. `click` stays put until you click the trigger again, click outside, or press Escape. `hover` follows the pointer — better for short labels than for this panel.",
88
+ table: { defaultValue: { summary: "click" } },
89
+ },
90
+ heading: { control: "text", description: "Title line." },
91
+ body: {
92
+ control: "text",
93
+ description: "Supporting copy beneath the heading.",
94
+ },
95
+ conditionalText: {
96
+ control: "text",
97
+ description:
98
+ "Extra copy below the dashed divider. Clear it to drop both.",
99
+ },
100
+ primaryLabel: { control: "text", description: "Filled action pill." },
101
+ secondaryLabel: { control: "text", description: "Muted action pill." },
102
+ tone: { table: { disable: true } },
103
+ align: { table: { disable: true } },
104
+ sideOffset: { table: { disable: true } },
105
+ alignOffset: { table: { disable: true } },
106
+ children: { table: { disable: true } },
107
+ className: { table: { disable: true } },
108
+ asChild: { table: { disable: true } },
109
+ },
110
+ parameters: {
111
+ layout: "padded",
112
+ docs: {
113
+ description: {
114
+ component: [
115
+ "Tooltip panel: a 400px card with a heading, supporting copy, a dashed",
116
+ "divider, conditional copy, and up to two action pills. A half-ellipse",
117
+ "marker sits flush with the edge that faces the trigger.",
118
+ "",
119
+ "Stories are the two tones: **Dark** and **Light** — click the trigger to",
120
+ "open one. Use **Controls** for `direction`, `trigger`, and the copy.",
121
+ "All Variants shows the full set of **8**.",
122
+ "",
123
+ "---",
124
+ "",
125
+ "## Import",
126
+ "",
127
+ "```tsx",
128
+ `import { Tooltip, TooltipTrigger, TooltipContent } from "@syscore/ui-library"`,
129
+ "```",
130
+ "",
131
+ "## How to use",
132
+ "",
133
+ "Pass the copy as props — `heading`, `body`, `conditionalText` — rather than",
134
+ "children, so the 32px rhythm and cap-height trim stay exact. Supplying",
135
+ "`conditionalText` adds the dashed divider above it.",
136
+ "",
137
+ "```tsx",
138
+ "<Tooltip>",
139
+ " <TooltipTrigger>Details</TooltipTrigger>",
140
+ " <TooltipContent",
141
+ ` direction="top"`,
142
+ ` tone="dark"`,
143
+ ` heading="Heading"`,
144
+ ` body="Body text"`,
145
+ ` conditionalText="Conditional body text"`,
146
+ ` primaryLabel="Label"`,
147
+ ` secondaryLabel="Label"`,
148
+ " onPrimaryAction={handleConfirm}",
149
+ " onSecondaryAction={handleDismiss}",
150
+ " />",
151
+ "</Tooltip>",
152
+ "```",
153
+ "",
154
+ "`trigger` defaults to `click`: the panel opens on click and stays until you",
155
+ "click the trigger again, click outside, or press Escape. That suits a surface",
156
+ "this size with two action pills, and it is the only mode that works on touch,",
157
+ "where there is no hover at all.",
158
+ "",
159
+ '`trigger="hover"` fits short labels instead. Content stays hoverable in that',
160
+ "mode, so pills are still reachable, but the panel closes as soon as the",
161
+ "pointer leaves.",
162
+ "",
163
+ "## Placement",
164
+ "",
165
+ "`direction` names the edge the marker sits on. That edge takes the extra 8px",
166
+ "of padding, and the panel opens on the opposite side so the marker points",
167
+ "back at the trigger:",
168
+ "",
169
+ "| `direction` | Marker edge | Extra padding | Panel opens |",
170
+ "|---|---|---|---|",
171
+ '| `"top"` | top | `padding-top` | below the trigger |',
172
+ '| `"bottom"` | bottom | `padding-bottom` | above the trigger |',
173
+ '| `"left"` | left | `padding-left` | right of the trigger |',
174
+ '| `"right"` | right | `padding-right` | left of the trigger |',
175
+ "",
176
+ "The marker keeps a fixed 54px offset from the leading edge and does not",
177
+ "follow the trigger — override `--tooltip-marker-inline` (top/bottom) or",
178
+ "`--tooltip-marker-block` (left/right) to move it.",
179
+ "",
180
+ "## Props",
181
+ "",
182
+ "| Prop | Values | Default | Effect |",
183
+ "|------|--------|---------|--------|",
184
+ '| `tone` | `"dark"` \\| `"light"` | `"dark"` | Colour scheme (stories) |',
185
+ '| `direction` | `"top"` \\| `"bottom"` \\| `"left"` \\| `"right"` | `"top"` | Marker edge; also sets the padding side and where the panel opens |',
186
+ "| `heading` | `ReactNode` | — | Title line (16px/21px, weight 500) |",
187
+ "| `body` | `ReactNode` | — | Supporting copy (16px/21px, weight 400) |",
188
+ "| `conditionalText` | `ReactNode` | — | Copy below the dashed divider; adds the divider |",
189
+ "| `primaryLabel` | `ReactNode` | — | Filled pill — cyan on dark, blue on light |",
190
+ "| `secondaryLabel` | `ReactNode` | — | Muted pill — grey on dark, outlined white on light |",
191
+ "| `onPrimaryAction` | `() => void` | — | Filled pill handler |",
192
+ "| `onSecondaryAction` | `() => void` | — | Muted pill handler |",
193
+ '| `trigger` (on `Tooltip`) | `"click"` \\| `"hover"` | `"click"` | How the panel opens |',
194
+ ].join("\n"),
195
+ },
196
+ },
197
+ },
198
+ render: ({ trigger, ...args }) => (
199
+ <div
200
+ style={{
201
+ display: "flex",
202
+ height: 340,
203
+ alignItems: "center",
204
+ justifyContent: "center",
205
+ }}
206
+ >
207
+ <div
208
+ style={{
209
+ position: "relative",
210
+ width: group[args.direction ?? "top"].width,
211
+ height: group[args.direction ?? "top"].height,
212
+ }}
213
+ >
214
+ <div
215
+ style={{
216
+ position: "absolute",
217
+ ...group[args.direction ?? "top"].trigger,
218
+ }}
219
+ >
220
+ <Tooltip trigger={trigger}>
221
+ <TooltipTrigger className="tooltip-link-trigger">
222
+ {trigger === "click" ? "Click me" : "Hover me"}
223
+ </TooltipTrigger>
224
+ <TooltipContent {...args} />
225
+ </Tooltip>
226
+ </div>
227
+ </div>
109
228
  </div>
110
229
  ),
230
+ } satisfies Meta<StoryArgs>;
231
+
232
+ export default meta;
233
+
234
+ type Story = StoryObj<typeof meta>;
235
+
236
+ export const Dark: Story = {
237
+ args: { tone: "dark" },
238
+ };
239
+
240
+ export const Light: Story = {
241
+ args: { tone: "light" },
111
242
  };
112
243
 
113
- export const Variants: Story = {
244
+ export const AllVariants: Story = {
245
+ name: "All Variants",
246
+ parameters: { layout: "padded", controls: { disable: true } },
247
+ argTypes: {
248
+ direction: { table: { disable: true } },
249
+ trigger: { table: { disable: true } },
250
+ },
114
251
  render: () => (
115
- <div className="p-16 flex gap-16">
116
- <Tooltip>
117
- <TooltipTrigger>Default</TooltipTrigger>
118
- <TooltipContent className="w-[300px]">
119
- <div className="space-y-2">
120
- <Text as="h1" variant="body-base" className="text-white">
121
- Default Variant
122
- </Text>
123
- <Text as="p" variant="body-small" className="text-white/90">
124
- Dark background tooltip.
125
- </Text>
252
+ <div
253
+ style={{
254
+ display: "grid",
255
+ // minmax(0, 1fr) so the track can shrink below its max-content width.
256
+ gridTemplateColumns: "minmax(0, 1fr)",
257
+ justifyItems: "center",
258
+ gap: 40,
259
+ width: "100%",
260
+ }}
261
+ >
262
+ {tones.map((tone) => (
263
+ <section key={tone} style={{ width: "100%", maxWidth: 824 }}>
264
+ <h3
265
+ style={{
266
+ margin: "0 0 16px",
267
+ fontSize: 14,
268
+ fontWeight: 600,
269
+ textTransform: "uppercase",
270
+ letterSpacing: 0.5,
271
+ color: "#52545d",
272
+ textAlign: "center",
273
+ }}
274
+ >
275
+ {tone}
276
+ </h3>
277
+ <div
278
+ style={{
279
+ display: "grid",
280
+ // Capped at two tracks so the four directions land 2x2.
281
+ gridTemplateColumns: "repeat(auto-fill, minmax(400px, 1fr))",
282
+ maxWidth: 824,
283
+ margin: "0 auto",
284
+ gap: 24,
285
+ alignItems: "start",
286
+ justifyItems: "center",
287
+ }}
288
+ >
289
+ {directions.map((direction) => (
290
+ <div key={direction}>
291
+ <p
292
+ style={{
293
+ margin: "0 0 8px",
294
+ fontSize: 11,
295
+ fontWeight: 600,
296
+ textTransform: "uppercase",
297
+ letterSpacing: 0.4,
298
+ color: "#71747d",
299
+ textAlign: "center",
300
+ }}
301
+ >
302
+ {direction}
303
+ </p>
304
+ <TooltipPanel tone={tone} direction={direction} {...copy} />
305
+ </div>
306
+ ))}
126
307
  </div>
127
- </TooltipContent>
128
- </Tooltip>
129
- <Tooltip>
130
- <TooltipTrigger>Simple</TooltipTrigger>
131
- <TooltipContent variant="simple">
132
- <Text as="p" variant="body-base">
133
- Required for certification
134
- </Text>
135
- </TooltipContent>
136
- </Tooltip>
308
+ </section>
309
+ ))}
137
310
  </div>
138
311
  ),
139
312
  };
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from "@storybook/react";
2
2
  import { Text, List, ListItem } from "@/components/ui/typography";
3
3
 
4
4
  const meta = {
5
- title: "Review/Typography",
5
+ title: "Components/Typography",
6
6
  component: Text,
7
7
  tags: ["autodocs"],
8
8
  parameters: {