@principal-ai/subsystems-react 0.53.6 → 0.55.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/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/pierre/PierreTrailCodeView.d.ts.map +1 -1
- package/dist/pierre/PierreTrailCodeView.js +7 -5
- package/dist/pierre/PierreTrailCodeView.js.map +1 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts.map +1 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js +1 -2
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js.map +1 -1
- package/dist/subsystem/C4Graph.d.ts +144 -9
- package/dist/subsystem/C4Graph.d.ts.map +1 -1
- package/dist/subsystem/C4Graph.js +924 -148
- package/dist/subsystem/C4Graph.js.map +1 -1
- package/dist/subsystem/C4NodeCard.d.ts +22 -1
- package/dist/subsystem/C4NodeCard.d.ts.map +1 -1
- package/dist/subsystem/C4NodeCard.js +10 -2
- package/dist/subsystem/C4NodeCard.js.map +1 -1
- package/dist/subsystem/ComponentDeclaration.d.ts +5 -4
- package/dist/subsystem/ComponentDeclaration.d.ts.map +1 -1
- package/dist/subsystem/ComponentDeclaration.js +12 -4
- package/dist/subsystem/ComponentDeclaration.js.map +1 -1
- package/dist/subsystem/ConstructsCatalog.d.ts.map +1 -1
- package/dist/subsystem/ConstructsCatalog.js +10 -7
- package/dist/subsystem/ConstructsCatalog.js.map +1 -1
- package/dist/subsystem/GraphSidebar.d.ts +71 -0
- package/dist/subsystem/GraphSidebar.d.ts.map +1 -0
- package/dist/subsystem/GraphSidebar.js +145 -0
- package/dist/subsystem/GraphSidebar.js.map +1 -0
- package/dist/subsystem/GraphWithSidebar.d.ts +24 -0
- package/dist/subsystem/GraphWithSidebar.d.ts.map +1 -0
- package/dist/subsystem/GraphWithSidebar.js +25 -0
- package/dist/subsystem/GraphWithSidebar.js.map +1 -0
- package/dist/subsystem/IssueList.d.ts +4 -5
- package/dist/subsystem/IssueList.d.ts.map +1 -1
- package/dist/subsystem/IssueList.js +33 -28
- package/dist/subsystem/IssueList.js.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.d.ts +33 -0
- package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.js +367 -414
- package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
- package/dist/subsystem/TrailStepsNav.d.ts +48 -0
- package/dist/subsystem/TrailStepsNav.d.ts.map +1 -0
- package/dist/subsystem/TrailStepsNav.js +189 -0
- package/dist/subsystem/TrailStepsNav.js.map +1 -0
- package/dist/subsystem/TrailsPanel.d.ts.map +1 -1
- package/dist/subsystem/TrailsPanel.js +3 -4
- package/dist/subsystem/TrailsPanel.js.map +1 -1
- package/dist/subsystem/__fixtures__/c4Drilldown.d.ts +151 -0
- package/dist/subsystem/__fixtures__/c4Drilldown.d.ts.map +1 -0
- package/dist/subsystem/__fixtures__/c4Drilldown.js +783 -0
- package/dist/subsystem/__fixtures__/c4Drilldown.js.map +1 -0
- package/dist/subsystem/c4.d.ts +39 -8
- package/dist/subsystem/c4.d.ts.map +1 -1
- package/dist/subsystem/c4.js.map +1 -1
- package/dist/subsystem/declarationRef.d.ts +26 -18
- package/dist/subsystem/declarationRef.d.ts.map +1 -1
- package/dist/subsystem/declarationRef.js +9 -4
- package/dist/subsystem/declarationRef.js.map +1 -1
- package/dist/subsystem/model.d.ts +19 -1
- package/dist/subsystem/model.d.ts.map +1 -1
- package/dist/subsystem/model.js +12 -2
- package/dist/subsystem/model.js.map +1 -1
- package/dist/subsystem/nodes.d.ts.map +1 -1
- package/dist/subsystem/nodes.js +24 -4
- package/dist/subsystem/nodes.js.map +1 -1
- package/dist/subsystem/trailBrief.d.ts.map +1 -1
- package/dist/subsystem/trailBrief.js +6 -1
- package/dist/subsystem/trailBrief.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +9 -2
- package/src/pierre/PierreTrailCodeView.tsx +9 -13
- package/src/stories/Subsystem/C4Graph/C4ToComponentDrill.stories.tsx +496 -0
- package/src/stories/Subsystem/ComponentGraph/AnimatedProgression.stories.tsx +7 -7
- package/src/stories/Subsystem/ComponentGraph/Constructs.stories.tsx +19 -25
- package/src/stories/Subsystem/ComponentGraph/DetailPanel.stories.tsx +8 -10
- package/src/stories/Subsystem/ComponentGraph/Issues.stories.tsx +40 -33
- package/src/stories/Subsystem/ComponentGraph/ProposedTrails.stories.tsx +6 -6
- package/src/stories/Subsystem/ComponentGraph/TrailAutoplay.stories.tsx +7 -7
- package/src/stories/Subsystem/ComponentGraph/Trails.stories.tsx +27 -30
- package/src/stories/Subsystem/ComponentGraph/fixtures.ts +1 -2
- package/src/stories/Subsystem/ConstructsCatalog.stories.tsx +10 -13
- package/src/stories/Subsystem/EgoGraph/EgoGraph.stories.tsx +3 -4
- package/src/stories/Subsystem/IssueIcons.stories.tsx +65 -0
- package/src/stories/Subsystem/Sidebar/GraphSidebar.stories.tsx +206 -0
- package/src/subsystem/C4Graph.groups.test.ts +96 -2
- package/src/subsystem/C4Graph.tsx +1377 -165
- package/src/subsystem/C4NodeCard.test.ts +1 -2
- package/src/subsystem/C4NodeCard.tsx +24 -2
- package/src/subsystem/ComponentDeclaration.tsx +18 -8
- package/src/subsystem/ConstructsCatalog.tsx +23 -8
- package/src/subsystem/GraphSidebar.test.tsx +152 -0
- package/src/subsystem/GraphSidebar.tsx +300 -0
- package/src/subsystem/GraphWithSidebar.test.tsx +78 -0
- package/src/subsystem/GraphWithSidebar.tsx +60 -0
- package/src/subsystem/IssueList.tsx +34 -29
- package/src/subsystem/SubsystemComponentGraph.tsx +290 -376
- package/src/subsystem/TrailStepsNav.test.tsx +179 -0
- package/src/subsystem/TrailStepsNav.tsx +372 -0
- package/src/subsystem/TrailsPanel.tsx +3 -4
- package/src/subsystem/__fixtures__/c4Drilldown.test.ts +338 -0
- package/src/subsystem/__fixtures__/c4Drilldown.ts +847 -0
- package/src/subsystem/c4.ts +41 -8
- package/src/subsystem/declarationRef.ts +28 -19
- package/src/subsystem/model.test.ts +32 -12
- package/src/subsystem/model.ts +30 -1
- package/src/subsystem/nodes.group.test.tsx +82 -0
- package/src/subsystem/nodes.tsx +30 -2
- package/src/subsystem/trailBrief.test.ts +2 -2
- package/src/subsystem/trailBrief.ts +6 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from 'bun:test';
|
|
2
|
+
import { createElement } from 'react';
|
|
3
|
+
import { Window } from 'happy-dom';
|
|
4
|
+
import { cleanup, render } from '@testing-library/react/pure';
|
|
5
|
+
import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
|
|
6
|
+
import { GraphWithSidebar, GraphSidebar } from './GraphWithSidebar';
|
|
7
|
+
|
|
8
|
+
// Bun's happy-dom test environment isn't registered (no @happy-dom/global-
|
|
9
|
+
// registrator), so wire the DOM primitives React DOM needs before rendering.
|
|
10
|
+
const dom = new Window();
|
|
11
|
+
const globals = globalThis as unknown as Record<string, unknown>;
|
|
12
|
+
globals.window = dom;
|
|
13
|
+
globals.document = dom.document;
|
|
14
|
+
globals.navigator = dom.navigator;
|
|
15
|
+
globals.HTMLElement = dom.HTMLElement;
|
|
16
|
+
globals.Node = dom.Node;
|
|
17
|
+
globals.Element = dom.Element;
|
|
18
|
+
globals.Event = dom.Event;
|
|
19
|
+
globals.getComputedStyle = dom.getComputedStyle.bind(dom);
|
|
20
|
+
globals.requestAnimationFrame = dom.requestAnimationFrame.bind(dom);
|
|
21
|
+
globals.cancelAnimationFrame = dom.cancelAnimationFrame.bind(dom);
|
|
22
|
+
globals.localStorage = dom.localStorage;
|
|
23
|
+
globals.matchMedia = dom.matchMedia.bind(dom);
|
|
24
|
+
|
|
25
|
+
afterEach(cleanup);
|
|
26
|
+
|
|
27
|
+
function renderWrapper(props: Record<string, unknown>) {
|
|
28
|
+
return render(
|
|
29
|
+
createElement(
|
|
30
|
+
ThemeProvider,
|
|
31
|
+
{ theme: defaultEditorTheme },
|
|
32
|
+
createElement(GraphWithSidebar as never, props as never),
|
|
33
|
+
),
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe('GraphWithSidebar composition', () => {
|
|
38
|
+
test('composes GraphSidebar next to graph content', () => {
|
|
39
|
+
const { getByText, getByLabelText } = renderWrapper({
|
|
40
|
+
header: createElement('span', null, 'title'),
|
|
41
|
+
sidebarBody: createElement('span', null, 'panel'),
|
|
42
|
+
children: createElement('div', null, 'canvas'),
|
|
43
|
+
});
|
|
44
|
+
expect(getByText('title')).toBeTruthy();
|
|
45
|
+
expect(getByText('panel')).toBeTruthy();
|
|
46
|
+
expect(getByText('canvas')).toBeTruthy();
|
|
47
|
+
expect(getByLabelText('Resize sidebar')).toBeTruthy();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('hidden renders only the content — no sidebar, no handle', () => {
|
|
51
|
+
const { queryByLabelText, getByText } = renderWrapper({
|
|
52
|
+
hidden: true,
|
|
53
|
+
header: createElement('span', null, 'title'),
|
|
54
|
+
children: createElement('div', null, 'canvas'),
|
|
55
|
+
});
|
|
56
|
+
expect(getByText('canvas')).toBeTruthy();
|
|
57
|
+
expect(queryByLabelText('Resize sidebar')).toBeNull();
|
|
58
|
+
expect(() => getByText('title')).toThrow();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('wrapper exposes GraphSidebar as its own export', () => {
|
|
62
|
+
expect(GraphSidebar).toBeTypeOf('function');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('the content slot is a flex column so a flex-filling child can size', () => {
|
|
66
|
+
// The component graph's canvas fills the pane with `flex: 1 1 0%`, which
|
|
67
|
+
// only resolves against a flex parent — a block slot collapses it to 0 and
|
|
68
|
+
// the canvas renders blank.
|
|
69
|
+
const { container } = renderWrapper({
|
|
70
|
+
sidebarBody: createElement('span', null, 'panel'),
|
|
71
|
+
children: createElement('div', null, 'canvas'),
|
|
72
|
+
});
|
|
73
|
+
const root = container.firstElementChild as HTMLElement;
|
|
74
|
+
const content = root.lastElementChild as HTMLElement;
|
|
75
|
+
expect(content.style.display).toBe('flex');
|
|
76
|
+
expect(content.style.flexDirection).toBe('column');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import { GraphSidebar } from './GraphSidebar';
|
|
4
|
+
import type { GraphSidebarProps } from './GraphSidebar';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* GraphWithSidebar — a graph laid out beside its own sidebar.
|
|
8
|
+
*
|
|
9
|
+
* A flex row: `[GraphSidebar][graph content]`. The sidebar is the standalone
|
|
10
|
+
* `GraphSidebar` panel (its right edge is the resize handle, and it owns the
|
|
11
|
+
* width drag + persistence); this component only composes it next to a graph
|
|
12
|
+
* and locks the row against text selection while a resize is in progress.
|
|
13
|
+
*
|
|
14
|
+
* Every sidebar prop is forwarded straight to `GraphSidebar`: `header`,
|
|
15
|
+
* `tabs`/`activeTab`/`onTabChange`, `sidebarBody`, `footer`, `hidden`,
|
|
16
|
+
* `bodyHidden`, `stretchHeader`, `width`/`onWidthChange`, `defaultWidth`,
|
|
17
|
+
* `minWidth`/`maxWidth`, `onResizeEnd`, `persistKey`. See GraphSidebar for the
|
|
18
|
+
* semantics.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export interface GraphWithSidebarProps extends GraphSidebarProps {
|
|
22
|
+
/** The graph content to the right of the sidebar. */
|
|
23
|
+
children?: ReactNode;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { GraphSidebar } from './GraphSidebar';
|
|
27
|
+
export type { GraphSidebarProps, GraphSidebarTab } from './GraphSidebar';
|
|
28
|
+
|
|
29
|
+
export function GraphWithSidebar({ children, ...sidebarProps }: GraphWithSidebarProps) {
|
|
30
|
+
const [dragging, setDragging] = useState(false);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
style={{
|
|
35
|
+
width: '100%',
|
|
36
|
+
height: '100%',
|
|
37
|
+
display: 'flex',
|
|
38
|
+
flexDirection: 'row',
|
|
39
|
+
userSelect: dragging ? 'none' : undefined,
|
|
40
|
+
}}
|
|
41
|
+
>
|
|
42
|
+
<GraphSidebar {...sidebarProps} onDraggingChange={setDragging} />
|
|
43
|
+
<div
|
|
44
|
+
style={{
|
|
45
|
+
flex: 1,
|
|
46
|
+
minWidth: 0,
|
|
47
|
+
position: 'relative',
|
|
48
|
+
minHeight: 0,
|
|
49
|
+
// A flex column so a child that fills the pane with flex-grow (the
|
|
50
|
+
// component graph's canvas tree) resolves against a real flex
|
|
51
|
+
// parent instead of collapsing to 0.
|
|
52
|
+
display: 'flex',
|
|
53
|
+
flexDirection: 'column',
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
{children}
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
@@ -20,14 +20,13 @@ import {
|
|
|
20
20
|
CircleCheck,
|
|
21
21
|
CircleDashed,
|
|
22
22
|
CircleEllipsis,
|
|
23
|
-
CircleHelp,
|
|
24
23
|
CircleSlash,
|
|
25
24
|
CircleX,
|
|
26
25
|
Component,
|
|
26
|
+
FileCodeCorner,
|
|
27
27
|
FileX,
|
|
28
28
|
FolderGit2,
|
|
29
29
|
Footprints,
|
|
30
|
-
History,
|
|
31
30
|
Link2Off,
|
|
32
31
|
MapPin,
|
|
33
32
|
Minus,
|
|
@@ -103,7 +102,8 @@ const KIND_CATEGORY: Record<string, SubsystemIssueCategory> = {
|
|
|
103
102
|
missing_file: 'construct',
|
|
104
103
|
symbol_ambiguous: 'construct',
|
|
105
104
|
symbol_unmatched: 'construct',
|
|
106
|
-
|
|
105
|
+
construct_span_unconfirmed: 'construct',
|
|
106
|
+
construct_span_stale: 'construct',
|
|
107
107
|
construct_mismatch: 'construct',
|
|
108
108
|
construct_unconfirmed: 'construct',
|
|
109
109
|
signature_mismatch: 'construct',
|
|
@@ -121,8 +121,8 @@ const KIND_CATEGORY: Record<string, SubsystemIssueCategory> = {
|
|
|
121
121
|
boundary_process_rejected: 'dynamic-topology',
|
|
122
122
|
boundary_process_unbacked: 'dynamic-topology',
|
|
123
123
|
trail: 'trail',
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
step_span_unconfirmed: 'trail',
|
|
125
|
+
step_span_stale: 'trail',
|
|
126
126
|
};
|
|
127
127
|
|
|
128
128
|
/**
|
|
@@ -142,7 +142,8 @@ const KIND_RUNG: Record<string, SubsystemIssueRung> = {
|
|
|
142
142
|
missing_file: 'file',
|
|
143
143
|
symbol_unmatched: 'symbol',
|
|
144
144
|
symbol_ambiguous: 'symbol',
|
|
145
|
-
|
|
145
|
+
construct_span_unconfirmed: 'declaration',
|
|
146
|
+
construct_span_stale: 'declaration',
|
|
146
147
|
construct_unconfirmed: 'type',
|
|
147
148
|
construct_mismatch: 'type',
|
|
148
149
|
signature_unconfirmed: 'signature',
|
|
@@ -173,11 +174,10 @@ export const ISSUE_RUNG_ICON: Record<SubsystemIssueRung, LucideIcon> = {
|
|
|
173
174
|
};
|
|
174
175
|
|
|
175
176
|
/**
|
|
176
|
-
* Icons for findings that
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
* construct is at fault.
|
|
177
|
+
* Icons for findings that name their own glyph rather than keying off a rung —
|
|
178
|
+
* boundary findings (which badge the region node they name) and the span
|
|
179
|
+
* findings (construct + step share `FileCodeCorner`). A kind listed here wins
|
|
180
|
+
* over its rung icon in the row; the span state is carried by color.
|
|
181
181
|
*/
|
|
182
182
|
export const ISSUE_KIND_ICON: Record<string, LucideIcon> = {
|
|
183
183
|
// One module, more than one claimed process parent — a containment that can't
|
|
@@ -191,9 +191,13 @@ export const ISSUE_KIND_ICON: Record<string, LucideIcon> = {
|
|
|
191
191
|
boundary_process_rejected: CircleX,
|
|
192
192
|
// Container-first gate: a claim cites a key no accepted container owns.
|
|
193
193
|
boundary_process_unbacked: Link2Off,
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
194
|
+
// Span findings — one family, state carried by color (red = not captured,
|
|
195
|
+
// amber = changed). Construct span sits on the declaration rung but keeps
|
|
196
|
+
// this glyph; step span has no rung.
|
|
197
|
+
construct_span_unconfirmed: FileCodeCorner,
|
|
198
|
+
construct_span_stale: FileCodeCorner,
|
|
199
|
+
step_span_unconfirmed: FileCodeCorner,
|
|
200
|
+
step_span_stale: FileCodeCorner,
|
|
197
201
|
};
|
|
198
202
|
|
|
199
203
|
/**
|
|
@@ -264,7 +268,8 @@ const KIND_LABEL: Record<string, string> = {
|
|
|
264
268
|
missing_file: 'Construct declaration file missing',
|
|
265
269
|
symbol_ambiguous: 'Multiple construct symbol matches',
|
|
266
270
|
symbol_unmatched: 'Construct symbol missing',
|
|
267
|
-
|
|
271
|
+
construct_span_unconfirmed: 'Construct span not captured',
|
|
272
|
+
construct_span_stale: 'Construct span drift',
|
|
268
273
|
construct_mismatch: 'Construct type mismatch',
|
|
269
274
|
construct_unconfirmed: 'Construct type unconfirmed',
|
|
270
275
|
signature_mismatch: 'Construct signature mismatch',
|
|
@@ -281,8 +286,8 @@ const KIND_LABEL: Record<string, string> = {
|
|
|
281
286
|
boundary_process_rejected: 'Container rejected',
|
|
282
287
|
boundary_process_unbacked: 'Process claim unbacked',
|
|
283
288
|
trail: 'Step issue',
|
|
284
|
-
|
|
285
|
-
|
|
289
|
+
step_span_unconfirmed: 'Step span not captured',
|
|
290
|
+
step_span_stale: 'Step span changed',
|
|
286
291
|
};
|
|
287
292
|
|
|
288
293
|
/** Label for an issue's kind. Explicit `kindLabel` wins; unknown kinds humanize. */
|
|
@@ -305,7 +310,8 @@ const KIND_ORDER: Record<string, number> = {
|
|
|
305
310
|
missing_file: 10,
|
|
306
311
|
symbol_unmatched: 11,
|
|
307
312
|
symbol_ambiguous: 12,
|
|
308
|
-
|
|
313
|
+
construct_span_unconfirmed: 12.5,
|
|
314
|
+
construct_span_stale: 13,
|
|
309
315
|
construct_unconfirmed: 14,
|
|
310
316
|
construct_mismatch: 15,
|
|
311
317
|
signature_unconfirmed: 16,
|
|
@@ -321,8 +327,8 @@ const KIND_ORDER: Record<string, number> = {
|
|
|
321
327
|
boundary_process_rejected: 35,
|
|
322
328
|
// Trail — mechanical issues first, then step verification
|
|
323
329
|
trail: 40,
|
|
324
|
-
|
|
325
|
-
|
|
330
|
+
step_span_unconfirmed: 41,
|
|
331
|
+
step_span_stale: 42,
|
|
326
332
|
};
|
|
327
333
|
|
|
328
334
|
const KIND_ORDER_FALLBACK = 100;
|
|
@@ -491,19 +497,18 @@ export function SubsystemIssueCard({
|
|
|
491
497
|
const showDetail = expanded;
|
|
492
498
|
const muted = theme.colors.textMuted ?? theme.colors.textSecondary;
|
|
493
499
|
const color = severityColor(issue.severity, theme.colors);
|
|
494
|
-
// Leading icon:
|
|
495
|
-
//
|
|
500
|
+
// Leading icon: an explicit kind glyph (boundary badge, span family) wins;
|
|
501
|
+
// else the verification rung (construct ladder); else the target kind; else
|
|
502
|
+
// the layer's icon — every row reads with an icon.
|
|
496
503
|
const rung = issueRung(issue.kind);
|
|
497
|
-
// Rung icons are the construct ladder; a boundary finding names its own icon
|
|
498
|
-
// (it has no rung); everything else falls back to its layer's icon.
|
|
499
504
|
const targetIcon = issue.target?.kind
|
|
500
505
|
? ISSUE_TARGET_KIND_ICON[issue.target.kind]
|
|
501
506
|
: undefined;
|
|
502
|
-
const RowIcon: LucideIcon =
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
SUBSYSTEM_ISSUE_CATEGORY_ICON[issueCategory(issue)]);
|
|
507
|
+
const RowIcon: LucideIcon =
|
|
508
|
+
ISSUE_KIND_ICON[issue.kind] ??
|
|
509
|
+
(rung
|
|
510
|
+
? ISSUE_RUNG_ICON[rung]
|
|
511
|
+
: (targetIcon ?? SUBSYSTEM_ISSUE_CATEGORY_ICON[issueCategory(issue)]));
|
|
507
512
|
const activate = () => {
|
|
508
513
|
const next = !expanded;
|
|
509
514
|
setExpanded(next);
|