+
+ event.stopPropagation()}
+ onChange={(value, event) => handleUserInputName(value, event)}
+ onBlur={commitUserInputName}
+ />
+ {isUserInputNameDuplicate && (
+
+ Please specify a unique property name
+
+ )}
+ |
+
+ {isObject ? (
+
+ ) : (
+ event.stopPropagation()}
+ onChange={(value, event) => handleUserInputValue(value, event)}
+ onBlur={commitUserInputValue}
+ />
+ )}
+ |
+
+
+ }
+ variant="link"
+ onClick={() => handleTrashClick(parentModel, nodeName)}
+ >
+
+ |
+
+ );
+}
diff --git a/src/components/metadata/TestUtil.tsx b/src/components/metadata/TestUtil.tsx
new file mode 100644
index 000000000..69820ede9
--- /dev/null
+++ b/src/components/metadata/TestUtil.tsx
@@ -0,0 +1,85 @@
+export const mockSchema = {
+ beans: {
+ title: 'Beans',
+ description: 'Beans Configuration',
+ type: 'array',
+ items: {
+ type: 'object',
+ additionalProperties: false,
+ properties: {
+ name: {
+ type: 'string',
+ description: 'Bean name',
+ },
+ type: {
+ type: 'string',
+ description:
+ 'What type to use for creating the bean. Can be one of: #class,#type,bean,groovy,joor,language,mvel,ognl. #class or #type then the bean is created via the fully qualified classname, such as #class:com.foo.MyBean The others are scripting languages that gives more power to create the bean with an inlined code in the script section, such as using groovy.',
+ },
+ properties: {
+ type: 'object',
+ description: 'Optional properties to set on the created local bean',
+ },
+ },
+ required: ['name', 'type'],
+ },
+ },
+ single: {
+ title: 'Single Object',
+ description: 'Single Object Configuration',
+ type: 'object',
+ additionalProperties: false,
+ properties: {
+ name: {
+ type: 'string',
+ description: 'Bean name',
+ },
+ type: {
+ type: 'string',
+ description:
+ 'What type to use for creating the bean. Can be one of: #class,#type,bean,groovy,joor,language,mvel,ognl. #class or #type then the bean is created via the fully qualified classname, such as #class:com.foo.MyBean The others are scripting languages that gives more power to create the bean with an inlined code in the script section, such as using groovy.',
+ },
+ properties: {
+ type: 'object',
+ description: 'Optional properties to set on the created local bean',
+ },
+ },
+ },
+};
+
+export const mockModel = {
+ beans: [
+ {
+ name: 'bean1',
+ type: 'type1',
+ properties: {
+ prop1: 'value1',
+ propObj1: {
+ propObj1Sub: 'valueObj1',
+ },
+ },
+ },
+ {
+ name: 'bean2',
+ type: 'type2',
+ properties: {
+ prop2: 'value2',
+ propObj2: {
+ propObj2Sub: 'valueObj2',
+ },
+ },
+ },
+ ],
+ beansNoProp: [
+ {
+ name: 'bean1',
+ type: 'type1',
+ properties: {},
+ },
+ {
+ name: 'bean2',
+ type: 'type2',
+ properties: {},
+ },
+ ],
+};
diff --git a/src/components/metadata/ToopmostArrayTable.tsx b/src/components/metadata/ToopmostArrayTable.tsx
new file mode 100644
index 000000000..e5e7bb3ba
--- /dev/null
+++ b/src/components/metadata/ToopmostArrayTable.tsx
@@ -0,0 +1,126 @@
+import './MetadataEditorModal.css';
+import { Button, EmptyState, EmptyStateBody, Tooltip, Truncate } from '@patternfly/react-core';
+import { PlusCircleIcon, TrashIcon } from '@patternfly/react-icons';
+import {
+ InnerScrollContainer,
+ OuterScrollContainer,
+ TableComposable,
+ TableVariant,
+ Tbody,
+ Td,
+ Th,
+ Thead,
+ Tr,
+} from '@patternfly/react-table';
+
+type TopmostArrayTableProps = {
+ model: any[];
+ itemSchema: any;
+ name: string;
+ selected: number;
+ onSelected: (index: number) => void;
+ onChangeModel: (model: any) => void;
+};
+
+/**
+ * The selectable table view for the topmost array metadata.
+ * @param props
+ * @constructor
+ */
+export function TopmostArrayTable(props: TopmostArrayTableProps) {
+ function handleTrashClick(index: number) {
+ const newMetadata = props.model ? props.model.slice() : [];
+ newMetadata.length !== 0 && newMetadata.splice(index, 1);
+ props.onChangeModel(newMetadata);
+ props.selected === index && props.onSelected(-1);
+ }
+
+ function handleAddNew() {
+ const newMetadata = props.model ? props.model.slice() : [];
+ newMetadata.push({});
+ props.onChangeModel(newMetadata);
+ props.onSelected(newMetadata.length - 1);
+ }
+
+ return (
+