Convert MethodRow to display multiple modelings
This commit is contained in:
@@ -7,6 +7,9 @@ import { CallClassification, Method } from "../../model-editor/method";
|
|||||||
import { ModeledMethod } from "../../model-editor/modeled-method";
|
import { ModeledMethod } from "../../model-editor/modeled-method";
|
||||||
import { VSCodeDataGrid } from "@vscode/webview-ui-toolkit/react";
|
import { VSCodeDataGrid } from "@vscode/webview-ui-toolkit/react";
|
||||||
import { GRID_TEMPLATE_COLUMNS } from "../../view/model-editor/ModeledMethodDataGrid";
|
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 {
|
export default {
|
||||||
title: "CodeQL Model Editor/Method Row",
|
title: "CodeQL Model Editor/Method Row",
|
||||||
@@ -66,51 +69,66 @@ const modeledMethod: ModeledMethod = {
|
|||||||
methodParameters: "()",
|
methodParameters: "()",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const viewState: ModelEditorViewState = {
|
||||||
|
extensionPack: createMockExtensionPack(),
|
||||||
|
showFlowGeneration: true,
|
||||||
|
showLlmButton: true,
|
||||||
|
showMultipleModels: true,
|
||||||
|
mode: Mode.Application,
|
||||||
|
};
|
||||||
|
|
||||||
export const Unmodeled = Template.bind({});
|
export const Unmodeled = Template.bind({});
|
||||||
Unmodeled.args = {
|
Unmodeled.args = {
|
||||||
method,
|
method,
|
||||||
modeledMethod: undefined,
|
modeledMethods: [],
|
||||||
methodCanBeModeled: true,
|
methodCanBeModeled: true,
|
||||||
|
viewState,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Source = Template.bind({});
|
export const Source = Template.bind({});
|
||||||
Source.args = {
|
Source.args = {
|
||||||
method,
|
method,
|
||||||
modeledMethod: { ...modeledMethod, type: "source" },
|
modeledMethods: [{ ...modeledMethod, type: "source" }],
|
||||||
methodCanBeModeled: true,
|
methodCanBeModeled: true,
|
||||||
|
viewState,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Sink = Template.bind({});
|
export const Sink = Template.bind({});
|
||||||
Sink.args = {
|
Sink.args = {
|
||||||
method,
|
method,
|
||||||
modeledMethod: { ...modeledMethod, type: "sink" },
|
modeledMethods: [{ ...modeledMethod, type: "sink" }],
|
||||||
methodCanBeModeled: true,
|
methodCanBeModeled: true,
|
||||||
|
viewState,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Summary = Template.bind({});
|
export const Summary = Template.bind({});
|
||||||
Summary.args = {
|
Summary.args = {
|
||||||
method,
|
method,
|
||||||
modeledMethod: { ...modeledMethod, type: "summary" },
|
modeledMethods: [{ ...modeledMethod, type: "summary" }],
|
||||||
methodCanBeModeled: true,
|
methodCanBeModeled: true,
|
||||||
|
viewState,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Neutral = Template.bind({});
|
export const Neutral = Template.bind({});
|
||||||
Neutral.args = {
|
Neutral.args = {
|
||||||
method,
|
method,
|
||||||
modeledMethod: { ...modeledMethod, type: "neutral" },
|
modeledMethods: [{ ...modeledMethod, type: "neutral" }],
|
||||||
methodCanBeModeled: true,
|
methodCanBeModeled: true,
|
||||||
|
viewState,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AlreadyModeled = Template.bind({});
|
export const AlreadyModeled = Template.bind({});
|
||||||
AlreadyModeled.args = {
|
AlreadyModeled.args = {
|
||||||
method: { ...method, supported: true },
|
method: { ...method, supported: true },
|
||||||
modeledMethod: undefined,
|
modeledMethods: [],
|
||||||
|
viewState,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ModelingInProgress = Template.bind({});
|
export const ModelingInProgress = Template.bind({});
|
||||||
ModelingInProgress.args = {
|
ModelingInProgress.args = {
|
||||||
method,
|
method,
|
||||||
modeledMethod,
|
modeledMethods: [modeledMethod],
|
||||||
modelingInProgress: true,
|
modelingInProgress: true,
|
||||||
methodCanBeModeled: true,
|
methodCanBeModeled: true,
|
||||||
|
viewState,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,6 +23,12 @@ import { ModelInputDropdown } from "./ModelInputDropdown";
|
|||||||
import { ModelOutputDropdown } from "./ModelOutputDropdown";
|
import { ModelOutputDropdown } from "./ModelOutputDropdown";
|
||||||
import { ModelEditorViewState } from "../../model-editor/shared/view-state";
|
import { ModelEditorViewState } from "../../model-editor/shared/view-state";
|
||||||
|
|
||||||
|
const MultiModelColumn = styled(VSCodeDataGridCell)`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5em;
|
||||||
|
`;
|
||||||
|
|
||||||
const ApiOrMethodRow = styled.div`
|
const ApiOrMethodRow = styled.div`
|
||||||
min-height: calc(var(--input-height) * 1px);
|
min-height: calc(var(--input-height) * 1px);
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -57,7 +63,7 @@ const DataGridRow = styled(VSCodeDataGridRow)<{ focused?: boolean }>`
|
|||||||
export type MethodRowProps = {
|
export type MethodRowProps = {
|
||||||
method: Method;
|
method: Method;
|
||||||
methodCanBeModeled: boolean;
|
methodCanBeModeled: boolean;
|
||||||
modeledMethod: ModeledMethod | undefined;
|
modeledMethods: ModeledMethod[];
|
||||||
methodIsUnsaved: boolean;
|
methodIsUnsaved: boolean;
|
||||||
modelingInProgress: boolean;
|
modelingInProgress: boolean;
|
||||||
viewState: ModelEditorViewState;
|
viewState: ModelEditorViewState;
|
||||||
@@ -90,22 +96,23 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
|
|||||||
(props, ref) => {
|
(props, ref) => {
|
||||||
const {
|
const {
|
||||||
method,
|
method,
|
||||||
modeledMethod,
|
modeledMethods: modeledMethodsArg,
|
||||||
methodIsUnsaved,
|
methodIsUnsaved,
|
||||||
viewState,
|
viewState,
|
||||||
revealedMethodSignature,
|
revealedMethodSignature,
|
||||||
onChange,
|
onChange,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
|
const modeledMethods = viewState.showMultipleModels
|
||||||
|
? modeledMethodsArg
|
||||||
|
: modeledMethodsArg.slice(0, 1);
|
||||||
|
|
||||||
const jumpToUsage = useCallback(
|
const jumpToUsage = useCallback(
|
||||||
() => sendJumpToUsageMessage(method),
|
() => sendJumpToUsageMessage(method),
|
||||||
[method],
|
[method],
|
||||||
);
|
);
|
||||||
|
|
||||||
const modelingStatus = getModelingStatus(
|
const modelingStatus = getModelingStatus(modeledMethods, methodIsUnsaved);
|
||||||
modeledMethod ? [modeledMethod] : [],
|
|
||||||
methodIsUnsaved,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataGridRow
|
<DataGridRow
|
||||||
@@ -145,34 +152,46 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
|
|||||||
)}
|
)}
|
||||||
{!props.modelingInProgress && (
|
{!props.modelingInProgress && (
|
||||||
<>
|
<>
|
||||||
<VSCodeDataGridCell gridColumn={2}>
|
<MultiModelColumn gridColumn={2}>
|
||||||
<ModelTypeDropdown
|
{forEachModeledMethod(modeledMethods, (modeledMethod) => (
|
||||||
method={method}
|
<ModelTypeDropdown
|
||||||
modeledMethod={modeledMethod}
|
key={JSON.stringify(modeledMethod)}
|
||||||
onChange={onChange}
|
method={method}
|
||||||
/>
|
modeledMethod={modeledMethod}
|
||||||
</VSCodeDataGridCell>
|
onChange={onChange}
|
||||||
<VSCodeDataGridCell gridColumn={3}>
|
/>
|
||||||
<ModelInputDropdown
|
))}
|
||||||
method={method}
|
</MultiModelColumn>
|
||||||
modeledMethod={modeledMethod}
|
<MultiModelColumn gridColumn={3}>
|
||||||
onChange={onChange}
|
{forEachModeledMethod(modeledMethods, (modeledMethod) => (
|
||||||
/>
|
<ModelInputDropdown
|
||||||
</VSCodeDataGridCell>
|
key={JSON.stringify(modeledMethod)}
|
||||||
<VSCodeDataGridCell gridColumn={4}>
|
method={method}
|
||||||
<ModelOutputDropdown
|
modeledMethod={modeledMethod}
|
||||||
method={method}
|
onChange={onChange}
|
||||||
modeledMethod={modeledMethod}
|
/>
|
||||||
onChange={onChange}
|
))}
|
||||||
/>
|
</MultiModelColumn>
|
||||||
</VSCodeDataGridCell>
|
<MultiModelColumn gridColumn={4}>
|
||||||
<VSCodeDataGridCell gridColumn={5}>
|
{forEachModeledMethod(modeledMethods, (modeledMethod) => (
|
||||||
<ModelKindDropdown
|
<ModelOutputDropdown
|
||||||
method={method}
|
key={JSON.stringify(modeledMethod)}
|
||||||
modeledMethod={modeledMethod}
|
method={method}
|
||||||
onChange={onChange}
|
modeledMethod={modeledMethod}
|
||||||
/>
|
onChange={onChange}
|
||||||
</VSCodeDataGridCell>
|
/>
|
||||||
|
))}
|
||||||
|
</MultiModelColumn>
|
||||||
|
<MultiModelColumn gridColumn={5}>
|
||||||
|
{forEachModeledMethod(modeledMethods, (modeledMethod) => (
|
||||||
|
<ModelKindDropdown
|
||||||
|
key={JSON.stringify(modeledMethod)}
|
||||||
|
method={method}
|
||||||
|
modeledMethod={modeledMethod}
|
||||||
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</MultiModelColumn>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</DataGridRow>
|
</DataGridRow>
|
||||||
@@ -227,3 +246,14 @@ function sendJumpToUsageMessage(method: Method) {
|
|||||||
usage: method.usages[0],
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -84,22 +84,25 @@ export const ModeledMethodDataGrid = ({
|
|||||||
Kind
|
Kind
|
||||||
</VSCodeDataGridCell>
|
</VSCodeDataGridCell>
|
||||||
</VSCodeDataGridRow>
|
</VSCodeDataGridRow>
|
||||||
{methodsWithModelability.map(({ method, methodCanBeModeled }) => (
|
{methodsWithModelability.map(({ method, methodCanBeModeled }) => {
|
||||||
<MethodRow
|
const modeledMethod = modeledMethods[method.signature];
|
||||||
key={method.signature}
|
return (
|
||||||
method={method}
|
<MethodRow
|
||||||
methodCanBeModeled={methodCanBeModeled}
|
key={method.signature}
|
||||||
modeledMethod={modeledMethods[method.signature]}
|
method={method}
|
||||||
methodIsUnsaved={modifiedSignatures.has(method.signature)}
|
methodCanBeModeled={methodCanBeModeled}
|
||||||
modelingInProgress={inProgressMethods.hasMethod(
|
modeledMethods={modeledMethod ? [modeledMethod] : []}
|
||||||
packageName,
|
methodIsUnsaved={modifiedSignatures.has(method.signature)}
|
||||||
method.signature,
|
modelingInProgress={inProgressMethods.hasMethod(
|
||||||
)}
|
packageName,
|
||||||
viewState={viewState}
|
method.signature,
|
||||||
revealedMethodSignature={revealedMethodSignature}
|
)}
|
||||||
onChange={onChange}
|
viewState={viewState}
|
||||||
/>
|
revealedMethodSignature={revealedMethodSignature}
|
||||||
))}
|
onChange={onChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<HiddenMethodsRow
|
<HiddenMethodsRow
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ describe(MethodRow.name, () => {
|
|||||||
<MethodRow
|
<MethodRow
|
||||||
method={method}
|
method={method}
|
||||||
methodCanBeModeled={true}
|
methodCanBeModeled={true}
|
||||||
modeledMethod={modeledMethod}
|
modeledMethods={[modeledMethod]}
|
||||||
methodIsUnsaved={false}
|
methodIsUnsaved={false}
|
||||||
modelingInProgress={false}
|
modelingInProgress={false}
|
||||||
revealedMethodSignature={null}
|
revealedMethodSignature={null}
|
||||||
@@ -120,7 +120,7 @@ describe(MethodRow.name, () => {
|
|||||||
|
|
||||||
it("shows the modeling status indicator when unmodeled", () => {
|
it("shows the modeling status indicator when unmodeled", () => {
|
||||||
render({
|
render({
|
||||||
modeledMethod: undefined,
|
modeledMethods: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(screen.getByLabelText("Method not modeled")).toBeInTheDocument();
|
expect(screen.getByLabelText("Method not modeled")).toBeInTheDocument();
|
||||||
@@ -137,7 +137,7 @@ describe(MethodRow.name, () => {
|
|||||||
it("renders an unmodelable method", () => {
|
it("renders an unmodelable method", () => {
|
||||||
render({
|
render({
|
||||||
methodCanBeModeled: false,
|
methodCanBeModeled: false,
|
||||||
modeledMethod: undefined,
|
modeledMethods: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(screen.queryByRole("combobox")).not.toBeInTheDocument();
|
expect(screen.queryByRole("combobox")).not.toBeInTheDocument();
|
||||||
|
|||||||
Reference in New Issue
Block a user