Skip to content

Commit eb1a5cf

Browse files
authored
Add "model alerts indicator" component (#3441)
1 parent c32065e commit eb1a5cf

File tree

11 files changed

+176
-24
lines changed

11 files changed

+176
-24
lines changed

extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import type { ModelEditorViewState } from "../../model-editor/shared/view-state";
1616
import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions";
1717
import { getCandidates } from "../../model-editor/shared/auto-model-candidates";
18+
import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state";
1819

1920
const LibraryContainer = styled.div`
2021
background-color: var(--vscode-peekViewResult-background);
@@ -80,6 +81,7 @@ export type LibraryRowProps = {
8081
hideModeledMethods: boolean;
8182
revealedMethodSignature: string | null;
8283
accessPathSuggestions?: AccessPathSuggestionOptions;
84+
evaluationRun: ModelEvaluationRunState | undefined;
8385
onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void;
8486
onMethodClick: (methodSignature: string) => void;
8587
onSaveModelClick: (methodSignatures: string[]) => void;
@@ -105,6 +107,7 @@ export const LibraryRow = ({
105107
hideModeledMethods,
106108
revealedMethodSignature,
107109
accessPathSuggestions,
110+
evaluationRun,
108111
onChange,
109112
onMethodClick,
110113
onSaveModelClick,
@@ -260,6 +263,7 @@ export const LibraryRow = ({
260263
hideModeledMethods={hideModeledMethods}
261264
revealedMethodSignature={revealedMethodSignature}
262265
accessPathSuggestions={accessPathSuggestions}
266+
evaluationRun={evaluationRun}
263267
onChange={onChange}
264268
onMethodClick={onMethodClick}
265269
/>

extensions/ql-vscode/src/view/model-editor/MethodRow.tsx

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ import type { AccessPathOption } from "../../model-editor/suggestions";
3838
import { ModelInputSuggestBox } from "./ModelInputSuggestBox";
3939
import { ModelOutputSuggestBox } from "./ModelOutputSuggestBox";
4040
import { getModelsAsDataLanguage } from "../../model-editor/languages";
41+
import { ModelAlertsIndicator } from "./ModelAlertsIndicator";
42+
import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state";
4143

4244
const ApiOrMethodRow = styled.div`
4345
min-height: calc(var(--input-height) * 1px);
@@ -47,6 +49,14 @@ const ApiOrMethodRow = styled.div`
4749
gap: 0.5em;
4850
`;
4951

52+
const ModelButtonsContainer = styled.div`
53+
min-height: calc(var(--input-height) * 1px);
54+
display: flex;
55+
flex-direction: row;
56+
align-items: center;
57+
gap: 1em;
58+
`;
59+
5060
const UsagesButton = styled.button`
5161
color: var(--vscode-editor-foreground);
5262
background-color: var(--vscode-input-background);
@@ -82,6 +92,7 @@ export type MethodRowProps = {
8292
revealedMethodSignature: string | null;
8393
inputAccessPathSuggestions?: AccessPathOption[];
8494
outputAccessPathSuggestions?: AccessPathOption[];
95+
evaluationRun: ModelEvaluationRunState | undefined;
8596
onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void;
8697
onMethodClick: (methodSignature: string) => void;
8798
};
@@ -119,6 +130,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
119130
revealedMethodSignature,
120131
inputAccessPathSuggestions,
121132
outputAccessPathSuggestions,
133+
evaluationRun,
122134
onChange,
123135
onMethodClick,
124136
} = props;
@@ -349,30 +361,37 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
349361
/>
350362
</DataGridCell>
351363
<DataGridCell>
352-
{index === 0 ? (
353-
<CodiconRow
354-
appearance="icon"
355-
aria-label="Add new model"
356-
onClick={(event: React.MouseEvent) => {
357-
event.stopPropagation();
358-
handleAddModelClick();
359-
}}
360-
disabled={addModelButtonDisabled}
361-
>
362-
<Codicon name="add" />
363-
</CodiconRow>
364-
) : (
365-
<CodiconRow
366-
appearance="icon"
367-
aria-label="Remove model"
368-
onClick={(event: React.MouseEvent) => {
369-
event.stopPropagation();
370-
removeModelClickedHandlers[index]();
371-
}}
372-
>
373-
<Codicon name="trash" />
374-
</CodiconRow>
375-
)}
364+
<ModelButtonsContainer>
365+
<ModelAlertsIndicator
366+
viewState={viewState}
367+
modeledMethod={modeledMethod}
368+
evaluationRun={evaluationRun}
369+
></ModelAlertsIndicator>
370+
{index === 0 ? (
371+
<CodiconRow
372+
appearance="icon"
373+
aria-label="Add new model"
374+
onClick={(event: React.MouseEvent) => {
375+
event.stopPropagation();
376+
handleAddModelClick();
377+
}}
378+
disabled={addModelButtonDisabled}
379+
>
380+
<Codicon name="add" />
381+
</CodiconRow>
382+
) : (
383+
<CodiconRow
384+
appearance="icon"
385+
aria-label="Remove model"
386+
onClick={(event: React.MouseEvent) => {
387+
event.stopPropagation();
388+
removeModelClickedHandlers[index]();
389+
}}
390+
>
391+
<Codicon name="trash" />
392+
</CodiconRow>
393+
)}
394+
</ModelButtonsContainer>
376395
</DataGridCell>
377396
</DataGridRow>
378397
);
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { styled } from "styled-components";
2+
import type { ModeledMethod } from "../../model-editor/modeled-method";
3+
import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state";
4+
import type { ModelEditorViewState } from "../../model-editor/shared/view-state";
5+
6+
const ModelAlertsButton = styled.button`
7+
color: var(--vscode-editor-foreground);
8+
background-color: var(--vscode-input-background);
9+
border: none;
10+
border-radius: 40%;
11+
cursor: pointer;
12+
`;
13+
14+
export type Props = {
15+
viewState: ModelEditorViewState;
16+
modeledMethod: ModeledMethod;
17+
evaluationRun: ModelEvaluationRunState | undefined;
18+
};
19+
20+
export const ModelAlertsIndicator = ({
21+
viewState,
22+
modeledMethod,
23+
evaluationRun,
24+
}: Props) => {
25+
if (!viewState.showEvaluationUi) {
26+
return null;
27+
}
28+
29+
if (!evaluationRun || !modeledMethod) {
30+
return null;
31+
}
32+
33+
// TODO: Once we have alert provenance, we can show actual alert counts here.
34+
// For now, we show a random number.
35+
const number = Math.floor(Math.random() * 10);
36+
37+
return (
38+
<ModelAlertsButton
39+
onClick={(event: React.MouseEvent) => {
40+
event.stopPropagation();
41+
}}
42+
>
43+
{number}
44+
</ModelAlertsButton>
45+
);
46+
};

extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ export function ModelEditor({
436436
hideModeledMethods={hideModeledMethods}
437437
revealedMethodSignature={revealedMethodSignature}
438438
accessPathSuggestions={accessPathSuggestions}
439+
evaluationRun={evaluationRun}
439440
onChange={onChange}
440441
onMethodClick={onMethodClick}
441442
onSaveModelClick={onSaveModelClick}

extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { ModelEditorViewState } from "../../model-editor/shared/view-state"
88
import { ScreenReaderOnly } from "../common/ScreenReaderOnly";
99
import { DataGrid, DataGridCell } from "../common/DataGrid";
1010
import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions";
11+
import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state";
1112

1213
export const MULTIPLE_MODELS_GRID_TEMPLATE_COLUMNS =
1314
"0.5fr 0.125fr 0.125fr 0.125fr 0.125fr max-content";
@@ -23,6 +24,7 @@ export type ModeledMethodDataGridProps = {
2324
hideModeledMethods: boolean;
2425
revealedMethodSignature: string | null;
2526
accessPathSuggestions?: AccessPathSuggestionOptions;
27+
evaluationRun: ModelEvaluationRunState | undefined;
2628
onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void;
2729
onMethodClick: (methodSignature: string) => void;
2830
};
@@ -38,6 +40,7 @@ export const ModeledMethodDataGrid = ({
3840
hideModeledMethods,
3941
revealedMethodSignature,
4042
accessPathSuggestions,
43+
evaluationRun,
4144
onChange,
4245
onMethodClick,
4346
}: ModeledMethodDataGridProps) => {
@@ -101,6 +104,7 @@ export const ModeledMethodDataGrid = ({
101104
revealedMethodSignature={revealedMethodSignature}
102105
inputAccessPathSuggestions={inputAccessPathSuggestions}
103106
outputAccessPathSuggestions={outputAccessPathSuggestions}
107+
evaluationRun={evaluationRun}
104108
onChange={onChange}
105109
onMethodClick={onMethodClick}
106110
/>

extensions/ql-vscode/src/view/model-editor/ModeledMethodsList.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
} from "../../model-editor/shared/sorting";
1010
import type { ModelEditorViewState } from "../../model-editor/shared/view-state";
1111
import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions";
12+
import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state";
1213

1314
export type ModeledMethodsListProps = {
1415
methods: Method[];
@@ -19,6 +20,7 @@ export type ModeledMethodsListProps = {
1920
processedByAutoModelMethods: Set<string>;
2021
revealedMethodSignature: string | null;
2122
accessPathSuggestions?: AccessPathSuggestionOptions;
23+
evaluationRun: ModelEvaluationRunState | undefined;
2224
viewState: ModelEditorViewState;
2325
hideModeledMethods: boolean;
2426
onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void;
@@ -48,6 +50,7 @@ export const ModeledMethodsList = ({
4850
hideModeledMethods,
4951
revealedMethodSignature,
5052
accessPathSuggestions,
53+
evaluationRun,
5154
onChange,
5255
onMethodClick,
5356
onSaveModelClick,
@@ -98,6 +101,7 @@ export const ModeledMethodsList = ({
98101
hideModeledMethods={hideModeledMethods}
99102
revealedMethodSignature={revealedMethodSignature}
100103
accessPathSuggestions={accessPathSuggestions}
104+
evaluationRun={evaluationRun}
101105
onChange={onChange}
102106
onMethodClick={onMethodClick}
103107
onSaveModelClick={onSaveModelClick}

extensions/ql-vscode/src/view/model-editor/__tests__/LibraryRow.spec.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ describe(LibraryRow.name, () => {
3737
selectedSignatures={new Set()}
3838
inProgressMethods={new Set()}
3939
processedByAutoModelMethods={new Set()}
40+
evaluationRun={undefined}
4041
viewState={viewState}
4142
hideModeledMethods={false}
4243
revealedMethodSignature={null}

extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ describe(MethodRow.name, () => {
4545
modelingInProgress={false}
4646
processedByAutoModel={false}
4747
revealedMethodSignature={null}
48+
evaluationRun={undefined}
4849
viewState={viewState}
4950
onChange={onChange}
5051
onMethodClick={onMethodClick}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { render as reactRender, screen } from "@testing-library/react";
2+
import { createMethod } from "../../../../test/factories/model-editor/method-factories";
3+
import { createSummaryModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories";
4+
import { createMockModelEditorViewState } from "../../../../test/factories/model-editor/view-state";
5+
import type { Props } from "../ModelAlertsIndicator";
6+
import { ModelAlertsIndicator } from "../ModelAlertsIndicator";
7+
import { createMockVariantAnalysis } from "../../../../test/factories/variant-analysis/shared/variant-analysis";
8+
import { VariantAnalysisStatus } from "../../../variant-analysis/shared/variant-analysis";
9+
10+
describe(ModelAlertsIndicator.name, () => {
11+
const method = createMethod();
12+
const modeledMethod = createSummaryModeledMethod(method);
13+
const evaluationRun = {
14+
isPreparing: false,
15+
variantAnalysis: createMockVariantAnalysis({
16+
status: VariantAnalysisStatus.Succeeded,
17+
}),
18+
};
19+
20+
const render = (props: Partial<Props> = {}) =>
21+
reactRender(
22+
<ModelAlertsIndicator
23+
viewState={createMockModelEditorViewState({ showEvaluationUi: true })}
24+
modeledMethod={modeledMethod}
25+
evaluationRun={evaluationRun}
26+
{...props}
27+
/>,
28+
);
29+
30+
describe("when showEvaluationUi is false", () => {
31+
it("does not render anything", () => {
32+
render({
33+
viewState: createMockModelEditorViewState({ showEvaluationUi: false }),
34+
});
35+
36+
expect(screen.queryByRole("button")).not.toBeInTheDocument();
37+
});
38+
});
39+
40+
describe("when there is no evaluation run", () => {
41+
it("does not render anything", () => {
42+
render({
43+
evaluationRun: undefined,
44+
});
45+
46+
expect(screen.queryByRole("button")).not.toBeInTheDocument();
47+
});
48+
});
49+
50+
describe("when there is no modeled method", () => {
51+
it("does not render anything", () => {
52+
render({
53+
modeledMethod: undefined,
54+
});
55+
56+
expect(screen.queryByRole("button")).not.toBeInTheDocument();
57+
});
58+
});
59+
60+
describe("when there is an evaluation run and a modeled method", () => {
61+
// TODO: Once we have alert provenance, this will be an actual alert count instead of a random number.
62+
it("renders a button with a random number", () => {
63+
render();
64+
65+
const button = screen.queryByRole("button");
66+
expect(button).toBeInTheDocument();
67+
expect(button).toHaveTextContent(/\d/);
68+
});
69+
});
70+
});

extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodDataGrid.spec.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ describe(ModeledMethodDataGrid.name, () => {
5959
selectedSignatures={new Set()}
6060
inProgressMethods={new Set()}
6161
processedByAutoModelMethods={new Set()}
62+
evaluationRun={undefined}
6263
viewState={viewState}
6364
hideModeledMethods={false}
6465
revealedMethodSignature={null}

0 commit comments

Comments
 (0)