Add modeling inputs to method modeling panel (#2849)
This commit is contained in:
@@ -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,
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user