From 7b447cc2cde2713d63a3c22c23c077618654c0bd Mon Sep 17 00:00:00 2001
From: Shivam Gupta
Date: Fri, 2 Aug 2024 02:25:08 +0530
Subject: [PATCH] feat(form): Enable 'Required' field feature in the canvas
form
---
.../basicNodeActions/stepDisable.cy.ts | 1 +
.../branchingFlows/branchingStepActions.cy.ts | 1 +
.../rootContainersConf.cy.ts | 3 +
.../sidepanelConfig/modelineConf.cy.ts | 1 +
.../sidepanelConfig/propertiesFilter.cy.ts | 9 +-
.../sidepanelConfig/routeBeanConf.cy.ts | 6 +
.../sidepanelConfig/stepConfiguration.cy.ts | 2 +
.../sidepanelConfig/uriRouteConfig.cy.ts | 3 +
.../specialCamelRoutes/errorHandler.cy.ts | 2 +
.../interceptSendToEndpoint.cy.ts | 1 +
.../specialCamelRoutes/onCompletion.cy.ts | 1 +
.../specialCamelRoutes/onException.cy.ts | 1 +
.../restConfiguration.cy.ts | 1 +
.../routeConfiguration.cy.ts | 1 +
.../dataFormatStepConfig.cy.ts | 2 +
.../expressionStepConfig.cy.ts | 5 +
.../loadBalancerConfig.cy.ts | 1 +
.../setHeadersStepConfig.cy.ts | 1 +
.../ui-tests/cypress/support/cypress.d.ts | 1 +
.../next-commands/nodeConfiguration.ts | 6 +
.../src/components/Form/CustomAutoFields.tsx | 16 +-
.../ui/src/components/Form/NoFieldFound.tsx | 21 +
.../Form/dataFormat/DataFormatEditor.test.tsx | 45 +
.../Form/dataFormat/DataFormatEditor.tsx | 24 +-
.../loadBalancer/LoadBalancerEditor.test.tsx | 40 +
.../Form/loadBalancer/LoadBalancerEditor.tsx | 24 +-
.../StepExpressionEditor.test.tsx | 60 +-
.../stepExpression/StepExpressionEditor.tsx | 26 +-
.../Visualization/Canvas/CanvasForm.scss | 16 +-
.../Visualization/Canvas/CanvasForm.test.tsx | 527 ++++++-
.../Visualization/Canvas/CanvasForm.tsx | 30 +-
.../Canvas/CanvasFormTabs.test.tsx | 365 +----
.../Visualization/Canvas/CanvasFormTabs.tsx | 29 +-
.../Canvas/Form/CanvasFormHeader.scss | 1 +
.../__snapshots__/CanvasForm.test.tsx.snap | 1216 +++++------------
.../Canvas/canvasformtabs.modes.ts | 24 +-
.../Visualization/Visualization.tsx | 5 +-
.../canvas-form-tabs.provider.test.tsx | 64 +
.../providers/canvas-form-tabs.provider.tsx | 49 +
packages/ui/src/providers/index.ts | 1 +
.../get-required-properties-schema.test.ts | 212 +++
.../utils/get-required-properties-schema.ts | 30 +
packages/ui/src/utils/index.ts | 1 +
43 files changed, 1609 insertions(+), 1266 deletions(-)
create mode 100644 packages/ui/src/components/Form/NoFieldFound.tsx
create mode 100644 packages/ui/src/providers/canvas-form-tabs.provider.test.tsx
create mode 100644 packages/ui/src/providers/canvas-form-tabs.provider.tsx
create mode 100644 packages/ui/src/utils/get-required-properties-schema.test.ts
create mode 100644 packages/ui/src/utils/get-required-properties-schema.ts
diff --git a/packages/ui-tests/cypress/e2e/designer/basicNodeActions/stepDisable.cy.ts b/packages/ui-tests/cypress/e2e/designer/basicNodeActions/stepDisable.cy.ts
index 63104c08c..4745de6bb 100644
--- a/packages/ui-tests/cypress/e2e/designer/basicNodeActions/stepDisable.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/basicNodeActions/stepDisable.cy.ts
@@ -9,6 +9,7 @@ describe('Tests for Design page', () => {
cy.selectDisableNode('setHeader');
cy.openStepConfigurationTab('setHeader');
+ cy.selectFormTab('All');
cy.checkConfigCheckboxObject('disabled', true);
cy.openSourceCode();
diff --git a/packages/ui-tests/cypress/e2e/designer/branchingFlows/branchingStepActions.cy.ts b/packages/ui-tests/cypress/e2e/designer/branchingFlows/branchingStepActions.cy.ts
index d2dd2dc42..e22a0939c 100644
--- a/packages/ui-tests/cypress/e2e/designer/branchingFlows/branchingStepActions.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/branchingFlows/branchingStepActions.cy.ts
@@ -8,6 +8,7 @@ describe('User completes normal actions on steps in a branch', () => {
cy.openDesignPage();
cy.openStepConfigurationTab('marshal');
+ cy.selectFormTab('All');
cy.interactWithConfigInputObject('allowJmsType');
cy.interactWithConfigInputObject('collectionType', 'collection Type');
diff --git a/packages/ui-tests/cypress/e2e/designer/rootContainerConfig/rootContainersConf.cy.ts b/packages/ui-tests/cypress/e2e/designer/rootContainerConfig/rootContainersConf.cy.ts
index 8bae9dfa0..8607206d9 100644
--- a/packages/ui-tests/cypress/e2e/designer/rootContainerConfig/rootContainersConf.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/rootContainerConfig/rootContainersConf.cy.ts
@@ -39,6 +39,7 @@ describe('Test for camel route root containers configuration', () => {
cy.openStepConfigurationTab('camel-route');
+ cy.selectFormTab('All');
cy.interactWithConfigInputObject('description', 'test.description');
cy.interactWithConfigInputObject('group', 'test.group');
cy.interactWithConfigInputObject('inputType.description', 'test.inputType.description');
@@ -91,6 +92,7 @@ describe('Test for camel route root containers configuration', () => {
cy.openStepConfigurationTab('eip-action');
+ cy.selectFormTab('All');
cy.interactWithConfigInputObject('name', 'test.name');
cy.interactWithConfigInputObject('title', 'test.title');
cy.interactWithConfigInputObject('description', 'test.description');
@@ -130,6 +132,7 @@ describe('Test for camel route root containers configuration', () => {
cy.openStepConfigurationTab('pipe');
+ cy.selectFormTab('All');
cy.get(`input[name="name"]`).clear();
cy.get(`input[name="name"]`).type('testName');
diff --git a/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/modelineConf.cy.ts b/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/modelineConf.cy.ts
index 063f0ea21..e410cd7a7 100644
--- a/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/modelineConf.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/modelineConf.cy.ts
@@ -9,6 +9,7 @@ describe('Tests for modeline', () => {
cy.removeNodeByName('https');
cy.openStepConfigurationTab('kamelet:log-sink');
+ cy.selectFormTab('All');
cy.get(`input[name="parameters.showProperties"]`).check();
cy.openSourceCode();
diff --git a/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/propertiesFilter.cy.ts b/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/propertiesFilter.cy.ts
index 76152681f..80aa5dc7b 100644
--- a/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/propertiesFilter.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/propertiesFilter.cy.ts
@@ -7,7 +7,7 @@ describe('Tests for side panel step filtering', () => {
cy.uploadFixture('flows/camelRoute/basic.yaml');
cy.openDesignPage();
cy.openStepConfigurationTab('setHeader');
-
+ cy.selectFormTab('All');
// expand wrapped section
cy.contains('button', 'Processor advanced properties').click();
@@ -32,6 +32,7 @@ describe('Tests for side panel step filtering', () => {
cy.uploadFixture('flows/camelRoute/basic.yaml');
cy.openDesignPage();
cy.openStepConfigurationTab('setHeader');
+ cy.selectFormTab('All');
// expand wrapped section
cy.contains('button', 'Processor advanced properties').click();
@@ -57,6 +58,7 @@ describe('Tests for side panel step filtering', () => {
cy.uploadFixture('flows/camelRoute/basic.yaml');
cy.openDesignPage();
cy.openStepConfigurationTab('log');
+ cy.selectFormTab('All');
// check all fields are present
cy.get(`input[name="id"]`).should('exist');
@@ -75,18 +77,19 @@ describe('Tests for side panel step filtering', () => {
cy.uploadFixture('flows/camelRoute/basic.yaml');
cy.openDesignPage();
cy.openStepConfigurationTab('log');
+ cy.selectFormTab('All');
cy.interactWithConfigInputObject('variableSend', 'testVariableSend');
cy.interactWithConfigInputObject('variableReceive', 'testVariableReceive');
- cy.get('button[id="User Modified"]').click();
+ cy.selectFormTab('Modified');
cy.get(`input[name="variableSend"]`).should('exist');
cy.get(`input[name="variableReceive"]`).should('exist');
cy.get(`textarea[name="description"]`).should('not.exist');
cy.get(`input[name="id"]`).should('not.exist');
- cy.get('button[id="All Fields"]').click();
+ cy.selectFormTab('All');
cy.get(`input[name="variableSend"]`).should('exist');
cy.get(`input[name="variableReceive"]`).should('exist');
diff --git a/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/routeBeanConf.cy.ts b/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/routeBeanConf.cy.ts
index 36304f3e9..3e8d91072 100644
--- a/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/routeBeanConf.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/routeBeanConf.cy.ts
@@ -13,6 +13,7 @@ describe('Test for node bean reference and configuration support', () => {
cy.openDesignPage();
cy.openStepConfigurationTab('sql');
+ cy.selectFormTab('All');
cy.configureNewBeanReference('parameters.dataSource');
cy.get(`input[name="name"]`).clear().type('test');
cy.get(`input[name="type"]`).clear().type('org.acme');
@@ -55,6 +56,7 @@ describe('Test for node bean reference and configuration support', () => {
cy.openDesignPage();
cy.openStepConfigurationTab('sql');
+ cy.selectFormTab('All');
cy.configureBeanReference('parameters.dataSource', data.dataSource);
cy.openSourceCode();
@@ -68,12 +70,14 @@ describe('Test for node bean reference and configuration support', () => {
cy.openDesignPage();
cy.openStepConfigurationTab('sql');
+ cy.selectFormTab('All');
cy.configureBeanReference('parameters.dataSource', data.dataSource);
cy.openSourceCode();
cy.checkCodeSpanLine('dataSource: "' + data.dataSource + '"');
cy.openDesignPage();
cy.openStepConfigurationTab('sql');
+ cy.selectFormTab('All');
cy.deselectNodeBean('parameters.dataSource');
cy.openSourceCode();
cy.checkCodeSpanLine('dataSource: "' + data.dataSource + '"', 0);
@@ -85,6 +89,7 @@ describe('Test for node bean reference and configuration support', () => {
cy.uploadFixture('flows/camelRoute/sqlBeans.yaml');
cy.openDesignPage();
cy.openStepConfigurationTab('sql');
+ cy.selectFormTab('All');
cy.configureBeanReference('parameters.dataSource', 'postgreSqlSource');
cy.openBeans();
@@ -93,6 +98,7 @@ describe('Test for node bean reference and configuration support', () => {
cy.get('[data-testid="metadata-row-1"]').should('not.exist');
cy.openDesignPage();
cy.openStepConfigurationTab('sql');
+ cy.selectFormTab('All');
cy.get(`div[data-fieldname="parameters.dataSource"] input[value="#postgreSqlSource"]`).should('not.exist');
});
});
diff --git a/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/stepConfiguration.cy.ts b/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/stepConfiguration.cy.ts
index 9fa7799b8..24feb6cbb 100644
--- a/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/stepConfiguration.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/stepConfiguration.cy.ts
@@ -8,6 +8,7 @@ describe('Tests for Design page', () => {
cy.openDesignPage();
// Configure timer - source step
cy.openStepConfigurationTab('timer-source');
+ cy.selectFormTab('All');
cy.interactWithConfigInputObject('period', '3000');
cy.interactWithConfigInputObject('message', 'test message');
cy.get(`input[name="message"]`).clear();
@@ -15,6 +16,7 @@ describe('Tests for Design page', () => {
// Configure kafka-sink step
cy.openStepConfigurationTab('kafka-sink');
+ cy.selectFormTab('All');
cy.interactWithConfigInputObject('topic', 'topicname');
cy.interactWithConfigInputObject('bootstrapServers', 'bootstrap');
cy.interactWithConfigInputObject('securityProtocol', 'security');
diff --git a/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/uriRouteConfig.cy.ts b/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/uriRouteConfig.cy.ts
index 27d6c55ae..970d81d3e 100644
--- a/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/uriRouteConfig.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/sidepanelConfig/uriRouteConfig.cy.ts
@@ -17,6 +17,7 @@ describe('Test URI node config', () => {
cy.checkNodeExist('file', 1);
cy.openStepConfigurationTab('timer');
+ cy.selectFormTab('All');
cy.checkConfigInputObject('parameters.period', '1000');
cy.checkConfigInputObject('parameters.delay', '2000');
cy.checkConfigInputObject('parameters.repeatCount', '10');
@@ -49,6 +50,7 @@ describe('Test URI node config', () => {
// CHECK the aws2-s3 properties
cy.openStepConfigurationTab('aws2-s3');
+ cy.selectFormTab('All');
cy.checkConfigCheckboxObject('parameters.autoCreateBucket', true);
cy.checkConfigInputObject('parameters.bucketNameOrArn', 'testBucket');
});
@@ -69,6 +71,7 @@ describe('Test URI node config', () => {
// CHECK the aws2-s3 properties
cy.openStepConfigurationTab('aws2-s3');
+ cy.selectFormTab('All');
cy.checkConfigCheckboxObject('parameters.autoCreateBucket', true);
cy.checkConfigInputObject('parameters.bucketNameOrArn', 'testBucket');
});
diff --git a/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/errorHandler.cy.ts b/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/errorHandler.cy.ts
index 6bb8a943c..4e4645bf8 100644
--- a/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/errorHandler.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/errorHandler.cy.ts
@@ -10,6 +10,7 @@ describe('Test for errorHandler configuration container', () => {
.find('.pf-topology__node__label')
.find('.pf-topology__node__label__background')
.click();
+ cy.selectFormTab('All');
cy.get('#-oneof-toggle').click();
cy.get('ul.pf-v5-c-menu__list > li:first')
@@ -33,6 +34,7 @@ describe('Test for errorHandler configuration container', () => {
.find('.pf-topology__node__label')
.find('.pf-topology__node__label__background')
.click();
+ cy.selectFormTab('All');
cy.get('#-oneof-toggle').click();
cy.get('[data-testid="-oneof-select-dropdownlist-Default Error Handler"]').click();
diff --git a/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/interceptSendToEndpoint.cy.ts b/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/interceptSendToEndpoint.cy.ts
index 76a2ce6c5..d379b3a06 100644
--- a/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/interceptSendToEndpoint.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/interceptSendToEndpoint.cy.ts
@@ -10,6 +10,7 @@ describe('Test for interceptSendToEndpoint configuration container', () => {
.find('.pf-topology__node__label')
.find('.pf-topology__node__label__background')
.click();
+ cy.selectFormTab('All');
cy.interactWithConfigInputObject('description', 'testDescription');
cy.interactWithConfigInputObject('skipSendToOriginalEndpoint', 'testSkipSendToOriginalEndpoint');
diff --git a/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/onCompletion.cy.ts b/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/onCompletion.cy.ts
index 82f294de8..a21123d24 100644
--- a/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/onCompletion.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/onCompletion.cy.ts
@@ -10,6 +10,7 @@ describe('Test for onCompletion configuration container', () => {
.find('.pf-topology__node__label')
.find('.pf-topology__node__label__background')
.click();
+ cy.selectFormTab('All');
cy.interactWithConfigInputObject('description', 'testDescription');
cy.interactWithConfigInputObject('onCompleteOnly');
diff --git a/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/onException.cy.ts b/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/onException.cy.ts
index cb544bf2c..38eee19c7 100644
--- a/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/onException.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/onException.cy.ts
@@ -33,6 +33,7 @@ describe('Test for root on exception container', () => {
.find('.pf-topology__node__label')
.find('.pf-topology__node__label__background')
.click();
+ cy.selectFormTab('All');
cy.selectInTypeaheadField('redeliveryPolicy.retriesExhaustedLogLevel', 'INFO');
cy.selectInTypeaheadField('redeliveryPolicy.retryAttemptedLogLevel', 'INFO');
diff --git a/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/restConfiguration.cy.ts b/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/restConfiguration.cy.ts
index 44a0ae50b..68082cd95 100644
--- a/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/restConfiguration.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/restConfiguration.cy.ts
@@ -10,6 +10,7 @@ describe('Test for root on rest configuration container', () => {
.find('.pf-topology__node__label')
.find('.pf-topology__node__label__background')
.click();
+ cy.selectFormTab('All');
cy.selectInTypeaheadField('component', 'coap');
cy.selectInTypeaheadField('apiComponent', 'openapi');
diff --git a/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/routeConfiguration.cy.ts b/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/routeConfiguration.cy.ts
index 6c8f9776c..0da2e913f 100644
--- a/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/routeConfiguration.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/specialCamelRoutes/routeConfiguration.cy.ts
@@ -10,6 +10,7 @@ describe('Test for root route configuration container', () => {
.find('.pf-topology__node__label')
.find('.pf-topology__node__label__background')
.click();
+ cy.selectFormTab('All');
cy.interactWithConfigInputObject('description', 'testDescription');
cy.interactWithConfigInputObject('errorHandler.id', 'testErrorHandlerId');
diff --git a/packages/ui-tests/cypress/e2e/designer/specialStepConfiguration/dataFormatStepConfig.cy.ts b/packages/ui-tests/cypress/e2e/designer/specialStepConfiguration/dataFormatStepConfig.cy.ts
index f20d00154..eb44c8bc7 100644
--- a/packages/ui-tests/cypress/e2e/designer/specialStepConfiguration/dataFormatStepConfig.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/specialStepConfiguration/dataFormatStepConfig.cy.ts
@@ -9,6 +9,7 @@ describe('Tests for sidebar dataformat configuration', () => {
// Configure marshal dataformat
cy.openStepConfigurationTab('marshal');
+ cy.selectFormTab('All');
cy.selectDataformat('Base64');
cy.interactWithDataformatInputObject('lineLength', '128');
cy.interactWithDataformatInputObject('id', 'simpleDataformatId');
@@ -29,6 +30,7 @@ describe('Tests for sidebar dataformat configuration', () => {
// Configure marshal dataformat
cy.openStepConfigurationTab('marshal');
+ cy.selectFormTab('All');
cy.selectDataformat('Avro');
cy.configureDropdownValue('library', 'avroJackson');
cy.interactWithDataformatInputObject('unmarshalType', 'com.fasterxml.jackson.databind.JsonNode');
diff --git a/packages/ui-tests/cypress/e2e/designer/specialStepConfiguration/expressionStepConfig.cy.ts b/packages/ui-tests/cypress/e2e/designer/specialStepConfiguration/expressionStepConfig.cy.ts
index c08c7f163..596dc95db 100644
--- a/packages/ui-tests/cypress/e2e/designer/specialStepConfiguration/expressionStepConfig.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/specialStepConfiguration/expressionStepConfig.cy.ts
@@ -8,6 +8,7 @@ describe('Tests for sidebar expression configuration', () => {
cy.openDesignPage();
// Configure setHeader expression
cy.openStepConfigurationTab('setHeader');
+ cy.selectFormTab('All');
cy.selectExpression('Simple');
cy.interactWithExpressionInputObject('expression', `{{}{{}header.baz}}`);
cy.interactWithExpressionInputObject('id', 'simpleExpressionId');
@@ -25,6 +26,7 @@ describe('Tests for sidebar expression configuration', () => {
cy.openDesignPage();
cy.openStepConfigurationTab('setHeader');
+ cy.selectFormTab('All');
cy.selectExpression('JQ');
cy.interactWithConfigInputObject('expression', '.id');
cy.addExpressionResultType('java.lang.String');
@@ -36,6 +38,7 @@ describe('Tests for sidebar expression configuration', () => {
cy.checkNodeExist('setHeader', 2);
cy.openStepConfigurationTab('setHeader', 1);
+ cy.selectFormTab('All');
cy.selectExpression('JQ');
cy.interactWithConfigInputObject('expression', '.name');
cy.addExpressionResultType('java.lang.String');
@@ -66,6 +69,7 @@ describe('Tests for sidebar expression configuration', () => {
cy.openDesignPage();
// Configure setHeader expression
cy.openStepConfigurationTab('setHeader');
+ cy.selectFormTab('All');
cy.selectExpression('Simple');
cy.interactWithExpressionInputObject('expression', `{{}{{}header.baz}}`);
cy.get('textarea[name="expression"]').should('have.value', '{{header.baz}}');
@@ -83,6 +87,7 @@ describe('Tests for sidebar expression configuration', () => {
cy.openDesignPage();
// Configure setBody expression
cy.openStepConfigurationTab('setBody');
+ cy.selectFormTab('All');
cy.selectExpression('Simple');
cy.interactWithExpressionInputObject('expression', `{{}{{}body.baz}}`);
cy.interactWithExpressionInputObject('id', 'simpleExpressionId');
diff --git a/packages/ui-tests/cypress/e2e/designer/specialStepConfiguration/loadBalancerConfig.cy.ts b/packages/ui-tests/cypress/e2e/designer/specialStepConfiguration/loadBalancerConfig.cy.ts
index 8bc66de2a..e9f680125 100644
--- a/packages/ui-tests/cypress/e2e/designer/specialStepConfiguration/loadBalancerConfig.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/specialStepConfiguration/loadBalancerConfig.cy.ts
@@ -10,6 +10,7 @@ describe('Tests for sidebar loadBalancer step configuration', () => {
cy.selectAppendNode('marshal');
cy.chooseFromCatalog('processor', 'loadBalance');
cy.openStepConfigurationTab('loadBalance');
+ cy.selectFormTab('All');
cy.get('[data-testid="loadbalancer-config-card"] button').click();
cy.get('[data-testid="loadbalancer-dropdownitem-roundRobinLoadBalancer"] button').click();
diff --git a/packages/ui-tests/cypress/e2e/designer/specialStepConfiguration/setHeadersStepConfig.cy.ts b/packages/ui-tests/cypress/e2e/designer/specialStepConfiguration/setHeadersStepConfig.cy.ts
index 159e5e6a7..da6f3a6ed 100644
--- a/packages/ui-tests/cypress/e2e/designer/specialStepConfiguration/setHeadersStepConfig.cy.ts
+++ b/packages/ui-tests/cypress/e2e/designer/specialStepConfiguration/setHeadersStepConfig.cy.ts
@@ -10,6 +10,7 @@ describe('Tests for sidebar setHeaders step configuration', () => {
cy.selectAppendNode('marshal');
cy.chooseFromCatalog('processor', 'setHeaders');
cy.openStepConfigurationTab('setHeaders');
+ cy.selectFormTab('All');
cy.get('[data-testid="list-add-field"]').click();
diff --git a/packages/ui-tests/cypress/support/cypress.d.ts b/packages/ui-tests/cypress/support/cypress.d.ts
index 9a9727741..af3366e22 100644
--- a/packages/ui-tests/cypress/support/cypress.d.ts
+++ b/packages/ui-tests/cypress/support/cypress.d.ts
@@ -81,6 +81,7 @@ declare global {
addProperty(propertyName: string): Chainable>;
addSingleKVProperty(propertyName: string, key: string, value: string): Chainable>;
filterFields(filter: string): Chainable>;
+ selectFormTab(tab: string): Chainable>;
// metadata
expandWrappedSection(sectionName: string): Chainable>;
closeWrappedSection(sectionName: string): Chainable>;
diff --git a/packages/ui-tests/cypress/support/next-commands/nodeConfiguration.ts b/packages/ui-tests/cypress/support/next-commands/nodeConfiguration.ts
index 9068f64e8..10387e5e6 100644
--- a/packages/ui-tests/cypress/support/next-commands/nodeConfiguration.ts
+++ b/packages/ui-tests/cypress/support/next-commands/nodeConfiguration.ts
@@ -139,3 +139,9 @@ Cypress.Commands.add('filterFields', (filter: string) => {
cy.get('input.pf-v5-c-text-input-group__text-input').type(filter);
});
});
+
+Cypress.Commands.add('selectFormTab', (value: string) => {
+ cy.get('div.form-tabs').within(() => {
+ cy.get(`[id$="${value}"]`).click();
+ });
+});
diff --git a/packages/ui/src/components/Form/CustomAutoFields.tsx b/packages/ui/src/components/Form/CustomAutoFields.tsx
index 3e7e9b07a..22d124e47 100644
--- a/packages/ui/src/components/Form/CustomAutoFields.tsx
+++ b/packages/ui/src/components/Form/CustomAutoFields.tsx
@@ -5,9 +5,11 @@ import { KaotoSchemaDefinition } from '../../models';
import { Card, CardBody } from '@patternfly/react-core';
import { getFieldGroups } from '../../utils';
import { CatalogKind } from '../../models';
-import { FilteredFieldContext } from '../../providers';
+import { CanvasFormTabsContext, FilteredFieldContext } from '../../providers';
import './CustomAutoFields.scss';
import { CustomExpandableSection } from './customField/CustomExpandableSection';
+import { NoFieldFound } from './NoFieldFound';
+import { FormTabsModes } from '../Visualization/Canvas';
export type AutoFieldsProps = {
autoField?: ComponentType<{ name: string }>;
@@ -26,6 +28,7 @@ export function CustomAutoFields({
const { schema } = useForm();
const rootField = schema.getField('');
const { filteredFieldText, isGroupExpanded } = useContext(FilteredFieldContext);
+ const { selectedTab } = useContext(CanvasFormTabsContext);
/** Special handling for oneOf schemas */
if (Array.isArray((rootField as KaotoSchemaDefinition['schema']).oneOf)) {
@@ -42,6 +45,17 @@ export function CustomAutoFields({
}, {});
const propertiesArray = getFieldGroups(actualFieldsSchema);
+ if (
+ selectedTab !== FormTabsModes.ALL_FIELDS &&
+ propertiesArray.common.length === 0 &&
+ Object.keys(propertiesArray.groups).length === 0
+ ) {
+ const comment = (rootField as KaotoSchemaDefinition['schema'])['$comment'] ?? '';
+ if (!comment.includes('expression') && !comment.includes('dataformat') && !comment.includes('loadbalance')) {
+ return ;
+ }
+ }
+
return createElement(
element,
props,
diff --git a/packages/ui/src/components/Form/NoFieldFound.tsx b/packages/ui/src/components/Form/NoFieldFound.tsx
new file mode 100644
index 000000000..af41b1716
--- /dev/null
+++ b/packages/ui/src/components/Form/NoFieldFound.tsx
@@ -0,0 +1,21 @@
+import { Alert, Card, CardBody, Button } from '@patternfly/react-core';
+import { FunctionComponent, useContext } from 'react';
+import { CanvasFormTabsContext } from '../../providers';
+import { FormTabsModes } from '../Visualization/Canvas';
+
+export const NoFieldFound: FunctionComponent = () => {
+ const { selectedTab, onTabChange } = useContext(CanvasFormTabsContext);
+ return (
+
+
+
+ No field found matching this criteria. Please switch to the{' '}
+ {' '}
+ tab.
+
+
+
+ );
+};
diff --git a/packages/ui/src/components/Form/dataFormat/DataFormatEditor.test.tsx b/packages/ui/src/components/Form/dataFormat/DataFormatEditor.test.tsx
index 36857a1eb..69cf48418 100644
--- a/packages/ui/src/components/Form/dataFormat/DataFormatEditor.test.tsx
+++ b/packages/ui/src/components/Form/dataFormat/DataFormatEditor.test.tsx
@@ -94,6 +94,51 @@ describe('DataFormatEditor', () => {
expect(inputIdModifiedTabElement).toHaveLength(1);
});
+ it('should render with only the Required fields', () => {
+ const visualComponentSchema: VisualComponentSchema = {
+ title: 'My Node',
+ schema: {
+ type: 'object',
+ properties: {
+ name: {
+ type: 'string',
+ },
+ },
+ } as unknown as KaotoSchemaDefinition['schema'],
+ definition: {
+ name: 'my node',
+ beanio: {},
+ },
+ };
+
+ mockNode = {
+ id: '1',
+ type: 'node',
+ data: {
+ vizNode: {
+ getComponentSchema: () => visualComponentSchema,
+ updateModel: (_value: unknown) => {},
+ } as IVisualizationNode,
+ },
+ };
+ render();
+ const buttons = screen.queryAllByRole('button', { name: 'Typeahead menu toggle' });
+ expect(buttons).toHaveLength(1);
+
+ const inputElement = screen.getAllByRole('combobox')[0];
+ expect(inputElement).toHaveValue('BeanIO');
+
+ const inputMappingElement = screen
+ .queryAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('label') === 'Mapping');
+ expect(inputMappingElement).toHaveLength(1);
+
+ const inputStreamNameElement = screen
+ .queryAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('label') === 'Stream Name');
+ expect(inputStreamNameElement).toHaveLength(1);
+ });
+
it('should render', async () => {
render();
const buttons = screen.getAllByRole('button', { name: 'Typeahead menu toggle' });
diff --git a/packages/ui/src/components/Form/dataFormat/DataFormatEditor.tsx b/packages/ui/src/components/Form/dataFormat/DataFormatEditor.tsx
index b6840bd61..87e1e56eb 100644
--- a/packages/ui/src/components/Form/dataFormat/DataFormatEditor.tsx
+++ b/packages/ui/src/components/Form/dataFormat/DataFormatEditor.tsx
@@ -12,7 +12,12 @@ import { CanvasNode } from '../../Visualization/Canvas/canvas.models';
import './DataFormatEditor.scss';
import { DataFormatService } from './dataformat.service';
import { TypeaheadEditor } from '../customField/TypeaheadEditor';
-import { getSerializedModel, getUserUpdatedPropertiesSchema, isDefined } from '../../../utils';
+import {
+ getRequiredPropertiesSchema,
+ getSerializedModel,
+ getUserUpdatedPropertiesSchema,
+ isDefined,
+} from '../../../utils';
import { FormTabsModes } from '../../Visualization/Canvas/canvasformtabs.modes';
interface DataFormatEditorProps {
@@ -61,11 +66,16 @@ export const DataFormatEditor: FunctionComponent = (props
}, [dataFormat]);
const processedSchema = useMemo(() => {
- if (props.formMode === FormTabsModes.ALL_FIELDS) return dataFormatSchema;
- return {
- ...dataFormatSchema,
- properties: getUserUpdatedPropertiesSchema(dataFormatSchema?.properties ?? {}, dataFormatModel ?? {}),
- };
+ if (props.formMode === FormTabsModes.REQUIRED_FIELDS) {
+ return getRequiredPropertiesSchema(dataFormatSchema ?? {});
+ } else if (props.formMode === FormTabsModes.ALL_FIELDS) {
+ return dataFormatSchema;
+ } else if (props.formMode === FormTabsModes.USER_MODIFIED) {
+ return {
+ ...dataFormatSchema,
+ properties: getUserUpdatedPropertiesSchema(dataFormatSchema?.properties ?? {}, dataFormatModel ?? {}),
+ };
+ }
}, [props.formMode, dataFormat]);
const handleOnChange = useCallback(
@@ -89,7 +99,7 @@ export const DataFormatEditor: FunctionComponent = (props
);
const showEditor = useMemo(() => {
- if (props.formMode === FormTabsModes.ALL_FIELDS) return true;
+ if (props.formMode === FormTabsModes.ALL_FIELDS || props.formMode === FormTabsModes.REQUIRED_FIELDS) return true;
return props.formMode === FormTabsModes.USER_MODIFIED && isDefined(selectedDataFormatOption);
}, [props.formMode]);
diff --git a/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.test.tsx b/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.test.tsx
index 23e15523f..5a8805106 100644
--- a/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.test.tsx
+++ b/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.test.tsx
@@ -95,6 +95,46 @@ describe('LoadBalancerEditor', () => {
expect(inputIdModifiedTabElement).toHaveLength(1);
});
+ it('should render with only the Required fields', () => {
+ const visualComponentSchema: VisualComponentSchema = {
+ title: 'My Node',
+ schema: {
+ type: 'object',
+ properties: {
+ name: {
+ type: 'string',
+ },
+ },
+ } as unknown as KaotoSchemaDefinition['schema'],
+ definition: {
+ name: 'my node',
+ weightedLoadBalancer: {},
+ },
+ };
+
+ mockNode = {
+ id: '1',
+ type: 'node',
+ data: {
+ vizNode: {
+ getComponentSchema: () => visualComponentSchema,
+ updateModel: (_value: unknown) => {},
+ } as IVisualizationNode,
+ },
+ };
+ render();
+ const buttons = screen.queryAllByRole('button', { name: 'Typeahead menu toggle' });
+ expect(buttons).toHaveLength(1);
+
+ const inputElement = screen.getAllByRole('combobox')[0];
+ expect(inputElement).toHaveValue('Weighted Load Balancer');
+
+ const inputDistributionRatioElement = screen
+ .queryAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('label') === 'Distribution Ratio');
+ expect(inputDistributionRatioElement).toHaveLength(1);
+ });
+
it('should render', async () => {
render();
const buttons = screen.getAllByRole('button', { name: 'Typeahead menu toggle' });
diff --git a/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.tsx b/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.tsx
index 5d37322e6..0b2c11dd6 100644
--- a/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.tsx
+++ b/packages/ui/src/components/Form/loadBalancer/LoadBalancerEditor.tsx
@@ -12,7 +12,12 @@ import { CanvasNode } from '../../Visualization/Canvas/canvas.models';
import { LoadBalancerService } from './loadbalancer.service';
import './LoadBalancerEditor.scss';
import { TypeaheadEditor } from '../customField/TypeaheadEditor';
-import { getSerializedModel, getUserUpdatedPropertiesSchema, isDefined } from '../../../utils';
+import {
+ getRequiredPropertiesSchema,
+ getSerializedModel,
+ getUserUpdatedPropertiesSchema,
+ isDefined,
+} from '../../../utils';
import { FormTabsModes } from '../../Visualization/Canvas/canvasformtabs.modes';
interface LoadBalancerEditorProps {
@@ -62,11 +67,16 @@ export const LoadBalancerEditor: FunctionComponent = (p
}, [loadBalancer]);
const processedSchema = useMemo(() => {
- if (props.formMode === FormTabsModes.ALL_FIELDS) return loadBalancerSchema;
- return {
- ...loadBalancerSchema,
- properties: getUserUpdatedPropertiesSchema(loadBalancerSchema?.properties ?? {}, loadBalancerModel ?? {}),
- };
+ if (props.formMode === FormTabsModes.REQUIRED_FIELDS) {
+ return getRequiredPropertiesSchema(loadBalancerSchema ?? {});
+ } else if (props.formMode === FormTabsModes.ALL_FIELDS) {
+ return loadBalancerSchema;
+ } else if (props.formMode === FormTabsModes.USER_MODIFIED) {
+ return {
+ ...loadBalancerSchema,
+ properties: getUserUpdatedPropertiesSchema(loadBalancerSchema?.properties ?? {}, loadBalancerModel ?? {}),
+ };
+ }
}, [props.formMode, loadBalancer]);
const handleOnChange = useCallback(
@@ -90,7 +100,7 @@ export const LoadBalancerEditor: FunctionComponent = (p
);
const showEditor = useMemo(() => {
- if (props.formMode === FormTabsModes.ALL_FIELDS) return true;
+ if (props.formMode === FormTabsModes.ALL_FIELDS || props.formMode === FormTabsModes.REQUIRED_FIELDS) return true;
return props.formMode === FormTabsModes.USER_MODIFIED && isDefined(selectedLoadBalancerOption);
}, [props.formMode]);
diff --git a/packages/ui/src/components/Form/stepExpression/StepExpressionEditor.test.tsx b/packages/ui/src/components/Form/stepExpression/StepExpressionEditor.test.tsx
index 20dfe0735..62cb85a40 100644
--- a/packages/ui/src/components/Form/stepExpression/StepExpressionEditor.test.tsx
+++ b/packages/ui/src/components/Form/stepExpression/StepExpressionEditor.test.tsx
@@ -45,13 +45,57 @@ describe('StepExpressionEditor', () => {
};
});
- it('should not render', () => {
+ it('should not render under Modified tab', () => {
render();
- const launcherButton = screen.queryAllByRole('button', { name: 'Configure Expression' });
- expect(launcherButton).toHaveLength(0);
+ const dropdown = screen.queryAllByRole('button', { name: 'Typeahead menu toggle' });
+ expect(dropdown).toHaveLength(0);
});
- it('should render', async () => {
+ it('should render with only the user updated fields', () => {
+ const visualComponentSchema: VisualComponentSchema = {
+ title: 'My Node',
+ schema: {
+ type: 'object',
+ properties: {
+ name: {
+ type: 'string',
+ },
+ },
+ } as unknown as KaotoSchemaDefinition['schema'],
+ definition: {
+ name: 'my node',
+ expression: {
+ jsonpath: {
+ id: 'test',
+ },
+ },
+ },
+ };
+
+ mockNode = {
+ id: '1',
+ type: 'node',
+ data: {
+ vizNode: {
+ getComponentSchema: () => visualComponentSchema,
+ updateModel: (_value: unknown) => {},
+ } as IVisualizationNode,
+ },
+ };
+ render();
+ const buttons = screen.queryAllByRole('button', { name: 'Typeahead menu toggle' });
+ expect(buttons).toHaveLength(1);
+
+ const inputElement = screen.getAllByRole('combobox')[0];
+ expect(inputElement).toHaveValue('JSONPath');
+
+ const inputIdModifiedTabElement = screen
+ .queryAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('label') === 'Id');
+ expect(inputIdModifiedTabElement).toHaveLength(1);
+ });
+
+ it('should render under all Tab', async () => {
render();
const dropdown = screen
.getAllByTestId('typeahead-select-input')
@@ -64,4 +108,12 @@ describe('StepExpressionEditor', () => {
const form = screen.getByTestId('metadata-editor-form-expression');
expect(form.innerHTML).toContain('Suppress Exceptions');
});
+
+ it('should render under Required Tab', async () => {
+ render();
+ const dropdown = screen
+ .getAllByTestId('typeahead-select-input')
+ .filter((input) => input.innerHTML.includes(SchemaService.DROPDOWN_PLACEHOLDER));
+ expect(dropdown).toHaveLength(1);
+ });
});
diff --git a/packages/ui/src/components/Form/stepExpression/StepExpressionEditor.tsx b/packages/ui/src/components/Form/stepExpression/StepExpressionEditor.tsx
index 8a28abef7..4bb5ae5f4 100644
--- a/packages/ui/src/components/Form/stepExpression/StepExpressionEditor.tsx
+++ b/packages/ui/src/components/Form/stepExpression/StepExpressionEditor.tsx
@@ -8,11 +8,16 @@ import {
} from '@patternfly/react-core';
import { FunctionComponent, useCallback, useContext, useMemo, useState } from 'react';
import { EntitiesContext } from '../../../providers';
-import { getSerializedModel, getUserUpdatedPropertiesSchema, isDefined } from '../../../utils';
+import {
+ getRequiredPropertiesSchema,
+ getSerializedModel,
+ getUserUpdatedPropertiesSchema,
+ isDefined,
+} from '../../../utils';
import { CanvasNode } from '../../Visualization/Canvas/canvas.models';
import { TypeaheadEditor } from '../customField/TypeaheadEditor';
import { ExpressionService } from '../expression/expression.service';
-import { FormTabsModes } from '../../Visualization/Canvas';
+import { FormTabsModes } from '../../Visualization/Canvas/canvasformtabs.modes';
interface StepExpressionEditorProps {
selectedNode: CanvasNode;
@@ -65,11 +70,16 @@ export const StepExpressionEditor: FunctionComponent
}, [language]);
const processedSchema = useMemo(() => {
- if (props.formMode === FormTabsModes.ALL_FIELDS) return languageSchema;
- return {
- ...languageSchema,
- properties: getUserUpdatedPropertiesSchema(languageSchema?.properties ?? {}, languageModel ?? {}),
- };
+ if (props.formMode === FormTabsModes.REQUIRED_FIELDS) {
+ return getRequiredPropertiesSchema(languageSchema ?? {});
+ } else if (props.formMode === FormTabsModes.ALL_FIELDS) {
+ return languageSchema;
+ } else if (props.formMode === FormTabsModes.USER_MODIFIED) {
+ return {
+ ...languageSchema,
+ properties: getUserUpdatedPropertiesSchema(languageSchema?.properties ?? {}, languageModel ?? {}),
+ };
+ }
}, [props.formMode, language]);
const handleOnChange = useCallback(
@@ -94,7 +104,7 @@ export const StepExpressionEditor: FunctionComponent
);
const showEditor = useMemo(() => {
- if (props.formMode === FormTabsModes.ALL_FIELDS) return true;
+ if (props.formMode === FormTabsModes.ALL_FIELDS || props.formMode === FormTabsModes.REQUIRED_FIELDS) return true;
return props.formMode === FormTabsModes.USER_MODIFIED && isDefined(selectedLanguageOption);
}, [props.formMode]);
diff --git a/packages/ui/src/components/Visualization/Canvas/CanvasForm.scss b/packages/ui/src/components/Visualization/Canvas/CanvasForm.scss
index 338858a46..5c3ec9ca1 100644
--- a/packages/ui/src/components/Visualization/Canvas/CanvasForm.scss
+++ b/packages/ui/src/components/Visualization/Canvas/CanvasForm.scss
@@ -5,7 +5,19 @@
overflow: auto;
}
- .filter-fields {
- margin-bottom: 15px;
+ .form-tabs {
+ display: flex;
+
+ /* stylelint-disable-next-line selector-class-pattern */
+ .pf-v5-c-toggle-group__item {
+ display: flex;
+ width: 20%;
+ }
+
+ button {
+ flex: 1;
+ justify-content: center;
+ font-weight: bold;
+ }
}
}
diff --git a/packages/ui/src/components/Visualization/Canvas/CanvasForm.test.tsx b/packages/ui/src/components/Visualization/Canvas/CanvasForm.test.tsx
index 9c7bf7d28..8bb85bd44 100644
--- a/packages/ui/src/components/Visualization/Canvas/CanvasForm.test.tsx
+++ b/packages/ui/src/components/Visualization/Canvas/CanvasForm.test.tsx
@@ -1,5 +1,5 @@
import catalogLibrary from '@kaoto/camel-catalog/index.json';
-import { CatalogLibrary } from '@kaoto/camel-catalog/types';
+import { CatalogLibrary, RouteDefinition } from '@kaoto/camel-catalog/types';
import { act, fireEvent, render, screen } from '@testing-library/react';
import {
CamelCatalogService,
@@ -13,13 +13,19 @@ import {
} from '../../../models';
import { IVisualizationNode, VisualComponentSchema } from '../../../models/visualization/base-visual-entity';
import { VisualFlowsApi } from '../../../models/visualization/flows/support/flows-visibility';
-import { VisibleFlowsContext, VisibleFlowsProvider } from '../../../providers';
+import {
+ VisibleFlowsContext,
+ VisibleFlowsProvider,
+ CanvasFormTabsContext,
+ CanvasFormTabsProvider,
+} from '../../../providers';
import { EntitiesContext, EntitiesProvider } from '../../../providers/entities.provider';
import { camelRouteJson, kameletJson } from '../../../stubs';
import { getFirstCatalogMap } from '../../../stubs/test-load-catalog';
import { CanvasForm } from './CanvasForm';
import { CanvasNode } from './canvas.models';
import { CanvasService } from './canvas.service';
+import { FormTabsModes } from './canvasformtabs.modes';
describe('CanvasForm', () => {
let camelRouteVisualEntity: CamelRouteVisualEntity;
@@ -159,7 +165,14 @@ describe('CanvasForm', () => {
render(
-
+
+
+
,
);
@@ -187,7 +200,14 @@ describe('CanvasForm', () => {
render(
-
+
+
+
,
);
@@ -216,7 +236,14 @@ describe('CanvasForm', () => {
render(
-
+
+
+
,
);
@@ -265,4 +292,494 @@ describe('CanvasForm', () => {
expect(kameletVisualEntity.id).toEqual(newName);
expect(dispatchSpy).toHaveBeenCalledWith({ type: 'renameFlow', flowId, newName });
});
+
+ describe('should show the User-updated field under the modified tab', () => {
+ beforeEach(() => {
+ camelRouteVisualEntity = new CamelRouteVisualEntity(camelRouteJson);
+ const { nodes } = CanvasService.getFlowDiagram(camelRouteVisualEntity.toVizNode());
+ selectedNode = nodes[0]; // timer
+ });
+
+ it('normal text field', async () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ const allTab = screen.getByRole('button', { name: 'All' });
+ const modifiedTab = screen.getByRole('button', { name: 'Modified' });
+
+ expect(allTab).toBeInTheDocument();
+ expect(modifiedTab).toBeInTheDocument();
+
+ act(() => {
+ fireEvent.click(modifiedTab);
+ });
+
+ const inputVariableReceiveModifiedTabElement = screen
+ .queryAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('label') === 'Variable Receive');
+ expect(inputVariableReceiveModifiedTabElement).toHaveLength(0);
+
+ act(() => {
+ fireEvent.click(allTab);
+ });
+
+ await act(async () => {
+ const inputVariableReceiveAllTabElement = screen
+ .getAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('label') === 'Variable Receive');
+ fireEvent.change(inputVariableReceiveAllTabElement[0], { target: { value: 'test' } });
+ fireEvent.blur(inputVariableReceiveAllTabElement[0]);
+ });
+
+ act(() => {
+ fireEvent.click(modifiedTab);
+ });
+
+ const inputVariableReceiveModifiedTabElementNew = screen
+ .getAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('label') === 'Variable Receive');
+ expect(inputVariableReceiveModifiedTabElementNew).toHaveLength(1);
+ });
+
+ it('expression field', async () => {
+ const camelRoute = {
+ from: {
+ uri: 'timer:tutorial',
+ steps: [
+ {
+ setHeader: {
+ name: 'foo',
+ },
+ },
+ ],
+ },
+ } as RouteDefinition;
+ const entity = new CamelRouteVisualEntity(camelRoute);
+ const rootNode: IVisualizationNode = entity.toVizNode();
+ const setHeaderNode = rootNode.getChildren()![1];
+ const selectedNode = {
+ id: '1',
+ type: 'node',
+ data: {
+ vizNode: setHeaderNode,
+ },
+ };
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ const allTab = screen.getByRole('button', { name: 'All' });
+ const modifiedTab = screen.getByRole('button', { name: 'Modified' });
+
+ act(() => {
+ fireEvent.click(modifiedTab);
+ });
+
+ expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeNull();
+
+ act(() => {
+ fireEvent.click(allTab);
+ });
+
+ const ExpressionAllTabButton = screen.getAllByRole('button', { name: 'Typeahead menu toggle' });
+
+ act(() => {
+ fireEvent.click(ExpressionAllTabButton[0]);
+ });
+
+ const simple = screen.getByTestId('expression-dropdownitem-simple');
+ act(() => {
+ fireEvent.click(simple.getElementsByTagName('button')[0]);
+ });
+
+ act(() => {
+ fireEvent.click(modifiedTab);
+ });
+
+ expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeInTheDocument();
+
+ const inputExpressionModifiedTabElement = screen
+ .queryAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('name') === 'expression');
+ expect(inputExpressionModifiedTabElement).toHaveLength(0);
+
+ act(() => {
+ fireEvent.click(allTab);
+ });
+
+ await act(async () => {
+ const expressionInput = screen
+ .getAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('name') === 'expression');
+ fireEvent.input(expressionInput[0], { target: { value: '${header.foo}' } });
+ fireEvent.blur(expressionInput[0]);
+ });
+
+ act(() => {
+ fireEvent.click(modifiedTab);
+ });
+
+ expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeInTheDocument();
+
+ const inputExpressionModifiedTabElementNew = screen
+ .getAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('name') === 'expression');
+ expect(inputExpressionModifiedTabElementNew).toHaveLength(1);
+ });
+
+ it('dataformat field', async () => {
+ const camelRoute = {
+ from: {
+ uri: 'timer:tutorial',
+ steps: [
+ {
+ marshal: {
+ id: 'ms',
+ },
+ },
+ ],
+ },
+ } as RouteDefinition;
+ const entity = new CamelRouteVisualEntity(camelRoute);
+ const rootNode: IVisualizationNode = entity.toVizNode();
+ const marshalNode = rootNode.getChildren()![1];
+ const selectedNode = {
+ id: '1',
+ type: 'node',
+ data: {
+ vizNode: marshalNode,
+ },
+ };
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ const allTab = screen.getByRole('button', { name: 'All' });
+ const modifiedTab = screen.getByRole('button', { name: 'Modified' });
+ act(() => {
+ fireEvent.click(modifiedTab);
+ });
+
+ expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeNull();
+
+ act(() => {
+ fireEvent.click(allTab);
+ });
+
+ const dataformatAllTabButton = screen.getAllByRole('button', { name: 'Typeahead menu toggle' });
+ await act(async () => {
+ fireEvent.click(dataformatAllTabButton[0]);
+ });
+ const asn1 = screen.getByTestId('dataformat-dropdownitem-asn1');
+ await act(async () => {
+ fireEvent.click(asn1.getElementsByTagName('button')[0]);
+ });
+
+ act(() => {
+ fireEvent.click(modifiedTab);
+ });
+
+ expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeInTheDocument();
+
+ const inputUnmarshalTypeModifiedTabElement = screen
+ .queryAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('label') === 'Unmarshal Type');
+ expect(inputUnmarshalTypeModifiedTabElement).toHaveLength(0);
+
+ act(() => {
+ fireEvent.click(allTab);
+ });
+
+ await act(async () => {
+ const inputUnmarshalTypeDefaultTabElement = screen
+ .getAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('label') === 'Unmarshal Type');
+ fireEvent.change(inputUnmarshalTypeDefaultTabElement[0], { target: { value: 'test' } });
+ fireEvent.blur(inputUnmarshalTypeDefaultTabElement[0]);
+ });
+
+ act(() => {
+ fireEvent.click(modifiedTab);
+ });
+
+ expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeInTheDocument();
+
+ const inputUnmarshalTypeModifiedTabElementNew = screen
+ .getAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('label') === 'Unmarshal Type');
+ expect(inputUnmarshalTypeModifiedTabElementNew).toHaveLength(1);
+ });
+
+ it('loadbalancer field', async () => {
+ const camelRoute = {
+ from: {
+ uri: 'timer:tutorial',
+ steps: [
+ {
+ loadBalance: {
+ id: 'lb',
+ },
+ },
+ ],
+ },
+ } as RouteDefinition;
+ const entity = new CamelRouteVisualEntity(camelRoute);
+ const rootNode: IVisualizationNode = entity.toVizNode();
+ const loadBalanceNode = rootNode.getChildren()![1];
+ const selectedNode = {
+ id: '1',
+ type: 'node',
+ data: {
+ vizNode: loadBalanceNode,
+ },
+ };
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+ const allTab = screen.getByRole('button', { name: 'All' });
+ const modifiedTab = screen.getByRole('button', { name: 'Modified' });
+ act(() => {
+ fireEvent.click(modifiedTab);
+ });
+
+ expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeNull();
+
+ act(() => {
+ fireEvent.click(allTab);
+ });
+
+ const button = screen.getAllByRole('button', { name: 'Typeahead menu toggle' });
+ await act(async () => {
+ fireEvent.click(button[0]);
+ });
+ const weightedLoadBalancer = screen.getByTestId('loadbalancer-dropdownitem-weightedLoadBalancer');
+ await act(async () => {
+ fireEvent.click(weightedLoadBalancer.getElementsByTagName('button')[0]);
+ });
+
+ act(() => {
+ fireEvent.click(modifiedTab);
+ });
+
+ expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeInTheDocument();
+
+ const inputDistributionRatioModifiedTabElement = screen
+ .queryAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('label') === 'Distribution Ratio');
+ expect(inputDistributionRatioModifiedTabElement).toHaveLength(0);
+
+ act(() => {
+ fireEvent.click(allTab);
+ });
+
+ await act(async () => {
+ const inputDistributionRatioDefaultTabElement = screen
+ .getAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('label') === 'Distribution Ratio');
+ fireEvent.change(inputDistributionRatioDefaultTabElement[0], { target: { value: 'test' } });
+ fireEvent.blur(inputDistributionRatioDefaultTabElement[0]);
+ });
+
+ act(() => {
+ fireEvent.click(modifiedTab);
+ });
+
+ expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeInTheDocument();
+
+ const inputDistributionRatioModifiedTabElementNew = screen
+ .getAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('label') === 'Distribution Ratio');
+ expect(inputDistributionRatioModifiedTabElementNew).toHaveLength(1);
+ });
+ });
+
+ describe('should show the Required field under the required tab', () => {
+ beforeEach(() => {
+ camelRouteVisualEntity = new CamelRouteVisualEntity(camelRouteJson);
+ const { nodes } = CanvasService.getFlowDiagram(camelRouteVisualEntity.toVizNode());
+ selectedNode = nodes[0]; // timer
+ });
+
+ it('normal text field', async () => {
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ const allTab = screen.getByRole('button', { name: 'All' });
+ const requiredTab = screen.getByRole('button', { name: 'Required' });
+
+ expect(allTab).toBeInTheDocument();
+ expect(requiredTab).toBeInTheDocument();
+
+ const inputTimerNameRequiredTabTabElement = screen
+ .queryAllByRole('textbox')
+ .filter((textbox) => textbox.getAttribute('label') === 'Timer Name');
+ expect(inputTimerNameRequiredTabTabElement).toHaveLength(1);
+ });
+
+ it('expression field', async () => {
+ const camelRoute = {
+ from: {
+ uri: 'timer:tutorial',
+ steps: [
+ {
+ setHeader: {
+ name: 'foo',
+ },
+ },
+ ],
+ },
+ } as RouteDefinition;
+ const entity = new CamelRouteVisualEntity(camelRoute);
+ const rootNode: IVisualizationNode = entity.toVizNode();
+ const setHeaderNode = rootNode.getChildren()![1];
+ const selectedNode = {
+ id: '1',
+ type: 'node',
+ data: {
+ vizNode: setHeaderNode,
+ },
+ };
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeInTheDocument();
+ });
+
+ it('dataformat field', async () => {
+ const camelRoute = {
+ from: {
+ uri: 'timer:tutorial',
+ steps: [
+ {
+ marshal: {
+ id: 'ms',
+ },
+ },
+ ],
+ },
+ } as RouteDefinition;
+ const entity = new CamelRouteVisualEntity(camelRoute);
+ const rootNode: IVisualizationNode = entity.toVizNode();
+ const marshalNode = rootNode.getChildren()![1];
+ const selectedNode = {
+ id: '1',
+ type: 'node',
+ data: {
+ vizNode: marshalNode,
+ },
+ };
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeInTheDocument();
+ });
+
+ it('loadbalancer field', async () => {
+ const camelRoute = {
+ from: {
+ uri: 'timer:tutorial',
+ steps: [
+ {
+ loadBalance: {
+ id: 'lb',
+ },
+ },
+ ],
+ },
+ } as RouteDefinition;
+ const entity = new CamelRouteVisualEntity(camelRoute);
+ const rootNode: IVisualizationNode = entity.toVizNode();
+ const loadBalanceNode = rootNode.getChildren()![1];
+ const selectedNode = {
+ id: '1',
+ type: 'node',
+ data: {
+ vizNode: loadBalanceNode,
+ },
+ };
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeInTheDocument();
+ });
+ });
});
diff --git a/packages/ui/src/components/Visualization/Canvas/CanvasForm.tsx b/packages/ui/src/components/Visualization/Canvas/CanvasForm.tsx
index b674007ea..17274e98e 100644
--- a/packages/ui/src/components/Visualization/Canvas/CanvasForm.tsx
+++ b/packages/ui/src/components/Visualization/Canvas/CanvasForm.tsx
@@ -1,4 +1,4 @@
-import { Card, CardBody, CardHeader, SearchInput } from '@patternfly/react-core';
+import { Card, CardBody, CardHeader, SearchInput, ToggleGroup, ToggleGroupItem, Tooltip } from '@patternfly/react-core';
import { FunctionComponent, useCallback, useContext, useEffect, useMemo, useRef } from 'react';
import { VisibleFlowsContext, FilteredFieldContext } from '../../../providers';
import { ErrorBoundary } from '../../ErrorBoundary';
@@ -6,6 +6,8 @@ import './CanvasForm.scss';
import { CanvasFormHeader } from './Form/CanvasFormHeader';
import { CanvasNode } from './canvas.models';
import { CanvasFormTabs } from './CanvasFormTabs';
+import { FormTabsModes, getTabTooltip } from './canvasformtabs.modes';
+import { CanvasFormTabsContext } from '../../../providers/canvas-form-tabs.provider';
interface CanvasFormProps {
selectedNode: CanvasNode;
@@ -15,6 +17,7 @@ interface CanvasFormProps {
export const CanvasForm: FunctionComponent = (props) => {
const { visualFlowsApi } = useContext(VisibleFlowsContext)!;
const { filteredFieldText, onFilterChange } = useContext(FilteredFieldContext);
+ const { selectedTab, onTabChange } = useContext(CanvasFormTabsContext);
const flowIdRef = useRef(undefined);
const visualComponentSchema = useMemo(() => {
@@ -44,6 +47,25 @@ export const CanvasForm: FunctionComponent = (props) => {
This node cannot be configured yet
}>
+
+
+ {Object.values(FormTabsModes).map((mode) => (
+
+
+
+ ))}
+
= (props) => {
onChange={onFilterChange}
onClear={onFilterChange}
/>
-
diff --git a/packages/ui/src/components/Visualization/Canvas/CanvasFormTabs.test.tsx b/packages/ui/src/components/Visualization/Canvas/CanvasFormTabs.test.tsx
index 9146849d2..a32e2c450 100644
--- a/packages/ui/src/components/Visualization/Canvas/CanvasFormTabs.test.tsx
+++ b/packages/ui/src/components/Visualization/Canvas/CanvasFormTabs.test.tsx
@@ -10,18 +10,15 @@ import {
IKameletDefinition,
} from '../../../models';
import { IVisualizationNode } from '../../../models/visualization/base-visual-entity';
-import { VisibleFlowsProvider } from '../../../providers';
-import { EntitiesContext, EntitiesProvider } from '../../../providers/entities.provider';
-import { camelRouteJson } from '../../../stubs';
+import { VisibleFlowsProvider, CanvasFormTabsContext } from '../../../providers';
+import { EntitiesContext } from '../../../providers/entities.provider';
import { getFirstCatalogMap } from '../../../stubs/test-load-catalog';
import { SchemaService } from '../../Form';
import { CanvasNode } from './canvas.models';
-import { CanvasService } from './canvas.service';
import { CanvasFormTabs } from './CanvasFormTabs';
+import { FormTabsModes } from './canvasformtabs.modes';
describe('CanvasFormTabs', () => {
- let camelRouteVisualEntity: CamelRouteVisualEntity;
- let selectedNode: CanvasNode;
let componentCatalogMap: Record;
let patternCatalogMap: Record;
let kameletCatalogMap: Record;
@@ -42,304 +39,6 @@ describe('CanvasFormTabs', () => {
CamelCatalogService.setCatalogKey(CatalogKind.Entity, catalogsMap.entitiesCatalog);
});
- beforeEach(() => {
- camelRouteVisualEntity = new CamelRouteVisualEntity(camelRouteJson);
- const { nodes } = CanvasService.getFlowDiagram(camelRouteVisualEntity.toVizNode());
- selectedNode = nodes[0]; // timer
- });
-
- afterEach(() => {
- jest.clearAllMocks();
- });
-
- describe('should show the User-updated field under the modified tab', () => {
- it('normal text field', async () => {
- render(
-
-
-
-
- ,
- );
-
- const defaultTab = screen.getByRole('button', { name: 'All Fields' });
- const modifiedTab = screen.getByRole('button', { name: 'User Modified' });
-
- expect(defaultTab).toBeInTheDocument();
- expect(modifiedTab).toBeInTheDocument();
-
- act(() => {
- fireEvent.click(modifiedTab);
- });
-
- const inputVariableReceiveModifiedTabElement = screen
- .queryAllByRole('textbox')
- .filter((textbox) => textbox.getAttribute('label') === 'Variable Receive');
- expect(inputVariableReceiveModifiedTabElement).toHaveLength(0);
-
- act(() => {
- fireEvent.click(defaultTab);
- });
-
- await act(async () => {
- const inputVariableReceiveDefaultTabElement = screen
- .getAllByRole('textbox')
- .filter((textbox) => textbox.getAttribute('label') === 'Variable Receive');
- fireEvent.change(inputVariableReceiveDefaultTabElement[0], { target: { value: 'test' } });
- fireEvent.blur(inputVariableReceiveDefaultTabElement[0]);
- });
-
- act(() => {
- fireEvent.click(modifiedTab);
- });
-
- const inputVariableReceiveModifiedTabElementNew = screen
- .getAllByRole('textbox')
- .filter((textbox) => textbox.getAttribute('label') === 'Variable Receive');
- expect(inputVariableReceiveModifiedTabElementNew).toHaveLength(1);
- });
-
- it('expression field', async () => {
- const camelRoute = {
- from: {
- uri: 'timer:tutorial',
- steps: [
- {
- setHeader: {
- name: 'foo',
- },
- },
- ],
- },
- } as RouteDefinition;
- const entity = new CamelRouteVisualEntity(camelRoute);
- const rootNode: IVisualizationNode = entity.toVizNode();
- const setHeaderNode = rootNode.getChildren()![1];
- const selectedNode = {
- id: '1',
- type: 'node',
- data: {
- vizNode: setHeaderNode,
- },
- };
-
- render(
-
-
-
-
- ,
- );
-
- const defaultTab = screen.getByRole('button', { name: 'All Fields' });
- const modifiedTab = screen.getByRole('button', { name: 'User Modified' });
-
- act(() => {
- fireEvent.click(modifiedTab);
- });
-
- act(() => {
- fireEvent.click(defaultTab);
- });
-
- const button = screen
- .getAllByTestId('typeahead-select-input')
- .filter((input) => input.innerHTML.includes(SchemaService.DROPDOWN_PLACEHOLDER));
- act(() => {
- fireEvent.click(button[0]);
- });
- const simple = screen.getByTestId('expression-dropdownitem-simple');
- act(() => {
- fireEvent.click(simple.getElementsByTagName('button')[0]);
- });
- const expressionInput = screen
- .getAllByRole('textbox')
- .filter((textbox) => textbox.getAttribute('name') === 'expression');
- act(() => {
- fireEvent.input(expressionInput[0], { target: { value: '${header.foo}' } });
- });
-
- act(() => {
- fireEvent.click(modifiedTab);
- });
- });
-
- it('dataformat field', async () => {
- const camelRoute = {
- from: {
- uri: 'timer:tutorial',
- steps: [
- {
- marshal: {
- id: 'ms',
- },
- },
- ],
- },
- } as RouteDefinition;
- const entity = new CamelRouteVisualEntity(camelRoute);
- const rootNode: IVisualizationNode = entity.toVizNode();
- const marshalNode = rootNode.getChildren()![1];
- const selectedNode = {
- id: '1',
- type: 'node',
- data: {
- vizNode: marshalNode,
- },
- };
-
- render(
-
-
-
-
- ,
- );
-
- const defaultTab = screen.getByRole('button', { name: 'All Fields' });
- const modifiedTab = screen.getByRole('button', { name: 'User Modified' });
- act(() => {
- fireEvent.click(modifiedTab);
- });
-
- expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeNull();
-
- act(() => {
- fireEvent.click(defaultTab);
- });
-
- const dataformatDefaultTabButton = screen.getAllByRole('button', { name: 'Typeahead menu toggle' });
- await act(async () => {
- fireEvent.click(dataformatDefaultTabButton[0]);
- });
- const asn1 = screen.getByTestId('dataformat-dropdownitem-asn1');
- await act(async () => {
- fireEvent.click(asn1.getElementsByTagName('button')[0]);
- });
-
- act(() => {
- fireEvent.click(modifiedTab);
- });
-
- expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeInTheDocument();
-
- const inputUnmarshalTypeModifiedTabElement = screen
- .queryAllByRole('textbox')
- .filter((textbox) => textbox.getAttribute('label') === 'Unmarshal Type');
- expect(inputUnmarshalTypeModifiedTabElement).toHaveLength(0);
-
- act(() => {
- fireEvent.click(defaultTab);
- });
-
- await act(async () => {
- const inputUnmarshalTypeDefaultTabElement = screen
- .getAllByRole('textbox')
- .filter((textbox) => textbox.getAttribute('label') === 'Unmarshal Type');
- fireEvent.change(inputUnmarshalTypeDefaultTabElement[0], { target: { value: 'test' } });
- fireEvent.blur(inputUnmarshalTypeDefaultTabElement[0]);
- });
-
- act(() => {
- fireEvent.click(modifiedTab);
- });
-
- expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeInTheDocument();
-
- const inputUnmarshalTypeModifiedTabElementNew = screen
- .getAllByRole('textbox')
- .filter((textbox) => textbox.getAttribute('label') === 'Unmarshal Type');
- expect(inputUnmarshalTypeModifiedTabElementNew).toHaveLength(1);
- });
-
- it('loadbalancer field', async () => {
- const camelRoute = {
- from: {
- uri: 'timer:tutorial',
- steps: [
- {
- loadBalance: {
- id: 'lb',
- },
- },
- ],
- },
- } as RouteDefinition;
- const entity = new CamelRouteVisualEntity(camelRoute);
- const rootNode: IVisualizationNode = entity.toVizNode();
- const loadBalanceNode = rootNode.getChildren()![1];
- const selectedNode = {
- id: '1',
- type: 'node',
- data: {
- vizNode: loadBalanceNode,
- },
- };
-
- render(
-
-
-
-
- ,
- );
- const defaultTab = screen.getByRole('button', { name: 'All Fields' });
- const modifiedTab = screen.getByRole('button', { name: 'User Modified' });
- act(() => {
- fireEvent.click(modifiedTab);
- });
-
- expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeNull();
-
- act(() => {
- fireEvent.click(defaultTab);
- });
-
- const button = screen.getAllByRole('button', { name: 'Typeahead menu toggle' });
- await act(async () => {
- fireEvent.click(button[0]);
- });
- const weightedLoadBalancer = screen.getByTestId('loadbalancer-dropdownitem-weightedLoadBalancer');
- await act(async () => {
- fireEvent.click(weightedLoadBalancer.getElementsByTagName('button')[0]);
- });
-
- act(() => {
- fireEvent.click(modifiedTab);
- });
-
- expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeInTheDocument();
-
- const inputDistributionRatioModifiedTabElement = screen
- .queryAllByRole('textbox')
- .filter((textbox) => textbox.getAttribute('label') === 'Distribution Ratio');
- expect(inputDistributionRatioModifiedTabElement).toHaveLength(0);
-
- act(() => {
- fireEvent.click(defaultTab);
- });
-
- await act(async () => {
- const inputDistributionRatioDefaultTabElement = screen
- .getAllByRole('textbox')
- .filter((textbox) => textbox.getAttribute('label') === 'Distribution Ratio');
- fireEvent.change(inputDistributionRatioDefaultTabElement[0], { target: { value: 'test' } });
- fireEvent.blur(inputDistributionRatioDefaultTabElement[0]);
- });
-
- act(() => {
- fireEvent.click(modifiedTab);
- });
-
- expect(screen.queryByRole('button', { name: 'Typeahead menu toggle' })).toBeInTheDocument();
-
- const inputDistributionRatioModifiedTabElementNew = screen
- .getAllByRole('textbox')
- .filter((textbox) => textbox.getAttribute('label') === 'Distribution Ratio');
- expect(inputDistributionRatioModifiedTabElementNew).toHaveLength(1);
- });
- });
-
describe('should persists changes from both expression editor and main form', () => {
beforeEach(() => {
jest.spyOn(console, 'error').mockImplementation(() => {});
@@ -372,7 +71,14 @@ describe('CanvasFormTabs', () => {
render(
-
+
+
+
,
);
@@ -432,7 +138,14 @@ describe('CanvasFormTabs', () => {
render(
-
+
+
+
,
);
@@ -497,10 +210,18 @@ describe('CanvasFormTabs', () => {
render(
-
+
+
+
,
);
+
const button = screen.getAllByRole('button', { name: 'Typeahead menu toggle' });
await act(async () => {
fireEvent.click(button[0]);
@@ -547,10 +268,18 @@ describe('CanvasFormTabs', () => {
render(
-
+
+
+
,
);
+
const idInput = screen.getAllByRole('textbox').filter((textbox) => textbox.getAttribute('label') === 'Id');
await act(async () => {
fireEvent.input(idInput[0], { target: { value: 'modified' } });
@@ -603,10 +332,18 @@ describe('CanvasFormTabs', () => {
render(
-
+
+
+
,
);
+
const button = screen.getAllByRole('button', { name: 'Typeahead menu toggle' });
await act(async () => {
fireEvent.click(button[0]);
@@ -653,10 +390,18 @@ describe('CanvasFormTabs', () => {
render(
-
+
+
+
,
);
+
const idInput = screen.getAllByRole('textbox').filter((textbox) => textbox.getAttribute('label') === 'Id');
await act(async () => {
fireEvent.input(idInput[0], { target: { value: 'modified' } });
diff --git a/packages/ui/src/components/Visualization/Canvas/CanvasFormTabs.tsx b/packages/ui/src/components/Visualization/Canvas/CanvasFormTabs.tsx
index d7d743b45..6e712e443 100644
--- a/packages/ui/src/components/Visualization/Canvas/CanvasFormTabs.tsx
+++ b/packages/ui/src/components/Visualization/Canvas/CanvasFormTabs.tsx
@@ -1,8 +1,7 @@
-import { ToggleGroup, ToggleGroupItem } from '@patternfly/react-core';
-import { FunctionComponent, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
+import { FunctionComponent, useCallback, useContext, useEffect, useMemo, useRef } from 'react';
import { EntitiesContext } from '../../../providers/entities.provider';
import { SchemaBridgeProvider } from '../../../providers/schema-bridge.provider';
-import { getUserUpdatedPropertiesSchema, isDefined, setValue } from '../../../utils';
+import { getUserUpdatedPropertiesSchema, getRequiredPropertiesSchema, isDefined, setValue } from '../../../utils';
import { CustomAutoForm, CustomAutoFormRef } from '../../Form/CustomAutoForm';
import { DataFormatEditor } from '../../Form/dataFormat/DataFormatEditor';
import { LoadBalancerEditor } from '../../Form/loadBalancer/LoadBalancerEditor';
@@ -11,6 +10,7 @@ import { UnknownNode } from '../Custom/UnknownNode';
import './CanvasFormTabs.scss';
import { CanvasNode } from './canvas.models';
import { FormTabsModes } from './canvasformtabs.modes';
+import { CanvasFormTabsContext } from '../../../providers/canvas-form-tabs.provider';
interface CanvasFormTabsProps {
selectedNode: CanvasNode;
@@ -18,10 +18,10 @@ interface CanvasFormTabsProps {
export const CanvasFormTabs: FunctionComponent = (props) => {
const entitiesContext = useContext(EntitiesContext);
+ const { selectedTab } = useContext(CanvasFormTabsContext);
const divRef = useRef(null);
const formRef = useRef(null);
const omitFields = useRef(props.selectedNode.data?.vizNode?.getBaseEntity()?.getOmitFormFields() || []);
- const [selectedTab, setSelectedTab] = useState(FormTabsModes.ALL_FIELDS);
const visualComponentSchema = useMemo(() => {
const answer = props.selectedNode.data?.vizNode?.getComponentSchema();
@@ -33,19 +33,15 @@ export const CanvasFormTabs: FunctionComponent = (props) =>
}, [props.selectedNode.data?.vizNode, selectedTab]);
const model = visualComponentSchema?.definition;
let processedSchema = visualComponentSchema?.schema;
- if (selectedTab === FormTabsModes.USER_MODIFIED) {
+ if (selectedTab === FormTabsModes.REQUIRED_FIELDS) {
+ processedSchema = getRequiredPropertiesSchema(visualComponentSchema?.schema ?? {});
+ } else if (selectedTab === FormTabsModes.USER_MODIFIED) {
processedSchema = {
...visualComponentSchema?.schema,
properties: getUserUpdatedPropertiesSchema(visualComponentSchema?.schema.properties ?? {}, model),
};
}
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- const handleItemClick = (event: any, _isSelected: boolean) => {
- const id = event.currentTarget.id;
- setSelectedTab(id);
- };
-
useEffect(() => {
formRef.current?.form.reset();
}, [props.selectedNode.data?.vizNode, selectedTab]);
@@ -87,17 +83,6 @@ export const CanvasFormTabs: FunctionComponent = (props) =>
) : (
-
- {Object.values(FormTabsModes).map((mode) => (
-
- ))}
-
{stepFeatures.isExpressionAwareStep && (
)}
diff --git a/packages/ui/src/components/Visualization/Canvas/Form/CanvasFormHeader.scss b/packages/ui/src/components/Visualization/Canvas/Form/CanvasFormHeader.scss
index e37752fb4..ac02be1c1 100644
--- a/packages/ui/src/components/Visualization/Canvas/Form/CanvasFormHeader.scss
+++ b/packages/ui/src/components/Visualization/Canvas/Form/CanvasFormHeader.scss
@@ -2,6 +2,7 @@
display: flex;
flex-flow: row nowrap;
align-items: center;
+ margin-bottom: 15px;
img[class^='form-header__icon-'] {
height: 40px;
diff --git a/packages/ui/src/components/Visualization/Canvas/__snapshots__/CanvasForm.test.tsx.snap b/packages/ui/src/components/Visualization/Canvas/__snapshots__/CanvasForm.test.tsx.snap
index 6559b1f35..6534639ec 100644
--- a/packages/ui/src/components/Visualization/Canvas/__snapshots__/CanvasForm.test.tsx.snap
+++ b/packages/ui/src/components/Visualization/Canvas/__snapshots__/CanvasForm.test.tsx.snap
@@ -15,43 +15,6 @@ exports[`CanvasForm should render 1`] = `
-
@@ -100,211 +63,121 @@ exports[`CanvasForm should render 1`] = `
-
-
-
+
-
-
-
-
+
+
+