From e75eccb4168798e258fcb1c3931f2459b5be56be Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 4 Oct 2023 12:34:40 +0100 Subject: [PATCH 1/9] Change MethodRow to accept ModelEditorViewState object instead of just Mode --- .../ql-vscode/src/view/model-editor/LibraryRow.tsx | 2 +- .../ql-vscode/src/view/model-editor/MethodRow.tsx | 11 ++++++----- .../src/view/model-editor/ModeledMethodDataGrid.tsx | 8 ++++---- .../view/model-editor/__tests__/MethodRow.spec.tsx | 12 +++++++++++- .../__tests__/ModeledMethodDataGrid.spec.tsx | 12 +++++++++++- 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx b/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx index 2c7871a698a..74c2e3fbc72 100644 --- a/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx @@ -235,7 +235,7 @@ export const LibraryRow = ({ modeledMethods={modeledMethods} modifiedSignatures={modifiedSignatures} inProgressMethods={inProgressMethods} - mode={viewState.mode} + viewState={viewState} hideModeledMethods={hideModeledMethods} revealedMethodSignature={revealedMethodSignature} onChange={onChange} diff --git a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx index b1880355cb1..21fde280141 100644 --- a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx @@ -21,6 +21,7 @@ import { MethodName } from "./MethodName"; import { ModelTypeDropdown } from "./ModelTypeDropdown"; import { ModelInputDropdown } from "./ModelInputDropdown"; import { ModelOutputDropdown } from "./ModelOutputDropdown"; +import { ModelEditorViewState } from "../../model-editor/shared/view-state"; const ApiOrMethodCell = styled(VSCodeDataGridCell)` display: flex; @@ -58,7 +59,7 @@ export type MethodRowProps = { modeledMethod: ModeledMethod | undefined; methodIsUnsaved: boolean; modelingInProgress: boolean; - mode: Mode; + viewState: ModelEditorViewState; revealedMethodSignature: string | null; onChange: (modeledMethod: ModeledMethod) => void; }; @@ -90,7 +91,7 @@ const ModelableMethodRow = forwardRef( method, modeledMethod, methodIsUnsaved, - mode, + viewState, revealedMethodSignature, onChange, } = props; @@ -112,7 +113,7 @@ const ModelableMethodRow = forwardRef( - {mode === Mode.Application && ( + {viewState.mode === Mode.Application && ( {method.usages.length} @@ -178,7 +179,7 @@ const UnmodelableMethodRow = forwardRef< HTMLElement | undefined, MethodRowProps >((props, ref) => { - const { method, mode, revealedMethodSignature } = props; + const { method, viewState, revealedMethodSignature } = props; const jumpToUsage = useCallback( () => sendJumpToUsageMessage(method), @@ -194,7 +195,7 @@ const UnmodelableMethodRow = forwardRef< - {mode === Mode.Application && ( + {viewState.mode === Mode.Application && ( {method.usages.length} diff --git a/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx b/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx index 11044c5ff69..9ff27ad21f8 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx @@ -8,10 +8,10 @@ import { MethodRow } from "./MethodRow"; import { Method } from "../../model-editor/method"; import { ModeledMethod } from "../../model-editor/modeled-method"; import { useMemo } from "react"; -import { Mode } from "../../model-editor/shared/mode"; import { sortMethods } from "../../model-editor/shared/sorting"; import { InProgressMethods } from "../../model-editor/shared/in-progress-methods"; import { HiddenMethodsRow } from "./HiddenMethodsRow"; +import { ModelEditorViewState } from "../../model-editor/shared/view-state"; export const GRID_TEMPLATE_COLUMNS = "0.5fr 0.125fr 0.125fr 0.125fr 0.125fr"; @@ -21,7 +21,7 @@ export type ModeledMethodDataGridProps = { modeledMethods: Record; modifiedSignatures: Set; inProgressMethods: InProgressMethods; - mode: Mode; + viewState: ModelEditorViewState; hideModeledMethods: boolean; revealedMethodSignature: string | null; onChange: (modeledMethod: ModeledMethod) => void; @@ -33,7 +33,7 @@ export const ModeledMethodDataGrid = ({ modeledMethods, modifiedSignatures, inProgressMethods, - mode, + viewState, hideModeledMethods, revealedMethodSignature, onChange, @@ -95,7 +95,7 @@ export const ModeledMethodDataGrid = ({ packageName, method.signature, )} - mode={mode} + viewState={viewState} revealedMethodSignature={revealedMethodSignature} onChange={onChange} /> diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx index 2c22455c0c8..64874f60cf9 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx @@ -9,6 +9,8 @@ import { Mode } from "../../../model-editor/shared/mode"; import { MethodRow, MethodRowProps } from "../MethodRow"; import { ModeledMethod } from "../../../model-editor/modeled-method"; import userEvent from "@testing-library/user-event"; +import { ModelEditorViewState } from "../../../model-editor/shared/view-state"; +import { createMockExtensionPack } from "../../../../test/factories/model-editor/extension-pack"; describe(MethodRow.name, () => { const method = createMethod({ @@ -31,6 +33,14 @@ describe(MethodRow.name, () => { }; const onChange = jest.fn(); + const viewState: ModelEditorViewState = { + mode: Mode.Application, + showFlowGeneration: false, + showLlmButton: false, + showMultipleModels: false, + extensionPack: createMockExtensionPack(), + }; + const render = (props: Partial = {}) => reactRender( { methodIsUnsaved={false} modelingInProgress={false} revealedMethodSignature={null} - mode={Mode.Application} + viewState={viewState} onChange={onChange} {...props} />, diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodDataGrid.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodDataGrid.spec.tsx index 25cb0268cda..a9ae87ba68d 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodDataGrid.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodDataGrid.spec.tsx @@ -7,6 +7,8 @@ import { ModeledMethodDataGrid, ModeledMethodDataGridProps, } from "../ModeledMethodDataGrid"; +import { ModelEditorViewState } from "../../../model-editor/shared/view-state"; +import { createMockExtensionPack } from "../../../../test/factories/model-editor/extension-pack"; describe(ModeledMethodDataGrid.name, () => { const method1 = createMethod({ @@ -41,6 +43,14 @@ describe(ModeledMethodDataGrid.name, () => { }); const onChange = jest.fn(); + const viewState: ModelEditorViewState = { + mode: Mode.Application, + showFlowGeneration: false, + showLlmButton: false, + showMultipleModels: false, + extensionPack: createMockExtensionPack(), + }; + const render = (props: Partial = {}) => reactRender( { }} modifiedSignatures={new Set([method1.signature])} inProgressMethods={new InProgressMethods()} - mode={Mode.Application} + viewState={viewState} hideModeledMethods={false} revealedMethodSignature={null} onChange={onChange} From a704cd7baed8c4179c8142c4cea3c16469613224 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 4 Oct 2023 14:27:59 +0100 Subject: [PATCH 2/9] Convert getModelingStatus to take ModeledMethod[] --- .../methods-usage/methods-usage-data-provider.ts | 5 ++++- .../ql-vscode/src/model-editor/shared/modeling-status.ts | 6 +++--- .../src/view/method-modeling/MethodModelingView.tsx | 3 ++- extensions/ql-vscode/src/view/model-editor/MethodRow.tsx | 5 ++++- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts b/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts index fe8ee875d0c..44427657927 100644 --- a/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts +++ b/extensions/ql-vscode/src/model-editor/methods-usage/methods-usage-data-provider.ts @@ -102,7 +102,10 @@ export class MethodsUsageDataProvider const modeledMethod = this.modeledMethods[method.signature]; const modifiedMethod = this.modifiedMethodSignatures.has(method.signature); - const status = getModelingStatus(modeledMethod, modifiedMethod); + const status = getModelingStatus( + modeledMethod ? [modeledMethod] : [], + modifiedMethod, + ); switch (status) { case "unmodeled": return new ThemeIcon("error", new ThemeColor("errorForeground")); diff --git a/extensions/ql-vscode/src/model-editor/shared/modeling-status.ts b/extensions/ql-vscode/src/model-editor/shared/modeling-status.ts index 496b4c0e7fc..f0737ec322d 100644 --- a/extensions/ql-vscode/src/model-editor/shared/modeling-status.ts +++ b/extensions/ql-vscode/src/model-editor/shared/modeling-status.ts @@ -3,13 +3,13 @@ import { ModeledMethod } from "../modeled-method"; export type ModelingStatus = "unmodeled" | "unsaved" | "saved"; export function getModelingStatus( - modeledMethod: ModeledMethod | undefined, + modeledMethods: ModeledMethod[], methodIsUnsaved: boolean, ): ModelingStatus { - if (modeledMethod) { + if (modeledMethods.length > 0) { if (methodIsUnsaved) { return "unsaved"; - } else if (modeledMethod.type !== "none") { + } else if (modeledMethods.some((m) => m.type !== "none")) { return "saved"; } } diff --git a/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx b/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx index 390d2fde9c4..9125fbe6aa6 100644 --- a/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx +++ b/extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx @@ -18,7 +18,8 @@ export function MethodModelingView(): JSX.Element { const [isMethodModified, setIsMethodModified] = useState(false); const modelingStatus = useMemo( - () => getModelingStatus(modeledMethod, isMethodModified), + () => + getModelingStatus(modeledMethod ? [modeledMethod] : [], isMethodModified), [modeledMethod, isMethodModified], ); diff --git a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx index 21fde280141..182366448f0 100644 --- a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx @@ -101,7 +101,10 @@ const ModelableMethodRow = forwardRef( [method], ); - const modelingStatus = getModelingStatus(modeledMethod, methodIsUnsaved); + const modelingStatus = getModelingStatus( + modeledMethod ? [modeledMethod] : [], + methodIsUnsaved, + ); return ( Date: Wed, 4 Oct 2023 15:41:08 +0100 Subject: [PATCH 3/9] Convert ApiOrMethodCell to ApiOrMethodRow --- .../src/view/model-editor/MethodRow.tsx | 53 ++++++++++--------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx index 182366448f0..7d7e0c66d35 100644 --- a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx @@ -23,7 +23,8 @@ import { ModelInputDropdown } from "./ModelInputDropdown"; import { ModelOutputDropdown } from "./ModelOutputDropdown"; import { ModelEditorViewState } from "../../model-editor/shared/view-state"; -const ApiOrMethodCell = styled(VSCodeDataGridCell)` +const ApiOrMethodRow = styled.div` + min-height: calc(var(--input-height) * 1px); display: flex; flex-direction: row; align-items: center; @@ -112,18 +113,20 @@ const ModelableMethodRow = forwardRef( ref={ref} focused={revealedMethodSignature === method.signature} > - - - - - {viewState.mode === Mode.Application && ( - - {method.usages.length} - - )} - View - {props.modelingInProgress && } - + + + + + + {viewState.mode === Mode.Application && ( + + {method.usages.length} + + )} + View + {props.modelingInProgress && } + + {props.modelingInProgress && ( <> @@ -195,17 +198,19 @@ const UnmodelableMethodRow = forwardRef< ref={ref} focused={revealedMethodSignature === method.signature} > - - - - {viewState.mode === Mode.Application && ( - - {method.usages.length} - - )} - View - - + + + + + {viewState.mode === Mode.Application && ( + + {method.usages.length} + + )} + View + + + Method already modeled From 252c7a20c4560ef029a6090394bbd86c3544acf6 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 4 Oct 2023 15:42:13 +0100 Subject: [PATCH 4/9] Convert MethodRow to display multiple modelings --- .../model-editor/MethodRow.stories.tsx | 32 ++++-- .../src/view/model-editor/MethodRow.tsx | 98 ++++++++++++------- .../model-editor/ModeledMethodDataGrid.tsx | 35 ++++--- .../model-editor/__tests__/MethodRow.spec.tsx | 6 +- 4 files changed, 111 insertions(+), 60 deletions(-) diff --git a/extensions/ql-vscode/src/stories/model-editor/MethodRow.stories.tsx b/extensions/ql-vscode/src/stories/model-editor/MethodRow.stories.tsx index 9c2118914d7..2e15420a141 100644 --- a/extensions/ql-vscode/src/stories/model-editor/MethodRow.stories.tsx +++ b/extensions/ql-vscode/src/stories/model-editor/MethodRow.stories.tsx @@ -7,6 +7,9 @@ import { CallClassification, Method } from "../../model-editor/method"; import { ModeledMethod } from "../../model-editor/modeled-method"; import { VSCodeDataGrid } from "@vscode/webview-ui-toolkit/react"; import { GRID_TEMPLATE_COLUMNS } from "../../view/model-editor/ModeledMethodDataGrid"; +import { ModelEditorViewState } from "../../model-editor/shared/view-state"; +import { createMockExtensionPack } from "../../../test/factories/model-editor/extension-pack"; +import { Mode } from "../../model-editor/shared/mode"; export default { title: "CodeQL Model Editor/Method Row", @@ -66,51 +69,66 @@ const modeledMethod: ModeledMethod = { methodParameters: "()", }; +const viewState: ModelEditorViewState = { + extensionPack: createMockExtensionPack(), + showFlowGeneration: true, + showLlmButton: true, + showMultipleModels: true, + mode: Mode.Application, +}; + export const Unmodeled = Template.bind({}); Unmodeled.args = { method, - modeledMethod: undefined, + modeledMethods: [], methodCanBeModeled: true, + viewState, }; export const Source = Template.bind({}); Source.args = { method, - modeledMethod: { ...modeledMethod, type: "source" }, + modeledMethods: [{ ...modeledMethod, type: "source" }], methodCanBeModeled: true, + viewState, }; export const Sink = Template.bind({}); Sink.args = { method, - modeledMethod: { ...modeledMethod, type: "sink" }, + modeledMethods: [{ ...modeledMethod, type: "sink" }], methodCanBeModeled: true, + viewState, }; export const Summary = Template.bind({}); Summary.args = { method, - modeledMethod: { ...modeledMethod, type: "summary" }, + modeledMethods: [{ ...modeledMethod, type: "summary" }], methodCanBeModeled: true, + viewState, }; export const Neutral = Template.bind({}); Neutral.args = { method, - modeledMethod: { ...modeledMethod, type: "neutral" }, + modeledMethods: [{ ...modeledMethod, type: "neutral" }], methodCanBeModeled: true, + viewState, }; export const AlreadyModeled = Template.bind({}); AlreadyModeled.args = { method: { ...method, supported: true }, - modeledMethod: undefined, + modeledMethods: [], + viewState, }; export const ModelingInProgress = Template.bind({}); ModelingInProgress.args = { method, - modeledMethod, + modeledMethods: [modeledMethod], modelingInProgress: true, methodCanBeModeled: true, + viewState, }; diff --git a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx index 7d7e0c66d35..32be92cda65 100644 --- a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx @@ -23,6 +23,12 @@ import { ModelInputDropdown } from "./ModelInputDropdown"; import { ModelOutputDropdown } from "./ModelOutputDropdown"; import { ModelEditorViewState } from "../../model-editor/shared/view-state"; +const MultiModelColumn = styled(VSCodeDataGridCell)` + display: flex; + flex-direction: column; + gap: 0.5em; +`; + const ApiOrMethodRow = styled.div` min-height: calc(var(--input-height) * 1px); display: flex; @@ -57,7 +63,7 @@ const DataGridRow = styled(VSCodeDataGridRow)<{ focused?: boolean }>` export type MethodRowProps = { method: Method; methodCanBeModeled: boolean; - modeledMethod: ModeledMethod | undefined; + modeledMethods: ModeledMethod[]; methodIsUnsaved: boolean; modelingInProgress: boolean; viewState: ModelEditorViewState; @@ -90,22 +96,23 @@ const ModelableMethodRow = forwardRef( (props, ref) => { const { method, - modeledMethod, + modeledMethods: modeledMethodsArg, methodIsUnsaved, viewState, revealedMethodSignature, onChange, } = props; + const modeledMethods = viewState.showMultipleModels + ? modeledMethodsArg + : modeledMethodsArg.slice(0, 1); + const jumpToUsage = useCallback( () => sendJumpToUsageMessage(method), [method], ); - const modelingStatus = getModelingStatus( - modeledMethod ? [modeledMethod] : [], - methodIsUnsaved, - ); + const modelingStatus = getModelingStatus(modeledMethods, methodIsUnsaved); return ( ( )} {!props.modelingInProgress && ( <> - - - - - - - - - - - - + + {forEachModeledMethod(modeledMethods, (modeledMethod) => ( + + ))} + + + {forEachModeledMethod(modeledMethods, (modeledMethod) => ( + + ))} + + + {forEachModeledMethod(modeledMethods, (modeledMethod) => ( + + ))} + + + {forEachModeledMethod(modeledMethods, (modeledMethod) => ( + + ))} + )} @@ -227,3 +246,14 @@ function sendJumpToUsageMessage(method: Method) { usage: method.usages[0], }); } + +function forEachModeledMethod( + modeledMethods: ModeledMethod[], + renderer: (modeledMethod: ModeledMethod | undefined) => JSX.Element, +): JSX.Element | JSX.Element[] { + if (modeledMethods.length === 0) { + return renderer(undefined); + } else { + return modeledMethods.map(renderer); + } +} diff --git a/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx b/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx index 9ff27ad21f8..bf53b479cdb 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx @@ -84,22 +84,25 @@ export const ModeledMethodDataGrid = ({ Kind - {methodsWithModelability.map(({ method, methodCanBeModeled }) => ( - - ))} + {methodsWithModelability.map(({ method, methodCanBeModeled }) => { + const modeledMethod = modeledMethods[method.signature]; + return ( + + ); + })} )} { { it("shows the modeling status indicator when unmodeled", () => { render({ - modeledMethod: undefined, + modeledMethods: [], }); expect(screen.getByLabelText("Method not modeled")).toBeInTheDocument(); @@ -137,7 +137,7 @@ describe(MethodRow.name, () => { it("renders an unmodelable method", () => { render({ methodCanBeModeled: false, - modeledMethod: undefined, + modeledMethods: [], }); expect(screen.queryByRole("combobox")).not.toBeInTheDocument(); From 8eef4eb4f25b4c65f02c6e0794cd545c8c3466f1 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 4 Oct 2023 15:42:35 +0100 Subject: [PATCH 5/9] Add story with multiple modelings --- .../src/stories/model-editor/MethodRow.stories.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/extensions/ql-vscode/src/stories/model-editor/MethodRow.stories.tsx b/extensions/ql-vscode/src/stories/model-editor/MethodRow.stories.tsx index 2e15420a141..9ea43028a21 100644 --- a/extensions/ql-vscode/src/stories/model-editor/MethodRow.stories.tsx +++ b/extensions/ql-vscode/src/stories/model-editor/MethodRow.stories.tsx @@ -132,3 +132,15 @@ ModelingInProgress.args = { methodCanBeModeled: true, viewState, }; + +export const MultipleModelings = Template.bind({}); +MultipleModelings.args = { + method, + modeledMethods: [ + { ...modeledMethod, type: "source" }, + { ...modeledMethod, type: "sink" }, + { ...modeledMethod }, + ], + methodCanBeModeled: true, + viewState, +}; From f87b1c46de5d6580626a152cd32ea0db2d90ddbc Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 4 Oct 2023 18:26:52 +0100 Subject: [PATCH 6/9] Add some simple tests of rendering multiple models --- .../model-editor/__tests__/MethodRow.spec.tsx | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx index 11d43502117..fb1b9960111 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx @@ -134,6 +134,44 @@ describe(MethodRow.name, () => { expect(screen.getByLabelText("Loading")).toBeInTheDocument(); }); + it("can render multiple models", () => { + render({ + modeledMethods: [ + { ...modeledMethod, type: "source" }, + { ...modeledMethod, type: "sink" }, + { ...modeledMethod, type: "summary" }, + ], + viewState: { + ...viewState, + showMultipleModels: true, + }, + }); + + const kindInputs = screen.getAllByRole("combobox", { name: "Model type" }); + expect(kindInputs).toHaveLength(3); + expect(kindInputs[0]).toHaveValue("source"); + expect(kindInputs[1]).toHaveValue("sink"); + expect(kindInputs[2]).toHaveValue("summary"); + }); + + it("renders only first model when showMultipleModels feature flag is disabled", () => { + render({ + modeledMethods: [ + { ...modeledMethod, type: "source" }, + { ...modeledMethod, type: "sink" }, + { ...modeledMethod, type: "summary" }, + ], + viewState: { + ...viewState, + showMultipleModels: false, + }, + }); + + const kindInputs = screen.getAllByRole("combobox", { name: "Model type" }); + expect(kindInputs.length).toBe(1); + expect(kindInputs[0]).toHaveValue("source"); + }); + it("renders an unmodelable method", () => { render({ methodCanBeModeled: false, From 5ba64a1db3553c3844c9428afa255676f5983ddc Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 4 Oct 2023 18:45:56 +0100 Subject: [PATCH 7/9] Add test for when there's no modeled method --- .../src/view/model-editor/__tests__/MethodRow.spec.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx index fb1b9960111..4f34ab1e7a2 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx @@ -64,6 +64,14 @@ describe(MethodRow.name, () => { expect(screen.queryByLabelText("Loading")).not.toBeInTheDocument(); }); + it("renders when there is no modeled method", () => { + render({ modeledMethods: [] }); + + expect(screen.queryAllByRole("combobox")).toHaveLength(4); + expect(screen.getByLabelText("Method not modeled")).toBeInTheDocument(); + expect(screen.queryByLabelText("Loading")).not.toBeInTheDocument(); + }); + it("can change the kind", async () => { render(); From 8b0825ab3c98dfc15c5b2cfe4c8c7784989de5ec Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 9 Oct 2023 14:32:56 +0100 Subject: [PATCH 8/9] Use better name for variable --- extensions/ql-vscode/src/view/model-editor/MethodRow.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx index 32be92cda65..710367f1fd5 100644 --- a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx @@ -96,7 +96,7 @@ const ModelableMethodRow = forwardRef( (props, ref) => { const { method, - modeledMethods: modeledMethodsArg, + modeledMethods: modeledMethodsProp, methodIsUnsaved, viewState, revealedMethodSignature, @@ -104,8 +104,8 @@ const ModelableMethodRow = forwardRef( } = props; const modeledMethods = viewState.showMultipleModels - ? modeledMethodsArg - : modeledMethodsArg.slice(0, 1); + ? modeledMethodsProp + : modeledMethodsProp.slice(0, 1); const jumpToUsage = useCallback( () => sendJumpToUsageMessage(method), From 0265353370e08d8628287e7db2b8150cee306464 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 9 Oct 2023 14:44:43 +0100 Subject: [PATCH 9/9] Use index as react key --- .../src/view/model-editor/MethodRow.tsx | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx index 710367f1fd5..dc8581c7d80 100644 --- a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx @@ -153,9 +153,9 @@ const ModelableMethodRow = forwardRef( {!props.modelingInProgress && ( <> - {forEachModeledMethod(modeledMethods, (modeledMethod) => ( + {forEachModeledMethod(modeledMethods, (modeledMethod, index) => ( ( ))} - {forEachModeledMethod(modeledMethods, (modeledMethod) => ( + {forEachModeledMethod(modeledMethods, (modeledMethod, index) => ( ( ))} - {forEachModeledMethod(modeledMethods, (modeledMethod) => ( + {forEachModeledMethod(modeledMethods, (modeledMethod, index) => ( ( ))} - {forEachModeledMethod(modeledMethods, (modeledMethod) => ( + {forEachModeledMethod(modeledMethods, (modeledMethod, index) => ( JSX.Element, + renderer: ( + modeledMethod: ModeledMethod | undefined, + index: number, + ) => JSX.Element, ): JSX.Element | JSX.Element[] { if (modeledMethods.length === 0) { - return renderer(undefined); + return renderer(undefined, 0); } else { return modeledMethods.map(renderer); }