Add "model alerts indicator" component (#3441)
This commit is contained in:
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -436,6 +436,7 @@ export function ModelEditor({
|
||||
hideModeledMethods={hideModeledMethods}
|
||||
revealedMethodSignature={revealedMethodSignature}
|
||||
accessPathSuggestions={accessPathSuggestions}
|
||||
evaluationRun={evaluationRun}
|
||||
onChange={onChange}
|
||||
onMethodClick={onMethodClick}
|
||||
onSaveModelClick={onSaveModelClick}
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -37,6 +37,7 @@ describe(LibraryRow.name, () => {
|
||||
selectedSignatures={new Set()}
|
||||
inProgressMethods={new Set()}
|
||||
processedByAutoModelMethods={new Set()}
|
||||
evaluationRun={undefined}
|
||||
viewState={viewState}
|
||||
hideModeledMethods={false}
|
||||
revealedMethodSignature={null}
|
||||
|
||||
@@ -45,6 +45,7 @@ describe(MethodRow.name, () => {
|
||||
modelingInProgress={false}
|
||||
processedByAutoModel={false}
|
||||
revealedMethodSignature={null}
|
||||
evaluationRun={undefined}
|
||||
viewState={viewState}
|
||||
onChange={onChange}
|
||||
onMethodClick={onMethodClick}
|
||||
|
||||
@@ -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/);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -59,6 +59,7 @@ describe(ModeledMethodDataGrid.name, () => {
|
||||
selectedSignatures={new Set()}
|
||||
inProgressMethods={new Set()}
|
||||
processedByAutoModelMethods={new Set()}
|
||||
evaluationRun={undefined}
|
||||
viewState={viewState}
|
||||
hideModeledMethods={false}
|
||||
revealedMethodSignature={null}
|
||||
|
||||
@@ -60,6 +60,7 @@ describe(ModeledMethodsList.name, () => {
|
||||
selectedSignatures={new Set()}
|
||||
inProgressMethods={new Set()}
|
||||
processedByAutoModelMethods={new Set()}
|
||||
evaluationRun={undefined}
|
||||
viewState={viewState}
|
||||
hideModeledMethods={false}
|
||||
revealedMethodSignature={null}
|
||||
|
||||
Reference in New Issue
Block a user