Add "model alerts indicator" component (#3441)

This commit is contained in:
Shati Patel
2024-03-12 14:08:49 +00:00
committed by GitHub
parent c32065e895
commit eb1a5cfba3
11 changed files with 176 additions and 24 deletions

View File

@@ -15,6 +15,7 @@ import {
import type { ModelEditorViewState } from "../../model-editor/shared/view-state";
import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions";
import { getCandidates } from "../../model-editor/shared/auto-model-candidates";
import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state";
const LibraryContainer = styled.div`
background-color: var(--vscode-peekViewResult-background);
@@ -80,6 +81,7 @@ export type LibraryRowProps = {
hideModeledMethods: boolean;
revealedMethodSignature: string | null;
accessPathSuggestions?: AccessPathSuggestionOptions;
evaluationRun: ModelEvaluationRunState | undefined;
onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void;
onMethodClick: (methodSignature: string) => void;
onSaveModelClick: (methodSignatures: string[]) => void;
@@ -105,6 +107,7 @@ export const LibraryRow = ({
hideModeledMethods,
revealedMethodSignature,
accessPathSuggestions,
evaluationRun,
onChange,
onMethodClick,
onSaveModelClick,
@@ -260,6 +263,7 @@ export const LibraryRow = ({
hideModeledMethods={hideModeledMethods}
revealedMethodSignature={revealedMethodSignature}
accessPathSuggestions={accessPathSuggestions}
evaluationRun={evaluationRun}
onChange={onChange}
onMethodClick={onMethodClick}
/>

View File

@@ -38,6 +38,8 @@ import type { AccessPathOption } from "../../model-editor/suggestions";
import { ModelInputSuggestBox } from "./ModelInputSuggestBox";
import { ModelOutputSuggestBox } from "./ModelOutputSuggestBox";
import { getModelsAsDataLanguage } from "../../model-editor/languages";
import { ModelAlertsIndicator } from "./ModelAlertsIndicator";
import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state";
const ApiOrMethodRow = styled.div`
min-height: calc(var(--input-height) * 1px);
@@ -47,6 +49,14 @@ const ApiOrMethodRow = styled.div`
gap: 0.5em;
`;
const ModelButtonsContainer = styled.div`
min-height: calc(var(--input-height) * 1px);
display: flex;
flex-direction: row;
align-items: center;
gap: 1em;
`;
const UsagesButton = styled.button`
color: var(--vscode-editor-foreground);
background-color: var(--vscode-input-background);
@@ -82,6 +92,7 @@ export type MethodRowProps = {
revealedMethodSignature: string | null;
inputAccessPathSuggestions?: AccessPathOption[];
outputAccessPathSuggestions?: AccessPathOption[];
evaluationRun: ModelEvaluationRunState | undefined;
onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void;
onMethodClick: (methodSignature: string) => void;
};
@@ -119,6 +130,7 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
revealedMethodSignature,
inputAccessPathSuggestions,
outputAccessPathSuggestions,
evaluationRun,
onChange,
onMethodClick,
} = props;
@@ -349,30 +361,37 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
/>
</DataGridCell>
<DataGridCell>
{index === 0 ? (
<CodiconRow
appearance="icon"
aria-label="Add new model"
onClick={(event: React.MouseEvent) => {
event.stopPropagation();
handleAddModelClick();
}}
disabled={addModelButtonDisabled}
>
<Codicon name="add" />
</CodiconRow>
) : (
<CodiconRow
appearance="icon"
aria-label="Remove model"
onClick={(event: React.MouseEvent) => {
event.stopPropagation();
removeModelClickedHandlers[index]();
}}
>
<Codicon name="trash" />
</CodiconRow>
)}
<ModelButtonsContainer>
<ModelAlertsIndicator
viewState={viewState}
modeledMethod={modeledMethod}
evaluationRun={evaluationRun}
></ModelAlertsIndicator>
{index === 0 ? (
<CodiconRow
appearance="icon"
aria-label="Add new model"
onClick={(event: React.MouseEvent) => {
event.stopPropagation();
handleAddModelClick();
}}
disabled={addModelButtonDisabled}
>
<Codicon name="add" />
</CodiconRow>
) : (
<CodiconRow
appearance="icon"
aria-label="Remove model"
onClick={(event: React.MouseEvent) => {
event.stopPropagation();
removeModelClickedHandlers[index]();
}}
>
<Codicon name="trash" />
</CodiconRow>
)}
</ModelButtonsContainer>
</DataGridCell>
</DataGridRow>
);

View File

@@ -0,0 +1,46 @@
import { styled } from "styled-components";
import type { ModeledMethod } from "../../model-editor/modeled-method";
import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state";
import type { ModelEditorViewState } from "../../model-editor/shared/view-state";
const ModelAlertsButton = styled.button`
color: var(--vscode-editor-foreground);
background-color: var(--vscode-input-background);
border: none;
border-radius: 40%;
cursor: pointer;
`;
export type Props = {
viewState: ModelEditorViewState;
modeledMethod: ModeledMethod;
evaluationRun: ModelEvaluationRunState | undefined;
};
export const ModelAlertsIndicator = ({
viewState,
modeledMethod,
evaluationRun,
}: Props) => {
if (!viewState.showEvaluationUi) {
return null;
}
if (!evaluationRun || !modeledMethod) {
return null;
}
// TODO: Once we have alert provenance, we can show actual alert counts here.
// For now, we show a random number.
const number = Math.floor(Math.random() * 10);
return (
<ModelAlertsButton
onClick={(event: React.MouseEvent) => {
event.stopPropagation();
}}
>
{number}
</ModelAlertsButton>
);
};

View File

@@ -436,6 +436,7 @@ export function ModelEditor({
hideModeledMethods={hideModeledMethods}
revealedMethodSignature={revealedMethodSignature}
accessPathSuggestions={accessPathSuggestions}
evaluationRun={evaluationRun}
onChange={onChange}
onMethodClick={onMethodClick}
onSaveModelClick={onSaveModelClick}

View File

@@ -8,6 +8,7 @@ import type { ModelEditorViewState } from "../../model-editor/shared/view-state"
import { ScreenReaderOnly } from "../common/ScreenReaderOnly";
import { DataGrid, DataGridCell } from "../common/DataGrid";
import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions";
import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state";
export const MULTIPLE_MODELS_GRID_TEMPLATE_COLUMNS =
"0.5fr 0.125fr 0.125fr 0.125fr 0.125fr max-content";
@@ -23,6 +24,7 @@ export type ModeledMethodDataGridProps = {
hideModeledMethods: boolean;
revealedMethodSignature: string | null;
accessPathSuggestions?: AccessPathSuggestionOptions;
evaluationRun: ModelEvaluationRunState | undefined;
onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void;
onMethodClick: (methodSignature: string) => void;
};
@@ -38,6 +40,7 @@ export const ModeledMethodDataGrid = ({
hideModeledMethods,
revealedMethodSignature,
accessPathSuggestions,
evaluationRun,
onChange,
onMethodClick,
}: ModeledMethodDataGridProps) => {
@@ -101,6 +104,7 @@ export const ModeledMethodDataGrid = ({
revealedMethodSignature={revealedMethodSignature}
inputAccessPathSuggestions={inputAccessPathSuggestions}
outputAccessPathSuggestions={outputAccessPathSuggestions}
evaluationRun={evaluationRun}
onChange={onChange}
onMethodClick={onMethodClick}
/>

View File

@@ -9,6 +9,7 @@ import {
} from "../../model-editor/shared/sorting";
import type { ModelEditorViewState } from "../../model-editor/shared/view-state";
import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions";
import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state";
export type ModeledMethodsListProps = {
methods: Method[];
@@ -19,6 +20,7 @@ export type ModeledMethodsListProps = {
processedByAutoModelMethods: Set<string>;
revealedMethodSignature: string | null;
accessPathSuggestions?: AccessPathSuggestionOptions;
evaluationRun: ModelEvaluationRunState | undefined;
viewState: ModelEditorViewState;
hideModeledMethods: boolean;
onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void;
@@ -48,6 +50,7 @@ export const ModeledMethodsList = ({
hideModeledMethods,
revealedMethodSignature,
accessPathSuggestions,
evaluationRun,
onChange,
onMethodClick,
onSaveModelClick,
@@ -98,6 +101,7 @@ export const ModeledMethodsList = ({
hideModeledMethods={hideModeledMethods}
revealedMethodSignature={revealedMethodSignature}
accessPathSuggestions={accessPathSuggestions}
evaluationRun={evaluationRun}
onChange={onChange}
onMethodClick={onMethodClick}
onSaveModelClick={onSaveModelClick}

View File

@@ -37,6 +37,7 @@ describe(LibraryRow.name, () => {
selectedSignatures={new Set()}
inProgressMethods={new Set()}
processedByAutoModelMethods={new Set()}
evaluationRun={undefined}
viewState={viewState}
hideModeledMethods={false}
revealedMethodSignature={null}

View File

@@ -45,6 +45,7 @@ describe(MethodRow.name, () => {
modelingInProgress={false}
processedByAutoModel={false}
revealedMethodSignature={null}
evaluationRun={undefined}
viewState={viewState}
onChange={onChange}
onMethodClick={onMethodClick}

View File

@@ -0,0 +1,70 @@
import { render as reactRender, screen } from "@testing-library/react";
import { createMethod } from "../../../../test/factories/model-editor/method-factories";
import { createSummaryModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories";
import { createMockModelEditorViewState } from "../../../../test/factories/model-editor/view-state";
import type { Props } from "../ModelAlertsIndicator";
import { ModelAlertsIndicator } from "../ModelAlertsIndicator";
import { createMockVariantAnalysis } from "../../../../test/factories/variant-analysis/shared/variant-analysis";
import { VariantAnalysisStatus } from "../../../variant-analysis/shared/variant-analysis";
describe(ModelAlertsIndicator.name, () => {
const method = createMethod();
const modeledMethod = createSummaryModeledMethod(method);
const evaluationRun = {
isPreparing: false,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.Succeeded,
}),
};
const render = (props: Partial<Props> = {}) =>
reactRender(
<ModelAlertsIndicator
viewState={createMockModelEditorViewState({ showEvaluationUi: true })}
modeledMethod={modeledMethod}
evaluationRun={evaluationRun}
{...props}
/>,
);
describe("when showEvaluationUi is false", () => {
it("does not render anything", () => {
render({
viewState: createMockModelEditorViewState({ showEvaluationUi: false }),
});
expect(screen.queryByRole("button")).not.toBeInTheDocument();
});
});
describe("when there is no evaluation run", () => {
it("does not render anything", () => {
render({
evaluationRun: undefined,
});
expect(screen.queryByRole("button")).not.toBeInTheDocument();
});
});
describe("when there is no modeled method", () => {
it("does not render anything", () => {
render({
modeledMethod: undefined,
});
expect(screen.queryByRole("button")).not.toBeInTheDocument();
});
});
describe("when there is an evaluation run and a modeled method", () => {
// TODO: Once we have alert provenance, this will be an actual alert count instead of a random number.
it("renders a button with a random number", () => {
render();
const button = screen.queryByRole("button");
expect(button).toBeInTheDocument();
expect(button).toHaveTextContent(/\d/);
});
});
});

View File

@@ -59,6 +59,7 @@ describe(ModeledMethodDataGrid.name, () => {
selectedSignatures={new Set()}
inProgressMethods={new Set()}
processedByAutoModelMethods={new Set()}
evaluationRun={undefined}
viewState={viewState}
hideModeledMethods={false}
revealedMethodSignature={null}

View File

@@ -60,6 +60,7 @@ describe(ModeledMethodsList.name, () => {
selectedSignatures={new Set()}
inProgressMethods={new Set()}
processedByAutoModelMethods={new Set()}
evaluationRun={undefined}
viewState={viewState}
hideModeledMethods={false}
revealedMethodSignature={null}