Add modeling inputs to method modeling panel (#2849)

This commit is contained in:
Charis Kyriakou
2023-09-22 16:13:40 +01:00
committed by GitHub
parent 4323aad254
commit 3e259f14c9
6 changed files with 257 additions and 1 deletions

View File

@@ -0,0 +1,54 @@
import * as React from "react";
import { Meta, StoryFn } from "@storybook/react";
import { MethodModelingInputs as MethodModelingInputsComponent } from "../../view/method-modeling/MethodModelingInputs";
import { createMethod } from "../../../test/factories/model-editor/method-factories";
import { createModeledMethod } from "../../../test/factories/model-editor/modeled-method-factories";
import { useState } from "react";
import { ModeledMethod } from "../../model-editor/modeled-method";
import { Method } from "../../model-editor/method";
export default {
title: "Method Modeling/Method Modeling Inputs",
component: MethodModelingInputsComponent,
argTypes: {
modeledMethod: {
control: {
disable: true,
},
},
},
} as Meta<typeof MethodModelingInputsComponent>;
const Template: StoryFn<typeof MethodModelingInputsComponent> = (args) => {
const [m, setModeledMethod] = useState<ModeledMethod | undefined>(
args.modeledMethod,
);
const onChange = (method: Method, modeledMethod: ModeledMethod) => {
setModeledMethod(modeledMethod);
};
return (
<MethodModelingInputsComponent
{...args}
modeledMethod={m}
onChange={onChange}
/>
);
};
const method = createMethod();
const modeledMethod = createModeledMethod();
export const UnmodeledMethod = Template.bind({});
UnmodeledMethod.args = {
method,
};
export const FullyModeledMethod = Template.bind({});
FullyModeledMethod.args = {
method,
modeledMethod,
};

View File

@@ -6,6 +6,8 @@ import {
} from "../model-editor/ModelingStatusIndicator";
import { Method } from "../../model-editor/method";
import { MethodName } from "../model-editor/MethodName";
import { ModeledMethod } from "../../model-editor/modeled-method";
import { MethodModelingInputs } from "./MethodModelingInputs";
const Container = styled.div`
padding: 0.3rem;
@@ -33,11 +35,15 @@ const DependencyContainer = styled.div`
export type MethodModelingProps = {
modelingStatus: ModelingStatus;
method: Method;
modeledMethod: ModeledMethod | undefined;
onChange: (method: Method, modeledMethod: ModeledMethod) => void;
};
export const MethodModeling = ({
modelingStatus,
modeledMethod,
method,
onChange,
}: MethodModelingProps): JSX.Element => {
return (
<Container>
@@ -49,6 +55,11 @@ export const MethodModeling = ({
<ModelingStatusIndicator status={modelingStatus} />
<MethodName {...method} />
</DependencyContainer>
<MethodModelingInputs
method={method}
modeledMethod={modeledMethod}
onChange={onChange}
/>
</Container>
);
};

View File

@@ -0,0 +1,66 @@
import * as React from "react";
import { styled } from "styled-components";
import { Method } from "../../model-editor/method";
import { ModeledMethod } from "../../model-editor/modeled-method";
import { ModelTypeDropdown } from "../model-editor/ModelTypeDropdown";
import { ModelInputDropdown } from "../model-editor/ModelInputDropdown";
import { ModelOutputDropdown } from "../model-editor/ModelOutputDropdown";
import { ModelKindDropdown } from "../model-editor/ModelKindDropdown";
const Container = styled.div`
padding-top: 0.5rem;
`;
const Input = styled.label``;
const Name = styled.span`
display: block;
padding-bottom: 0.3rem;
`;
export type MethodModelingInputsProps = {
method: Method;
modeledMethod: ModeledMethod | undefined;
onChange: (method: Method, modeledMethod: ModeledMethod) => void;
};
export const MethodModelingInputs = ({
method,
modeledMethod,
onChange,
}: MethodModelingInputsProps): JSX.Element => {
const inputProps = {
method,
modeledMethod,
onChange,
};
return (
<>
<Container>
<Input>
<Name>Model Type</Name>
<ModelTypeDropdown {...inputProps} />
</Input>
</Container>
<Container>
<Input>
<Name>Input</Name>
<ModelInputDropdown {...inputProps} />
</Input>
</Container>
<Container>
<Input>
<Name>Output</Name>
<ModelOutputDropdown {...inputProps} />
</Input>
</Container>
<Container>
<Input>
<Name>Kind</Name>
<ModelKindDropdown {...inputProps} />
</Input>
</Container>
</>
);
};

View File

@@ -5,10 +5,15 @@ import { ModelingStatus } from "../model-editor/ModelingStatusIndicator";
import { Method } from "../../model-editor/method";
import { ToMethodModelingMessage } from "../../common/interface-types";
import { assertNever } from "../../common/helpers-pure";
import { ModeledMethod } from "../../model-editor/modeled-method";
export function MethodModelingView(): JSX.Element {
const [method, setMethod] = useState<Method | undefined>(undefined);
const [modeledMethod, setModeledMethod] = React.useState<
ModeledMethod | undefined
>(undefined);
useEffect(() => {
const listener = (evt: MessageEvent) => {
if (evt.origin === window.origin) {
@@ -36,5 +41,19 @@ export function MethodModelingView(): JSX.Element {
}
const modelingStatus: ModelingStatus = "saved";
return <MethodModeling modelingStatus={modelingStatus} method={method} />;
// For now we just store the updated method in the state but soon
// we'll need to send it back to the other views.
const onChange = (method: Method, modeledMethod: ModeledMethod) => {
setModeledMethod(modeledMethod);
};
return (
<MethodModeling
modelingStatus={modelingStatus}
method={method}
modeledMethod={modeledMethod}
onChange={onChange}
/>
);
}

View File

@@ -2,6 +2,7 @@ import * as React from "react";
import { render as reactRender, screen } from "@testing-library/react";
import { MethodModeling, MethodModelingProps } from "../MethodModeling";
import { createMethod } from "../../../../test/factories/model-editor/method-factories";
import { createModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories";
describe(MethodModeling.name, () => {
const render = (props: MethodModelingProps) =>
@@ -9,10 +10,14 @@ describe(MethodModeling.name, () => {
it("renders method modeling panel", () => {
const method = createMethod();
const modeledMethod = createModeledMethod();
const onChange = jest.fn();
render({
modelingStatus: "saved",
method,
modeledMethod,
onChange,
});
expect(

View File

@@ -0,0 +1,101 @@
import * as React from "react";
import { render as reactRender, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import {
MethodModelingInputs,
MethodModelingInputsProps,
} from "../MethodModelingInputs";
import { createMethod } from "../../../../test/factories/model-editor/method-factories";
import { createModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories";
describe(MethodModelingInputs.name, () => {
const render = (props: MethodModelingInputsProps) =>
reactRender(<MethodModelingInputs {...props} />);
const method = createMethod();
const modeledMethod = createModeledMethod();
const onChange = jest.fn();
it("renders the method modeling inputs", () => {
render({
method,
modeledMethod,
onChange,
});
// Check that all the labels are rendered.
expect(screen.getByText("Model Type")).toBeInTheDocument();
expect(screen.getByText("Input")).toBeInTheDocument();
expect(screen.getByText("Output")).toBeInTheDocument();
expect(screen.getByText("Kind")).toBeInTheDocument();
// Check that all the dropdowns are rendered.
const comboboxes = screen.getAllByRole("combobox");
expect(comboboxes.length).toBe(4);
const modelTypeDropdown = screen.getByRole("combobox", {
name: "Model type",
});
expect(modelTypeDropdown).toHaveValue("sink");
const modelTypeOptions = modelTypeDropdown.querySelectorAll("option");
expect(modelTypeOptions.length).toBe(5);
});
it("allows changing the type", async () => {
render({
method,
modeledMethod,
onChange,
});
const modelTypeDropdown = screen.getByRole("combobox", {
name: "Model type",
});
await userEvent.selectOptions(modelTypeDropdown, "source");
expect(onChange).toHaveBeenCalledWith(
method,
expect.objectContaining({
type: "source",
}),
);
});
it("sets other dropdowns when model type is changed", () => {
const { rerender } = render({
method,
modeledMethod,
onChange,
});
const updatedModeledMethod = createModeledMethod({
type: "source",
});
rerender(
<MethodModelingInputs
method={method}
modeledMethod={updatedModeledMethod}
onChange={onChange}
/>,
);
const modelTypeDropdown = screen.getByRole("combobox", {
name: "Model type",
});
const modelInputDropdown = screen.getByRole("combobox", {
name: "Input",
});
const modelOutputDropdown = screen.getByRole("combobox", {
name: "Output",
});
const modelKindDropdown = screen.getByRole("combobox", {
name: "Kind",
});
expect(modelTypeDropdown).toHaveValue("source");
expect(modelInputDropdown).toHaveValue("-");
expect(modelOutputDropdown).toHaveValue("ReturnValue");
expect(modelKindDropdown).toHaveValue("local");
});
});