Skip to content

Commit 252c7a2

Browse files
Convert MethodRow to display multiple modelings
1 parent 86d7d83 commit 252c7a2

4 files changed

Lines changed: 111 additions & 60 deletions

File tree

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

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import { CallClassification, Method } from "../../model-editor/method";
77
import { ModeledMethod } from "../../model-editor/modeled-method";
88
import { VSCodeDataGrid } from "@vscode/webview-ui-toolkit/react";
99
import { GRID_TEMPLATE_COLUMNS } from "../../view/model-editor/ModeledMethodDataGrid";
10+
import { ModelEditorViewState } from "../../model-editor/shared/view-state";
11+
import { createMockExtensionPack } from "../../../test/factories/model-editor/extension-pack";
12+
import { Mode } from "../../model-editor/shared/mode";
1013

1114
export default {
1215
title: "CodeQL Model Editor/Method Row",
@@ -66,51 +69,66 @@ const modeledMethod: ModeledMethod = {
6669
methodParameters: "()",
6770
};
6871

72+
const viewState: ModelEditorViewState = {
73+
extensionPack: createMockExtensionPack(),
74+
showFlowGeneration: true,
75+
showLlmButton: true,
76+
showMultipleModels: true,
77+
mode: Mode.Application,
78+
};
79+
6980
export const Unmodeled = Template.bind({});
7081
Unmodeled.args = {
7182
method,
72-
modeledMethod: undefined,
83+
modeledMethods: [],
7384
methodCanBeModeled: true,
85+
viewState,
7486
};
7587

7688
export const Source = Template.bind({});
7789
Source.args = {
7890
method,
79-
modeledMethod: { ...modeledMethod, type: "source" },
91+
modeledMethods: [{ ...modeledMethod, type: "source" }],
8092
methodCanBeModeled: true,
93+
viewState,
8194
};
8295

8396
export const Sink = Template.bind({});
8497
Sink.args = {
8598
method,
86-
modeledMethod: { ...modeledMethod, type: "sink" },
99+
modeledMethods: [{ ...modeledMethod, type: "sink" }],
87100
methodCanBeModeled: true,
101+
viewState,
88102
};
89103

90104
export const Summary = Template.bind({});
91105
Summary.args = {
92106
method,
93-
modeledMethod: { ...modeledMethod, type: "summary" },
107+
modeledMethods: [{ ...modeledMethod, type: "summary" }],
94108
methodCanBeModeled: true,
109+
viewState,
95110
};
96111

97112
export const Neutral = Template.bind({});
98113
Neutral.args = {
99114
method,
100-
modeledMethod: { ...modeledMethod, type: "neutral" },
115+
modeledMethods: [{ ...modeledMethod, type: "neutral" }],
101116
methodCanBeModeled: true,
117+
viewState,
102118
};
103119

104120
export const AlreadyModeled = Template.bind({});
105121
AlreadyModeled.args = {
106122
method: { ...method, supported: true },
107-
modeledMethod: undefined,
123+
modeledMethods: [],
124+
viewState,
108125
};
109126

110127
export const ModelingInProgress = Template.bind({});
111128
ModelingInProgress.args = {
112129
method,
113-
modeledMethod,
130+
modeledMethods: [modeledMethod],
114131
modelingInProgress: true,
115132
methodCanBeModeled: true,
133+
viewState,
116134
};

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

Lines changed: 64 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ import { ModelInputDropdown } from "./ModelInputDropdown";
2323
import { ModelOutputDropdown } from "./ModelOutputDropdown";
2424
import { ModelEditorViewState } from "../../model-editor/shared/view-state";
2525

26+
const MultiModelColumn = styled(VSCodeDataGridCell)`
27+
display: flex;
28+
flex-direction: column;
29+
gap: 0.5em;
30+
`;
31+
2632
const ApiOrMethodRow = styled.div`
2733
min-height: calc(var(--input-height) * 1px);
2834
display: flex;
@@ -57,7 +63,7 @@ const DataGridRow = styled(VSCodeDataGridRow)<{ focused?: boolean }>`
5763
export type MethodRowProps = {
5864
method: Method;
5965
methodCanBeModeled: boolean;
60-
modeledMethod: ModeledMethod | undefined;
66+
modeledMethods: ModeledMethod[];
6167
methodIsUnsaved: boolean;
6268
modelingInProgress: boolean;
6369
viewState: ModelEditorViewState;
@@ -90,22 +96,23 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
9096
(props, ref) => {
9197
const {
9298
method,
93-
modeledMethod,
99+
modeledMethods: modeledMethodsArg,
94100
methodIsUnsaved,
95101
viewState,
96102
revealedMethodSignature,
97103
onChange,
98104
} = props;
99105

106+
const modeledMethods = viewState.showMultipleModels
107+
? modeledMethodsArg
108+
: modeledMethodsArg.slice(0, 1);
109+
100110
const jumpToUsage = useCallback(
101111
() => sendJumpToUsageMessage(method),
102112
[method],
103113
);
104114

105-
const modelingStatus = getModelingStatus(
106-
modeledMethod ? [modeledMethod] : [],
107-
methodIsUnsaved,
108-
);
115+
const modelingStatus = getModelingStatus(modeledMethods, methodIsUnsaved);
109116

110117
return (
111118
<DataGridRow
@@ -145,34 +152,46 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
145152
)}
146153
{!props.modelingInProgress && (
147154
<>
148-
<VSCodeDataGridCell gridColumn={2}>
149-
<ModelTypeDropdown
150-
method={method}
151-
modeledMethod={modeledMethod}
152-
onChange={onChange}
153-
/>
154-
</VSCodeDataGridCell>
155-
<VSCodeDataGridCell gridColumn={3}>
156-
<ModelInputDropdown
157-
method={method}
158-
modeledMethod={modeledMethod}
159-
onChange={onChange}
160-
/>
161-
</VSCodeDataGridCell>
162-
<VSCodeDataGridCell gridColumn={4}>
163-
<ModelOutputDropdown
164-
method={method}
165-
modeledMethod={modeledMethod}
166-
onChange={onChange}
167-
/>
168-
</VSCodeDataGridCell>
169-
<VSCodeDataGridCell gridColumn={5}>
170-
<ModelKindDropdown
171-
method={method}
172-
modeledMethod={modeledMethod}
173-
onChange={onChange}
174-
/>
175-
</VSCodeDataGridCell>
155+
<MultiModelColumn gridColumn={2}>
156+
{forEachModeledMethod(modeledMethods, (modeledMethod) => (
157+
<ModelTypeDropdown
158+
key={JSON.stringify(modeledMethod)}
159+
method={method}
160+
modeledMethod={modeledMethod}
161+
onChange={onChange}
162+
/>
163+
))}
164+
</MultiModelColumn>
165+
<MultiModelColumn gridColumn={3}>
166+
{forEachModeledMethod(modeledMethods, (modeledMethod) => (
167+
<ModelInputDropdown
168+
key={JSON.stringify(modeledMethod)}
169+
method={method}
170+
modeledMethod={modeledMethod}
171+
onChange={onChange}
172+
/>
173+
))}
174+
</MultiModelColumn>
175+
<MultiModelColumn gridColumn={4}>
176+
{forEachModeledMethod(modeledMethods, (modeledMethod) => (
177+
<ModelOutputDropdown
178+
key={JSON.stringify(modeledMethod)}
179+
method={method}
180+
modeledMethod={modeledMethod}
181+
onChange={onChange}
182+
/>
183+
))}
184+
</MultiModelColumn>
185+
<MultiModelColumn gridColumn={5}>
186+
{forEachModeledMethod(modeledMethods, (modeledMethod) => (
187+
<ModelKindDropdown
188+
key={JSON.stringify(modeledMethod)}
189+
method={method}
190+
modeledMethod={modeledMethod}
191+
onChange={onChange}
192+
/>
193+
))}
194+
</MultiModelColumn>
176195
</>
177196
)}
178197
</DataGridRow>
@@ -227,3 +246,14 @@ function sendJumpToUsageMessage(method: Method) {
227246
usage: method.usages[0],
228247
});
229248
}
249+
250+
function forEachModeledMethod(
251+
modeledMethods: ModeledMethod[],
252+
renderer: (modeledMethod: ModeledMethod | undefined) => JSX.Element,
253+
): JSX.Element | JSX.Element[] {
254+
if (modeledMethods.length === 0) {
255+
return renderer(undefined);
256+
} else {
257+
return modeledMethods.map(renderer);
258+
}
259+
}

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

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -84,22 +84,25 @@ export const ModeledMethodDataGrid = ({
8484
Kind
8585
</VSCodeDataGridCell>
8686
</VSCodeDataGridRow>
87-
{methodsWithModelability.map(({ method, methodCanBeModeled }) => (
88-
<MethodRow
89-
key={method.signature}
90-
method={method}
91-
methodCanBeModeled={methodCanBeModeled}
92-
modeledMethod={modeledMethods[method.signature]}
93-
methodIsUnsaved={modifiedSignatures.has(method.signature)}
94-
modelingInProgress={inProgressMethods.hasMethod(
95-
packageName,
96-
method.signature,
97-
)}
98-
viewState={viewState}
99-
revealedMethodSignature={revealedMethodSignature}
100-
onChange={onChange}
101-
/>
102-
))}
87+
{methodsWithModelability.map(({ method, methodCanBeModeled }) => {
88+
const modeledMethod = modeledMethods[method.signature];
89+
return (
90+
<MethodRow
91+
key={method.signature}
92+
method={method}
93+
methodCanBeModeled={methodCanBeModeled}
94+
modeledMethods={modeledMethod ? [modeledMethod] : []}
95+
methodIsUnsaved={modifiedSignatures.has(method.signature)}
96+
modelingInProgress={inProgressMethods.hasMethod(
97+
packageName,
98+
method.signature,
99+
)}
100+
viewState={viewState}
101+
revealedMethodSignature={revealedMethodSignature}
102+
onChange={onChange}
103+
/>
104+
);
105+
})}
103106
</>
104107
)}
105108
<HiddenMethodsRow

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ describe(MethodRow.name, () => {
4646
<MethodRow
4747
method={method}
4848
methodCanBeModeled={true}
49-
modeledMethod={modeledMethod}
49+
modeledMethods={[modeledMethod]}
5050
methodIsUnsaved={false}
5151
modelingInProgress={false}
5252
revealedMethodSignature={null}
@@ -120,7 +120,7 @@ describe(MethodRow.name, () => {
120120

121121
it("shows the modeling status indicator when unmodeled", () => {
122122
render({
123-
modeledMethod: undefined,
123+
modeledMethods: [],
124124
});
125125

126126
expect(screen.getByLabelText("Method not modeled")).toBeInTheDocument();
@@ -137,7 +137,7 @@ describe(MethodRow.name, () => {
137137
it("renders an unmodelable method", () => {
138138
render({
139139
methodCanBeModeled: false,
140-
modeledMethod: undefined,
140+
modeledMethods: [],
141141
});
142142

143143
expect(screen.queryByRole("combobox")).not.toBeInTheDocument();

0 commit comments

Comments
 (0)