Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const ArraySelectorTrigger = ({
onClick,
icon,
}: {
value: string[] | string;
value?: string[] | string;
disabled: boolean
onClick: () => void
icon?: React.ReactNode
Expand All @@ -24,7 +24,9 @@ const ArraySelectorTrigger = ({
? value.slice(0, MAX_SHOWN_VALUES).join(", ")
: value
}
<span className={styles.counter}>{(value.length > MAX_SHOWN_VALUES) ? `+${(value.length - MAX_SHOWN_VALUES)}` : ""}</span>
{value && <span className={styles.counter}>
{(value.length > MAX_SHOWN_VALUES) ? `+${(value.length - MAX_SHOWN_VALUES)}` : ""}
</span>}
</span>
{!disabled && icon}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { getSearchStringIndex } from "../../utils";
type IProps = {
open: boolean;
onClose: () => void;
value: string[];
value?: string[];
metadata: QubitMetadataList;
onChange: (value: string[]) => void;
}
Expand Down Expand Up @@ -87,7 +87,7 @@ const QubitsSelectorPopup = ({
onClose();
};
const handleCancel = () => {
setSelection(value);
setSelection(value || []);
onClose();
};

Expand All @@ -99,7 +99,7 @@ const QubitsSelectorPopup = ({
}, [open]);

useEffect(() => {
setSelection(value);
setSelection(value || []);
}, [value]);

return <Dialog
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Input/InputField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export type InputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, "onCh
const InputField = (props: InputProps) => {
const {
name = "",
value,
value = "",
onChange,
typeOfField,
className,
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/Parameters/InputElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const ParameterSelector = ({
const subgraphBreadcrumbs = useSelector(getSubgraphBreadcrumbs);
const selectedWorkflowName = useSelector(getSelectedWorkflowName);

const handleChange = (newValue: string | number | boolean) => {
const handleChange = (newValue: string | number | boolean | undefined) => {
dispatch(setNodeParameter({
paramKey: parameterKey,
newValue,
Expand All @@ -35,16 +35,16 @@ const ParameterSelector = ({
case "boolean":
return (
<Checkbox
checked={parameter.default as boolean}
onClick={() => handleChange(!parameter.default)}
checked={parameter.value as boolean}
onClick={() => handleChange(!parameter.value)}
inputProps={{ "aria-label": "controlled" }}
/>
);
default:
return (
<InputField
placeholder={parameterKey}
value={parameter.default ? parameter.default.toString() : ""}
value={parameter.value ? parameter.value.toString() : ""}
onChange={handleChange}
/>
);
Expand Down
16 changes: 9 additions & 7 deletions frontend/src/components/Parameters/ParameterSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useCallback, useState } from "react";
import { Checkbox } from "@mui/material";
import { ParamaterValue, SingleParameter } from "./Parameters";
import { validate } from "./utils";
import { getParameterType, validate } from "./utils";
// eslint-disable-next-line css-modules/no-unused-class
import styles from "./Parameters.module.scss";
import { NodeDTO } from "../../modules/Nodes";
Expand All @@ -22,9 +22,9 @@ const ParameterSelector = ({
onChange: (paramKey: string, newValue: ParamaterValue, isValid: boolean, nodeId?: string | undefined) => void
}) => {
const [error, setError] = useState<undefined | string>(undefined);
const [inputValue, setInputValue] = useState(parameter.default);
const [inputValue, setInputValue] = useState<ParamaterValue | undefined>(parameter.value);

const handleBlur = useCallback((value: SingleParameter["default"]) => {
const handleBlur = useCallback((value?: ParamaterValue) => {
const { isValid, error } = validate(parameter, value);

onChange(parameterKey, value as string, isValid, node?.name);
Expand All @@ -49,7 +49,9 @@ const ParameterSelector = ({
* time on backend, not during input. Consider adding number input with validation.
*/
const renderInput = useCallback(() => {
if (parameter.type === "boolean")
const { type } = getParameterType(parameter);

if (type === "boolean")
return (
<Checkbox
checked={inputValue as boolean}
Expand Down Expand Up @@ -90,11 +92,11 @@ const ParameterSelector = ({
return (
<InputField
placeholder={parameterKey}
value={inputValue as string}
onChange={setInputValue}
value={inputValue as string || undefined}
onChange={(value) => setInputValue(value || undefined)}
onBlur={() => handleBlur(inputValue)}
className={styles.input}
type={["number", "integer"].includes(parameter.type) ? "number" : "string"}
type={["number", "integer"].includes(type) ? "number" : "string"}
data-testid={`input-field-${parameterKey}`}
/>
);
Expand Down
10 changes: 7 additions & 3 deletions frontend/src/components/Parameters/Parameters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,19 @@ interface IProps {
getInputElement: (key: string, parameter: SingleParameter, node?: NodeDTO | GraphWorkflow) => React.JSX.Element;
}

export type ParameterTypes = "boolean" | "number" | "integer" | "array" | "string";
export type ParamaterValue = string | boolean | number | string[];
export type ParameterTypes = "boolean" | "number" | "integer" | "array" | "string" | "null";
export type ParamaterValue = string | boolean | number | string[] | undefined;
export type QubitMetadata = { active: boolean; fidelity: number; }
export type QubitMetadataList = Record<string, QubitMetadata>
export interface SingleParameter {
id?: string;
name?: string;
parameters?: InputParameter;
default?: ParamaterValue;
value?: ParamaterValue;
items?: { type: string };
enum?: string[]
metadata?: QubitMetadataList;
metadata?: QubitMetadataList | null;
options?: {
id: string;
title: string;
Expand All @@ -40,6 +41,9 @@ export interface SingleParameter {
}[];
title: string;
type: ParameterTypes;
anyOf?: Array<{
type: ParameterTypes
}>
is_targets: boolean;
description?: string | null;
}
Expand Down
17 changes: 13 additions & 4 deletions frontend/src/components/Parameters/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { SingleParameter } from "./Parameters";

export const getParameterType = (parameter: SingleParameter) => {
return {
type: parameter.type || (parameter.anyOf || []).find(({ type }) => type !== "null")?.type,
allowEmpty: parameter.type === "null" || (parameter.anyOf || []).some(({ type }) => type === "null"),
};
};

export const validate = (parameter: SingleParameter, value: unknown) => {
if (value === undefined || value === "" || (Array.isArray(value) && value.length === 0)) {
const { type, allowEmpty } = getParameterType(parameter);

if (!allowEmpty && (value === undefined || value === null || value === "" || (Array.isArray(value) && value.length === 0))) {
return {
isValid: false,
error: "Must be not empty",
Expand All @@ -10,9 +19,9 @@ export const validate = (parameter: SingleParameter, value: unknown) => {

const num = Number(value);

switch (parameter.type) {
switch (type) {
case "number":
if (isNaN(num)) {
if (!allowEmpty && isNaN(num)) {
return {
isValid: false,
error: "Must be a number"
Expand All @@ -21,7 +30,7 @@ export const validate = (parameter: SingleParameter, value: unknown) => {
return { isValid: true, error: undefined };

case "integer":
if (isNaN(num) || !Number.isInteger(num)) {
if (!allowEmpty && (isNaN(num) || !Number.isInteger(num))) {
return {
isValid: false,
error: "Must be an integer"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export const NodeElement: React.FC<{ nodeKey: string }> = ({ nodeKey }) => {
* (title, type, description). Triggers a full NodesContext state update,
* causing re-render of all consumers.
*/
const updateParameter = (paramKey: string, newValue: boolean | number | string | string[], isValid: boolean) => {
const updateParameter = (paramKey: string, newValue: boolean | number | string | string[] | undefined, isValid: boolean) => {
handleSetError(paramKey, isValid);
dispatch(setNodeParameter({ nodeKey, paramKey, newValue }));
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const RunningJobParameters: React.FC = () => {
<div key={key} className={styles.parameterValues} data-testid={`parameter-item-${key}`}>
<div className={styles.parameterLabel} data-testid={`parameter-label-${key}`}
>{parameter.title}:</div>
<div className={styles.parameterValue} data-testid={`parameter-value-${key}`}>{parameter.default?.toString()}</div>
<div className={styles.parameterValue} data-testid={`parameter-value-${key}`}>{parameter.value?.toString()}</div>
</div>
))
}
Expand Down
14 changes: 12 additions & 2 deletions frontend/src/stores/GraphStores/GraphLibrary/GraphLibraryStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ export const graphLibrarySlice = createSlice({
reducers: {
setAllGraphs: (state, action) => {
state.allGraphs = action.payload;

const applyDefaultValue = (workflow?: GraphMap) =>
Object.values(workflow || {}).map(graph => {
Object.values(graph.parameters || {}).map(parameter =>
parameter.value = parameter.default
);
graph.nodes && applyDefaultValue(graph.nodes);
});

applyDefaultValue(state.allGraphs);
},
setSelectedWorkflowName: (state, action) => {
state.selectedWorkflowName = action.payload;
Expand All @@ -94,7 +104,7 @@ export const graphLibrarySlice = createSlice({
},
setNodeParameter: (state, action: PayloadAction<{
paramKey: string
newValue: boolean | number | string | string[]
newValue: boolean | number | string | string[] | undefined
nodeId?: string
selectedWorkflowName?: string
subgraphBreadcrumbs: string[]
Expand All @@ -114,7 +124,7 @@ export const graphLibrarySlice = createSlice({
}

if (graph.parameters)
graph.parameters[paramKey].default = newValue;
graph.parameters[paramKey].value = newValue;
},
setErrorObject: (state, action) => {
state.errorObject = action.payload;
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/stores/GraphStores/GraphLibrary/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export const submitWorkflow = () => async (dispatch: RootDispatch, getState: ()
if (!params) return transformedParams;

for (const key in params) {
transformedParams = { ...transformedParams, [key]: params[key].default };
transformedParams = { ...transformedParams, [key]: params[key].value };
}
return transformedParams;
};
Expand Down Expand Up @@ -168,7 +168,7 @@ export const submitWorkflow = () => async (dispatch: RootDispatch, getState: ()
}
};

export const setGraphNodeParameter = (paramKey: string, newValue: boolean | number | string | string[], nodeId?: string) =>
export const setGraphNodeParameter = (paramKey: string, newValue: boolean | number | string | string[] | undefined, nodeId?: string) =>
(dispatch: RootDispatch, getState: () => RootState) => {
const subgraphBreadcrumbs = getSubgraphBreadcrumbs(getState());
const selectedWorkflowName = getSelectedWorkflowName(getState());
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/stores/NodesStore/NodesStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,16 @@ export const nodesSlice = createSlice({
},
setAllNodes: (state, action) => {
state.allNodes = action.payload;
Object.values(state.allNodes || {}).map((node) =>
Object.values(node.parameters || {}).map(parameter =>
parameter.value = parameter.default
)
);
},
setNodeParameter: (state, action) => {
const { nodeKey, paramKey, newValue } = action.payload;
if (state.allNodes && state.allNodes[nodeKey].parameters)
state.allNodes[nodeKey].parameters[paramKey].default = newValue;
state.allNodes[nodeKey].parameters[paramKey].value = newValue;
},
setIsNodeRunning: (state, action) => {
state.isNodeRunning = action.payload;
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/stores/NodesStore/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,10 @@ const formatDate = (date: Date) => {
const transformInputParameters = (parameters: InputParameter) => {
return Object.entries(parameters).reduce(
(acc, [key, parameter]) => {
acc[key] = parameter.default ?? null;
acc[key] = parameter.value ?? null;
return acc;
},
{} as { [key: string]: boolean | number | string | null | string[] }
{} as { [key: string]: boolean | number | string | null | string[] | undefined }
);
};

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/stores/NodesStore/api/NodesAPI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class NodesApi extends Api {
static submitNodeParameters(
nodeName: string,
inputParameter: {
[key: string]: string | number | boolean | null | string[];
[key: string]: string | number | boolean | null | string[] | undefined;
}
): Promise<Res<void>> {
return this._fetch(this.api(SUBMIT_NODE_RUN()), API_METHODS.POST, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ describe("NodeElement - Execution", () => {
parameters: {
resonator: {
default: "q1.resonator",
value: "q1.resonator",
title: "Resonator",
type: "string" as ParameterTypes,
is_targets: false
Expand Down
3 changes: 1 addition & 2 deletions frontend/tests/unit/modules/Graph/GraphElement.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ describe("GraphElement - Parameter Management", () => {
});
//TODO: mock WebSocket event
mockStore.dispatch(setSelectedWorkflowName("test_workflow"));
mockStore.dispatch(setAllGraphs({ test_workflow: mockGraph }));

render(
<Providers>
Expand Down Expand Up @@ -163,7 +162,7 @@ describe("GraphElement - Parameter Management", () => {
// Verify setAllGraphs was called with updated parameters
await waitFor(() => {
expect(getAllGraphs(mockStore.getState())?.test_workflow.parameters?.frequency)
.toHaveProperty("default", "6.5");
.toHaveProperty("value", "6.5");
});
});

Expand Down
Loading