@scm-manager/ui-components 4.0.0-REACT18-20250701-125025 → 4.0.0-REACT19

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 (124) hide show
  1. package/package.json +46 -51
  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/LinkPaginator.tsx +9 -6
  16. package/src/Loading.stories.tsx +26 -6
  17. package/src/Logo.stories.tsx +44 -13
  18. package/src/Notification.stories.tsx +111 -23
  19. package/src/OverviewPageActions.tsx +5 -5
  20. package/src/Paginator.test.tsx +115 -94
  21. package/src/PdfViewer.stories.tsx +83 -5
  22. package/src/PreformattedCodeBlock.stories.tsx +97 -26
  23. package/src/ProtectedRoute.tsx +14 -39
  24. package/src/SmallLoadingSpinner.stories.tsx +26 -6
  25. package/src/SyntaxHighlighter.stories.tsx +122 -40
  26. package/src/SyntaxHighlighterRenderer.tsx +7 -7
  27. package/src/Tag.stories.tsx +135 -18
  28. package/src/Tag.tsx +2 -1
  29. package/src/Tooltip.stories.tsx +147 -34
  30. package/src/__snapshots__/storyshots.test.ts.snap +6 -112
  31. package/src/avatar/Avatar.ts +1 -1
  32. package/src/avatar/AvatarImage.tsx +1 -1
  33. package/src/avatar/AvatarWrapper.tsx +2 -2
  34. package/src/buttons/Button.stories.tsx +159 -0
  35. package/src/buttons/Button.tsx +5 -5
  36. package/src/config/ConfigurationBinder.tsx +39 -39
  37. package/src/config/ConfigurationForm.tsx +2 -1
  38. package/src/config/TitledSettings.tsx +4 -1
  39. package/src/forms/AddKeyValueEntryToTableField.stories.tsx +60 -25
  40. package/src/forms/Checkbox.stories.tsx +165 -27
  41. package/src/forms/Checkbox.tsx +1 -0
  42. package/src/forms/DropDown.stories.tsx +81 -35
  43. package/src/forms/FileInput.stories.tsx +85 -10
  44. package/src/forms/InputField.stories.tsx +210 -37
  45. package/src/forms/InputField.tsx +1 -0
  46. package/src/forms/Radio.stories.tsx +181 -34
  47. package/src/forms/Radio.tsx +1 -0
  48. package/src/forms/Select.stories.tsx +266 -46
  49. package/src/forms/Select.tsx +1 -0
  50. package/src/forms/Textarea.stories.tsx +99 -53
  51. package/src/forms/Textarea.tsx +1 -0
  52. package/src/forms/index.ts +3 -1
  53. package/src/index.ts +5 -5
  54. package/src/jest-dom.d.ts +17 -0
  55. package/src/layout/Footer.stories.tsx +114 -22
  56. package/src/layout/FooterSection.tsx +1 -0
  57. package/src/layout/Header.tsx +2 -1
  58. package/src/layout/Page.tsx +1 -3
  59. package/src/layout/PrimaryContentColumn.tsx +2 -2
  60. package/src/layout/SecondaryNavigationColumn.tsx +3 -3
  61. package/src/layout/SubSubtitle.tsx +2 -1
  62. package/src/layout/Subtitle.tsx +2 -1
  63. package/src/layout/Title.tsx +2 -5
  64. package/src/markdown/LazyMarkdownView.tsx +16 -5
  65. package/src/markdown/MarkdownHeadingRenderer.test.ts +9 -1
  66. package/src/markdown/MarkdownHeadingRenderer.tsx +3 -3
  67. package/src/markdown/MarkdownImageRenderer.test.ts +8 -1
  68. package/src/markdown/MarkdownImageRenderer.tsx +2 -1
  69. package/src/markdown/MarkdownLinkRenderer.test.tsx +9 -0
  70. package/src/markdown/MarkdownLinkRenderer.tsx +6 -4
  71. package/src/markdown/MarkdownView.stories.tsx +224 -72
  72. package/src/markdown/markdownExtensions.ts +6 -4
  73. package/src/modals/ConfirmAlert.stories.tsx +133 -44
  74. package/src/modals/ConfirmAlert.tsx +5 -4
  75. package/src/modals/Modal.stories.tsx +461 -252
  76. package/src/modals/Modal.tsx +12 -11
  77. package/src/modals/useRegisterModal.test.tsx +5 -4
  78. package/src/navigation/MenuContext.tsx +2 -2
  79. package/src/navigation/NavLink.tsx +4 -7
  80. package/src/navigation/PrimaryNavigation.tsx +2 -2
  81. package/src/navigation/PrimaryNavigationLink.tsx +6 -5
  82. package/src/navigation/RoutingProps.ts +1 -3
  83. package/src/navigation/SecondaryNavigation.stories.tsx +157 -45
  84. package/src/navigation/SecondaryNavigation.tsx +17 -16
  85. package/src/navigation/SecondaryNavigationItem.tsx +2 -1
  86. package/src/navigation/SubNavigation.tsx +4 -8
  87. package/src/navigation/useActiveMatch.ts +20 -22
  88. package/src/navigation/useNavigationLock.ts +1 -0
  89. package/src/popover/Popover.stories.tsx +111 -41
  90. package/src/popover/Popover.tsx +2 -5
  91. package/src/repos/Diff.stories.tsx +418 -223
  92. package/src/repos/Diff.tsx +1 -5
  93. package/src/repos/HunkExpandDivider.tsx +2 -2
  94. package/src/repos/LoadingDiff.tsx +11 -6
  95. package/src/repos/RepositoryEntry.stories.tsx +217 -53
  96. package/src/repos/RepositoryFlag.tsx +2 -1
  97. package/src/repos/TokenizedDiffView.tsx +4 -2
  98. package/src/repos/annotate/Annotate.stories.tsx +225 -111
  99. package/src/repos/annotate/AnnotateLine.tsx +2 -1
  100. package/src/repos/changesets/ChangesetAuthor.tsx +2 -2
  101. package/src/repos/changesets/ChangesetButtonGroup.test.tsx +9 -5
  102. package/src/repos/changesets/ChangesetDiff.test.ts +10 -2
  103. package/src/repos/changesets/Changesets.stories.tsx +388 -197
  104. package/src/repos/changesets/ContributorRow.tsx +2 -2
  105. package/src/repos/changesets/SignatureIcon.tsx +1 -0
  106. package/src/repos/diff/DiffFileTree.tsx +1 -1
  107. package/src/repos/diff/styledElements.tsx +4 -3
  108. package/src/repos/index.ts +15 -15
  109. package/src/search/Hit.tsx +3 -2
  110. package/src/search/TextHitField.stories.tsx +131 -43
  111. package/src/search/TextHitField.tsx +3 -2
  112. package/src/search/index.ts +1 -1
  113. package/src/storyshots.test.ts +66 -60
  114. package/src/table/Table.stories.tsx +146 -48
  115. package/src/table/Table.tsx +7 -8
  116. package/src/table/TextColumn.tsx +0 -9
  117. package/src/toast/Toast.tsx +2 -1
  118. package/src/toast/ToastArea.tsx +2 -2
  119. package/src/toast/ToastButton.tsx +2 -1
  120. package/src/toast/ToastButtons.tsx +4 -2
  121. package/src/toast/ToastNotification.tsx +2 -1
  122. package/src/toast/index.stories.tsx +144 -39
  123. package/src/toast/index.ts +1 -1
  124. package/src/buttons/index.stories.tsx +0 -85
@@ -14,24 +14,354 @@
14
14
  * along with this program. If not, see https://www.gnu.org/licenses/.
15
15
  */
16
16
 
17
- import { storiesOf } from "@storybook/react";
18
- import { MemoryRouter } from "react-router-dom";
19
- import React, { FC, useRef, useState } from "react";
17
+ // import { storiesOf } from "@storybook/react";
18
+ // import { MemoryRouter } from "react-router-dom";
19
+ // import React, { FC, ReactNode, useRef, useState } from "react";
20
+ // import Modal from "./Modal";
21
+ // import Checkbox from "../forms/Checkbox";
22
+ // import styled from "styled-components";
23
+ // import ExternalLink from "../navigation/ExternalLink";
24
+ // import { InputField, Radio, Textarea } from "../forms";
25
+ // import { Button, ButtonGroup } from "../buttons";
26
+ // import Notification from "../Notification";
27
+ // import { Autocomplete } from "../index";
28
+ // import { SelectValue } from "@scm-manager/ui-types";
29
+ // import ActiveModalCountContext from "./activeModalCountContext";
30
+ //
31
+ // const TopAndBottomMargin = styled.div`
32
+ // margin: 0.75rem 0; // only for aesthetic reasons
33
+ // `;
34
+ //
35
+ // const RadioList = styled.div`
36
+ // display: flex;
37
+ // flex-direction: column;
38
+ // > label:not(:last-child) {
39
+ // margin-bottom: 0.6em;
40
+ // }
41
+ // `;
42
+ //
43
+ // const text = `Mind-paralyzing change needed improbability vortex machine sorts sought same theory upending job just allows
44
+ // hostess’s really oblong Infinite Improbability thing into the starship against which behavior accordance.with
45
+ // Kakrafoon humanoid undergarment ship powered by GPP-guided bowl of petunias nothing was frequently away incredibly
46
+ // ordinary mob.`;
47
+ //
48
+ // const doNothing = () => {
49
+ // // Do nothing
50
+ // };
51
+ // const withFormElementsBody = (
52
+ // <>
53
+ // <RadioList>
54
+ // <Radio label="One" checked={true} helpText="The first one" />
55
+ // <Radio label="Two" checked={false} helpText="The second one" />
56
+ // </RadioList>
57
+ // <hr />
58
+ // <p>{text}</p>
59
+ // <hr />
60
+ // <Textarea label="Text" onChange={doNothing} />
61
+ // </>
62
+ // );
63
+ // const withFormElementsFooter = (
64
+ // <ButtonGroup>
65
+ // <Button label="One" />
66
+ // <Button label="Two" />
67
+ // </ButtonGroup>
68
+ // );
69
+ //
70
+ // const loadSuggestions: (p: string) => Promise<SelectValue[]> = () =>
71
+ // new Promise((resolve) => {
72
+ // setTimeout(() => {
73
+ // resolve([
74
+ // { value: { id: "trillian", displayName: "Tricia McMillan" }, label: "Tricia McMillan" },
75
+ // { value: { id: "zaphod", displayName: "Zaphod Beeblebrox" }, label: "Zaphod Beeblebrox" },
76
+ // { value: { id: "ford", displayName: "Ford Prefect" }, label: "Ford Prefect" },
77
+ // { value: { id: "dent", displayName: "Arthur Dent" }, label: "Arthur Dent" },
78
+ // { value: { id: "marvin", displayName: "Marvin" }, label: "Marvin the Paranoid Android " },
79
+ // ]);
80
+ // });
81
+ // });
82
+ //
83
+ // storiesOf("Modal/Modal", module)
84
+ // .addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
85
+ // .addDecorator((story) => (
86
+ // <ActiveModalCountContext.Provider value={{ value: 0, increment: doNothing, decrement: doNothing }}>
87
+ // {story()}
88
+ // </ActiveModalCountContext.Provider>
89
+ // ))
90
+ // .add("Default", () => (
91
+ // <NonCloseableModal>
92
+ // <p>{text}</p>
93
+ // </NonCloseableModal>
94
+ // ))
95
+ // .add("Closeable", () => (
96
+ // <CloseableModal>
97
+ // <p>{text}</p>
98
+ // </CloseableModal>
99
+ // ))
100
+ // .add("Nested", () => (
101
+ // <NestedModal>
102
+ // <p>{text}</p>
103
+ // </NestedModal>
104
+ // ))
105
+ // .add("With form elements", () => (
106
+ // <Modal
107
+ // body={withFormElementsBody}
108
+ // closeFunction={doNothing}
109
+ // active={true}
110
+ // title={"Hitchhiker Modal"}
111
+ // footer={withFormElementsFooter}
112
+ // />
113
+ // ))
114
+ // .add("With initial input field focus", () => {
115
+ // const ref = useRef<HTMLInputElement | null>(null);
116
+ // return (
117
+ // <Modal
118
+ // closeFunction={doNothing}
119
+ // active={true}
120
+ // title={"Hitchhiker Modal"}
121
+ // footer={withFormElementsFooter}
122
+ // initialFocusRef={ref}
123
+ // >
124
+ // <InputField ref={ref} />
125
+ // </Modal>
126
+ // );
127
+ // })
128
+ // .add("With initial button focus", () => <RefModal />)
129
+ // .add("With long tooltips", () => {
130
+ // return (
131
+ // <NonCloseableModal>
132
+ // <Notification type="info">
133
+ // This story exists because we had a problem, that long tooltips causes a horizontal scrollbar on the modal.
134
+ // </Notification>
135
+ // <hr />
136
+ // <p>The following elements will have a very long help text, which has triggered the scrollbar in the past.</p>
137
+ // <hr />
138
+ // <TopAndBottomMargin>
139
+ // <Checkbox label="Checkbox" checked={true} helpText={text} />
140
+ // </TopAndBottomMargin>
141
+ // <hr />
142
+ // <TopAndBottomMargin>
143
+ // <Radio label="Radio button" checked={false} helpText={text} />
144
+ // </TopAndBottomMargin>
145
+ // <hr />
146
+ // <TopAndBottomMargin>
147
+ // <InputField onChange={doNothing} label="Input" helpText={text} />
148
+ // </TopAndBottomMargin>
149
+ // <hr />
150
+ // <TopAndBottomMargin>
151
+ // <Textarea onChange={doNothing} label="Textarea" helpText={text} />
152
+ // </TopAndBottomMargin>
153
+ // <hr />
154
+ // <p>If this modal has no horizontal scrollbar the issue is fixed</p>
155
+ // </NonCloseableModal>
156
+ // );
157
+ // })
158
+ // .add("Long content", () => (
159
+ // <NonCloseableModal>
160
+ // <h1 className="title">Marvin</h1>
161
+ // <h2 className="subtitle">The Paranoid Android</h2>
162
+ // <hr />
163
+ // <Notification type="info">
164
+ // The following content comes from the awesome{" "}
165
+ // <ExternalLink to="https://hitchhikers.fandom.com/wiki/Main_Page">Hitchhikers Wiki</ExternalLink>
166
+ // </Notification>
167
+ // <hr />
168
+ // <div className="has-text-centered">
169
+ // <img
170
+ // alt="Marvin"
171
+ // src="https://vignette.wikia.nocookie.net/hitchhikers/images/a/a4/Marvin.jpg/revision/latest/scale-to-width-down/150?cb=20100530114055"
172
+ // />
173
+ // </div>
174
+ // <hr />
175
+ // <p className="content">
176
+ // Marvin, more fully known as Marvin the Paranoid Android, is an incredibly brilliant but overwhelmingly depressed
177
+ // robot manufactured by the Sirius Cybernetics Corporation and unwilling servant to the crew of the Heart of Gold.
178
+ // </p>
179
+ // <hr />
180
+ // <div className="content">
181
+ // <h4>Physical Appearance</h4>
182
+ // <p>
183
+ // In the novels, Marvin is described thusly: "...though it was beautifully constructed and polished it looked
184
+ // somehow as if the various parts of its more or less humanoid body didn't quite fit properly. In fact, they fit
185
+ // perfectly well, but something in its bearing suggested that they might have fitted better."
186
+ // </p>
187
+ // <p>
188
+ // On the radio show, there's no physical description of Marvin, though his voice is digitally altered to sound
189
+ // more robotic, and any scene that focuses on him is accompanied by sounds of mechanical clanking and hissing.
190
+ // </p>
191
+ // <p>
192
+ // In the TV series, Marvin is built in the style of a 1950's robot similar to Robbie the Robot from Forbidden
193
+ // Planet or Twiki from Buck Rogers. His body is blocky and angular, with a pair of clamp-claw hands, shuffling
194
+ // feet and a squarish head with a dour face.
195
+ // </p>
196
+ // <p>
197
+ // In the movie, Marvin is a short, stout robot built of smooth, white plastic. His arms are much longer than his
198
+ // legs, and his head is a massive sphere with only a pair of triangle eyes for a face. His large head and
199
+ // simian-like proportions give Marvin a perpetual slouch, adding to his melancholy personality. At the start of
200
+ // the film his eyes glow, but at the end he is shot but unharmed, leaving a hole in his head and dimming his
201
+ // eyes. This is probably the most depressing and unacceptable manifestation of Marvin ever conceived, and thus
202
+ // paradoxically the most accurate.
203
+ // </p>
204
+ // </div>
205
+ // <hr />
206
+ // <div className="content">
207
+ // <h4>Personality</h4>
208
+ // <p>
209
+ // Marvin the robot has a prototype version of the Genuine People Personality (GPP) software from SCC, allowing
210
+ // him sentience and the ability to feel emotions and develop a personality. He's also incredibly smart, having a
211
+ // "brain the size of a planet" capable of computing extremely complex mathematics, as well as solving difficult
212
+ // problems and operating high-tech devices.
213
+ // </p>
214
+ // <p>
215
+ // However, despite being so smart, Marvin is typically made to perform menial tasks and labour such as escorting
216
+ // people, opening doors, picking up pieces of paper, and other tasks well beneath his skills. Even extremely
217
+ // hard tasks, such as computing for the vast Krikkit robot army, are trivial for Marvin. All this leaves him
218
+ // extremely bored, frustrated, and overwhelmingly depressed. Because of this, all modern GPP-capable machines,
219
+ // such as Eddie the computer and the Heart of Gold's automatic doors, are programmed to be extremely cheerful
220
+ // and happy, much to Marvin's disgust.
221
+ // </p>
222
+ // <p>
223
+ // Marvin hates everyone and everything he comes into contact with, having no respect for anybody and will
224
+ // criticise and insult others at any opportunity, or otherwise rant and complain for hours on end about his own
225
+ // problems, such as the terrible pain he suffers in all the diodes down his left side. His contempt for everyone
226
+ // is often justified, as almost every person he comes across, even those who consider him a friend, (such as
227
+ // Arthur and Trillian, who treat him more kindly than Ford and Zaphod) treat Marvin as an expendable servant,
228
+ // even sending him to his death more than once (such as when Zaphod ordered Marvin to fight the gigantic,
229
+ // heavy-duty Frogstar Scout Robot Class D so he could escape). Being a robot, he still does what he's told (he
230
+ // won't enjoy it, nor will he let you forget it, but he'll do it anyway), though he'd much rather sulk in a
231
+ // corner by himself.
232
+ // </p>
233
+ // <p>
234
+ // Several times in the series Marvin ends up alone and isolated for extremely long periods of time, sometimes
235
+ // spanning millions of years, either by sheer bad luck (such as the explosion that propelled everyone but Marvin
236
+ // to Milliways in the far-off future) or because his unpleasantly depressing personality drives them away or, in
237
+ // more than one case, makes them commit suicide. In his spare time (which he has a lot of), Marvin will attempt
238
+ // to occupy himself by composing songs and writing poetry. Of course, none of them are particularly cheerful, or
239
+ // even that good.
240
+ // </p>
241
+ // </div>
242
+ // </NonCloseableModal>
243
+ // ))
244
+ // .add("With overflow", () => {
245
+ // return (
246
+ // <NonCloseableModal overflowVisible={true}>
247
+ // <h1 className="title">Please Select</h1>
248
+ // <Autocomplete
249
+ // valueSelected={() => {
250
+ // // nothing to do
251
+ // }}
252
+ // loadSuggestions={loadSuggestions}
253
+ // />
254
+ // </NonCloseableModal>
255
+ // );
256
+ // })
257
+ // .add("With overflow and footer", () => {
258
+ // return (
259
+ // <NonCloseableModal overflowVisible={true} footer={withFormElementsFooter}>
260
+ // <h1 className="title">Please Select</h1>
261
+ // <Autocomplete
262
+ // valueSelected={() => {
263
+ // // nothing to do
264
+ // }}
265
+ // loadSuggestions={loadSuggestions}
266
+ // />
267
+ // </NonCloseableModal>
268
+ // );
269
+ // });
270
+ //
271
+ // type NonCloseableModalProps = { overflowVisible?: boolean; footer?: any; children?: ReactNode };
272
+ //
273
+ // const NonCloseableModal: FC<NonCloseableModalProps> = ({ overflowVisible, footer, children }) => {
274
+ // return (
275
+ // <Modal
276
+ // body={children}
277
+ // closeFunction={doNothing}
278
+ // active={true}
279
+ // title={"Hitchhiker Modal"}
280
+ // overflowVisible={overflowVisible}
281
+ // footer={footer}
282
+ // />
283
+ // );
284
+ // };
285
+ //
286
+ // const CloseableModal: FC<{ children: ReactNode }> = ({ children }) => {
287
+ // const [show, setShow] = useState(true);
288
+ //
289
+ // const toggleModal = () => {
290
+ // setShow(!show);
291
+ // };
292
+ //
293
+ // return <Modal body={children} closeFunction={toggleModal} active={show} title={"Hitchhiker Modal"} />;
294
+ // };
295
+ //
296
+ // const NestedModal: FC<{ children: ReactNode }> = ({ children }) => {
297
+ // const [showOuter, setShowOuter] = useState(true);
298
+ // const [showInner, setShowInner] = useState(false);
299
+ // const outerBody = (
300
+ // <>
301
+ // {showInner && (
302
+ // <Modal
303
+ // body={children}
304
+ // closeFunction={() => setShowInner(!showInner)}
305
+ // active={showInner}
306
+ // title="Inner Hitchhiker Modal"
307
+ // />
308
+ // )}
309
+ //
310
+ // <Button title="Open inner modal" className="button" action={() => setShowInner(true)}>
311
+ // Open inner modal
312
+ // </Button>
313
+ // </>
314
+ // );
315
+ //
316
+ // return (
317
+ // <>
318
+ // {showOuter && (
319
+ // <Modal
320
+ // body={outerBody}
321
+ // closeFunction={() => setShowOuter(!showOuter)}
322
+ // active={showOuter}
323
+ // title="Outer Hitchhiker Modal"
324
+ // size="M"
325
+ // />
326
+ // )}
327
+ // </>
328
+ // );
329
+ // };
330
+ //
331
+ // const RefModal = () => {
332
+ // const ref = useRef<HTMLButtonElement>(null);
333
+ // return (
334
+ // <Modal
335
+ // closeFunction={doNothing}
336
+ // active={true}
337
+ // title={"Hitchhiker Modal"}
338
+ // footer={withFormElementsFooter}
339
+ // initialFocusRef={ref}
340
+ // >
341
+ // <button ref={ref}>Hello</button>
342
+ // </Modal>
343
+ // );
344
+ // };
345
+
346
+ import React, { FC, ReactNode, useRef, useState } from "react";
347
+ import styled from "styled-components";
348
+ import type { Meta, StoryObj } from "@storybook/react";
349
+ import { SelectValue } from "@scm-manager/ui-types";
350
+
20
351
  import Modal from "./Modal";
21
352
  import Checkbox from "../forms/Checkbox";
22
- import styled from "styled-components";
23
353
  import ExternalLink from "../navigation/ExternalLink";
24
354
  import { InputField, Radio, Textarea } from "../forms";
25
355
  import { Button, ButtonGroup } from "../buttons";
26
356
  import Notification from "../Notification";
27
357
  import { Autocomplete } from "../index";
28
- import { SelectValue } from "@scm-manager/ui-types";
29
358
  import ActiveModalCountContext from "./activeModalCountContext";
30
359
 
360
+ // --- Helfer-Komponenten und Mock-Daten (aus der Original-Story übernommen) ---
361
+
31
362
  const TopAndBottomMargin = styled.div`
32
- margin: 0.75rem 0; // only for aesthetic reasons
363
+ margin: 0.75rem 0;
33
364
  `;
34
-
35
365
  const RadioList = styled.div`
36
366
  display: flex;
37
367
  flex-direction: column;
@@ -40,14 +370,9 @@ const RadioList = styled.div`
40
370
  }
41
371
  `;
42
372
 
43
- const text = `Mind-paralyzing change needed improbability vortex machine sorts sought same theory upending job just allows
44
- hostess’s really oblong Infinite Improbability thing into the starship against which behavior accordance.with
45
- Kakrafoon humanoid undergarment ship powered by GPP-guided bowl of petunias nothing was frequently away incredibly
46
- ordinary mob.`;
373
+ const text = `Mind-paralyzing change needed improbability vortex machine sorts sought same theory upending job just allows hostess’s really oblong Infinite Improbability thing into the starship against which behavior accordance.with Kakrafoon humanoid undergarment ship powered by GPP-guided bowl of petunias nothing was frequently away incredibly ordinary mob.`;
374
+ const doNothing = () => {};
47
375
 
48
- const doNothing = () => {
49
- // Do nothing
50
- };
51
376
  const withFormElementsBody = (
52
377
  <>
53
378
  <RadioList>
@@ -67,233 +392,27 @@ const withFormElementsFooter = (
67
392
  </ButtonGroup>
68
393
  );
69
394
 
70
- const loadSuggestions: (p: string) => Promise<SelectValue[]> = () =>
71
- new Promise((resolve) => {
72
- setTimeout(() => {
73
- resolve([
74
- { value: { id: "trillian", displayName: "Tricia McMillan" }, label: "Tricia McMillan" },
75
- { value: { id: "zaphod", displayName: "Zaphod Beeblebrox" }, label: "Zaphod Beeblebrox" },
76
- { value: { id: "ford", displayName: "Ford Prefect" }, label: "Ford Prefect" },
77
- { value: { id: "dent", displayName: "Arthur Dent" }, label: "Arthur Dent" },
78
- { value: { id: "marvin", displayName: "Marvin" }, label: "Marvin the Paranoid Android " },
79
- ]);
80
- });
81
- });
82
-
83
- storiesOf("Modal/Modal", module)
84
- .addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
85
- .addDecorator((story) => (
86
- <ActiveModalCountContext.Provider value={{ value: 0, increment: doNothing, decrement: doNothing }}>
87
- {story()}
88
- </ActiveModalCountContext.Provider>
89
- ))
90
- .add("Default", () => (
91
- <NonCloseableModal>
92
- <p>{text}</p>
93
- </NonCloseableModal>
94
- ))
95
- .add("Closeable", () => (
96
- <CloseableModal>
97
- <p>{text}</p>
98
- </CloseableModal>
99
- ))
100
- .add("Nested", () => (
101
- <NestedModal>
102
- <p>{text}</p>
103
- </NestedModal>
104
- ))
105
- .add("With form elements", () => (
106
- <Modal
107
- body={withFormElementsBody}
108
- closeFunction={doNothing}
109
- active={true}
110
- title={"Hitchhiker Modal"}
111
- footer={withFormElementsFooter}
112
- />
113
- ))
114
- .add("With initial input field focus", () => {
115
- const ref = useRef<HTMLInputElement | null>(null);
116
- return (
117
- <Modal
118
- closeFunction={doNothing}
119
- active={true}
120
- title={"Hitchhiker Modal"}
121
- footer={withFormElementsFooter}
122
- initialFocusRef={ref}
123
- >
124
- <InputField ref={ref} />
125
- </Modal>
126
- );
127
- })
128
- .add("With initial button focus", () => <RefModal />)
129
- .add("With long tooltips", () => {
130
- return (
131
- <NonCloseableModal>
132
- <Notification type="info">
133
- This story exists because we had a problem, that long tooltips causes a horizontal scrollbar on the modal.
134
- </Notification>
135
- <hr />
136
- <p>The following elements will have a very long help text, which has triggered the scrollbar in the past.</p>
137
- <hr />
138
- <TopAndBottomMargin>
139
- <Checkbox label="Checkbox" checked={true} helpText={text} />
140
- </TopAndBottomMargin>
141
- <hr />
142
- <TopAndBottomMargin>
143
- <Radio label="Radio button" checked={false} helpText={text} />
144
- </TopAndBottomMargin>
145
- <hr />
146
- <TopAndBottomMargin>
147
- <InputField onChange={doNothing} label="Input" helpText={text} />
148
- </TopAndBottomMargin>
149
- <hr />
150
- <TopAndBottomMargin>
151
- <Textarea onChange={doNothing} label="Textarea" helpText={text} />
152
- </TopAndBottomMargin>
153
- <hr />
154
- <p>If this modal has no horizontal scrollbar the issue is fixed</p>
155
- </NonCloseableModal>
156
- );
157
- })
158
- .add("Long content", () => (
159
- <NonCloseableModal>
160
- <h1 className="title">Marvin</h1>
161
- <h2 className="subtitle">The Paranoid Android</h2>
162
- <hr />
163
- <Notification type="info">
164
- The following content comes from the awesome{" "}
165
- <ExternalLink to="https://hitchhikers.fandom.com/wiki/Main_Page">Hitchhikers Wiki</ExternalLink>
166
- </Notification>
167
- <hr />
168
- <div className="has-text-centered">
169
- <img
170
- alt="Marvin"
171
- src="https://vignette.wikia.nocookie.net/hitchhikers/images/a/a4/Marvin.jpg/revision/latest/scale-to-width-down/150?cb=20100530114055"
172
- />
173
- </div>
174
- <hr />
175
- <p className="content">
176
- Marvin, more fully known as Marvin the Paranoid Android, is an incredibly brilliant but overwhelmingly depressed
177
- robot manufactured by the Sirius Cybernetics Corporation and unwilling servant to the crew of the Heart of Gold.
178
- </p>
179
- <hr />
180
- <div className="content">
181
- <h4>Physical Appearance</h4>
182
- <p>
183
- In the novels, Marvin is described thusly: "...though it was beautifully constructed and polished it looked
184
- somehow as if the various parts of its more or less humanoid body didn't quite fit properly. In fact, they fit
185
- perfectly well, but something in its bearing suggested that they might have fitted better."
186
- </p>
187
- <p>
188
- On the radio show, there's no physical description of Marvin, though his voice is digitally altered to sound
189
- more robotic, and any scene that focuses on him is accompanied by sounds of mechanical clanking and hissing.
190
- </p>
191
- <p>
192
- In the TV series, Marvin is built in the style of a 1950's robot similar to Robbie the Robot from Forbidden
193
- Planet or Twiki from Buck Rogers. His body is blocky and angular, with a pair of clamp-claw hands, shuffling
194
- feet and a squarish head with a dour face.
195
- </p>
196
- <p>
197
- In the movie, Marvin is a short, stout robot built of smooth, white plastic. His arms are much longer than his
198
- legs, and his head is a massive sphere with only a pair of triangle eyes for a face. His large head and
199
- simian-like proportions give Marvin a perpetual slouch, adding to his melancholy personality. At the start of
200
- the film his eyes glow, but at the end he is shot but unharmed, leaving a hole in his head and dimming his
201
- eyes. This is probably the most depressing and unacceptable manifestation of Marvin ever conceived, and thus
202
- paradoxically the most accurate.
203
- </p>
204
- </div>
205
- <hr />
206
- <div className="content">
207
- <h4>Personality</h4>
208
- <p>
209
- Marvin the robot has a prototype version of the Genuine People Personality (GPP) software from SCC, allowing
210
- him sentience and the ability to feel emotions and develop a personality. He's also incredibly smart, having a
211
- "brain the size of a planet" capable of computing extremely complex mathematics, as well as solving difficult
212
- problems and operating high-tech devices.
213
- </p>
214
- <p>
215
- However, despite being so smart, Marvin is typically made to perform menial tasks and labour such as escorting
216
- people, opening doors, picking up pieces of paper, and other tasks well beneath his skills. Even extremely
217
- hard tasks, such as computing for the vast Krikkit robot army, are trivial for Marvin. All this leaves him
218
- extremely bored, frustrated, and overwhelmingly depressed. Because of this, all modern GPP-capable machines,
219
- such as Eddie the computer and the Heart of Gold's automatic doors, are programmed to be extremely cheerful
220
- and happy, much to Marvin's disgust.
221
- </p>
222
- <p>
223
- Marvin hates everyone and everything he comes into contact with, having no respect for anybody and will
224
- criticise and insult others at any opportunity, or otherwise rant and complain for hours on end about his own
225
- problems, such as the terrible pain he suffers in all the diodes down his left side. His contempt for everyone
226
- is often justified, as almost every person he comes across, even those who consider him a friend, (such as
227
- Arthur and Trillian, who treat him more kindly than Ford and Zaphod) treat Marvin as an expendable servant,
228
- even sending him to his death more than once (such as when Zaphod ordered Marvin to fight the gigantic,
229
- heavy-duty Frogstar Scout Robot Class D so he could escape). Being a robot, he still does what he's told (he
230
- won't enjoy it, nor will he let you forget it, but he'll do it anyway), though he'd much rather sulk in a
231
- corner by himself.
232
- </p>
233
- <p>
234
- Several times in the series Marvin ends up alone and isolated for extremely long periods of time, sometimes
235
- spanning millions of years, either by sheer bad luck (such as the explosion that propelled everyone but Marvin
236
- to Milliways in the far-off future) or because his unpleasantly depressing personality drives them away or, in
237
- more than one case, makes them commit suicide. In his spare time (which he has a lot of), Marvin will attempt
238
- to occupy himself by composing songs and writing poetry. Of course, none of them are particularly cheerful, or
239
- even that good.
240
- </p>
241
- </div>
242
- </NonCloseableModal>
243
- ))
244
- .add("With overflow", () => {
245
- return (
246
- <NonCloseableModal overflowVisible={true}>
247
- <h1 className="title">Please Select</h1>
248
- <Autocomplete
249
- valueSelected={() => {
250
- // nothing to do
251
- }}
252
- loadSuggestions={loadSuggestions}
253
- />
254
- </NonCloseableModal>
255
- );
256
- })
257
- .add("With overflow and footer", () => {
258
- return (
259
- <NonCloseableModal overflowVisible={true} footer={withFormElementsFooter}>
260
- <h1 className="title">Please Select</h1>
261
- <Autocomplete
262
- valueSelected={() => {
263
- // nothing to do
264
- }}
265
- loadSuggestions={loadSuggestions}
266
- />
267
- </NonCloseableModal>
268
- );
269
- });
270
-
271
- type NonCloseableModalProps = { overflowVisible?: boolean; footer?: any };
272
-
273
- const NonCloseableModal: FC<NonCloseableModalProps> = ({ overflowVisible, footer, children }) => {
274
- return (
275
- <Modal
276
- body={children}
277
- closeFunction={doNothing}
278
- active={true}
279
- title={"Hitchhiker Modal"}
280
- overflowVisible={overflowVisible}
281
- footer={footer}
282
- />
395
+ const loadSuggestions = (p: string): Promise<SelectValue[]> =>
396
+ new Promise((resolve) =>
397
+ setTimeout(
398
+ () =>
399
+ resolve([
400
+ { value: { id: "trillian", displayName: "Tricia McMillan" }, label: "Tricia McMillan" },
401
+ { value: { id: "zaphod", displayName: "Zaphod Beeblebrox" }, label: "Zaphod Beeblebrox" },
402
+ ]),
403
+ 500,
404
+ ),
283
405
  );
284
- };
285
406
 
286
- const CloseableModal: FC = ({ children }) => {
407
+ // Story-spezifische Helfer-Komponenten
408
+ const NonCloseableModal: FC<any> = ({ children, ...rest }) => (
409
+ <Modal body={children} closeFunction={doNothing} active={true} title="Hitchhiker Modal" {...rest} />
410
+ );
411
+ const CloseableModal: FC<{ children: ReactNode }> = ({ children }) => {
287
412
  const [show, setShow] = useState(true);
288
-
289
- const toggleModal = () => {
290
- setShow(!show);
291
- };
292
-
293
- return <Modal body={children} closeFunction={toggleModal} active={show} title={"Hitchhiker Modal"} />;
413
+ return <Modal body={children} closeFunction={() => setShow(!show)} active={show} title="Hitchhiker Modal" />;
294
414
  };
295
-
296
- const NestedModal: FC = ({ children }) => {
415
+ const NestedModal: FC<{ children: ReactNode }> = ({ children }) => {
297
416
  const [showOuter, setShowOuter] = useState(true);
298
417
  const [showInner, setShowInner] = useState(false);
299
418
  const outerBody = (
@@ -306,35 +425,27 @@ const NestedModal: FC = ({ children }) => {
306
425
  title="Inner Hitchhiker Modal"
307
426
  />
308
427
  )}
309
-
310
428
  <Button title="Open inner modal" className="button" action={() => setShowInner(true)}>
311
429
  Open inner modal
312
430
  </Button>
313
431
  </>
314
432
  );
315
-
316
433
  return (
317
- <>
318
- {showOuter && (
319
- <Modal
320
- body={outerBody}
321
- closeFunction={() => setShowOuter(!showOuter)}
322
- active={showOuter}
323
- title="Outer Hitchhiker Modal"
324
- size="M"
325
- />
326
- )}
327
- </>
434
+ <Modal
435
+ body={outerBody}
436
+ closeFunction={() => setShowOuter(!showOuter)}
437
+ active={showOuter}
438
+ title="Outer Hitchhiker Modal"
439
+ />
328
440
  );
329
441
  };
330
-
331
442
  const RefModal = () => {
332
443
  const ref = useRef<HTMLButtonElement>(null);
333
444
  return (
334
445
  <Modal
335
446
  closeFunction={doNothing}
336
447
  active={true}
337
- title={"Hitchhiker Modal"}
448
+ title="Hitchhiker Modal"
338
449
  footer={withFormElementsFooter}
339
450
  initialFocusRef={ref}
340
451
  >
@@ -342,3 +453,101 @@ const RefModal = () => {
342
453
  </Modal>
343
454
  );
344
455
  };
456
+
457
+ // --- Storybook Metadaten ---
458
+
459
+ const meta: Meta<typeof Modal> = {
460
+ title: "Modal/Modal",
461
+ component: Modal,
462
+ decorators: [
463
+ (Story) => (
464
+ <ActiveModalCountContext.Provider value={{ value: 0, increment: doNothing, decrement: doNothing }}>
465
+ {/* Storybook rendert Modals im Canvas; für reale Nutzung wird ein Portal benötigt. */}
466
+ <Story />
467
+ <div id="modalRoot" />
468
+ </ActiveModalCountContext.Provider>
469
+ ),
470
+ ],
471
+ tags: ["autodocs"],
472
+ };
473
+
474
+ export default meta;
475
+
476
+ // --- Story-Definitionen ---
477
+
478
+ type Story = StoryObj<typeof meta>;
479
+
480
+ // Alle Stories verwenden eine `render`-Funktion, um ihre komplexe Struktur abzubilden.
481
+ export const Default: Story = {
482
+ render: () => <NonCloseableModal>{text}</NonCloseableModal>,
483
+ };
484
+
485
+ export const Closeable: Story = {
486
+ render: () => <CloseableModal>{text}</CloseableModal>,
487
+ };
488
+
489
+ export const Nested: Story = {
490
+ render: () => <NestedModal>{text}</NestedModal>,
491
+ };
492
+
493
+ export const WithFormElements: Story = {
494
+ name: "With Form Elements",
495
+ render: () => (
496
+ <Modal
497
+ body={withFormElementsBody}
498
+ closeFunction={doNothing}
499
+ active={true}
500
+ title="Hitchhiker Modal"
501
+ footer={withFormElementsFooter}
502
+ />
503
+ ),
504
+ };
505
+
506
+ export const WithInitialFocus: Story = {
507
+ name: "With Initial Focus",
508
+ render: () => {
509
+ const ref = useRef<HTMLInputElement | null>(null);
510
+ return (
511
+ <Modal
512
+ closeFunction={doNothing}
513
+ active={true}
514
+ title="Hitchhiker Modal"
515
+ footer={withFormElementsFooter}
516
+ initialFocusRef={ref}
517
+ >
518
+ <InputField ref={ref} />
519
+ </Modal>
520
+ );
521
+ },
522
+ };
523
+
524
+ export const WithLongTooltips: Story = {
525
+ name: "With Long Tooltips",
526
+ render: () => (
527
+ <NonCloseableModal>
528
+ <Notification type="info">
529
+ Diese Story testet, ob lange Tooltips einen horizontalen Scrollbalken verursachen.
530
+ </Notification>
531
+ <hr />
532
+ <TopAndBottomMargin>
533
+ <Checkbox label="Checkbox" checked={true} helpText={text} />
534
+ </TopAndBottomMargin>
535
+ <TopAndBottomMargin>
536
+ <Radio label="Radio button" checked={false} helpText={text} />
537
+ </TopAndBottomMargin>
538
+ <TopAndBottomMargin>
539
+ <InputField onChange={doNothing} label="Input" helpText={text} />
540
+ </TopAndBottomMargin>
541
+ </NonCloseableModal>
542
+ ),
543
+ };
544
+
545
+ export const WithOverflow: Story = {
546
+ name: "With Overflow (e.g., Autocomplete)",
547
+ render: () => (
548
+ <NonCloseableModal overflowVisible={true} footer={withFormElementsFooter}>
549
+ <h1 className="title">Please Select</h1>
550
+ <Autocomplete valueSelected={() => {}} loadSuggestions={loadSuggestions} />
551
+ </NonCloseableModal>
552
+ ),
553
+ };