github-issue-tower-defence-management 1.112.6 → 1.114.0
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 +14 -0
- package/README.md +1 -1
- package/bin/adapter/entry-points/cli/index.js +1 -0
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/entry-points/console/consoleReadApi.js +58 -2
- package/bin/adapter/entry-points/console/consoleReadApi.js.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/{index-DKFbU8lr.css → index-Blb_xqIx.css} +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/index-DQyZ8KpG.js +101 -0
- package/bin/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/bin/adapter/entry-points/console/webServer.js +6 -0
- package/bin/adapter/entry-points/console/webServer.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/cli/index.ts +5 -1
- package/src/adapter/entry-points/console/consoleReadApi.test.ts +171 -0
- package/src/adapter/entry-points/console/consoleReadApi.ts +92 -1
- package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +44 -0
- package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +31 -8
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.stories.tsx +16 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.test.tsx +61 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.tsx +59 -5
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.stories.tsx +76 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.test.tsx +56 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.tsx +39 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.tsx +4 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.stories.tsx +64 -3
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.test.tsx +68 -2
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.tsx +21 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.test.tsx +46 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.tsx +12 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.stories.tsx +41 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.test.tsx +72 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.tsx +71 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleCaches.ts +5 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.test.ts +21 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.ts +18 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleReferenceLink.test.ts +48 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleReferenceLink.ts +39 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.test.ts +66 -2
- package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.ts +38 -6
- package/src/adapter/entry-points/console/ui/src/features/console/logic/references.test.ts +80 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/references.ts +44 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/types.ts +10 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +10 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.tsx +13 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleReferenceLinkContainer.tsx +25 -0
- package/src/adapter/entry-points/console/ui/src/features/console/testing/fixtures.ts +40 -0
- package/src/adapter/entry-points/console/ui/src/index.css +35 -0
- package/src/adapter/entry-points/console/ui-dist/assets/{index-DKFbU8lr.css → index-Blb_xqIx.css} +1 -1
- package/src/adapter/entry-points/console/ui-dist/assets/index-DQyZ8KpG.js +101 -0
- package/src/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/src/adapter/entry-points/console/webServer.test.ts +52 -1
- package/src/adapter/entry-points/console/webServer.ts +13 -0
- package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
- package/types/adapter/entry-points/console/consoleReadApi.d.ts +21 -0
- package/types/adapter/entry-points/console/consoleReadApi.d.ts.map +1 -1
- package/types/adapter/entry-points/console/webServer.d.ts +2 -1
- package/types/adapter/entry-points/console/webServer.d.ts.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/index-DUygHx5u.js +0 -101
- package/src/adapter/entry-points/console/ui-dist/assets/index-DUygHx5u.js +0 -101
|
@@ -33,4 +33,65 @@ describe('ConsoleMarkdownContent', () => {
|
|
|
33
33
|
).not.toBeNull();
|
|
34
34
|
});
|
|
35
35
|
});
|
|
36
|
+
|
|
37
|
+
it('decorates pull request and issue links via the reference renderer', async () => {
|
|
38
|
+
const body =
|
|
39
|
+
'See https://github.com/octo/repo/pull/7 and https://github.com/octo/repo/issues/9 for details.';
|
|
40
|
+
const renderReferenceLink = jest.fn((href: string) => (
|
|
41
|
+
<span className="decorated-reference" data-href={href}>
|
|
42
|
+
decorated:{href}
|
|
43
|
+
</span>
|
|
44
|
+
));
|
|
45
|
+
const { container } = render(
|
|
46
|
+
<ConsoleMarkdownContent
|
|
47
|
+
body={body}
|
|
48
|
+
renderReferenceLink={renderReferenceLink}
|
|
49
|
+
/>,
|
|
50
|
+
);
|
|
51
|
+
await waitFor(() => {
|
|
52
|
+
expect(container.querySelectorAll('.decorated-reference').length).toBe(2);
|
|
53
|
+
});
|
|
54
|
+
const decoratedHrefs = Array.from(
|
|
55
|
+
container.querySelectorAll('.decorated-reference'),
|
|
56
|
+
).map((node) => node.getAttribute('data-href'));
|
|
57
|
+
expect(decoratedHrefs).toEqual([
|
|
58
|
+
'https://github.com/octo/repo/pull/7',
|
|
59
|
+
'https://github.com/octo/repo/issues/9',
|
|
60
|
+
]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('leaves non-issue links untouched', async () => {
|
|
64
|
+
const body =
|
|
65
|
+
'Docs at https://example.com/page and source https://github.com/octo/repo/blob/main/file.ts';
|
|
66
|
+
const renderReferenceLink = jest.fn(() => (
|
|
67
|
+
<span className="decorated-reference">decorated</span>
|
|
68
|
+
));
|
|
69
|
+
const { container } = render(
|
|
70
|
+
<ConsoleMarkdownContent
|
|
71
|
+
body={body}
|
|
72
|
+
renderReferenceLink={renderReferenceLink}
|
|
73
|
+
/>,
|
|
74
|
+
);
|
|
75
|
+
await waitFor(() => {
|
|
76
|
+
const anchors = container.querySelectorAll('a[href]');
|
|
77
|
+
expect(anchors.length).toBe(2);
|
|
78
|
+
});
|
|
79
|
+
expect(renderReferenceLink).not.toHaveBeenCalled();
|
|
80
|
+
expect(container.querySelector('.decorated-reference')).toBeNull();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('keeps reference links as plain anchors when no renderer is provided', () => {
|
|
84
|
+
const { container } = render(
|
|
85
|
+
<ConsoleMarkdownContent
|
|
86
|
+
body={'See https://github.com/octo/repo/pull/7 now.'}
|
|
87
|
+
/>,
|
|
88
|
+
);
|
|
89
|
+
const anchor = container.querySelector('a[href]');
|
|
90
|
+
expect(anchor?.getAttribute('href')).toBe(
|
|
91
|
+
'https://github.com/octo/repo/pull/7',
|
|
92
|
+
);
|
|
93
|
+
expect(
|
|
94
|
+
container.querySelector('.console-markdown-reference-host'),
|
|
95
|
+
).toBeNull();
|
|
96
|
+
});
|
|
36
97
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { useEffect, useMemo, useRef } from 'react';
|
|
1
|
+
import { type ReactNode, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { createPortal } from 'react-dom';
|
|
2
3
|
import {
|
|
3
4
|
type ImageProxyUrlBuilder,
|
|
4
5
|
rewriteGitHubImageSources,
|
|
@@ -7,21 +8,54 @@ import {
|
|
|
7
8
|
renderMarkdownToSafeHtml,
|
|
8
9
|
splitMarkdownSegments,
|
|
9
10
|
} from '../../lib/markdown';
|
|
11
|
+
import { parseGitHubReferenceUrl } from '../../logic/references';
|
|
10
12
|
import { ConsoleMermaidDiagram } from './ConsoleMermaidDiagram';
|
|
11
13
|
|
|
14
|
+
export type ConsoleReferenceLinkRenderer = (
|
|
15
|
+
href: string,
|
|
16
|
+
fallbackText: string,
|
|
17
|
+
) => ReactNode;
|
|
18
|
+
|
|
12
19
|
export type ConsoleMarkdownViewProps = {
|
|
13
20
|
body: string;
|
|
14
21
|
buildImageProxyUrl?: ImageProxyUrlBuilder;
|
|
22
|
+
renderReferenceLink?: ConsoleReferenceLinkRenderer;
|
|
15
23
|
};
|
|
16
24
|
|
|
17
25
|
type ConsoleMarkdownHtmlBlockProps = {
|
|
18
26
|
source: string;
|
|
19
27
|
buildImageProxyUrl?: ImageProxyUrlBuilder;
|
|
28
|
+
renderReferenceLink?: ConsoleReferenceLinkRenderer;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type ReferenceMount = {
|
|
32
|
+
key: string;
|
|
33
|
+
host: HTMLElement;
|
|
34
|
+
href: string;
|
|
35
|
+
fallbackText: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const collectReferenceMounts = (container: HTMLElement): ReferenceMount[] => {
|
|
39
|
+
const anchors = container.querySelectorAll<HTMLAnchorElement>('a[href]');
|
|
40
|
+
const mounts: ReferenceMount[] = [];
|
|
41
|
+
anchors.forEach((anchor, index) => {
|
|
42
|
+
const href = anchor.getAttribute('href') ?? '';
|
|
43
|
+
if (parseGitHubReferenceUrl(href) === null) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const fallbackText = anchor.textContent ?? href;
|
|
47
|
+
const host = document.createElement('span');
|
|
48
|
+
host.className = 'console-markdown-reference-host';
|
|
49
|
+
anchor.replaceWith(host);
|
|
50
|
+
mounts.push({ key: `${index}:${href}`, host, href, fallbackText });
|
|
51
|
+
});
|
|
52
|
+
return mounts;
|
|
20
53
|
};
|
|
21
54
|
|
|
22
55
|
const ConsoleMarkdownHtmlBlock = ({
|
|
23
56
|
source,
|
|
24
57
|
buildImageProxyUrl,
|
|
58
|
+
renderReferenceLink,
|
|
25
59
|
}: ConsoleMarkdownHtmlBlockProps) => {
|
|
26
60
|
const html = useMemo(() => {
|
|
27
61
|
const safeHtml = renderMarkdownToSafeHtml(source);
|
|
@@ -31,20 +65,39 @@ const ConsoleMarkdownHtmlBlock = ({
|
|
|
31
65
|
return rewriteGitHubImageSources(safeHtml, buildImageProxyUrl);
|
|
32
66
|
}, [source, buildImageProxyUrl]);
|
|
33
67
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
68
|
+
const [referenceMounts, setReferenceMounts] = useState<ReferenceMount[]>([]);
|
|
34
69
|
|
|
35
70
|
useEffect(() => {
|
|
36
71
|
const container = containerRef.current;
|
|
37
|
-
if (container
|
|
38
|
-
|
|
72
|
+
if (container === null) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
container.innerHTML = html;
|
|
76
|
+
if (renderReferenceLink === undefined) {
|
|
77
|
+
setReferenceMounts([]);
|
|
78
|
+
return;
|
|
39
79
|
}
|
|
40
|
-
|
|
80
|
+
setReferenceMounts(collectReferenceMounts(container));
|
|
81
|
+
}, [html, renderReferenceLink]);
|
|
41
82
|
|
|
42
|
-
return
|
|
83
|
+
return (
|
|
84
|
+
<div ref={containerRef} className="console-markdown">
|
|
85
|
+
{renderReferenceLink !== undefined &&
|
|
86
|
+
referenceMounts.map((mount) =>
|
|
87
|
+
createPortal(
|
|
88
|
+
renderReferenceLink(mount.href, mount.fallbackText),
|
|
89
|
+
mount.host,
|
|
90
|
+
mount.key,
|
|
91
|
+
),
|
|
92
|
+
)}
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
43
95
|
};
|
|
44
96
|
|
|
45
97
|
export const ConsoleMarkdownContent = ({
|
|
46
98
|
body,
|
|
47
99
|
buildImageProxyUrl,
|
|
100
|
+
renderReferenceLink,
|
|
48
101
|
}: ConsoleMarkdownViewProps) => {
|
|
49
102
|
const segments = useMemo(() => splitMarkdownSegments(body), [body]);
|
|
50
103
|
|
|
@@ -62,6 +115,7 @@ export const ConsoleMarkdownContent = ({
|
|
|
62
115
|
key={segment.key}
|
|
63
116
|
source={segment.source}
|
|
64
117
|
buildImageProxyUrl={buildImageProxyUrl}
|
|
118
|
+
renderReferenceLink={renderReferenceLink}
|
|
65
119
|
/>
|
|
66
120
|
),
|
|
67
121
|
)}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { ConsoleReferenceLink } from './ConsoleReferenceLink';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof ConsoleReferenceLink> = {
|
|
5
|
+
title: 'Console/ConsoleReferenceLink',
|
|
6
|
+
component: ConsoleReferenceLink,
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default meta;
|
|
10
|
+
|
|
11
|
+
type Story = StoryObj<typeof ConsoleReferenceLink>;
|
|
12
|
+
|
|
13
|
+
export const OpenPullRequest: Story = {
|
|
14
|
+
args: {
|
|
15
|
+
href: 'https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/pull/995',
|
|
16
|
+
fallbackText:
|
|
17
|
+
'https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/pull/995',
|
|
18
|
+
state: {
|
|
19
|
+
state: 'open',
|
|
20
|
+
merged: false,
|
|
21
|
+
isPullRequest: true,
|
|
22
|
+
title: 'Rename serveConsole subcommand to serveWeb',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const MergedPullRequest: Story = {
|
|
28
|
+
args: {
|
|
29
|
+
href: 'https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/pull/851',
|
|
30
|
+
fallbackText:
|
|
31
|
+
'https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/pull/851',
|
|
32
|
+
state: {
|
|
33
|
+
state: 'closed',
|
|
34
|
+
merged: true,
|
|
35
|
+
isPullRequest: true,
|
|
36
|
+
title: 'Add serveConsole subcommand under entry-points',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const OpenIssue: Story = {
|
|
42
|
+
args: {
|
|
43
|
+
href: 'https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/845',
|
|
44
|
+
fallbackText:
|
|
45
|
+
'https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/845',
|
|
46
|
+
state: {
|
|
47
|
+
state: 'open',
|
|
48
|
+
merged: false,
|
|
49
|
+
isPullRequest: false,
|
|
50
|
+
title: 'Scaffold React console UI under entry-points with build bundling',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const ClosedIssue: Story = {
|
|
56
|
+
args: {
|
|
57
|
+
href: 'https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/692',
|
|
58
|
+
fallbackText:
|
|
59
|
+
'https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/692',
|
|
60
|
+
state: {
|
|
61
|
+
state: 'closed',
|
|
62
|
+
merged: false,
|
|
63
|
+
isPullRequest: false,
|
|
64
|
+
title: 'Publish the generated documentation site to GitHub Pages',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const UnresolvedFallback: Story = {
|
|
70
|
+
args: {
|
|
71
|
+
href: 'https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/pull/995',
|
|
72
|
+
fallbackText:
|
|
73
|
+
'https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/pull/995',
|
|
74
|
+
state: null,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import { ConsoleReferenceLink } from './ConsoleReferenceLink';
|
|
3
|
+
|
|
4
|
+
describe('ConsoleReferenceLink', () => {
|
|
5
|
+
const href = 'https://github.com/octo/repo/pull/7';
|
|
6
|
+
|
|
7
|
+
it('renders the state icon and fetched title when state is resolved', () => {
|
|
8
|
+
const { container, getByText } = render(
|
|
9
|
+
<ConsoleReferenceLink
|
|
10
|
+
href={href}
|
|
11
|
+
fallbackText={href}
|
|
12
|
+
state={{
|
|
13
|
+
state: 'open',
|
|
14
|
+
merged: false,
|
|
15
|
+
isPullRequest: true,
|
|
16
|
+
title: 'Decorate PR links',
|
|
17
|
+
}}
|
|
18
|
+
/>,
|
|
19
|
+
);
|
|
20
|
+
expect(getByText('Decorate PR links')).toBeInTheDocument();
|
|
21
|
+
expect(container.querySelector('.console-item-icon')).not.toBeNull();
|
|
22
|
+
const anchor = container.querySelector('a');
|
|
23
|
+
expect(anchor?.getAttribute('href')).toBe(href);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('falls back to the plain link when state is null', () => {
|
|
27
|
+
const { container, getByText } = render(
|
|
28
|
+
<ConsoleReferenceLink
|
|
29
|
+
href={href}
|
|
30
|
+
fallbackText="plain text"
|
|
31
|
+
state={null}
|
|
32
|
+
/>,
|
|
33
|
+
);
|
|
34
|
+
expect(getByText('plain text')).toBeInTheDocument();
|
|
35
|
+
expect(container.querySelector('.console-item-icon')).toBeNull();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('falls back to the plain link when the resolved title is empty', () => {
|
|
39
|
+
const { container } = render(
|
|
40
|
+
<ConsoleReferenceLink
|
|
41
|
+
href={href}
|
|
42
|
+
fallbackText="plain text"
|
|
43
|
+
state={{
|
|
44
|
+
state: 'open',
|
|
45
|
+
merged: false,
|
|
46
|
+
isPullRequest: true,
|
|
47
|
+
title: '',
|
|
48
|
+
}}
|
|
49
|
+
/>,
|
|
50
|
+
);
|
|
51
|
+
expect(container.querySelector('.console-item-icon')).toBeNull();
|
|
52
|
+
expect(
|
|
53
|
+
container.querySelector('.console-markdown-reference-plain'),
|
|
54
|
+
).not.toBeNull();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { referenceStateToIconInput } from '../../logic/references';
|
|
2
|
+
import type { ConsoleIssueState } from '../../logic/types';
|
|
3
|
+
import { ConsoleItemIcon } from '../detail/ConsoleItemIcon';
|
|
4
|
+
|
|
5
|
+
export type ConsoleReferenceLinkProps = {
|
|
6
|
+
href: string;
|
|
7
|
+
fallbackText: string;
|
|
8
|
+
state: ConsoleIssueState | null;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const ConsoleReferenceLink = ({
|
|
12
|
+
href,
|
|
13
|
+
fallbackText,
|
|
14
|
+
state,
|
|
15
|
+
}: ConsoleReferenceLinkProps) => {
|
|
16
|
+
if (state === null || state.title.trim() === '') {
|
|
17
|
+
return (
|
|
18
|
+
<a
|
|
19
|
+
className="console-markdown-reference console-markdown-reference-plain"
|
|
20
|
+
href={href}
|
|
21
|
+
target="_blank"
|
|
22
|
+
rel="noopener noreferrer"
|
|
23
|
+
>
|
|
24
|
+
{fallbackText}
|
|
25
|
+
</a>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return (
|
|
29
|
+
<a
|
|
30
|
+
className="console-markdown-reference"
|
|
31
|
+
href={href}
|
|
32
|
+
target="_blank"
|
|
33
|
+
rel="noopener noreferrer"
|
|
34
|
+
>
|
|
35
|
+
<ConsoleItemIcon {...referenceStateToIconInput(state)} />
|
|
36
|
+
<span className="console-markdown-reference-title">{state.title}</span>
|
|
37
|
+
</a>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
@@ -2,6 +2,7 @@ import { useState } from 'react';
|
|
|
2
2
|
import type { ImageProxyUrlBuilder } from '../../lib/imageProxy';
|
|
3
3
|
import { formatRelativeTime } from '../../logic/relativeTime';
|
|
4
4
|
import type { ConsoleComment } from '../../logic/types';
|
|
5
|
+
import type { ConsoleReferenceLinkRenderer } from '../content/ConsoleMarkdownContent';
|
|
5
6
|
import { ConsoleMarkdownContent } from '../content/ConsoleMarkdownContent';
|
|
6
7
|
|
|
7
8
|
export type ConsoleCommentListProps = {
|
|
@@ -10,6 +11,7 @@ export type ConsoleCommentListProps = {
|
|
|
10
11
|
error: string | null;
|
|
11
12
|
now: number;
|
|
12
13
|
buildImageProxyUrl?: ImageProxyUrlBuilder;
|
|
14
|
+
renderReferenceLink?: ConsoleReferenceLinkRenderer;
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
export const ConsoleCommentList = ({
|
|
@@ -18,6 +20,7 @@ export const ConsoleCommentList = ({
|
|
|
18
20
|
error,
|
|
19
21
|
now,
|
|
20
22
|
buildImageProxyUrl,
|
|
23
|
+
renderReferenceLink,
|
|
21
24
|
}: ConsoleCommentListProps) => {
|
|
22
25
|
const [showAll, setShowAll] = useState<boolean>(false);
|
|
23
26
|
|
|
@@ -64,6 +67,7 @@ export const ConsoleCommentList = ({
|
|
|
64
67
|
<ConsoleMarkdownContent
|
|
65
68
|
body={comment.body}
|
|
66
69
|
buildImageProxyUrl={buildImageProxyUrl}
|
|
70
|
+
renderReferenceLink={renderReferenceLink}
|
|
67
71
|
/>
|
|
68
72
|
</article>
|
|
69
73
|
))}
|
|
@@ -68,7 +68,48 @@ export const PullRequestItem: Story = {
|
|
|
68
68
|
storyName: 'TDPM Console port',
|
|
69
69
|
storyColorEnum: 'BLUE',
|
|
70
70
|
overlayStatus: { name: 'Awaiting Workspace', color: 'BLUE' },
|
|
71
|
-
state: {
|
|
71
|
+
state: {
|
|
72
|
+
state: 'open',
|
|
73
|
+
merged: false,
|
|
74
|
+
isPullRequest: true,
|
|
75
|
+
title: 'Add serveConsole subcommand under entry-points',
|
|
76
|
+
},
|
|
77
|
+
body: consoleMermaidBodyFixture,
|
|
78
|
+
bodyIsLoading: false,
|
|
79
|
+
bodyError: null,
|
|
80
|
+
comments: consoleCommentsFixture,
|
|
81
|
+
commentsAreLoading: false,
|
|
82
|
+
commentsError: null,
|
|
83
|
+
files: consoleChangedFilesFixture,
|
|
84
|
+
filesAreLoading: false,
|
|
85
|
+
filesError: null,
|
|
86
|
+
commits: consoleCommitsFixture,
|
|
87
|
+
commitsAreLoading: false,
|
|
88
|
+
commitsError: null,
|
|
89
|
+
pullRequestStatus: {
|
|
90
|
+
found: true,
|
|
91
|
+
isConflicted: false,
|
|
92
|
+
isPassedAllCiJob: true,
|
|
93
|
+
isCiStateSuccess: true,
|
|
94
|
+
isBranchOutOfDate: false,
|
|
95
|
+
missingRequiredCheckNames: [],
|
|
96
|
+
},
|
|
97
|
+
relatedPullRequests: [],
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const PullRequestItemFailingCiWithConflict: Story = {
|
|
102
|
+
args: {
|
|
103
|
+
item: consoleListItemsFixture[0],
|
|
104
|
+
storyName: 'TDPM Console port',
|
|
105
|
+
storyColorEnum: 'BLUE',
|
|
106
|
+
overlayStatus: { name: 'Awaiting Quality Check', color: 'YELLOW' },
|
|
107
|
+
state: {
|
|
108
|
+
state: 'open',
|
|
109
|
+
merged: false,
|
|
110
|
+
isPullRequest: true,
|
|
111
|
+
title: 'Add serveConsole subcommand under entry-points',
|
|
112
|
+
},
|
|
72
113
|
body: consoleMermaidBodyFixture,
|
|
73
114
|
bodyIsLoading: false,
|
|
74
115
|
bodyError: null,
|
|
@@ -81,6 +122,14 @@ export const PullRequestItem: Story = {
|
|
|
81
122
|
commits: consoleCommitsFixture,
|
|
82
123
|
commitsAreLoading: false,
|
|
83
124
|
commitsError: null,
|
|
125
|
+
pullRequestStatus: {
|
|
126
|
+
found: true,
|
|
127
|
+
isConflicted: true,
|
|
128
|
+
isPassedAllCiJob: false,
|
|
129
|
+
isCiStateSuccess: false,
|
|
130
|
+
isBranchOutOfDate: true,
|
|
131
|
+
missingRequiredCheckNames: ['build', 'test'],
|
|
132
|
+
},
|
|
84
133
|
relatedPullRequests: [],
|
|
85
134
|
},
|
|
86
135
|
};
|
|
@@ -91,7 +140,12 @@ export const IssueWithRichMarkdownBody: Story = {
|
|
|
91
140
|
storyName: 'TDPM Console port',
|
|
92
141
|
storyColorEnum: 'BLUE',
|
|
93
142
|
overlayStatus: null,
|
|
94
|
-
state: {
|
|
143
|
+
state: {
|
|
144
|
+
state: 'open',
|
|
145
|
+
merged: false,
|
|
146
|
+
isPullRequest: false,
|
|
147
|
+
title: 'Scaffold React console UI under entry-points with build bundling',
|
|
148
|
+
},
|
|
95
149
|
body: richMarkdownBody,
|
|
96
150
|
bodyIsLoading: false,
|
|
97
151
|
bodyError: null,
|
|
@@ -104,6 +158,7 @@ export const IssueWithRichMarkdownBody: Story = {
|
|
|
104
158
|
commits: [],
|
|
105
159
|
commitsAreLoading: false,
|
|
106
160
|
commitsError: null,
|
|
161
|
+
pullRequestStatus: null,
|
|
107
162
|
relatedPullRequests: [],
|
|
108
163
|
operationBar: (
|
|
109
164
|
<ConsoleOperationMenu
|
|
@@ -124,7 +179,12 @@ export const IssueWithLinkedPullRequest: Story = {
|
|
|
124
179
|
storyName: 'TDPM Console port',
|
|
125
180
|
storyColorEnum: 'BLUE',
|
|
126
181
|
overlayStatus: null,
|
|
127
|
-
state: {
|
|
182
|
+
state: {
|
|
183
|
+
state: 'open',
|
|
184
|
+
merged: false,
|
|
185
|
+
isPullRequest: false,
|
|
186
|
+
title: 'Scaffold React console UI under entry-points with build bundling',
|
|
187
|
+
},
|
|
128
188
|
body: '## Issue body\n\nThis issue has a linked pull request.',
|
|
129
189
|
bodyIsLoading: false,
|
|
130
190
|
bodyError: null,
|
|
@@ -137,6 +197,7 @@ export const IssueWithLinkedPullRequest: Story = {
|
|
|
137
197
|
commits: [],
|
|
138
198
|
commitsAreLoading: false,
|
|
139
199
|
commitsError: null,
|
|
200
|
+
pullRequestStatus: null,
|
|
140
201
|
relatedPullRequests: [
|
|
141
202
|
{
|
|
142
203
|
pullRequest: consoleRelatedPullRequestsFixture[0],
|
|
@@ -19,7 +19,7 @@ const baseProps = {
|
|
|
19
19
|
storyName: 'TDPM Console port',
|
|
20
20
|
storyColorEnum: 'BLUE' as const,
|
|
21
21
|
overlayStatus: null,
|
|
22
|
-
state: { state: 'open', merged: false, isPullRequest: true },
|
|
22
|
+
state: { state: 'open', merged: false, isPullRequest: true, title: '' },
|
|
23
23
|
body: '## Body heading',
|
|
24
24
|
bodyIsLoading: false,
|
|
25
25
|
bodyError: null,
|
|
@@ -32,6 +32,7 @@ const baseProps = {
|
|
|
32
32
|
commits: consoleCommitsFixture,
|
|
33
33
|
commitsAreLoading: false,
|
|
34
34
|
commitsError: null,
|
|
35
|
+
pullRequestStatus: null,
|
|
35
36
|
relatedPullRequests: [],
|
|
36
37
|
now,
|
|
37
38
|
commentComposer: <div>comment-composer</div>,
|
|
@@ -78,7 +79,12 @@ describe('ConsoleItemDetail', () => {
|
|
|
78
79
|
<ConsoleItemDetail
|
|
79
80
|
item={issueItem}
|
|
80
81
|
{...baseProps}
|
|
81
|
-
state={{
|
|
82
|
+
state={{
|
|
83
|
+
state: 'open',
|
|
84
|
+
merged: false,
|
|
85
|
+
isPullRequest: false,
|
|
86
|
+
title: '',
|
|
87
|
+
}}
|
|
82
88
|
/>,
|
|
83
89
|
);
|
|
84
90
|
expect(queryByText('Changed files')).toBeNull();
|
|
@@ -115,4 +121,64 @@ describe('ConsoleItemDetail', () => {
|
|
|
115
121
|
expect(title).not.toBeNull();
|
|
116
122
|
expect(title?.contains(getByText('Awaiting Workspace'))).toBe(true);
|
|
117
123
|
});
|
|
124
|
+
|
|
125
|
+
it('renders failing CI, missing checks, and conflict badges in the PR header', () => {
|
|
126
|
+
const { getByText, container } = render(
|
|
127
|
+
<ConsoleItemDetail
|
|
128
|
+
item={prItem}
|
|
129
|
+
{...baseProps}
|
|
130
|
+
pullRequestStatus={{
|
|
131
|
+
found: true,
|
|
132
|
+
isConflicted: true,
|
|
133
|
+
isPassedAllCiJob: false,
|
|
134
|
+
isCiStateSuccess: false,
|
|
135
|
+
isBranchOutOfDate: true,
|
|
136
|
+
missingRequiredCheckNames: ['build', 'test'],
|
|
137
|
+
}}
|
|
138
|
+
/>,
|
|
139
|
+
);
|
|
140
|
+
const title = container.querySelector('.console-detail-title');
|
|
141
|
+
expect(title?.contains(getByText('CI failing'))).toBe(true);
|
|
142
|
+
expect(getByText(/missing: build, test/)).toBeInTheDocument();
|
|
143
|
+
expect(getByText('Conflict')).toBeInTheDocument();
|
|
144
|
+
expect(getByText('Out of date')).toBeInTheDocument();
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('renders a passing CI badge and no conflict badge when the PR is healthy', () => {
|
|
148
|
+
const { getByText, queryByText } = render(
|
|
149
|
+
<ConsoleItemDetail
|
|
150
|
+
item={prItem}
|
|
151
|
+
{...baseProps}
|
|
152
|
+
pullRequestStatus={{
|
|
153
|
+
found: true,
|
|
154
|
+
isConflicted: false,
|
|
155
|
+
isPassedAllCiJob: true,
|
|
156
|
+
isCiStateSuccess: true,
|
|
157
|
+
isBranchOutOfDate: false,
|
|
158
|
+
missingRequiredCheckNames: [],
|
|
159
|
+
}}
|
|
160
|
+
/>,
|
|
161
|
+
);
|
|
162
|
+
expect(getByText('CI passing')).toBeInTheDocument();
|
|
163
|
+
expect(queryByText('Conflict')).toBeNull();
|
|
164
|
+
expect(queryByText('Out of date')).toBeNull();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('does not render PR status badges for an issue item', () => {
|
|
168
|
+
const { queryByText } = render(
|
|
169
|
+
<ConsoleItemDetail
|
|
170
|
+
item={issueItem}
|
|
171
|
+
{...baseProps}
|
|
172
|
+
state={{
|
|
173
|
+
state: 'open',
|
|
174
|
+
merged: false,
|
|
175
|
+
isPullRequest: false,
|
|
176
|
+
title: '',
|
|
177
|
+
}}
|
|
178
|
+
pullRequestStatus={null}
|
|
179
|
+
/>,
|
|
180
|
+
);
|
|
181
|
+
expect(queryByText('CI passing')).toBeNull();
|
|
182
|
+
expect(queryByText('CI failing')).toBeNull();
|
|
183
|
+
});
|
|
118
184
|
});
|
|
@@ -13,8 +13,10 @@ import type {
|
|
|
13
13
|
ConsoleIssueState,
|
|
14
14
|
ConsoleListItem,
|
|
15
15
|
ConsoleOverlayStatus,
|
|
16
|
+
ConsolePullRequestStatus,
|
|
16
17
|
ConsoleRelatedPullRequest,
|
|
17
18
|
} from '../../logic/types';
|
|
19
|
+
import type { ConsoleReferenceLinkRenderer } from '../content/ConsoleMarkdownContent';
|
|
18
20
|
import { ConsoleMarkdownContent } from '../content/ConsoleMarkdownContent';
|
|
19
21
|
import { ConsolePanel } from '../layout/ConsolePanel';
|
|
20
22
|
import { ConsoleChangedFileList } from './ConsoleChangedFileList';
|
|
@@ -24,6 +26,7 @@ import { ConsoleCopyUrlButton } from './ConsoleCopyUrlButton';
|
|
|
24
26
|
import type { ConsoleAddInlineComment } from './ConsoleFileDiff';
|
|
25
27
|
import { ConsoleItemIcon } from './ConsoleItemIcon';
|
|
26
28
|
import { ConsolePullRequestDetail } from './ConsolePullRequestDetail';
|
|
29
|
+
import { ConsolePullRequestStatusBadges } from './ConsolePullRequestStatusBadges';
|
|
27
30
|
|
|
28
31
|
export type ConsoleRelatedPullRequestView = {
|
|
29
32
|
pullRequest: ConsoleRelatedPullRequest;
|
|
@@ -53,11 +56,13 @@ export type ConsoleItemDetailProps = {
|
|
|
53
56
|
commits: ConsoleCommit[];
|
|
54
57
|
commitsAreLoading: boolean;
|
|
55
58
|
commitsError: string | null;
|
|
59
|
+
pullRequestStatus: ConsolePullRequestStatus | null;
|
|
56
60
|
relatedPullRequests: ConsoleRelatedPullRequestView[];
|
|
57
61
|
now: number;
|
|
58
62
|
commentComposer: ReactNode;
|
|
59
63
|
operationBar: ReactNode;
|
|
60
64
|
buildImageProxyUrl?: ImageProxyUrlBuilder;
|
|
65
|
+
renderReferenceLink?: ConsoleReferenceLinkRenderer;
|
|
61
66
|
onAddInlineComment?: ConsoleAddInlineComment;
|
|
62
67
|
buildAddInlineComment?: (prUrl: string) => ConsoleAddInlineComment;
|
|
63
68
|
};
|
|
@@ -80,11 +85,13 @@ export const ConsoleItemDetail = ({
|
|
|
80
85
|
commits,
|
|
81
86
|
commitsAreLoading,
|
|
82
87
|
commitsError,
|
|
88
|
+
pullRequestStatus,
|
|
83
89
|
relatedPullRequests,
|
|
84
90
|
now,
|
|
85
91
|
commentComposer,
|
|
86
92
|
operationBar,
|
|
87
93
|
buildImageProxyUrl,
|
|
94
|
+
renderReferenceLink,
|
|
88
95
|
onAddInlineComment,
|
|
89
96
|
buildAddInlineComment,
|
|
90
97
|
}: ConsoleItemDetailProps) => {
|
|
@@ -130,6 +137,17 @@ export const ConsoleItemDetail = ({
|
|
|
130
137
|
{overlayStatus.name}
|
|
131
138
|
</span>
|
|
132
139
|
)}
|
|
140
|
+
{item.isPr && pullRequestStatus?.found && (
|
|
141
|
+
<ConsolePullRequestStatusBadges
|
|
142
|
+
isConflicted={pullRequestStatus.isConflicted}
|
|
143
|
+
isPassedAllCiJob={pullRequestStatus.isPassedAllCiJob}
|
|
144
|
+
isCiStateSuccess={pullRequestStatus.isCiStateSuccess}
|
|
145
|
+
isBranchOutOfDate={pullRequestStatus.isBranchOutOfDate}
|
|
146
|
+
missingRequiredCheckNames={
|
|
147
|
+
pullRequestStatus.missingRequiredCheckNames
|
|
148
|
+
}
|
|
149
|
+
/>
|
|
150
|
+
)}
|
|
133
151
|
<ConsoleItemIcon
|
|
134
152
|
isPr={item.isPr}
|
|
135
153
|
state={resolvedState}
|
|
@@ -204,6 +222,7 @@ export const ConsoleItemDetail = ({
|
|
|
204
222
|
<ConsoleMarkdownContent
|
|
205
223
|
body={body}
|
|
206
224
|
buildImageProxyUrl={buildImageProxyUrl}
|
|
225
|
+
renderReferenceLink={renderReferenceLink}
|
|
207
226
|
/>
|
|
208
227
|
)}
|
|
209
228
|
</ConsolePanel>
|
|
@@ -230,6 +249,7 @@ export const ConsoleItemDetail = ({
|
|
|
230
249
|
error={commentsError}
|
|
231
250
|
now={now}
|
|
232
251
|
buildImageProxyUrl={buildImageProxyUrl}
|
|
252
|
+
renderReferenceLink={renderReferenceLink}
|
|
233
253
|
/>
|
|
234
254
|
</ConsolePanel>
|
|
235
255
|
|
|
@@ -259,6 +279,7 @@ export const ConsoleItemDetail = ({
|
|
|
259
279
|
commitsError={related.commitsError}
|
|
260
280
|
now={now}
|
|
261
281
|
buildImageProxyUrl={buildImageProxyUrl}
|
|
282
|
+
renderReferenceLink={renderReferenceLink}
|
|
262
283
|
onAddInlineComment={
|
|
263
284
|
buildAddInlineComment
|
|
264
285
|
? buildAddInlineComment(related.pullRequest.url)
|