@scm-manager/ui-components 3.10.4-20250824-132529 → 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.
- package/package.json +45 -50
- 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/Image.tsx +2 -3
- 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 +21 -144
- package/src/avatar/Avatar.ts +1 -1
- package/src/avatar/AvatarImage.tsx +2 -2
- 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/Footer.tsx +11 -7
- 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/ActiveModalCountContextProvider.tsx +2 -2
- 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/DiffTypes.ts +2 -14
- 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 +4 -4
- 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/Contributor.tsx +1 -1
- package/src/repos/changesets/ContributorRow.tsx +2 -2
- package/src/repos/changesets/SignatureIcon.tsx +1 -0
- package/src/repos/changesets/SingleChangeset.tsx +0 -1
- package/src/repos/diff/DiffFileTree.tsx +37 -89
- 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,99 @@
|
|
|
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 Table from "./Table";
|
|
20
|
+
// import Column from "./Column";
|
|
21
|
+
// import TextColumn from "./TextColumn";
|
|
22
|
+
// import styled from "styled-components";
|
|
23
|
+
//
|
|
24
|
+
// const StyledTable = styled(Table)`
|
|
25
|
+
// width: 400px;
|
|
26
|
+
// border: 1px dashed black;
|
|
27
|
+
// padding: 4px;
|
|
28
|
+
// margin: 4px;
|
|
29
|
+
// td {
|
|
30
|
+
// word-break: break-word;
|
|
31
|
+
// }
|
|
32
|
+
// `;
|
|
33
|
+
//
|
|
34
|
+
// storiesOf("Table", module)
|
|
35
|
+
// .add("Default", () => (
|
|
36
|
+
// <Table
|
|
37
|
+
// data={[
|
|
38
|
+
// { firstname: "Tricia", lastname: "McMillan", email: "tricia@hitchhiker.com" },
|
|
39
|
+
// { firstname: "Arthur", lastname: "Dent", email: "arthur@hitchhiker.com" },
|
|
40
|
+
// ]}
|
|
41
|
+
// >
|
|
42
|
+
// <Column header={"First Name"}>{(row: any) => <h4>{row.firstname}</h4>}</Column>
|
|
43
|
+
// <Column
|
|
44
|
+
// className="has-background-success"
|
|
45
|
+
// header={"Last Name"}
|
|
46
|
+
// createComparator={() => {
|
|
47
|
+
// return (a: any, b: any) => {
|
|
48
|
+
// if (a.lastname > b.lastname) {
|
|
49
|
+
// return -1;
|
|
50
|
+
// } else if (a.lastname < b.lastname) {
|
|
51
|
+
// return 1;
|
|
52
|
+
// } else {
|
|
53
|
+
// return 0;
|
|
54
|
+
// }
|
|
55
|
+
// };
|
|
56
|
+
// }}
|
|
57
|
+
// >
|
|
58
|
+
// {(row: any) => <b className="has-text-danger">{row.lastname}</b>}
|
|
59
|
+
// </Column>
|
|
60
|
+
// <Column header={"E-Mail"}>{(row: any) => <span>{row.email}</span>}</Column>
|
|
61
|
+
// </Table>
|
|
62
|
+
// ))
|
|
63
|
+
// .add("TextColumn", () => (
|
|
64
|
+
// <Table
|
|
65
|
+
// data={[
|
|
66
|
+
// { id: "21", title: "Pommes", desc: "Fried potato sticks" },
|
|
67
|
+
// { id: "42", title: "Quarter-Pounder", desc: "Big burger" },
|
|
68
|
+
// { id: "-84", title: "Icecream", desc: "Cold dessert" },
|
|
69
|
+
// ]}
|
|
70
|
+
// >
|
|
71
|
+
// <TextColumn header="Id" dataKey="id" />
|
|
72
|
+
// <TextColumn header="Name" dataKey="title" />
|
|
73
|
+
// <TextColumn header="Description" dataKey="desc" />
|
|
74
|
+
// </Table>
|
|
75
|
+
// ))
|
|
76
|
+
// .add("Empty", () => (
|
|
77
|
+
// <Table data={[]} emptyMessage="No data found.">
|
|
78
|
+
// <TextColumn header="Id" dataKey="id" />
|
|
79
|
+
// <TextColumn header="Name" dataKey="name" />
|
|
80
|
+
// </Table>
|
|
81
|
+
// ))
|
|
82
|
+
// .add("Table with Word-Break", () => (
|
|
83
|
+
// <StyledTable
|
|
84
|
+
// data={[
|
|
85
|
+
// {
|
|
86
|
+
// id: "42",
|
|
87
|
+
// name: "herp_derp_schlerp_ferp_gerp_nerp_terp_ierp_perp_lerp_merp_oerp_zerp_serp_verp_herp",
|
|
88
|
+
// },
|
|
89
|
+
// {
|
|
90
|
+
// id: "17",
|
|
91
|
+
// name: "herp_derp_schlerp_ferp_gerp_nerp_terp_ierp_perp_lerp_merp_oerp_zerp_serp_verp",
|
|
92
|
+
// },
|
|
93
|
+
// ]}
|
|
94
|
+
// emptyMessage="No data found."
|
|
95
|
+
// >
|
|
96
|
+
// <TextColumn header="Id" dataKey="id" />
|
|
97
|
+
// <TextColumn header="Name" dataKey="name" />
|
|
98
|
+
// </StyledTable>
|
|
99
|
+
// ));
|
|
100
|
+
|
|
17
101
|
import React from "react";
|
|
18
|
-
import
|
|
102
|
+
import styled from "styled-components";
|
|
103
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
104
|
+
|
|
19
105
|
import Table from "./Table";
|
|
20
106
|
import Column from "./Column";
|
|
21
107
|
import TextColumn from "./TextColumn";
|
|
22
|
-
|
|
108
|
+
|
|
109
|
+
// --- Helper Components & Mock Data (preserved from your original story) ---
|
|
23
110
|
|
|
24
111
|
const StyledTable = styled(Table)`
|
|
25
112
|
width: 400px;
|
|
@@ -31,69 +118,80 @@ const StyledTable = styled(Table)`
|
|
|
31
118
|
}
|
|
32
119
|
`;
|
|
33
120
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
121
|
+
const tableData = [
|
|
122
|
+
{ firstname: "Tricia", lastname: "McMillan", email: "tricia@hitchhiker.com" },
|
|
123
|
+
{ firstname: "Arthur", lastname: "Dent", email: "arthur@hitchhiker.com" },
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
const textColumnData = [
|
|
127
|
+
{ id: "21", title: "Pommes", desc: "Fried potato sticks" },
|
|
128
|
+
{ id: "42", title: "Quarter-Pounder", desc: "Big burger" },
|
|
129
|
+
{ id: "-84", title: "Icecream", desc: "Cold dessert" },
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
const wordBreakData = [
|
|
133
|
+
{ id: "42", name: "herp_derp_schlerp_ferp_gerp_nerp_terp_ierp_perp_lerp_merp_oerp_zerp_serp_verp_herp" },
|
|
134
|
+
{ id: "17", name: "herp_derp_schlerp_ferp_gerp_nerp_terp_ierp_perp_lerp_merp_oerp_zerp_serp_verp" },
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
// --- Storybook Metadata ---
|
|
138
|
+
|
|
139
|
+
const meta: Meta<typeof Table> = {
|
|
140
|
+
title: "Components/Table",
|
|
141
|
+
component: Table,
|
|
142
|
+
decorators: [(Story) => <div style={{ margin: "2rem" }}>{Story()}</div>],
|
|
143
|
+
tags: ["autodocs"],
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export default meta;
|
|
147
|
+
|
|
148
|
+
// --- Story Definitions ---
|
|
149
|
+
|
|
150
|
+
type Story = StoryObj<typeof meta>;
|
|
151
|
+
|
|
152
|
+
// For stories with complex children or custom components, we use a `render` function.
|
|
153
|
+
export const Default: Story = {
|
|
154
|
+
render: () => (
|
|
155
|
+
<Table data={tableData}>
|
|
42
156
|
<Column header={"First Name"}>{(row: any) => <h4>{row.firstname}</h4>}</Column>
|
|
43
157
|
<Column
|
|
44
158
|
className="has-background-success"
|
|
45
159
|
header={"Last Name"}
|
|
46
|
-
createComparator={() =>
|
|
47
|
-
return (a: any, b: any) => {
|
|
48
|
-
if (a.lastname > b.lastname) {
|
|
49
|
-
return -1;
|
|
50
|
-
} else if (a.lastname < b.lastname) {
|
|
51
|
-
return 1;
|
|
52
|
-
} else {
|
|
53
|
-
return 0;
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
}}
|
|
160
|
+
createComparator={() => (a: any, b: any) => b.lastname.localeCompare(a.lastname)}
|
|
57
161
|
>
|
|
58
162
|
{(row: any) => <b className="has-text-danger">{row.lastname}</b>}
|
|
59
163
|
</Column>
|
|
60
164
|
<Column header={"E-Mail"}>{(row: any) => <span>{row.email}</span>}</Column>
|
|
61
165
|
</Table>
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
]}
|
|
70
|
-
>
|
|
166
|
+
),
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
export const WithTextColumn: Story = {
|
|
170
|
+
name: "With TextColumn",
|
|
171
|
+
render: () => (
|
|
172
|
+
<Table data={textColumnData}>
|
|
71
173
|
<TextColumn header="Id" dataKey="id" />
|
|
72
174
|
<TextColumn header="Name" dataKey="title" />
|
|
73
175
|
<TextColumn header="Description" dataKey="desc" />
|
|
74
176
|
</Table>
|
|
75
|
-
)
|
|
76
|
-
|
|
177
|
+
),
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export const Empty: Story = {
|
|
181
|
+
render: () => (
|
|
77
182
|
<Table data={[]} emptyMessage="No data found.">
|
|
78
183
|
<TextColumn header="Id" dataKey="id" />
|
|
79
184
|
<TextColumn header="Name" dataKey="name" />
|
|
80
185
|
</Table>
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
id: "17",
|
|
91
|
-
name: "herp_derp_schlerp_ferp_gerp_nerp_terp_ierp_perp_lerp_merp_oerp_zerp_serp_verp",
|
|
92
|
-
},
|
|
93
|
-
]}
|
|
94
|
-
emptyMessage="No data found."
|
|
95
|
-
>
|
|
186
|
+
),
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
export const WithWordBreak: Story = {
|
|
190
|
+
name: "With Word-Break",
|
|
191
|
+
render: () => (
|
|
192
|
+
<StyledTable data={wordBreakData} emptyMessage="No data found.">
|
|
96
193
|
<TextColumn header="Id" dataKey="id" />
|
|
97
194
|
<TextColumn header="Name" dataKey="name" />
|
|
98
195
|
</StyledTable>
|
|
99
|
-
)
|
|
196
|
+
),
|
|
197
|
+
};
|
package/src/table/Table.tsx
CHANGED
|
@@ -28,10 +28,7 @@ type Props = {
|
|
|
28
28
|
className?: string;
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
* @deprecated
|
|
33
|
-
*/
|
|
34
|
-
const Table: FC<Props> = ({ data, sortable, children, emptyMessage, className }) => {
|
|
31
|
+
const Table: FC<Props> = ({ data, sortable = true, children, emptyMessage, className }) => {
|
|
35
32
|
const [tableData, setTableData] = useState(data);
|
|
36
33
|
useEffect(() => {
|
|
37
34
|
setTableData(data);
|
|
@@ -41,12 +38,14 @@ const Table: FC<Props> = ({ data, sortable, children, emptyMessage, className })
|
|
|
41
38
|
const [hoveredColumnIndex, setHoveredColumnIndex] = useState<number | undefined>();
|
|
42
39
|
|
|
43
40
|
const isSortable = (child: ReactElement) => {
|
|
41
|
+
// @ts-ignore
|
|
44
42
|
return sortable && child.props.createComparator;
|
|
45
43
|
};
|
|
46
44
|
|
|
47
45
|
const sortFunctions: Comparator | undefined[] = [];
|
|
48
46
|
React.Children.forEach(children, (child, index) => {
|
|
49
47
|
if (child && isSortable(child as ReactElement)) {
|
|
48
|
+
// @ts-ignore
|
|
50
49
|
sortFunctions.push((child as ReactElement).props.createComparator((child as ReactElement).props, index));
|
|
51
50
|
} else {
|
|
52
51
|
sortFunctions.push(undefined);
|
|
@@ -57,6 +56,7 @@ const Table: FC<Props> = ({ data, sortable, children, emptyMessage, className })
|
|
|
57
56
|
return (
|
|
58
57
|
<tr key={rowIndex}>
|
|
59
58
|
{React.Children.map(children, (child, columnIndex) => {
|
|
59
|
+
// @ts-ignore
|
|
60
60
|
const { className: columnClassName, ...childProperties } = (child as ReactElement).props;
|
|
61
61
|
return (
|
|
62
62
|
<td className={columnClassName}>
|
|
@@ -116,6 +116,7 @@ const Table: FC<Props> = ({ data, sortable, children, emptyMessage, className })
|
|
|
116
116
|
onMouseLeave={() => setHoveredColumnIndex(undefined)}
|
|
117
117
|
key={index}
|
|
118
118
|
>
|
|
119
|
+
{/* @ts-ignore */}
|
|
119
120
|
{(child as ReactElement).props.header}
|
|
120
121
|
{isSortable(child as ReactElement) &&
|
|
121
122
|
renderSortIcon(child as ReactElement, ascending, shouldShowIcon(index))}
|
|
@@ -128,12 +129,10 @@ const Table: FC<Props> = ({ data, sortable, children, emptyMessage, className })
|
|
|
128
129
|
);
|
|
129
130
|
};
|
|
130
131
|
|
|
131
|
-
Table.defaultProps = {
|
|
132
|
-
sortable: true,
|
|
133
|
-
};
|
|
134
|
-
|
|
135
132
|
const renderSortIcon = (child: ReactElement, ascending: boolean, showIcon: boolean) => {
|
|
133
|
+
// @ts-ignore
|
|
136
134
|
if (child.props.ascendingIcon && child.props.descendingIcon) {
|
|
135
|
+
// @ts-ignore
|
|
137
136
|
return <SortIcon name={ascending ? child.props.ascendingIcon : child.props.descendingIcon} isVisible={showIcon} />;
|
|
138
137
|
} else {
|
|
139
138
|
return <SortIcon name={ascending ? "sort-amount-down-alt" : "sort-amount-down"} isVisible={showIcon} />;
|
package/src/table/TextColumn.tsx
CHANGED
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
|
|
17
17
|
import React, { FC } from "react";
|
|
18
18
|
import { ColumnProps } from "./types";
|
|
19
|
-
import comparators from "../comparators";
|
|
20
19
|
|
|
21
20
|
type Props = ColumnProps & {
|
|
22
21
|
dataKey: string;
|
|
@@ -29,12 +28,4 @@ const TextColumn: FC<Props> = ({ row, dataKey }) => {
|
|
|
29
28
|
return row[dataKey];
|
|
30
29
|
};
|
|
31
30
|
|
|
32
|
-
TextColumn.defaultProps = {
|
|
33
|
-
createComparator: (props: Props) => {
|
|
34
|
-
return comparators.byKey(props.dataKey);
|
|
35
|
-
},
|
|
36
|
-
ascendingIcon: "sort-alpha-down-alt",
|
|
37
|
-
descendingIcon: "sort-alpha-down",
|
|
38
|
-
};
|
|
39
|
-
|
|
40
31
|
export default TextColumn;
|
package/src/toast/Toast.tsx
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 React, { FC } from "react";
|
|
17
|
+
import React, { FC, ReactNode } from "react";
|
|
18
18
|
import { createPortal } from "react-dom";
|
|
19
19
|
import styled from "styled-components";
|
|
20
20
|
import { getTheme, Themeable, ToastThemeContext, Type } from "./themes";
|
|
@@ -23,6 +23,7 @@ import usePortalRootElement from "../usePortalRootElement";
|
|
|
23
23
|
type Props = {
|
|
24
24
|
type: Type;
|
|
25
25
|
title: string;
|
|
26
|
+
children?: ReactNode;
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
const Container = styled.div<Themeable>`
|
package/src/toast/ToastArea.tsx
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 React, { FC } from "react";
|
|
17
|
+
import React, { FC, ReactNode } from "react";
|
|
18
18
|
import usePortalRootElement from "../usePortalRootElement";
|
|
19
19
|
import { createPortal } from "react-dom";
|
|
20
20
|
import styled from "styled-components";
|
|
@@ -37,7 +37,7 @@ const Container = styled.div`
|
|
|
37
37
|
}
|
|
38
38
|
`;
|
|
39
39
|
|
|
40
|
-
const ToastArea: FC = ({ children }) => {
|
|
40
|
+
const ToastArea: FC<{ children?: ReactNode }> = ({ children }) => {
|
|
41
41
|
const rootElement = usePortalRootElement("toastRoot");
|
|
42
42
|
if (!rootElement) {
|
|
43
43
|
// portal not yet ready
|
|
@@ -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, useContext, MouseEvent } from "react";
|
|
17
|
+
import React, { FC, useContext, MouseEvent, ReactNode } from "react";
|
|
18
18
|
import { ToastThemeContext, Themeable } from "./themes";
|
|
19
19
|
import classNames from "classnames";
|
|
20
20
|
import styled from "styled-components";
|
|
@@ -22,6 +22,7 @@ import styled from "styled-components";
|
|
|
22
22
|
type Props = {
|
|
23
23
|
icon?: string;
|
|
24
24
|
onClick?: () => void;
|
|
25
|
+
children?: ReactNode;
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
const ThemedButton = styled.button.attrs((props) => ({
|
|
@@ -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 styled from "styled-components";
|
|
19
19
|
|
|
20
20
|
const FullWidthDiv = styled.div`
|
|
@@ -29,6 +29,8 @@ const FullWidthDiv = styled.div`
|
|
|
29
29
|
}
|
|
30
30
|
`;
|
|
31
31
|
|
|
32
|
-
const ToastButtons: FC = ({ children }) =>
|
|
32
|
+
const ToastButtons: FC<{ children?: ReactNode }> = ({ children }) => (
|
|
33
|
+
<FullWidthDiv className="is-flex pt-2">{children}</FullWidthDiv>
|
|
34
|
+
);
|
|
33
35
|
|
|
34
36
|
export default ToastButtons;
|
|
@@ -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 classNames from "classnames";
|
|
19
19
|
import styled from "styled-components";
|
|
20
20
|
import { getTheme, Themeable, ToastThemeContext, Type } from "./themes";
|
|
@@ -23,6 +23,7 @@ type Props = {
|
|
|
23
23
|
type: Type;
|
|
24
24
|
title: string;
|
|
25
25
|
close?: () => void;
|
|
26
|
+
children?: ReactNode;
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
const Container = styled.div<Themeable>`
|
|
@@ -14,8 +14,87 @@
|
|
|
14
14
|
* along with this program. If not, see https://www.gnu.org/licenses/.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
// import React, { useState } from "react";
|
|
18
|
+
// import { storiesOf } from "@storybook/react";
|
|
19
|
+
// import Toast from "./Toast";
|
|
20
|
+
// import ToastButtons from "./ToastButtons";
|
|
21
|
+
// import ToastButton from "./ToastButton";
|
|
22
|
+
// import { types } from "./themes";
|
|
23
|
+
// import ToastArea from "./ToastArea";
|
|
24
|
+
// import ToastNotification from "./ToastNotification";
|
|
25
|
+
//
|
|
26
|
+
// const toastStories = storiesOf("Toast", module);
|
|
27
|
+
//
|
|
28
|
+
// const AnimatedToast = () => (
|
|
29
|
+
// <Toast type="primary" title="Animated">
|
|
30
|
+
// Awesome animated Toast
|
|
31
|
+
// </Toast>
|
|
32
|
+
// );
|
|
33
|
+
//
|
|
34
|
+
// const Animator = () => {
|
|
35
|
+
// const [display, setDisplay] = useState(false);
|
|
36
|
+
//
|
|
37
|
+
// return (
|
|
38
|
+
// <div style={{ padding: "2rem" }}>
|
|
39
|
+
// {display && <AnimatedToast />}
|
|
40
|
+
// <button className="button is-primary" onClick={() => setDisplay(!display)}>
|
|
41
|
+
// {display ? "Close" : "Open"} Toast
|
|
42
|
+
// </button>
|
|
43
|
+
// </div>
|
|
44
|
+
// );
|
|
45
|
+
// };
|
|
46
|
+
//
|
|
47
|
+
// const Closeable = () => {
|
|
48
|
+
// const [show, setShow] = useState(true);
|
|
49
|
+
//
|
|
50
|
+
// const hide = () => {
|
|
51
|
+
// setShow(false);
|
|
52
|
+
// };
|
|
53
|
+
//
|
|
54
|
+
// if (!show) {
|
|
55
|
+
// return null;
|
|
56
|
+
// }
|
|
57
|
+
//
|
|
58
|
+
// return (
|
|
59
|
+
// <Toast type="success" title="Awesome feature">
|
|
60
|
+
// <p>Close the message with a click</p>
|
|
61
|
+
// <ToastButtons>
|
|
62
|
+
// <ToastButton icon="times" onClick={hide}>
|
|
63
|
+
// Click to close
|
|
64
|
+
// </ToastButton>
|
|
65
|
+
// </ToastButtons>
|
|
66
|
+
// </Toast>
|
|
67
|
+
// );
|
|
68
|
+
// };
|
|
69
|
+
//
|
|
70
|
+
// toastStories.add("Open/Close", () => <Animator />);
|
|
71
|
+
// toastStories.add("Click to close", () => <Closeable />);
|
|
72
|
+
//
|
|
73
|
+
// types.forEach((type) => {
|
|
74
|
+
// toastStories.add(type.charAt(0).toUpperCase() + type.slice(1), () => (
|
|
75
|
+
// <Toast type={type} title="New Changes">
|
|
76
|
+
// <p>The underlying Pull-Request has changed. Press reload to see the changes.</p>
|
|
77
|
+
// <p>Warning: Non saved modification will be lost.</p>
|
|
78
|
+
// <ToastButtons>
|
|
79
|
+
// <ToastButton icon="redo">Reload</ToastButton>
|
|
80
|
+
// <ToastButton icon="times">Ignore</ToastButton>
|
|
81
|
+
// </ToastButtons>
|
|
82
|
+
// </Toast>
|
|
83
|
+
// ));
|
|
84
|
+
// });
|
|
85
|
+
//
|
|
86
|
+
// toastStories.add("Multiple", () => (
|
|
87
|
+
// <ToastArea>
|
|
88
|
+
// {types.map((type) => (
|
|
89
|
+
// <ToastNotification key={type} type={type} title="New notification">
|
|
90
|
+
// <p>The notification received.</p>
|
|
91
|
+
// </ToastNotification>
|
|
92
|
+
// ))}
|
|
93
|
+
// </ToastArea>
|
|
94
|
+
// ));
|
|
95
|
+
|
|
17
96
|
import React, { useState } from "react";
|
|
18
|
-
import {
|
|
97
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
19
98
|
import Toast from "./Toast";
|
|
20
99
|
import ToastButtons from "./ToastButtons";
|
|
21
100
|
import ToastButton from "./ToastButton";
|
|
@@ -23,43 +102,34 @@ import { types } from "./themes";
|
|
|
23
102
|
import ToastArea from "./ToastArea";
|
|
24
103
|
import ToastNotification from "./ToastNotification";
|
|
25
104
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
const AnimatedToast = () => (
|
|
29
|
-
<Toast type="primary" title="Animated">
|
|
30
|
-
Awesome animated Toast
|
|
31
|
-
</Toast>
|
|
32
|
-
);
|
|
105
|
+
// --- Helfer-Komponenten (aus der Original-Story übernommen) ---
|
|
33
106
|
|
|
34
|
-
const
|
|
107
|
+
const OpenCloseExample = () => {
|
|
35
108
|
const [display, setDisplay] = useState(false);
|
|
36
|
-
|
|
37
109
|
return (
|
|
38
|
-
<div
|
|
39
|
-
{display &&
|
|
40
|
-
|
|
110
|
+
<div>
|
|
111
|
+
{display && (
|
|
112
|
+
<Toast type="primary" title="Animated">
|
|
113
|
+
Awesome animated Toast
|
|
114
|
+
</Toast>
|
|
115
|
+
)}
|
|
116
|
+
<button className="button is-primary mt-2" onClick={() => setDisplay(!display)}>
|
|
41
117
|
{display ? "Close" : "Open"} Toast
|
|
42
118
|
</button>
|
|
43
119
|
</div>
|
|
44
120
|
);
|
|
45
121
|
};
|
|
46
122
|
|
|
47
|
-
const
|
|
123
|
+
const ClickToCloseExample = () => {
|
|
48
124
|
const [show, setShow] = useState(true);
|
|
49
|
-
|
|
50
|
-
const hide = () => {
|
|
51
|
-
setShow(false);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
125
|
if (!show) {
|
|
55
|
-
return
|
|
126
|
+
return <p>Toast wurde geschlossen.</p>;
|
|
56
127
|
}
|
|
57
|
-
|
|
58
128
|
return (
|
|
59
129
|
<Toast type="success" title="Awesome feature">
|
|
60
130
|
<p>Close the message with a click</p>
|
|
61
131
|
<ToastButtons>
|
|
62
|
-
<ToastButton icon="times" onClick={
|
|
132
|
+
<ToastButton icon="times" onClick={() => setShow(false)}>
|
|
63
133
|
Click to close
|
|
64
134
|
</ToastButton>
|
|
65
135
|
</ToastButtons>
|
|
@@ -67,28 +137,63 @@ const Closeable = () => {
|
|
|
67
137
|
);
|
|
68
138
|
};
|
|
69
139
|
|
|
70
|
-
|
|
71
|
-
|
|
140
|
+
// --- Storybook Metadaten ---
|
|
141
|
+
|
|
142
|
+
const meta: Meta<typeof Toast> = {
|
|
143
|
+
title: "Components/Toast",
|
|
144
|
+
component: Toast,
|
|
145
|
+
decorators: [(Story) => <div style={{ padding: "2rem" }}>{Story()}</div>],
|
|
146
|
+
tags: ["autodocs"],
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export default meta;
|
|
150
|
+
|
|
151
|
+
// --- Story-Definitionen ---
|
|
72
152
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
153
|
+
type Story = StoryObj<typeof meta>;
|
|
154
|
+
|
|
155
|
+
// Basis-Props für die verschiedenen Toast-Typen
|
|
156
|
+
const baseArgs = {
|
|
157
|
+
title: "New Changes",
|
|
158
|
+
children: (
|
|
159
|
+
<>
|
|
76
160
|
<p>The underlying Pull-Request has changed. Press reload to see the changes.</p>
|
|
77
161
|
<p>Warning: Non saved modification will be lost.</p>
|
|
78
162
|
<ToastButtons>
|
|
79
163
|
<ToastButton icon="redo">Reload</ToastButton>
|
|
80
164
|
<ToastButton icon="times">Ignore</ToastButton>
|
|
81
165
|
</ToastButtons>
|
|
82
|
-
|
|
83
|
-
)
|
|
84
|
-
}
|
|
166
|
+
</>
|
|
167
|
+
),
|
|
168
|
+
};
|
|
85
169
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
170
|
+
// Anstatt einer Schleife erstellen wir explizite Stories für jeden Typ
|
|
171
|
+
export const Primary: Story = { args: { ...baseArgs, type: "primary" } };
|
|
172
|
+
export const Success: Story = { args: { ...baseArgs, type: "success" } };
|
|
173
|
+
export const Info: Story = { args: { ...baseArgs, type: "info" } };
|
|
174
|
+
export const Warning: Story = { args: { ...baseArgs, type: "warning" } };
|
|
175
|
+
export const Danger: Story = { args: { ...baseArgs, type: "danger" } };
|
|
176
|
+
|
|
177
|
+
// Stories mit eigener Logik verwenden eine `render`-Funktion
|
|
178
|
+
export const OpenClose: Story = {
|
|
179
|
+
name: "Open and Close Animation",
|
|
180
|
+
render: () => <OpenCloseExample />,
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export const ClickToClose: Story = {
|
|
184
|
+
name: "Click to Close",
|
|
185
|
+
render: () => <ClickToCloseExample />,
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
export const Multiple: Story = {
|
|
189
|
+
name: "Multiple Toasts in an Area",
|
|
190
|
+
render: () => (
|
|
191
|
+
<ToastArea>
|
|
192
|
+
{types.map((type) => (
|
|
193
|
+
<ToastNotification key={type} type={type} title="New notification">
|
|
194
|
+
<p>The notification received.</p>
|
|
195
|
+
</ToastNotification>
|
|
196
|
+
))}
|
|
197
|
+
</ToastArea>
|
|
198
|
+
),
|
|
199
|
+
};
|
package/src/toast/index.ts
CHANGED
|
@@ -17,6 +17,6 @@
|
|
|
17
17
|
export { default as Toast } from "./Toast";
|
|
18
18
|
export { default as ToastArea } from "./ToastArea";
|
|
19
19
|
export { default as ToastNotification } from "./ToastNotification";
|
|
20
|
-
export { Type as ToastType } from "./themes";
|
|
20
|
+
export { type Type as ToastType } from "./themes";
|
|
21
21
|
export { default as ToastButton } from "./ToastButton";
|
|
22
22
|
export { default as ToastButtons } from "./ToastButtons";
|