@scm-manager/ui-components 4.0.0-REACT18-20250701-125025 → 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 (124) 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/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,10 +14,136 @@
14
14
  * along with this program. If not, see https://www.gnu.org/licenses/.
15
15
  */
16
16
 
17
+ // import React, { FC } from "react";
18
+ // import { storiesOf } from "@storybook/react";
19
+ // import MarkdownView from "./MarkdownView";
20
+ // import styled from "styled-components";
21
+ //
22
+ // import TestPage from "../__resources__/test-page.md";
23
+ // import MarkdownWithoutLang from "../__resources__/markdown-without-lang.md";
24
+ // import MarkdownXmlCodeBlock from "../__resources__/markdown-xml-codeblock.md";
25
+ // import MarkdownUmlCodeBlock from "../__resources__/markdown-uml-codeblock.md";
26
+ // import MarkdownInlineXml from "../__resources__/markdown-inline-xml.md";
27
+ // import MarkdownLinks from "../__resources__/markdown-links.md";
28
+ // import MarkdownImages from "../__resources__/markdown-images.md";
29
+ // import MarkdownCommitLinks from "../__resources__/markdown-commit-link.md";
30
+ // import MarkdownXss from "../__resources__/markdown-xss.md";
31
+ // import MarkdownChangelog from "../__resources__/markdown-changelog.md";
32
+ // import Title from "../layout/Title";
33
+ // import { Subtitle } from "../layout";
34
+ // import { MemoryRouter } from "react-router-dom";
35
+ // import { Binder, BinderContext, extensionPoints } from "@scm-manager/ui-extensions";
36
+ // import { ProtocolLinkRendererProps } from "./markdownExtensions";
37
+ // import { RepositoryContextProvider, RepositoryRevisionContextProvider } from "@scm-manager/ui-api";
38
+ //
39
+ // const Spacing = styled.div`
40
+ // padding: 2em;
41
+ // `;
42
+ //
43
+ // storiesOf("MarkdownView", module)
44
+ // .addDecorator(story => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
45
+ // .addDecorator(story => <Spacing>{story()}</Spacing>)
46
+ // // Add async parameter, because the tests needs to render async before snapshot is taken so that
47
+ // // code fragments get highlighted properly
48
+ // .addParameters({ storyshots: { async: true } })
49
+ // .add("Default", () => <MarkdownView content={TestPage} skipHtml={false} />)
50
+ // .add("Skip Html", () => <MarkdownView content={TestPage} skipHtml={true} />)
51
+ // .add("Code without Lang", () => <MarkdownView content={MarkdownWithoutLang} skipHtml={false} />)
52
+ // .add("Xml Code Block", () => <MarkdownView content={MarkdownXmlCodeBlock} />)
53
+ // .add("Inline Xml", () => (
54
+ // <>
55
+ // <Title title="Inline Xml" />
56
+ // <Subtitle subtitle="Inline xml outside of a code block is not supported" />
57
+ // <MarkdownView content={MarkdownInlineXml} />
58
+ // </>
59
+ // ))
60
+ // .add("Links", () => {
61
+ // const binder = new Binder("custom protocol link renderer");
62
+ // binder.bind<extensionPoints.MarkdownLinkProtocolRenderer<"scw">>("markdown-renderer.link.protocol", {
63
+ // protocol: "scw",
64
+ // renderer: ProtocolLinkRenderer
65
+ // });
66
+ // return (
67
+ // <BinderContext.Provider value={binder}>
68
+ // <MarkdownView content={MarkdownLinks} basePath="/scm/" />
69
+ // </BinderContext.Provider>
70
+ // );
71
+ // })
72
+ // .add("Links without Base Path", () => {
73
+ // const binder = new Binder("custom protocol link renderer");
74
+ // binder.bind<extensionPoints.MarkdownLinkProtocolRenderer<"scw">>("markdown-renderer.link.protocol", {
75
+ // protocol: "scw",
76
+ // renderer: ProtocolLinkRenderer
77
+ // });
78
+ // return (
79
+ // <BinderContext.Provider value={binder}>
80
+ // <MarkdownView content={MarkdownLinks} />
81
+ // </BinderContext.Provider>
82
+ // );
83
+ // })
84
+ // .add("Header Anchor Links", () => (
85
+ // <MarkdownView
86
+ // content={MarkdownChangelog}
87
+ // basePath={"/"}
88
+ // permalink={"/?path=/story/markdownview--header-anchor-links"}
89
+ // enableAnchorHeadings={true}
90
+ // />
91
+ // ))
92
+ // .add("Commit Links", () => <MarkdownView content={MarkdownCommitLinks} />)
93
+ // .add("Custom code renderer", () => {
94
+ // const binder = new Binder("custom code renderer");
95
+ // const Container: FC<{ value: string }> = ({ value }) => {
96
+ // return (
97
+ // <div>
98
+ // <h4 style={{ border: "1px dashed lightgray", padding: "2px" }}>
99
+ // To render plantuml as images within markdown, please install the scm-markdown-plantuml-plugin
100
+ // </h4>
101
+ // <pre>{value}</pre>
102
+ // </div>
103
+ // );
104
+ // };
105
+ // binder.bind<extensionPoints.MarkdownCodeRenderer<"uml">>("markdown-renderer.code.uml", Container);
106
+ // return (
107
+ // <BinderContext.Provider value={binder}>
108
+ // <MarkdownView content={MarkdownUmlCodeBlock} />
109
+ // </BinderContext.Provider>
110
+ // );
111
+ // })
112
+ // .add("XSS Prevention", () => <MarkdownView content={MarkdownXss} skipHtml={false} />)
113
+ // .add("Images", () => (
114
+ // <RepositoryContextProvider
115
+ // // @ts-ignore We do not need a valid repository here, only one with a content link
116
+ // repository={{
117
+ // _links: { content: { href: "https://my.scm/scm/api/v2/some/repository/content/{revision}/{path}" } }
118
+ // }}
119
+ // >
120
+ // <RepositoryRevisionContextProvider revision={"42"}>
121
+ // <MarkdownView basePath={"/scm/"} content={MarkdownImages} />
122
+ // </RepositoryRevisionContextProvider>
123
+ // </RepositoryContextProvider>
124
+ // ));
125
+ //
126
+ // export const ProtocolLinkRenderer: FC<ProtocolLinkRendererProps<"scw">> = ({ protocol, href, children }) => {
127
+ // return (
128
+ // <div style={{ border: "1px dashed lightgray", padding: "2px" }}>
129
+ // <h4>
130
+ // Link: {href} [Protocol: {protocol}]
131
+ // </h4>
132
+ // <div>children: {children}</div>
133
+ // </div>
134
+ // );
135
+ // };
136
+
17
137
  import React, { FC } from "react";
18
- import { storiesOf } from "@storybook/react";
19
- import MarkdownView from "./MarkdownView";
20
138
  import styled from "styled-components";
139
+ import type { Meta, StoryObj } from "@storybook/react";
140
+ import { Binder, BinderContext, extensionPoints } from "@scm-manager/ui-extensions";
141
+ import { RepositoryContextProvider, RepositoryRevisionContextProvider } from "@scm-manager/ui-api";
142
+
143
+ import MarkdownView from "./MarkdownView";
144
+ import Title from "../layout/Title";
145
+ import { Subtitle } from "../layout";
146
+ import { ProtocolLinkRendererProps } from "./markdownExtensions";
21
147
 
22
148
  import TestPage from "../__resources__/test-page.md";
23
149
  import MarkdownWithoutLang from "../__resources__/markdown-without-lang.md";
@@ -29,107 +155,133 @@ import MarkdownImages from "../__resources__/markdown-images.md";
29
155
  import MarkdownCommitLinks from "../__resources__/markdown-commit-link.md";
30
156
  import MarkdownXss from "../__resources__/markdown-xss.md";
31
157
  import MarkdownChangelog from "../__resources__/markdown-changelog.md";
32
- import Title from "../layout/Title";
33
- import { Subtitle } from "../layout";
34
158
  import { MemoryRouter } from "react-router-dom";
35
- import { Binder, BinderContext, extensionPoints } from "@scm-manager/ui-extensions";
36
- import { ProtocolLinkRendererProps } from "./markdownExtensions";
37
- import { RepositoryContextProvider, RepositoryRevisionContextProvider } from "@scm-manager/ui-api";
159
+
160
+ // --- Helfer-Komponenten und Mock-Daten (aus der Original-Story übernommen) ---
38
161
 
39
162
  const Spacing = styled.div`
40
163
  padding: 2em;
41
164
  `;
42
165
 
43
- storiesOf("MarkdownView", module)
44
- .addDecorator(story => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
45
- .addDecorator(story => <Spacing>{story()}</Spacing>)
46
- // Add async parameter, because the tests needs to render async before snapshot is taken so that
47
- // code fragments get highlighted properly
48
- .addParameters({ storyshots: { async: true } })
49
- .add("Default", () => <MarkdownView content={TestPage} skipHtml={false} />)
50
- .add("Skip Html", () => <MarkdownView content={TestPage} skipHtml={true} />)
51
- .add("Code without Lang", () => <MarkdownView content={MarkdownWithoutLang} skipHtml={false} />)
52
- .add("Xml Code Block", () => <MarkdownView content={MarkdownXmlCodeBlock} />)
53
- .add("Inline Xml", () => (
166
+ const ProtocolLinkRenderer: FC<ProtocolLinkRendererProps<"scw">> = ({ protocol, href, children }) => (
167
+ <div style={{ border: "1px dashed lightgray", padding: "2px" }}>
168
+ <h4>
169
+ Link: {href} [Protocol: {protocol}]
170
+ </h4>
171
+ <div>children: {children}</div>
172
+ </div>
173
+ );
174
+
175
+ // --- Storybook Metadaten ---
176
+
177
+ const meta: Meta<typeof MarkdownView> = {
178
+ title: "Global/MarkdownView",
179
+ component: MarkdownView,
180
+ decorators: [
181
+ (Story) => <MemoryRouter initialEntries={["/"]}>{Story()}</MemoryRouter>,
182
+ (Story) => <Spacing>{Story()}</Spacing>],
183
+ tags: ["autodocs"],
184
+ };
185
+
186
+ export default meta;
187
+
188
+ // --- Story-Definitionen ---
189
+
190
+ type Story = StoryObj<typeof meta>;
191
+
192
+ // Einfache Stories, die `args` verwenden
193
+ export const Default: Story = {
194
+ args: { content: TestPage, skipHtml: false },
195
+ };
196
+
197
+ export const SkipHtml: Story = {
198
+ args: { content: TestPage, skipHtml: true },
199
+ };
200
+
201
+ export const CodeWithoutLanguage: Story = {
202
+ name: "Code Without Language",
203
+ args: { content: MarkdownWithoutLang, skipHtml: false },
204
+ };
205
+
206
+ export const XmlCodeBlock: Story = {
207
+ args: { content: MarkdownXmlCodeBlock },
208
+ };
209
+
210
+ export const CommitLinks: Story = {
211
+ args: { content: MarkdownCommitLinks },
212
+ };
213
+
214
+ export const HeaderAnchorLinks: Story = {
215
+ args: {
216
+ content: MarkdownChangelog,
217
+ basePath: "/",
218
+ permalink: "/?path=/story/markdownview--header-anchor-links",
219
+ enableAnchorHeadings: true,
220
+ },
221
+ };
222
+
223
+ export const XSSPrevention: Story = {
224
+ args: { content: MarkdownXss, skipHtml: false },
225
+ };
226
+
227
+ // Komplexe Stories, die eine `render`-Funktion benötigen
228
+ export const InlineXml: Story = {
229
+ render: () => (
54
230
  <>
55
231
  <Title title="Inline Xml" />
56
232
  <Subtitle subtitle="Inline xml outside of a code block is not supported" />
57
233
  <MarkdownView content={MarkdownInlineXml} />
58
234
  </>
59
- ))
60
- .add("Links", () => {
235
+ ),
236
+ };
237
+
238
+ export const Links: Story = {
239
+ render: () => {
61
240
  const binder = new Binder("custom protocol link renderer");
62
241
  binder.bind<extensionPoints.MarkdownLinkProtocolRenderer<"scw">>("markdown-renderer.link.protocol", {
63
242
  protocol: "scw",
64
- renderer: ProtocolLinkRenderer
243
+ renderer: ProtocolLinkRenderer,
65
244
  });
66
245
  return (
67
246
  <BinderContext.Provider value={binder}>
68
247
  <MarkdownView content={MarkdownLinks} basePath="/scm/" />
69
248
  </BinderContext.Provider>
70
249
  );
71
- })
72
- .add("Links without Base Path", () => {
73
- const binder = new Binder("custom protocol link renderer");
74
- binder.bind<extensionPoints.MarkdownLinkProtocolRenderer<"scw">>("markdown-renderer.link.protocol", {
75
- protocol: "scw",
76
- renderer: ProtocolLinkRenderer
77
- });
78
- return (
79
- <BinderContext.Provider value={binder}>
80
- <MarkdownView content={MarkdownLinks} />
81
- </BinderContext.Provider>
82
- );
83
- })
84
- .add("Header Anchor Links", () => (
85
- <MarkdownView
86
- content={MarkdownChangelog}
87
- basePath={"/"}
88
- permalink={"/?path=/story/markdownview--header-anchor-links"}
89
- enableAnchorHeadings={true}
90
- />
91
- ))
92
- .add("Commit Links", () => <MarkdownView content={MarkdownCommitLinks} />)
93
- .add("Custom code renderer", () => {
250
+ },
251
+ };
252
+
253
+ export const CustomCodeRenderer: Story = {
254
+ name: "Custom Code Renderer (UML)",
255
+ render: () => {
94
256
  const binder = new Binder("custom code renderer");
95
- const Container: FC<{ value: string }> = ({ value }) => {
96
- return (
97
- <div>
98
- <h4 style={{ border: "1px dashed lightgray", padding: "2px" }}>
99
- To render plantuml as images within markdown, please install the scm-markdown-plantuml-plugin
100
- </h4>
101
- <pre>{value}</pre>
102
- </div>
103
- );
104
- };
257
+ const Container: FC<{ value: string }> = ({ value }) => (
258
+ <div>
259
+ <h4 style={{ border: "1px dashed lightgray", padding: "2px" }}>
260
+ To render plantuml as images within markdown, please install the scm-markdown-plantuml-plugin
261
+ </h4>
262
+ <pre>{value}</pre>
263
+ </div>
264
+ );
105
265
  binder.bind<extensionPoints.MarkdownCodeRenderer<"uml">>("markdown-renderer.code.uml", Container);
106
266
  return (
107
267
  <BinderContext.Provider value={binder}>
108
268
  <MarkdownView content={MarkdownUmlCodeBlock} />
109
269
  </BinderContext.Provider>
110
270
  );
111
- })
112
- .add("XSS Prevention", () => <MarkdownView content={MarkdownXss} skipHtml={false} />)
113
- .add("Images", () => (
271
+ },
272
+ };
273
+
274
+ export const Images: Story = {
275
+ render: () => (
114
276
  <RepositoryContextProvider
115
- // @ts-ignore We do not need a valid repository here, only one with a content link
277
+ // @ts-ignore We do not need a valid repository here
116
278
  repository={{
117
- _links: { content: { href: "https://my.scm/scm/api/v2/some/repository/content/{revision}/{path}" } }
279
+ _links: { content: { href: "https://my.scm/scm/api/v2/some/repository/content/{revision}/{path}" } },
118
280
  }}
119
281
  >
120
- <RepositoryRevisionContextProvider revision={"42"}>
121
- <MarkdownView basePath={"/scm/"} content={MarkdownImages} />
282
+ <RepositoryRevisionContextProvider revision="42">
283
+ <MarkdownView basePath="/scm/" content={MarkdownImages} />
122
284
  </RepositoryRevisionContextProvider>
123
285
  </RepositoryContextProvider>
124
- ));
125
-
126
- export const ProtocolLinkRenderer: FC<ProtocolLinkRendererProps<"scw">> = ({ protocol, href, children }) => {
127
- return (
128
- <div style={{ border: "1px dashed lightgray", padding: "2px" }}>
129
- <h4>
130
- Link: {href} [Protocol: {protocol}]
131
- </h4>
132
- <div>children: {children}</div>
133
- </div>
134
- );
286
+ ),
135
287
  };
@@ -15,17 +15,19 @@
15
15
  */
16
16
 
17
17
  import { extensionPoints } from "@scm-manager/ui-extensions";
18
- import { ComponentProps } from "react";
18
+ import { ComponentProps, FC, ReactNode } from "react";
19
+
20
+ type WithChildren<T> = T extends FC<infer P> ? FC<P & { children?: ReactNode }> : never;
19
21
 
20
22
  export type ProtocolLinkRendererProps<Protocol extends string | undefined = undefined> = ComponentProps<
21
- extensionPoints.MarkdownLinkProtocolRenderer<Protocol>["type"]["renderer"]
23
+ WithChildren<extensionPoints.MarkdownLinkProtocolRenderer<Protocol>["type"]["renderer"]>
22
24
  >;
23
25
 
24
26
  /**
25
27
  * @deprecated use {@link MarkdownLinkProtocolRenderer}`["type"]` instead
26
28
  */
27
- export type ProtocolLinkRendererExtension = extensionPoints.MarkdownLinkProtocolRenderer["type"];
29
+ export type ProtocolLinkRendererExtension = WithChildren<extensionPoints.MarkdownLinkProtocolRenderer["type"]>;
28
30
 
29
31
  export type ProtocolLinkRendererExtensionMap = {
30
- [protocol: string]: extensionPoints.MarkdownLinkProtocolRenderer["type"]["renderer"] | undefined;
32
+ [protocol: string]: WithChildren<extensionPoints.MarkdownLinkProtocolRenderer["type"]["renderer"]> | undefined;
31
33
  };
@@ -14,64 +14,153 @@
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 * as React from "react";
17
+ // import { storiesOf } from "@storybook/react";
18
+ // import { MemoryRouter } from "react-router-dom";
19
+ // import * as React from "react";
20
+ // import ConfirmAlert, { confirmAlert } from "./ConfirmAlert";
21
+ // import ActiveModalCountContext from "./activeModalCountContext";
22
+ //
23
+ // const body =
24
+ // "Mind-paralyzing change needed improbability vortex machine sorts sought same theory upending job just allows\n " +
25
+ // "hostess’s really oblong Infinite Improbability thing into the starship against which behavior accordance with\n " +
26
+ // "Kakrafoon humanoid undergarment ship powered by GPP-guided bowl of petunias nothing was frequently away incredibly\n " +
27
+ // "ordinary mob.";
28
+ //
29
+ // const buttons = [
30
+ // {
31
+ // className: "is-outlined",
32
+ // label: "Cancel",
33
+ // onClick: () => null,
34
+ // },
35
+ // {
36
+ // label: "Submit",
37
+ // },
38
+ // ];
39
+ //
40
+ // const buttonsWithAutofocus = [
41
+ // {
42
+ // label: "Cancel",
43
+ // onClick: () => null,
44
+ // },
45
+ // {
46
+ // className: "is-info",
47
+ // label: "I should be focused",
48
+ // autofocus: true,
49
+ // },
50
+ // ];
51
+ //
52
+ // const doNothing = () => {
53
+ // // Do nothing
54
+ // };
55
+ //
56
+ // storiesOf("Modal/ConfirmAlert", module)
57
+ // .addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
58
+ // .addDecorator((story) => (
59
+ // <ActiveModalCountContext.Provider value={{ value: 0, increment: doNothing, decrement: doNothing }}>
60
+ // {story()}
61
+ // </ActiveModalCountContext.Provider>
62
+ // ))
63
+ // .add("Default", () => <ConfirmAlert message={body} title={"Are you sure about that?"} buttons={buttons} />)
64
+ // .add("WithButton", () => {
65
+ // const buttonClick = () => {
66
+ // confirmAlert({ message: body, title: "Are you sure about that?", buttons });
67
+ // };
68
+ // return (
69
+ // <>
70
+ // <button onClick={buttonClick}>Open ConfirmAlert</button>
71
+ // <div id="modalRoot" />
72
+ // </>
73
+ // );
74
+ // })
75
+ // .add("Autofocus", () => (
76
+ // <ConfirmAlert message={body} title={"Are you sure about that?"} buttons={buttonsWithAutofocus} />
77
+ // ));
78
+
79
+ import React from "react";
80
+ import type { Meta, StoryObj } from "@storybook/react";
20
81
  import ConfirmAlert, { confirmAlert } from "./ConfirmAlert";
21
82
  import ActiveModalCountContext from "./activeModalCountContext";
22
83
 
84
+ // --- Helfer-Funktionen und Mock-Daten (aus der Original-Story übernommen) ---
85
+
23
86
  const body =
24
87
  "Mind-paralyzing change needed improbability vortex machine sorts sought same theory upending job just allows\n " +
25
88
  "hostess’s really oblong Infinite Improbability thing into the starship against which behavior accordance with\n " +
26
89
  "Kakrafoon humanoid undergarment ship powered by GPP-guided bowl of petunias nothing was frequently away incredibly\n " +
27
90
  "ordinary mob.";
28
91
 
29
- const buttons = [
30
- {
31
- className: "is-outlined",
32
- label: "Cancel",
33
- onClick: () => null,
34
- },
35
- {
36
- label: "Submit",
37
- },
38
- ];
92
+ const buttons = [{ className: "is-outlined", label: "Cancel", onClick: () => null }, { label: "Submit" }];
39
93
 
40
94
  const buttonsWithAutofocus = [
41
- {
42
- label: "Cancel",
43
- onClick: () => null,
44
- },
45
- {
46
- className: "is-info",
47
- label: "I should be focused",
48
- autofocus: true,
49
- },
95
+ { label: "Cancel", onClick: () => null },
96
+ { className: "is-info", label: "I should be focused", autofocus: true },
50
97
  ];
51
98
 
52
99
  const doNothing = () => {
53
100
  // Do nothing
54
101
  };
55
102
 
56
- storiesOf("Modal/ConfirmAlert", module)
57
- .addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
58
- .addDecorator((story) => (
59
- <ActiveModalCountContext.Provider value={{ value: 0, increment: doNothing, decrement: doNothing }}>
60
- {story()}
61
- </ActiveModalCountContext.Provider>
62
- ))
63
- .add("Default", () => <ConfirmAlert message={body} title={"Are you sure about that?"} buttons={buttons} />)
64
- .add("WithButton", () => {
65
- const buttonClick = () => {
66
- confirmAlert({ message: body, title: "Are you sure about that?", buttons });
67
- };
68
- return (
69
- <>
70
- <button onClick={buttonClick}>Open ConfirmAlert</button>
71
- <div id="modalRoot" />
72
- </>
73
- );
74
- })
75
- .add("Autofocus", () => (
76
- <ConfirmAlert message={body} title={"Are you sure about that?"} buttons={buttonsWithAutofocus} />
77
- ));
103
+ // Helfer-Komponente für die Story, die den Alert per Klick auslöst
104
+ const TriggerConfirmAlertExample = () => {
105
+ const handleClick = () => {
106
+ confirmAlert({ message: body, title: "Are you sure about that?", buttons });
107
+ };
108
+ return (
109
+ <>
110
+ <p>
111
+ Klicken Sie auf den Button, um den Bestätigungsdialog imperativ auszulösen. Der Dialog wird in{" "}
112
+ <code>#modalRoot</code> gerendert.
113
+ </p>
114
+ <button className="button" onClick={handleClick}>
115
+ Open ConfirmAlert
116
+ </button>
117
+ {/* Das ist notwendig, damit die `confirmAlert`-Funktion ein Einhängepunkt im DOM findet. */}
118
+ <div id="modalRoot" />
119
+ </>
120
+ );
121
+ };
122
+
123
+ // --- Storybook Metadaten ---
124
+
125
+ const meta: Meta<typeof ConfirmAlert> = {
126
+ title: "Modal/ConfirmAlert",
127
+ component: ConfirmAlert,
128
+ // Der ActiveModalCountContext.Provider wird als Decorator für alle Stories beibehalten.
129
+ decorators: [
130
+ (Story) => (
131
+ <ActiveModalCountContext.Provider value={{ value: 0, increment: doNothing, decrement: doNothing }}>
132
+ <div style={{ margin: "2rem" }}>
133
+ <Story />
134
+ </div>
135
+ </ActiveModalCountContext.Provider>
136
+ ),
137
+ ],
138
+ tags: ["autodocs"],
139
+ };
140
+
141
+ export default meta;
142
+
143
+ // --- Story-Definitionen ---
144
+
145
+ type Story = StoryObj<typeof meta>;
146
+
147
+ export const Default: Story = {
148
+ args: {
149
+ message: body,
150
+ title: "Are you sure about that?",
151
+ buttons: buttons,
152
+ },
153
+ };
154
+
155
+ export const Autofocus: Story = {
156
+ args: {
157
+ ...Default.args,
158
+ buttons: buttonsWithAutofocus,
159
+ },
160
+ };
161
+
162
+ // Diese Story verwendet eine `render`-Funktion, um die imperative API zu demonstrieren.
163
+ export const TriggeredByButton: Story = {
164
+ name: "Triggered by Button",
165
+ render: () => <TriggerConfirmAlertExample />,
166
+ };
@@ -14,9 +14,9 @@
14
14
  * along with this program. If not, see https://www.gnu.org/licenses/.
15
15
  */
16
16
 
17
+ import { createRoot } from "react-dom/client";
17
18
  import * as React from "react";
18
- import { FC, useRef, useState } from "react";
19
- import ReactDOM from "react-dom";
19
+ import { FC, ReactNode, useRef, useState } from "react";
20
20
  import Modal from "./Modal";
21
21
  import classNames from "classnames";
22
22
 
@@ -34,6 +34,7 @@ type Props = {
34
34
  message?: string;
35
35
  buttons: Button[];
36
36
  close?: () => void;
37
+ children?: ReactNode;
37
38
  };
38
39
 
39
40
  export const ConfirmAlert: FC<Props> = ({ title, message, buttons, close, children }) => {
@@ -102,11 +103,11 @@ export function confirmAlert(properties: Props) {
102
103
  const close = () => {
103
104
  const container = document.getElementById("modalRoot");
104
105
  if (container) {
105
- ReactDOM.unmountComponentAtNode(container);
106
+ createRoot(container).unmount();
106
107
  }
107
108
  };
108
109
  const props = { ...properties, close };
109
- ReactDOM.render(<ConfirmAlert {...props} />, root);
110
+ createRoot(root).render(<ConfirmAlert {...props} />);
110
111
  }
111
112
  }
112
113