Skip to content

Commit 3e259f1

Browse files
authored
Add modeling inputs to method modeling panel (#2849)
1 parent 4323aad commit 3e259f1

File tree

6 files changed

+257
-1
lines changed

6 files changed

+257
-1
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import * as React from "react";
2+
3+
import { Meta, StoryFn } from "@storybook/react";
4+
5+
import { MethodModelingInputs as MethodModelingInputsComponent } from "../../view/method-modeling/MethodModelingInputs";
6+
import { createMethod } from "../../../test/factories/model-editor/method-factories";
7+
import { createModeledMethod } from "../../../test/factories/model-editor/modeled-method-factories";
8+
import { useState } from "react";
9+
import { ModeledMethod } from "../../model-editor/modeled-method";
10+
import { Method } from "../../model-editor/method";
11+
12+
export default {
13+
title: "Method Modeling/Method Modeling Inputs",
14+
component: MethodModelingInputsComponent,
15+
argTypes: {
16+
modeledMethod: {
17+
control: {
18+
disable: true,
19+
},
20+
},
21+
},
22+
} as Meta<typeof MethodModelingInputsComponent>;
23+
24+
const Template: StoryFn<typeof MethodModelingInputsComponent> = (args) => {
25+
const [m, setModeledMethod] = useState<ModeledMethod | undefined>(
26+
args.modeledMethod,
27+
);
28+
29+
const onChange = (method: Method, modeledMethod: ModeledMethod) => {
30+
setModeledMethod(modeledMethod);
31+
};
32+
33+
return (
34+
<MethodModelingInputsComponent
35+
{...args}
36+
modeledMethod={m}
37+
onChange={onChange}
38+
/>
39+
);
40+
};
41+
42+
const method = createMethod();
43+
const modeledMethod = createModeledMethod();
44+
45+
export const UnmodeledMethod = Template.bind({});
46+
UnmodeledMethod.args = {
47+
method,
48+
};
49+
50+
export const FullyModeledMethod = Template.bind({});
51+
FullyModeledMethod.args = {
52+
method,
53+
modeledMethod,
54+
};

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
} from "../model-editor/ModelingStatusIndicator";
77
import { Method } from "../../model-editor/method";
88
import { MethodName } from "../model-editor/MethodName";
9+
import { ModeledMethod } from "../../model-editor/modeled-method";
10+
import { MethodModelingInputs } from "./MethodModelingInputs";
911

1012
const Container = styled.div`
1113
padding: 0.3rem;
@@ -33,11 +35,15 @@ const DependencyContainer = styled.div`
3335
export type MethodModelingProps = {
3436
modelingStatus: ModelingStatus;
3537
method: Method;
38+
modeledMethod: ModeledMethod | undefined;
39+
onChange: (method: Method, modeledMethod: ModeledMethod) => void;
3640
};
3741

3842
export const MethodModeling = ({
3943
modelingStatus,
44+
modeledMethod,
4045
method,
46+
onChange,
4147
}: MethodModelingProps): JSX.Element => {
4248
return (
4349
<Container>
@@ -49,6 +55,11 @@ export const MethodModeling = ({
4955
<ModelingStatusIndicator status={modelingStatus} />
5056
<MethodName {...method} />
5157
</DependencyContainer>
58+
<MethodModelingInputs
59+
method={method}
60+
modeledMethod={modeledMethod}
61+
onChange={onChange}
62+
/>
5263
</Container>
5364
);
5465
};
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import * as React from "react";
2+
import { styled } from "styled-components";
3+
import { Method } from "../../model-editor/method";
4+
import { ModeledMethod } from "../../model-editor/modeled-method";
5+
import { ModelTypeDropdown } from "../model-editor/ModelTypeDropdown";
6+
import { ModelInputDropdown } from "../model-editor/ModelInputDropdown";
7+
import { ModelOutputDropdown } from "../model-editor/ModelOutputDropdown";
8+
import { ModelKindDropdown } from "../model-editor/ModelKindDropdown";
9+
10+
const Container = styled.div`
11+
padding-top: 0.5rem;
12+
`;
13+
14+
const Input = styled.label``;
15+
16+
const Name = styled.span`
17+
display: block;
18+
padding-bottom: 0.3rem;
19+
`;
20+
21+
export type MethodModelingInputsProps = {
22+
method: Method;
23+
modeledMethod: ModeledMethod | undefined;
24+
onChange: (method: Method, modeledMethod: ModeledMethod) => void;
25+
};
26+
27+
export const MethodModelingInputs = ({
28+
method,
29+
modeledMethod,
30+
onChange,
31+
}: MethodModelingInputsProps): JSX.Element => {
32+
const inputProps = {
33+
method,
34+
modeledMethod,
35+
onChange,
36+
};
37+
38+
return (
39+
<>
40+
<Container>
41+
<Input>
42+
<Name>Model Type</Name>
43+
<ModelTypeDropdown {...inputProps} />
44+
</Input>
45+
</Container>
46+
<Container>
47+
<Input>
48+
<Name>Input</Name>
49+
<ModelInputDropdown {...inputProps} />
50+
</Input>
51+
</Container>
52+
<Container>
53+
<Input>
54+
<Name>Output</Name>
55+
<ModelOutputDropdown {...inputProps} />
56+
</Input>
57+
</Container>
58+
<Container>
59+
<Input>
60+
<Name>Kind</Name>
61+
<ModelKindDropdown {...inputProps} />
62+
</Input>
63+
</Container>
64+
</>
65+
);
66+
};

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ import { ModelingStatus } from "../model-editor/ModelingStatusIndicator";
55
import { Method } from "../../model-editor/method";
66
import { ToMethodModelingMessage } from "../../common/interface-types";
77
import { assertNever } from "../../common/helpers-pure";
8+
import { ModeledMethod } from "../../model-editor/modeled-method";
89

910
export function MethodModelingView(): JSX.Element {
1011
const [method, setMethod] = useState<Method | undefined>(undefined);
1112

13+
const [modeledMethod, setModeledMethod] = React.useState<
14+
ModeledMethod | undefined
15+
>(undefined);
16+
1217
useEffect(() => {
1318
const listener = (evt: MessageEvent) => {
1419
if (evt.origin === window.origin) {
@@ -36,5 +41,19 @@ export function MethodModelingView(): JSX.Element {
3641
}
3742

3843
const modelingStatus: ModelingStatus = "saved";
39-
return <MethodModeling modelingStatus={modelingStatus} method={method} />;
44+
45+
// For now we just store the updated method in the state but soon
46+
// we'll need to send it back to the other views.
47+
const onChange = (method: Method, modeledMethod: ModeledMethod) => {
48+
setModeledMethod(modeledMethod);
49+
};
50+
51+
return (
52+
<MethodModeling
53+
modelingStatus={modelingStatus}
54+
method={method}
55+
modeledMethod={modeledMethod}
56+
onChange={onChange}
57+
/>
58+
);
4059
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,22 @@ import * as React from "react";
22
import { render as reactRender, screen } from "@testing-library/react";
33
import { MethodModeling, MethodModelingProps } from "../MethodModeling";
44
import { createMethod } from "../../../../test/factories/model-editor/method-factories";
5+
import { createModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories";
56

67
describe(MethodModeling.name, () => {
78
const render = (props: MethodModelingProps) =>
89
reactRender(<MethodModeling {...props} />);
910

1011
it("renders method modeling panel", () => {
1112
const method = createMethod();
13+
const modeledMethod = createModeledMethod();
14+
const onChange = jest.fn();
1215

1316
render({
1417
modelingStatus: "saved",
1518
method,
19+
modeledMethod,
20+
onChange,
1621
});
1722

1823
expect(
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import * as React from "react";
2+
import { render as reactRender, screen } from "@testing-library/react";
3+
import userEvent from "@testing-library/user-event";
4+
import {
5+
MethodModelingInputs,
6+
MethodModelingInputsProps,
7+
} from "../MethodModelingInputs";
8+
import { createMethod } from "../../../../test/factories/model-editor/method-factories";
9+
import { createModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories";
10+
11+
describe(MethodModelingInputs.name, () => {
12+
const render = (props: MethodModelingInputsProps) =>
13+
reactRender(<MethodModelingInputs {...props} />);
14+
15+
const method = createMethod();
16+
const modeledMethod = createModeledMethod();
17+
const onChange = jest.fn();
18+
19+
it("renders the method modeling inputs", () => {
20+
render({
21+
method,
22+
modeledMethod,
23+
onChange,
24+
});
25+
26+
// Check that all the labels are rendered.
27+
expect(screen.getByText("Model Type")).toBeInTheDocument();
28+
expect(screen.getByText("Input")).toBeInTheDocument();
29+
expect(screen.getByText("Output")).toBeInTheDocument();
30+
expect(screen.getByText("Kind")).toBeInTheDocument();
31+
32+
// Check that all the dropdowns are rendered.
33+
const comboboxes = screen.getAllByRole("combobox");
34+
expect(comboboxes.length).toBe(4);
35+
const modelTypeDropdown = screen.getByRole("combobox", {
36+
name: "Model type",
37+
});
38+
expect(modelTypeDropdown).toHaveValue("sink");
39+
const modelTypeOptions = modelTypeDropdown.querySelectorAll("option");
40+
expect(modelTypeOptions.length).toBe(5);
41+
});
42+
43+
it("allows changing the type", async () => {
44+
render({
45+
method,
46+
modeledMethod,
47+
onChange,
48+
});
49+
50+
const modelTypeDropdown = screen.getByRole("combobox", {
51+
name: "Model type",
52+
});
53+
54+
await userEvent.selectOptions(modelTypeDropdown, "source");
55+
56+
expect(onChange).toHaveBeenCalledWith(
57+
method,
58+
expect.objectContaining({
59+
type: "source",
60+
}),
61+
);
62+
});
63+
64+
it("sets other dropdowns when model type is changed", () => {
65+
const { rerender } = render({
66+
method,
67+
modeledMethod,
68+
onChange,
69+
});
70+
71+
const updatedModeledMethod = createModeledMethod({
72+
type: "source",
73+
});
74+
75+
rerender(
76+
<MethodModelingInputs
77+
method={method}
78+
modeledMethod={updatedModeledMethod}
79+
onChange={onChange}
80+
/>,
81+
);
82+
83+
const modelTypeDropdown = screen.getByRole("combobox", {
84+
name: "Model type",
85+
});
86+
const modelInputDropdown = screen.getByRole("combobox", {
87+
name: "Input",
88+
});
89+
const modelOutputDropdown = screen.getByRole("combobox", {
90+
name: "Output",
91+
});
92+
const modelKindDropdown = screen.getByRole("combobox", {
93+
name: "Kind",
94+
});
95+
96+
expect(modelTypeDropdown).toHaveValue("source");
97+
expect(modelInputDropdown).toHaveValue("-");
98+
expect(modelOutputDropdown).toHaveValue("ReturnValue");
99+
expect(modelKindDropdown).toHaveValue("local");
100+
});
101+
});

0 commit comments

Comments
 (0)