@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.
- package/package.json +46 -51
- package/src/BranchSelector.stories.tsx +60 -11
- package/src/Breadcrumb.stories.tsx +131 -57
- package/src/Breadcrumb.tsx +3 -3
- package/src/CardColumn.stories.tsx +94 -27
- package/src/CardColumnSmall.stories.tsx +102 -27
- package/src/CommaSeparatedList.tsx +2 -2
- package/src/Date.stories.tsx +64 -17
- package/src/Duration.stories.tsx +92 -45
- package/src/ErrorBoundary.tsx +38 -58
- package/src/ErrorNotification.tsx +1 -1
- package/src/Help.stories.tsx +61 -17
- package/src/Help.tsx +5 -11
- package/src/Icon.stories.tsx +93 -13
- package/src/LinkPaginator.tsx +9 -6
- package/src/Loading.stories.tsx +26 -6
- package/src/Logo.stories.tsx +44 -13
- package/src/Notification.stories.tsx +111 -23
- package/src/OverviewPageActions.tsx +5 -5
- package/src/Paginator.test.tsx +115 -94
- package/src/PdfViewer.stories.tsx +83 -5
- package/src/PreformattedCodeBlock.stories.tsx +97 -26
- package/src/ProtectedRoute.tsx +14 -39
- package/src/SmallLoadingSpinner.stories.tsx +26 -6
- package/src/SyntaxHighlighter.stories.tsx +122 -40
- package/src/SyntaxHighlighterRenderer.tsx +7 -7
- package/src/Tag.stories.tsx +135 -18
- package/src/Tag.tsx +2 -1
- package/src/Tooltip.stories.tsx +147 -34
- package/src/__snapshots__/storyshots.test.ts.snap +6 -112
- package/src/avatar/Avatar.ts +1 -1
- package/src/avatar/AvatarImage.tsx +1 -1
- package/src/avatar/AvatarWrapper.tsx +2 -2
- package/src/buttons/Button.stories.tsx +159 -0
- package/src/buttons/Button.tsx +5 -5
- package/src/config/ConfigurationBinder.tsx +39 -39
- package/src/config/ConfigurationForm.tsx +2 -1
- package/src/config/TitledSettings.tsx +4 -1
- package/src/forms/AddKeyValueEntryToTableField.stories.tsx +60 -25
- package/src/forms/Checkbox.stories.tsx +165 -27
- package/src/forms/Checkbox.tsx +1 -0
- package/src/forms/DropDown.stories.tsx +81 -35
- package/src/forms/FileInput.stories.tsx +85 -10
- package/src/forms/InputField.stories.tsx +210 -37
- package/src/forms/InputField.tsx +1 -0
- package/src/forms/Radio.stories.tsx +181 -34
- package/src/forms/Radio.tsx +1 -0
- package/src/forms/Select.stories.tsx +266 -46
- package/src/forms/Select.tsx +1 -0
- package/src/forms/Textarea.stories.tsx +99 -53
- package/src/forms/Textarea.tsx +1 -0
- package/src/forms/index.ts +3 -1
- package/src/index.ts +5 -5
- package/src/jest-dom.d.ts +17 -0
- package/src/layout/Footer.stories.tsx +114 -22
- package/src/layout/FooterSection.tsx +1 -0
- package/src/layout/Header.tsx +2 -1
- package/src/layout/Page.tsx +1 -3
- package/src/layout/PrimaryContentColumn.tsx +2 -2
- package/src/layout/SecondaryNavigationColumn.tsx +3 -3
- package/src/layout/SubSubtitle.tsx +2 -1
- package/src/layout/Subtitle.tsx +2 -1
- package/src/layout/Title.tsx +2 -5
- package/src/markdown/LazyMarkdownView.tsx +16 -5
- package/src/markdown/MarkdownHeadingRenderer.test.ts +9 -1
- package/src/markdown/MarkdownHeadingRenderer.tsx +3 -3
- package/src/markdown/MarkdownImageRenderer.test.ts +8 -1
- package/src/markdown/MarkdownImageRenderer.tsx +2 -1
- package/src/markdown/MarkdownLinkRenderer.test.tsx +9 -0
- package/src/markdown/MarkdownLinkRenderer.tsx +6 -4
- package/src/markdown/MarkdownView.stories.tsx +224 -72
- package/src/markdown/markdownExtensions.ts +6 -4
- package/src/modals/ConfirmAlert.stories.tsx +133 -44
- package/src/modals/ConfirmAlert.tsx +5 -4
- package/src/modals/Modal.stories.tsx +461 -252
- package/src/modals/Modal.tsx +12 -11
- package/src/modals/useRegisterModal.test.tsx +5 -4
- package/src/navigation/MenuContext.tsx +2 -2
- package/src/navigation/NavLink.tsx +4 -7
- package/src/navigation/PrimaryNavigation.tsx +2 -2
- package/src/navigation/PrimaryNavigationLink.tsx +6 -5
- package/src/navigation/RoutingProps.ts +1 -3
- package/src/navigation/SecondaryNavigation.stories.tsx +157 -45
- package/src/navigation/SecondaryNavigation.tsx +17 -16
- package/src/navigation/SecondaryNavigationItem.tsx +2 -1
- package/src/navigation/SubNavigation.tsx +4 -8
- package/src/navigation/useActiveMatch.ts +20 -22
- package/src/navigation/useNavigationLock.ts +1 -0
- package/src/popover/Popover.stories.tsx +111 -41
- package/src/popover/Popover.tsx +2 -5
- package/src/repos/Diff.stories.tsx +418 -223
- package/src/repos/Diff.tsx +1 -5
- package/src/repos/HunkExpandDivider.tsx +2 -2
- package/src/repos/LoadingDiff.tsx +11 -6
- package/src/repos/RepositoryEntry.stories.tsx +217 -53
- package/src/repos/RepositoryFlag.tsx +2 -1
- package/src/repos/TokenizedDiffView.tsx +4 -2
- package/src/repos/annotate/Annotate.stories.tsx +225 -111
- package/src/repos/annotate/AnnotateLine.tsx +2 -1
- package/src/repos/changesets/ChangesetAuthor.tsx +2 -2
- package/src/repos/changesets/ChangesetButtonGroup.test.tsx +9 -5
- package/src/repos/changesets/ChangesetDiff.test.ts +10 -2
- package/src/repos/changesets/Changesets.stories.tsx +388 -197
- package/src/repos/changesets/ContributorRow.tsx +2 -2
- package/src/repos/changesets/SignatureIcon.tsx +1 -0
- package/src/repos/diff/DiffFileTree.tsx +1 -1
- package/src/repos/diff/styledElements.tsx +4 -3
- package/src/repos/index.ts +15 -15
- package/src/search/Hit.tsx +3 -2
- package/src/search/TextHitField.stories.tsx +131 -43
- package/src/search/TextHitField.tsx +3 -2
- package/src/search/index.ts +1 -1
- package/src/storyshots.test.ts +66 -60
- package/src/table/Table.stories.tsx +146 -48
- package/src/table/Table.tsx +7 -8
- package/src/table/TextColumn.tsx +0 -9
- package/src/toast/Toast.tsx +2 -1
- package/src/toast/ToastArea.tsx +2 -2
- package/src/toast/ToastButton.tsx +2 -1
- package/src/toast/ToastButtons.tsx +4 -2
- package/src/toast/ToastNotification.tsx +2 -1
- package/src/toast/index.stories.tsx +144 -39
- package/src/toast/index.ts +1 -1
- package/src/buttons/index.stories.tsx +0 -85
package/src/avatar/Avatar.ts
CHANGED
|
@@ -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,
|
|
@@ -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
|
+
};
|
package/src/buttons/Button.tsx
CHANGED
|
@@ -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
|
|
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)}
|
|
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
|
-
|
|
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
|
|
109
|
-
this.navLink(
|
|
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
|
|
113
|
-
this.route(
|
|
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 = ({
|
|
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
|
-
|
|
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 = ({
|
|
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
|
-
|
|
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"]> = (
|
|
199
|
+
const NamespaceNavLink: FC<extensionPoints.NamespaceRoute["props"]> = () => {
|
|
202
200
|
const [t] = useTranslation(this.i18nNamespace);
|
|
203
|
-
return this.navLink(
|
|
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
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
|
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 {
|
|
19
|
-
|
|
47
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
48
|
+
|
|
20
49
|
import AddKeyValueEntryToTableField from "./AddKeyValueEntryToTableField";
|
|
21
50
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
+
};
|