@scm-manager/ui-components 4.0.0-REACT18-20250701-125025 → 4.0.0-REACT18-20250824-143504

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/package.json +45 -50
  2. package/src/BranchSelector.stories.tsx +60 -11
  3. package/src/Breadcrumb.stories.tsx +131 -57
  4. package/src/Breadcrumb.tsx +3 -3
  5. package/src/CardColumn.stories.tsx +94 -27
  6. package/src/CardColumnSmall.stories.tsx +102 -27
  7. package/src/CommaSeparatedList.tsx +2 -2
  8. package/src/Date.stories.tsx +64 -17
  9. package/src/Duration.stories.tsx +92 -45
  10. package/src/ErrorBoundary.tsx +38 -58
  11. package/src/ErrorNotification.tsx +1 -1
  12. package/src/Help.stories.tsx +61 -17
  13. package/src/Help.tsx +5 -11
  14. package/src/Icon.stories.tsx +93 -13
  15. package/src/LinkPaginator.tsx +9 -6
  16. package/src/Loading.stories.tsx +26 -6
  17. package/src/Logo.stories.tsx +44 -13
  18. package/src/Notification.stories.tsx +111 -23
  19. package/src/OverviewPageActions.tsx +5 -5
  20. package/src/Paginator.test.tsx +115 -94
  21. package/src/PdfViewer.stories.tsx +83 -5
  22. package/src/PreformattedCodeBlock.stories.tsx +97 -26
  23. package/src/ProtectedRoute.tsx +14 -39
  24. package/src/SmallLoadingSpinner.stories.tsx +26 -6
  25. package/src/SyntaxHighlighter.stories.tsx +122 -40
  26. package/src/SyntaxHighlighterRenderer.tsx +7 -7
  27. package/src/Tag.stories.tsx +135 -18
  28. package/src/Tag.tsx +2 -1
  29. package/src/Tooltip.stories.tsx +147 -34
  30. package/src/__snapshots__/storyshots.test.ts.snap +6 -112
  31. package/src/avatar/Avatar.ts +1 -1
  32. package/src/avatar/AvatarImage.tsx +1 -1
  33. package/src/avatar/AvatarWrapper.tsx +2 -2
  34. package/src/buttons/Button.stories.tsx +159 -0
  35. package/src/buttons/Button.tsx +5 -5
  36. package/src/config/ConfigurationBinder.tsx +39 -39
  37. package/src/config/ConfigurationForm.tsx +2 -1
  38. package/src/config/TitledSettings.tsx +4 -1
  39. package/src/forms/AddKeyValueEntryToTableField.stories.tsx +60 -25
  40. package/src/forms/Checkbox.stories.tsx +165 -27
  41. package/src/forms/Checkbox.tsx +1 -0
  42. package/src/forms/DropDown.stories.tsx +81 -35
  43. package/src/forms/FileInput.stories.tsx +85 -10
  44. package/src/forms/InputField.stories.tsx +210 -37
  45. package/src/forms/InputField.tsx +1 -0
  46. package/src/forms/Radio.stories.tsx +181 -34
  47. package/src/forms/Radio.tsx +1 -0
  48. package/src/forms/Select.stories.tsx +266 -46
  49. package/src/forms/Select.tsx +1 -0
  50. package/src/forms/Textarea.stories.tsx +99 -53
  51. package/src/forms/Textarea.tsx +1 -0
  52. package/src/forms/index.ts +3 -1
  53. package/src/index.ts +5 -5
  54. package/src/jest-dom.d.ts +17 -0
  55. package/src/layout/Footer.stories.tsx +114 -22
  56. package/src/layout/FooterSection.tsx +1 -0
  57. package/src/layout/Header.tsx +2 -1
  58. package/src/layout/Page.tsx +1 -3
  59. package/src/layout/PrimaryContentColumn.tsx +2 -2
  60. package/src/layout/SecondaryNavigationColumn.tsx +3 -3
  61. package/src/layout/SubSubtitle.tsx +2 -1
  62. package/src/layout/Subtitle.tsx +2 -1
  63. package/src/layout/Title.tsx +2 -5
  64. package/src/markdown/LazyMarkdownView.tsx +16 -5
  65. package/src/markdown/MarkdownHeadingRenderer.test.ts +9 -1
  66. package/src/markdown/MarkdownHeadingRenderer.tsx +3 -3
  67. package/src/markdown/MarkdownImageRenderer.test.ts +8 -1
  68. package/src/markdown/MarkdownImageRenderer.tsx +2 -1
  69. package/src/markdown/MarkdownLinkRenderer.test.tsx +9 -0
  70. package/src/markdown/MarkdownLinkRenderer.tsx +6 -4
  71. package/src/markdown/MarkdownView.stories.tsx +224 -72
  72. package/src/markdown/markdownExtensions.ts +6 -4
  73. package/src/modals/ConfirmAlert.stories.tsx +133 -44
  74. package/src/modals/ConfirmAlert.tsx +5 -4
  75. package/src/modals/Modal.stories.tsx +461 -252
  76. package/src/modals/Modal.tsx +12 -11
  77. package/src/modals/useRegisterModal.test.tsx +5 -4
  78. package/src/navigation/MenuContext.tsx +2 -2
  79. package/src/navigation/NavLink.tsx +4 -7
  80. package/src/navigation/PrimaryNavigation.tsx +2 -2
  81. package/src/navigation/PrimaryNavigationLink.tsx +6 -5
  82. package/src/navigation/RoutingProps.ts +1 -3
  83. package/src/navigation/SecondaryNavigation.stories.tsx +157 -45
  84. package/src/navigation/SecondaryNavigation.tsx +17 -16
  85. package/src/navigation/SecondaryNavigationItem.tsx +2 -1
  86. package/src/navigation/SubNavigation.tsx +4 -8
  87. package/src/navigation/useActiveMatch.ts +20 -22
  88. package/src/navigation/useNavigationLock.ts +1 -0
  89. package/src/popover/Popover.stories.tsx +111 -41
  90. package/src/popover/Popover.tsx +2 -5
  91. package/src/repos/Diff.stories.tsx +418 -223
  92. package/src/repos/Diff.tsx +1 -5
  93. package/src/repos/HunkExpandDivider.tsx +2 -2
  94. package/src/repos/LoadingDiff.tsx +11 -6
  95. package/src/repos/RepositoryEntry.stories.tsx +217 -53
  96. package/src/repos/RepositoryFlag.tsx +2 -1
  97. package/src/repos/TokenizedDiffView.tsx +4 -2
  98. package/src/repos/annotate/Annotate.stories.tsx +225 -111
  99. package/src/repos/annotate/AnnotateLine.tsx +2 -1
  100. package/src/repos/changesets/ChangesetAuthor.tsx +2 -2
  101. package/src/repos/changesets/ChangesetButtonGroup.test.tsx +9 -5
  102. package/src/repos/changesets/ChangesetDiff.test.ts +10 -2
  103. package/src/repos/changesets/Changesets.stories.tsx +388 -197
  104. package/src/repos/changesets/ContributorRow.tsx +2 -2
  105. package/src/repos/changesets/SignatureIcon.tsx +1 -0
  106. package/src/repos/diff/DiffFileTree.tsx +1 -1
  107. package/src/repos/diff/styledElements.tsx +4 -3
  108. package/src/repos/index.ts +15 -15
  109. package/src/search/Hit.tsx +3 -2
  110. package/src/search/TextHitField.stories.tsx +131 -43
  111. package/src/search/TextHitField.tsx +3 -2
  112. package/src/search/index.ts +1 -1
  113. package/src/storyshots.test.ts +66 -60
  114. package/src/table/Table.stories.tsx +146 -48
  115. package/src/table/Table.tsx +7 -8
  116. package/src/table/TextColumn.tsx +0 -9
  117. package/src/toast/Toast.tsx +2 -1
  118. package/src/toast/ToastArea.tsx +2 -2
  119. package/src/toast/ToastButton.tsx +2 -1
  120. package/src/toast/ToastButtons.tsx +4 -2
  121. package/src/toast/ToastNotification.tsx +2 -1
  122. package/src/toast/index.stories.tsx +144 -39
  123. package/src/toast/index.ts +1 -1
  124. package/src/buttons/index.stories.tsx +0 -85
@@ -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} />;
@@ -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;
@@ -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>`
@@ -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 }) => <FullWidthDiv className="is-flex pt-2">{children}</FullWidthDiv>;
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 { storiesOf } from "@storybook/react";
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
- const toastStories = storiesOf("Toast", module);
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 Animator = () => {
107
+ const OpenCloseExample = () => {
35
108
  const [display, setDisplay] = useState(false);
36
-
37
109
  return (
38
- <div style={{ padding: "2rem" }}>
39
- {display && <AnimatedToast />}
40
- <button className="button is-primary" onClick={() => setDisplay(!display)}>
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 Closeable = () => {
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 null;
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={hide}>
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
- toastStories.add("Open/Close", () => <Animator />);
71
- toastStories.add("Click to close", () => <Closeable />);
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
- types.forEach((type) => {
74
- toastStories.add(type.charAt(0).toUpperCase() + type.slice(1), () => (
75
- <Toast type={type} title="New Changes">
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
- </Toast>
83
- ));
84
- });
166
+ </>
167
+ ),
168
+ };
85
169
 
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
- ));
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
+ };
@@ -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";
@@ -1,85 +0,0 @@
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 Button from "./Button";
19
- import { storiesOf } from "@storybook/react";
20
- import styled from "styled-components";
21
- import AddButton from "./AddButton";
22
- import CreateButton from "./CreateButton";
23
- import DeleteButton from "./DeleteButton";
24
- import DownloadButton from "./DownloadButton";
25
- import EditButton from "./EditButton";
26
- import SubmitButton from "./SubmitButton";
27
- import { MemoryRouter } from "react-router-dom";
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
- });