github-issue-tower-defence-management 1.90.0 → 1.91.1
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/CHANGELOG.md +15 -0
- package/README.md +19 -5
- package/bin/adapter/entry-points/cli/index.js +17 -13
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/entry-points/cli/projectConfig.js +2 -0
- package/bin/adapter/entry-points/cli/projectConfig.js.map +1 -1
- package/bin/adapter/entry-points/console/consoleOperationApi.js +54 -27
- package/bin/adapter/entry-points/console/consoleOperationApi.js.map +1 -1
- package/bin/adapter/entry-points/console/consoleProjectResolver.js +38 -0
- package/bin/adapter/entry-points/console/consoleProjectResolver.js.map +1 -0
- package/bin/adapter/entry-points/console/consoleServer.js +3 -4
- package/bin/adapter/entry-points/console/consoleServer.js.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/index-BU6p3cGU.css +1 -0
- package/bin/adapter/entry-points/console/ui-dist/assets/index-BvuSQN9s.js +100 -0
- package/bin/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/bin/adapter/entry-points/handlers/consoleListsWriter.js +1 -0
- package/bin/adapter/entry-points/handlers/consoleListsWriter.js.map +1 -1
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +16 -0
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
- package/bin/domain/usecases/console/GenerateConsoleListsUseCase.js +3 -0
- package/bin/domain/usecases/console/GenerateConsoleListsUseCase.js.map +1 -1
- package/jest.config.js +57 -9
- package/package.json +17 -13
- package/src/adapter/entry-points/cli/index.test.ts +18 -3
- package/src/adapter/entry-points/cli/index.ts +32 -14
- package/src/adapter/entry-points/cli/projectConfig.ts +3 -0
- package/src/adapter/entry-points/console/consoleOperationApi.test.ts +129 -15
- package/src/adapter/entry-points/console/consoleOperationApi.ts +83 -28
- package/src/adapter/entry-points/console/consoleProjectResolver.test.ts +96 -0
- package/src/adapter/entry-points/console/consoleProjectResolver.ts +50 -0
- package/src/adapter/entry-points/console/consoleServer.test.ts +5 -4
- package/src/adapter/entry-points/console/consoleServer.ts +5 -7
- package/src/adapter/entry-points/console/ui/jest.setup.ts +1 -0
- package/src/adapter/entry-points/console/ui/jest.styleMock.js +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.stories.tsx +27 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.test.tsx +36 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.tsx +50 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagram.stories.tsx +22 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagram.test.tsx +38 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagram.tsx +65 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleChangedFileList.stories.tsx +28 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleChangedFileList.test.tsx +42 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleChangedFileList.tsx +55 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.stories.tsx +29 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.test.tsx +55 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.tsx +66 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommitList.stories.tsx +25 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommitList.test.tsx +53 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommitList.tsx +53 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.stories.tsx +79 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.test.tsx +81 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.tsx +229 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemIcon.stories.tsx +82 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemIcon.test.tsx +52 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemIcon.tsx +32 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.stories.tsx +31 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.test.tsx +40 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.tsx +88 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsolePanel.stories.tsx +26 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsolePanel.test.tsx +32 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsolePanel.tsx +36 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/{ConsoleProjectHeader.stories.tsx → layout/ConsoleProjectSummary.stories.tsx} +5 -5
- package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleProjectSummary.test.tsx +14 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/{ConsoleProjectHeader.tsx → layout/ConsoleProjectSummary.tsx} +3 -1
- package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.stories.tsx +70 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.test.tsx +59 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.tsx +41 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemList.stories.tsx +60 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemList.test.tsx +87 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemList.tsx +68 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemSummary.stories.tsx +25 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemSummary.test.tsx +43 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemSummary.tsx +34 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStorySummary.stories.tsx +27 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStorySummary.test.tsx +24 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStorySummary.tsx +28 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleCloseActions.stories.tsx +14 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleCloseActions.test.tsx +21 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleCloseActions.tsx +26 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.stories.tsx +20 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.test.tsx +42 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.tsx +28 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.stories.tsx +55 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.test.tsx +85 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.tsx +58 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsolePullRequestReviewActions.stories.tsx +14 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsolePullRequestReviewActions.test.tsx +33 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsolePullRequestReviewActions.tsx +34 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStatusActions.stories.tsx +17 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStatusActions.test.tsx +49 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStatusActions.tsx +66 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStoryActions.stories.tsx +17 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStoryActions.test.tsx +39 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStoryActions.tsx +42 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleCaches.test.ts +22 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleCaches.ts +42 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.test.ts +126 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.ts +167 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.test.ts +198 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.ts +243 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOverlay.test.ts +40 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOverlay.ts +71 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleResource.test.ts +41 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleResource.ts +57 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTabData.test.ts +63 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTabData.ts +129 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleToken.test.ts +41 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.test.ts +155 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.ts +187 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/markdown.test.ts +76 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/markdown.ts +73 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidLoader.test.ts +27 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidLoader.ts +71 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.test.ts +56 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.ts +51 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/colors.test.ts +34 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/colors.ts +73 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/fileStatus.test.ts +35 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/fileStatus.ts +21 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/grouping.test.ts +91 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/grouping.ts +79 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/itemIcons.test.ts +97 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/itemIcons.ts +95 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/operations.test.ts +37 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/operations.ts +35 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/overlay.test.ts +124 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/overlay.ts +101 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/relativeTime.test.ts +52 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/relativeTime.ts +51 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/types.ts +141 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +79 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.tsx +109 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.test.tsx +74 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.tsx +137 -11
- package/src/adapter/entry-points/console/ui/src/features/console/testing/fixtures.ts +244 -0
- package/src/adapter/entry-points/console/ui/src/index.css +352 -2
- package/src/adapter/entry-points/console/ui/tsconfig.json +1 -0
- package/src/adapter/entry-points/console/ui/vite.config.ts +5 -0
- package/src/adapter/entry-points/console/ui-dist/assets/index-BU6p3cGU.css +1 -0
- package/src/adapter/entry-points/console/ui-dist/assets/index-PtVrAcBb.js +100 -0
- package/src/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/src/adapter/entry-points/handlers/consoleListsWriter.test.ts +27 -2
- package/src/adapter/entry-points/handlers/consoleListsWriter.ts +1 -0
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +25 -0
- package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +4 -0
- package/src/domain/usecases/console/GenerateConsoleListsUseCase.test.ts +26 -0
- package/src/domain/usecases/console/GenerateConsoleListsUseCase.ts +17 -1
- package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
- package/types/adapter/entry-points/cli/projectConfig.d.ts +1 -0
- package/types/adapter/entry-points/cli/projectConfig.d.ts.map +1 -1
- package/types/adapter/entry-points/console/consoleOperationApi.d.ts +6 -2
- package/types/adapter/entry-points/console/consoleOperationApi.d.ts.map +1 -1
- package/types/adapter/entry-points/console/consoleProjectResolver.d.ts +6 -0
- package/types/adapter/entry-points/console/consoleProjectResolver.d.ts.map +1 -0
- package/types/adapter/entry-points/console/consoleServer.d.ts +2 -3
- package/types/adapter/entry-points/console/consoleServer.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/consoleListsWriter.d.ts.map +1 -1
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts +1 -0
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts +1 -0
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts.map +1 -1
- package/types/domain/usecases/console/GenerateConsoleListsUseCase.d.ts +2 -1
- package/types/domain/usecases/console/GenerateConsoleListsUseCase.d.ts.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/index-DDjYPXRT.js +0 -49
- package/bin/adapter/entry-points/console/ui-dist/assets/index-DHlBLm7d.css +0 -1
- package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleListView.stories.tsx +0 -44
- package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleListView.tsx +0 -58
- package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleTabBar.stories.tsx +0 -34
- package/src/adapter/entry-points/console/ui/src/features/console/components/ConsoleTabBar.tsx +0 -32
- package/src/adapter/entry-points/console/ui/src/features/console/fixtures.ts +0 -47
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleList.ts +0 -78
- package/src/adapter/entry-points/console/ui/src/features/console/types.ts +0 -69
- package/src/adapter/entry-points/console/ui-dist/assets/index-DDjYPXRT.js +0 -49
- package/src/adapter/entry-points/console/ui-dist/assets/index-DHlBLm7d.css +0 -1
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { mock } from 'jest-mock-extended';
|
|
2
|
+
import { Project } from '../../../domain/entities/Project';
|
|
3
|
+
import {
|
|
4
|
+
buildPjcodeToProjectUrl,
|
|
5
|
+
createConsoleProjectResolver,
|
|
6
|
+
} from './consoleProjectResolver';
|
|
7
|
+
|
|
8
|
+
describe('buildPjcodeToProjectUrl', () => {
|
|
9
|
+
it('adds the default pjcode entry when it is not already present', () => {
|
|
10
|
+
const mapping = buildPjcodeToProjectUrl(
|
|
11
|
+
'umino',
|
|
12
|
+
'https://github.com/orgs/umino/projects/1',
|
|
13
|
+
{ xmile: 'https://github.com/orgs/xmile/projects/2' },
|
|
14
|
+
);
|
|
15
|
+
expect(mapping).toEqual({
|
|
16
|
+
umino: 'https://github.com/orgs/umino/projects/1',
|
|
17
|
+
xmile: 'https://github.com/orgs/xmile/projects/2',
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('keeps an explicit default pjcode entry from consoleProjects', () => {
|
|
22
|
+
const mapping = buildPjcodeToProjectUrl(
|
|
23
|
+
'umino',
|
|
24
|
+
'https://github.com/orgs/umino/projects/1',
|
|
25
|
+
{ umino: 'https://github.com/orgs/umino/projects/9' },
|
|
26
|
+
);
|
|
27
|
+
expect(mapping.umino).toBe('https://github.com/orgs/umino/projects/9');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('uses only the default entry when no consoleProjects mapping is configured', () => {
|
|
31
|
+
const mapping = buildPjcodeToProjectUrl(
|
|
32
|
+
'umino',
|
|
33
|
+
'https://github.com/orgs/umino/projects/1',
|
|
34
|
+
null,
|
|
35
|
+
);
|
|
36
|
+
expect(mapping).toEqual({
|
|
37
|
+
umino: 'https://github.com/orgs/umino/projects/1',
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('createConsoleProjectResolver', () => {
|
|
43
|
+
const uminoProject: Project = { ...mock<Project>(), id: 'PVT_umino' };
|
|
44
|
+
const xmileProject: Project = { ...mock<Project>(), id: 'PVT_xmile' };
|
|
45
|
+
|
|
46
|
+
it('resolves a known pjcode to its loaded project', async () => {
|
|
47
|
+
const loadProject = jest.fn(async (url: string) =>
|
|
48
|
+
url.includes('umino') ? uminoProject : xmileProject,
|
|
49
|
+
);
|
|
50
|
+
const resolver = createConsoleProjectResolver(
|
|
51
|
+
{
|
|
52
|
+
umino: 'https://github.com/orgs/umino/projects/1',
|
|
53
|
+
xmile: 'https://github.com/orgs/xmile/projects/2',
|
|
54
|
+
},
|
|
55
|
+
loadProject,
|
|
56
|
+
);
|
|
57
|
+
await expect(resolver('umino')).resolves.toEqual({
|
|
58
|
+
pjcode: 'umino',
|
|
59
|
+
project: uminoProject,
|
|
60
|
+
});
|
|
61
|
+
await expect(resolver('xmile')).resolves.toEqual({
|
|
62
|
+
pjcode: 'xmile',
|
|
63
|
+
project: xmileProject,
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('returns null for a pjcode that has no configured project url', async () => {
|
|
68
|
+
const loadProject = jest.fn(async () => uminoProject);
|
|
69
|
+
const resolver = createConsoleProjectResolver(
|
|
70
|
+
{ umino: 'https://github.com/orgs/umino/projects/1' },
|
|
71
|
+
loadProject,
|
|
72
|
+
);
|
|
73
|
+
await expect(resolver('unknown')).resolves.toBeNull();
|
|
74
|
+
expect(loadProject).not.toHaveBeenCalled();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('returns null when the project fails to load', async () => {
|
|
78
|
+
const loadProject = jest.fn(async () => null);
|
|
79
|
+
const resolver = createConsoleProjectResolver(
|
|
80
|
+
{ umino: 'https://github.com/orgs/umino/projects/1' },
|
|
81
|
+
loadProject,
|
|
82
|
+
);
|
|
83
|
+
await expect(resolver('umino')).resolves.toBeNull();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('loads each project at most once and serves later calls from cache', async () => {
|
|
87
|
+
const loadProject = jest.fn(async () => uminoProject);
|
|
88
|
+
const resolver = createConsoleProjectResolver(
|
|
89
|
+
{ umino: 'https://github.com/orgs/umino/projects/1' },
|
|
90
|
+
loadProject,
|
|
91
|
+
);
|
|
92
|
+
await resolver('umino');
|
|
93
|
+
await resolver('umino');
|
|
94
|
+
expect(loadProject).toHaveBeenCalledTimes(1);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Project } from '../../../domain/entities/Project';
|
|
2
|
+
import {
|
|
3
|
+
ConsoleProjectBinding,
|
|
4
|
+
ConsoleProjectResolver,
|
|
5
|
+
} from './consoleOperationApi';
|
|
6
|
+
|
|
7
|
+
export type ConsoleProjectLoader = (
|
|
8
|
+
projectUrl: string,
|
|
9
|
+
) => Promise<Project | null>;
|
|
10
|
+
|
|
11
|
+
export const buildPjcodeToProjectUrl = (
|
|
12
|
+
defaultPjcode: string,
|
|
13
|
+
defaultProjectUrl: string,
|
|
14
|
+
consoleProjects: Record<string, string> | null,
|
|
15
|
+
): Record<string, string> => {
|
|
16
|
+
const mapping: Record<string, string> = {};
|
|
17
|
+
if (consoleProjects !== null) {
|
|
18
|
+
for (const [pjcode, projectUrl] of Object.entries(consoleProjects)) {
|
|
19
|
+
mapping[pjcode] = projectUrl;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (!(defaultPjcode in mapping)) {
|
|
23
|
+
mapping[defaultPjcode] = defaultProjectUrl;
|
|
24
|
+
}
|
|
25
|
+
return mapping;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const createConsoleProjectResolver = (
|
|
29
|
+
pjcodeToProjectUrl: Record<string, string>,
|
|
30
|
+
loadProject: ConsoleProjectLoader,
|
|
31
|
+
): ConsoleProjectResolver => {
|
|
32
|
+
const cache = new Map<string, ConsoleProjectBinding>();
|
|
33
|
+
return async (pjcode: string): Promise<ConsoleProjectBinding | null> => {
|
|
34
|
+
const cached = cache.get(pjcode);
|
|
35
|
+
if (cached !== undefined) {
|
|
36
|
+
return cached;
|
|
37
|
+
}
|
|
38
|
+
const projectUrl = pjcodeToProjectUrl[pjcode];
|
|
39
|
+
if (projectUrl === undefined) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const project = await loadProject(projectUrl);
|
|
43
|
+
if (project === null) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const binding: ConsoleProjectBinding = { pjcode, project };
|
|
47
|
+
cache.set(pjcode, binding);
|
|
48
|
+
return binding;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
@@ -480,7 +480,6 @@ describe('consoleServer new routes integration', () => {
|
|
|
480
480
|
accessToken: testToken,
|
|
481
481
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
482
482
|
consoleDataOutputDir: dataDir,
|
|
483
|
-
pjcode: 'umino',
|
|
484
483
|
port: 0,
|
|
485
484
|
});
|
|
486
485
|
try {
|
|
@@ -546,9 +545,9 @@ describe('consoleServer new routes integration', () => {
|
|
|
546
545
|
accessToken: testToken,
|
|
547
546
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
548
547
|
consoleDataOutputDir: dataDir,
|
|
549
|
-
pjcode: 'umino',
|
|
550
548
|
issueRepository,
|
|
551
|
-
|
|
549
|
+
resolveProject: async (pjcode) =>
|
|
550
|
+
pjcode === 'umino' ? { pjcode, project: buildProject() } : null,
|
|
552
551
|
port: 0,
|
|
553
552
|
});
|
|
554
553
|
try {
|
|
@@ -557,6 +556,7 @@ describe('consoleServer new routes integration', () => {
|
|
|
557
556
|
'POST',
|
|
558
557
|
`/api/review?k=${testToken}`,
|
|
559
558
|
{
|
|
559
|
+
pjcode: 'umino',
|
|
560
560
|
action: 'approve',
|
|
561
561
|
prUrl: 'https://github.com/o/r/pull/1',
|
|
562
562
|
projectItemId: 'PVTI_op',
|
|
@@ -583,7 +583,8 @@ describe('consoleServer new routes integration', () => {
|
|
|
583
583
|
uiDistDir: path.join(tmpDir, 'ui-dist'),
|
|
584
584
|
consoleDataOutputDir: null,
|
|
585
585
|
issueRepository,
|
|
586
|
-
|
|
586
|
+
resolveProject: async (pjcode) =>
|
|
587
|
+
pjcode === 'umino' ? { pjcode, project: buildProject() } : null,
|
|
587
588
|
port: 0,
|
|
588
589
|
});
|
|
589
590
|
try {
|
|
@@ -2,7 +2,6 @@ import * as http from 'http';
|
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
4
|
import { IssueRepository } from '../../../domain/usecases/adapter-interfaces/IssueRepository';
|
|
5
|
-
import { Project } from '../../../domain/entities/Project';
|
|
6
5
|
import {
|
|
7
6
|
CONSOLE_LIST_TAB_NAMES,
|
|
8
7
|
buildConsoleDataResponse,
|
|
@@ -19,6 +18,7 @@ import {
|
|
|
19
18
|
} from './consoleReadApi';
|
|
20
19
|
import {
|
|
21
20
|
ConsoleOperationContext,
|
|
21
|
+
ConsoleProjectResolver,
|
|
22
22
|
handleIntmux,
|
|
23
23
|
handleReview,
|
|
24
24
|
handleTriage,
|
|
@@ -153,9 +153,8 @@ export type ConsoleServerOptions = {
|
|
|
153
153
|
accessToken: string;
|
|
154
154
|
uiDistDir: string;
|
|
155
155
|
consoleDataOutputDir: string | null;
|
|
156
|
-
pjcode?: string | null;
|
|
157
156
|
issueRepository?: IssueRepository | null;
|
|
158
|
-
|
|
157
|
+
resolveProject?: ConsoleProjectResolver | null;
|
|
159
158
|
issueTitleStateCache?: IssueTitleStateCache | null;
|
|
160
159
|
};
|
|
161
160
|
|
|
@@ -291,15 +290,14 @@ const handleOperationApi = async (
|
|
|
291
290
|
body: Record<string, unknown>,
|
|
292
291
|
): Promise<{ statusCode: number; body: unknown } | null> => {
|
|
293
292
|
const issueRepository = options.issueRepository ?? null;
|
|
294
|
-
const
|
|
295
|
-
if (issueRepository === null ||
|
|
293
|
+
const resolveProject = options.resolveProject ?? null;
|
|
294
|
+
if (issueRepository === null || resolveProject === null) {
|
|
296
295
|
return null;
|
|
297
296
|
}
|
|
298
297
|
const context: ConsoleOperationContext = {
|
|
299
298
|
issueRepository,
|
|
300
|
-
|
|
299
|
+
resolveProject,
|
|
301
300
|
consoleDataOutputDir: options.consoleDataOutputDir,
|
|
302
|
-
pjcode: options.pjcode ?? null,
|
|
303
301
|
};
|
|
304
302
|
switch (requestPath) {
|
|
305
303
|
case '/api/review':
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import {
|
|
3
|
+
consoleMarkdownBodyFixture,
|
|
4
|
+
consoleMermaidBodyFixture,
|
|
5
|
+
} from '../../testing/fixtures';
|
|
6
|
+
import { ConsoleMarkdownContent } from './ConsoleMarkdownContent';
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof ConsoleMarkdownContent> = {
|
|
9
|
+
title: 'Console/ConsoleMarkdownContent',
|
|
10
|
+
component: ConsoleMarkdownContent,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default meta;
|
|
14
|
+
|
|
15
|
+
type Story = StoryObj<typeof ConsoleMarkdownContent>;
|
|
16
|
+
|
|
17
|
+
export const RichMarkdown: Story = {
|
|
18
|
+
args: { body: consoleMarkdownBodyFixture },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const WithMermaidFence: Story = {
|
|
22
|
+
args: { body: consoleMermaidBodyFixture },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const Empty: Story = {
|
|
26
|
+
args: { body: '' },
|
|
27
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { render, waitFor } from '@testing-library/react';
|
|
2
|
+
import { ConsoleMarkdownContent } from './ConsoleMarkdownContent';
|
|
3
|
+
|
|
4
|
+
jest.mock('../../lib/mermaidLoader', () => ({
|
|
5
|
+
renderMermaidToSvg: jest.fn(
|
|
6
|
+
async () => '<svg data-testid="mermaid-svg"></svg>',
|
|
7
|
+
),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
describe('ConsoleMarkdownContent', () => {
|
|
11
|
+
it('renders markdown body content', () => {
|
|
12
|
+
const { container } = render(
|
|
13
|
+
<ConsoleMarkdownContent body={'## Heading\n\n- bullet'} />,
|
|
14
|
+
);
|
|
15
|
+
expect(container.querySelector('h2')).not.toBeNull();
|
|
16
|
+
expect(container.querySelector('li')).not.toBeNull();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('shows the empty message for a blank body', () => {
|
|
20
|
+
const { getByText } = render(<ConsoleMarkdownContent body=" " />);
|
|
21
|
+
expect(getByText('No description provided.')).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('renders a mermaid fence via the diagram component', async () => {
|
|
25
|
+
const { container } = render(
|
|
26
|
+
<ConsoleMarkdownContent
|
|
27
|
+
body={'intro\n\n```mermaid\ngraph TD; A-->B;\n```'}
|
|
28
|
+
/>,
|
|
29
|
+
);
|
|
30
|
+
await waitFor(() => {
|
|
31
|
+
expect(
|
|
32
|
+
container.querySelector('.console-mermaid-rendered'),
|
|
33
|
+
).not.toBeNull();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
renderMarkdownToSafeHtml,
|
|
4
|
+
splitMarkdownSegments,
|
|
5
|
+
} from '../../lib/markdown';
|
|
6
|
+
import { ConsoleMermaidDiagram } from './ConsoleMermaidDiagram';
|
|
7
|
+
|
|
8
|
+
export type ConsoleMarkdownViewProps = {
|
|
9
|
+
body: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type ConsoleMarkdownHtmlBlockProps = {
|
|
13
|
+
source: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const ConsoleMarkdownHtmlBlock = ({
|
|
17
|
+
source,
|
|
18
|
+
}: ConsoleMarkdownHtmlBlockProps) => {
|
|
19
|
+
const html = useMemo(() => renderMarkdownToSafeHtml(source), [source]);
|
|
20
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const container = containerRef.current;
|
|
24
|
+
if (container !== null) {
|
|
25
|
+
container.innerHTML = html;
|
|
26
|
+
}
|
|
27
|
+
}, [html]);
|
|
28
|
+
|
|
29
|
+
return <div ref={containerRef} className="console-markdown" />;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const ConsoleMarkdownContent = ({ body }: ConsoleMarkdownViewProps) => {
|
|
33
|
+
const segments = useMemo(() => splitMarkdownSegments(body), [body]);
|
|
34
|
+
|
|
35
|
+
if (body.trim() === '') {
|
|
36
|
+
return <p className="console-markdown-empty">No description provided.</p>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div className="console-markdown-view">
|
|
41
|
+
{segments.map((segment) =>
|
|
42
|
+
segment.kind === 'mermaid' ? (
|
|
43
|
+
<ConsoleMermaidDiagram key={segment.key} code={segment.code} />
|
|
44
|
+
) : (
|
|
45
|
+
<ConsoleMarkdownHtmlBlock key={segment.key} source={segment.source} />
|
|
46
|
+
),
|
|
47
|
+
)}
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { consoleMermaidCodeFixture } from '../../testing/fixtures';
|
|
3
|
+
import { ConsoleMermaidDiagram } from './ConsoleMermaidDiagram';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof ConsoleMermaidDiagram> = {
|
|
6
|
+
title: 'Console/ConsoleMermaidDiagram',
|
|
7
|
+
component: ConsoleMermaidDiagram,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
|
|
12
|
+
type Story = StoryObj<typeof ConsoleMermaidDiagram>;
|
|
13
|
+
|
|
14
|
+
export const SequenceDiagram: Story = {
|
|
15
|
+
args: { code: consoleMermaidCodeFixture },
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const FlowDiagram: Story = {
|
|
19
|
+
args: {
|
|
20
|
+
code: 'graph TD;\n Start-->Fetch;\n Fetch-->Render;\n Render-->Done;',
|
|
21
|
+
},
|
|
22
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { render, waitFor } from '@testing-library/react';
|
|
2
|
+
import { renderMermaidToSvg } from '../../lib/mermaidLoader';
|
|
3
|
+
import { ConsoleMermaidDiagram } from './ConsoleMermaidDiagram';
|
|
4
|
+
|
|
5
|
+
jest.mock('../../lib/mermaidLoader', () => ({
|
|
6
|
+
renderMermaidToSvg: jest.fn(),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
const mockedRender = renderMermaidToSvg as jest.MockedFunction<
|
|
10
|
+
typeof renderMermaidToSvg
|
|
11
|
+
>;
|
|
12
|
+
|
|
13
|
+
describe('ConsoleMermaidDiagram', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
mockedRender.mockReset();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('renders the sanitized svg once ready', async () => {
|
|
19
|
+
mockedRender.mockResolvedValue('<svg id="ok"></svg>');
|
|
20
|
+
const { container } = render(
|
|
21
|
+
<ConsoleMermaidDiagram code="graph TD; A-->B;" />,
|
|
22
|
+
);
|
|
23
|
+
await waitFor(() => {
|
|
24
|
+
expect(container.querySelector('svg#ok')).not.toBeNull();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('shows the source and an error note when rendering fails', async () => {
|
|
29
|
+
mockedRender.mockRejectedValue(new Error('parse error'));
|
|
30
|
+
const { getByText } = render(<ConsoleMermaidDiagram code="bad diagram" />);
|
|
31
|
+
await waitFor(() => {
|
|
32
|
+
expect(
|
|
33
|
+
getByText(/Mermaid render error: parse error/),
|
|
34
|
+
).toBeInTheDocument();
|
|
35
|
+
});
|
|
36
|
+
expect(getByText('bad diagram')).toBeInTheDocument();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { renderMermaidToSvg } from '../../lib/mermaidLoader';
|
|
3
|
+
|
|
4
|
+
export type ConsoleMermaidDiagramProps = {
|
|
5
|
+
code: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
type MermaidRenderState =
|
|
9
|
+
| { status: 'loading' }
|
|
10
|
+
| { status: 'ready'; svg: string }
|
|
11
|
+
| { status: 'error'; message: string };
|
|
12
|
+
|
|
13
|
+
export const ConsoleMermaidDiagram = ({ code }: ConsoleMermaidDiagramProps) => {
|
|
14
|
+
const [state, setState] = useState<MermaidRenderState>({ status: 'loading' });
|
|
15
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
let cancelled = false;
|
|
19
|
+
setState({ status: 'loading' });
|
|
20
|
+
renderMermaidToSvg(code)
|
|
21
|
+
.then((svg) => {
|
|
22
|
+
if (!cancelled) {
|
|
23
|
+
setState({ status: 'ready', svg });
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
.catch((cause: unknown) => {
|
|
27
|
+
if (!cancelled) {
|
|
28
|
+
setState({
|
|
29
|
+
status: 'error',
|
|
30
|
+
message: cause instanceof Error ? cause.message : String(cause),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
return () => {
|
|
35
|
+
cancelled = true;
|
|
36
|
+
};
|
|
37
|
+
}, [code]);
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const container = containerRef.current;
|
|
41
|
+
if (container === null) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
container.innerHTML = state.status === 'ready' ? state.svg : '';
|
|
45
|
+
}, [state]);
|
|
46
|
+
|
|
47
|
+
if (state.status === 'loading') {
|
|
48
|
+
return <div className="console-mermaid-loading">Rendering diagram...</div>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (state.status === 'error') {
|
|
52
|
+
return (
|
|
53
|
+
<div className="console-mermaid">
|
|
54
|
+
<div className="console-mermaid-error">
|
|
55
|
+
Mermaid render error: {state.message}
|
|
56
|
+
</div>
|
|
57
|
+
<pre className="console-mermaid-source">
|
|
58
|
+
<code>{code}</code>
|
|
59
|
+
</pre>
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return <div ref={containerRef} className="console-mermaid-rendered" />;
|
|
65
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { consoleChangedFilesFixture } from '../../testing/fixtures';
|
|
3
|
+
import { ConsoleChangedFileList } from './ConsoleChangedFileList';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof ConsoleChangedFileList> = {
|
|
6
|
+
title: 'Console/ConsoleChangedFileList',
|
|
7
|
+
component: ConsoleChangedFileList,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default meta;
|
|
11
|
+
|
|
12
|
+
type Story = StoryObj<typeof ConsoleChangedFileList>;
|
|
13
|
+
|
|
14
|
+
export const WithFiles: Story = {
|
|
15
|
+
args: { files: consoleChangedFilesFixture, isLoading: false, error: null },
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const Loading: Story = {
|
|
19
|
+
args: { files: [], isLoading: true, error: null },
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const Empty: Story = {
|
|
23
|
+
args: { files: [], isLoading: false, error: null },
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const ErrorState: Story = {
|
|
27
|
+
args: { files: [], isLoading: false, error: 'HTTP 502' },
|
|
28
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import { consoleChangedFilesFixture } from '../../testing/fixtures';
|
|
3
|
+
import { ConsoleChangedFileList } from './ConsoleChangedFileList';
|
|
4
|
+
|
|
5
|
+
describe('ConsoleChangedFileList', () => {
|
|
6
|
+
it('renders each file path, status badge and additions/deletions', () => {
|
|
7
|
+
const { getByText, getAllByText } = render(
|
|
8
|
+
<ConsoleChangedFileList
|
|
9
|
+
files={consoleChangedFilesFixture}
|
|
10
|
+
isLoading={false}
|
|
11
|
+
error={null}
|
|
12
|
+
/>,
|
|
13
|
+
);
|
|
14
|
+
expect(
|
|
15
|
+
getByText('src/adapter/entry-points/console/consoleServer.ts'),
|
|
16
|
+
).toBeInTheDocument();
|
|
17
|
+
expect(getByText('+312')).toBeInTheDocument();
|
|
18
|
+
expect(getAllByText('A').length).toBe(2);
|
|
19
|
+
expect(getByText('M')).toBeInTheDocument();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('shows the loading state', () => {
|
|
23
|
+
const { getByText } = render(
|
|
24
|
+
<ConsoleChangedFileList files={[]} isLoading error={null} />,
|
|
25
|
+
);
|
|
26
|
+
expect(getByText('Loading changed files...')).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('shows the empty state', () => {
|
|
30
|
+
const { getByText } = render(
|
|
31
|
+
<ConsoleChangedFileList files={[]} isLoading={false} error={null} />,
|
|
32
|
+
);
|
|
33
|
+
expect(getByText('No changed files.')).toBeInTheDocument();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('shows the error state', () => {
|
|
37
|
+
const { getByRole } = render(
|
|
38
|
+
<ConsoleChangedFileList files={[]} isLoading={false} error="HTTP 502" />,
|
|
39
|
+
);
|
|
40
|
+
expect(getByRole('alert')).toHaveTextContent('HTTP 502');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { fileStatusBadge } from '../../logic/fileStatus';
|
|
2
|
+
import type { ConsoleChangedFile } from '../../logic/types';
|
|
3
|
+
|
|
4
|
+
export type ConsoleChangedFileListProps = {
|
|
5
|
+
files: ConsoleChangedFile[];
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
error: string | null;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const ConsoleChangedFileList = ({
|
|
11
|
+
files,
|
|
12
|
+
isLoading,
|
|
13
|
+
error,
|
|
14
|
+
}: ConsoleChangedFileListProps) => {
|
|
15
|
+
if (error !== null) {
|
|
16
|
+
return (
|
|
17
|
+
<p role="alert" className="console-files-error">
|
|
18
|
+
Failed to load changed files: {error}
|
|
19
|
+
</p>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (isLoading) {
|
|
24
|
+
return <p className="console-files-loading">Loading changed files...</p>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (files.length === 0) {
|
|
28
|
+
return <p className="console-files-empty">No changed files.</p>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<ul className="console-files">
|
|
33
|
+
{files.map((file) => {
|
|
34
|
+
const badge = fileStatusBadge(file.status);
|
|
35
|
+
return (
|
|
36
|
+
<li key={file.path} className="console-file">
|
|
37
|
+
<span
|
|
38
|
+
className="console-file-badge"
|
|
39
|
+
style={{ color: badge.color, borderColor: badge.color }}
|
|
40
|
+
>
|
|
41
|
+
{badge.label}
|
|
42
|
+
</span>
|
|
43
|
+
<span className="console-file-path">{file.path}</span>
|
|
44
|
+
<span className="console-file-stat console-file-add">
|
|
45
|
+
+{file.additions}
|
|
46
|
+
</span>
|
|
47
|
+
<span className="console-file-stat console-file-del">
|
|
48
|
+
-{file.deletions}
|
|
49
|
+
</span>
|
|
50
|
+
</li>
|
|
51
|
+
);
|
|
52
|
+
})}
|
|
53
|
+
</ul>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { consoleCommentsFixture } from '../../testing/fixtures';
|
|
3
|
+
import { ConsoleCommentList } from './ConsoleCommentList';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof ConsoleCommentList> = {
|
|
6
|
+
title: 'Console/ConsoleCommentList',
|
|
7
|
+
component: ConsoleCommentList,
|
|
8
|
+
args: { now: Date.parse('2026-06-19T12:00:00.000Z') },
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default meta;
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof ConsoleCommentList>;
|
|
14
|
+
|
|
15
|
+
export const WithComments: Story = {
|
|
16
|
+
args: { comments: consoleCommentsFixture, isLoading: false, error: null },
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const Loading: Story = {
|
|
20
|
+
args: { comments: [], isLoading: true, error: null },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const Empty: Story = {
|
|
24
|
+
args: { comments: [], isLoading: false, error: null },
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const ErrorState: Story = {
|
|
28
|
+
args: { comments: [], isLoading: false, error: 'HTTP 500' },
|
|
29
|
+
};
|