@adres/mgda-ui 0.0.0-stage → 0.0.1
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.
- package/brand-knowledge.json +451 -0
- package/knowledge/accordion.md +135 -0
- package/knowledge/activity-rings.md +123 -0
- package/knowledge/alert.md +184 -0
- package/knowledge/answer-actions.md +217 -0
- package/knowledge/aspect-ratio.md +82 -0
- package/knowledge/avatar.md +158 -0
- package/knowledge/badge.md +172 -0
- package/knowledge/bar-graph.md +167 -0
- package/knowledge/bento-grid.md +115 -0
- package/knowledge/blockquote.md +153 -0
- package/knowledge/box-list.md +177 -0
- package/knowledge/bullet-list.md +128 -0
- package/knowledge/button-group.md +179 -0
- package/knowledge/button.md +231 -0
- package/knowledge/calendar.md +168 -0
- package/knowledge/card.md +321 -0
- package/knowledge/cards-nav.md +170 -0
- package/knowledge/chain-of-thought.md +164 -0
- package/knowledge/chat-container.md +135 -0
- package/knowledge/chat-message.md +224 -0
- package/knowledge/checkbox-card.md +172 -0
- package/knowledge/checkbox.md +111 -0
- package/knowledge/citations.md +241 -0
- package/knowledge/combobox.md +265 -0
- package/knowledge/countdown-pill.md +153 -0
- package/knowledge/cta.md +118 -0
- package/knowledge/data-list.md +173 -0
- package/knowledge/data-table.md +220 -0
- package/knowledge/dialog.md +249 -0
- package/knowledge/drawer.md +236 -0
- package/knowledge/dropdown-menu.md +238 -0
- package/knowledge/dropzone.md +153 -0
- package/knowledge/email-chips-input.md +161 -0
- package/knowledge/empty-state.md +106 -0
- package/knowledge/entity-header.md +166 -0
- package/knowledge/feature-grid.md +120 -0
- package/knowledge/feedback-bar.md +127 -0
- package/knowledge/footer.md +116 -0
- package/knowledge/heading.md +189 -0
- package/knowledge/hero.md +361 -0
- package/knowledge/input-button.md +187 -0
- package/knowledge/input-group.md +185 -0
- package/knowledge/input.md +256 -0
- package/knowledge/inset.md +177 -0
- package/knowledge/kpi-card.md +206 -0
- package/knowledge/list-nav.md +181 -0
- package/knowledge/logo-cloud.md +102 -0
- package/knowledge/media-gallery.md +158 -0
- package/knowledge/message-actions.md +175 -0
- package/knowledge/nav-sidebar.md +225 -0
- package/knowledge/number-stepper.md +159 -0
- package/knowledge/page-header.md +109 -0
- package/knowledge/pagination.md +225 -0
- package/knowledge/popover.md +159 -0
- package/knowledge/pricing.md +132 -0
- package/knowledge/progress.md +156 -0
- package/knowledge/prompt-input.md +337 -0
- package/knowledge/prompt-suggestions.md +168 -0
- package/knowledge/quota-bar.md +160 -0
- package/knowledge/quote.md +116 -0
- package/knowledge/radio-card.md +172 -0
- package/knowledge/radio-group.md +135 -0
- package/knowledge/reasoning.md +142 -0
- package/knowledge/search.md +131 -0
- package/knowledge/section-nav.md +162 -0
- package/knowledge/segmented-control.md +181 -0
- package/knowledge/select-button.md +205 -0
- package/knowledge/select.md +210 -0
- package/knowledge/separator.md +75 -0
- package/knowledge/shimmer-text.md +140 -0
- package/knowledge/sidebar.md +227 -0
- package/knowledge/skeleton.md +142 -0
- package/knowledge/slider.md +133 -0
- package/knowledge/slideshow.md +159 -0
- package/knowledge/source.md +156 -0
- package/knowledge/spinner.md +120 -0
- package/knowledge/stat-strip.md +128 -0
- package/knowledge/stats.md +117 -0
- package/knowledge/step-indicator.md +157 -0
- package/knowledge/switch.md +88 -0
- package/knowledge/table.md +190 -0
- package/knowledge/tabs.md +169 -0
- package/knowledge/testimonials.md +115 -0
- package/knowledge/text-link.md +192 -0
- package/knowledge/text.md +194 -0
- package/knowledge/thinking-bar.md +115 -0
- package/knowledge/thinking-steps.md +140 -0
- package/knowledge/toaster.md +127 -0
- package/knowledge/toggle.md +113 -0
- package/knowledge/tool-call-card.md +241 -0
- package/knowledge/tooltip.md +120 -0
- package/knowledge/top-nav.md +138 -0
- package/knowledge.json +19854 -0
- package/package.json +94 -4
- package/src/components/accordion.stories.tsx +46 -0
- package/src/components/accordion.tsx +74 -0
- package/src/components/activity-rings.stories.tsx +73 -0
- package/src/components/activity-rings.tsx +121 -0
- package/src/components/alert.stories.tsx +43 -0
- package/src/components/alert.tsx +132 -0
- package/src/components/answer-actions.stories.tsx +54 -0
- package/src/components/answer-actions.test.tsx +37 -0
- package/src/components/answer-actions.tsx +313 -0
- package/src/components/aspect-ratio.stories.tsx +32 -0
- package/src/components/aspect-ratio.tsx +5 -0
- package/src/components/avatar.stories.tsx +40 -0
- package/src/components/avatar.tsx +154 -0
- package/src/components/badge.stories.tsx +27 -0
- package/src/components/badge.tsx +63 -0
- package/src/components/bar-graph.stories.tsx +28 -0
- package/src/components/bar-graph.tsx +204 -0
- package/src/components/bento-grid.stories.tsx +23 -0
- package/src/components/bento-grid.tsx +67 -0
- package/src/components/blockquote.stories.tsx +28 -0
- package/src/components/blockquote.tsx +88 -0
- package/src/components/box-list.stories.tsx +25 -0
- package/src/components/box-list.tsx +108 -0
- package/src/components/bullet-list.stories.tsx +22 -0
- package/src/components/bullet-list.tsx +101 -0
- package/src/components/button-group.stories.tsx +31 -0
- package/src/components/button-group.tsx +156 -0
- package/src/components/button.stories.tsx +66 -0
- package/src/components/button.test.ts +17 -0
- package/src/components/button.tsx +139 -0
- package/src/components/calendar.stories.tsx +25 -0
- package/src/components/calendar.tsx +367 -0
- package/src/components/card.stories.tsx +44 -0
- package/src/components/card.tsx +392 -0
- package/src/components/cards-nav.stories.tsx +35 -0
- package/src/components/cards-nav.tsx +115 -0
- package/src/components/chain-of-thought.stories.tsx +67 -0
- package/src/components/chain-of-thought.test.tsx +50 -0
- package/src/components/chain-of-thought.tsx +170 -0
- package/src/components/chat-container.stories.tsx +47 -0
- package/src/components/chat-container.test.tsx +49 -0
- package/src/components/chat-container.tsx +222 -0
- package/src/components/chat-message.stories.tsx +52 -0
- package/src/components/chat-message.test.tsx +36 -0
- package/src/components/chat-message.tsx +163 -0
- package/src/components/checkbox-card.stories.tsx +27 -0
- package/src/components/checkbox-card.tsx +200 -0
- package/src/components/checkbox.stories.tsx +26 -0
- package/src/components/checkbox.tsx +61 -0
- package/src/components/citations.stories.tsx +64 -0
- package/src/components/citations.test.tsx +61 -0
- package/src/components/citations.tsx +220 -0
- package/src/components/combobox.stories.tsx +184 -0
- package/src/components/combobox.test.tsx +82 -0
- package/src/components/combobox.tsx +407 -0
- package/src/components/countdown-pill.stories.tsx +24 -0
- package/src/components/countdown-pill.tsx +129 -0
- package/src/components/cta.stories.tsx +29 -0
- package/src/components/cta.tsx +52 -0
- package/src/components/data-list.stories.tsx +50 -0
- package/src/components/data-list.test.tsx +35 -0
- package/src/components/data-list.tsx +181 -0
- package/src/components/data-table.stories.tsx +29 -0
- package/src/components/data-table.tsx +200 -0
- package/src/components/dialog.stories.tsx +42 -0
- package/src/components/dialog.tsx +200 -0
- package/src/components/drawer.stories.tsx +48 -0
- package/src/components/drawer.tsx +385 -0
- package/src/components/dropdown-menu.stories.tsx +41 -0
- package/src/components/dropdown-menu.tsx +252 -0
- package/src/components/dropzone.stories.tsx +19 -0
- package/src/components/dropzone.tsx +250 -0
- package/src/components/email-chips-input.stories.tsx +135 -0
- package/src/components/email-chips-input.tsx +210 -0
- package/src/components/empty-state.stories.tsx +22 -0
- package/src/components/empty-state.tsx +35 -0
- package/src/components/entity-header.stories.tsx +43 -0
- package/src/components/entity-header.test.tsx +35 -0
- package/src/components/entity-header.tsx +92 -0
- package/src/components/feature-grid.stories.tsx +25 -0
- package/src/components/feature-grid.tsx +87 -0
- package/src/components/feedback-bar.stories.tsx +25 -0
- package/src/components/feedback-bar.test.tsx +24 -0
- package/src/components/feedback-bar.tsx +62 -0
- package/src/components/footer.stories.tsx +23 -0
- package/src/components/footer.tsx +78 -0
- package/src/components/heading.stories.tsx +45 -0
- package/src/components/heading.test.tsx +22 -0
- package/src/components/heading.tsx +77 -0
- package/src/components/hero.stories.tsx +38 -0
- package/src/components/hero.tsx +498 -0
- package/src/components/input-button.stories.tsx +19 -0
- package/src/components/input-button.tsx +112 -0
- package/src/components/input-group.stories.tsx +38 -0
- package/src/components/input-group.tsx +157 -0
- package/src/components/input.stories.tsx +31 -0
- package/src/components/input.tsx +284 -0
- package/src/components/inset.stories.tsx +29 -0
- package/src/components/inset.tsx +70 -0
- package/src/components/kpi-card.stories.tsx +29 -0
- package/src/components/kpi-card.tsx +284 -0
- package/src/components/list-nav.stories.tsx +22 -0
- package/src/components/list-nav.tsx +145 -0
- package/src/components/logo-cloud.stories.tsx +23 -0
- package/src/components/logo-cloud.tsx +42 -0
- package/src/components/media-gallery.stories.tsx +25 -0
- package/src/components/media-gallery.tsx +347 -0
- package/src/components/message-actions.stories.tsx +45 -0
- package/src/components/message-actions.test.tsx +32 -0
- package/src/components/message-actions.tsx +119 -0
- package/src/components/nav-sidebar.stories.tsx +58 -0
- package/src/components/nav-sidebar.test.tsx +54 -0
- package/src/components/nav-sidebar.tsx +462 -0
- package/src/components/number-stepper.stories.tsx +19 -0
- package/src/components/number-stepper.tsx +158 -0
- package/src/components/page-header.stories.tsx +25 -0
- package/src/components/page-header.tsx +64 -0
- package/src/components/pagination.stories.tsx +110 -0
- package/src/components/pagination.test.tsx +46 -0
- package/src/components/pagination.tsx +144 -0
- package/src/components/popover.stories.tsx +26 -0
- package/src/components/popover.tsx +62 -0
- package/src/components/pricing.stories.tsx +42 -0
- package/src/components/pricing.tsx +130 -0
- package/src/components/progress.stories.tsx +22 -0
- package/src/components/progress.tsx +189 -0
- package/src/components/prompt-input.stories.tsx +62 -0
- package/src/components/prompt-input.test.tsx +106 -0
- package/src/components/prompt-input.tsx +343 -0
- package/src/components/prompt-suggestions.stories.tsx +53 -0
- package/src/components/prompt-suggestions.test.tsx +37 -0
- package/src/components/prompt-suggestions.tsx +122 -0
- package/src/components/quota-bar.stories.tsx +19 -0
- package/src/components/quota-bar.tsx +191 -0
- package/src/components/quote.stories.tsx +18 -0
- package/src/components/quote.tsx +43 -0
- package/src/components/radio-card.stories.tsx +27 -0
- package/src/components/radio-card.tsx +225 -0
- package/src/components/radio-group.stories.tsx +32 -0
- package/src/components/radio-group.tsx +68 -0
- package/src/components/reasoning.stories.tsx +50 -0
- package/src/components/reasoning.test.tsx +48 -0
- package/src/components/reasoning.tsx +143 -0
- package/src/components/search.stories.tsx +18 -0
- package/src/components/search.tsx +133 -0
- package/src/components/section-nav.stories.tsx +59 -0
- package/src/components/section-nav.test.tsx +36 -0
- package/src/components/section-nav.tsx +146 -0
- package/src/components/segmented-control.stories.tsx +22 -0
- package/src/components/segmented-control.tsx +301 -0
- package/src/components/select-button.stories.tsx +27 -0
- package/src/components/select-button.tsx +174 -0
- package/src/components/select.stories.tsx +27 -0
- package/src/components/select.tsx +198 -0
- package/src/components/separator.stories.tsx +22 -0
- package/src/components/separator.tsx +23 -0
- package/src/components/shimmer-text.stories.tsx +23 -0
- package/src/components/shimmer-text.test.tsx +35 -0
- package/src/components/shimmer-text.tsx +43 -0
- package/src/components/sidebar.stories.tsx +25 -0
- package/src/components/sidebar.tsx +234 -0
- package/src/components/skeleton.stories.tsx +22 -0
- package/src/components/skeleton.tsx +96 -0
- package/src/components/slider.stories.tsx +20 -0
- package/src/components/slider.tsx +176 -0
- package/src/components/slideshow.stories.tsx +30 -0
- package/src/components/slideshow.tsx +291 -0
- package/src/components/source.stories.tsx +32 -0
- package/src/components/source.test.tsx +40 -0
- package/src/components/source.tsx +161 -0
- package/src/components/spinner.stories.tsx +19 -0
- package/src/components/spinner.tsx +76 -0
- package/src/components/stat-strip.stories.tsx +36 -0
- package/src/components/stat-strip.test.tsx +25 -0
- package/src/components/stat-strip.tsx +85 -0
- package/src/components/stats.stories.tsx +24 -0
- package/src/components/stats.tsx +83 -0
- package/src/components/step-indicator.stories.tsx +18 -0
- package/src/components/step-indicator.test.tsx +18 -0
- package/src/components/step-indicator.tsx +286 -0
- package/src/components/switch.stories.tsx +26 -0
- package/src/components/switch.tsx +32 -0
- package/src/components/table.stories.tsx +38 -0
- package/src/components/table.tsx +220 -0
- package/src/components/tabs.stories.tsx +25 -0
- package/src/components/tabs.tsx +276 -0
- package/src/components/testimonials.stories.tsx +22 -0
- package/src/components/testimonials.tsx +109 -0
- package/src/components/text-link.stories.tsx +47 -0
- package/src/components/text-link.test.tsx +18 -0
- package/src/components/text-link.tsx +88 -0
- package/src/components/text.stories.tsx +49 -0
- package/src/components/text.tsx +105 -0
- package/src/components/thinking-bar.stories.tsx +19 -0
- package/src/components/thinking-bar.test.tsx +27 -0
- package/src/components/thinking-bar.tsx +64 -0
- package/src/components/thinking-steps.stories.tsx +44 -0
- package/src/components/thinking-steps.test.tsx +42 -0
- package/src/components/thinking-steps.tsx +92 -0
- package/src/components/toaster.stories.tsx +80 -0
- package/src/components/toaster.tsx +36 -0
- package/src/components/toggle.stories.tsx +21 -0
- package/src/components/toggle.tsx +103 -0
- package/src/components/tool-call-card.stories.tsx +97 -0
- package/src/components/tool-call-card.test.tsx +88 -0
- package/src/components/tool-call-card.tsx +231 -0
- package/src/components/tooltip.stories.tsx +25 -0
- package/src/components/tooltip.tsx +43 -0
- package/src/components/top-nav.stories.tsx +20 -0
- package/src/components/top-nav.tsx +76 -0
- package/src/docs/Colors.stories.tsx +192 -0
- package/src/docs/GettingStarted.mdx +72 -0
- package/src/docs/Mcp.mdx +52 -0
- package/src/docs/Typography.stories.tsx +114 -0
- package/src/hooks/use-in-view.ts +32 -0
- package/src/hooks/use-reduced-motion.ts +26 -0
- package/src/index.ts +343 -0
- package/src/lib/cn.test.ts +67 -0
- package/src/lib/cn.ts +81 -0
- package/src/lib/motion.ts +109 -0
- package/src/lib/prefix-utilities.test.ts +56 -0
- package/src/lib/prefix-utilities.ts +50 -0
- package/src/styles/base.css +489 -0
- package/src/styles/motion.css +69 -0
- package/src/styles/overrides.css +44 -0
- package/src/styles/typography.css +173 -0
- package/src/theme/theme.css +391 -0
- package/src/theme/tokens.ts +29 -0
- package/src/theme/types.ts +26 -0
- package/README.md +0 -3
package/package.json
CHANGED
|
@@ -1,6 +1,96 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adres/mgda-ui",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"src",
|
|
8
|
+
"knowledge",
|
|
9
|
+
"knowledge.json",
|
|
10
|
+
"brand-knowledge.json"
|
|
11
|
+
],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./src/index.ts",
|
|
15
|
+
"import": "./src/index.ts"
|
|
16
|
+
},
|
|
17
|
+
"./knowledge.json": "./knowledge.json",
|
|
18
|
+
"./styles": "./src/styles/base.css",
|
|
19
|
+
"./components/*": "./src/components/*.tsx",
|
|
20
|
+
"./lib/*": "./src/lib/*.ts",
|
|
21
|
+
"./hooks/*": "./src/hooks/*.ts",
|
|
22
|
+
"./theme.css": "./src/theme/theme.css",
|
|
23
|
+
"./tokens": {
|
|
24
|
+
"types": "./src/theme/tokens.ts",
|
|
25
|
+
"import": "./src/theme/tokens.ts"
|
|
26
|
+
},
|
|
27
|
+
"./brand-knowledge.json": "./brand-knowledge.json"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"lint": "echo \"(lint configured at app level)\"",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"test:watch": "vitest",
|
|
34
|
+
"test:storybook": "node scripts/test-storybook.mjs",
|
|
35
|
+
"storybook": "storybook dev -p 6006",
|
|
36
|
+
"build-storybook": "storybook build -o storybook-static",
|
|
37
|
+
"clean": "rm -rf .turbo *.tsbuildinfo"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@radix-ui/react-accordion": "^1.2.12",
|
|
41
|
+
"@radix-ui/react-aspect-ratio": "^1.1.8",
|
|
42
|
+
"@radix-ui/react-avatar": "^1.1.11",
|
|
43
|
+
"@radix-ui/react-checkbox": "^1.3.3",
|
|
44
|
+
"@radix-ui/react-dialog": "^1.1.4",
|
|
45
|
+
"@radix-ui/react-dropdown-menu": "^2.1.4",
|
|
46
|
+
"@radix-ui/react-label": "^2.1.1",
|
|
47
|
+
"@radix-ui/react-popover": "^1.1.4",
|
|
48
|
+
"@radix-ui/react-radio-group": "^1.3.8",
|
|
49
|
+
"@radix-ui/react-select": "^2.2.6",
|
|
50
|
+
"@radix-ui/react-separator": "^1.1.8",
|
|
51
|
+
"@radix-ui/react-slider": "^1.3.6",
|
|
52
|
+
"@radix-ui/react-slot": "^1.1.1",
|
|
53
|
+
"@radix-ui/react-switch": "^1.2.6",
|
|
54
|
+
"@radix-ui/react-tabs": "^1.1.2",
|
|
55
|
+
"@radix-ui/react-tooltip": "^1.1.6",
|
|
56
|
+
"class-variance-authority": "^0.7.1",
|
|
57
|
+
"clsx": "^2.1.1",
|
|
58
|
+
"lucide-react": "^0.469.0",
|
|
59
|
+
"motion": "^11.15.0",
|
|
60
|
+
"sonner": "^1.7.1",
|
|
61
|
+
"tailwind-merge": "^2.6.0",
|
|
62
|
+
"tailwind-variants": "^3.3.1"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@storybook/addon-a11y": "^8.6.18",
|
|
66
|
+
"@storybook/addon-essentials": "^8.6.14",
|
|
67
|
+
"@storybook/blocks": "^8.6.14",
|
|
68
|
+
"@storybook/react": "^8.6.18",
|
|
69
|
+
"@storybook/react-vite": "^8.6.18",
|
|
70
|
+
"@storybook/test": "^8.6.18",
|
|
71
|
+
"@storybook/test-runner": "^0.22.0",
|
|
72
|
+
"@tailwindcss/postcss": "^4.0.0",
|
|
73
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
74
|
+
"@types/react": "^19.0.2",
|
|
75
|
+
"@types/react-dom": "^19.0.2",
|
|
76
|
+
"postcss": "^8.5.0",
|
|
77
|
+
"react": "^19.0.0",
|
|
78
|
+
"react-dom": "^19.0.0",
|
|
79
|
+
"storybook": "^8.6.18",
|
|
80
|
+
"tailwindcss": "^4.0.0",
|
|
81
|
+
"typescript": "^5.7.2",
|
|
82
|
+
"vite": "^6.0.0",
|
|
83
|
+
"playwright": "^1.51.0",
|
|
84
|
+
"vitest": "^3.0.0",
|
|
85
|
+
"wait-on": "^8.0.3"
|
|
86
|
+
},
|
|
87
|
+
"publishConfig": {
|
|
88
|
+
"access": "public",
|
|
89
|
+
"registry": "https://registry.npmjs.org/"
|
|
90
|
+
},
|
|
91
|
+
"peerDependencies": {
|
|
92
|
+
"react": "^19.0.0",
|
|
93
|
+
"react-dom": "^19.0.0",
|
|
94
|
+
"tailwindcss": "^4.0.0"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "./accordion"
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Accordion> = {
|
|
6
|
+
title: "Components/Accordion",
|
|
7
|
+
component: Accordion,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default meta
|
|
12
|
+
type Story = StoryObj<typeof Accordion>
|
|
13
|
+
|
|
14
|
+
export const Default: Story = {
|
|
15
|
+
render: () => (
|
|
16
|
+
<Accordion type="single" collapsible className="ax:w-full ax:max-w-md">
|
|
17
|
+
<AccordionItem value="item-1">
|
|
18
|
+
<AccordionTrigger>What is Adres?</AccordionTrigger>
|
|
19
|
+
<AccordionContent>
|
|
20
|
+
Adres is a multi-brand design system for property and education products.
|
|
21
|
+
</AccordionContent>
|
|
22
|
+
</AccordionItem>
|
|
23
|
+
<AccordionItem value="item-2">
|
|
24
|
+
<AccordionTrigger>Can I switch brands?</AccordionTrigger>
|
|
25
|
+
<AccordionContent>
|
|
26
|
+
Yes, brand tokens load via CSS and can be swapped at runtime.
|
|
27
|
+
</AccordionContent>
|
|
28
|
+
</AccordionItem>
|
|
29
|
+
</Accordion>
|
|
30
|
+
),
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const Multiple: Story = {
|
|
34
|
+
render: () => (
|
|
35
|
+
<Accordion type="multiple" className="ax:w-full ax:max-w-md">
|
|
36
|
+
<AccordionItem value="a">
|
|
37
|
+
<AccordionTrigger>Section A</AccordionTrigger>
|
|
38
|
+
<AccordionContent>Content for section A.</AccordionContent>
|
|
39
|
+
</AccordionItem>
|
|
40
|
+
<AccordionItem value="b">
|
|
41
|
+
<AccordionTrigger>Section B</AccordionTrigger>
|
|
42
|
+
<AccordionContent>Content for section B.</AccordionContent>
|
|
43
|
+
</AccordionItem>
|
|
44
|
+
</Accordion>
|
|
45
|
+
),
|
|
46
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as AccordionPrimitive from "@radix-ui/react-accordion"
|
|
5
|
+
import { ChevronDown } from "lucide-react"
|
|
6
|
+
import { cn } from "../lib/cn"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Accordion root prop type - Radix's discriminated union of
|
|
10
|
+
* `type="single"` / `type="multiple"` modes.
|
|
11
|
+
*
|
|
12
|
+
* We re-export `AccordionPrimitive.Root` directly (no forwardRef wrapper)
|
|
13
|
+
* to preserve the discriminated union: `forwardRef` collapses it and
|
|
14
|
+
* TypeScript loses the ability to narrow `value` / `defaultValue` based
|
|
15
|
+
* on the `type` discriminant.
|
|
16
|
+
*/
|
|
17
|
+
export type AccordionProps = React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Root>
|
|
18
|
+
|
|
19
|
+
export const Accordion = AccordionPrimitive.Root
|
|
20
|
+
|
|
21
|
+
export const AccordionItem = React.forwardRef<
|
|
22
|
+
React.ElementRef<typeof AccordionPrimitive.Item>,
|
|
23
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
|
24
|
+
>(({ className, ...props }, ref) => (
|
|
25
|
+
<AccordionPrimitive.Item
|
|
26
|
+
ref={ref}
|
|
27
|
+
className={cn("ax:border-b ax:border-border ax:last:border-0", className)}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
))
|
|
31
|
+
AccordionItem.displayName = AccordionPrimitive.Item.displayName
|
|
32
|
+
|
|
33
|
+
export const AccordionTrigger = React.forwardRef<
|
|
34
|
+
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
|
35
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
|
36
|
+
>(({ className, children, ...props }, ref) => (
|
|
37
|
+
<AccordionPrimitive.Header className="ax:flex">
|
|
38
|
+
<AccordionPrimitive.Trigger
|
|
39
|
+
ref={ref}
|
|
40
|
+
className={cn(
|
|
41
|
+
"ax:flex ax:flex-1 ax:items-center ax:justify-between ax:py-md ax:t-label-sm ax:text-foreground ax:cursor-pointer",
|
|
42
|
+
"ax:[transition:color_var(--motion-fast)_var(--motion-ease-out)]",
|
|
43
|
+
"ax:hover:underline",
|
|
44
|
+
"ax:focus-visible:outline-none ax:focus-visible:ring-2 ax:focus-visible:ring-ring ax:focus-visible:ring-offset-2 ax:focus-visible:ring-offset-background",
|
|
45
|
+
"ax:[&[data-state=open]>svg]:rotate-180",
|
|
46
|
+
className
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
>
|
|
50
|
+
{children}
|
|
51
|
+
<ChevronDown className="ax:size-4 ax:shrink-0 ax:text-muted-foreground ax:[transition:transform_var(--motion-fast)_var(--motion-ease-out)] ax:motion-reduce:transition-none" />
|
|
52
|
+
</AccordionPrimitive.Trigger>
|
|
53
|
+
</AccordionPrimitive.Header>
|
|
54
|
+
))
|
|
55
|
+
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
|
|
56
|
+
|
|
57
|
+
export const AccordionContent = React.forwardRef<
|
|
58
|
+
React.ElementRef<typeof AccordionPrimitive.Content>,
|
|
59
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
|
60
|
+
>(({ className, children, ...props }, ref) => (
|
|
61
|
+
<AccordionPrimitive.Content
|
|
62
|
+
ref={ref}
|
|
63
|
+
className={cn(
|
|
64
|
+
"ax:overflow-hidden ax:t-body-sm ax:text-muted-foreground",
|
|
65
|
+
"ax:data-[state=closed]:animate-accordion-up ax:data-[state=open]:animate-accordion-down",
|
|
66
|
+
"ax:motion-reduce:animate-none",
|
|
67
|
+
className
|
|
68
|
+
)}
|
|
69
|
+
{...props}
|
|
70
|
+
>
|
|
71
|
+
<div className="ax:pb-md ax:pt-0 ax:max-w-prose">{children}</div>
|
|
72
|
+
</AccordionPrimitive.Content>
|
|
73
|
+
))
|
|
74
|
+
AccordionContent.displayName = AccordionPrimitive.Content.displayName
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import { ActivityRings, type ActivityRing } from "./activity-rings"
|
|
4
|
+
|
|
5
|
+
const defaultRings: ActivityRing[] = [
|
|
6
|
+
{ pct: 0.75, className: "ax:stroke-chart-1" },
|
|
7
|
+
{ pct: 0.5, className: "ax:stroke-chart-2" },
|
|
8
|
+
{ pct: 0.25, className: "ax:stroke-chart-3" },
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
const meta: Meta<typeof ActivityRings> = {
|
|
12
|
+
title: "Components/ActivityRings",
|
|
13
|
+
component: ActivityRings,
|
|
14
|
+
tags: ["autodocs"],
|
|
15
|
+
parameters: {
|
|
16
|
+
layout: "centered",
|
|
17
|
+
docs: {
|
|
18
|
+
description: {
|
|
19
|
+
component:
|
|
20
|
+
"Concentric progress arcs in one SVG. Pass 1–4 rings outside-in with Tailwind stroke classes such as `ax:stroke-chart-1` or `ax:stroke-primary`.",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
argTypes: {
|
|
25
|
+
rings: { control: false },
|
|
26
|
+
size: { control: { type: "number", min: 48, max: 240, step: 8 } },
|
|
27
|
+
strokeWidth: { control: { type: "number", min: 4, max: 20, step: 1 } },
|
|
28
|
+
ariaLabel: { control: "text" },
|
|
29
|
+
},
|
|
30
|
+
args: {
|
|
31
|
+
size: 120,
|
|
32
|
+
strokeWidth: 12,
|
|
33
|
+
ariaLabel: "Progress rings",
|
|
34
|
+
rings: defaultRings,
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default meta
|
|
39
|
+
type Story = StoryObj<typeof ActivityRings>
|
|
40
|
+
|
|
41
|
+
export const Default: Story = {
|
|
42
|
+
render: (args) => (
|
|
43
|
+
<div className="ax:flex ax:items-center ax:gap-lg ax:p-lg">
|
|
44
|
+
<ActivityRings {...args} />
|
|
45
|
+
<ul className="ax:t-body-sm ax:text-muted-foreground ax:space-y-2xs">
|
|
46
|
+
<li>
|
|
47
|
+
<span className="ax:inline-block ax:size-2 ax:rounded-full ax:bg-chart-1 ax:me-xs" />
|
|
48
|
+
Modules - 75%
|
|
49
|
+
</li>
|
|
50
|
+
<li>
|
|
51
|
+
<span className="ax:inline-block ax:size-2 ax:rounded-full ax:bg-chart-2 ax:me-xs" />
|
|
52
|
+
Attendance - 50%
|
|
53
|
+
</li>
|
|
54
|
+
<li>
|
|
55
|
+
<span className="ax:inline-block ax:size-2 ax:rounded-full ax:bg-chart-3 ax:me-xs" />
|
|
56
|
+
Assignments - 25%
|
|
57
|
+
</li>
|
|
58
|
+
</ul>
|
|
59
|
+
</div>
|
|
60
|
+
),
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const SingleRing: Story = {
|
|
64
|
+
args: {
|
|
65
|
+
rings: [{ pct: 0.6, className: "ax:stroke-primary" }],
|
|
66
|
+
ariaLabel: "Single progress ring at 60%",
|
|
67
|
+
},
|
|
68
|
+
render: (args) => (
|
|
69
|
+
<div className="ax:p-lg">
|
|
70
|
+
<ActivityRings {...args} />
|
|
71
|
+
</div>
|
|
72
|
+
),
|
|
73
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activity rings - concentric Apple-style progress arcs in a single SVG.
|
|
3
|
+
*
|
|
4
|
+
* The components here are pure visual primitives. They know nothing about
|
|
5
|
+
* the domain that the rings represent (courses, fitness, fundraising, etc.);
|
|
6
|
+
* callers compute the percentages and tell the rings which stroke colour to
|
|
7
|
+
* use. Pair `ActivityRings` with your own `computeProgress`-style helper.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as React from "react"
|
|
11
|
+
|
|
12
|
+
export interface ActivityRing {
|
|
13
|
+
/** Progress for this ring, expressed as a 0–1 fraction. Values are
|
|
14
|
+
* clamped to the legal range. */
|
|
15
|
+
pct: number
|
|
16
|
+
/** Tailwind / project stroke class, e.g. `"ax:stroke-chart-1"`. The ring
|
|
17
|
+
* draws a dimmed track of the same colour at 15% opacity. */
|
|
18
|
+
className: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ActivityRingsProps {
|
|
22
|
+
/** Rings rendered from the *outside in*. Pass 1–4 entries; the spacing
|
|
23
|
+
* is computed from the SVG box so a small set still looks balanced. */
|
|
24
|
+
rings: ActivityRing[]
|
|
25
|
+
/** Visual diameter in CSS pixels. The internal SVG viewBox is always
|
|
26
|
+
* `0 0 120 120`; this prop just scales it. */
|
|
27
|
+
size?: number
|
|
28
|
+
/** Stroke thickness for each ring in viewBox units. Defaults to 12. */
|
|
29
|
+
strokeWidth?: number
|
|
30
|
+
/** Accessible label describing what the rings represent in aggregate. */
|
|
31
|
+
ariaLabel?: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function Ring({
|
|
35
|
+
cx,
|
|
36
|
+
cy,
|
|
37
|
+
r,
|
|
38
|
+
pct,
|
|
39
|
+
strokeWidth,
|
|
40
|
+
className,
|
|
41
|
+
}: {
|
|
42
|
+
cx: number
|
|
43
|
+
cy: number
|
|
44
|
+
r: number
|
|
45
|
+
pct: number
|
|
46
|
+
strokeWidth: number
|
|
47
|
+
className: string
|
|
48
|
+
}) {
|
|
49
|
+
const circ = 2 * Math.PI * r
|
|
50
|
+
const clamped = Math.max(0, Math.min(1, pct))
|
|
51
|
+
return (
|
|
52
|
+
<g>
|
|
53
|
+
<circle
|
|
54
|
+
cx={cx}
|
|
55
|
+
cy={cy}
|
|
56
|
+
r={r}
|
|
57
|
+
fill="none"
|
|
58
|
+
strokeWidth={strokeWidth}
|
|
59
|
+
className={`${className} ax:opacity-15`}
|
|
60
|
+
/>
|
|
61
|
+
{clamped > 0 && (
|
|
62
|
+
<circle
|
|
63
|
+
cx={cx}
|
|
64
|
+
cy={cy}
|
|
65
|
+
r={r}
|
|
66
|
+
fill="none"
|
|
67
|
+
strokeWidth={strokeWidth}
|
|
68
|
+
strokeLinecap="round"
|
|
69
|
+
strokeDasharray={circ}
|
|
70
|
+
strokeDashoffset={circ * (1 - clamped)}
|
|
71
|
+
transform={`rotate(-90 ${cx} ${cy})`}
|
|
72
|
+
className={className}
|
|
73
|
+
/>
|
|
74
|
+
)}
|
|
75
|
+
</g>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Concentric rings in a single SVG. Composes 1–4 `Ring`s from the outside
|
|
80
|
+
* in. Ring spacing and starting radius are derived so the set stays
|
|
81
|
+
* visually balanced for typical 2–3 ring counts. */
|
|
82
|
+
export function ActivityRings({
|
|
83
|
+
rings,
|
|
84
|
+
size = 112,
|
|
85
|
+
strokeWidth = 12,
|
|
86
|
+
ariaLabel,
|
|
87
|
+
}: ActivityRingsProps) {
|
|
88
|
+
if (!rings?.length) return null
|
|
89
|
+
|
|
90
|
+
const outerR = 53
|
|
91
|
+
const step = strokeWidth // distance between successive ring centrelines
|
|
92
|
+
return (
|
|
93
|
+
<svg
|
|
94
|
+
viewBox="0 0 120 120"
|
|
95
|
+
width={size}
|
|
96
|
+
height={size}
|
|
97
|
+
className="ax:shrink-0"
|
|
98
|
+
role="img"
|
|
99
|
+
aria-label={ariaLabel}
|
|
100
|
+
>
|
|
101
|
+
{rings.map((ring, i) => (
|
|
102
|
+
<Ring
|
|
103
|
+
key={i}
|
|
104
|
+
cx={60}
|
|
105
|
+
cy={60}
|
|
106
|
+
r={outerR - i * step}
|
|
107
|
+
strokeWidth={strokeWidth}
|
|
108
|
+
pct={ring.pct}
|
|
109
|
+
className={ring.className}
|
|
110
|
+
/>
|
|
111
|
+
))}
|
|
112
|
+
</svg>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Note: the previous `ProgressRing` single-arc component lived here. It was
|
|
117
|
+
// merged into the `Progress` component as `<Progress shape="ring">` so the
|
|
118
|
+
// design system has one mental model for "show one value's progress", see
|
|
119
|
+
// packages/design-system/src/components/progress.tsx. ActivityRings (the
|
|
120
|
+
// concentric multi-value visual) stays separate because it expresses a
|
|
121
|
+
// fundamentally different concept: 2–4 simultaneous progresses, not one.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import { Alert, AlertDescription, AlertTitle } from "./alert"
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof Alert> = {
|
|
6
|
+
title: "Components/Alert",
|
|
7
|
+
component: Alert,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
args: {
|
|
10
|
+
variant: "default",
|
|
11
|
+
size: "md",
|
|
12
|
+
},
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default meta
|
|
16
|
+
type Story = StoryObj<typeof Alert>
|
|
17
|
+
|
|
18
|
+
export const Default: Story = {
|
|
19
|
+
render: (args) => (
|
|
20
|
+
<Alert {...args}>
|
|
21
|
+
<AlertTitle>Heads up</AlertTitle>
|
|
22
|
+
<AlertDescription>You can adjust your notification preferences in settings.</AlertDescription>
|
|
23
|
+
</Alert>
|
|
24
|
+
),
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const Success: Story = {
|
|
28
|
+
render: (args) => (
|
|
29
|
+
<Alert {...args} variant="success">
|
|
30
|
+
<AlertTitle>Payment received</AlertTitle>
|
|
31
|
+
<AlertDescription>Your subscription is now active.</AlertDescription>
|
|
32
|
+
</Alert>
|
|
33
|
+
),
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const Destructive: Story = {
|
|
37
|
+
render: (args) => (
|
|
38
|
+
<Alert {...args} variant="destructive">
|
|
39
|
+
<AlertTitle>Something went wrong</AlertTitle>
|
|
40
|
+
<AlertDescription>Please try again or contact support.</AlertDescription>
|
|
41
|
+
</Alert>
|
|
42
|
+
),
|
|
43
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
|
+
import { AlertCircle, CheckCircle2, Info, AlertTriangle } from "lucide-react"
|
|
6
|
+
import { cn } from "../lib/cn"
|
|
7
|
+
|
|
8
|
+
// ─── Types & context ──────────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
export type AlertSize = "sm" | "md"
|
|
11
|
+
|
|
12
|
+
const AlertContext = React.createContext<{ size: AlertSize }>({ size: "md" })
|
|
13
|
+
|
|
14
|
+
// ─── Variants ─────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
const alertVariants = cva(
|
|
17
|
+
[
|
|
18
|
+
"ax:relative ax:w-full ax:rounded-md ax:border",
|
|
19
|
+
"ax:grid ax:grid-cols-[auto_1fr] ax:items-start",
|
|
20
|
+
],
|
|
21
|
+
{
|
|
22
|
+
variants: {
|
|
23
|
+
variant: {
|
|
24
|
+
// Icon color tracks the title color (i.e. the variant's strongest text
|
|
25
|
+
// shade) so the leading glyph and the headline read as one composed
|
|
26
|
+
// signal rather than two competing emphases.
|
|
27
|
+
default: "ax:bg-card ax:text-foreground-strong ax:border-border",
|
|
28
|
+
info: "ax:bg-card ax:text-foreground-strong ax:border-border",
|
|
29
|
+
primary: "ax:bg-primary-weakest/80 ax:text-primary-foreground ax:border-primary-weaker ax:[&>svg]:text-primary-foreground",
|
|
30
|
+
success: "ax:bg-success-weakest/80 ax:text-success-foreground ax:border-success-weaker ax:[&>svg]:text-success-foreground",
|
|
31
|
+
warning: "ax:bg-warning-weakest/80 ax:dark:bg-warning-muted ax:text-warning-foreground ax:border-warning-weaker ax:dark:border-warning-weakest ax:[&>svg]:text-warning-foreground",
|
|
32
|
+
destructive: "ax:bg-destructive-weakest/80 ax:text-destructive-foreground ax:border-destructive-weaker ax:[&>svg]:text-destructive-foreground",
|
|
33
|
+
},
|
|
34
|
+
size: {
|
|
35
|
+
sm: "ax:p-sm ax:gap-x-xs ax:gap-y-2xs ax:text-xs ax:[&>svg]:size-3.5 ax:[&>svg]:mt-px",
|
|
36
|
+
md: "ax:p-md ax:gap-x-sm ax:gap-y-2xs ax:[&>svg]:size-4 ax:[&>svg]:mt-0.5",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
defaultVariants: {
|
|
40
|
+
variant: "default",
|
|
41
|
+
size: "md",
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
const iconMap = {
|
|
47
|
+
default: Info,
|
|
48
|
+
info: Info,
|
|
49
|
+
primary: Info,
|
|
50
|
+
success: CheckCircle2,
|
|
51
|
+
warning: AlertTriangle,
|
|
52
|
+
destructive: AlertCircle,
|
|
53
|
+
} as const
|
|
54
|
+
|
|
55
|
+
// ─── Alert ────────────────────────────────────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
export interface AlertProps
|
|
58
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
59
|
+
VariantProps<typeof alertVariants> {
|
|
60
|
+
size?: AlertSize
|
|
61
|
+
/** Hide the leading icon AND snap content to the first column. Use when the
|
|
62
|
+
* alert appears inline next to another leading element that already conveys
|
|
63
|
+
* the meaning. */
|
|
64
|
+
hideIcon?: boolean
|
|
65
|
+
/** Override the default variant icon with custom content (e.g. <Calendar />,
|
|
66
|
+
* <Sparkles />, <Lock />). The custom icon inherits the variant's color
|
|
67
|
+
* via the alert's `[&>svg]:text-*` selector - pass an SVG element.
|
|
68
|
+
* Remember to set `aria-hidden="true"` on the icon yourself.
|
|
69
|
+
* Ignored when `hideIcon` is true. */
|
|
70
|
+
icon?: React.ReactNode
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
|
|
74
|
+
({ className, variant = "default", size = "md", hideIcon, icon, children, ...props }, ref) => {
|
|
75
|
+
const DefaultIcon = iconMap[variant ?? "default"]
|
|
76
|
+
const iconNode = icon !== undefined ? icon : <DefaultIcon aria-hidden="true" />
|
|
77
|
+
return (
|
|
78
|
+
<AlertContext.Provider value={{ size }}>
|
|
79
|
+
<div
|
|
80
|
+
ref={ref}
|
|
81
|
+
role="alert"
|
|
82
|
+
className={cn(alertVariants({ variant, size }), className)}
|
|
83
|
+
{...props}
|
|
84
|
+
>
|
|
85
|
+
{!hideIcon && iconNode}
|
|
86
|
+
<div className={cn("ax:col-start-2 ax:flex ax:flex-col ax:gap-2xs", hideIcon && "ax:col-start-1")}>
|
|
87
|
+
{children}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</AlertContext.Provider>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
Alert.displayName = "Alert"
|
|
95
|
+
|
|
96
|
+
// ─── AlertTitle ───────────────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
export const AlertTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
|
99
|
+
({ className, ...props }, ref) => {
|
|
100
|
+
const { size } = React.useContext(AlertContext)
|
|
101
|
+
return (
|
|
102
|
+
<h5
|
|
103
|
+
ref={ref}
|
|
104
|
+
className={cn(size === "sm" ? "ax:t-label-sm" : "ax:t-label-md", className)}
|
|
105
|
+
{...props}
|
|
106
|
+
/>
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
)
|
|
110
|
+
AlertTitle.displayName = "AlertTitle"
|
|
111
|
+
|
|
112
|
+
// ─── AlertDescription ─────────────────────────────────────────────────────────
|
|
113
|
+
|
|
114
|
+
// Description renders a <div> (not a <p>) so it can host block-level children
|
|
115
|
+
// like lists, paragraphs, or button rows. Type matches the rendered element.
|
|
116
|
+
export const AlertDescription = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
117
|
+
({ className, ...props }, ref) => {
|
|
118
|
+
const { size } = React.useContext(AlertContext)
|
|
119
|
+
return (
|
|
120
|
+
<div
|
|
121
|
+
ref={ref}
|
|
122
|
+
className={cn(
|
|
123
|
+
"ax:text-foreground ax:[&_p]:leading-relaxed",
|
|
124
|
+
size === "sm" ? "ax:text-xs" : "ax:t-body-sm",
|
|
125
|
+
className
|
|
126
|
+
)}
|
|
127
|
+
{...props}
|
|
128
|
+
/>
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
AlertDescription.displayName = "AlertDescription"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import { AnswerActions } from "./answer-actions"
|
|
4
|
+
import { TooltipProvider } from "./tooltip"
|
|
5
|
+
|
|
6
|
+
const VERSIONS = [
|
|
7
|
+
"Budget for yearly service charges of roughly AED 26,000 to 38,000 for this 1,734 sqft unit.",
|
|
8
|
+
"Expect service charges, home insurance and utilities; service charges come to about AED 26,000 to 38,000 a year.",
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
const meta: Meta<typeof AnswerActions> = {
|
|
12
|
+
title: "Components/AnswerActions",
|
|
13
|
+
component: AnswerActions,
|
|
14
|
+
tags: ["autodocs"],
|
|
15
|
+
decorators: [(Story) => <TooltipProvider><div style={{ maxWidth: 640 }}><Story /></div></TooltipProvider>],
|
|
16
|
+
args: {
|
|
17
|
+
text: VERSIONS[0],
|
|
18
|
+
negativeReasons: ["Wrong price", "Out of date", "Didn't answer", "Too long"],
|
|
19
|
+
positiveReasons: ["Accurate", "Clear", "Saved me time"],
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default meta
|
|
24
|
+
type Story = StoryObj<typeof AnswerActions>
|
|
25
|
+
|
|
26
|
+
export const Default: Story = {}
|
|
27
|
+
|
|
28
|
+
function VersionsDemo(props: React.ComponentProps<typeof AnswerActions>) {
|
|
29
|
+
const [version, setVersion] = React.useState(0)
|
|
30
|
+
return (
|
|
31
|
+
<AnswerActions
|
|
32
|
+
{...props}
|
|
33
|
+
text={VERSIONS[version] ?? ""}
|
|
34
|
+
version={version}
|
|
35
|
+
versionCount={VERSIONS.length}
|
|
36
|
+
onVersionChange={setVersion}
|
|
37
|
+
onRegenerate={() => undefined}
|
|
38
|
+
regenerateDisabled
|
|
39
|
+
/>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const WithVersions: Story = { render: (args) => <VersionsDemo {...args} /> }
|
|
44
|
+
|
|
45
|
+
export const WithReport: Story = {
|
|
46
|
+
args: {
|
|
47
|
+
onCopyLink: () => undefined,
|
|
48
|
+
reportReasons: [
|
|
49
|
+
{ value: "wrong", label: "Wrong or misleading" },
|
|
50
|
+
{ value: "unsafe", label: "Unsafe or offensive" },
|
|
51
|
+
{ value: "other", label: "Something else" },
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest"
|
|
2
|
+
import * as React from "react"
|
|
3
|
+
import { renderToStaticMarkup } from "react-dom/server"
|
|
4
|
+
import { AnswerActions, FeedbackReasonChips } from "./answer-actions"
|
|
5
|
+
import { TooltipProvider } from "./tooltip"
|
|
6
|
+
|
|
7
|
+
const render = (node: React.ReactNode) => renderToStaticMarkup(<TooltipProvider>{node}</TooltipProvider>)
|
|
8
|
+
const REASONS = { negativeReasons: ["Wrong price"], positiveReasons: ["Clear"] }
|
|
9
|
+
|
|
10
|
+
describe("FeedbackReasonChips", () => {
|
|
11
|
+
it("marks selected reasons as pressed", () => {
|
|
12
|
+
const html = render(<FeedbackReasonChips label="What went wrong?" reasons={["Wrong price", "Too long"]} selected={["Too long"]} onToggle={() => undefined} />)
|
|
13
|
+
expect(html).toMatch(/role="group" aria-label="What went wrong\?"/)
|
|
14
|
+
expect(html).toMatch(/aria-pressed="false"[^>]*>Wrong price|>Wrong price<\/button>/)
|
|
15
|
+
expect(html.match(/aria-pressed="true"/g)).toHaveLength(1)
|
|
16
|
+
})
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
describe("AnswerActions", () => {
|
|
20
|
+
it("is a labelled toolbar with copy and rating", () => {
|
|
21
|
+
const html = render(<AnswerActions text="Answer" {...REASONS} />)
|
|
22
|
+
expect(html).toContain('role="toolbar" aria-label="Answer actions"')
|
|
23
|
+
expect(html).toContain('aria-label="Copy"')
|
|
24
|
+
expect(html).toContain('aria-label="Good response"')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it("shows the version pager only with several versions", () => {
|
|
28
|
+
expect(render(<AnswerActions text="Answer" {...REASONS} />)).not.toContain("Response versions")
|
|
29
|
+
const html = render(<AnswerActions text="Answer" {...REASONS} version={1} versionCount={3} onVersionChange={() => undefined} />)
|
|
30
|
+
expect(html).toContain("2 of 3")
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it("offers More actions only when there is something in it", () => {
|
|
34
|
+
expect(render(<AnswerActions text="Answer" {...REASONS} />)).not.toContain("More actions")
|
|
35
|
+
expect(render(<AnswerActions text="Answer" {...REASONS} onCopyLink={() => undefined} />)).toContain("More actions")
|
|
36
|
+
})
|
|
37
|
+
})
|