@scm-manager/ui-components 3.10.4-20250824-132529 → 4.0.0-REACT18-20250824-143504

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/package.json +45 -50
  2. package/src/BranchSelector.stories.tsx +60 -11
  3. package/src/Breadcrumb.stories.tsx +131 -57
  4. package/src/Breadcrumb.tsx +3 -3
  5. package/src/CardColumn.stories.tsx +94 -27
  6. package/src/CardColumnSmall.stories.tsx +102 -27
  7. package/src/CommaSeparatedList.tsx +2 -2
  8. package/src/Date.stories.tsx +64 -17
  9. package/src/Duration.stories.tsx +92 -45
  10. package/src/ErrorBoundary.tsx +38 -58
  11. package/src/ErrorNotification.tsx +1 -1
  12. package/src/Help.stories.tsx +61 -17
  13. package/src/Help.tsx +5 -11
  14. package/src/Icon.stories.tsx +93 -13
  15. package/src/Image.tsx +2 -3
  16. package/src/LinkPaginator.tsx +9 -6
  17. package/src/Loading.stories.tsx +26 -6
  18. package/src/Logo.stories.tsx +44 -13
  19. package/src/Notification.stories.tsx +111 -23
  20. package/src/OverviewPageActions.tsx +5 -5
  21. package/src/Paginator.test.tsx +115 -94
  22. package/src/PdfViewer.stories.tsx +83 -5
  23. package/src/PreformattedCodeBlock.stories.tsx +97 -26
  24. package/src/ProtectedRoute.tsx +14 -39
  25. package/src/SmallLoadingSpinner.stories.tsx +26 -6
  26. package/src/SyntaxHighlighter.stories.tsx +122 -40
  27. package/src/SyntaxHighlighterRenderer.tsx +7 -7
  28. package/src/Tag.stories.tsx +135 -18
  29. package/src/Tag.tsx +2 -1
  30. package/src/Tooltip.stories.tsx +147 -34
  31. package/src/__snapshots__/storyshots.test.ts.snap +21 -144
  32. package/src/avatar/Avatar.ts +1 -1
  33. package/src/avatar/AvatarImage.tsx +2 -2
  34. package/src/avatar/AvatarWrapper.tsx +2 -2
  35. package/src/buttons/Button.stories.tsx +159 -0
  36. package/src/buttons/Button.tsx +5 -5
  37. package/src/config/ConfigurationBinder.tsx +39 -39
  38. package/src/config/ConfigurationForm.tsx +2 -1
  39. package/src/config/TitledSettings.tsx +4 -1
  40. package/src/forms/AddKeyValueEntryToTableField.stories.tsx +60 -25
  41. package/src/forms/Checkbox.stories.tsx +165 -27
  42. package/src/forms/Checkbox.tsx +1 -0
  43. package/src/forms/DropDown.stories.tsx +81 -35
  44. package/src/forms/FileInput.stories.tsx +85 -10
  45. package/src/forms/InputField.stories.tsx +210 -37
  46. package/src/forms/InputField.tsx +1 -0
  47. package/src/forms/Radio.stories.tsx +181 -34
  48. package/src/forms/Radio.tsx +1 -0
  49. package/src/forms/Select.stories.tsx +266 -46
  50. package/src/forms/Select.tsx +1 -0
  51. package/src/forms/Textarea.stories.tsx +99 -53
  52. package/src/forms/Textarea.tsx +1 -0
  53. package/src/forms/index.ts +3 -1
  54. package/src/index.ts +5 -5
  55. package/src/jest-dom.d.ts +17 -0
  56. package/src/layout/Footer.stories.tsx +114 -22
  57. package/src/layout/Footer.tsx +11 -7
  58. package/src/layout/FooterSection.tsx +1 -0
  59. package/src/layout/Header.tsx +2 -1
  60. package/src/layout/Page.tsx +1 -3
  61. package/src/layout/PrimaryContentColumn.tsx +2 -2
  62. package/src/layout/SecondaryNavigationColumn.tsx +3 -3
  63. package/src/layout/SubSubtitle.tsx +2 -1
  64. package/src/layout/Subtitle.tsx +2 -1
  65. package/src/layout/Title.tsx +2 -5
  66. package/src/markdown/LazyMarkdownView.tsx +16 -5
  67. package/src/markdown/MarkdownHeadingRenderer.test.ts +9 -1
  68. package/src/markdown/MarkdownHeadingRenderer.tsx +3 -3
  69. package/src/markdown/MarkdownImageRenderer.test.ts +8 -1
  70. package/src/markdown/MarkdownImageRenderer.tsx +2 -1
  71. package/src/markdown/MarkdownLinkRenderer.test.tsx +9 -0
  72. package/src/markdown/MarkdownLinkRenderer.tsx +6 -4
  73. package/src/markdown/MarkdownView.stories.tsx +224 -72
  74. package/src/markdown/markdownExtensions.ts +6 -4
  75. package/src/modals/ActiveModalCountContextProvider.tsx +2 -2
  76. package/src/modals/ConfirmAlert.stories.tsx +133 -44
  77. package/src/modals/ConfirmAlert.tsx +5 -4
  78. package/src/modals/Modal.stories.tsx +461 -252
  79. package/src/modals/Modal.tsx +12 -11
  80. package/src/modals/useRegisterModal.test.tsx +5 -4
  81. package/src/navigation/MenuContext.tsx +2 -2
  82. package/src/navigation/NavLink.tsx +4 -7
  83. package/src/navigation/PrimaryNavigation.tsx +2 -2
  84. package/src/navigation/PrimaryNavigationLink.tsx +6 -5
  85. package/src/navigation/RoutingProps.ts +1 -3
  86. package/src/navigation/SecondaryNavigation.stories.tsx +157 -45
  87. package/src/navigation/SecondaryNavigation.tsx +17 -16
  88. package/src/navigation/SecondaryNavigationItem.tsx +2 -1
  89. package/src/navigation/SubNavigation.tsx +4 -8
  90. package/src/navigation/useActiveMatch.ts +20 -22
  91. package/src/navigation/useNavigationLock.ts +1 -0
  92. package/src/popover/Popover.stories.tsx +111 -41
  93. package/src/popover/Popover.tsx +2 -5
  94. package/src/repos/Diff.stories.tsx +418 -223
  95. package/src/repos/Diff.tsx +1 -5
  96. package/src/repos/DiffTypes.ts +2 -14
  97. package/src/repos/HunkExpandDivider.tsx +2 -2
  98. package/src/repos/LoadingDiff.tsx +11 -6
  99. package/src/repos/RepositoryEntry.stories.tsx +217 -53
  100. package/src/repos/RepositoryFlag.tsx +2 -1
  101. package/src/repos/TokenizedDiffView.tsx +4 -2
  102. package/src/repos/annotate/Annotate.stories.tsx +225 -111
  103. package/src/repos/annotate/AnnotateLine.tsx +2 -1
  104. package/src/repos/changesets/ChangesetAuthor.tsx +4 -4
  105. package/src/repos/changesets/ChangesetButtonGroup.test.tsx +9 -5
  106. package/src/repos/changesets/ChangesetDiff.test.ts +10 -2
  107. package/src/repos/changesets/Changesets.stories.tsx +388 -197
  108. package/src/repos/changesets/Contributor.tsx +1 -1
  109. package/src/repos/changesets/ContributorRow.tsx +2 -2
  110. package/src/repos/changesets/SignatureIcon.tsx +1 -0
  111. package/src/repos/changesets/SingleChangeset.tsx +0 -1
  112. package/src/repos/diff/DiffFileTree.tsx +37 -89
  113. package/src/repos/diff/styledElements.tsx +4 -3
  114. package/src/repos/index.ts +15 -15
  115. package/src/search/Hit.tsx +3 -2
  116. package/src/search/TextHitField.stories.tsx +131 -43
  117. package/src/search/TextHitField.tsx +3 -2
  118. package/src/search/index.ts +1 -1
  119. package/src/storyshots.test.ts +66 -60
  120. package/src/table/Table.stories.tsx +146 -48
  121. package/src/table/Table.tsx +7 -8
  122. package/src/table/TextColumn.tsx +0 -9
  123. package/src/toast/Toast.tsx +2 -1
  124. package/src/toast/ToastArea.tsx +2 -2
  125. package/src/toast/ToastButton.tsx +2 -1
  126. package/src/toast/ToastButtons.tsx +4 -2
  127. package/src/toast/ToastNotification.tsx +2 -1
  128. package/src/toast/index.stories.tsx +144 -39
  129. package/src/toast/index.ts +1 -1
  130. package/src/buttons/index.stories.tsx +0 -85
@@ -14,15 +14,212 @@
14
14
  * along with this program. If not, see https://www.gnu.org/licenses/.
15
15
  */
16
16
 
17
+ // import React, { FC, useRef, useState } from "react";
18
+ // import Button from "../buttons/Button";
19
+ // import { useForm } from "react-hook-form";
20
+ // import { SubmitButton } from "../buttons";
21
+ // import { storiesOf } from "@storybook/react";
22
+ // import { MemoryRouter } from "react-router-dom";
23
+ // import Select from "./Select";
24
+ //
25
+ // const Ref: FC = () => {
26
+ // const ref = useRef<HTMLSelectElement>(null);
27
+ // const [selected, setSelected] = useState("false");
28
+ // return (
29
+ // <>
30
+ // <Select
31
+ // options={[
32
+ // { label: "foo", value: "true" },
33
+ // { label: "bar", value: "false" },
34
+ // ]}
35
+ // value={selected}
36
+ // label={"Ref Radio Button"}
37
+ // onChange={(e) => setSelected(e.target.value)}
38
+ // ref={ref}
39
+ // />
40
+ // <Button
41
+ // action={() => {
42
+ // if (ref.current) {
43
+ // ref.current.focus();
44
+ // }
45
+ // }}
46
+ // color="primary"
47
+ // >
48
+ // Focus Select Field
49
+ // </Button>
50
+ // {selected}
51
+ // </>
52
+ // );
53
+ // };
54
+ //
55
+ // type Settings = {
56
+ // rememberMe: string;
57
+ // scramblePassword: string;
58
+ // disabled: string;
59
+ // readonly: string;
60
+ // };
61
+ //
62
+ // const ReactHookForm: FC = () => {
63
+ // const { register, handleSubmit } = useForm<Settings>();
64
+ // const [stored, setStored] = useState<Settings>();
65
+ //
66
+ // const onSubmit = (settings: Settings) => {
67
+ // setStored(settings);
68
+ // };
69
+ //
70
+ // return (
71
+ // <>
72
+ // <form onSubmit={handleSubmit(onSubmit)}>
73
+ // <Select
74
+ // options={[
75
+ // { label: "Yes", value: "true" },
76
+ // { label: "No", value: "false" },
77
+ // ]}
78
+ // label="Remember Me"
79
+ // {...register("rememberMe")}
80
+ // />
81
+ // <Select
82
+ // options={[
83
+ // { label: "Yes", value: "true" },
84
+ // { label: "No", value: "false" },
85
+ // ]}
86
+ // label="Scramble Password"
87
+ // {...register("scramblePassword")}
88
+ // />
89
+ // <Select
90
+ // options={[
91
+ // { label: "Yes", value: "true" },
92
+ // { label: "No", value: "false" },
93
+ // ]}
94
+ // label="Disabled wont be submitted"
95
+ // defaultValue="false"
96
+ // disabled={true}
97
+ // {...register("disabled")}
98
+ // />
99
+ // <Select
100
+ // options={[
101
+ // { label: "Yes", value: "true" },
102
+ // { label: "No", value: "false" },
103
+ // ]}
104
+ // label="Readonly will be submitted"
105
+ // readOnly={true}
106
+ // defaultValue="false"
107
+ // {...register("readonly")}
108
+ // />
109
+ //
110
+ // <div className="pt-2">
111
+ // <SubmitButton>Submit</SubmitButton>
112
+ // </div>
113
+ // </form>
114
+ // {stored ? (
115
+ // <div className="mt-5">
116
+ // <pre>
117
+ // <code>{JSON.stringify(stored, null, 2)}</code>
118
+ // </pre>
119
+ // </div>
120
+ // ) : null}
121
+ // </>
122
+ // );
123
+ // };
124
+ //
125
+ // const LegacyEvents: FC = () => {
126
+ // const [value, setValue] = useState<string>();
127
+ // return (
128
+ // <>
129
+ // <Select
130
+ // options={[
131
+ // { label: "Yes", value: "true" },
132
+ // { label: "No", value: "false" },
133
+ // ]}
134
+ // onChange={setValue}
135
+ // />
136
+ // <div className="mt-3">{JSON.stringify(value)}</div>
137
+ // </>
138
+ // );
139
+ // };
140
+ //
141
+ // const AddNoExistingValue: FC = () => {
142
+ // const [value, setValue] = useState<string>("uscss-prometheus");
143
+ //
144
+ // return (
145
+ // <>
146
+ // <Select
147
+ // options={[
148
+ // {
149
+ // label: "Millennium Falcon",
150
+ // value: "millennium-falcon",
151
+ // },
152
+ // {
153
+ // label: "Razor Crest",
154
+ // value: "razor-crest",
155
+ // },
156
+ // ]}
157
+ // onChange={setValue}
158
+ // addValueToOptions={true}
159
+ // value={value}
160
+ // />
161
+ // <div className="mt-3">{value}</div>
162
+ // </>
163
+ // );
164
+ // };
165
+ //
166
+ // const PreselectOption: FC = () => {
167
+ // const [value, setValue] = useState<string>("razor-crest");
168
+ //
169
+ // return (
170
+ // <>
171
+ // <Select
172
+ // options={[
173
+ // {
174
+ // label: "Millennium Falcon",
175
+ // value: "millennium-falcon",
176
+ // },
177
+ // {
178
+ // label: "Razor Crest",
179
+ // value: "razor-crest",
180
+ // },
181
+ // {
182
+ // label: "USCSS Prometheus",
183
+ // value: "uscss-prometheus",
184
+ // },
185
+ // ]}
186
+ // onChange={setValue}
187
+ // value={value}
188
+ // />
189
+ // <div className="mt-3">{value}</div>
190
+ // </>
191
+ // );
192
+ // };
193
+ //
194
+ // storiesOf("Forms/Select", module)
195
+ // .addDecorator((storyFn) => <MemoryRouter>{storyFn()}</MemoryRouter>)
196
+ // .add("Add no existing value", () => <AddNoExistingValue />)
197
+ // .add("Ref", () => <Ref />)
198
+ // .add("Legacy Events", () => <LegacyEvents />)
199
+ // .add("ReactHookForm", () => <ReactHookForm />)
200
+ // .add("Preselect option", () => <PreselectOption />);
201
+
17
202
  import React, { FC, useRef, useState } from "react";
18
- import Button from "../buttons/Button";
19
- import { useForm } from "react-hook-form";
20
- import { SubmitButton } from "../buttons";
21
- import { storiesOf } from "@storybook/react";
22
203
  import { MemoryRouter } from "react-router-dom";
204
+ import { useForm } from "react-hook-form";
205
+ import styled from "styled-components";
206
+ import type { Meta, StoryObj } from "@storybook/react";
207
+
23
208
  import Select from "./Select";
209
+ import Button from "../buttons/Button";
210
+ import { SubmitButton } from "../buttons";
24
211
 
25
- const Ref: FC = () => {
212
+ // --- Helper Components & Styles (preserved from your original story) ---
213
+
214
+ const Decorator = styled.div`
215
+ padding: 2rem;
216
+ max-width: 30rem;
217
+ display: flex;
218
+ flex-direction: column;
219
+ gap: 1rem;
220
+ `;
221
+
222
+ const RefExample: FC = () => {
26
223
  const ref = useRef<HTMLSelectElement>(null);
27
224
  const [selected, setSelected] = useState("false");
28
225
  return (
@@ -33,7 +230,7 @@ const Ref: FC = () => {
33
230
  { label: "bar", value: "false" },
34
231
  ]}
35
232
  value={selected}
36
- label={"Ref Radio Button"}
233
+ label={"Ref Select Field"}
37
234
  onChange={(e) => setSelected(e.target.value)}
38
235
  ref={ref}
39
236
  />
@@ -47,7 +244,7 @@ const Ref: FC = () => {
47
244
  >
48
245
  Focus Select Field
49
246
  </Button>
50
- {selected}
247
+ Selected value: {selected}
51
248
  </>
52
249
  );
53
250
  };
@@ -59,7 +256,7 @@ type Settings = {
59
256
  readonly: string;
60
257
  };
61
258
 
62
- const ReactHookForm: FC = () => {
259
+ const ReactHookFormExample: FC = () => {
63
260
  const { register, handleSubmit } = useForm<Settings>();
64
261
  const [stored, setStored] = useState<Settings>();
65
262
 
@@ -106,23 +303,22 @@ const ReactHookForm: FC = () => {
106
303
  defaultValue="false"
107
304
  {...register("readonly")}
108
305
  />
109
-
110
306
  <div className="pt-2">
111
307
  <SubmitButton>Submit</SubmitButton>
112
308
  </div>
113
309
  </form>
114
- {stored ? (
310
+ {stored && (
115
311
  <div className="mt-5">
116
312
  <pre>
117
313
  <code>{JSON.stringify(stored, null, 2)}</code>
118
314
  </pre>
119
315
  </div>
120
- ) : null}
316
+ )}
121
317
  </>
122
318
  );
123
319
  };
124
320
 
125
- const LegacyEvents: FC = () => {
321
+ const OnChangeExample: FC = () => {
126
322
  const [value, setValue] = useState<string>();
127
323
  return (
128
324
  <>
@@ -133,68 +329,92 @@ const LegacyEvents: FC = () => {
133
329
  ]}
134
330
  onChange={setValue}
135
331
  />
136
- <div className="mt-3">{JSON.stringify(value)}</div>
332
+ <div className="mt-3">Selected value: {JSON.stringify(value)}</div>
137
333
  </>
138
334
  );
139
335
  };
140
336
 
141
- const AddNoExistingValue: FC = () => {
337
+ const AddNoExistingValueExample: FC = () => {
142
338
  const [value, setValue] = useState<string>("uscss-prometheus");
143
-
144
339
  return (
145
340
  <>
146
341
  <Select
147
342
  options={[
148
- {
149
- label: "Millennium Falcon",
150
- value: "millennium-falcon",
151
- },
152
- {
153
- label: "Razor Crest",
154
- value: "razor-crest",
155
- },
343
+ { label: "Millennium Falcon", value: "millennium-falcon" },
344
+ { label: "Razor Crest", value: "razor-crest" },
156
345
  ]}
157
346
  onChange={setValue}
158
347
  addValueToOptions={true}
159
348
  value={value}
160
349
  />
161
- <div className="mt-3">{value}</div>
350
+ <div className="mt-3">Selected value: {value}</div>
162
351
  </>
163
352
  );
164
353
  };
165
354
 
166
- const PreselectOption: FC = () => {
355
+ const PreselectOptionExample: FC = () => {
167
356
  const [value, setValue] = useState<string>("razor-crest");
168
-
169
357
  return (
170
358
  <>
171
359
  <Select
172
360
  options={[
173
- {
174
- label: "Millennium Falcon",
175
- value: "millennium-falcon",
176
- },
177
- {
178
- label: "Razor Crest",
179
- value: "razor-crest",
180
- },
181
- {
182
- label: "USCSS Prometheus",
183
- value: "uscss-prometheus",
184
- },
361
+ { label: "Millennium Falcon", value: "millennium-falcon" },
362
+ { label: "Razor Crest", value: "razor-crest" },
363
+ { label: "USCSS Prometheus", value: "uscss-prometheus" },
185
364
  ]}
186
365
  onChange={setValue}
187
366
  value={value}
188
367
  />
189
- <div className="mt-3">{value}</div>
368
+ <div className="mt-3">Selected value: {value}</div>
190
369
  </>
191
370
  );
192
371
  };
193
372
 
194
- storiesOf("Forms/Select", module)
195
- .addDecorator((storyFn) => <MemoryRouter>{storyFn()}</MemoryRouter>)
196
- .add("Add no existing value", () => <AddNoExistingValue />)
197
- .add("Ref", () => <Ref />)
198
- .add("Legacy Events", () => <LegacyEvents />)
199
- .add("ReactHookForm", () => <ReactHookForm />)
200
- .add("Preselect option", () => <PreselectOption />);
373
+ // --- Storybook Metadata ---
374
+
375
+ const meta: Meta<typeof Select> = {
376
+ title: "Forms/Select",
377
+ component: Select,
378
+ decorators: [(Story) => <Decorator>{Story()}</Decorator>, (Story) => <MemoryRouter>{Story()}</MemoryRouter>],
379
+ argTypes: {
380
+ onChange: { action: "changed" },
381
+ },
382
+ tags: ["autodocs"],
383
+ };
384
+
385
+ export default meta;
386
+
387
+ // --- Story Definitions ---
388
+
389
+ type Story = StoryObj<typeof meta>;
390
+
391
+ // A new, simple story to show the basic component and enable Controls
392
+ export const Default: Story = {
393
+ args: {
394
+ label: "Starship",
395
+ options: [
396
+ { label: "Millennium Falcon", value: "millennium-falcon" },
397
+ { label: "Razor Crest", value: "razor-crest" },
398
+ ],
399
+ },
400
+ };
401
+
402
+ export const AddNoExistingValue: Story = {
403
+ render: () => <AddNoExistingValueExample />,
404
+ };
405
+
406
+ export const UsingRefs: Story = {
407
+ render: () => <RefExample />,
408
+ };
409
+
410
+ export const HandlingOnChange: Story = {
411
+ render: () => <OnChangeExample />,
412
+ };
413
+
414
+ export const WithReactHookForm: Story = {
415
+ render: () => <ReactHookFormExample />,
416
+ };
417
+
418
+ export const PreselectingAnOption: Story = {
419
+ render: () => <PreselectOptionExample />,
420
+ };
@@ -140,6 +140,7 @@ const InnerSelect: FC<FieldProps<BaseProps, HTMLSelectElement, string>> = ({
140
140
  /**
141
141
  * @deprecated
142
142
  */
143
+ // @ts-ignore
143
144
  const Select: FieldType<BaseProps, HTMLSelectElement, string> = createFormFieldWrapper(InnerSelect);
144
145
 
145
146
  export default Select;
@@ -15,32 +15,38 @@
15
15
  */
16
16
 
17
17
  import React, { FC, useRef, useState } from "react";
18
- import { storiesOf } from "@storybook/react";
19
18
  import styled from "styled-components";
19
+ import { useForm } from "react-hook-form";
20
+ import { MemoryRouter } from "react-router-dom";
21
+ import type { Meta, StoryObj } from "@storybook/react";
22
+
20
23
  import Textarea from "./Textarea";
21
24
  import Button from "../buttons/Button";
22
- import { useForm } from "react-hook-form";
23
25
  import { SubmitButton } from "../buttons";
24
- import { MemoryRouter } from "react-router-dom";
25
- import InputField from "./InputField";
26
+
27
+ // --- Helper Components & Styles (preserved from your original story) ---
26
28
 
27
29
  const Spacing = styled.div`
28
30
  padding: 2em;
31
+ max-width: 40rem;
32
+ display: flex;
33
+ flex-direction: column;
34
+ gap: 1rem;
29
35
  `;
30
36
 
31
- const OnChangeTextarea = () => {
37
+ const OnChangeExample: FC = () => {
32
38
  const [value, setValue] = useState("Start typing");
33
39
  return (
34
- <Spacing>
35
- <Textarea value={value} onChange={(v) => setValue(v)} />
40
+ <>
41
+ <Textarea value={value} onChange={setValue} />
36
42
  <hr />
37
43
  <p>{value}</p>
38
- </Spacing>
44
+ </>
39
45
  );
40
46
  };
41
47
 
42
- const OnSubmitTextare = () => {
43
- const [value, setValue] = useState("Use the ctrl/command + Enter to submit the textarea");
48
+ const OnSubmitExample: FC = () => {
49
+ const [value, setValue] = useState("Use ctrl/command + Enter to submit the textarea");
44
50
  const [submitted, setSubmitted] = useState("");
45
51
 
46
52
  const submit = () => {
@@ -49,48 +55,46 @@ const OnSubmitTextare = () => {
49
55
  };
50
56
 
51
57
  return (
52
- <Spacing>
53
- <Textarea value={value} onChange={(v) => setValue(v)} onSubmit={submit} />
58
+ <>
59
+ <Textarea value={value} onChange={setValue} onSubmit={submit} />
54
60
  <hr />
55
- <p>{submitted}</p>
56
- </Spacing>
61
+ <p>
62
+ <strong>Submitted:</strong> {submitted}
63
+ </p>
64
+ </>
57
65
  );
58
66
  };
59
67
 
60
- const OnCancelTextarea = () => {
68
+ const OnCancelExample: FC = () => {
61
69
  const [value, setValue] = useState("Use the escape key to clear the textarea");
62
70
 
63
71
  const cancel = () => {
64
72
  setValue("");
65
73
  };
66
74
 
67
- return (
68
- <Spacing>
69
- <Textarea value={value} onChange={(v) => setValue(v)} onCancel={cancel} />
70
- </Spacing>
71
- );
75
+ return <Textarea value={value} onChange={setValue} onCancel={cancel} />;
72
76
  };
73
77
 
74
- const Ref: FC = () => {
78
+ const RefExample: FC = () => {
75
79
  const ref = useRef<HTMLTextAreaElement>(null);
76
80
  return (
77
81
  <>
78
- <Textarea ref={ref} />
82
+ <Textarea ref={ref} placeholder="I will be focused" />
79
83
  <Button action={() => ref.current?.focus()} color="primary">
80
- Focus InputField
84
+ Focus Textarea
81
85
  </Button>
82
86
  </>
83
87
  );
84
88
  };
85
89
 
86
- const AutoFocusAndRef: FC = () => {
90
+ const AutoFocusAndRefExample: FC = () => {
87
91
  const ref = useRef<HTMLTextAreaElement>(null);
88
92
  return (
89
93
  <>
90
- <Textarea ref={ref} autofocus={true} placeholder="Click the button to focus me" />
94
+ <Textarea ref={ref} autofocus={true} placeholder="I am focused initially" />
91
95
  <Textarea placeholder="Click me to switch focus" />
92
96
  <Button action={() => ref.current?.focus()} color="primary">
93
- Focus First InputField
97
+ Focus First Textarea
94
98
  </Button>
95
99
  </>
96
100
  );
@@ -103,7 +107,7 @@ type Commit = {
103
107
  disabled: string;
104
108
  };
105
109
 
106
- const ReactHookForm: FC = () => {
110
+ const ReactHookFormExample: FC = () => {
107
111
  const {
108
112
  register,
109
113
  handleSubmit,
@@ -142,40 +146,82 @@ const ReactHookForm: FC = () => {
142
146
  <SubmitButton>Submit</SubmitButton>
143
147
  </div>
144
148
  </form>
145
- {stored ? (
149
+ {stored && (
146
150
  <div className="mt-5">
147
151
  <pre>
148
152
  <code>{JSON.stringify(stored, null, 2)}</code>
149
153
  </pre>
150
154
  </div>
151
- ) : null}
155
+ )}
152
156
  </>
153
157
  );
154
158
  };
155
159
 
156
- const LegacyEvents: FC = () => {
157
- const [value, setValue] = useState<string>("");
158
- return (
159
- <>
160
- <Textarea placeholder="Legacy onChange handler" value={value} onChange={(e) => setValue(e)} />
161
- <div className="mt-3">{value}</div>
162
- </>
163
- );
160
+ // --- Storybook Metadata ---
161
+
162
+ const meta: Meta<typeof Textarea> = {
163
+ title: "Forms/Textarea",
164
+ component: Textarea,
165
+ decorators: [(Story) => <Spacing>{Story()}</Spacing>, (Story) => <MemoryRouter>{Story()}</MemoryRouter>],
166
+ argTypes: {
167
+ onChange: { action: "changed" },
168
+ onSubmit: { action: "submitted" },
169
+ onCancel: { action: "cancelled" },
170
+ },
171
+ tags: ["autodocs"],
172
+ };
173
+
174
+ export default meta;
175
+
176
+ // --- Story Definitions ---
177
+
178
+ type Story = StoryObj<typeof meta>;
179
+
180
+ // Simple stories using `args`
181
+ export const Default: Story = {
182
+ args: {
183
+ label: "Default Textarea",
184
+ placeholder: "Type something...",
185
+ },
186
+ };
187
+
188
+ export const AutoFocus: Story = {
189
+ args: {
190
+ label: "Field with AutoFocus",
191
+ autofocus: true,
192
+ },
193
+ };
194
+
195
+ export const DefaultValue: Story = {
196
+ args: {
197
+ label: "Field with Default Value",
198
+ defaultValue: "I am a text area with so much default value its crazy!",
199
+ },
200
+ };
201
+
202
+ // Complex stories using `render` and helper components
203
+ export const HandlingOnChange: Story = {
204
+ render: () => <OnChangeExample />,
164
205
  };
165
206
 
166
- storiesOf("Forms/Textarea", module)
167
- .addDecorator((storyFn) => <MemoryRouter>{storyFn()}</MemoryRouter>)
168
- .add("OnChange", () => <OnChangeTextarea />)
169
- .add("OnSubmit", () => <OnSubmitTextare />)
170
- .add("OnCancel", () => <OnCancelTextarea />)
171
- .add("AutoFocus", () => <Textarea label="Field with AutoFocus" autofocus={true} />)
172
- .add("Default Value", () => (
173
- <Textarea
174
- label="Field with Default Value"
175
- defaultValue={"I am a text area with so much default value its crazy!"}
176
- />
177
- ))
178
- .add("Ref", () => <Ref />)
179
- .add("Legacy Events", () => <LegacyEvents />)
180
- .add("AutoFocusAndRef", () => <AutoFocusAndRef />)
181
- .add("ReactHookForm", () => <ReactHookForm />);
207
+ export const HandlingOnSubmit: Story = {
208
+ render: () => <OnSubmitExample />,
209
+ };
210
+
211
+ export const HandlingOnCancel: Story = {
212
+ render: () => <OnCancelExample />,
213
+ };
214
+
215
+ export const UsingRefs: Story = {
216
+ render: () => <RefExample />,
217
+ };
218
+
219
+ export const WithAutoFocusAndRef: Story = {
220
+ name: "With AutoFocus and Ref",
221
+ render: () => <AutoFocusAndRefExample />,
222
+ };
223
+
224
+ export const WithReactHookForm: Story = {
225
+ name: "With React Hook Form",
226
+ render: () => <ReactHookFormExample />,
227
+ };
@@ -129,6 +129,7 @@ const InnerTextarea: FC<FieldProps<BaseProps, HTMLTextAreaElement, string>> = ({
129
129
  /**
130
130
  * @deprecated
131
131
  */
132
+ // @ts-ignore
132
133
  const Textarea: FieldType<BaseProps, HTMLTextAreaElement, string> = createFormFieldWrapper(InnerTextarea);
133
134
 
134
135
  export default Textarea;
@@ -25,10 +25,12 @@ export { default as Checkbox } from "./Checkbox";
25
25
  export { default as Radio } from "./Radio";
26
26
  export { default as FilterInput } from "./FilterInput";
27
27
  export { default as InputField } from "./InputField";
28
- export { default as Select, SelectItem } from "./Select";
28
+ export { default as Select } from "./Select";
29
29
  export { default as Textarea } from "./Textarea";
30
30
  export { default as PasswordConfirmation } from "./PasswordConfirmation";
31
31
  export { default as LabelWithHelpIcon } from "./LabelWithHelpIcon";
32
32
  export { default as DropDown } from "./DropDown";
33
33
  export { default as FileUpload } from "./FileUpload";
34
34
  export { default as FileInput } from "./FileInput";
35
+
36
+ export type { SelectItem } from "./Select";