You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2021/12/05 21:00:00 UTC

[camel-karavan] branch main updated: Properties UI decomposition (#140)

This is an automated email from the ASF dual-hosted git repository.

marat pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git


The following commit(s) were added to refs/heads/main by this push:
     new 5aa32bf  Properties UI decomposition (#140)
5aa32bf is described below

commit 5aa32bf85aafdc1875ecc9824677ba4fa378dbf7
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Sun Dec 5 15:59:57 2021 -0500

    Properties UI decomposition (#140)
---
 karavan-designer/src/App.tsx                       |   5 +-
 karavan-designer/src/designer/api/CamelApiExt.tsx  |  16 +-
 karavan-designer/src/designer/api/CamelUi.tsx      |  12 +-
 karavan-designer/src/designer/ui/DslProperties.tsx | 243 ++-------------------
 .../designer/ui/field/ComponentParameterField.tsx  | 120 ++++++++++
 .../src/designer/ui/field/DataFormatField.tsx      | 124 +++++++++++
 .../src/designer/ui/field/DslPropertyField.tsx     | 176 +++++++++++++++
 .../src/designer/ui/field/ExpressionField.tsx      | 115 ++++++++++
 .../src/designer/ui/field/KameletPropertyField.tsx |  99 +++++++++
 9 files changed, 667 insertions(+), 243 deletions(-)

diff --git a/karavan-designer/src/App.tsx b/karavan-designer/src/App.tsx
index 4d3f39e..df7f5cf 100644
--- a/karavan-designer/src/App.tsx
+++ b/karavan-designer/src/App.tsx
@@ -53,7 +53,10 @@ class App extends React.Component<Props, State> {
             '          - pollEnrich:\n' +
             '              expression: {}\n' +
             '          - to: \n' +
-            '               uri: "log:info"\n' +
+            '               uri: "log:info:xxx"\n' +
+            '               parameters:\n' +
+            '                   level: \'OFF\'\n' +
+            '                   logMask: true \n' +
             '          - choice:\n' +
             '              otherwise: {}\n' +
             '              when:\n' +
diff --git a/karavan-designer/src/designer/api/CamelApiExt.tsx b/karavan-designer/src/designer/api/CamelApiExt.tsx
index 58a5143..5c1eec7 100644
--- a/karavan-designer/src/designer/api/CamelApiExt.tsx
+++ b/karavan-designer/src/designer/api/CamelApiExt.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import {CamelElement, FromStep, Integration, ProcessorStep} from "../model/CamelModel";
+import {CamelElement, Expression, FromStep, Integration, ProcessorStep} from "../model/CamelModel";
 import {CamelMetadataApi, DataFormats, PropertyMeta} from "./CamelMetadata";
 import {CamelApi} from "./CamelApi";
 import {ComponentApi} from "./ComponentApi";
@@ -63,10 +63,10 @@ export class CamelApiExt {
         return integration;
     }
 
-    static getExpressionLanguage = (element: CamelElement | undefined): string | undefined => {
-        const el: any = Object.assign({}, element);
-        if (el.hasOwnProperty('expression') && el.expression) {
-            return el.expression.language
+    static getExpressionLanguage = (expression: Expression | undefined): string | undefined => {
+        const el: any = Object.assign({}, expression);
+        if (el.hasOwnProperty('language') && el.language) {
+            return el.language
         } else {
             return undefined;
         }
@@ -78,10 +78,10 @@ export class CamelApiExt {
         return dataFormat ? [dataFormat, el[dataFormat]] : undefined;
     }
 
-    static getExpressionValue = (element: CamelElement | undefined): string | undefined => {
-        const language = CamelApiExt.getExpressionLanguage(element);
+    static getExpressionValue = (expression: Expression | undefined): string | undefined => {
+        const language = CamelApiExt.getExpressionLanguage(expression);
         if (language) {
-            return (element as any).expression[language];
+            return (expression as any)[language];
         } else {
             return undefined;
         }
diff --git a/karavan-designer/src/designer/api/CamelUi.tsx b/karavan-designer/src/designer/api/CamelUi.tsx
index 79c19de..6d6be18 100644
--- a/karavan-designer/src/designer/api/CamelUi.tsx
+++ b/karavan-designer/src/designer/api/CamelUi.tsx
@@ -240,8 +240,11 @@ export class CamelUi {
     };
 
     static isShowExpressionTooltip = (element: CamelElement): boolean => {
-        const exp = CamelApiExt.getExpressionValue(element);
-        return element.hasOwnProperty("expression") && (exp !== undefined && exp?.trim().length > 0);
+        if (element.hasOwnProperty("expression")){
+            const exp = CamelApiExt.getExpressionValue((element as any).expression);
+            return (exp !== undefined && exp?.trim().length > 0);
+        }
+        return false;
     }
 
     static isShowUriTooltip = (element: CamelElement): boolean => {
@@ -250,8 +253,9 @@ export class CamelUi {
     }
 
     static getExpressionTooltip = (element: CamelElement): string => {
-        const language = CamelApiExt.getExpressionLanguage(element) || 'simple';
-        const value = CamelApiExt.getExpressionValue(element) || '';
+        const e = (element as any).expression;
+        const language = CamelApiExt.getExpressionLanguage(e) || 'simple';
+        const value = CamelApiExt.getExpressionValue(e) || '';
         return language.concat(": ", value);
     }
 
diff --git a/karavan-designer/src/designer/ui/DslProperties.tsx b/karavan-designer/src/designer/ui/DslProperties.tsx
index 80cd53d..c06f74a 100644
--- a/karavan-designer/src/designer/ui/DslProperties.tsx
+++ b/karavan-designer/src/designer/ui/DslProperties.tsx
@@ -24,12 +24,6 @@ import {
     Popover,
     Switch,
     TextVariants,
-    Select,
-    SelectVariant,
-    SelectDirection,
-    SelectOption,
-    TextArea,
-    ExpandableSection,
 } from '@patternfly/react-core';
 import '../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
@@ -38,11 +32,12 @@ import {Property} from "../model/KameletModels";
 import {CamelElement, Expression, Integration} from "../model/CamelModel";
 import {CamelApi} from "../api/CamelApi";
 import {CamelApiExt} from "../api/CamelApiExt";
-import {CamelMetadataApi, DataFormats, Languages, PropertyMeta} from "../api/CamelMetadata";
+import {CamelMetadataApi, PropertyMeta} from "../api/CamelMetadata";
 import {CamelYaml} from "../api/CamelYaml";
 import {CamelUi} from "../api/CamelUi";
 import {ComponentApi} from "../api/ComponentApi";
-import {ComponentProperty} from "../model/ComponentModels";
+import {DslPropertyField} from "./field/DslPropertyField";
+import {DataFormatField} from "./field/DataFormatField";
 
 interface Props {
     integration: Integration,
@@ -114,7 +109,7 @@ export class DslProperties extends React.Component<Props, State> {
                 this.props.onPropertyUpdate?.call(this, clone, this.state.step.uuid);
             }
         }
-    };
+    }
 
     componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
         if (prevProps.step !== this.props.step) {
@@ -130,14 +125,6 @@ export class DslProperties extends React.Component<Props, State> {
         });
     }
 
-    openSelect = (propertyName: string) => {
-        this.setState({selectStatus: new Map<string, boolean>([[propertyName, true]])});
-    }
-
-    isSelectOpen = (propertyName: string): boolean => {
-        return this.state.selectStatus.has(propertyName) && this.state.selectStatus.get(propertyName) === true;
-    }
-
     getIntegrationHeader = (): JSX.Element => {
         return (
             <div className="headers">
@@ -215,225 +202,21 @@ export class DslProperties extends React.Component<Props, State> {
         )
     }
 
-    createComponentProperty = (property: ComponentProperty): JSX.Element => {
-        const prefix = "parameters";
-        const id = prefix + "-" + property.name;
-        const value = CamelApiExt.getParametersValue(this.state.element, property.name, property.kind === 'path');
-        const selectOptions: JSX.Element[] = []
-        if (property.enum && property.enum.length > 0) {
-            selectOptions.push(<SelectOption key={0} value={"Select ..."} isPlaceholder/>);
-            property.enum.forEach(v => selectOptions.push(<SelectOption key={v} value={v}/>));
-        }
-        return (
-            <FormGroup
-                key={id}
-                label={property.displayName}
-                fieldId={id}
-                isRequired={property.kind === 'path' || property.required}
-                labelIcon={
-                    <Popover
-                        position={"left"}
-                        headerContent={property.displayName}
-                        bodyContent={property.description}
-                        footerContent={property.defaultValue !== undefined ? "Default: " + property.defaultValue : undefined}>
-                        <button type="button" aria-label="More info" onClick={e => e.preventDefault()}
-                                className="pf-c-form__group-label-help">
-                            <HelpIcon noVerticalAlign/>
-                        </button>
-                    </Popover>
-                }>
-                {['string', 'duration', 'integer', 'int', 'number'].includes(property.type) && property.enum === undefined &&
-                <TextInput
-                    className="text-field" isRequired
-                    type={['integer', 'int', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
-                    id={id} name={id}
-                    value={value !== undefined ? value : property.defaultValue}
-                    onChange={e => this.parametersChanged(property.name, ['integer', 'int', 'number'].includes(property.type) ? Number(e) : e, property.kind === 'path')}/>
-                }
-                {property.type === 'string' && property.enum &&
-                <Select
-                    variant={SelectVariant.single}
-                    aria-label={property.name}
-                    onToggle={isExpanded => {
-                        this.openSelect(property.name)
-                    }}
-                    onSelect={(e, value, isPlaceholder) => this.parametersChanged(property.name, (!isPlaceholder ? value : undefined), property.kind === 'path')}
-                    selections={value !== undefined ? value.toString() : property.defaultValue}
-                    isOpen={this.isSelectOpen(property.name)}
-                    aria-labelledby={property.name}
-                    direction={SelectDirection.down}
-                >
-                    {selectOptions}
-                </Select>
-                }
-                {property.type === 'boolean' && <Switch
-                    id={id} name={id}
-                    value={value?.toString()}
-                    aria-label={id}
-                    isChecked={value !== undefined ? Boolean(value) === true : Boolean(property.defaultValue) === true}
-                    onChange={e => this.parametersChanged(property.name, !Boolean(value))}/>
-                }
-            </FormGroup>
-        )
-    }
-
-    createExpressionProperty = (property: PropertyMeta): JSX.Element => {
-        const prefix = "language";
-        const language = CamelApiExt.getExpressionLanguage(this.state.element) || 'Simple'
-        const dslLanguage = Languages.find((l: [string, string, string]) => l[0] === language);
-        const value = language ? CamelApiExt.getExpressionValue(this.state.element) : undefined;
-        const selectOptions: JSX.Element[] = []
-        Languages.forEach((lang: [string, string, string]) => {
-            const s = <SelectOption key={lang[0]} value={lang[0]} description={lang[2]}/>;
-            selectOptions.push(s);
-        })
-        return (
-            <div>
-                <FormGroup key={prefix + "-" + property.name} fieldId={property.name}>
-                    <Select
-                        variant={SelectVariant.typeahead}
-                        aria-label={property.name}
-                        onToggle={isExpanded => {
-                            this.openSelect(property.name)
-                        }}
-                        onSelect={(e, lang, isPlaceholder) => this.expressionChanged(lang.toString(), value)}
-                        selections={dslLanguage}
-                        isOpen={this.isSelectOpen(property.name)}
-                        aria-labelledby={property.name}
-                        direction={SelectDirection.down}
-                    >
-                        {selectOptions}
-                    </Select>
-                </FormGroup>
-                <FormGroup
-                    key={property.name}
-                    fieldId={property.name}
-                    labelIcon={property.description ?
-                        <Popover
-                            position={"left"}
-                            headerContent={property.displayName}
-                            bodyContent={property.description}>
-                            <button type="button" aria-label="More info" onClick={e => {
-                                e.preventDefault();
-                                e.stopPropagation();
-                            }}
-                                    className="pf-c-form__group-label-help">
-                                <HelpIcon noVerticalAlign/>
-                            </button>
-                        </Popover> : <div></div>
-                    }>
-                    <TextArea
-                        autoResize
-                        className="text-field" isRequired
-                        type={"text"}
-                        id={property.name} name={property.name}
-                        height={"100px"}
-                        value={value?.toString()}
-                        onChange={e => this.expressionChanged(language, e)}/>
-                </FormGroup>
-            </div>
-        )
-    }
-
-    createEipDslProperty = (property: PropertyMeta): JSX.Element => {
-        const value = this.state.element ? (this.state.element as any)[property.name] : undefined;
-        const selectOptions: JSX.Element[] = []
-        if (property.enumVals && property.enumVals.length > 0) {
-            selectOptions.push(<SelectOption key={0} value={"Select " + property.name} isPlaceholder/>);
-            selectOptions.push(...property.enumVals.split(',').map((value: string) =>
-                <SelectOption key={value} value={value.trim()}/>));
-        }
-        return (
-            <FormGroup
-                key={property.name}
-                label={CamelApi.capitalizeName(property.displayName)}
-                fieldId={property.name}
-                labelIcon={property.description ?
-                    <Popover
-                        position={"left"}
-                        headerContent={property.displayName}
-                        bodyContent={property.description}
-                        footerContent={property.defaultValue !== undefined ? "Default: " + property.defaultValue : undefined}>
-                        <button type="button" aria-label="More info" onClick={e => {
-                            e.preventDefault();
-                            e.stopPropagation();
-                        }}
-                                className="pf-c-form__group-label-help">
-                            <HelpIcon noVerticalAlign/>
-                        </button>
-                    </Popover> : <div></div>
-                }>
-                {['string', 'duration', 'integer', 'number'].includes(property.type) && !property.enumVals &&
-                <TextInput
-                    isReadOnly={property.name === 'uri' && this.state.element?.dslName !== 'toD'}
-                    className="text-field" isRequired
-                    type={['integer', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
-                    id={property.name} name={property.name}
-                    value={value?.toString()}
-                    onChange={e => this.propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(e) : e)}/>
-                }
-                {property.type === 'boolean' && <Switch
-                    id={property.name} name={property.name}
-                    value={this.state.element?.toString()}
-                    aria-label={property.name}
-                    isChecked={Boolean(value) === true}
-                    onChange={e => this.propertyChanged(property.name, !Boolean(value))}/>
-                }
-
-                {property.enumVals &&
-                <Select
-                    variant={SelectVariant.single}
-                    aria-label={property.name}
-                    onToggle={isExpanded => {
-                        this.openSelect(property.name)
-                    }}
-                    onSelect={(e, value, isPlaceholder) => this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
-                    selections={value}
-                    isOpen={this.isSelectOpen(property.name)}
-                    aria-labelledby={property.name}
-                    direction={SelectDirection.down}
-                >
-                    {selectOptions}
-                </Select>
-                }
-                {property.name === 'expression' && property.type === "Expression" &&
-                    <div className="expression">
-                        {this.createExpressionProperty(property)}
-                    </div>
-                }
-                <div className="parameters">
-                    {property.name === 'parameters' && CamelUi.isKameletComponent(this.state.element)
-                    && CamelUi.getKameletProperties(this.state.element).map(kp => this.createKameletProperty(kp))}
-
-                    {property.name === 'parameters' && this.state.element && !CamelUi.isKameletComponent(this.state.element)
-                    && CamelUi.getComponentProperties(this.state.element, false).map(kp => this.createComponentProperty(kp))}
-                </div>
-                {property.name === 'parameters' && this.state.element && !CamelUi.isKameletComponent(this.state.element) && CamelUi.getComponentProperties(this.state.element, true).length > 0 && (
-                    <ExpandableSection
-                        toggleText={'Advanced parameters'}
-                        onToggle={isExpanded => this.setState({isShowAdvanced: !this.state.isShowAdvanced})}
-                        isExpanded={this.state.isShowAdvanced}>
-                        <div className="parameters">
-                            {CamelUi.getComponentProperties(this.state.element, true).map(kp => this.createComponentProperty(kp))}
-                        </div>
-                    </ExpandableSection>
-                )}
-            </FormGroup>
-        )
-    }
-
-    setDataFormat = (dataFormat: string, props: any) => {
-        console.log(dataFormat);
-        console.log(props);
-    }
-
     render() {
         return (
             <div key={this.state.step ? this.state.step.uuid : 'integration'} className='properties'>
                 <Form autoComplete="off">
                     {this.state.element === undefined && this.getIntegrationHeader()}
                     {this.state.element && this.getComponentHeader()}
-                    {this.state.element && CamelApiExt.getElementProperties(this.state.element.dslName).map((property: PropertyMeta) => this.createEipDslProperty(property))}
+                    {this.state.element && CamelApiExt.getElementProperties(this.state.element.dslName).map((property: PropertyMeta) =>
+                        <DslPropertyField property={property}
+                                          element={this.state.element}
+                                          value={this.state.element ? (this.state.element as any)[property.name] : undefined}
+                                          onExpressionChange={this.expressionChanged}
+                                          onParameterChange={this.parametersChanged}
+                                          onChange={this.propertyChanged} />
+                    )}
+                    {this.state.element && ['marshal', 'unmarshal'].includes(this.state.element.dslName) && <DataFormatField element={this.state.element}/>}
                 </Form>
             </div>
         )
diff --git a/karavan-designer/src/designer/ui/field/ComponentParameterField.tsx b/karavan-designer/src/designer/ui/field/ComponentParameterField.tsx
new file mode 100644
index 0000000..f9b6e15
--- /dev/null
+++ b/karavan-designer/src/designer/ui/field/ComponentParameterField.tsx
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    FormGroup,
+    TextInput,
+    Popover,
+    Switch,
+    Select,
+    SelectVariant,
+    SelectDirection,
+    SelectOption,
+} from '@patternfly/react-core';
+import '../../karavan.css';
+import "@patternfly/patternfly/patternfly.css";
+import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
+import {ComponentProperty} from "../../model/ComponentModels";
+
+interface Props {
+    property: ComponentProperty,
+    value: any,
+    onParameterChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean) => void
+}
+
+interface State {
+    selectIsOpen: boolean
+}
+
+export class ComponentParameterField extends React.Component<Props, State> {
+
+    public state: State = {
+        selectIsOpen: false,
+    }
+
+    openSelect = () => {
+        this.setState({selectIsOpen: true});
+    }
+
+    parametersChanged = (parameter: string, value: string | number | boolean | any, pathParameter?: boolean) => {
+        this.props.onParameterChange?.call(this, parameter, value, pathParameter);
+        this.setState({selectIsOpen: false});
+    }
+
+    render() {
+        const property: ComponentProperty = this.props.property;
+        const value = this.props.value;
+        const prefix = "parameters";
+        const id = prefix + "-" + property.name;
+        const selectOptions: JSX.Element[] = []
+        if (property.enum && property.enum.length > 0) {
+            selectOptions.push(<SelectOption key={0} value={"Select ..."} isPlaceholder/>);
+            property.enum.forEach(v => selectOptions.push(<SelectOption key={v} value={v}/>));
+        }
+        return (
+            <FormGroup
+                key={id}
+                label={property.displayName}
+                fieldId={id}
+                isRequired={property.kind === 'path' || property.required}
+                labelIcon={
+                    <Popover
+                        position={"left"}
+                        headerContent={property.displayName}
+                        bodyContent={property.description}
+                        footerContent={property.defaultValue !== undefined ? "Default: " + property.defaultValue : undefined}>
+                        <button type="button" aria-label="More info" onClick={e => e.preventDefault()}
+                                className="pf-c-form__group-label-help">
+                            <HelpIcon noVerticalAlign/>
+                        </button>
+                    </Popover>
+                }>
+                {['string', 'duration', 'integer', 'int', 'number'].includes(property.type) && property.enum === undefined &&
+                <TextInput
+                    className="text-field" isRequired
+                    type={['integer', 'int', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
+                    id={id} name={id}
+                    value={value !== undefined ? value : property.defaultValue}
+                    onChange={e => this.parametersChanged(property.name, ['integer', 'int', 'number'].includes(property.type) ? Number(e) : e, property.kind === 'path')}/>
+                }
+                {property.type === 'string' && property.enum &&
+                <Select
+                    variant={SelectVariant.single}
+                    aria-label={property.name}
+                    onToggle={isExpanded => {
+                        this.openSelect()
+                    }}
+                    onSelect={(e, value, isPlaceholder) => this.parametersChanged(property.name, (!isPlaceholder ? value : undefined), property.kind === 'path')}
+                    selections={value !== undefined ? value.toString() : property.defaultValue}
+                    isOpen={this.state.selectIsOpen}
+                    aria-labelledby={property.name}
+                    direction={SelectDirection.down}
+                >
+                    {selectOptions}
+                </Select>
+                }
+                {property.type === 'boolean' && <Switch
+                    id={id} name={id}
+                    value={value?.toString()}
+                    aria-label={id}
+                    isChecked={value !== undefined ? Boolean(value) === true : Boolean(property.defaultValue) === true}
+                    onChange={e => this.parametersChanged(property.name, !Boolean(value))}/>
+                }
+            </FormGroup>
+        )
+    }
+}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/ui/field/DataFormatField.tsx b/karavan-designer/src/designer/ui/field/DataFormatField.tsx
new file mode 100644
index 0000000..a4168f2
--- /dev/null
+++ b/karavan-designer/src/designer/ui/field/DataFormatField.tsx
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    FormGroup,
+    Popover,
+    Select,
+    SelectVariant,
+    SelectDirection,
+    SelectOption,
+    TextArea,
+} from '@patternfly/react-core';
+import '../../karavan.css';
+import "@patternfly/patternfly/patternfly.css";
+import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
+import {CamelElement} from "../../model/CamelModel";
+import {CamelApiExt} from "../../api/CamelApiExt";
+import {CamelMetadataApi, DataFormats, PropertyMeta} from "../../api/CamelMetadata";
+
+interface Props {
+    element: CamelElement,
+}
+
+interface State {
+    property: PropertyMeta,
+    element?: CamelElement,
+    selectStatus: Map<string, boolean>
+}
+
+export class DataFormatField extends React.Component<Props, State> {
+
+    public state: State = {
+        property: new PropertyMeta('id', 'Id', "The id of this node", 'string', '', '', false, false, false, false),
+        selectStatus: new Map<string, boolean>(),
+        element: this.props.element,
+    }
+
+    setDataFormat = (dataFormat: string, props: any) => {
+        console.log(dataFormat);
+        console.log(props);
+    }
+
+    openSelect = (propertyName: string) => {
+        this.setState({selectStatus: new Map<string, boolean>([[propertyName, true]])});
+    }
+
+    isSelectOpen = (propertyName: string): boolean => {
+        return this.state.selectStatus.has(propertyName) && this.state.selectStatus.get(propertyName) === true;
+    }
+
+    render() {
+        const fieldId = "dataFormat";
+        const dataFormat  = CamelApiExt.getDataFormat(this.state.element)
+        const dataFormatName = dataFormat ? dataFormat[0] : '';
+        const value = dataFormat ? CamelApiExt.getExpressionValue(this.state.element) : undefined;
+        const properties = CamelMetadataApi.getCamelDataFormatMetadata(dataFormatName)?.properties;
+        const selectOptions: JSX.Element[] = []
+        DataFormats.forEach((df: [string, string, string]) => {
+            const s = <SelectOption key={df[0]} value={df[0]} description={df[2]}/>;
+            selectOptions.push(s);
+        })
+        return (
+            <div>
+                <FormGroup
+                    key={fieldId}
+                    label="Data Format"
+                    fieldId="dataFormat"
+                    labelIcon={
+                        <Popover
+                            position={"left"}
+                            headerContent="Data Format"
+                            bodyContent="Specified format for transmission over a transport or component">
+                            <button type="button" aria-label="More info" onClick={e => {
+                                e.preventDefault();
+                                e.stopPropagation();
+                            }}
+                                    className="pf-c-form__group-label-help">
+                                <HelpIcon noVerticalAlign/>
+                            </button>
+                        </Popover>}
+                >
+                    <div className="dataformat">
+                        <Select
+                            variant={SelectVariant.typeahead}
+                            aria-label="dataFormat"
+                            onToggle={isExpanded => {
+                                this.openSelect(fieldId)
+                            }}
+                            onSelect={(e, df, isPlaceholder) => this.setDataFormat(df.toString(), value)}
+                            selections={dataFormatName}
+                            isOpen={this.isSelectOpen(fieldId)}
+                            aria-labelledby={fieldId}
+                            direction={SelectDirection.down}
+                        >
+                            {selectOptions}
+                        </Select>
+                        <TextArea
+                            autoResize
+                            className="text-field" isRequired
+                            type={"text"}
+                            id={fieldId+"text"} name={fieldId+"text"}
+                            height={"100px"}
+                            value={value?.toString()}
+                            onChange={e => this.setDataFormat(dataFormatName, e)}/>
+                    </div>
+                </FormGroup>
+            </div>
+        )
+    }
+}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/ui/field/DslPropertyField.tsx b/karavan-designer/src/designer/ui/field/DslPropertyField.tsx
new file mode 100644
index 0000000..073907d
--- /dev/null
+++ b/karavan-designer/src/designer/ui/field/DslPropertyField.tsx
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    FormGroup,
+    TextInput,
+    Popover,
+    Switch,
+    Select,
+    SelectVariant,
+    SelectDirection,
+    SelectOption, ExpandableSection,
+} from '@patternfly/react-core';
+import '../../karavan.css';
+import "@patternfly/patternfly/patternfly.css";
+import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
+import {CamelApi} from "../../api/CamelApi";
+import { PropertyMeta} from "../../api/CamelMetadata";
+import {CamelApiExt} from "../../api/CamelApiExt";
+import {ExpressionField} from "./ExpressionField";
+import {CamelUi} from "../../api/CamelUi";
+import {ComponentParameterField} from "./ComponentParameterField";
+import {CamelElement} from "../../model/CamelModel";
+import {KameletPropertyField} from "./KameletPropertyField";
+
+interface Props {
+    property: PropertyMeta,
+    value: any,
+    onChange?: (fieldId: string, value: string | number | boolean | any) => void,
+    onExpressionChange?: (language: string, value: string | undefined) => void,
+    onParameterChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean) => void,
+    element?: CamelElement
+}
+
+interface State {
+    selectStatus: Map<string, boolean>,
+    isShowAdvanced: boolean
+}
+
+export class DslPropertyField extends React.Component<Props, State> {
+
+    public state: State = {
+        selectStatus: new Map<string, boolean>(),
+        isShowAdvanced: false
+    }
+
+    openSelect = (propertyName: string) => {
+        this.setState({selectStatus: new Map<string, boolean>([[propertyName, true]])});
+    }
+
+    isSelectOpen = (propertyName: string): boolean => {
+        return this.state.selectStatus.has(propertyName) && this.state.selectStatus.get(propertyName) === true;
+    }
+
+    propertyChanged = (fieldId: string, value: string | number | boolean | any) => {
+        this.props.onChange?.call(this, fieldId, value);
+    }
+
+    render() {
+        const isKamelet = CamelUi.isKameletComponent(this.props.element);
+        const property: PropertyMeta = this.props.property;
+        const value = this.props.value;
+        const selectOptions: JSX.Element[] = []
+        if (property.enumVals && property.enumVals.length > 0) {
+            selectOptions.push(<SelectOption key={0} value={"Select " + property.name} isPlaceholder/>);
+            selectOptions.push(...property.enumVals.split(',').map((value: string) =>
+                <SelectOption key={value} value={value.trim()}/>));
+        }
+        return (
+            <FormGroup
+                key={property.name}
+                label={CamelApi.capitalizeName(property.displayName)}
+                fieldId={property.name}
+                labelIcon={property.description ?
+                    <Popover
+                        position={"left"}
+                        headerContent={property.displayName}
+                        bodyContent={property.description}
+                        footerContent={property.defaultValue !== undefined ? "Default: " + property.defaultValue : undefined}>
+                        <button type="button" aria-label="More info" onClick={e => {
+                            e.preventDefault();
+                            e.stopPropagation();
+                        }}
+                                className="pf-c-form__group-label-help">
+                            <HelpIcon noVerticalAlign/>
+                        </button>
+                    </Popover> : <div></div>
+                }>
+                {['string', 'duration', 'integer', 'number'].includes(property.type) && !property.enumVals &&
+                <TextInput
+                    // isReadOnly={property.name === 'uri' && this.state.element?.dslName !== 'toD'}
+                    className="text-field" isRequired
+                    type={['integer', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
+                    id={property.name} name={property.name}
+                    value={value?.toString()}
+                    onChange={e => this.propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(e) : e)}/>
+                }
+                {property.type === 'boolean' && <Switch
+                    id={property.name} name={property.name}
+                    value={value?.toString()}
+                    aria-label={property.name}
+                    isChecked={Boolean(value) === true}
+                    onChange={e => this.propertyChanged(property.name, !Boolean(value))}/>
+                }
+
+                {property.enumVals &&
+                <Select
+                    variant={SelectVariant.single}
+                    aria-label={property.name}
+                    onToggle={isExpanded => {
+                        this.openSelect(property.name)
+                    }}
+                    onSelect={(e, value, isPlaceholder) => this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
+                    selections={value}
+                    isOpen={this.isSelectOpen(property.name)}
+                    aria-labelledby={property.name}
+                    direction={SelectDirection.down}
+                >
+                    {selectOptions}
+                </Select>
+                }
+                {property.name === 'expression' && property.type === "Expression" &&
+                <div className="expression">
+                    <ExpressionField property={property} value={value} onExpressionChange={this.props.onExpressionChange} />
+                </div>
+                }
+                {property.name === 'parameters' &&
+                <div className="parameters">
+                    {!isKamelet && CamelUi.getComponentProperties(this.props.element, false).map(kp =>
+                        <ComponentParameterField
+                            property={kp}
+                            value={CamelApiExt.getParametersValue(this.props.element, kp.name, kp.kind === 'path')}
+                            onParameterChange={this.props.onParameterChange}
+                        />)}
+                    {isKamelet && CamelUi.getKameletProperties(this.props.element).map(property =>
+                         <KameletPropertyField
+                            property={property}
+                            value={CamelApiExt.getParametersValue(this.props.element, property.id)}
+                            onParameterChange={this.props.onParameterChange}
+                        />)}
+                </div>
+                }
+                {property.name === 'parameters' && this.props.element && !isKamelet && CamelUi.getComponentProperties(this.props.element, true).length > 0 && (
+                    <ExpandableSection
+                        toggleText={'Advanced parameters'}
+                        onToggle={isExpanded => this.setState({isShowAdvanced: !this.state.isShowAdvanced})}
+                        isExpanded={this.state.isShowAdvanced}>
+                        <div className="parameters">
+                            {CamelUi.getComponentProperties(this.props.element, true).map(kp =>
+                                <ComponentParameterField
+                                    property={kp}
+                                    value={CamelApiExt.getParametersValue(this.props.element, kp.name, kp.kind === 'path')}
+                                    onParameterChange={this.props.onParameterChange}
+                                />
+                            )}
+                        </div>
+                    </ExpandableSection>
+                )}
+            </FormGroup>
+        )
+    }
+}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/ui/field/ExpressionField.tsx b/karavan-designer/src/designer/ui/field/ExpressionField.tsx
new file mode 100644
index 0000000..62eb70c
--- /dev/null
+++ b/karavan-designer/src/designer/ui/field/ExpressionField.tsx
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    FormGroup,
+    Popover,
+    Select,
+    SelectVariant,
+    SelectDirection,
+    SelectOption, TextArea,
+} from '@patternfly/react-core';
+import '../../karavan.css';
+import "@patternfly/patternfly/patternfly.css";
+import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
+import {Languages, PropertyMeta} from "../../api/CamelMetadata";
+import {CamelApiExt} from "../../api/CamelApiExt";
+
+interface Props {
+    property: PropertyMeta,
+    value: any,
+    onExpressionChange?: (language: string, value: string | undefined) => void
+}
+
+interface State {
+    selectIsOpen: boolean;
+}
+
+export class ExpressionField extends React.Component<Props, State> {
+
+    public state: State = {
+        selectIsOpen: false,
+    }
+
+    openSelect = () => {
+        this.setState({selectIsOpen: true});
+    }
+
+    expressionChanged = (language: string, value: string | undefined) => {
+        this.props.onExpressionChange?.call(this, language, value);
+        this.setState({selectIsOpen: false});
+    }
+
+    render() {
+        const property: PropertyMeta = this.props.property;
+        const prefix = "language";
+        const language = CamelApiExt.getExpressionLanguage(this.props.value) || 'Simple'
+        const dslLanguage = Languages.find((l: [string, string, string]) => l[0] === language);
+        const value = language ? CamelApiExt.getExpressionValue(this.props.value) : undefined;
+        const selectOptions: JSX.Element[] = []
+        Languages.forEach((lang: [string, string, string]) => {
+            const s = <SelectOption key={lang[0]} value={lang[0]} description={lang[2]}/>;
+            selectOptions.push(s);
+        })
+        return (
+            <div>
+                <FormGroup key={prefix + "-" + property.name} fieldId={property.name}>
+                    <Select
+                        variant={SelectVariant.typeahead}
+                        aria-label={property.name}
+                        onToggle={isExpanded => {
+                            this.openSelect()
+                        }}
+                        onSelect={(e, lang, isPlaceholder) => this.expressionChanged(lang.toString(), value)}
+                        selections={dslLanguage}
+                        isOpen={this.state.selectIsOpen}
+                        aria-labelledby={property.name}
+                        direction={SelectDirection.down}
+                    >
+                        {selectOptions}
+                    </Select>
+                </FormGroup>
+                <FormGroup
+                    key={property.name}
+                    fieldId={property.name}
+                    labelIcon={property.description ?
+                        <Popover
+                            position={"left"}
+                            headerContent={property.displayName}
+                            bodyContent={property.description}>
+                            <button type="button" aria-label="More info" onClick={e => {
+                                e.preventDefault();
+                                e.stopPropagation();
+                            }}
+                                    className="pf-c-form__group-label-help">
+                                <HelpIcon noVerticalAlign/>
+                            </button>
+                        </Popover> : <div></div>
+                    }>
+                    <TextArea
+                        autoResize
+                        className="text-field" isRequired
+                        type={"text"}
+                        id={property.name} name={property.name}
+                        height={"100px"}
+                        value={value?.toString()}
+                        onChange={e => this.expressionChanged(language, e)}/>
+                </FormGroup>
+            </div>
+        )
+    }
+}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/ui/field/KameletPropertyField.tsx b/karavan-designer/src/designer/ui/field/KameletPropertyField.tsx
new file mode 100644
index 0000000..78ed047
--- /dev/null
+++ b/karavan-designer/src/designer/ui/field/KameletPropertyField.tsx
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    FormGroup,
+    TextInput,
+    Popover,
+    Switch,
+} from '@patternfly/react-core';
+import '../../karavan.css';
+import "@patternfly/patternfly/patternfly.css";
+import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
+import {Property} from "../../model/KameletModels";
+
+interface Props {
+    property: Property,
+    value: any,
+    onParameterChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean) => void
+}
+
+interface State {
+    selectIsOpen: boolean
+}
+
+export class KameletPropertyField extends React.Component<Props, State> {
+
+    public state: State = {
+        selectIsOpen: false,
+    }
+
+    openSelect = () => {
+        this.setState({selectIsOpen: true});
+    }
+
+    parametersChanged = (parameter: string, value: string | number | boolean | any, pathParameter?: boolean) => {
+        this.props.onParameterChange?.call(this, parameter, value, pathParameter);
+        this.setState({selectIsOpen: false});
+    }
+
+    render() {
+        const property = this.props.property;
+        const value = this.props.value;
+        const prefix = "parameters";
+        const id = prefix + "-" + property.id;
+        return (
+            <FormGroup
+                key={id}
+                label={property.title}
+                fieldId={id}
+                labelIcon={
+                    <Popover
+                        position={"left"}
+                        headerContent={property.title}
+                        bodyContent={property.description}
+                        footerContent={
+                            <div>
+                                {property.default !== undefined &&
+                                <div>Default: {property.default.toString()}</div>}
+                                {property.example !== undefined && <div>Example: {property.example}</div>}
+                            </div>
+                        }>
+                        <button type="button" aria-label="More info" onClick={e => e.preventDefault()}
+                                className="pf-c-form__group-label-help">
+                            <HelpIcon noVerticalAlign/>
+                        </button>
+                    </Popover>
+                }>
+                {['string', 'integer', 'int', 'number'].includes(property.type) && <TextInput
+                    className="text-field" isRequired
+                    type={['integer', 'int', 'number'].includes(property.type) ? 'number' : (property.format ? "password" : "text")}
+                    id={id} name={id}
+                    value={value}
+                    onChange={e => this.parametersChanged(property.id, ['integer', 'int', 'number'].includes(property.type) ? Number(e) : e)}/>
+                }
+                {property.type === 'boolean' && <Switch
+                    id={id} name={id}
+                    value={value?.toString()}
+                    aria-label={id}
+                    isChecked={Boolean(value) === true}
+                    onChange={e => this.parametersChanged(property.id, !Boolean(value))}/>
+                }
+            </FormGroup>
+        )
+    }
+}
\ No newline at end of file