aural-ui 4.0.1 → 4.2.0
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/README.md +8 -1
- package/dist/components/aspect-ratio/AspectRatio.stories.tsx +290 -1228
- package/dist/components/avatar/Avatar.stories.tsx +219 -235
- package/dist/components/badge/Badge.stories.tsx +379 -116
- package/dist/components/banner/Banner.stories.tsx +445 -391
- package/dist/components/breadcrumb/Breadcrumb.stories.tsx +453 -199
- package/dist/components/button/Button.stories.tsx +585 -230
- package/dist/components/card/Card.stories.tsx +619 -301
- package/dist/components/char-count/CharCount.stories.tsx +350 -248
- package/dist/components/checkbox/Checkbox.stories.tsx +309 -167
- package/dist/components/chip/Chip.stories.tsx +362 -168
- package/dist/components/circular-loader/CircularLoader.stories.tsx +221 -636
- package/dist/components/clamp-lines/ClampLines.stories.tsx +246 -117
- package/dist/components/collapsible/Collapsible.stories.tsx +391 -252
- package/dist/components/command/Command.stories.tsx +530 -867
- package/dist/components/dialog/Dialog.stories.tsx +501 -950
- package/dist/components/divider/Divider.stories.tsx +264 -527
- package/dist/components/dot-loader/DotLoader.stories.tsx +256 -257
- package/dist/components/drawer/Drawer.stories.tsx +659 -1023
- package/dist/components/dropdown/Dropdown.stories.tsx +643 -1028
- package/dist/components/form/Form.stories.tsx +560 -274
- package/dist/components/helper-text/HelperText.stories.tsx +199 -200
- package/dist/components/hover-card/HoverCard.stories.tsx +318 -1254
- package/dist/components/icon-button/IconButton.stories.tsx +837 -194
- package/dist/components/if-else/if-else.stories.tsx +370 -83
- package/dist/components/input/Input.stories.tsx +436 -368
- package/dist/components/label/Label.stories.tsx +156 -154
- package/dist/components/list/List.stories.tsx +484 -835
- package/dist/components/marquee/Marquee.stories.tsx +356 -712
- package/dist/components/otp-inputs/OtpInputs.stories.tsx +352 -422
- package/dist/components/overlay/Overlay.stories.tsx +452 -824
- package/dist/components/pagination/Pagination.stories.tsx +721 -210
- package/dist/components/popover/Popover.stories.tsx +481 -896
- package/dist/components/radio/Radio.stories.tsx +432 -124
- package/dist/components/resizable/Resizable.stories.tsx +495 -799
- package/dist/components/scroll-area/ScrollArea.stories.tsx +383 -1059
- package/dist/components/search/Search.stories.tsx +312 -595
- package/dist/components/select/Select.stories.tsx +684 -789
- package/dist/components/sheet/Sheet.stories.tsx +671 -950
- package/dist/components/skelton/Skelton.stories.tsx +230 -764
- package/dist/components/slider/Slider.stories.tsx +383 -760
- package/dist/components/stepper/Stepper.stories.tsx +371 -514
- package/dist/components/switch/Switch.stories.tsx +461 -208
- package/dist/components/switch-case/SwitchCase.stories.tsx +367 -188
- package/dist/components/table/Table.stories.tsx +770 -916
- package/dist/components/tabs/Tabs.stories.tsx +458 -1455
- package/dist/components/tag/Tag.stories.tsx +714 -542
- package/dist/components/textarea/TextArea.stories.tsx +621 -562
- package/dist/components/thumbnail-tags/ThumbnailTags.stories.tsx +228 -154
- package/dist/components/toast/Toast.stories.tsx +452 -1339
- package/dist/components/toggle/Toggle.stories.tsx +488 -931
- package/dist/components/tooltip/Tooltip.stories.tsx +344 -1388
- package/dist/components/typography/Typography.stories.tsx +406 -89
- package/dist/hooks/use-change-state/UseChangeState.stories.tsx +309 -606
- package/dist/hooks/use-previous/UsePrevious.stories.tsx +367 -917
- package/dist/hooks/use-standalone-pagination/UseStandalonePagination.stories.tsx +639 -867
- package/dist/icons/Icons.stories.tsx +0 -12
- package/dist/icons/ai-avatar-icon/AiAvatarIcon.stories.tsx +223 -1060
- package/dist/icons/alert-icon/AlertIcon.stories.tsx +106 -968
- package/dist/icons/all-icons.tsx +37 -16
- package/dist/icons/angle-down-icon/AngleDownIcon.stories.tsx +137 -1010
- package/dist/icons/apple-logo-icon/AppleLogoIcon.stories.tsx +145 -935
- package/dist/icons/arrow-box-left-icon/ArrowBoxLeftIcon.stories.tsx +132 -1046
- package/dist/icons/arrow-corner-up-left-icon/ArrowCornerUpLeftIcon.stories.tsx +134 -986
- package/dist/icons/arrow-corner-up-right-icon/ArrowCornerUpRightIcon.stories.tsx +135 -1028
- package/dist/icons/arrow-left-icon/ArrowLeftIcon.stories.tsx +133 -971
- package/dist/icons/arrow-right-icon/ArrowRightIcon.stories.tsx +145 -1123
- package/dist/icons/arrow-right-up-icon/ArrowRightUpIcon.stories.tsx +143 -1252
- package/dist/icons/art-board-icon/ArtBoardIcon.stories.tsx +123 -632
- package/dist/icons/audio-bar-icon/AudioBarIcon.stories.tsx +141 -1223
- package/dist/icons/backward-ten-seconds-icon/BackwardTenSecondsIcon.stories.tsx +164 -1018
- package/dist/icons/bubble-check-icon/BubbleCheckIcon.stories.tsx +121 -1236
- package/dist/icons/bubble-crossed-icon/BubbleCrossedIcon.stories.tsx +121 -1213
- package/dist/icons/bubble-sparkle-icon/BubbleSparkleIcon.stories.tsx +116 -893
- package/dist/icons/camera-icon/CameraIcon.stories.tsx +109 -1254
- package/dist/icons/capital-a-letter-icon/CapitalALetterIcon.stories.tsx +114 -975
- package/dist/icons/chevron-double-left-icon/ChevronDoubleLeftIcon.stories.tsx +157 -994
- package/dist/icons/chevron-double-right-icon/ChevronDoubleRightIcon.stories.tsx +160 -992
- package/dist/icons/chevron-down-icon/ChevronDownIcon.stories.tsx +140 -970
- package/dist/icons/chevron-left-icon/ChevronLeftIcon.stories.tsx +126 -993
- package/dist/icons/chevron-right-icon/ChevronRightIcon.stories.tsx +144 -987
- package/dist/icons/chevron-up-icon/ChevronUpIcon.stories.tsx +141 -1007
- package/dist/icons/circle-tick-icon/CircleTickIcon.stories.tsx +147 -1187
- package/dist/icons/circular-play-icon/CircularPlayIcon.stories.tsx +110 -476
- package/dist/icons/coin-icon/CoinIcon.stories.tsx +120 -1364
- package/dist/icons/coin-toons-icon/CoinToonsIcon.stories.tsx +113 -1360
- package/dist/icons/column-wide-add-icon/ColumnWideAddIcon.stories.tsx +111 -942
- package/dist/icons/command-icon/CommandIcon.stories.tsx +124 -1087
- package/dist/icons/copy-icon/CopyIcon.stories.tsx +119 -996
- package/dist/icons/cross-circle-icon/CrossCircleIcon.stories.tsx +144 -1046
- package/dist/icons/cross-icon/CrossIcon.stories.tsx +136 -999
- package/dist/icons/download-icon/DownloadIcon.stories.tsx +123 -857
- package/dist/icons/edit-big-icon/EditBigIcon.stories.tsx +121 -1080
- package/dist/icons/email-icon/EmailIcon.stories.tsx +112 -979
- package/dist/icons/expand-icon/ExpandIcon.stories.tsx +109 -1146
- package/dist/icons/eye-close-icon/EyeCloseIcon.stories.tsx +141 -1068
- package/dist/icons/eye-open-icon/EyeOpenIcon.stories.tsx +140 -1081
- package/dist/icons/feature-shine-icon/FeatureShineIcon.stories.tsx +124 -1050
- package/dist/icons/file-chart-icon/FileChartIcon.stories.tsx +123 -1091
- package/dist/icons/file-text-icon/FileTextIcon.stories.tsx +122 -633
- package/dist/icons/filter-bar-row-icon/FilterBarRowIcon.stories.tsx +116 -1087
- package/dist/icons/forward-ten-seconds-icon/ForwardTenSecondsIcon.stories.tsx +166 -1020
- package/dist/icons/git-branch-icon/GitBranchIcon.stories.tsx +112 -1182
- package/dist/icons/git-fork-icon/GitForkIcon.stories.tsx +112 -1155
- package/dist/icons/globe-icon/GlobeIcon.stories.tsx +127 -325
- package/dist/icons/google-logo-icon/GoogleLogoIcon.stories.tsx +142 -985
- package/dist/icons/grip-vertical-icon/GripVerticalIcon.stories.tsx +116 -1217
- package/dist/icons/head-icon/HeadIcon.stories.tsx +108 -953
- package/dist/icons/heart-icon/HeartIcon.stories.tsx +117 -1060
- package/dist/icons/image-avatar-sparkle-icon/ImageAvatarSparkleIcon.stories.tsx +116 -716
- package/dist/icons/image-icon/ImageIcon.stories.tsx +102 -1164
- package/dist/icons/import-folder-icon/ImportFolderIcon.stories.tsx +108 -1233
- package/dist/icons/import-left-arrow-folder-icon/ImportLeftArrowFolderIcon.stories.tsx +133 -1289
- package/dist/icons/indian-flag-icon/IndianFlagIcon.stories.tsx +155 -1012
- package/dist/icons/instagram-icon/InstagramIcon.stories.tsx +158 -1438
- package/dist/icons/layout-column-icon/LayoutColumnIcon.stories.tsx +121 -1011
- package/dist/icons/layout-left-icon/LayoutLeftIcon.stories.tsx +116 -981
- package/dist/icons/layout-right-icon/LayoutRightIcon.stories.tsx +116 -979
- package/dist/icons/light-bulb-simple-icon/LightBulbSimpleIcon.stories.tsx +105 -1252
- package/dist/icons/linked-in-icon/LinkedInIcon.stories.tsx +151 -1554
- package/dist/icons/magic-book-icon/MagicBookIcon.stories.tsx +107 -1227
- package/dist/icons/magic-edit-icon/MagicEditIcon.stories.tsx +116 -707
- package/dist/icons/maintenance-icon/MaintenanceIcon.stories.tsx +119 -1226
- package/dist/icons/message-icon/MessageIcon.stories.tsx +111 -557
- package/dist/icons/minimize-icon/MinimizeIcon.stories.tsx +112 -1198
- package/dist/icons/moon-icon/MoonIcon.stories.tsx +117 -557
- package/dist/icons/move-horizontal-icon/MoveHorizontalIcon.stories.tsx +106 -1235
- package/dist/icons/move-vertical-icon/MoveVerticalIcon.stories.tsx +112 -1185
- package/dist/icons/musical-note-icon/MusicalNoteIcon.stories.tsx +116 -1012
- package/dist/icons/notepad-icon/NotepadIcon.stories.tsx +108 -1137
- package/dist/icons/notes-icon/NotesIcon.stories.tsx +116 -1138
- package/dist/icons/page-search-icon/PageSearchIcon.stories.tsx +106 -1146
- package/dist/icons/page-text-icon/PageTextIcon.stories.tsx +119 -719
- package/dist/icons/paint-roll-icon/PaintRollIcon.stories.tsx +110 -999
- package/dist/icons/paper-plane-icon/PaperPlaneIcon.stories.tsx +109 -912
- package/dist/icons/pause-icon/PauseIcon.stories.tsx +110 -1041
- package/dist/icons/pencil-icon/PencilIcon.stories.tsx +112 -1109
- package/dist/icons/phone-icon/PhoneIcon.stories.tsx +112 -1023
- package/dist/icons/plus-icon/PlusIcon.stories.tsx +103 -1132
- package/dist/icons/pocket-studio-icon/PocketStudioIcon.stories.tsx +104 -870
- package/dist/icons/scroll-down-icon/ScrollDownIcon.stories.tsx +99 -476
- package/dist/icons/search-icon/SearchIcon.stories.tsx +108 -1161
- package/dist/icons/setting-icon/SettingIcon.stories.tsx +104 -1009
- package/dist/icons/share-icon/ShareIcon.stories.tsx +117 -1064
- package/dist/icons/shield-icon/ShieldIcon.stories.tsx +114 -974
- package/dist/icons/site-logo-icon/SiteLogoIcon.stories.tsx +134 -1160
- package/dist/icons/skip-backward-icon/SkipBackwardIcon.stories.tsx +169 -1017
- package/dist/icons/skip-forward-icon/SkipForwardIcon.stories.tsx +161 -1016
- package/dist/icons/sparkles-soft-icon/SparklesSoftIcon.stories.tsx +102 -1001
- package/dist/icons/spinner-gradient-icon/SpinnerGradientIcon.stories.tsx +155 -593
- package/dist/icons/spinner-solid-icon/SpinnerSolidIcon.stories.tsx +155 -608
- package/dist/icons/spinner-solid-neutral-icon/SpinnerSolidINeutralcon.stories.tsx +142 -712
- package/dist/icons/star-icon/StarIcon.stories.tsx +120 -946
- package/dist/icons/store-coin-icon/StoreCoinIcon.stories.tsx +109 -1013
- package/dist/icons/suggestion-icon/SuggestionIcon.stories.tsx +113 -891
- package/dist/icons/sun-icon/SunIcon.stories.tsx +117 -864
- package/dist/icons/text-color-icon/TextColorIcon.stories.tsx +113 -989
- package/dist/icons/text-indicator-icon/TextIndicatorIcon.stories.tsx +120 -1027
- package/dist/icons/threads-icon/ThreadsIcon.stories.tsx +153 -1476
- package/dist/icons/tick-circle-icon/TickCircleIcon.stories.tsx +143 -1187
- package/dist/icons/tick-icon/TickIcon.stories.tsx +142 -1322
- package/dist/icons/trash-icon/TrashIcon.stories.tsx +105 -970
- package/dist/icons/twitter-x-icon/TwitterXIcon.stories.tsx +154 -1457
- package/dist/icons/upload-icon/UploadIcon.stories.tsx +112 -930
- package/dist/icons/vertical-menu-icon/VerticalMenuIcon.stories.tsx +115 -1019
- package/dist/icons/video-play-list-icon/VideoPlaylistIcon.stories.tsx +122 -1092
- package/dist/icons/voice-playing-icon/VoicePlayingIcon.stories.tsx +120 -1401
- package/dist/icons/volume-full-icon/VolumeFullIcon.stories.tsx +107 -1212
- package/dist/icons/volume-half-icon/VolumeHalfIcon.stories.tsx +109 -1122
- package/dist/icons/volume-off-icon/VolumeOffIcon.stories.tsx +112 -1124
- package/dist/icons/warning-icon/WarningIcon.stories.tsx +119 -1083
- package/dist/icons/youtube-icon/YoutubeIcon.stories.tsx +158 -983
- package/dist/index.cjs +90 -90
- package/dist/index.js +90 -90
- package/package.json +8 -3
|
@@ -1,25 +1,40 @@
|
|
|
1
1
|
import React, { useState } from "react"
|
|
2
2
|
import type { Meta, StoryObj } from "@storybook/react-vite"
|
|
3
3
|
|
|
4
|
+
import { AuralComponentDocsPage } from "src/ui/story-spec/components/component-story-docs-page"
|
|
5
|
+
|
|
4
6
|
import TextArea from "."
|
|
5
7
|
|
|
8
|
+
// ─── Meta ─────────────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
6
10
|
const meta: Meta<typeof TextArea> = {
|
|
7
11
|
title: "Components/UI/TextArea",
|
|
8
12
|
component: TextArea,
|
|
9
13
|
parameters: {
|
|
10
14
|
layout: "centered",
|
|
11
|
-
backgrounds: {
|
|
12
|
-
default: "dark",
|
|
13
|
-
values: [
|
|
14
|
-
{ name: "dark", value: "#0a0a0a" },
|
|
15
|
-
{ name: "light", value: "#ffffff" },
|
|
16
|
-
],
|
|
17
|
-
},
|
|
18
15
|
docs: {
|
|
19
16
|
description: {
|
|
20
17
|
component:
|
|
21
|
-
"A flexible textarea
|
|
18
|
+
"A flexible textarea built with atomic design principles. Supports three decoration styles (underline, outline, filled), four validation variants, auto-growing height, character counting, and full ARIA accessibility. Can be used as a single convenience component or composed from individual atomic parts.",
|
|
22
19
|
},
|
|
20
|
+
page: () => (
|
|
21
|
+
<AuralComponentDocsPage
|
|
22
|
+
features={[
|
|
23
|
+
{
|
|
24
|
+
title: "4 Validation States",
|
|
25
|
+
description: "Default to success",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
title: "Auto-growing Height",
|
|
29
|
+
description: "Expands with content",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
title: "3 Decoration Styles",
|
|
33
|
+
description: "Underline, outline, filled",
|
|
34
|
+
},
|
|
35
|
+
]}
|
|
36
|
+
/>
|
|
37
|
+
),
|
|
23
38
|
},
|
|
24
39
|
},
|
|
25
40
|
tags: ["autodocs"],
|
|
@@ -27,704 +42,748 @@ const meta: Meta<typeof TextArea> = {
|
|
|
27
42
|
label: {
|
|
28
43
|
control: { type: "text" },
|
|
29
44
|
description: "Label text for the textarea",
|
|
30
|
-
table: {
|
|
31
|
-
type: { summary: "ReactNode" },
|
|
32
|
-
defaultValue: { summary: "undefined" },
|
|
33
|
-
},
|
|
34
45
|
},
|
|
35
46
|
placeholder: {
|
|
36
47
|
control: { type: "text" },
|
|
37
|
-
description: "Placeholder
|
|
38
|
-
table: {
|
|
39
|
-
type: { summary: "string" },
|
|
40
|
-
defaultValue: { summary: '""' },
|
|
41
|
-
},
|
|
48
|
+
description: "Placeholder shown when the textarea is empty",
|
|
42
49
|
},
|
|
43
50
|
helperText: {
|
|
44
51
|
control: { type: "text" },
|
|
45
52
|
description: "Helper text displayed below the textarea",
|
|
46
|
-
table: {
|
|
47
|
-
type: { summary: "ReactNode" },
|
|
48
|
-
defaultValue: { summary: "undefined" },
|
|
49
|
-
},
|
|
50
53
|
},
|
|
51
54
|
variant: {
|
|
52
55
|
control: { type: "select" },
|
|
53
56
|
options: ["default", "error", "warning", "success"],
|
|
54
|
-
description: "Visual
|
|
55
|
-
table: {
|
|
56
|
-
type: { summary: '"default" | "error" | "warning" | "success"' },
|
|
57
|
-
defaultValue: { summary: '"default"' },
|
|
58
|
-
},
|
|
57
|
+
description: "Visual validation state",
|
|
59
58
|
},
|
|
60
59
|
decoration: {
|
|
61
60
|
control: { type: "select" },
|
|
62
61
|
options: ["underline", "outline", "filled"],
|
|
63
|
-
description: "
|
|
64
|
-
table: {
|
|
65
|
-
type: { summary: '"underline" | "outline" | "filled"' },
|
|
66
|
-
defaultValue: { summary: '"filled"' },
|
|
67
|
-
},
|
|
62
|
+
description: "Border and background style",
|
|
68
63
|
},
|
|
69
64
|
disabled: {
|
|
70
65
|
control: { type: "boolean" },
|
|
71
|
-
description: "
|
|
72
|
-
table: {
|
|
73
|
-
type: { summary: "boolean" },
|
|
74
|
-
defaultValue: { summary: "false" },
|
|
75
|
-
},
|
|
66
|
+
description: "Disables the textarea",
|
|
76
67
|
},
|
|
77
68
|
required: {
|
|
78
69
|
control: { type: "boolean" },
|
|
79
|
-
description: "
|
|
80
|
-
table: {
|
|
81
|
-
type: { summary: "boolean" },
|
|
82
|
-
defaultValue: { summary: "false" },
|
|
83
|
-
},
|
|
70
|
+
description: "Adds asterisk to label and aria-required",
|
|
84
71
|
},
|
|
85
72
|
fullWidth: {
|
|
86
73
|
control: { type: "boolean" },
|
|
87
|
-
description: "
|
|
88
|
-
table: {
|
|
89
|
-
type: { summary: "boolean" },
|
|
90
|
-
defaultValue: { summary: "false" },
|
|
91
|
-
},
|
|
74
|
+
description: "Expands the textarea to fill its container",
|
|
92
75
|
},
|
|
93
76
|
showCharCount: {
|
|
94
77
|
control: { type: "boolean" },
|
|
95
|
-
description: "
|
|
96
|
-
table: {
|
|
97
|
-
type: { summary: "boolean" },
|
|
98
|
-
defaultValue: { summary: "false" },
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
value: {
|
|
102
|
-
control: { type: "text" },
|
|
103
|
-
description: "Controlled value of the textarea",
|
|
104
|
-
table: {
|
|
105
|
-
type: { summary: "string" },
|
|
106
|
-
defaultValue: { summary: "undefined" },
|
|
107
|
-
},
|
|
78
|
+
description: "Shows remaining character count (requires maxLength)",
|
|
108
79
|
},
|
|
109
80
|
maxLength: {
|
|
110
81
|
control: { type: "number" },
|
|
111
82
|
description: "Maximum number of characters allowed",
|
|
112
|
-
table: {
|
|
113
|
-
type: { summary: "number" },
|
|
114
|
-
defaultValue: { summary: "undefined" },
|
|
115
|
-
},
|
|
116
83
|
},
|
|
117
84
|
rows: {
|
|
118
85
|
control: { type: "number", min: 1, max: 10 },
|
|
119
|
-
description: "
|
|
120
|
-
table: {
|
|
121
|
-
type: { summary: "number" },
|
|
122
|
-
defaultValue: { summary: "3" },
|
|
123
|
-
},
|
|
86
|
+
description: "Visible rows when autoGrow is false",
|
|
124
87
|
},
|
|
125
88
|
autoGrow: {
|
|
126
89
|
control: { type: "boolean" },
|
|
127
|
-
description:
|
|
128
|
-
"Whether the textarea should automatically grow/shrink based on content",
|
|
129
|
-
table: {
|
|
130
|
-
type: { summary: "boolean" },
|
|
131
|
-
defaultValue: { summary: "true" },
|
|
132
|
-
},
|
|
90
|
+
description: "Textarea grows/shrinks automatically with content",
|
|
133
91
|
},
|
|
134
92
|
minHeight: {
|
|
135
93
|
control: { type: "number" },
|
|
136
|
-
description: "Minimum height in pixels (only
|
|
137
|
-
table: {
|
|
138
|
-
type: { summary: "number" },
|
|
139
|
-
defaultValue: { summary: "undefined" },
|
|
140
|
-
},
|
|
94
|
+
description: "Minimum height in pixels (autoGrow only)",
|
|
141
95
|
},
|
|
142
96
|
maxHeight: {
|
|
143
97
|
control: { type: "number" },
|
|
144
|
-
description: "Maximum height in pixels (only
|
|
145
|
-
table: {
|
|
146
|
-
type: { summary: "number" },
|
|
147
|
-
defaultValue: { summary: "undefined" },
|
|
148
|
-
},
|
|
149
|
-
},
|
|
150
|
-
onChange: {
|
|
151
|
-
action: "changed",
|
|
152
|
-
description: "Callback fired when the value changes",
|
|
153
|
-
table: {
|
|
154
|
-
type: {
|
|
155
|
-
summary: "(e: React.ChangeEvent<HTMLTextAreaElement>) => void",
|
|
156
|
-
},
|
|
157
|
-
},
|
|
158
|
-
},
|
|
159
|
-
onFocus: {
|
|
160
|
-
action: "focused",
|
|
161
|
-
description: "Callback fired when the textarea receives focus",
|
|
162
|
-
table: {
|
|
163
|
-
type: { summary: "(e: React.FocusEvent<HTMLTextAreaElement>) => void" },
|
|
164
|
-
},
|
|
165
|
-
},
|
|
166
|
-
onBlur: {
|
|
167
|
-
action: "blurred",
|
|
168
|
-
description: "Callback fired when the textarea loses focus",
|
|
169
|
-
table: {
|
|
170
|
-
type: { summary: "(e: React.FocusEvent<HTMLTextAreaElement>) => void" },
|
|
171
|
-
},
|
|
172
|
-
},
|
|
173
|
-
unstyled: {
|
|
174
|
-
control: { type: "boolean" },
|
|
175
|
-
description: "Whether to remove all default styling",
|
|
176
|
-
table: {
|
|
177
|
-
type: { summary: "boolean" },
|
|
178
|
-
defaultValue: { summary: "false" },
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
classes: {
|
|
182
|
-
control: { type: "object" },
|
|
183
|
-
description: "Override classes for different parts of the component",
|
|
184
|
-
table: {
|
|
185
|
-
type: {
|
|
186
|
-
summary:
|
|
187
|
-
"{ root?: string; label?: string; wrapper?: string; textarea?: string; helperText?: string; charCount?: string }",
|
|
188
|
-
},
|
|
189
|
-
defaultValue: { summary: "{}" },
|
|
190
|
-
},
|
|
98
|
+
description: "Maximum height in pixels (autoGrow only)",
|
|
191
99
|
},
|
|
192
100
|
},
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
export default meta
|
|
196
|
-
type Story = StoryObj<typeof meta>
|
|
197
|
-
|
|
198
|
-
// Default story
|
|
199
|
-
export const Default: Story = {
|
|
200
101
|
args: {
|
|
201
|
-
|
|
202
|
-
placeholder: "Enter your message here...",
|
|
102
|
+
variant: "default",
|
|
203
103
|
decoration: "filled",
|
|
204
|
-
|
|
104
|
+
disabled: false,
|
|
105
|
+
required: false,
|
|
106
|
+
fullWidth: false,
|
|
107
|
+
showCharCount: false,
|
|
108
|
+
autoGrow: true,
|
|
109
|
+
label: "Label",
|
|
110
|
+
placeholder: "Enter text…",
|
|
205
111
|
},
|
|
206
112
|
}
|
|
207
113
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
render: () => (
|
|
211
|
-
<div className="max-w-lg space-y-6">
|
|
212
|
-
<div>
|
|
213
|
-
<h3 className="text-fm-primary mb-2 text-sm font-medium">Underline</h3>
|
|
214
|
-
<TextArea
|
|
215
|
-
label="Underline Style"
|
|
216
|
-
placeholder="Minimalist underline style..."
|
|
217
|
-
decoration="underline"
|
|
218
|
-
fullWidth
|
|
219
|
-
/>
|
|
220
|
-
</div>
|
|
114
|
+
export default meta
|
|
115
|
+
type Story = StoryObj<typeof meta>
|
|
221
116
|
|
|
222
|
-
|
|
223
|
-
<h3 className="text-fm-primary mb-2 text-sm font-medium">Outline</h3>
|
|
224
|
-
<TextArea
|
|
225
|
-
label="Outline Style"
|
|
226
|
-
placeholder="Traditional outlined style..."
|
|
227
|
-
decoration="outline"
|
|
228
|
-
fullWidth
|
|
229
|
-
/>
|
|
230
|
-
</div>
|
|
117
|
+
// ─── 1. Playground ───────────────────────────────────────────────────────────
|
|
231
118
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
119
|
+
export const Playground: Story = {
|
|
120
|
+
parameters: {
|
|
121
|
+
docs: {
|
|
122
|
+
description: {
|
|
123
|
+
story:
|
|
124
|
+
"Use the Storybook controls panel to configure every prop. The textarea below reflects your selections live.",
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
render: (args) => (
|
|
129
|
+
<div className="w-96 space-y-4">
|
|
130
|
+
<TextArea {...args} fullWidth />
|
|
131
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border px-4 py-3">
|
|
132
|
+
<code className="text-fm-secondary text-fm-md leading-fm-md font-(--font-fm-mono)">
|
|
133
|
+
{`<TextArea variant="${args.variant}" decoration="${args.decoration}"${args.disabled ? " disabled" : ""}${args.required ? " required" : ""}${args.showCharCount ? " showCharCount" : ""} />`}
|
|
134
|
+
</code>
|
|
240
135
|
</div>
|
|
241
136
|
</div>
|
|
242
137
|
),
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ─── 2. AllVariants ──────────────────────────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
export const AllVariants: Story = {
|
|
243
143
|
parameters: {
|
|
244
144
|
docs: {
|
|
245
145
|
description: {
|
|
246
146
|
story:
|
|
247
|
-
"
|
|
147
|
+
"Full matrix of all four validation variants (default, error, warning, success) across all three decoration styles (underline, outline, filled).",
|
|
248
148
|
},
|
|
249
149
|
},
|
|
250
150
|
},
|
|
251
|
-
|
|
151
|
+
render: () => {
|
|
152
|
+
const variants = [
|
|
153
|
+
{
|
|
154
|
+
variant: "default" as const,
|
|
155
|
+
label: "Default",
|
|
156
|
+
helperText: "Helper text",
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
variant: "error" as const,
|
|
160
|
+
label: "Error",
|
|
161
|
+
helperText: "Something went wrong",
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
variant: "warning" as const,
|
|
165
|
+
label: "Warning",
|
|
166
|
+
helperText: "Double-check this value",
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
variant: "success" as const,
|
|
170
|
+
label: "Success",
|
|
171
|
+
helperText: "Looks great!",
|
|
172
|
+
},
|
|
173
|
+
]
|
|
174
|
+
const decorations = [
|
|
175
|
+
{ decoration: "underline" as const, label: "Underline" },
|
|
176
|
+
{ decoration: "outline" as const, label: "Outline" },
|
|
177
|
+
{ decoration: "filled" as const, label: "Filled" },
|
|
178
|
+
]
|
|
252
179
|
|
|
253
|
-
// Controlled component story
|
|
254
|
-
export const Controlled: Story = {
|
|
255
|
-
render: (args) => {
|
|
256
|
-
const [value, setValue] = useState("")
|
|
257
180
|
return (
|
|
258
|
-
<
|
|
259
|
-
{
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
181
|
+
<div className="space-y-10">
|
|
182
|
+
{decorations.map(({ decoration, label: decLabel }) => (
|
|
183
|
+
<div key={decoration}>
|
|
184
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-4 font-medium">
|
|
185
|
+
{decLabel}
|
|
186
|
+
</h4>
|
|
187
|
+
<div className="flex flex-wrap gap-6">
|
|
188
|
+
{variants.map(({ variant, label, helperText }) => (
|
|
189
|
+
<div key={variant} className="w-52 space-y-2 text-center">
|
|
190
|
+
<TextArea
|
|
191
|
+
label={label}
|
|
192
|
+
placeholder={`${label} state…`}
|
|
193
|
+
variant={variant}
|
|
194
|
+
decoration={decoration}
|
|
195
|
+
helperText={helperText}
|
|
196
|
+
fullWidth
|
|
197
|
+
/>
|
|
198
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
199
|
+
{label}
|
|
200
|
+
</p>
|
|
201
|
+
</div>
|
|
202
|
+
))}
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
))}
|
|
206
|
+
</div>
|
|
263
207
|
)
|
|
264
208
|
},
|
|
265
|
-
args: {
|
|
266
|
-
label: "Controlled TextArea",
|
|
267
|
-
placeholder: "Type something...",
|
|
268
|
-
decoration: "filled",
|
|
269
|
-
fullWidth: true,
|
|
270
|
-
},
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// With helper text
|
|
274
|
-
export const WithHelperText: Story = {
|
|
275
|
-
args: {
|
|
276
|
-
label: "Description",
|
|
277
|
-
placeholder: "Enter a description...",
|
|
278
|
-
helperText: "Please provide a detailed description",
|
|
279
|
-
decoration: "outline",
|
|
280
|
-
fullWidth: true,
|
|
281
|
-
},
|
|
282
209
|
}
|
|
283
210
|
|
|
284
|
-
//
|
|
285
|
-
export const WithCharCount: Story = {
|
|
286
|
-
args: {
|
|
287
|
-
label: "Comment",
|
|
288
|
-
placeholder: "Share your thoughts...",
|
|
289
|
-
maxLength: 280,
|
|
290
|
-
showCharCount: true,
|
|
291
|
-
decoration: "filled",
|
|
292
|
-
fullWidth: true,
|
|
293
|
-
},
|
|
294
|
-
}
|
|
211
|
+
// ─── 3. Configurations ───────────────────────────────────────────────────────
|
|
295
212
|
|
|
296
|
-
|
|
297
|
-
export const ErrorStates: Story = {
|
|
298
|
-
render: () => (
|
|
299
|
-
<div className="max-w-lg space-y-4">
|
|
300
|
-
<TextArea
|
|
301
|
-
label="Underline Error"
|
|
302
|
-
placeholder="Error with underline..."
|
|
303
|
-
variant="error"
|
|
304
|
-
decoration="underline"
|
|
305
|
-
helperText="This field is required"
|
|
306
|
-
fullWidth
|
|
307
|
-
/>
|
|
308
|
-
<TextArea
|
|
309
|
-
label="Outline Error"
|
|
310
|
-
placeholder="Error with outline..."
|
|
311
|
-
variant="error"
|
|
312
|
-
decoration="outline"
|
|
313
|
-
helperText="This field is required"
|
|
314
|
-
fullWidth
|
|
315
|
-
/>
|
|
316
|
-
<TextArea
|
|
317
|
-
label="Filled Error"
|
|
318
|
-
placeholder="Error with filled..."
|
|
319
|
-
variant="error"
|
|
320
|
-
decoration="filled"
|
|
321
|
-
helperText="This field is required"
|
|
322
|
-
fullWidth
|
|
323
|
-
/>
|
|
324
|
-
</div>
|
|
325
|
-
),
|
|
213
|
+
export const Configurations: Story = {
|
|
326
214
|
parameters: {
|
|
327
215
|
docs: {
|
|
328
216
|
description: {
|
|
329
|
-
story:
|
|
217
|
+
story:
|
|
218
|
+
"Non-variant configuration axes: character count with maxLength, fixed height with autoGrow disabled, height constraints (minHeight + maxHeight), and required field indicator.",
|
|
330
219
|
},
|
|
331
220
|
},
|
|
332
221
|
},
|
|
333
|
-
|
|
222
|
+
render: () => {
|
|
223
|
+
const [tweetValue, setTweetValue] = useState("")
|
|
224
|
+
const [bioValue, setBioValue] = useState("")
|
|
334
225
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
226
|
+
return (
|
|
227
|
+
<div className="w-full max-w-sm space-y-10">
|
|
228
|
+
{/* Character count */}
|
|
229
|
+
<div>
|
|
230
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-4 font-medium">
|
|
231
|
+
Character Count
|
|
232
|
+
</h4>
|
|
233
|
+
<div className="space-y-2 text-center">
|
|
234
|
+
<TextArea
|
|
235
|
+
label="Tweet"
|
|
236
|
+
placeholder="What's happening?"
|
|
237
|
+
value={tweetValue}
|
|
238
|
+
onChange={(e) => setTweetValue(e.target.value)}
|
|
239
|
+
maxLength={280}
|
|
240
|
+
showCharCount
|
|
241
|
+
decoration="underline"
|
|
242
|
+
fullWidth
|
|
243
|
+
/>
|
|
244
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
245
|
+
showCharCount + maxLength={280}
|
|
246
|
+
</p>
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
346
249
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
250
|
+
{/* Fixed height */}
|
|
251
|
+
<div>
|
|
252
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-4 font-medium">
|
|
253
|
+
Fixed Height
|
|
254
|
+
</h4>
|
|
255
|
+
<div className="space-y-2 text-center">
|
|
256
|
+
<TextArea
|
|
257
|
+
label="Notes"
|
|
258
|
+
placeholder="Type your notes here…"
|
|
259
|
+
autoGrow={false}
|
|
260
|
+
rows={5}
|
|
261
|
+
decoration="outline"
|
|
262
|
+
fullWidth
|
|
263
|
+
/>
|
|
264
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
265
|
+
autoGrow=false + rows={5}
|
|
266
|
+
</p>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
358
269
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
270
|
+
{/* Height constraints */}
|
|
271
|
+
<div>
|
|
272
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-4 font-medium">
|
|
273
|
+
Height Constraints
|
|
274
|
+
</h4>
|
|
275
|
+
<div className="space-y-2 text-center">
|
|
276
|
+
<TextArea
|
|
277
|
+
label="Bio"
|
|
278
|
+
placeholder="Tell us about yourself (grows up to 200 px)…"
|
|
279
|
+
value={bioValue}
|
|
280
|
+
onChange={(e) => setBioValue(e.target.value)}
|
|
281
|
+
autoGrow
|
|
282
|
+
minHeight={60}
|
|
283
|
+
maxHeight={200}
|
|
284
|
+
decoration="filled"
|
|
285
|
+
fullWidth
|
|
286
|
+
/>
|
|
287
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
288
|
+
autoGrow + minHeight=60 + maxHeight=200
|
|
289
|
+
</p>
|
|
290
|
+
</div>
|
|
291
|
+
</div>
|
|
370
292
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
293
|
+
{/* Required */}
|
|
294
|
+
<div>
|
|
295
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-4 font-medium">
|
|
296
|
+
Required
|
|
297
|
+
</h4>
|
|
298
|
+
<div className="space-y-2 text-center">
|
|
299
|
+
<TextArea
|
|
300
|
+
label="Feedback"
|
|
301
|
+
placeholder="Your feedback is required…"
|
|
302
|
+
required
|
|
303
|
+
helperText="This field is required"
|
|
304
|
+
decoration="outline"
|
|
305
|
+
fullWidth
|
|
306
|
+
/>
|
|
307
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm">
|
|
308
|
+
required — asterisk added to label
|
|
309
|
+
</p>
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
)
|
|
380
314
|
},
|
|
381
315
|
}
|
|
382
316
|
|
|
383
|
-
//
|
|
384
|
-
export const FixedHeight: Story = {
|
|
385
|
-
args: {
|
|
386
|
-
label: "Fixed Height TextArea",
|
|
387
|
-
placeholder: "This textarea has a fixed height...",
|
|
388
|
-
autoGrow: false,
|
|
389
|
-
rows: 5,
|
|
390
|
-
decoration: "outline",
|
|
391
|
-
fullWidth: true,
|
|
392
|
-
},
|
|
393
|
-
}
|
|
317
|
+
// ─── 4. States ───────────────────────────────────────────────────────────────
|
|
394
318
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
decoration: "filled",
|
|
404
|
-
fullWidth: true,
|
|
319
|
+
export const States: Story = {
|
|
320
|
+
parameters: {
|
|
321
|
+
docs: {
|
|
322
|
+
description: {
|
|
323
|
+
story:
|
|
324
|
+
"All five textarea states side by side: default (idle), disabled (non-interactive with pre-filled content), error, warning, and success.",
|
|
325
|
+
},
|
|
326
|
+
},
|
|
405
327
|
},
|
|
328
|
+
render: () => (
|
|
329
|
+
<div className="w-full max-w-sm space-y-8">
|
|
330
|
+
{[
|
|
331
|
+
{
|
|
332
|
+
label: "Default",
|
|
333
|
+
props: {
|
|
334
|
+
label: "Default",
|
|
335
|
+
placeholder: "Enter your message…",
|
|
336
|
+
variant: "default" as const,
|
|
337
|
+
helperText: "Helper text",
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
label: "Disabled",
|
|
342
|
+
props: {
|
|
343
|
+
label: "Disabled",
|
|
344
|
+
value: "This textarea cannot be edited",
|
|
345
|
+
variant: "default" as const,
|
|
346
|
+
disabled: true,
|
|
347
|
+
helperText: "Read-only content",
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
label: "Error",
|
|
352
|
+
props: {
|
|
353
|
+
label: "Error",
|
|
354
|
+
placeholder: "Enter your message…",
|
|
355
|
+
variant: "error" as const,
|
|
356
|
+
helperText: "This field is required",
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
label: "Warning",
|
|
361
|
+
props: {
|
|
362
|
+
label: "Warning",
|
|
363
|
+
value: "Short",
|
|
364
|
+
variant: "warning" as const,
|
|
365
|
+
helperText: "Please provide more detail",
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
label: "Success",
|
|
370
|
+
props: {
|
|
371
|
+
label: "Success",
|
|
372
|
+
value:
|
|
373
|
+
"This is a detailed and valid message that meets all requirements.",
|
|
374
|
+
variant: "success" as const,
|
|
375
|
+
helperText: "Looks great!",
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
].map(({ label, props }) => (
|
|
379
|
+
<div key={label}>
|
|
380
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-3 font-medium">
|
|
381
|
+
{label}
|
|
382
|
+
</h4>
|
|
383
|
+
<TextArea decoration="outline" fullWidth {...props} />
|
|
384
|
+
</div>
|
|
385
|
+
))}
|
|
386
|
+
</div>
|
|
387
|
+
),
|
|
406
388
|
}
|
|
407
389
|
|
|
408
|
-
//
|
|
409
|
-
export const CharacterLimitWithWarning: Story = {
|
|
410
|
-
render: (args) => {
|
|
411
|
-
const [value, setValue] = useState("")
|
|
412
|
-
const isNearLimit = value.length > 250
|
|
413
|
-
const isOverHalfway = value.length > 140
|
|
390
|
+
// ─── 5. Interactive ──────────────────────────────────────────────────────────
|
|
414
391
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
helperText={
|
|
424
|
-
isNearLimit
|
|
425
|
-
? "You're approaching the character limit"
|
|
426
|
-
: "Share what's on your mind"
|
|
427
|
-
}
|
|
428
|
-
/>
|
|
429
|
-
)
|
|
430
|
-
},
|
|
431
|
-
args: {
|
|
432
|
-
label: "Tweet",
|
|
433
|
-
placeholder: "What's happening?",
|
|
434
|
-
maxLength: 280,
|
|
435
|
-
showCharCount: true,
|
|
436
|
-
decoration: "underline",
|
|
437
|
-
fullWidth: true,
|
|
392
|
+
export const Interactive: Story = {
|
|
393
|
+
parameters: {
|
|
394
|
+
docs: {
|
|
395
|
+
description: {
|
|
396
|
+
story:
|
|
397
|
+
"Live textarea demo with real-time character counting, dynamic variant changes based on content length, and preset buttons for quickly populating different content lengths. The variant and helper text update as you type.",
|
|
398
|
+
},
|
|
399
|
+
},
|
|
438
400
|
},
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
// Atomic composition story - demonstrates how to use individual components
|
|
442
|
-
export const AtomicComposition: Story = {
|
|
443
|
-
render: () => {
|
|
401
|
+
render: function InteractiveTextArea() {
|
|
444
402
|
const [value, setValue] = useState("")
|
|
445
|
-
const
|
|
403
|
+
const [decoration, setDecoration] = useState<
|
|
404
|
+
"underline" | "outline" | "filled"
|
|
405
|
+
>("outline")
|
|
406
|
+
const maxLength = 280
|
|
407
|
+
|
|
408
|
+
const variant =
|
|
409
|
+
value.length === 0
|
|
410
|
+
? "default"
|
|
411
|
+
: value.length > 240
|
|
412
|
+
? "warning"
|
|
413
|
+
: value.length > 10
|
|
414
|
+
? "success"
|
|
415
|
+
: "default"
|
|
416
|
+
|
|
417
|
+
const helperText =
|
|
418
|
+
value.length === 0
|
|
419
|
+
? "Start typing…"
|
|
420
|
+
: value.length > 240
|
|
421
|
+
? "Approaching the character limit"
|
|
422
|
+
: "Looks good!"
|
|
423
|
+
|
|
424
|
+
const presets = [
|
|
425
|
+
{ label: "Short", text: "Hello world." },
|
|
426
|
+
{
|
|
427
|
+
label: "Medium",
|
|
428
|
+
text: "This is a medium-length message that provides some context and detail about the topic at hand.",
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
label: "Long",
|
|
432
|
+
text: "This is a longer message that demonstrates what happens when content approaches the character limit. Keep writing to see the warning state activate as you get closer to 280 characters. Almost there now — just a few more words should do it to trigger the warning.",
|
|
433
|
+
},
|
|
434
|
+
{ label: "Clear", text: "" },
|
|
435
|
+
]
|
|
446
436
|
|
|
447
437
|
return (
|
|
448
|
-
<
|
|
449
|
-
<
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
438
|
+
<div className="w-full p-8">
|
|
439
|
+
<div className="mx-auto max-w-3xl space-y-6">
|
|
440
|
+
<div className="grid grid-cols-1 gap-6 lg:grid-cols-3">
|
|
441
|
+
{/* Controls panel */}
|
|
442
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary space-y-5 rounded-xl border p-5">
|
|
443
|
+
<p className="text-fm-primary font-fm-brand text-fm-sm leading-fm-sm font-semibold tracking-widest uppercase">
|
|
444
|
+
Presets
|
|
445
|
+
</p>
|
|
446
|
+
<div className="space-y-2">
|
|
447
|
+
{presets.map(({ label, text }) => (
|
|
448
|
+
<button
|
|
449
|
+
key={label}
|
|
450
|
+
onClick={() => setValue(text)}
|
|
451
|
+
className="border-fm-divider-secondary text-fm-primary font-fm-text text-fm-sm leading-fm-sm hover:bg-fm-surface-primary w-full rounded-lg border px-3 py-2 text-left transition-colors"
|
|
452
|
+
>
|
|
453
|
+
{label}
|
|
454
|
+
</button>
|
|
455
|
+
))}
|
|
456
|
+
</div>
|
|
457
|
+
<div className="border-fm-divider-secondary border-t pt-4" />
|
|
458
|
+
<p className="text-fm-primary font-fm-brand text-fm-sm leading-fm-sm font-semibold tracking-widest uppercase">
|
|
459
|
+
Decoration
|
|
460
|
+
</p>
|
|
461
|
+
<div className="space-y-2">
|
|
462
|
+
{(["underline", "outline", "filled"] as const).map((d) => (
|
|
463
|
+
<button
|
|
464
|
+
key={d}
|
|
465
|
+
onClick={() => setDecoration(d)}
|
|
466
|
+
className={`border-fm-divider-secondary text-fm-primary font-fm-text text-fm-sm leading-fm-sm w-full rounded-lg border px-3 py-2 text-left capitalize transition-colors ${decoration === d ? "bg-fm-surface-primary" : "hover:bg-fm-surface-primary"}`}
|
|
467
|
+
>
|
|
468
|
+
{d}
|
|
469
|
+
</button>
|
|
470
|
+
))}
|
|
471
|
+
</div>
|
|
472
|
+
<div className="border-fm-divider-secondary border-t pt-4" />
|
|
473
|
+
<div className="border-fm-divider-secondary bg-fm-surface-primary rounded-lg border p-3">
|
|
474
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm mb-1 font-medium">
|
|
475
|
+
Characters
|
|
476
|
+
</p>
|
|
477
|
+
<p className="text-fm-primary text-fm-sm leading-fm-sm font-(--font-fm-mono)">
|
|
478
|
+
{value.length} / {maxLength}
|
|
479
|
+
</p>
|
|
480
|
+
</div>
|
|
481
|
+
</div>
|
|
482
|
+
|
|
483
|
+
{/* Preview stage */}
|
|
484
|
+
<div className="flex flex-col gap-3 lg:col-span-2">
|
|
485
|
+
<TextArea
|
|
486
|
+
label="Message"
|
|
487
|
+
placeholder="Start typing to see real-time validation…"
|
|
488
|
+
value={value}
|
|
489
|
+
onChange={(e) => setValue(e.target.value)}
|
|
490
|
+
variant={variant as "default" | "warning" | "success"}
|
|
491
|
+
helperText={helperText}
|
|
492
|
+
decoration={decoration}
|
|
493
|
+
maxLength={maxLength}
|
|
494
|
+
showCharCount
|
|
495
|
+
autoGrow
|
|
496
|
+
minHeight={80}
|
|
497
|
+
fullWidth
|
|
498
|
+
/>
|
|
499
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border px-4 py-3">
|
|
500
|
+
<code className="text-fm-secondary text-fm-md leading-fm-md font-(--font-fm-mono)">
|
|
501
|
+
{`variant="${variant}" | ${value.length}/${maxLength} chars`}
|
|
502
|
+
</code>
|
|
503
|
+
</div>
|
|
504
|
+
</div>
|
|
505
|
+
</div>
|
|
474
506
|
</div>
|
|
475
|
-
</
|
|
507
|
+
</div>
|
|
476
508
|
)
|
|
477
509
|
},
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// ─── 6. Parts ────────────────────────────────────────────────────────────────
|
|
513
|
+
|
|
514
|
+
export const Parts: Story = {
|
|
478
515
|
parameters: {
|
|
479
516
|
docs: {
|
|
480
517
|
description: {
|
|
481
518
|
story:
|
|
482
|
-
"
|
|
519
|
+
"Each atomic sub-component shown individually: TextArea (convenience wrapper), and the atomic TextArea.Root + TextArea.Label + TextArea.Wrapper + TextArea.Base + TextArea.HelperText + TextArea.CharCount composition for full layout control.",
|
|
483
520
|
},
|
|
484
521
|
},
|
|
485
522
|
},
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// Complex form example
|
|
489
|
-
export const ComplexFormExample: Story = {
|
|
490
523
|
render: () => {
|
|
491
|
-
const [
|
|
492
|
-
const
|
|
524
|
+
const [atomicValue, setAtomicValue] = useState("")
|
|
525
|
+
const maxLength = 100
|
|
493
526
|
|
|
494
527
|
return (
|
|
495
|
-
<div className="max-w-lg space-y-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
528
|
+
<div className="w-full max-w-lg space-y-10">
|
|
529
|
+
{/* Convenience component */}
|
|
530
|
+
<div>
|
|
531
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-4 font-medium">
|
|
532
|
+
TextArea — convenience wrapper
|
|
533
|
+
</h4>
|
|
534
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-4">
|
|
535
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm mb-3">
|
|
536
|
+
The default export combines all atomic parts into a single
|
|
537
|
+
component with label, textarea, helper text, and optional
|
|
538
|
+
character count.
|
|
539
|
+
</p>
|
|
540
|
+
<TextArea
|
|
541
|
+
label="Description"
|
|
542
|
+
placeholder="Tell us more…"
|
|
543
|
+
helperText="Provide a clear description"
|
|
544
|
+
maxLength={200}
|
|
545
|
+
showCharCount
|
|
546
|
+
decoration="outline"
|
|
547
|
+
fullWidth
|
|
548
|
+
/>
|
|
549
|
+
</div>
|
|
550
|
+
</div>
|
|
507
551
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
552
|
+
{/* Atomic composition */}
|
|
553
|
+
<div>
|
|
554
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-4 font-medium">
|
|
555
|
+
Atomic Composition
|
|
556
|
+
</h4>
|
|
557
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-4">
|
|
558
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm mb-3">
|
|
559
|
+
Build the same result using individual primitives for full control
|
|
560
|
+
over layout and positioning.
|
|
561
|
+
</p>
|
|
562
|
+
<TextArea.Root fullWidth>
|
|
563
|
+
<TextArea.Label htmlFor="atomic-textarea" required>
|
|
564
|
+
Custom Composed TextArea
|
|
565
|
+
</TextArea.Label>
|
|
566
|
+
<TextArea.Wrapper>
|
|
567
|
+
<TextArea.Base
|
|
568
|
+
id="atomic-textarea"
|
|
569
|
+
placeholder="Built with atomic components…"
|
|
570
|
+
value={atomicValue}
|
|
571
|
+
onChange={(e) => setAtomicValue(e.target.value)}
|
|
572
|
+
maxLength={maxLength}
|
|
573
|
+
variant="default"
|
|
574
|
+
decoration="outline"
|
|
575
|
+
/>
|
|
576
|
+
</TextArea.Wrapper>
|
|
577
|
+
<div className="flex items-center justify-between gap-2">
|
|
578
|
+
<TextArea.HelperText variant="default">
|
|
579
|
+
Full control via atomic parts
|
|
580
|
+
</TextArea.HelperText>
|
|
581
|
+
<TextArea.CharCount
|
|
582
|
+
currentLength={atomicValue.length}
|
|
583
|
+
maxLength={maxLength}
|
|
584
|
+
/>
|
|
585
|
+
</div>
|
|
586
|
+
</TextArea.Root>
|
|
587
|
+
</div>
|
|
588
|
+
</div>
|
|
526
589
|
</div>
|
|
527
590
|
)
|
|
528
591
|
},
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// ─── 7. UseCases ─────────────────────────────────────────────────────────────
|
|
595
|
+
|
|
596
|
+
export const UseCases: Story = {
|
|
529
597
|
parameters: {
|
|
530
598
|
docs: {
|
|
531
599
|
description: {
|
|
532
600
|
story:
|
|
533
|
-
"
|
|
601
|
+
"Real product-shaped examples: a social post composer with character counter and dynamic warnings, a support ticket form with required feedback, and a profile bio editor with success validation.",
|
|
534
602
|
},
|
|
535
603
|
},
|
|
536
604
|
},
|
|
537
|
-
|
|
605
|
+
render: function UseCasesTextArea() {
|
|
606
|
+
const [post, setPost] = useState("")
|
|
607
|
+
const [ticket, setTicket] = useState("")
|
|
608
|
+
const [bio, setBio] = useState("")
|
|
609
|
+
|
|
610
|
+
const postVariant =
|
|
611
|
+
post.length === 0 ? "default" : post.length > 240 ? "warning" : "success"
|
|
612
|
+
const postHelper =
|
|
613
|
+
post.length > 240
|
|
614
|
+
? "Approaching the 280-character limit"
|
|
615
|
+
: post.length > 0
|
|
616
|
+
? "Ready to post"
|
|
617
|
+
: "What's on your mind?"
|
|
618
|
+
|
|
619
|
+
const bioVariant =
|
|
620
|
+
bio.length > 10 ? "success" : bio.length > 0 ? "default" : "default"
|
|
621
|
+
const bioHelper =
|
|
622
|
+
bio.length > 10 ? "Looking good!" : "Tell listeners a bit about yourself"
|
|
538
623
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
624
|
+
return (
|
|
625
|
+
<div className="mx-auto max-w-3xl space-y-8 p-8">
|
|
626
|
+
{/* Social post composer */}
|
|
627
|
+
<div className="space-y-4">
|
|
628
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
629
|
+
Post Composer
|
|
630
|
+
</h4>
|
|
631
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary max-w-md rounded-xl border p-5">
|
|
632
|
+
<TextArea
|
|
633
|
+
label="New Post"
|
|
634
|
+
placeholder="What's happening?"
|
|
635
|
+
value={post}
|
|
636
|
+
onChange={(e) => setPost(e.target.value)}
|
|
637
|
+
variant={postVariant}
|
|
638
|
+
helperText={postHelper}
|
|
639
|
+
maxLength={280}
|
|
640
|
+
showCharCount
|
|
641
|
+
decoration="underline"
|
|
642
|
+
fullWidth
|
|
643
|
+
/>
|
|
644
|
+
</div>
|
|
645
|
+
</div>
|
|
553
646
|
|
|
554
|
-
|
|
555
|
-
<
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
647
|
+
{/* Support ticket */}
|
|
648
|
+
<div className="space-y-4">
|
|
649
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
650
|
+
Support Ticket
|
|
651
|
+
</h4>
|
|
652
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary max-w-md rounded-xl border p-5">
|
|
653
|
+
<TextArea
|
|
654
|
+
label="Describe your issue"
|
|
655
|
+
placeholder="Please describe what happened in as much detail as possible…"
|
|
656
|
+
value={ticket}
|
|
657
|
+
onChange={(e) => setTicket(e.target.value)}
|
|
658
|
+
variant={
|
|
659
|
+
ticket.length > 20
|
|
660
|
+
? "success"
|
|
661
|
+
: ticket.length > 0
|
|
662
|
+
? "default"
|
|
663
|
+
: "default"
|
|
664
|
+
}
|
|
665
|
+
helperText={
|
|
666
|
+
ticket.length > 20
|
|
667
|
+
? "Thank you — our team will review this"
|
|
668
|
+
: "Required — minimum 20 characters for a useful report"
|
|
669
|
+
}
|
|
670
|
+
required
|
|
671
|
+
autoGrow
|
|
672
|
+
minHeight={80}
|
|
673
|
+
maxHeight={240}
|
|
674
|
+
decoration="outline"
|
|
675
|
+
fullWidth
|
|
676
|
+
/>
|
|
677
|
+
</div>
|
|
678
|
+
</div>
|
|
564
679
|
|
|
565
|
-
|
|
566
|
-
<
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
680
|
+
{/* Profile bio */}
|
|
681
|
+
<div className="space-y-4">
|
|
682
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md font-medium">
|
|
683
|
+
Profile Bio Editor
|
|
684
|
+
</h4>
|
|
685
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary max-w-md rounded-xl border p-5">
|
|
686
|
+
<TextArea
|
|
687
|
+
label="Bio"
|
|
688
|
+
placeholder="I'm a podcast creator who loves…"
|
|
689
|
+
value={bio}
|
|
690
|
+
onChange={(e) => setBio(e.target.value)}
|
|
691
|
+
variant={bioVariant}
|
|
692
|
+
helperText={bioHelper}
|
|
693
|
+
maxLength={160}
|
|
694
|
+
showCharCount
|
|
695
|
+
decoration="filled"
|
|
696
|
+
fullWidth
|
|
697
|
+
/>
|
|
698
|
+
</div>
|
|
699
|
+
</div>
|
|
574
700
|
</div>
|
|
575
|
-
|
|
576
|
-
|
|
701
|
+
)
|
|
702
|
+
},
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// ─── 8. Accessibility ────────────────────────────────────────────────────────
|
|
706
|
+
|
|
707
|
+
export const Accessibility: Story = {
|
|
577
708
|
parameters: {
|
|
578
709
|
docs: {
|
|
579
710
|
description: {
|
|
580
711
|
story:
|
|
581
|
-
"
|
|
712
|
+
"Accessibility-focused examples covering ARIA attributes, required field announcements, error state with aria-invalid, and the character count linked via aria-describedby. All interactive elements are keyboard-reachable and screen-reader friendly.",
|
|
582
713
|
},
|
|
583
714
|
},
|
|
584
715
|
},
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
// All variants with all decorations
|
|
588
|
-
export const AllVariantsAllDecorations: Story = {
|
|
589
716
|
render: () => (
|
|
590
|
-
<div className="
|
|
591
|
-
{/*
|
|
592
|
-
<div
|
|
593
|
-
<
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
placeholder="Default underline..."
|
|
597
|
-
variant="default"
|
|
598
|
-
decoration="underline"
|
|
599
|
-
helperText="Default variant"
|
|
600
|
-
fullWidth
|
|
601
|
-
/>
|
|
602
|
-
<TextArea
|
|
603
|
-
label="Error"
|
|
604
|
-
placeholder="Error underline..."
|
|
605
|
-
variant="error"
|
|
606
|
-
decoration="underline"
|
|
607
|
-
helperText="Something went wrong"
|
|
608
|
-
fullWidth
|
|
609
|
-
/>
|
|
610
|
-
<TextArea
|
|
611
|
-
label="Warning"
|
|
612
|
-
placeholder="Warning underline..."
|
|
613
|
-
variant="warning"
|
|
614
|
-
decoration="underline"
|
|
615
|
-
helperText="Please check input"
|
|
616
|
-
fullWidth
|
|
617
|
-
/>
|
|
717
|
+
<div className="w-full max-w-lg space-y-8">
|
|
718
|
+
{/* Required */}
|
|
719
|
+
<div>
|
|
720
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-3 font-medium">
|
|
721
|
+
Required Field
|
|
722
|
+
</h4>
|
|
618
723
|
<TextArea
|
|
619
|
-
label="
|
|
620
|
-
placeholder="
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
724
|
+
label="Feedback"
|
|
725
|
+
placeholder="Your feedback is required…"
|
|
726
|
+
required
|
|
727
|
+
helperText="Required — helps us improve the product"
|
|
728
|
+
decoration="outline"
|
|
624
729
|
fullWidth
|
|
625
730
|
/>
|
|
626
731
|
</div>
|
|
627
732
|
|
|
628
|
-
{/*
|
|
629
|
-
<div
|
|
630
|
-
<
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
placeholder="Default outline..."
|
|
634
|
-
variant="default"
|
|
635
|
-
decoration="outline"
|
|
636
|
-
helperText="Default variant"
|
|
637
|
-
fullWidth
|
|
638
|
-
/>
|
|
733
|
+
{/* Error + aria-invalid */}
|
|
734
|
+
<div>
|
|
735
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-3 font-medium">
|
|
736
|
+
Error State (aria-invalid)
|
|
737
|
+
</h4>
|
|
639
738
|
<TextArea
|
|
640
|
-
label="
|
|
641
|
-
placeholder="
|
|
739
|
+
label="Description"
|
|
740
|
+
placeholder="Enter a description…"
|
|
642
741
|
variant="error"
|
|
742
|
+
helperText="This field cannot be empty"
|
|
643
743
|
decoration="outline"
|
|
644
|
-
helperText="Something went wrong"
|
|
645
|
-
fullWidth
|
|
646
|
-
/>
|
|
647
|
-
<TextArea
|
|
648
|
-
label="Warning"
|
|
649
|
-
placeholder="Warning outline..."
|
|
650
|
-
variant="warning"
|
|
651
|
-
decoration="outline"
|
|
652
|
-
helperText="Please check input"
|
|
653
744
|
fullWidth
|
|
654
745
|
/>
|
|
746
|
+
</div>
|
|
747
|
+
|
|
748
|
+
{/* Character count + aria-describedby */}
|
|
749
|
+
<div>
|
|
750
|
+
<h4 className="text-fm-secondary font-fm-text text-fm-md leading-fm-md mb-3 font-medium">
|
|
751
|
+
Character Count (aria-describedby)
|
|
752
|
+
</h4>
|
|
655
753
|
<TextArea
|
|
656
|
-
label="
|
|
657
|
-
placeholder="
|
|
658
|
-
|
|
754
|
+
label="Short Bio"
|
|
755
|
+
placeholder="A brief introduction…"
|
|
756
|
+
maxLength={160}
|
|
757
|
+
showCharCount
|
|
758
|
+
helperText="The character count is announced to screen readers via aria-describedby"
|
|
659
759
|
decoration="outline"
|
|
660
|
-
helperText="Looks good!"
|
|
661
760
|
fullWidth
|
|
662
761
|
/>
|
|
663
762
|
</div>
|
|
664
763
|
|
|
665
|
-
{/*
|
|
666
|
-
<div className="
|
|
667
|
-
<
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
variant="warning"
|
|
688
|
-
decoration="filled"
|
|
689
|
-
helperText="Please check input"
|
|
690
|
-
fullWidth
|
|
691
|
-
/>
|
|
692
|
-
<TextArea
|
|
693
|
-
label="Success"
|
|
694
|
-
placeholder="Success filled..."
|
|
695
|
-
variant="success"
|
|
696
|
-
decoration="filled"
|
|
697
|
-
helperText="Looks good!"
|
|
698
|
-
fullWidth
|
|
699
|
-
/>
|
|
764
|
+
{/* Accessibility best practices */}
|
|
765
|
+
<div className="border-fm-divider-secondary bg-fm-surface-secondary rounded-lg border p-4">
|
|
766
|
+
<p className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm mb-2 font-medium">
|
|
767
|
+
Accessibility Best Practices
|
|
768
|
+
</p>
|
|
769
|
+
<ul className="space-y-1">
|
|
770
|
+
{[
|
|
771
|
+
"Always provide a label — the Label is linked via htmlFor/id",
|
|
772
|
+
"Use helperText to add context; it is linked via aria-describedby",
|
|
773
|
+
"The error variant sets aria-invalid automatically",
|
|
774
|
+
"required adds aria-required and an asterisk to the label",
|
|
775
|
+
"The character count element is also linked via aria-describedby",
|
|
776
|
+
"Tab moves focus into the textarea; Shift+Tab moves out",
|
|
777
|
+
].map((item) => (
|
|
778
|
+
<li
|
|
779
|
+
key={item}
|
|
780
|
+
className="text-fm-secondary font-fm-text text-fm-sm leading-fm-sm"
|
|
781
|
+
>
|
|
782
|
+
{item}
|
|
783
|
+
</li>
|
|
784
|
+
))}
|
|
785
|
+
</ul>
|
|
700
786
|
</div>
|
|
701
787
|
</div>
|
|
702
788
|
),
|
|
703
|
-
parameters: {
|
|
704
|
-
docs: {
|
|
705
|
-
description: {
|
|
706
|
-
story:
|
|
707
|
-
"Complete showcase of all visual variants across all decoration styles.",
|
|
708
|
-
},
|
|
709
|
-
},
|
|
710
|
-
},
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
// Unstyled example
|
|
714
|
-
export const Unstyled: Story = {
|
|
715
|
-
args: {
|
|
716
|
-
placeholder: "Completely unstyled textarea...",
|
|
717
|
-
unstyled: true,
|
|
718
|
-
className:
|
|
719
|
-
"border-2 border-dashed border-fm-divider-secondary p-4 rounded-lg bg-fm-surface-secondary",
|
|
720
|
-
fullWidth: true,
|
|
721
|
-
},
|
|
722
|
-
parameters: {
|
|
723
|
-
docs: {
|
|
724
|
-
description: {
|
|
725
|
-
story:
|
|
726
|
-
"Example of using the unstyled prop to create completely custom styling.",
|
|
727
|
-
},
|
|
728
|
-
},
|
|
729
|
-
},
|
|
730
789
|
}
|