Skip to content

Commit ea86671

Browse files
Martha Cryanlresende
Martha Cryan
authored andcommitted
Add initial support for pipeline properties (#122)
1 parent 25b9a0f commit ea86671

18 files changed

+274
-69
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ clean: ## Make a clean source tree and unlink packages
2626
lint: ## Run linters
2727
yarn lint
2828

29-
install: lint ## Install dependencies and build packages
29+
install: ## Install dependencies and build packages
3030
yarn install && yarn build
3131

3232
dev-link: ## Link packages

examples/create-react-app/src/theme.js

+5
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ const theme = {
113113
<path d="M14.431 3.323l-8.47 10-.79-.036-3.35-4.77.818-.574 2.978 4.24 8.051-9.506.764.646z" />
114114
</SvgIcon>
115115
),
116+
pipelineIcon: (
117+
<SvgIcon>
118+
<path d="M20,23 L11.86,23 C11.7609757,22.6493104 11.616411,22.3131134 11.43,22 L22,11.43 C22.6019656,11.7993928 23.293741,11.9965488 24,12 C26.0803346,12.0067496 27.8185594,10.4177446 27.9980602,8.34515757 C28.177561,6.27257049 26.7384074,4.4083819 24.6878883,4.05737018 C22.6373692,3.70635845 20.6600973,4.98571688 20.14,7 L11.86,7 C11.356433,5.04969328 9.48121328,3.77807479 7.48299948,4.03188121 C5.48478569,4.28568764 3.98701665,5.98573188 3.98701665,8 C3.98701665,10.0142681 5.48478569,11.7143124 7.48299948,11.9681188 C9.48121328,12.2219252 11.356433,10.9503067 11.86,9 L20.14,9 C20.2390243,9.35068963 20.383589,9.68688662 20.57,10 L10,20.57 C9.39803439,20.2006072 8.70625898,20.0034512 8,20 C5.91966537,19.9932504 4.18144061,21.5822554 4.00193981,23.6548424 C3.822439,25.7274295 5.26159259,27.5916181 7.31211167,27.9426298 C9.36263076,28.2936415 11.3399027,27.0142831 11.86,25 L20,25 L20,28 L28,28 L28,20 L20,20 L20,23 Z M8,10 C6.8954305,10 6,9.1045695 6,8 C6,6.8954305 6.8954305,6 8,6 C9.1045695,6 10,6.8954305 10,8 C10,8.53043298 9.78928632,9.03914081 9.41421356,9.41421356 C9.03914081,9.78928632 8.53043298,10 8,10 Z M24,6 C25.1045695,6 26,6.8954305 26,8 C26,9.1045695 25.1045695,10 24,10 C22.8954305,10 22,9.1045695 22,8 C22,6.8954305 22.8954305,6 24,6 Z M8,26 C6.8954305,26 6,25.1045695 6,24 C6,22.8954305 6.8954305,22 8,22 C9.1045695,22 10,22.8954305 10,24 C10,25.1045695 9.1045695,26 8,26 Z M22,22 L26,22 L26,26 L22,26 L22,22 Z" />
119+
</SvgIcon>
120+
),
116121
},
117122
};
118123

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2018-2021 Elyra Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { createControl, useControlState } from "./control";
18+
19+
function DisplayControl() {
20+
const [value] = useControlState<string>();
21+
22+
return <p>{value}</p>;
23+
}
24+
25+
export default createControl("DisplayControl", DisplayControl);

packages/pipeline-editor/src/CustomFormControls/StringControl.tsx

+17-8
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,23 @@ function StringControl({
130130
return (
131131
<Container className={errorMessages.length > 0 ? "error" : undefined}>
132132
<InputContainer>
133-
<input
134-
type="text"
135-
value={renderedValue}
136-
placeholder={placeholder}
137-
onChange={handleChange}
138-
disabled={format === "file"}
139-
onBlur={handleBlur}
140-
/>
133+
{format === "multiline" ? (
134+
<textarea
135+
value={renderedValue}
136+
placeholder={placeholder}
137+
onChange={handleChange}
138+
onBlur={handleBlur}
139+
/>
140+
) : (
141+
<input
142+
type="text"
143+
value={renderedValue}
144+
placeholder={placeholder}
145+
onChange={handleChange}
146+
disabled={format === "file"}
147+
onBlur={handleBlur}
148+
/>
149+
)}
141150
{errorMessages[0] !== undefined && (
142151
<ErrorMessage>{errorMessages[0]}</ErrorMessage>
143152
)}

packages/pipeline-editor/src/CustomFormControls/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
/* istanbul ignore file */
1818

1919
export { default as StringControl } from "./StringControl";
20+
export { default as DisplayControl } from "./DisplayControl";
2021
export { default as StringArrayControl } from "./StringArrayControl";
2122
export { default as BooleanControl } from "./BooleanControl";
2223
export { default as EnumControl } from "./EnumControl";

packages/pipeline-editor/src/CustomFormControls/validators/string-validators.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export interface StringValidatorOptions {
2222
patternErrorMessage?: string; // for giving a tailored error message when a pattern does not match
2323
minLength?: number; // for restricting string length
2424
maxLength?: number; // for restricting string length
25-
format?: "file"; // for restricting strings to well-known formats. Potential future formats: "date" | "time" | "ipv4" | "email" | "uri"
25+
format?: "file" | "multiline"; // for restricting strings to well-known formats. Potential future formats: "date" | "time" | "ipv4" | "email" | "uri"
2626
}
2727

2828
export function getStringValidators<T extends string>({

packages/pipeline-editor/src/PipelineEditor/index.tsx

+37-5
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import styled, { useTheme } from "styled-components";
4242
import NodeTooltip from "../NodeTooltip";
4343
import PalettePanel from "../PalettePanel";
4444
import PipelineController from "../PipelineController";
45-
import PropertiesPanel from "../PropertiesPanel";
45+
import { NodeProperties, PipelineProperties } from "../properties-panels";
4646
import SplitPanelLayout from "../SplitPanelLayout";
4747
import TabbedPanelLayout from "../TabbedPanelLayout";
4848
import { InternalThemeProvider } from "../ThemeProvider";
@@ -52,6 +52,7 @@ interface Props {
5252
pipeline: any;
5353
toolbar?: any;
5454
nodes?: any;
55+
pipelineProperties?: any;
5556
onAction?: (action: { type: string; payload?: any }) => any;
5657
onChange?: (pipeline: any) => any;
5758
onDoubleClickNode?: (e: CanvasClickEvent) => any;
@@ -147,6 +148,7 @@ const PipelineEditor = forwardRef(
147148
{
148149
pipeline,
149150
nodes,
151+
pipelineProperties,
150152
toolbar,
151153
onAction,
152154
onChange,
@@ -439,9 +441,9 @@ const PipelineEditor = forwardRef(
439441
[onDoubleClickNode]
440442
);
441443

442-
const [selectedNodes, setSelectedNodes] = useState<NodeTypeDef[]>();
444+
const [selectedNodes, setSelectedNodes] = useState<string[]>();
443445
const handleSelectionChange = useCallback((e: CanvasSelectionEvent) => {
444-
setSelectedNodes(e.selectedNodes);
446+
setSelectedNodes(e.selectedNodes.map((n: NodeTypeDef) => n.id));
445447
}, []);
446448

447449
const handleEditAction = useCallback(
@@ -541,6 +543,18 @@ const PipelineEditor = forwardRef(
541543
[onChange]
542544
);
543545

546+
const handlePipelinePropertiesChange = useCallback(
547+
(data) => {
548+
const pipeline = controller.current.getPipelineFlow();
549+
if (pipeline?.pipelines?.[0]?.app_data) {
550+
pipeline.pipelines[0].app_data.properties = data;
551+
controller.current.setPipelineFlow(pipeline);
552+
onChange?.(controller.current.getPipelineFlow());
553+
}
554+
},
555+
[onChange]
556+
);
557+
544558
const handleTooltip = (tipType: string, e: TipEvent) => {
545559
function isNodeTipEvent(type: string, _e: TipEvent): _e is TipNode {
546560
return type === "tipTypeNode";
@@ -664,13 +678,31 @@ const PipelineEditor = forwardRef(
664678
setPanelOpen(true);
665679
}}
666680
tabs={[
681+
{
682+
id: "pipeline-properties",
683+
label: "Pipeline Properties",
684+
icon: theme.overrides?.pipelineIcon,
685+
content: (
686+
<PipelineProperties
687+
pipelineFlow={pipeline}
688+
propertiesSchema={pipelineProperties}
689+
onFileRequested={onFileRequested}
690+
onPropertiesUpdateRequested={
691+
onPropertiesUpdateRequested
692+
}
693+
onChange={handlePipelinePropertiesChange}
694+
/>
695+
),
696+
},
667697
{
668698
id: "properties",
669699
label: "Node Properties",
670700
icon: theme.overrides?.propertiesIcon,
671701
content: (
672-
<PropertiesPanel
673-
selectedNodes={selectedNodes}
702+
<NodeProperties
703+
selectedNodes={pipeline?.pipelines?.[0]?.nodes?.filter(
704+
(n: NodeTypeDef) => selectedNodes?.includes(n.id)
705+
)}
674706
nodes={nodes}
675707
onFileRequested={onFileRequested}
676708
onPropertiesUpdateRequested={

packages/pipeline-editor/src/ThemeProvider/styles.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ export const CanvasOverrides = css`
121121
}
122122
123123
.properties-wrapper input[type="text" i],
124-
.properties-wrapper input[type="number" i] {
124+
.properties-wrapper input[type="number" i],
125+
.properties-wrapper textarea {
125126
box-sizing: border-box;
126127
padding: 4px;
127128
background-color: ${({ theme }) => theme.palette.background.input};
@@ -134,6 +135,16 @@ export const CanvasOverrides = css`
134135
font-size: ${({ theme }) => theme.typography.fontSize};
135136
}
136137
138+
.properties-wrapper p {
139+
font-family: ${({ theme }) => theme.typography.fontFamily};
140+
font-weight: ${({ theme }) => theme.typography.fontWeight};
141+
font-size: ${({ theme }) => theme.typography.fontSize};
142+
}
143+
144+
.properties-wrapper textarea {
145+
resize: vertical;
146+
}
147+
137148
.properties-wrapper input[type="text" i]:focus,
138149
.properties-wrapper input[type="number" i]:focus {
139150
outline: 1px solid ${({ theme }) => theme.palette.focus};

packages/pipeline-editor/src/common-canvas.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,7 @@ declare module "@elyra/canvas" {
596596
app_data?: {
597597
// NOTE: elyra specific.
598598
version: number;
599+
properties: any;
599600
/**
600601
* Pipeline level UI information
601602
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright 2018-2021 Elyra Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { PropertiesPanel, Message } from "./PropertiesPanel";
18+
19+
interface Props {
20+
selectedNodes?: any[];
21+
nodes: any[];
22+
onFileRequested?: (options: any) => any;
23+
onPropertiesUpdateRequested?: (options: any) => any;
24+
onChange?: (nodeID: string, data: any) => any;
25+
}
26+
27+
function NodeProperties({
28+
selectedNodes,
29+
nodes,
30+
onFileRequested,
31+
onPropertiesUpdateRequested,
32+
onChange,
33+
}: Props) {
34+
if (selectedNodes === undefined || selectedNodes.length === 0) {
35+
return <Message>Select a node to edit its properties.</Message>;
36+
}
37+
38+
if (selectedNodes.length > 1) {
39+
return (
40+
<Message>
41+
Multiple nodes are selected. Select a single node to edit its
42+
properties.
43+
</Message>
44+
);
45+
}
46+
47+
const selectedNode = selectedNodes[0];
48+
49+
if (!selectedNode) {
50+
return <Message>Select a node to edit its properties.</Message>;
51+
}
52+
53+
if (selectedNode.type !== "execution_node") {
54+
return (
55+
<Message>This node type doesn't have any editable properties.</Message>
56+
);
57+
}
58+
59+
const nodePropertiesSchema = nodes.find((n: any) => n.op === selectedNode.op);
60+
61+
if (nodePropertiesSchema === undefined) {
62+
return (
63+
<Message>This node type doesn't have any editable properties.</Message>
64+
);
65+
}
66+
67+
return (
68+
<PropertiesPanel
69+
currentProperties={selectedNode.app_data}
70+
propertiesSchema={nodePropertiesSchema}
71+
onFileRequested={onFileRequested}
72+
onChange={(data: any) => {
73+
onChange?.(selectedNode.id, data);
74+
}}
75+
id={selectedNode.id}
76+
/>
77+
);
78+
}
79+
80+
export default NodeProperties;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2018-2021 Elyra Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { PropertiesPanel, Message } from "./PropertiesPanel";
18+
19+
interface Props {
20+
pipelineFlow: any;
21+
propertiesSchema?: any;
22+
onFileRequested?: (options: any) => any;
23+
onPropertiesUpdateRequested?: (options: any) => any;
24+
onChange?: (data: any) => any;
25+
}
26+
27+
function PipelineProperties({
28+
pipelineFlow,
29+
propertiesSchema,
30+
onFileRequested,
31+
onPropertiesUpdateRequested,
32+
onChange,
33+
}: Props) {
34+
if (propertiesSchema === undefined) {
35+
return <Message>No pipeline properties defined.</Message>;
36+
}
37+
38+
return (
39+
<PropertiesPanel
40+
currentProperties={
41+
pipelineFlow?.pipelines?.[0]?.app_data?.properties ?? {}
42+
}
43+
propertiesSchema={propertiesSchema}
44+
onFileRequested={onFileRequested}
45+
onChange={onChange}
46+
id={pipelineFlow.id}
47+
/>
48+
);
49+
}
50+
51+
export default PipelineProperties;

0 commit comments

Comments
 (0)