Skip to content

Commit 848664c

Browse files
committed
feat(ui)!: update app store to support nesting children
1 parent a1cf06b commit 848664c

38 files changed

+1466
-1142
lines changed

ui/src/api/flow.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import { Edge, Node } from "reactflow"
22
import { Attribute } from "./generated/eipComponentDef"
33
import { Attributes } from "./generated/eipFlow"
4-
import { EipId } from "./id"
54

65
export interface Layout {
76
orientation: "horizontal" | "vertical"
87
density: "compact" | "comfortable"
98
}
109

1110
export interface EipNodeData {
12-
eipId: EipId
1311
label?: string
1412
}
1513

ui/src/api/id.ts

Lines changed: 0 additions & 18 deletions
This file was deleted.

ui/src/components/canvas/EipNode.tsx

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,28 @@ import { ServiceId } from "@carbon/react/icons"
33
import { Handle, NodeProps, Position } from "reactflow"
44
import { EipNodeData, Layout } from "../../api/flow"
55
import { ConnectionType, EipRole } from "../../api/generated/eipComponentDef"
6-
import { ChildNodeId, EipId } from "../../api/id"
6+
import { EipId } from "../../api/generated/eipFlow"
77
import { lookupEipComponent } from "../../singletons/eipDefinitions"
88
import getIconUrl from "../../singletons/eipIconCatalog"
99
import {
1010
clearSelectedChildNode,
1111
updateSelectedChildNode,
1212
} from "../../singletons/store/appActions"
1313
import {
14-
useGetChildren,
14+
useGetEnabledChildren,
1515
useGetLayout,
16-
useIsChildSelected,
16+
useGetSelectedChildNode,
1717
} from "../../singletons/store/getterHooks"
18+
import { getEipId } from "../../singletons/store/storeViews"
1819
import { toTitleCase } from "../../utils/titleTransform"
1920
import "./nodes.scss"
2021

2122
interface ChildrenIconsProps {
22-
childrenNames: string[]
23-
parentNodeId: string
24-
parentEipId: EipId
23+
childIds: string[]
24+
}
25+
26+
interface ChildIconButtonProps {
27+
id: string
2528
}
2629

2730
const DEFAULT_NAMESPACE = "integration"
@@ -102,36 +105,39 @@ const getClassNames = (props: NodeProps<EipNodeData>, role: EipRole) => {
102105
return ["eip-node", roleClsName, selectedClsName].join(" ")
103106
}
104107

105-
const ChildIconButton = (props: ChildNodeId) => {
106-
const selected = useIsChildSelected(props)
108+
const ChildIconButton = (props: ChildIconButtonProps) => {
109+
const currSelection = useGetSelectedChildNode()
110+
const isSelected = currSelection === props.id
107111

108112
const clsNames = ["child-icon-button"]
109-
selected && clsNames.push("child-icon-button-focused")
113+
isSelected && clsNames.push("child-icon-button-focused")
110114

111-
return (
115+
const eipId = getEipId(props.id)
116+
117+
return eipId ? (
112118
<Button
113119
className={clsNames.join(" ")}
114120
hasIconOnly
115121
renderIcon={ServiceId}
116-
iconDescription={props.name}
122+
iconDescription={eipId.name}
117123
size="sm"
118124
tooltipPosition="bottom"
119125
kind="primary"
120126
onClick={(ev) => {
121127
ev.stopPropagation()
122-
updateSelectedChildNode(props)
128+
updateSelectedChildNode(props.id)
123129
}}
124130
/>
125-
)
131+
) : null
126132
}
127133

128134
// TODO: Account for a large number of children to be displayed
129135
// TODO: Create a mapping of children to icons (with a fallback option)
130-
const ChildrenIcons = ({ childrenNames, parentNodeId }: ChildrenIconsProps) => {
136+
const ChildrenIcons = ({ childIds }: ChildrenIconsProps) => {
131137
return (
132138
<Stack className="eip-node-children" orientation="horizontal" gap={2}>
133-
{childrenNames.map((name) => (
134-
<ChildIconButton key={name} name={name} parentNodeId={parentNodeId} />
139+
{childIds.map((id) => (
140+
<ChildIconButton key={id} id={id} />
135141
))}
136142
</Stack>
137143
)
@@ -140,37 +146,37 @@ const ChildrenIcons = ({ childrenNames, parentNodeId }: ChildrenIconsProps) => {
140146
// TODO: Consider separating into Endpoint and Channel custom node types
141147
export const EipNode = (props: NodeProps<EipNodeData>) => {
142148
// TODO: clearSelectedChildNode is used in too many different components. See if that can be reduced (or elimnated).
143-
const childrenState = useGetChildren(props.id)
149+
const layout = useGetLayout()
150+
const childrenState = useGetEnabledChildren(props.id)
144151
const hasChildren = childrenState.length > 0
145152

146-
const { data } = props
147-
const componentDefinition = lookupEipComponent(data.eipId)!
148-
const layout = useGetLayout()
153+
const eipId = getEipId(props.id)
154+
const componentDefinition = eipId && lookupEipComponent(eipId)
155+
if (!componentDefinition) {
156+
return null
157+
}
158+
149159
const handles = renderHandles(
150160
componentDefinition.connectionType,
151161
layout.orientation
152162
)
153163

164+
const { data } = props
165+
154166
return (
155167
<Tile
156168
className={getClassNames(props, componentDefinition.role)}
157169
onClick={hasChildren ? () => clearSelectedChildNode() : undefined}
158170
>
159-
<div>{getNamespacedTitle(data.eipId)}</div>
160-
<img className="eip-node-image" src={getIconUrl(data.eipId)} />
171+
<div>{getNamespacedTitle(eipId)}</div>
172+
<img className="eip-node-image" src={getIconUrl(eipId)} />
161173
<div
162174
className="eip-node-label"
163175
style={hasChildren ? { marginBottom: "0.5rem" } : {}}
164176
>
165177
<strong>{data.label || DEFAULT_NODE_LABEL}</strong>
166178
</div>
167-
{hasChildren && (
168-
<ChildrenIcons
169-
childrenNames={childrenState}
170-
parentNodeId={props.id}
171-
parentEipId={props.data.eipId}
172-
/>
173-
)}
179+
{hasChildren && <ChildrenIcons childIds={childrenState} />}
174180
{handles}
175181
</Tile>
176182
)

ui/src/components/canvas/FlowCanvas.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
} from "reactflow"
2222
import "reactflow/dist/base.css"
2323
import { DYNAMIC_EDGE_TYPE, EIP_NODE_TYPE } from "../../api/flow"
24-
import { EipId } from "../../api/id"
24+
import { EipId } from "../../api/generated/eipFlow"
2525
import {
2626
clearFlow,
2727
clearSelectedChildNode,

ui/src/components/config-panel/AttributeConfigForm.tsx

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,23 @@ const addPaddingClass = "cfg-panel__container__padding-add"
2323
interface AttributeInputFactoryProps {
2424
attr: Attribute
2525
id: string
26-
parentId: string
2726
}
2827

2928
interface AttributeInputProps<T> {
3029
id: string
31-
parentId: string
3230
attr: Attribute
3331
attrValue: T
3432
}
3533

3634
interface AttributeFormProps {
3735
attrs: Attribute[]
3836
id: string
39-
parentId: string
4037
}
4138

4239
const AttributeSelectInput = ({
4340
attr,
4441
attrValue,
4542
id,
46-
parentId,
4743
}: AttributeInputProps<string>) => {
4844
const emptySelect = ""
4945
const options = useMemo(
@@ -56,8 +52,8 @@ const AttributeSelectInput = ({
5652

5753
const handleSelect = (ev: ChangeEvent<HTMLSelectElement>) => {
5854
ev.target.value === emptySelect
59-
? deleteEipAttribute(id, parentId, attr.name)
60-
: updateEipAttribute(id, parentId, attr.name, ev.target.value)
55+
? deleteEipAttribute(id, attr.name)
56+
: updateEipAttribute(id, attr.name, ev.target.value)
6157
}
6258

6359
return (
@@ -81,10 +77,9 @@ const AttributeBoolInput = ({
8177
attr,
8278
attrValue,
8379
id,
84-
parentId,
8580
}: AttributeInputProps<boolean>) => {
8681
const handleToggle = (checked: boolean) =>
87-
updateEipAttribute(id, parentId, attr.name, checked)
82+
updateEipAttribute(id, attr.name, checked)
8883

8984
return (
9085
<DescriptionTooltipWrapper id={attr.name} description={attr.description}>
@@ -110,16 +105,15 @@ const AttributeTextInput = ({
110105
attr,
111106
attrValue,
112107
id,
113-
parentId,
114108
}: AttributeInputProps<string>) => {
115109
const handleTextUpdates = useMemo(
116110
() =>
117111
debounce((ev: ChangeEvent<HTMLInputElement>) => {
118112
ev.target.value === ""
119-
? deleteEipAttribute(id, parentId, attr.name)
120-
: updateEipAttribute(id, parentId, attr.name, ev.target.value)
113+
? deleteEipAttribute(id, attr.name)
114+
: updateEipAttribute(id, attr.name, ev.target.value)
121115
}, 300),
122-
[id, parentId, attr.name]
116+
[id, attr.name]
123117
)
124118

125119
return (
@@ -136,11 +130,7 @@ const AttributeTextInput = ({
136130
}
137131

138132
const AttributeInput = (props: AttributeInputFactoryProps) => {
139-
const attrValue = useGetEipAttribute(
140-
props.id,
141-
props.parentId,
142-
props.attr.name
143-
)
133+
const attrValue = useGetEipAttribute(props.id, props.attr.name)
144134

145135
switch (props.attr.type) {
146136
// TODO: Handle number types with more specific input components (e.g. NumberInput, Slider).
Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { Stack, TextArea } from "@carbon/react"
22
import { Attribute } from "../../api/generated/eipComponentDef"
3-
import { ChildNodeId } from "../../api/id"
3+
import { getEipId } from "../../singletons/store/storeViews"
44
import { toTitleCase } from "../../utils/titleTransform"
55
import { AttributeConfigForm } from "./AttributeConfigForm"
66
import ConfigurationInputTabs from "./ConfigurationTabs"
77

88
interface ChildAttributePanelProps {
9-
childId: ChildNodeId
9+
childId: string
1010
parentName: string
1111
attributes: Attribute[]
1212
hasChildren: boolean
@@ -18,33 +18,32 @@ const ChildNodeConfig = ({
1818
parentName,
1919
attributes,
2020
hasChildren,
21-
}: ChildAttributePanelProps) => (
22-
<Stack gap={6}>
23-
<Stack gap={6} className="cfg-panel__container__padding-add">
24-
<h4>{toTitleCase(parentName)}</h4>
25-
<h5>{toTitleCase(childId.name)}</h5>
21+
}: ChildAttributePanelProps) => {
22+
const eipId = getEipId(childId)
23+
const childName = eipId?.name ?? ""
24+
25+
return (
26+
<Stack gap={6}>
27+
<Stack gap={6} className="cfg-panel__container__padding-add">
28+
<h4>{toTitleCase(parentName)}</h4>
29+
<h5>{toTitleCase(childName)}</h5>
30+
</Stack>
31+
<ConfigurationInputTabs
32+
hasAttributes={attributes.length > 0}
33+
hasChildren={hasChildren}
34+
attributesForm={<AttributeConfigForm id={childId} attrs={attributes} />}
35+
childrenForm={
36+
<TextArea
37+
labelText="Children XML"
38+
helperText="Pass in any additional nested children as XML elements"
39+
id="children-xml-escape"
40+
enableCounter
41+
maxCount={5000}
42+
/>
43+
}
44+
/>
2645
</Stack>
27-
<ConfigurationInputTabs
28-
hasAttributes={attributes.length > 0}
29-
hasChildren={hasChildren}
30-
attributesForm={
31-
<AttributeConfigForm
32-
id={childId.name}
33-
parentId={childId.parentNodeId}
34-
attrs={attributes}
35-
/>
36-
}
37-
childrenForm={
38-
<TextArea
39-
labelText="Children XML"
40-
helperText="Pass in any additional nested children as XML elements"
41-
id="children-xml-escape"
42-
enableCounter
43-
maxCount={5000}
44-
/>
45-
}
46-
/>
47-
</Stack>
48-
)
46+
)
47+
}
4948

5049
export default ChildNodeConfig

0 commit comments

Comments
 (0)