@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,7 +14,7 @@
14
14
  * along with this program. If not, see https://www.gnu.org/licenses/.
15
15
  */
16
16
 
17
- import { Person } from "@scm-manager/ui-types";
17
+ import type { Person } from "@scm-manager/ui-types";
18
18
  import { extensionPoints } from "@scm-manager/ui-extensions";
19
19
 
20
20
  // re export type to avoid breaking changes,
@@ -15,7 +15,7 @@
15
15
  */
16
16
 
17
17
  import React, { FC } from "react";
18
- import { Image } from "..";
18
+ import Image from "../Image";
19
19
  import { EXTENSION_POINT, Person } from "./Avatar";
20
20
  import { useBinder } from "@scm-manager/ui-extensions";
21
21
 
@@ -14,11 +14,11 @@
14
14
  * along with this program. If not, see https://www.gnu.org/licenses/.
15
15
  */
16
16
 
17
- import React, { FC } from "react";
17
+ import React, { FC, ReactNode } from "react";
18
18
  import { extensionPoints, useBinder } from "@scm-manager/ui-extensions";
19
19
  import { EXTENSION_POINT } from "./Avatar";
20
20
 
21
- const AvatarWrapper: FC = ({ children }) => {
21
+ const AvatarWrapper: FC<{ children: ReactNode }> = ({ children }) => {
22
22
  const binder = useBinder();
23
23
  if (binder.hasExtension<extensionPoints.AvatarFactory>(EXTENSION_POINT)) {
24
24
  return <>{children}</>;
@@ -0,0 +1,159 @@
1
+ /*
2
+ * Copyright (c) 2020 - present Cloudogu GmbH
3
+ *
4
+ * This program is free software: you can redistribute it and/or modify it under
5
+ * the terms of the GNU Affero General Public License as published by the Free
6
+ * Software Foundation, version 3.
7
+ *
8
+ * This program is distributed in the hope that it will be useful, but WITHOUT
9
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10
+ * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
11
+ * details.
12
+ *
13
+ * You should have received a copy of the GNU Affero General Public License
14
+ * along with this program. If not, see https://www.gnu.org/licenses/.
15
+ */
16
+
17
+ // import React, {ReactElement, ReactNode, useEffect, useState} from "react";
18
+ // import styled from "styled-components";
19
+ // import {Meta, storiesOf, StoryObj} from "@storybook/react";
20
+ // import Button from "./Button";
21
+ // import {MemoryRouter} from "react-router-dom";
22
+ // import AddButton from "./AddButton";
23
+ // import CreateButton from "./CreateButton";
24
+ // import DeleteButton from "./DeleteButton";
25
+ // import DownloadButton from "./DownloadButton";
26
+ // import EditButton from "./EditButton";
27
+ // import SubmitButton from "./SubmitButton";
28
+
29
+ // const colors = ["primary", "link", "info", "success", "warning", "danger", "white", "light", "dark", "black", "text"];
30
+ //
31
+ // const Spacing = styled.div`
32
+ // padding: 1em;
33
+ // `;
34
+ //
35
+ // const SpacingDecorator = (story: () => ReactNode) => <Spacing>{story()}</Spacing>;
36
+ // const RoutingDecorator = (story: () => ReactNode) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>;
37
+ //
38
+ // storiesOf("Buttons/Button", module)
39
+ // .addDecorator(RoutingDecorator)
40
+ // .add("Colors", () => (
41
+ // <div>
42
+ // {colors.map((color) => (
43
+ // <Spacing key={color}>
44
+ // <Button color={color} label={color} />
45
+ // </Spacing>
46
+ // ))}
47
+ // </div>
48
+ // ))
49
+ // .add("Loading", () => (
50
+ // <Spacing>
51
+ // <Button color={"primary"} loading={true}>
52
+ // Loading Button
53
+ // </Button>
54
+ // </Spacing>
55
+ // ))
56
+ // .add("Disabled", () => (
57
+ // <div>
58
+ // {colors.map((color) => (
59
+ // <Spacing key={color}>
60
+ // <Button color={color} label={color} disabled={true} />
61
+ // </Spacing>
62
+ // ))}
63
+ // </div>
64
+ // ));
65
+ //
66
+ // const buttonStory = (name: string, storyFn: () => ReactElement) => {
67
+ // return storiesOf("Buttons/" + name, module)
68
+ // .addDecorator(RoutingDecorator)
69
+ // .addDecorator(SpacingDecorator)
70
+ // .add("Default", storyFn);
71
+ // };
72
+ // buttonStory("AddButton", () => <AddButton>Add</AddButton>);
73
+ // buttonStory("CreateButton", () => <CreateButton>Create</CreateButton>);
74
+ // buttonStory("DeleteButton", () => <DeleteButton>Delete</DeleteButton>);
75
+ // buttonStory("DownloadButton", () => <DownloadButton displayName="Download" disabled={false} url="" />).add(
76
+ // "Disabled",
77
+ // () => <DownloadButton displayName="Download" disabled={true} url="" />
78
+ // );
79
+ // buttonStory("EditButton", () => <EditButton>Edit</EditButton>);
80
+ // buttonStory("SubmitButton", () => <SubmitButton>Submit</SubmitButton>);
81
+ // buttonStory("Button Ref", () => {
82
+ // const [ref, setRef] = useState<HTMLButtonElement | HTMLAnchorElement | null>();
83
+ // useEffect(() => ref?.focus(), [ref]);
84
+ // return <Button ref={setRef}>Focus me!</Button>;
85
+ // });
86
+
87
+ import React, { useEffect, useState } from "react";
88
+ import styled from "styled-components";
89
+ import type { Meta, StoryObj } from "@storybook/react";
90
+ import Button from "./Button";
91
+
92
+ // --- Helfer-Komponenten und Mock-Daten ---
93
+
94
+ const colors = ["primary", "link", "info", "success", "warning", "danger", "white", "light", "dark", "black", "text"];
95
+ const Spacing = styled.div`
96
+ padding: 1em;
97
+ display: inline-block;
98
+ `;
99
+
100
+ const RefExample = () => {
101
+ const [ref, setRef] = useState<HTMLButtonElement | HTMLAnchorElement | null>(null);
102
+ useEffect(() => {
103
+ ref?.focus();
104
+ }, [ref]);
105
+ return <Button ref={setRef}>Focus me!</Button>;
106
+ };
107
+
108
+ // --- Storybook Metadaten ---
109
+
110
+ const meta: Meta<typeof Button> = {
111
+ title: "Buttons/Button",
112
+ component: Button,
113
+ decorators: [(Story) => <Spacing>{Story()}</Spacing>],
114
+ tags: ["autodocs"],
115
+ };
116
+
117
+ export default meta;
118
+
119
+ // --- Story-Definitionen ---
120
+
121
+ type Story = StoryObj<typeof meta>;
122
+
123
+ export const Loading: Story = {
124
+ args: {
125
+ color: "primary",
126
+ loading: true,
127
+ children: "Loading Button",
128
+ },
129
+ };
130
+
131
+ // Stories mit komplexem Layout verwenden eine `render`-Funktion
132
+ export const Colors: Story = {
133
+ render: () => (
134
+ <div>
135
+ {colors.map((color) => (
136
+ <Spacing key={color}>
137
+ <Button color={color} label={color} />
138
+ </Spacing>
139
+ ))}
140
+ </div>
141
+ ),
142
+ };
143
+
144
+ export const Disabled: Story = {
145
+ render: () => (
146
+ <div>
147
+ {colors.map((color) => (
148
+ <Spacing key={color}>
149
+ <Button color={color} label={color} disabled={true} />
150
+ </Spacing>
151
+ ))}
152
+ </div>
153
+ ),
154
+ };
155
+
156
+ export const UsingRefs: Story = {
157
+ name: "Using Refs",
158
+ render: () => <RefExample />,
159
+ };
@@ -16,7 +16,7 @@
16
16
 
17
17
  import React, { KeyboardEvent, MouseEvent, ReactNode, useCallback } from "react";
18
18
  import classNames from "classnames";
19
- import { Link } from "react-router-dom";
19
+ import { Link } from "react-router";
20
20
  import Icon from "../Icon";
21
21
  import { createAttributesForTesting } from "../devBuild";
22
22
 
@@ -61,7 +61,7 @@ const Button = React.forwardRef<HTMLButtonElement | HTMLAnchorElement, Props>(
61
61
  action,
62
62
  color = "default",
63
63
  },
64
- ref
64
+ ref,
65
65
  ) => {
66
66
  const executeRef = useCallback(
67
67
  (el: HTMLButtonElement | HTMLAnchorElement | null) => {
@@ -71,7 +71,7 @@ const Button = React.forwardRef<HTMLButtonElement | HTMLAnchorElement, Props>(
71
71
  ref.current = el;
72
72
  }
73
73
  },
74
- [ref]
74
+ [ref],
75
75
  );
76
76
  const renderIcon = () => {
77
77
  return (
@@ -89,7 +89,7 @@ const Button = React.forwardRef<HTMLButtonElement | HTMLAnchorElement, Props>(
89
89
  { "is-loading": loading },
90
90
  { "is-fullwidth": fullWidth },
91
91
  { "is-reduced-mobile": reducedMobile },
92
- className
92
+ className,
93
93
  );
94
94
 
95
95
  const content = (
@@ -131,7 +131,7 @@ const Button = React.forwardRef<HTMLButtonElement | HTMLAnchorElement, Props>(
131
131
  {content}
132
132
  </button>
133
133
  );
134
- }
134
+ },
135
135
  );
136
136
 
137
137
  export default Button;
@@ -33,7 +33,6 @@ type GlobalRouteProps = {
33
33
  };
34
34
 
35
35
  type NamespaceRouteProps = {
36
- url: string;
37
36
  namespace: Namespace;
38
37
  };
39
38
 
@@ -53,13 +52,13 @@ class ConfigurationBinder {
53
52
  to: string,
54
53
  labelI18nKey: string,
55
54
  t: any,
56
- options: Omit<ComponentProps<typeof NavLink>, "label" | "to"> = {}
55
+ options: Omit<ComponentProps<typeof NavLink>, "label" | "to"> = {},
57
56
  ) {
58
57
  return <NavLink to={to} label={t(labelI18nKey)} {...options} />;
59
58
  }
60
59
 
61
60
  route(path: string, Component: any) {
62
- return <Route path={urls.escapeUrlForRoute(path)}>{Component}</Route>;
61
+ return <Route path={urls.escapeUrlForRoute(path)} element={Component} />;
63
62
  }
64
63
 
65
64
  bindGlobal(to: string, labelI18nKey: string, linkName: string, ConfigurationComponent: any, sortKey?: string) {
@@ -83,10 +82,10 @@ class ConfigurationBinder {
83
82
 
84
83
  if (link) {
85
84
  return this.route(
86
- url + "/settings" + to,
85
+ "/settings" + to,
87
86
  <TitledGlobalSettingComponent i18nNamespace={this.i18nNamespace} label={labelI18nKey}>
88
87
  <ConfigurationComponent link={(link as Link).href} {...additionalProps} />
89
- </TitledGlobalSettingComponent>
88
+ </TitledGlobalSettingComponent>,
90
89
  );
91
90
  }
92
91
  };
@@ -100,20 +99,20 @@ class ConfigurationBinder {
100
99
  labelI18nKey: string,
101
100
  icon: string,
102
101
  linkName: string,
103
- Component: React.ComponentType<{ link: string }>
102
+ Component: React.ComponentType<{ link: string }>,
104
103
  ) {
105
104
  const predicate = ({ links }: extensionPoints.AdminRoute["props"]) => links[linkName];
106
105
 
107
106
  const AdminNavLink = withTranslation(this.i18nNamespace)(
108
- ({ t, url }: WithTranslation & extensionPoints.AdminNavigation["props"]) =>
109
- this.navLink(url + to, labelI18nKey, t, { icon })
107
+ ({ t }: WithTranslation & extensionPoints.AdminNavigation["props"]) =>
108
+ this.navLink(to, labelI18nKey, t, { icon }),
110
109
  );
110
+ binder.bind("admin.navigation", AdminNavLink, predicate);
111
111
 
112
- const AdminRoute: extensionPoints.AdminRoute["type"] = ({ links, url }) =>
113
- this.route(url + to, <Component link={(links[linkName] as Link).href} />);
112
+ const AdminRoute: extensionPoints.AdminRoute["type"] = ({ links }) =>
113
+ this.route(to, <Component link={(links[linkName] as Link).href} />);
114
114
 
115
115
  binder.bind<extensionPoints.AdminRoute>("admin.route", AdminRoute, predicate);
116
- binder.bind<extensionPoints.AdminNavigation>("admin.navigation", AdminNavLink, predicate);
117
116
  }
118
117
 
119
118
  bindRepository(to: string, labelI18nKey: string, linkName: string, RepositoryComponent: any) {
@@ -131,18 +130,17 @@ class ConfigurationBinder {
131
130
  // bind navigation link to extension point
132
131
  binder.bind("repository.navigation", RepoNavLink, repoPredicate);
133
132
 
134
- // route for global configuration, passes the current repository to component
135
- const RepoRoute = ({ url, repository, ...additionalProps }: RepositoryRouteProps) => {
133
+ // // route for global configuration, passes the current repository to component
134
+ const RepoRoute = ({ repository, ...additionalProps }: RepositoryRouteProps) => {
136
135
  const link = repository._links[linkName];
137
136
  if (link) {
138
137
  return this.route(
139
- url + to,
140
- <RepositoryComponent repository={repository} link={(link as Link).href} {...additionalProps} />
138
+ to,
139
+ <RepositoryComponent repository={repository} link={(link as Link).href} {...additionalProps} />,
141
140
  );
142
141
  }
143
142
  };
144
-
145
- // bind config route to extension point
143
+ // // bind config route to extension point
146
144
  binder.bind("repository.route", RepoRoute, repoPredicate);
147
145
  }
148
146
 
@@ -151,7 +149,7 @@ class ConfigurationBinder {
151
149
  labelI18nKey: string,
152
150
  linkName: string,
153
151
  RepositoryComponent: any,
154
- sortKey?: string
152
+ sortKey?: string,
155
153
  ) {
156
154
  // create predicate based on the link name of the current repository route
157
155
  // if the linkname is not available, the navigation link and the route are not bound to the extension points
@@ -168,19 +166,19 @@ class ConfigurationBinder {
168
166
  binder.bind("repository.setting", RepoNavLink, repoPredicate, sortKey ?? linkName);
169
167
 
170
168
  // route for global configuration, passes the current repository to component
171
- const RepoRoute = ({ url, repository, ...additionalProps }: RepositoryRouteProps) => {
169
+ const RepoRoute = ({ repository, ...additionalProps }: RepositoryRouteProps) => {
172
170
  const link = repository._links[linkName];
173
171
 
174
172
  if (link) {
175
173
  return this.route(
176
- urls.unescapeUrlForRoute(url) + "/settings" + to,
174
+ "/settings" + to,
177
175
  <TitledRepositorySettingComponent
178
176
  i18nNamespace={this.i18nNamespace}
179
177
  label={labelI18nKey}
180
178
  repository={repository}
181
179
  >
182
180
  <RepositoryComponent repository={repository} link={(link as Link).href} {...additionalProps} />
183
- </TitledRepositorySettingComponent>
181
+ </TitledRepositorySettingComponent>,
184
182
  );
185
183
  }
186
184
  };
@@ -193,37 +191,39 @@ class ConfigurationBinder {
193
191
  to: string,
194
192
  labelI18nKey: string,
195
193
  linkName: string,
196
- ConfigurationComponent: ComponentType<{ link: string; namespace: Namespace }>
194
+ ConfigurationComponent: ComponentType<{ link: string; namespace: Namespace }>,
197
195
  ) {
198
196
  const namespacePredicate = (props: (extensionPoints.NamespaceSetting | extensionPoints.NamespaceRoute)["props"]) =>
199
197
  props.namespace && props.namespace._links && props.namespace._links[linkName];
200
198
 
201
- const NamespaceNavLink: FC<extensionPoints.NamespaceRoute["props"]> = ({ url }) => {
199
+ const NamespaceNavLink: FC<extensionPoints.NamespaceRoute["props"]> = () => {
202
200
  const [t] = useTranslation(this.i18nNamespace);
203
- return this.navLink(url + "/settings" + to, labelI18nKey, t, { activeOnlyWhenExact: false });
201
+ return this.navLink("/settings" + to, labelI18nKey, t, { activeOnlyWhenExact: false });
204
202
  };
205
203
 
206
204
  binder.bind<extensionPoints.NamespaceSetting>("namespace.setting", NamespaceNavLink, namespacePredicate);
207
205
 
208
- const NamespaceRoute: FC<extensionPoints.NamespaceRoute["props"]> = ({ url, namespace, ...additionalProps }) => {
209
- const link = namespace._links[linkName];
210
-
211
- if (link) {
212
- return this.route(
213
- urls.unescapeUrlForRoute(url) + "/settings" + to,
214
- <TitledNamespaceSettingComponent
215
- i18nNamespace={this.i18nNamespace}
216
- label={labelI18nKey}
217
- namespace={namespace}
218
- >
219
- <ConfigurationComponent namespace={namespace} link={(link as Link).href} {...additionalProps} />
220
- </TitledNamespaceSettingComponent>
221
- );
206
+ const NamespaceRoute = ({ namespace, ...additionalProps }: NamespaceRouteProps) => {
207
+ if (namespace) {
208
+ const link = namespace._links[linkName];
209
+
210
+ if (link) {
211
+ return this.route(
212
+ "/settings" + to,
213
+ <TitledNamespaceSettingComponent
214
+ i18nNamespace={this.i18nNamespace}
215
+ label={labelI18nKey}
216
+ namespace={namespace}
217
+ >
218
+ <ConfigurationComponent namespace={namespace} link={(link as Link).href} {...additionalProps} />
219
+ </TitledNamespaceSettingComponent>,
220
+ );
221
+ }
222
222
  }
223
223
  return null;
224
224
  };
225
225
 
226
- binder.bind<extensionPoints.NamespaceRoute>("namespace.route", NamespaceRoute, namespacePredicate);
226
+ binder.bind("namespace.route", NamespaceRoute, namespacePredicate);
227
227
  }
228
228
  }
229
229
 
@@ -14,7 +14,7 @@
14
14
  * along with this program. If not, see https://www.gnu.org/licenses/.
15
15
  */
16
16
 
17
- import React, { FC, FormEvent, useEffect, useState } from "react";
17
+ import React, { FC, FormEvent, ReactNode, useEffect, useState } from "react";
18
18
  import { ErrorNotification, Level, SubmitButton } from "../index";
19
19
  import { useTranslation } from "react-i18next";
20
20
  import Loading from "../Loading";
@@ -27,6 +27,7 @@ type Props = {
27
27
  isValid: boolean;
28
28
  error?: Error | null;
29
29
  onSubmit: (e: FormEvent<HTMLFormElement>) => void;
30
+ children?: ReactNode;
30
31
  };
31
32
 
32
33
  const ConfigurationChangedNotification: FC<{ configChanged: boolean }> = ({ configChanged }) => {
@@ -14,7 +14,7 @@
14
14
  * along with this program. If not, see https://www.gnu.org/licenses/.
15
15
  */
16
16
 
17
- import React, { FC } from "react";
17
+ import React, { FC, ReactNode } from "react";
18
18
  import { useTranslation } from "react-i18next";
19
19
  import { useDocumentTitle, useDocumentTitleForRepository } from "@scm-manager/ui-core";
20
20
  import { Namespace, Repository } from "@scm-manager/ui-types";
@@ -22,6 +22,7 @@ import { Namespace, Repository } from "@scm-manager/ui-types";
22
22
  type GlobalProps = {
23
23
  i18nNamespace: string;
24
24
  label: string;
25
+ children?: ReactNode;
25
26
  };
26
27
 
27
28
  export const TitledGlobalSettingComponent: FC<GlobalProps> = ({ children, i18nNamespace, label }) => {
@@ -35,6 +36,7 @@ type RepositoryProps = {
35
36
  i18nNamespace: string;
36
37
  label: string;
37
38
  repository: Repository;
39
+ children?: ReactNode;
38
40
  };
39
41
 
40
42
  export const TitledRepositorySettingComponent: FC<RepositoryProps> = ({
@@ -53,6 +55,7 @@ type NamespaceProps = {
53
55
  i18nNamespace: string;
54
56
  label: string;
55
57
  namespace: Namespace;
58
+ children?: ReactNode;
56
59
  };
57
60
 
58
61
  export const TitledNamespaceSettingComponent: FC<NamespaceProps> = ({ children, i18nNamespace, label, namespace }) => {
@@ -14,31 +14,66 @@
14
14
  * along with this program. If not, see https://www.gnu.org/licenses/.
15
15
  */
16
16
 
17
+ // import React from "react";
18
+ // import { MemoryRouter } from "react-router-dom";
19
+ // import { storiesOf } from "@storybook/react";
20
+ // import AddKeyValueEntryToTableField from "./AddKeyValueEntryToTableField";
21
+ //
22
+ // storiesOf("Forms/AddKeyValueEntryToTableField", module)
23
+ // .addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
24
+ // .add("Default", () => (
25
+ // <div className="m-6">
26
+ // <AddKeyValueEntryToTableField
27
+ // keyFieldLabel="Key"
28
+ // valueFieldLabel="Value"
29
+ // buttonLabel="Add to Table"
30
+ // addEntry={() => null}
31
+ // />
32
+ // </div>
33
+ // ))
34
+ // .add("Disabled", () => (
35
+ // <div className="m-6">
36
+ // <AddKeyValueEntryToTableField
37
+ // keyFieldLabel="Key"
38
+ // valueFieldLabel="Value"
39
+ // buttonLabel="Add to Table"
40
+ // addEntry={() => null}
41
+ // disabled={true}
42
+ // />
43
+ // </div>
44
+ // ));
45
+
17
46
  import React from "react";
18
- import { MemoryRouter } from "react-router-dom";
19
- import { storiesOf } from "@storybook/react";
47
+ import type { Meta, StoryObj } from "@storybook/react";
48
+
20
49
  import AddKeyValueEntryToTableField from "./AddKeyValueEntryToTableField";
21
50
 
22
- storiesOf("Forms/AddKeyValueEntryToTableField", module)
23
- .addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
24
- .add("Default", () => (
25
- <div className="m-6">
26
- <AddKeyValueEntryToTableField
27
- keyFieldLabel="Key"
28
- valueFieldLabel="Value"
29
- buttonLabel="Add to Table"
30
- addEntry={() => null}
31
- />
32
- </div>
33
- ))
34
- .add("Disabled", () => (
35
- <div className="m-6">
36
- <AddKeyValueEntryToTableField
37
- keyFieldLabel="Key"
38
- valueFieldLabel="Value"
39
- buttonLabel="Add to Table"
40
- addEntry={() => null}
41
- disabled={true}
42
- />
43
- </div>
44
- ));
51
+ const meta: Meta<typeof AddKeyValueEntryToTableField> = {
52
+ title: "Forms/AddKeyValueEntryToTableField",
53
+ component: AddKeyValueEntryToTableField,
54
+ decorators: [(Story) => Story(), (Story) => <div className="m-6">{Story()}</div>],
55
+ argTypes: {
56
+ addEntry: { action: "entryAdded" },
57
+ },
58
+ tags: ["autodocs"],
59
+ };
60
+
61
+ export default meta;
62
+
63
+ type Story = StoryObj<typeof meta>;
64
+
65
+ export const Default: Story = {
66
+ args: {
67
+ keyFieldLabel: "Key",
68
+ valueFieldLabel: "Value",
69
+ buttonLabel: "Add to Table",
70
+ disabled: false,
71
+ },
72
+ };
73
+
74
+ export const Disabled: Story = {
75
+ args: {
76
+ ...Default.args,
77
+ disabled: true,
78
+ },
79
+ };