@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
|
@@ -14,12 +14,57 @@
|
|
|
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 CardColumnSmall from "./CardColumnSmall";
|
|
21
|
+
// import Icon from "./Icon";
|
|
22
|
+
// import styled from "styled-components";
|
|
23
|
+
//
|
|
24
|
+
// const Wrapper = styled.div`
|
|
25
|
+
// margin: 2rem;
|
|
26
|
+
// max-width: 400px;
|
|
27
|
+
// `;
|
|
28
|
+
//
|
|
29
|
+
// const link = "/foo/bar";
|
|
30
|
+
// const avatar = <Icon name="icons fa-2x fa-fw" alt="avatar" />;
|
|
31
|
+
// const contentLeft = <strong className="m-0">main content</strong>;
|
|
32
|
+
// const contentRight = <small>more text</small>;
|
|
33
|
+
//
|
|
34
|
+
// storiesOf("CardColumnSmall", module)
|
|
35
|
+
// .addDecorator((story) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>)
|
|
36
|
+
// .addDecorator((storyFn) => <Wrapper>{storyFn()}</Wrapper>)
|
|
37
|
+
// .add("Default", () => (
|
|
38
|
+
// <CardColumnSmall link={link} avatar={avatar} contentLeft={contentLeft} contentRight={contentRight} />
|
|
39
|
+
// ))
|
|
40
|
+
// .add("Minimal", () => <CardColumnSmall link={link} contentLeft={contentLeft} contentRight={contentRight} />)
|
|
41
|
+
// .add("Task", () => (
|
|
42
|
+
// <CardColumnSmall
|
|
43
|
+
// link={link}
|
|
44
|
+
// avatar={<Icon name="exchange-alt" className="fa-fw fa-lg" color="inherit" alt="avatar" />}
|
|
45
|
+
// contentLeft={<strong>Repository created</strong>}
|
|
46
|
+
// contentRight={<small>over 42 years ago</small>}
|
|
47
|
+
// footer="New: scmadmin/spaceship"
|
|
48
|
+
// />
|
|
49
|
+
// ))
|
|
50
|
+
// .add("Linkless", () => (
|
|
51
|
+
// <CardColumnSmall
|
|
52
|
+
// avatar={<Icon name="eraser" className="fa-fw fa-lg" color="inherit" alt="avatar" />}
|
|
53
|
+
// contentLeft={<strong>Repository deleted</strong>}
|
|
54
|
+
// contentRight={<small>over 1337 minutes ago</small>}
|
|
55
|
+
// footer="Deleted: scmadmin/spaceship"
|
|
56
|
+
// />
|
|
57
|
+
// ));
|
|
58
|
+
|
|
17
59
|
import React from "react";
|
|
18
|
-
import
|
|
19
|
-
import {
|
|
60
|
+
import styled from "styled-components";
|
|
61
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
62
|
+
|
|
20
63
|
import CardColumnSmall from "./CardColumnSmall";
|
|
21
64
|
import Icon from "./Icon";
|
|
22
|
-
import
|
|
65
|
+
import { MemoryRouter } from "react-router-dom";
|
|
66
|
+
|
|
67
|
+
// --- Mock Data and Helpers (preserved from your original story) ---
|
|
23
68
|
|
|
24
69
|
const Wrapper = styled.div`
|
|
25
70
|
margin: 2rem;
|
|
@@ -31,27 +76,57 @@ const avatar = <Icon name="icons fa-2x fa-fw" alt="avatar" />;
|
|
|
31
76
|
const contentLeft = <strong className="m-0">main content</strong>;
|
|
32
77
|
const contentRight = <small>more text</small>;
|
|
33
78
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
79
|
+
// --- Storybook Metadata ---
|
|
80
|
+
|
|
81
|
+
const meta: Meta<typeof CardColumnSmall> = {
|
|
82
|
+
title: "Components/CardColumnSmall",
|
|
83
|
+
component: CardColumnSmall,
|
|
84
|
+
// The Wrapper is now a decorator in the meta object
|
|
85
|
+
decorators: [
|
|
86
|
+
(Story) => <MemoryRouter initialEntries={["/"]}>{Story()}</MemoryRouter>,
|
|
87
|
+
(Story) => <Wrapper>{Story()}</Wrapper>],
|
|
88
|
+
tags: ["autodocs"], // Enables automatic documentation generation
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export default meta;
|
|
92
|
+
|
|
93
|
+
// --- Story Definitions ---
|
|
94
|
+
|
|
95
|
+
type Story = StoryObj<typeof meta>;
|
|
96
|
+
|
|
97
|
+
export const Default: Story = {
|
|
98
|
+
args: {
|
|
99
|
+
// Props for the component go into the `args` object
|
|
100
|
+
link: link,
|
|
101
|
+
avatar: avatar,
|
|
102
|
+
contentLeft: contentLeft,
|
|
103
|
+
contentRight: contentRight,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export const Minimal: Story = {
|
|
108
|
+
args: {
|
|
109
|
+
link: link,
|
|
110
|
+
contentLeft: contentLeft,
|
|
111
|
+
contentRight: contentRight,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export const Task: Story = {
|
|
116
|
+
args: {
|
|
117
|
+
link: link,
|
|
118
|
+
avatar: <Icon name="exchange-alt" className="fa-fw fa-lg" color="inherit" alt="avatar" />,
|
|
119
|
+
contentLeft: <strong>Repository created</strong>,
|
|
120
|
+
contentRight: <small>over 42 years ago</small>,
|
|
121
|
+
footer: "New: scmadmin/spaceship",
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export const Linkless: Story = {
|
|
126
|
+
args: {
|
|
127
|
+
avatar: <Icon name="eraser" className="fa-fw fa-lg" color="inherit" alt="avatar" />,
|
|
128
|
+
contentLeft: <strong>Repository deleted</strong>,
|
|
129
|
+
contentRight: <small>over 1337 minutes ago</small>,
|
|
130
|
+
footer: "Deleted: scmadmin/spaceship",
|
|
131
|
+
},
|
|
132
|
+
};
|
|
@@ -14,10 +14,10 @@
|
|
|
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
|
|
|
20
|
-
const CommaSeparatedList: FC = ({ children }) => {
|
|
20
|
+
const CommaSeparatedList: FC<{ children: ReactNode }> = ({ children }) => {
|
|
21
21
|
const [t] = useTranslation("commons");
|
|
22
22
|
const childArray = React.Children.toArray(children);
|
|
23
23
|
return (
|
package/src/Date.stories.tsx
CHANGED
|
@@ -14,10 +14,47 @@
|
|
|
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 { storiesOf } from "@storybook/react";
|
|
19
|
+
// import DateFromNow from "./DateFromNow";
|
|
20
|
+
// import DateShort from "./DateShort";
|
|
21
|
+
//
|
|
22
|
+
// const baseProps = {
|
|
23
|
+
// timeZone: "Europe/Berlin",
|
|
24
|
+
// baseDate: "2019-10-12T13:56:42+02:00",
|
|
25
|
+
// };
|
|
26
|
+
//
|
|
27
|
+
// const dates = [
|
|
28
|
+
// "2009-06-30T18:30:00+02:00",
|
|
29
|
+
// "2019-06-30T18:30:00+02:00",
|
|
30
|
+
// "2019-10-12T13:56:40+02:00",
|
|
31
|
+
// "2019-10-11T13:56:40+02:00",
|
|
32
|
+
// ];
|
|
33
|
+
//
|
|
34
|
+
// storiesOf("Date", module)
|
|
35
|
+
// .add("Date from now", () => (
|
|
36
|
+
// <div className="p-5">
|
|
37
|
+
// {dates.map((d) => (
|
|
38
|
+
// <p>
|
|
39
|
+
// <DateFromNow date={d} {...baseProps} />
|
|
40
|
+
// </p>
|
|
41
|
+
// ))}
|
|
42
|
+
// </div>
|
|
43
|
+
// ))
|
|
44
|
+
// .add("Short", () => (
|
|
45
|
+
// <div className="p-5">
|
|
46
|
+
// {dates.map((d) => (
|
|
47
|
+
// <p>
|
|
48
|
+
// <DateShort date={d} {...baseProps} />
|
|
49
|
+
// </p>
|
|
50
|
+
// ))}
|
|
51
|
+
// </div>
|
|
52
|
+
// ));
|
|
53
|
+
|
|
17
54
|
import React from "react";
|
|
18
|
-
import {
|
|
55
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
56
|
+
|
|
19
57
|
import DateFromNow from "./DateFromNow";
|
|
20
|
-
import DateShort from "./DateShort";
|
|
21
58
|
|
|
22
59
|
const baseProps = {
|
|
23
60
|
timeZone: "Europe/Berlin",
|
|
@@ -31,22 +68,32 @@ const dates = [
|
|
|
31
68
|
"2019-10-11T13:56:40+02:00",
|
|
32
69
|
];
|
|
33
70
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
71
|
+
const meta: Meta<typeof DateFromNow> = {
|
|
72
|
+
title: "Components/DateFromNow",
|
|
73
|
+
component: DateFromNow,
|
|
74
|
+
decorators: [(Story) => <div className="p-5">{Story()}</div>],
|
|
75
|
+
tags: ["autodocs"],
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export default meta;
|
|
79
|
+
|
|
80
|
+
type Story = StoryObj<typeof meta>;
|
|
81
|
+
|
|
82
|
+
export const Primary: Story = {
|
|
83
|
+
args: {
|
|
84
|
+
date: dates[3],
|
|
85
|
+
...baseProps,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const AllExamples: Story = {
|
|
90
|
+
render: () => (
|
|
91
|
+
<div>
|
|
92
|
+
{dates.map((d, i) => (
|
|
93
|
+
<p key={i}>
|
|
39
94
|
<DateFromNow date={d} {...baseProps} />
|
|
40
95
|
</p>
|
|
41
96
|
))}
|
|
42
97
|
</div>
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
<div className="p-5">
|
|
46
|
-
{dates.map((d) => (
|
|
47
|
-
<p>
|
|
48
|
-
<DateShort date={d} {...baseProps} />
|
|
49
|
-
</p>
|
|
50
|
-
))}
|
|
51
|
-
</div>
|
|
52
|
-
));
|
|
98
|
+
),
|
|
99
|
+
};
|
package/src/Duration.stories.tsx
CHANGED
|
@@ -14,50 +14,97 @@
|
|
|
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 Duration from "./Duration";
|
|
17
|
+
// import { storiesOf } from "@storybook/react";
|
|
18
|
+
// import Duration from "./Duration";
|
|
19
|
+
// import React from "react";
|
|
20
|
+
//
|
|
21
|
+
// storiesOf("Duration", module).add("Duration", () => (
|
|
22
|
+
// <div className="m-5 p-5">
|
|
23
|
+
// <p>
|
|
24
|
+
// <Duration duration={1} />
|
|
25
|
+
// </p>
|
|
26
|
+
// <p>
|
|
27
|
+
// <Duration duration={500} />
|
|
28
|
+
// </p>
|
|
29
|
+
// <p>
|
|
30
|
+
// <Duration duration={1000 + 1} />
|
|
31
|
+
// </p>
|
|
32
|
+
// <p>
|
|
33
|
+
// <Duration duration={2000} />
|
|
34
|
+
// </p>
|
|
35
|
+
// <p>
|
|
36
|
+
// <Duration duration={1000 * 60 + 1} />
|
|
37
|
+
// </p>
|
|
38
|
+
// <p>
|
|
39
|
+
// <Duration duration={42 * 1000 * 60} />
|
|
40
|
+
// </p>
|
|
41
|
+
// <p>
|
|
42
|
+
// <Duration duration={1000 * 60 * 60 + 1} />
|
|
43
|
+
// </p>
|
|
44
|
+
// <p>
|
|
45
|
+
// <Duration duration={21 * 1000 * 60 * 60} />
|
|
46
|
+
// </p>
|
|
47
|
+
// <p>
|
|
48
|
+
// <Duration duration={1000 * 60 * 60 * 24 + 1} />
|
|
49
|
+
// </p>
|
|
50
|
+
// <p>
|
|
51
|
+
// <Duration duration={5 * 1000 * 60 * 60 * 24} />
|
|
52
|
+
// </p>
|
|
53
|
+
// <p>
|
|
54
|
+
// <Duration duration={1000 * 60 * 60 * 24 * 7 + 1} />
|
|
55
|
+
// </p>
|
|
56
|
+
// <p>
|
|
57
|
+
// <Duration duration={3 * 1000 * 60 * 60 * 24 * 7} />
|
|
58
|
+
// </p>
|
|
59
|
+
// <p>
|
|
60
|
+
// <Duration duration={12 * 1000 * 60 * 60 * 24} />
|
|
61
|
+
// </p>
|
|
62
|
+
// </div>
|
|
63
|
+
// ));
|
|
64
|
+
|
|
19
65
|
import React from "react";
|
|
66
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
67
|
+
|
|
68
|
+
import DateShort from "./DateShort";
|
|
69
|
+
|
|
70
|
+
const baseProps = {
|
|
71
|
+
timeZone: "Europe/Berlin",
|
|
72
|
+
baseDate: "2019-10-12T13:56:42+02:00",
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const dates = [
|
|
76
|
+
"2009-06-30T18:30:00+02:00",
|
|
77
|
+
"2019-06-30T18:30:00+02:00",
|
|
78
|
+
"2019-10-12T13:56:40+02:00",
|
|
79
|
+
"2019-10-11T13:56:40+02:00",
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const meta: Meta<typeof DateShort> = {
|
|
83
|
+
title: "Components/DateShort",
|
|
84
|
+
component: DateShort,
|
|
85
|
+
decorators: [(Story) => <div className="p-5">{Story()}</div>],
|
|
86
|
+
tags: ["autodocs"],
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export default meta;
|
|
90
|
+
|
|
91
|
+
type Story = StoryObj<typeof meta>;
|
|
92
|
+
|
|
93
|
+
export const Primary: Story = {
|
|
94
|
+
args: {
|
|
95
|
+
date: dates[3],
|
|
96
|
+
...baseProps,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
20
99
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
<p>
|
|
33
|
-
<Duration duration={2000} />
|
|
34
|
-
</p>
|
|
35
|
-
<p>
|
|
36
|
-
<Duration duration={1000 * 60 + 1} />
|
|
37
|
-
</p>
|
|
38
|
-
<p>
|
|
39
|
-
<Duration duration={42 * 1000 * 60} />
|
|
40
|
-
</p>
|
|
41
|
-
<p>
|
|
42
|
-
<Duration duration={1000 * 60 * 60 + 1} />
|
|
43
|
-
</p>
|
|
44
|
-
<p>
|
|
45
|
-
<Duration duration={21 * 1000 * 60 * 60} />
|
|
46
|
-
</p>
|
|
47
|
-
<p>
|
|
48
|
-
<Duration duration={1000 * 60 * 60 * 24 + 1} />
|
|
49
|
-
</p>
|
|
50
|
-
<p>
|
|
51
|
-
<Duration duration={5 * 1000 * 60 * 60 * 24} />
|
|
52
|
-
</p>
|
|
53
|
-
<p>
|
|
54
|
-
<Duration duration={1000 * 60 * 60 * 24 * 7 + 1} />
|
|
55
|
-
</p>
|
|
56
|
-
<p>
|
|
57
|
-
<Duration duration={3 * 1000 * 60 * 60 * 24 * 7} />
|
|
58
|
-
</p>
|
|
59
|
-
<p>
|
|
60
|
-
<Duration duration={12 * 1000 * 60 * 60 * 24} />
|
|
61
|
-
</p>
|
|
62
|
-
</div>
|
|
63
|
-
));
|
|
100
|
+
export const AllExamples: Story = {
|
|
101
|
+
render: () => (
|
|
102
|
+
<div>
|
|
103
|
+
{dates.map((d, i) => (
|
|
104
|
+
<p key={i}>
|
|
105
|
+
<DateShort date={d} {...baseProps} />
|
|
106
|
+
</p>
|
|
107
|
+
))}
|
|
108
|
+
</div>
|
|
109
|
+
),
|
|
110
|
+
};
|
package/src/ErrorBoundary.tsx
CHANGED
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
* along with this program. If not, see https://www.gnu.org/licenses/.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import React, { FC, ReactNode, useEffect } from "react";
|
|
18
|
-
import {
|
|
17
|
+
import React, { ErrorInfo, FC, ReactNode, useEffect } from "react";
|
|
18
|
+
import { useLocation } from "react-router-dom";
|
|
19
|
+
import { ErrorBoundary as ReactErrorBoundary, FallbackProps } from "react-error-boundary";
|
|
19
20
|
import { useTranslation } from "react-i18next";
|
|
20
21
|
import classNames from "classnames";
|
|
21
22
|
import styled from "styled-components";
|
|
@@ -26,37 +27,31 @@ import ErrorPage from "./ErrorPage";
|
|
|
26
27
|
import { Subtitle, Title } from "./layout";
|
|
27
28
|
import Icon from "./Icon";
|
|
28
29
|
|
|
29
|
-
type
|
|
30
|
-
error
|
|
31
|
-
errorInfo?: ErrorInfo;
|
|
30
|
+
type ErrorState = {
|
|
31
|
+
error: Error;
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
type ExportedProps = {
|
|
35
|
-
fallback?: React.ComponentType<
|
|
35
|
+
fallback?: React.ComponentType<ErrorState>;
|
|
36
36
|
children: ReactNode;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
type Props = RouteComponentProps & ExportedProps;
|
|
40
|
-
|
|
41
|
-
type ErrorInfo = {
|
|
42
|
-
componentStack: string;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
39
|
type ErrorDisplayProps = {
|
|
46
|
-
fallback?: React.ComponentType<State>;
|
|
47
40
|
error: Error;
|
|
48
|
-
errorInfo
|
|
41
|
+
// react-error-boundary does not pass errorInfo to the fallback component by default
|
|
42
|
+
// for performance reasons, but it can be logged via the `onError` prop.
|
|
43
|
+
// We keep it in the signature for compatibility with the custom fallback prop.
|
|
44
|
+
errorInfo?: { componentStack: string };
|
|
45
|
+
fallback?: React.ComponentType<ErrorState>;
|
|
49
46
|
};
|
|
50
47
|
|
|
51
48
|
const RedirectIconContainer = styled.div`
|
|
52
49
|
min-height: 256px;
|
|
53
50
|
`;
|
|
54
51
|
|
|
55
|
-
const RedirectPage = () => {
|
|
52
|
+
const RedirectPage: FC = () => {
|
|
56
53
|
const [t] = useTranslation("commons");
|
|
57
54
|
useDocumentTitle(t("errorNotification.prefix"));
|
|
58
|
-
// we use an icon instead of loading spinner,
|
|
59
|
-
// because a redirect is synchron and a spinner does not spin on a synchron action
|
|
60
55
|
return (
|
|
61
56
|
<section className="section">
|
|
62
57
|
<div className="container">
|
|
@@ -82,6 +77,7 @@ const ErrorDisplay: FC<ErrorDisplayProps> = ({ error, errorInfo, fallback: Fallb
|
|
|
82
77
|
const [t] = useTranslation("commons");
|
|
83
78
|
const location = useLocation();
|
|
84
79
|
const isMissingLink = error instanceof MissingLinkError;
|
|
80
|
+
|
|
85
81
|
useEffect(() => {
|
|
86
82
|
if (isMissingLink && loginLink) {
|
|
87
83
|
window.location.assign(urls.withContextPath("/login?from=" + location.pathname));
|
|
@@ -90,20 +86,11 @@ const ErrorDisplay: FC<ErrorDisplayProps> = ({ error, errorInfo, fallback: Fallb
|
|
|
90
86
|
|
|
91
87
|
if (isMissingLink) {
|
|
92
88
|
if (loginLink) {
|
|
93
|
-
// we can render a loading screen,
|
|
94
|
-
// because the effect hook above should redirect
|
|
95
89
|
return <RedirectPage />;
|
|
96
|
-
} else {
|
|
97
|
-
// missing link error without login link means we have no permissions
|
|
98
|
-
// and we should render an error
|
|
99
|
-
return (
|
|
100
|
-
<ErrorPage error={error} title={t("errorNotification.prefix")} subtitle={t("errorNotification.forbidden")} />
|
|
101
|
-
);
|
|
102
90
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return <ErrorNotification error={error} />;
|
|
91
|
+
return (
|
|
92
|
+
<ErrorPage error={error} title={t("errorNotification.prefix")} subtitle={t("errorNotification.forbidden")} />
|
|
93
|
+
);
|
|
107
94
|
}
|
|
108
95
|
|
|
109
96
|
const fallbackProps = {
|
|
@@ -111,37 +98,30 @@ const ErrorDisplay: FC<ErrorDisplayProps> = ({ error, errorInfo, fallback: Fallb
|
|
|
111
98
|
errorInfo,
|
|
112
99
|
};
|
|
113
100
|
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
class ErrorBoundary extends React.Component<Props, State> {
|
|
118
|
-
constructor(props: Props) {
|
|
119
|
-
super(props);
|
|
120
|
-
this.state = {};
|
|
101
|
+
if (FallbackComponent) {
|
|
102
|
+
return <FallbackComponent {...fallbackProps} />;
|
|
121
103
|
}
|
|
104
|
+
return <ErrorNotification error={error} />;
|
|
105
|
+
};
|
|
122
106
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (this.state.error && prevProps.location !== this.props.location) {
|
|
126
|
-
this.setState({ error: undefined, errorInfo: undefined });
|
|
127
|
-
}
|
|
128
|
-
}
|
|
107
|
+
const ErrorBoundary: FC<ExportedProps> = ({ children, fallback }) => {
|
|
108
|
+
const location = useLocation();
|
|
129
109
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
error
|
|
133
|
-
errorInfo
|
|
134
|
-
|
|
135
|
-
|
|
110
|
+
const ErrorBoundaryFallback = ({ error }: FallbackProps) => (
|
|
111
|
+
<ErrorDisplay
|
|
112
|
+
error={error}
|
|
113
|
+
// For compatibility with potential custom fallbacks that expect errorInfo.
|
|
114
|
+
// The actual component stack is available in the `onError` callback below.
|
|
115
|
+
errorInfo={{ componentStack: "" }}
|
|
116
|
+
fallback={fallback}
|
|
117
|
+
/>
|
|
118
|
+
);
|
|
136
119
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return this.props.children;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
120
|
+
return (
|
|
121
|
+
<ReactErrorBoundary resetKeys={[location.pathname]} FallbackComponent={ErrorBoundaryFallback}>
|
|
122
|
+
{children}
|
|
123
|
+
</ReactErrorBoundary>
|
|
124
|
+
);
|
|
125
|
+
};
|
|
146
126
|
|
|
147
|
-
export default
|
|
127
|
+
export default ErrorBoundary;
|
|
@@ -19,7 +19,7 @@ import { useTranslation } from "react-i18next";
|
|
|
19
19
|
import { BackendError, ForbiddenError, UnauthorizedError, urls } from "@scm-manager/ui-api";
|
|
20
20
|
import Notification from "./Notification";
|
|
21
21
|
import BackendErrorNotification from "./BackendErrorNotification";
|
|
22
|
-
import { useLocation } from "react-router
|
|
22
|
+
import { useLocation } from "react-router";
|
|
23
23
|
|
|
24
24
|
type Props = ComponentProps<typeof BasicErrorMessage> & {
|
|
25
25
|
error?: Error | null;
|
package/src/Help.stories.tsx
CHANGED
|
@@ -14,8 +14,35 @@
|
|
|
14
14
|
* along with this program. If not, see https://www.gnu.org/licenses/.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import * as React from "react";
|
|
18
|
-
import { storiesOf } from "@storybook/react";
|
|
17
|
+
// import * as React from "react";
|
|
18
|
+
// import { storiesOf } from "@storybook/react";
|
|
19
|
+
// import Help from "./Help";
|
|
20
|
+
//
|
|
21
|
+
// const longContent =
|
|
22
|
+
// "Cleverness nuclear genuine static irresponsibility invited President Zaphod\n" +
|
|
23
|
+
// "Beeblebrox hyperspace ship. Another custard through computer-generated universe\n" +
|
|
24
|
+
// "shapes field strong disaster parties Russell’s ancestors infinite colour\n" +
|
|
25
|
+
// "imaginative generator sweep.";
|
|
26
|
+
//
|
|
27
|
+
// storiesOf("Help", module)
|
|
28
|
+
// .addDecorator((storyFn) => <div className="m-6">{storyFn()}</div>)
|
|
29
|
+
// .add("Default", () => <Help message="This is a help message" />)
|
|
30
|
+
// .add("Multiline", () => (
|
|
31
|
+
// <>
|
|
32
|
+
// <div className="mt-4">
|
|
33
|
+
// <label>With multiline (default):</label>
|
|
34
|
+
// <Help message={longContent} />
|
|
35
|
+
// </div>
|
|
36
|
+
// <div className="mt-4">
|
|
37
|
+
// <label>Without multiline:</label>
|
|
38
|
+
// <Help message={longContent} multiline={false} />
|
|
39
|
+
// </div>
|
|
40
|
+
// </>
|
|
41
|
+
// ));
|
|
42
|
+
|
|
43
|
+
import React from "react";
|
|
44
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
45
|
+
|
|
19
46
|
import Help from "./Help";
|
|
20
47
|
|
|
21
48
|
const longContent =
|
|
@@ -24,18 +51,35 @@ const longContent =
|
|
|
24
51
|
"shapes field strong disaster parties Russell’s ancestors infinite colour\n" +
|
|
25
52
|
"imaginative generator sweep.";
|
|
26
53
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
54
|
+
const meta: Meta<typeof Help> = {
|
|
55
|
+
title: "Components/Help",
|
|
56
|
+
component: Help,
|
|
57
|
+
decorators: [(Story) => <div className="m-6">{Story()}</div>],
|
|
58
|
+
parameters: {
|
|
59
|
+
layout: "centered",
|
|
60
|
+
},
|
|
61
|
+
tags: ["autodocs"],
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export default meta;
|
|
65
|
+
|
|
66
|
+
type Story = StoryObj<typeof meta>;
|
|
67
|
+
|
|
68
|
+
export const Default: Story = {
|
|
69
|
+
args: {
|
|
70
|
+
message: "This is a help message",
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const Multiline: Story = {
|
|
75
|
+
args: {
|
|
76
|
+
message: longContent,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const SingleLine: Story = {
|
|
81
|
+
args: {
|
|
82
|
+
message: longContent,
|
|
83
|
+
multiline: false,
|
|
84
|
+
},
|
|
85
|
+
};
|
package/src/Help.tsx
CHANGED
|
@@ -26,19 +26,13 @@ type Props = {
|
|
|
26
26
|
id?: string;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
multiline={multiline}
|
|
35
|
-
>
|
|
29
|
+
/**
|
|
30
|
+
* @deprecated Please import the identical module from "@scm-manager/ui-core"
|
|
31
|
+
*/
|
|
32
|
+
const Help: FC<Props> = ({ message, multiline = true, className, id }) => (
|
|
33
|
+
<Tooltip className={classNames("is-inline-block", "pl-1", className)} message={message} id={id} multiline={multiline}>
|
|
36
34
|
<HelpIcon />
|
|
37
35
|
</Tooltip>
|
|
38
36
|
);
|
|
39
37
|
|
|
40
|
-
Help.defaultProps = {
|
|
41
|
-
multiline: true
|
|
42
|
-
};
|
|
43
|
-
|
|
44
38
|
export default Help;
|