Skip to content

Commit 5c368ce

Browse files
authored
Merge pull request #2921 from github/koesie10/modeling-panel-multiple-models
Add ability for method modeling panel to render multiple modelings of the same method
2 parents 603c799 + 323c536 commit 5c368ce

File tree

11 files changed

+574
-14
lines changed

11 files changed

+574
-14
lines changed

extensions/ql-vscode/src/stories/method-modeling/MethodModeling.stories.tsx

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Meta, StoryFn } from "@storybook/react";
44

55
import { MethodModeling as MethodModelingComponent } from "../../view/method-modeling/MethodModeling";
66
import { createMethod } from "../../../test/factories/model-editor/method-factories";
7+
import { createModeledMethod } from "../../../test/factories/model-editor/modeled-method-factories";
78
export default {
89
title: "Method Modeling/Method Modeling",
910
component: MethodModelingComponent,
@@ -18,18 +19,53 @@ const method = createMethod();
1819
export const MethodUnmodeled = Template.bind({});
1920
MethodUnmodeled.args = {
2021
method,
22+
modeledMethods: [],
2123
modelingStatus: "unmodeled",
2224
};
2325

2426
export const MethodModeled = Template.bind({});
2527
MethodModeled.args = {
2628
method,
27-
29+
modeledMethods: [],
2830
modelingStatus: "unsaved",
2931
};
3032

3133
export const MethodSaved = Template.bind({});
3234
MethodSaved.args = {
3335
method,
36+
modeledMethods: [],
37+
modelingStatus: "saved",
38+
};
39+
40+
export const MultipleModelingsUnmodeled = Template.bind({});
41+
MultipleModelingsUnmodeled.args = {
42+
method,
43+
modeledMethods: [],
44+
showMultipleModels: true,
45+
modelingStatus: "saved",
46+
};
47+
48+
export const MultipleModelingsModeledSingle = Template.bind({});
49+
MultipleModelingsModeledSingle.args = {
50+
method,
51+
modeledMethods: [createModeledMethod(method)],
52+
showMultipleModels: true,
53+
modelingStatus: "saved",
54+
};
55+
56+
export const MultipleModelingsModeledMultiple = Template.bind({});
57+
MultipleModelingsModeledMultiple.args = {
58+
method,
59+
modeledMethods: [
60+
createModeledMethod(method),
61+
createModeledMethod({
62+
...method,
63+
type: "source",
64+
input: "",
65+
output: "ReturnValue",
66+
kind: "remote",
67+
}),
68+
],
69+
showMultipleModels: true,
3470
modelingStatus: "saved",
3571
};

extensions/ql-vscode/src/view/common/icon/Codicon.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import classNames from "classnames";
44

55
type Props = {
66
name: string;
7-
label: string;
7+
label?: string;
88
className?: string;
99
slot?: string;
1010
};

extensions/ql-vscode/src/view/method-modeling/MethodModeling.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { ModelingStatusIndicator } from "../model-editor/ModelingStatusIndicator
55
import { Method } from "../../model-editor/method";
66
import { MethodName } from "../model-editor/MethodName";
77
import { ModeledMethod } from "../../model-editor/modeled-method";
8-
import { MethodModelingInputs } from "./MethodModelingInputs";
98
import { VSCodeTag } from "@vscode/webview-ui-toolkit/react";
109
import { ReviewInEditorButton } from "./ReviewInEditorButton";
10+
import { ModeledMethodsPanel } from "./ModeledMethodsPanel";
1111

1212
const Container = styled.div`
1313
padding-top: 0.5rem;
@@ -38,10 +38,6 @@ const DependencyContainer = styled.div`
3838
margin-bottom: 0.8rem;
3939
`;
4040

41-
const StyledMethodModelingInputs = styled(MethodModelingInputs)`
42-
padding-bottom: 0.5rem;
43-
`;
44-
4541
const StyledVSCodeTag = styled(VSCodeTag)<{ visible: boolean }>`
4642
visibility: ${(props) => (props.visible ? "visible" : "hidden")};
4743
`;
@@ -55,15 +51,16 @@ const UnsavedTag = ({ modelingStatus }: { modelingStatus: ModelingStatus }) => (
5551
export type MethodModelingProps = {
5652
modelingStatus: ModelingStatus;
5753
method: Method;
58-
modeledMethod: ModeledMethod | undefined;
54+
modeledMethods: ModeledMethod[];
5955
showMultipleModels?: boolean;
6056
onChange: (modeledMethod: ModeledMethod) => void;
6157
};
6258

6359
export const MethodModeling = ({
6460
modelingStatus,
65-
modeledMethod,
61+
modeledMethods,
6662
method,
63+
showMultipleModels = false,
6764
onChange,
6865
}: MethodModelingProps): JSX.Element => {
6966
return (
@@ -77,9 +74,10 @@ export const MethodModeling = ({
7774
<ModelingStatusIndicator status={modelingStatus} />
7875
<MethodName {...method} />
7976
</DependencyContainer>
80-
<StyledMethodModelingInputs
77+
<ModeledMethodsPanel
8178
method={method}
82-
modeledMethod={modeledMethod}
79+
modeledMethods={modeledMethods}
80+
showMultipleModels={showMultipleModels}
8381
onChange={onChange}
8482
/>
8583
<ReviewInEditorButton method={method} />

extensions/ql-vscode/src/view/method-modeling/MethodModelingView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export function MethodModelingView({ initialViewState }: Props): JSX.Element {
9494
<MethodModeling
9595
modelingStatus={modelingStatus}
9696
method={method}
97-
modeledMethod={modeledMethod}
97+
modeledMethods={modeledMethod ? [modeledMethod] : []}
9898
showMultipleModels={viewState?.showMultipleModels}
9999
onChange={onChange}
100100
/>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import * as React from "react";
2+
import { ModeledMethod } from "../../model-editor/modeled-method";
3+
import { MethodModelingInputs } from "./MethodModelingInputs";
4+
import { Method } from "../../model-editor/method";
5+
import { styled } from "styled-components";
6+
import { MultipleModeledMethodsPanel } from "./MultipleModeledMethodsPanel";
7+
8+
export type ModeledMethodsPanelProps = {
9+
method: Method;
10+
modeledMethods: ModeledMethod[];
11+
showMultipleModels: boolean;
12+
onChange: (modeledMethod: ModeledMethod) => void;
13+
};
14+
15+
const SingleMethodModelingInputs = styled(MethodModelingInputs)`
16+
padding-bottom: 0.5rem;
17+
`;
18+
19+
export const ModeledMethodsPanel = ({
20+
method,
21+
modeledMethods,
22+
showMultipleModels,
23+
onChange,
24+
}: ModeledMethodsPanelProps) => {
25+
if (!showMultipleModels) {
26+
return (
27+
<SingleMethodModelingInputs
28+
method={method}
29+
modeledMethod={
30+
modeledMethods.length > 0 ? modeledMethods[0] : undefined
31+
}
32+
onChange={onChange}
33+
/>
34+
);
35+
}
36+
37+
return (
38+
<MultipleModeledMethodsPanel
39+
method={method}
40+
modeledMethods={modeledMethods}
41+
onChange={onChange}
42+
/>
43+
);
44+
};
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import * as React from "react";
2+
import { useCallback, useState } from "react";
3+
import { Method } from "../../model-editor/method";
4+
import { ModeledMethod } from "../../model-editor/modeled-method";
5+
import { styled } from "styled-components";
6+
import { MethodModelingInputs } from "./MethodModelingInputs";
7+
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react";
8+
import { Codicon } from "../common";
9+
10+
export type MultipleModeledMethodsPanelProps = {
11+
method: Method;
12+
modeledMethods: ModeledMethod[];
13+
onChange: (modeledMethod: ModeledMethod) => void;
14+
};
15+
16+
const Container = styled.div`
17+
display: flex;
18+
flex-direction: column;
19+
gap: 0.25rem;
20+
21+
padding-bottom: 0.5rem;
22+
border-bottom: 0.05rem solid var(--vscode-panelSection-border);
23+
`;
24+
25+
const Footer = styled.div`
26+
display: flex;
27+
flex-direction: row;
28+
`;
29+
30+
const PaginationActions = styled.div`
31+
display: flex;
32+
flex-direction: row;
33+
gap: 0.5rem;
34+
`;
35+
36+
export const MultipleModeledMethodsPanel = ({
37+
method,
38+
modeledMethods,
39+
onChange,
40+
}: MultipleModeledMethodsPanelProps) => {
41+
const [selectedIndex, setSelectedIndex] = useState<number>(0);
42+
43+
const handlePreviousClick = useCallback(() => {
44+
setSelectedIndex((previousIndex) => previousIndex - 1);
45+
}, []);
46+
const handleNextClick = useCallback(() => {
47+
setSelectedIndex((previousIndex) => previousIndex + 1);
48+
}, []);
49+
50+
return (
51+
<Container>
52+
{modeledMethods.length > 0 ? (
53+
<MethodModelingInputs
54+
method={method}
55+
modeledMethod={modeledMethods[selectedIndex]}
56+
onChange={onChange}
57+
/>
58+
) : (
59+
<MethodModelingInputs
60+
method={method}
61+
modeledMethod={undefined}
62+
onChange={onChange}
63+
/>
64+
)}
65+
<Footer>
66+
<PaginationActions>
67+
<VSCodeButton
68+
appearance="icon"
69+
aria-label="Previous modeling"
70+
onClick={handlePreviousClick}
71+
disabled={modeledMethods.length < 2 || selectedIndex === 0}
72+
>
73+
<Codicon name="chevron-left" />
74+
</VSCodeButton>
75+
{modeledMethods.length > 1 && (
76+
<div>
77+
{selectedIndex + 1}/{modeledMethods.length}
78+
</div>
79+
)}
80+
<VSCodeButton
81+
appearance="icon"
82+
aria-label="Next modeling"
83+
onClick={handleNextClick}
84+
disabled={
85+
modeledMethods.length < 2 ||
86+
selectedIndex === modeledMethods.length - 1
87+
}
88+
>
89+
<Codicon name="chevron-right" />
90+
</VSCodeButton>
91+
</PaginationActions>
92+
</Footer>
93+
</Container>
94+
);
95+
};

extensions/ql-vscode/src/view/method-modeling/__tests__/MethodModeling.spec.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ describe(MethodModeling.name, () => {
1616
render({
1717
modelingStatus: "saved",
1818
method,
19-
modeledMethod,
19+
modeledMethods: [modeledMethod],
2020
onChange,
2121
});
2222

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import * as React from "react";
2+
import { render as reactRender, screen } from "@testing-library/react";
3+
import { createMethod } from "../../../../test/factories/model-editor/method-factories";
4+
import { createModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories";
5+
import {
6+
ModeledMethodsPanel,
7+
ModeledMethodsPanelProps,
8+
} from "../ModeledMethodsPanel";
9+
10+
describe(ModeledMethodsPanel.name, () => {
11+
const render = (props: ModeledMethodsPanelProps) =>
12+
reactRender(<ModeledMethodsPanel {...props} />);
13+
14+
const method = createMethod();
15+
const modeledMethods = [createModeledMethod(), createModeledMethod()];
16+
const onChange = jest.fn();
17+
18+
describe("when show multiple models is disabled", () => {
19+
const showMultipleModels = false;
20+
21+
it("renders the method modeling inputs", () => {
22+
render({
23+
method,
24+
modeledMethods,
25+
onChange,
26+
showMultipleModels,
27+
});
28+
29+
expect(screen.getAllByRole("combobox")).toHaveLength(4);
30+
});
31+
32+
it("does not render the pagination", () => {
33+
render({
34+
method,
35+
modeledMethods,
36+
onChange,
37+
showMultipleModels,
38+
});
39+
40+
expect(
41+
screen.queryByLabelText("Previous modeling"),
42+
).not.toBeInTheDocument();
43+
expect(screen.queryByLabelText("Next modeling")).not.toBeInTheDocument();
44+
});
45+
});
46+
47+
describe("when show multiple models is enabled", () => {
48+
const showMultipleModels = true;
49+
50+
it("renders the method modeling inputs once", () => {
51+
render({
52+
method,
53+
modeledMethods,
54+
onChange,
55+
showMultipleModels,
56+
});
57+
58+
expect(screen.getAllByRole("combobox")).toHaveLength(4);
59+
});
60+
61+
it("renders the pagination", () => {
62+
render({
63+
method,
64+
modeledMethods,
65+
onChange,
66+
showMultipleModels,
67+
});
68+
69+
expect(screen.getByLabelText("Previous modeling")).toBeInTheDocument();
70+
expect(screen.getByLabelText("Next modeling")).toBeInTheDocument();
71+
expect(screen.getByText("1/2")).toBeInTheDocument();
72+
});
73+
});
74+
});

0 commit comments

Comments
 (0)