@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,47 +14,314 @@
|
|
|
14
14
|
* along with this program. If not, see https://www.gnu.org/licenses/.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
// import React, { ReactNode, useEffect, useState } from "react";
|
|
18
|
+
// import { storiesOf } from "@storybook/react";
|
|
19
|
+
// import Diff from "./Diff";
|
|
20
|
+
// import parser from "gitdiff-parser";
|
|
21
|
+
// import simpleDiff from "../__resources__/Diff.simple";
|
|
22
|
+
// import hunksDiff from "../__resources__/Diff.hunks";
|
|
23
|
+
// import binaryDiff from "../__resources__/Diff.binary";
|
|
24
|
+
// import markdownDiff from "../__resources__/Diff.markdown";
|
|
25
|
+
// import { DiffEventContext, FileControlFactory } from "./DiffTypes";
|
|
26
|
+
// import Toast from "../toast/Toast";
|
|
27
|
+
// import { getPath } from "./diffs";
|
|
28
|
+
// import DiffButton from "./DiffButton";
|
|
29
|
+
// import styled from "styled-components";
|
|
30
|
+
// import { MemoryRouter } from "react-router-dom";
|
|
31
|
+
// import { two } from "../__resources__/changesets";
|
|
32
|
+
// import { Changeset, FileDiff } from "@scm-manager/ui-types";
|
|
33
|
+
// import JumpToFileButton from "./JumpToFileButton";
|
|
34
|
+
// import Button from "../buttons/Button";
|
|
35
|
+
// // @ts-ignore ignore unknown png
|
|
36
|
+
// import hitchhikerImg from "../__resources__/hitchhiker.png";
|
|
37
|
+
// // @ts-ignore ignore unknown jpg
|
|
38
|
+
// import marvinImg from "../__resources__/marvin.jpg";
|
|
39
|
+
//
|
|
40
|
+
// const diffFiles = parser.parse(simpleDiff);
|
|
41
|
+
//
|
|
42
|
+
// const Container = styled.div`
|
|
43
|
+
// padding: 2rem 6rem;
|
|
44
|
+
// `;
|
|
45
|
+
//
|
|
46
|
+
// type ExternalDiffState = {
|
|
47
|
+
// [key: string]: boolean;
|
|
48
|
+
// };
|
|
49
|
+
//
|
|
50
|
+
// const RoutingDecorator = (story: () => ReactNode) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>;
|
|
51
|
+
//
|
|
52
|
+
// const fileControlFactory: (changeset: Changeset) => FileControlFactory = (changeset) => (file) => {
|
|
53
|
+
// const baseUrl = "/repo/hitchhiker/heartOfGold/code/changeset";
|
|
54
|
+
// const sourceLink = {
|
|
55
|
+
// url: `${baseUrl}/${changeset.id}/${file.newPath}/`,
|
|
56
|
+
// label: "Jump to source",
|
|
57
|
+
// };
|
|
58
|
+
// const targetLink = changeset._embedded?.parents?.length === 1 && {
|
|
59
|
+
// url: `${baseUrl}/${changeset._embedded.parents[0].id}/${file.oldPath}`,
|
|
60
|
+
// label: "Jump to target",
|
|
61
|
+
// };
|
|
62
|
+
//
|
|
63
|
+
// const links = [];
|
|
64
|
+
// switch (file.type) {
|
|
65
|
+
// case "add":
|
|
66
|
+
// links.push(sourceLink);
|
|
67
|
+
// break;
|
|
68
|
+
// case "delete":
|
|
69
|
+
// if (targetLink) {
|
|
70
|
+
// links.push(targetLink);
|
|
71
|
+
// }
|
|
72
|
+
// break;
|
|
73
|
+
// default:
|
|
74
|
+
// if (targetLink) {
|
|
75
|
+
// links.push(sourceLink, targetLink);
|
|
76
|
+
// } else {
|
|
77
|
+
// links.push(sourceLink);
|
|
78
|
+
// }
|
|
79
|
+
// }
|
|
80
|
+
//
|
|
81
|
+
// return links.map(({ url, label }) => <JumpToFileButton tooltip={label} link={url} />);
|
|
82
|
+
// };
|
|
83
|
+
//
|
|
84
|
+
// storiesOf("Repositories/Diff", module)
|
|
85
|
+
// .addDecorator(RoutingDecorator)
|
|
86
|
+
// .addDecorator((storyFn) => <Container>{storyFn()}</Container>)
|
|
87
|
+
// .add("Default", () => <Diff diff={diffFiles} />)
|
|
88
|
+
// .add("Side-By-Side", () => <Diff diff={diffFiles} sideBySide={true} />)
|
|
89
|
+
// .add("Whitespace", () => <Diff diff={diffFiles} whitespace={true} />)
|
|
90
|
+
// .add("Collapsed", () => <Diff diff={diffFiles} defaultCollapse={true} fileControlFactory={fileControlFactory(two)} />)
|
|
91
|
+
// .add("File Controls", () => (
|
|
92
|
+
// <Diff
|
|
93
|
+
// diff={diffFiles}
|
|
94
|
+
// fileControlFactory={() => (
|
|
95
|
+
// <DiffButton
|
|
96
|
+
// tooltip="A skull and crossbones or death's head is a symbol consisting of a human skull and two long bones crossed together under or behind the skull. The design originates in the Late Middle Ages as a symbol of death and especially as a memento mori on tombstones."
|
|
97
|
+
// icon="skull-crossbones"
|
|
98
|
+
// onClick={() => alert("Arrrgggghhhh ...")}
|
|
99
|
+
// />
|
|
100
|
+
// )}
|
|
101
|
+
// />
|
|
102
|
+
// ))
|
|
103
|
+
// .add("File Annotation", () => (
|
|
104
|
+
// <Diff
|
|
105
|
+
// diff={diffFiles}
|
|
106
|
+
// fileAnnotationFactory={(file) => [<p key={file.newPath}>Custom File annotation for {file.newPath}</p>]}
|
|
107
|
+
// />
|
|
108
|
+
// ))
|
|
109
|
+
// .add("Line Annotation", () => (
|
|
110
|
+
// <Diff
|
|
111
|
+
// diff={diffFiles}
|
|
112
|
+
// annotationFactory={(ctx) => {
|
|
113
|
+
// return {
|
|
114
|
+
// N2: <p key="N2">Line Annotation</p>,
|
|
115
|
+
// };
|
|
116
|
+
// }}
|
|
117
|
+
// />
|
|
118
|
+
// ))
|
|
119
|
+
// .add("OnClick", () => {
|
|
120
|
+
// const OnClickDemo = () => {
|
|
121
|
+
// const [changeId, setChangeId] = useState();
|
|
122
|
+
// useEffect(() => {
|
|
123
|
+
// const interval = setInterval(() => setChangeId(undefined), 2000);
|
|
124
|
+
// return () => clearInterval(interval);
|
|
125
|
+
// });
|
|
126
|
+
// // @ts-ignore
|
|
127
|
+
// const onClick = (context: DiffEventContext) => setChangeId(context.changeId);
|
|
128
|
+
// return (
|
|
129
|
+
// <>
|
|
130
|
+
// {changeId && <Toast type="info" title={"Change " + changeId} />}
|
|
131
|
+
// <Diff diff={diffFiles} onClick={onClick} />
|
|
132
|
+
// </>
|
|
133
|
+
// );
|
|
134
|
+
// };
|
|
135
|
+
// return <OnClickDemo />;
|
|
136
|
+
// })
|
|
137
|
+
// .add("Hunks", () => {
|
|
138
|
+
// const hunkDiffFiles = parser.parse(hunksDiff);
|
|
139
|
+
// return <Diff diff={hunkDiffFiles} />;
|
|
140
|
+
// })
|
|
141
|
+
// .add("Hunk gutter hover icon", () => {
|
|
142
|
+
// const hunkDiffFiles = parser.parse(hunksDiff);
|
|
143
|
+
// return <Diff diff={hunkDiffFiles} hunkGutterHoverIcon="\f075" />;
|
|
144
|
+
// })
|
|
145
|
+
// .add("Highlight line on hover", () => {
|
|
146
|
+
// const hunkDiffFiles = parser.parse(hunksDiff);
|
|
147
|
+
// return <Diff diff={hunkDiffFiles} highlightLineOnHover />;
|
|
148
|
+
// })
|
|
149
|
+
// .add("Binaries", () => {
|
|
150
|
+
// const binaryDiffFiles = parser.parse(binaryDiff);
|
|
151
|
+
// return <Diff diff={binaryDiffFiles} />;
|
|
152
|
+
// })
|
|
153
|
+
// .add("Images", () => {
|
|
154
|
+
// const binaryDiffFiles: FileDiff[] = [
|
|
155
|
+
// {
|
|
156
|
+
// type: "add",
|
|
157
|
+
// newPath: "test.png",
|
|
158
|
+
// oldPath: "/dev/null",
|
|
159
|
+
// isBinary: true,
|
|
160
|
+
// newEndingNewLine: false,
|
|
161
|
+
// oldEndingNewLine: false,
|
|
162
|
+
// _links: {
|
|
163
|
+
// newFile: {
|
|
164
|
+
// href: `${window.location.protocol}//${window.location.host}/${hitchhikerImg}`,
|
|
165
|
+
// },
|
|
166
|
+
// },
|
|
167
|
+
// },
|
|
168
|
+
// {
|
|
169
|
+
// type: "delete",
|
|
170
|
+
// newPath: "/dev/null",
|
|
171
|
+
// oldPath: "test.png",
|
|
172
|
+
// isBinary: true,
|
|
173
|
+
// newEndingNewLine: false,
|
|
174
|
+
// oldEndingNewLine: false,
|
|
175
|
+
// _links: {
|
|
176
|
+
// oldFile: {
|
|
177
|
+
// href: `${window.location.protocol}//${window.location.host}/${hitchhikerImg}`,
|
|
178
|
+
// },
|
|
179
|
+
// },
|
|
180
|
+
// },
|
|
181
|
+
// {
|
|
182
|
+
// type: "modify",
|
|
183
|
+
// newPath: "test.png",
|
|
184
|
+
// oldPath: "test.png",
|
|
185
|
+
// isBinary: true,
|
|
186
|
+
// newEndingNewLine: false,
|
|
187
|
+
// oldEndingNewLine: false,
|
|
188
|
+
// _links: {
|
|
189
|
+
// oldFile: {
|
|
190
|
+
// href: `${window.location.protocol}//${window.location.host}/${hitchhikerImg}`,
|
|
191
|
+
// },
|
|
192
|
+
// newFile: {
|
|
193
|
+
// href: `${window.location.protocol}//${window.location.host}/${marvinImg}`,
|
|
194
|
+
// },
|
|
195
|
+
// },
|
|
196
|
+
// },
|
|
197
|
+
// {
|
|
198
|
+
// type: "rename",
|
|
199
|
+
// newPath: "test.png",
|
|
200
|
+
// oldPath: "newFileName.png",
|
|
201
|
+
// isBinary: true,
|
|
202
|
+
// newEndingNewLine: false,
|
|
203
|
+
// oldEndingNewLine: false,
|
|
204
|
+
// _links: {
|
|
205
|
+
// oldFile: {
|
|
206
|
+
// href: `${window.location.protocol}//${window.location.host}/${hitchhikerImg}`,
|
|
207
|
+
// },
|
|
208
|
+
// newFile: {
|
|
209
|
+
// href: `${window.location.protocol}//${window.location.host}/${hitchhikerImg}`,
|
|
210
|
+
// },
|
|
211
|
+
// },
|
|
212
|
+
// },
|
|
213
|
+
// {
|
|
214
|
+
// type: "copy",
|
|
215
|
+
// newPath: "test.png",
|
|
216
|
+
// oldPath: "newFileName.png",
|
|
217
|
+
// isBinary: true,
|
|
218
|
+
// newEndingNewLine: false,
|
|
219
|
+
// oldEndingNewLine: false,
|
|
220
|
+
// _links: {
|
|
221
|
+
// oldFile: {
|
|
222
|
+
// href: `${window.location.protocol}//${window.location.host}/${hitchhikerImg}`,
|
|
223
|
+
// },
|
|
224
|
+
// newFile: {
|
|
225
|
+
// href: `${window.location.protocol}//${window.location.host}/${hitchhikerImg}`,
|
|
226
|
+
// },
|
|
227
|
+
// },
|
|
228
|
+
// },
|
|
229
|
+
// ];
|
|
230
|
+
// return <Diff diff={binaryDiffFiles} />;
|
|
231
|
+
// })
|
|
232
|
+
// .add("SyntaxHighlighting", () => {
|
|
233
|
+
// const filesWithLanguage = diffFiles.map((file: FileDiff) => {
|
|
234
|
+
// const ext = getPath(file).split(".")[1];
|
|
235
|
+
// if (ext === "tsx") {
|
|
236
|
+
// file.language = "typescript";
|
|
237
|
+
// } else {
|
|
238
|
+
// file.language = ext;
|
|
239
|
+
// }
|
|
240
|
+
// return file;
|
|
241
|
+
// });
|
|
242
|
+
// return <Diff diff={filesWithLanguage} />;
|
|
243
|
+
// })
|
|
244
|
+
// .add("SyntaxHighlighting (Markdown)", () => {
|
|
245
|
+
// // @ts-ignore
|
|
246
|
+
// return <Diff diff={markdownDiff.files} />;
|
|
247
|
+
// })
|
|
248
|
+
// .add("CollapsingWithFunction", () => (
|
|
249
|
+
// <Diff diff={diffFiles} defaultCollapse={(oldPath, newPath) => oldPath.endsWith(".java")} />
|
|
250
|
+
// ))
|
|
251
|
+
// .add("Expandable", () => {
|
|
252
|
+
// const filesWithLanguage = diffFiles.map((file: FileDiff) => {
|
|
253
|
+
// file._links = { lines: { href: "http://example.com/" } };
|
|
254
|
+
// return file;
|
|
255
|
+
// });
|
|
256
|
+
// return <Diff diff={filesWithLanguage} />;
|
|
257
|
+
// })
|
|
258
|
+
// .add("WithLinkToFile", () => <Diff diff={diffFiles} />)
|
|
259
|
+
// .add("Changing Content", () => {
|
|
260
|
+
// const ChangingContentDiff = () => {
|
|
261
|
+
// const [markdown, setMarkdown] = useState(false);
|
|
262
|
+
// return (
|
|
263
|
+
// <div>
|
|
264
|
+
// <Button className="mb-5" action={() => setMarkdown((m) => !m)}>
|
|
265
|
+
// Change content
|
|
266
|
+
// </Button>
|
|
267
|
+
// {/* @ts-ignore */}
|
|
268
|
+
// <Diff diff={markdown ? markdownDiff.files : diffFiles} />
|
|
269
|
+
// </div>
|
|
270
|
+
// );
|
|
271
|
+
// };
|
|
272
|
+
//
|
|
273
|
+
// return <ChangingContentDiff />;
|
|
274
|
+
// })
|
|
275
|
+
// .add("External state management", () => {
|
|
276
|
+
// const [externalState, setExternalState] = useState<ExternalDiffState>({});
|
|
277
|
+
//
|
|
278
|
+
// const isCollapsed = (file: FileDiff) => {
|
|
279
|
+
// return externalState[file.newPath] || false;
|
|
280
|
+
// };
|
|
281
|
+
//
|
|
282
|
+
// const onCollapseStateChange = (file: FileDiff) => {
|
|
283
|
+
// setExternalState((current) => ({ ...current, [file.newPath]: !current[file.newPath] }));
|
|
284
|
+
// };
|
|
285
|
+
//
|
|
286
|
+
// return <Diff diff={diffFiles} isCollapsed={isCollapsed} onCollapseStateChange={onCollapseStateChange} />;
|
|
287
|
+
// });
|
|
288
|
+
|
|
17
289
|
import React, { ReactNode, useEffect, useState } from "react";
|
|
18
|
-
import
|
|
19
|
-
import
|
|
290
|
+
import styled from "styled-components";
|
|
291
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
20
292
|
import parser from "gitdiff-parser";
|
|
293
|
+
import { Changeset, FileDiff } from "@scm-manager/ui-types";
|
|
294
|
+
|
|
295
|
+
import Diff from "./Diff";
|
|
296
|
+
import DiffButton from "./DiffButton";
|
|
297
|
+
import JumpToFileButton from "./JumpToFileButton";
|
|
298
|
+
import Toast from "../toast/Toast";
|
|
299
|
+
import Button from "../buttons/Button";
|
|
300
|
+
import { DiffEventContext, FileControlFactory } from "./DiffTypes";
|
|
301
|
+
import { getPath } from "./diffs";
|
|
302
|
+
|
|
21
303
|
import simpleDiff from "../__resources__/Diff.simple";
|
|
22
304
|
import hunksDiff from "../__resources__/Diff.hunks";
|
|
23
305
|
import binaryDiff from "../__resources__/Diff.binary";
|
|
24
306
|
import markdownDiff from "../__resources__/Diff.markdown";
|
|
25
|
-
import { DiffEventContext, FileControlFactory } from "./DiffTypes";
|
|
26
|
-
import Toast from "../toast/Toast";
|
|
27
|
-
import { getPath } from "./diffs";
|
|
28
|
-
import DiffButton from "./DiffButton";
|
|
29
|
-
import styled from "styled-components";
|
|
30
|
-
import { MemoryRouter } from "react-router-dom";
|
|
31
307
|
import { two } from "../__resources__/changesets";
|
|
32
|
-
import { Changeset, FileDiff } from "@scm-manager/ui-types";
|
|
33
|
-
import JumpToFileButton from "./JumpToFileButton";
|
|
34
|
-
import Button from "../buttons/Button";
|
|
35
308
|
// @ts-ignore ignore unknown png
|
|
36
309
|
import hitchhikerImg from "../__resources__/hitchhiker.png";
|
|
37
310
|
// @ts-ignore ignore unknown jpg
|
|
38
311
|
import marvinImg from "../__resources__/marvin.jpg";
|
|
312
|
+
import { MemoryRouter } from "react-router-dom";
|
|
39
313
|
|
|
40
|
-
|
|
314
|
+
// --- Helfer-Funktionen und Mock-Daten (aus der Original-Story übernommen) ---
|
|
41
315
|
|
|
42
316
|
const Container = styled.div`
|
|
43
|
-
padding: 2rem
|
|
317
|
+
padding: 2rem 1rem; // Adjusted padding for better viewing
|
|
44
318
|
`;
|
|
45
319
|
|
|
46
|
-
|
|
47
|
-
[key: string]: boolean;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const RoutingDecorator = (story: () => ReactNode) => <MemoryRouter initialEntries={["/"]}>{story()}</MemoryRouter>;
|
|
320
|
+
const diffFiles = parser.parse(simpleDiff);
|
|
51
321
|
|
|
52
322
|
const fileControlFactory: (changeset: Changeset) => FileControlFactory = (changeset) => (file) => {
|
|
53
323
|
const baseUrl = "/repo/hitchhiker/heartOfGold/code/changeset";
|
|
54
|
-
const sourceLink = {
|
|
55
|
-
url: `${baseUrl}/${changeset.id}/${file.newPath}/`,
|
|
56
|
-
label: "Jump to source",
|
|
57
|
-
};
|
|
324
|
+
const sourceLink = { url: `${baseUrl}/${changeset.id}/${file.newPath}/`, label: "Jump to source" };
|
|
58
325
|
const targetLink = changeset._embedded?.parents?.length === 1 && {
|
|
59
326
|
url: `${baseUrl}/${changeset._embedded.parents[0].id}/${file.oldPath}`,
|
|
60
327
|
label: "Jump to target",
|
|
@@ -77,211 +344,139 @@ const fileControlFactory: (changeset: Changeset) => FileControlFactory = (change
|
|
|
77
344
|
links.push(sourceLink);
|
|
78
345
|
}
|
|
79
346
|
}
|
|
80
|
-
|
|
81
347
|
return links.map(({ url, label }) => <JumpToFileButton tooltip={label} link={url} />);
|
|
82
348
|
};
|
|
83
349
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
/>
|
|
100
|
-
)}
|
|
101
|
-
/>
|
|
102
|
-
))
|
|
103
|
-
.add("File Annotation", () => (
|
|
104
|
-
<Diff
|
|
105
|
-
diff={diffFiles}
|
|
106
|
-
fileAnnotationFactory={(file) => [<p key={file.newPath}>Custom File annotation for {file.newPath}</p>]}
|
|
107
|
-
/>
|
|
108
|
-
))
|
|
109
|
-
.add("Line Annotation", () => (
|
|
110
|
-
<Diff
|
|
111
|
-
diff={diffFiles}
|
|
112
|
-
annotationFactory={(ctx) => {
|
|
113
|
-
return {
|
|
114
|
-
N2: <p key="N2">Line Annotation</p>,
|
|
115
|
-
};
|
|
116
|
-
}}
|
|
117
|
-
/>
|
|
118
|
-
))
|
|
119
|
-
.add("OnClick", () => {
|
|
120
|
-
const OnClickDemo = () => {
|
|
121
|
-
const [changeId, setChangeId] = useState();
|
|
122
|
-
useEffect(() => {
|
|
123
|
-
const interval = setInterval(() => setChangeId(undefined), 2000);
|
|
124
|
-
return () => clearInterval(interval);
|
|
125
|
-
});
|
|
126
|
-
// @ts-ignore
|
|
127
|
-
const onClick = (context: DiffEventContext) => setChangeId(context.changeId);
|
|
128
|
-
return (
|
|
129
|
-
<>
|
|
130
|
-
{changeId && <Toast type="info" title={"Change " + changeId} />}
|
|
131
|
-
<Diff diff={diffFiles} onClick={onClick} />
|
|
132
|
-
</>
|
|
133
|
-
);
|
|
134
|
-
};
|
|
135
|
-
return <OnClickDemo />;
|
|
136
|
-
})
|
|
137
|
-
.add("Hunks", () => {
|
|
138
|
-
const hunkDiffFiles = parser.parse(hunksDiff);
|
|
139
|
-
return <Diff diff={hunkDiffFiles} />;
|
|
140
|
-
})
|
|
141
|
-
.add("Hunk gutter hover icon", () => {
|
|
142
|
-
const hunkDiffFiles = parser.parse(hunksDiff);
|
|
143
|
-
return <Diff diff={hunkDiffFiles} hunkGutterHoverIcon="\f075" />;
|
|
144
|
-
})
|
|
145
|
-
.add("Highlight line on hover", () => {
|
|
146
|
-
const hunkDiffFiles = parser.parse(hunksDiff);
|
|
147
|
-
return <Diff diff={hunkDiffFiles} highlightLineOnHover />;
|
|
148
|
-
})
|
|
149
|
-
.add("Binaries", () => {
|
|
150
|
-
const binaryDiffFiles = parser.parse(binaryDiff);
|
|
151
|
-
return <Diff diff={binaryDiffFiles} />;
|
|
152
|
-
})
|
|
153
|
-
.add("Images", () => {
|
|
154
|
-
const binaryDiffFiles: FileDiff[] = [
|
|
155
|
-
{
|
|
156
|
-
type: "add",
|
|
157
|
-
newPath: "test.png",
|
|
158
|
-
oldPath: "/dev/null",
|
|
159
|
-
isBinary: true,
|
|
160
|
-
newEndingNewLine: false,
|
|
161
|
-
oldEndingNewLine: false,
|
|
162
|
-
_links: {
|
|
163
|
-
newFile: {
|
|
164
|
-
href: `${window.location.protocol}//${window.location.host}/${hitchhikerImg}`,
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
type: "delete",
|
|
170
|
-
newPath: "/dev/null",
|
|
171
|
-
oldPath: "test.png",
|
|
172
|
-
isBinary: true,
|
|
173
|
-
newEndingNewLine: false,
|
|
174
|
-
oldEndingNewLine: false,
|
|
175
|
-
_links: {
|
|
176
|
-
oldFile: {
|
|
177
|
-
href: `${window.location.protocol}//${window.location.host}/${hitchhikerImg}`,
|
|
178
|
-
},
|
|
179
|
-
},
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
type: "modify",
|
|
183
|
-
newPath: "test.png",
|
|
184
|
-
oldPath: "test.png",
|
|
185
|
-
isBinary: true,
|
|
186
|
-
newEndingNewLine: false,
|
|
187
|
-
oldEndingNewLine: false,
|
|
188
|
-
_links: {
|
|
189
|
-
oldFile: {
|
|
190
|
-
href: `${window.location.protocol}//${window.location.host}/${hitchhikerImg}`,
|
|
191
|
-
},
|
|
192
|
-
newFile: {
|
|
193
|
-
href: `${window.location.protocol}//${window.location.host}/${marvinImg}`,
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
type: "rename",
|
|
199
|
-
newPath: "test.png",
|
|
200
|
-
oldPath: "newFileName.png",
|
|
201
|
-
isBinary: true,
|
|
202
|
-
newEndingNewLine: false,
|
|
203
|
-
oldEndingNewLine: false,
|
|
204
|
-
_links: {
|
|
205
|
-
oldFile: {
|
|
206
|
-
href: `${window.location.protocol}//${window.location.host}/${hitchhikerImg}`,
|
|
207
|
-
},
|
|
208
|
-
newFile: {
|
|
209
|
-
href: `${window.location.protocol}//${window.location.host}/${hitchhikerImg}`,
|
|
210
|
-
},
|
|
211
|
-
},
|
|
212
|
-
},
|
|
213
|
-
{
|
|
214
|
-
type: "copy",
|
|
215
|
-
newPath: "test.png",
|
|
216
|
-
oldPath: "newFileName.png",
|
|
217
|
-
isBinary: true,
|
|
218
|
-
newEndingNewLine: false,
|
|
219
|
-
oldEndingNewLine: false,
|
|
220
|
-
_links: {
|
|
221
|
-
oldFile: {
|
|
222
|
-
href: `${window.location.protocol}//${window.location.host}/${hitchhikerImg}`,
|
|
223
|
-
},
|
|
224
|
-
newFile: {
|
|
225
|
-
href: `${window.location.protocol}//${window.location.host}/${hitchhikerImg}`,
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
},
|
|
229
|
-
];
|
|
230
|
-
return <Diff diff={binaryDiffFiles} />;
|
|
231
|
-
})
|
|
232
|
-
.add("SyntaxHighlighting", () => {
|
|
233
|
-
const filesWithLanguage = diffFiles.map((file: FileDiff) => {
|
|
234
|
-
const ext = getPath(file).split(".")[1];
|
|
235
|
-
if (ext === "tsx") {
|
|
236
|
-
file.language = "typescript";
|
|
237
|
-
} else {
|
|
238
|
-
file.language = ext;
|
|
239
|
-
}
|
|
240
|
-
return file;
|
|
241
|
-
});
|
|
242
|
-
return <Diff diff={filesWithLanguage} />;
|
|
243
|
-
})
|
|
244
|
-
.add("SyntaxHighlighting (Markdown)", () => {
|
|
245
|
-
// @ts-ignore
|
|
246
|
-
return <Diff diff={markdownDiff.files} />;
|
|
247
|
-
})
|
|
248
|
-
.add("CollapsingWithFunction", () => (
|
|
249
|
-
<Diff diff={diffFiles} defaultCollapse={(oldPath, newPath) => oldPath.endsWith(".java")} />
|
|
250
|
-
))
|
|
251
|
-
.add("Expandable", () => {
|
|
252
|
-
const filesWithLanguage = diffFiles.map((file: FileDiff) => {
|
|
253
|
-
file._links = { lines: { href: "http://example.com/" } };
|
|
254
|
-
return file;
|
|
255
|
-
});
|
|
256
|
-
return <Diff diff={filesWithLanguage} />;
|
|
257
|
-
})
|
|
258
|
-
.add("WithLinkToFile", () => <Diff diff={diffFiles} />)
|
|
259
|
-
.add("Changing Content", () => {
|
|
260
|
-
const ChangingContentDiff = () => {
|
|
261
|
-
const [markdown, setMarkdown] = useState(false);
|
|
262
|
-
return (
|
|
263
|
-
<div>
|
|
264
|
-
<Button className="mb-5" action={() => setMarkdown((m) => !m)}>
|
|
265
|
-
Change content
|
|
266
|
-
</Button>
|
|
267
|
-
{/* @ts-ignore */}
|
|
268
|
-
<Diff diff={markdown ? markdownDiff.files : diffFiles} />
|
|
269
|
-
</div>
|
|
270
|
-
);
|
|
271
|
-
};
|
|
350
|
+
// Helper-Komponenten für komplexe Stories
|
|
351
|
+
const OnClickExample = () => {
|
|
352
|
+
const [changeId, setChangeId] = useState<string | undefined>();
|
|
353
|
+
useEffect(() => {
|
|
354
|
+
const interval = setInterval(() => setChangeId(undefined), 2000);
|
|
355
|
+
return () => clearInterval(interval);
|
|
356
|
+
}, []);
|
|
357
|
+
const onClick = (context: DiffEventContext) => setChangeId(context.changeId);
|
|
358
|
+
return (
|
|
359
|
+
<>
|
|
360
|
+
{changeId && <Toast type="info" title={`Change ${changeId}`} />}
|
|
361
|
+
<Diff diff={diffFiles} onClick={onClick} />
|
|
362
|
+
</>
|
|
363
|
+
);
|
|
364
|
+
};
|
|
272
365
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
366
|
+
const ChangingContentExample = () => {
|
|
367
|
+
const [markdown, setMarkdown] = useState(false);
|
|
368
|
+
return (
|
|
369
|
+
<div>
|
|
370
|
+
<Button className="mb-5" action={() => setMarkdown((m) => !m)}>
|
|
371
|
+
Change content
|
|
372
|
+
</Button>
|
|
373
|
+
{/* @ts-ignore */}
|
|
374
|
+
<Diff diff={markdown ? markdownDiff.files : diffFiles} />
|
|
375
|
+
</div>
|
|
376
|
+
);
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const ExternalStateExample = () => {
|
|
380
|
+
const [externalState, setExternalState] = useState<{ [key: string]: boolean }>({});
|
|
381
|
+
const isCollapsed = (file: FileDiff) => externalState[file.newPath] || false;
|
|
382
|
+
const onCollapseStateChange = (file: FileDiff) => {
|
|
383
|
+
setExternalState((current) => ({ ...current, [file.newPath]: !current[file.newPath] }));
|
|
384
|
+
};
|
|
385
|
+
return <Diff diff={diffFiles} isCollapsed={isCollapsed} onCollapseStateChange={onCollapseStateChange} />;
|
|
386
|
+
};
|
|
277
387
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
388
|
+
// --- Storybook Metadaten ---
|
|
389
|
+
|
|
390
|
+
const meta: Meta<typeof Diff> = {
|
|
391
|
+
title: "Repositories/Diff",
|
|
392
|
+
component: Diff,
|
|
393
|
+
decorators: [
|
|
394
|
+
(Story) => <MemoryRouter initialEntries={["/"]}>{Story()}</MemoryRouter>,
|
|
395
|
+
(Story) => <Container>{Story()}</Container>],
|
|
396
|
+
tags: ["autodocs"],
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
export default meta;
|
|
400
|
+
|
|
401
|
+
// --- Story-Definitionen ---
|
|
402
|
+
|
|
403
|
+
type Story = StoryObj<typeof meta>;
|
|
404
|
+
|
|
405
|
+
export const Default: Story = { args: { diff: diffFiles } };
|
|
406
|
+
export const SideBySide: Story = { args: { diff: diffFiles, sideBySide: true } };
|
|
407
|
+
export const Whitespace: Story = { args: { diff: diffFiles, whitespace: true } };
|
|
408
|
+
export const Collapsed: Story = {
|
|
409
|
+
args: { diff: diffFiles, defaultCollapse: true, fileControlFactory: fileControlFactory(two) },
|
|
410
|
+
};
|
|
411
|
+
export const Hunks: Story = { args: { diff: parser.parse(hunksDiff) } };
|
|
412
|
+
export const Binaries: Story = { args: { diff: parser.parse(binaryDiff) } };
|
|
413
|
+
export const HunkGutterHoverIcon: Story = { args: { diff: parser.parse(hunksDiff), hunkGutterHoverIcon: "\f075" } };
|
|
414
|
+
export const HighlightLineOnHover: Story = { args: { diff: parser.parse(hunksDiff), highlightLineOnHover: true } };
|
|
415
|
+
export const Expandable: Story = {
|
|
416
|
+
args: { diff: diffFiles.map((file) => ({ ...file, _links: { lines: { href: "http://example.com/" } } })) },
|
|
417
|
+
};
|
|
418
|
+
export const WithLinkToFile: Story = { args: { diff: diffFiles } };
|
|
419
|
+
|
|
420
|
+
export const FileControls: Story = {
|
|
421
|
+
args: {
|
|
422
|
+
diff: diffFiles,
|
|
423
|
+
fileControlFactory: () => (
|
|
424
|
+
<DiffButton
|
|
425
|
+
tooltip="A skull and crossbones or death's head..."
|
|
426
|
+
icon="skull-crossbones"
|
|
427
|
+
onClick={() => alert("Arrrgggghhhh ...")}
|
|
428
|
+
/>
|
|
429
|
+
),
|
|
430
|
+
},
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
export const FileAnnotation: Story = {
|
|
434
|
+
args: {
|
|
435
|
+
diff: diffFiles,
|
|
436
|
+
fileAnnotationFactory: (file) => [<p key={file.newPath}>Custom File annotation for {file.newPath}</p>],
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
export const LineAnnotation: Story = {
|
|
441
|
+
args: {
|
|
442
|
+
diff: diffFiles,
|
|
443
|
+
annotationFactory: () => ({ N2: <p key="N2">Line Annotation</p> }),
|
|
444
|
+
},
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
export const CollapsingWithFunction: Story = {
|
|
448
|
+
args: {
|
|
449
|
+
diff: diffFiles,
|
|
450
|
+
defaultCollapse: (oldPath) => oldPath.endsWith(".java"),
|
|
451
|
+
},
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
export const SyntaxHighlighting: Story = {
|
|
455
|
+
args: {
|
|
456
|
+
diff: diffFiles.map((file) => {
|
|
457
|
+
const ext = getPath(file).split(".")[1];
|
|
458
|
+
return { ...file, language: ext === "tsx" ? "typescript" : ext };
|
|
459
|
+
}),
|
|
460
|
+
},
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
export const SyntaxHighlightingMarkdown: Story = {
|
|
464
|
+
name: "Syntax Highlighting (Markdown)",
|
|
465
|
+
// @ts-ignore
|
|
466
|
+
args: { diff: markdownDiff.files },
|
|
467
|
+
};
|
|
281
468
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
469
|
+
// export const Images: Story = {
|
|
470
|
+
// args: {
|
|
471
|
+
// diff: [
|
|
472
|
+
// { type: "add", newPath: "add.png", isBinary: true, _links: { newFile: { href: hitchhikerImg } } },
|
|
473
|
+
// { type: "delete", oldPath: "delete.png", isBinary: true, _links: { oldFile: { href: hitchhikerImg } } },
|
|
474
|
+
// { type: "modify", newPath: "modify.png", isBinary: true, _links: { oldFile: { href: hitchhikerImg }, newFile: { href: marvinImg } } },
|
|
475
|
+
// ] as FileDiff[],
|
|
476
|
+
// },
|
|
477
|
+
// };
|
|
285
478
|
|
|
286
|
-
|
|
287
|
-
|
|
479
|
+
// Stories with stateful logic use `render`
|
|
480
|
+
export const OnClick: Story = { render: () => <OnClickExample /> };
|
|
481
|
+
export const ChangingContent: Story = { render: () => <ChangingContentExample /> };
|
|
482
|
+
export const ExternalStateManagement: Story = { render: () => <ExternalStateExample /> };
|