diff --git a/canvas_modules/common-canvas/__tests__/common-properties/conditions/nested-conditions-test.js b/canvas_modules/common-canvas/__tests__/common-properties/conditions/nested-conditions-test.js
new file mode 100644
index 0000000000..6c6478035c
--- /dev/null
+++ b/canvas_modules/common-canvas/__tests__/common-properties/conditions/nested-conditions-test.js
@@ -0,0 +1,704 @@
+/*
+ * Copyright 2025 Elyra Authors
+ *
+ * Licensed 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 { expect } from "chai";
+import { fireEvent } from "@testing-library/react";
+
+// import { validateInput } from "../../../src/common-properties/ui-conditions/ui-conditions.js";
+import propertyUtilsRTL from "../../_utils_/property-utilsRTL.js";
+import tableUtilsRTL from "./../../_utils_/table-utilsRTL";
+import nestedConditionsParamDef from "../../test_resources/paramDefs/nestedConditions_paramDef.json";
+
+describe("nested conditions display the error in the correct cell and table", () => {
+ let wrapper;
+ let controller;
+ beforeEach(() => {
+ const renderedObject = propertyUtilsRTL.flyoutEditorForm(nestedConditionsParamDef);
+ wrapper = renderedObject.wrapper;
+ controller = renderedObject.controller;
+ // controller.setErrorMessages({});
+ });
+
+ afterEach(() => {
+ wrapper.unmount();
+ });
+
+ it("error message does not appear when common properties first opened", () => {
+ const expectedError = {
+ "nested_table": {
+ "0": {
+ "2": {
+ "displayError": false,
+ "propertyId": {
+ "col": 2,
+ "name": "nested_table",
+ "row": 0
+ },
+ "required": false,
+ "text": "Cannot select 'orange'",
+ "type": "error",
+ "validation_id": "nested_table"
+ }
+ }
+ }
+ };
+ const allMessages = controller.getAllErrorMessages();
+ expect(allMessages).to.eql(expectedError);
+ expect(controller.getErrorMessages()).to.eql({});
+
+ const container = wrapper.container;
+ const errors = container.querySelectorAll(".properties-validation-message.error.inTable");
+ expect(errors.length).to.equal(0);
+ });
+
+ it("conditions work correctly for a nested list control", () => {
+ const container = wrapper.container;
+ let mainTable = container.querySelector("div[data-id='properties-ci-nested_table']");
+ tableUtilsRTL.selectCheckboxes(mainTable, [0]); // Select first row for onPanel edit
+
+ // verify onPanel edit shows list control
+ mainTable = container.querySelector("div[data-id='properties-ci-nested_table']");
+ const onPanelList = mainTable.querySelectorAll(".properties-onpanel-container");
+ expect(onPanelList).to.have.length(1);
+
+ const listControl = onPanelList[0].querySelectorAll(".properties-list-table");
+ expect(listControl.length).to.equal(1);
+ const inputs = listControl[0].querySelectorAll("input[type='text']");
+ expect(inputs.length).to.equal(2);
+
+ const expectedErrors = {
+ "nested_table": {
+ "0": {
+ "0": {
+ "1": {
+ "propertyId": {
+ "col": 0,
+ "name": "nested_table",
+ "propertyId": {
+ "name": "list",
+ "row": 1
+ },
+ "row": 0
+ },
+ "required": false,
+ "text": "Cannot be 'error'",
+ "type": "error",
+ "validation_id": "nested_table"
+ },
+ "propertyId": {
+ "col": 0,
+ "name": "nested_table",
+ "propertyId": {
+ "name": "list",
+ "row": 1
+ },
+ "row": 0
+ },
+ "required": false,
+ "text": "Cannot be 'error'",
+ "type": "error",
+ "validation_id": "nested_table"
+ }
+ }
+ }
+ };
+
+ // Modify second input in list to show error
+ fireEvent.change(inputs[1], { target: { value: "error" } });
+ expect(controller.getErrorMessages()).to.eql(expectedErrors);
+
+ // Verify main table also display the error in cell
+ let errorCells = mainTable.querySelectorAll(".properties-validation-message.error.inTable");
+ expect(errorCells.length).to.equal(2); // parent table and on panel `list` table
+
+ // Remove error from list will also clear from parent table
+ fireEvent.change(inputs[1], { target: { value: "removed error cell" } });
+ expect(controller.getErrorMessages()).to.eql({});
+ errorCells = mainTable.querySelectorAll(".properties-validation-message.error.inTable");
+ expect(errorCells.length).to.equal(0);
+ });
+
+ it("conditions work correctly for a nested structuretable control", () => {
+ const container = wrapper.container;
+ let mainTable = container.querySelector("div[data-id='properties-ci-nested_table']");
+ tableUtilsRTL.selectCheckboxes(mainTable, [0]); // Select first row for onPanel edit
+
+ // verify onPanel edit shows structuretable control
+ mainTable = container.querySelector("div[data-id='properties-ci-nested_table']");
+ const onPanelTable = mainTable.querySelectorAll(".properties-onpanel-container");
+ expect(onPanelTable).to.have.length(1);
+
+ const structureTableControl = onPanelTable[0].querySelectorAll("div[data-id='properties-structuretable']");
+ expect(structureTableControl.length).to.equal(1);
+ const dropdowns = structureTableControl[0].querySelectorAll("select");
+ expect(dropdowns.length).to.equal(2);
+
+ const expectedOneError = {
+ "nested_table": {
+ "0": {
+ "3": {
+ "0": {
+ "1": {
+ "propertyId": {
+ "col": 3,
+ "name": "nested_table",
+ "propertyId": {
+ "col": 1,
+ "name": "structuretable",
+ "row": 0
+ },
+ "row": 0
+ },
+ "required": false,
+ "text": "Cannot select 'green'",
+ "type": "error",
+ "validation_id": "nested_table"
+ },
+ },
+ "propertyId": {
+ "col": 3,
+ "name": "nested_table",
+ "propertyId": {
+ "col": 1,
+ "name": "structuretable",
+ "row": 0
+ },
+ "row": 0
+ },
+ "required": false,
+ "text": "Cannot select 'green'",
+ "type": "error",
+ "validation_id": "nested_table"
+ }
+ }
+ }
+ };
+
+ // Modify first dropdown to show error
+ fireEvent.change(dropdowns[0], { target: { value: "green" } });
+ expect(controller.getErrorMessages()).to.eql(expectedOneError);
+
+ // Verify main table also display the error in cell
+ const errors = mainTable.querySelectorAll(".properties-validation-message.error.inTable");
+ expect(errors.length).to.equal(2); // parent table and on panel `list` table
+
+ const expectedTwoErrors = {
+ "nested_table": {
+ "0": {
+ "3": {
+ "0": {
+ "1": {
+ "propertyId": {
+ "col": 3,
+ "name": "nested_table",
+ "propertyId": {
+ "col": 1,
+ "name": "structuretable",
+ "row": 0
+ },
+ "row": 0
+ },
+ "required": false,
+ "text": "Cannot select 'green'",
+ "type": "error",
+ "validation_id": "nested_table"
+ }
+ },
+ "1": {
+ "1": {
+ "propertyId": {
+ "col": 3,
+ "name": "nested_table",
+ "propertyId": {
+ "col": 1,
+ "name": "structuretable",
+ "row": 1
+ },
+ "row": 0
+ },
+ "required": false,
+ "tableText": "There are 2 error cells. ",
+ "text": "Cannot select 'green'",
+ "type": "error",
+ "validation_id": "nested_table"
+ }
+ },
+ "propertyId": {
+ "col": 3,
+ "name": "nested_table",
+ "propertyId": {
+ "col": 1,
+ "name": "structuretable",
+ "row": 1
+ },
+ "row": 0
+ },
+ "required": false,
+ "text": "Cannot select 'green'",
+ "type": "error",
+ "validation_id": "nested_table"
+ }
+ }
+ }
+ };
+
+ // Modify second dropdown to show another error
+ fireEvent.change(dropdowns[1], { target: { value: "green" } });
+ expect(controller.getErrorMessages()).to.eql(expectedTwoErrors);
+
+ const validationErrors = structureTableControl[0].querySelectorAll(".properties-validation-message.error");
+ expect(validationErrors.length).to.equal(3); // First two are in cells, last one is on table
+ expect(validationErrors[2].querySelector("span").textContent).to.equal("There are 2 error cells. ");
+
+ // Verify main table also display the error in cell
+ let errorCells = mainTable.querySelectorAll(".properties-validation-message.error.inTable");
+ expect(errorCells.length).to.equal(3); // parent table and on panel `structuretable` table
+
+ // Remove error from second dropdown will also update the error propertyId of structuretable
+ fireEvent.change(dropdowns[1], { target: { value: "yellow" } });
+ expect(controller.getErrorMessages()).to.eql(expectedOneError);
+
+ // Remove error fom first dropdown will clear all error cells from both structuretable and parent table
+ fireEvent.change(dropdowns[0], { target: { value: "red" } });
+ expect(controller.getErrorMessages()).to.eql({});
+ errorCells = mainTable.querySelectorAll(".properties-validation-message.error.inTable");
+ expect(errorCells.length).to.equal(0);
+ });
+
+ it("conditions work correctly for a nested selectColumns control", () => {
+ const container = wrapper.container;
+ const mainTable = container.querySelector("div[data-id='properties-ci-nested_table']");
+ const editButton = mainTable.querySelectorAll("button.properties-subpanel-button")[0];
+ fireEvent.click(editButton); // Click subpanel edit button on first row
+
+ // verify subpanel shows selectColumns control
+ const subPanel = container.querySelectorAll(".properties-editstyle-sub-panel");
+ expect(subPanel).to.have.length(1);
+
+ const selectColumnsControl = subPanel[0].querySelectorAll(".properties-column-select-table");
+ expect(selectColumnsControl.length).to.equal(1);
+
+ // Select 'K' to show error
+ const fieldPicker = tableUtilsRTL.openFieldPicker(container, "properties-ft-select_columns");
+ tableUtilsRTL.fieldPicker(fieldPicker, ["K"]);
+
+ const expectedSelectedValues = ["Age", "Na", "K"];
+ expect(controller.getPropertyValue({ name: "nested_table", row: 0, col: 1 })).to.eql(expectedSelectedValues);
+ const expectedOneError = {
+ "nested_table": {
+ "0": {
+ "1": {
+ "2": {
+ "propertyId": {
+ "col": 1,
+ "name": "nested_table",
+ "propertyId": {
+ "row": 2
+ },
+ "row": 0
+ },
+ "required": false,
+ "text": "Cannot contain 'K'",
+ "type": "error",
+ "validation_id": "nested_table"
+ },
+ "propertyId": {
+ "col": 1,
+ "name": "nested_table",
+ "propertyId": {
+ "row": 2
+ },
+ "row": 0
+ },
+ "required": false,
+ "text": "Cannot contain 'K'",
+ "type": "error",
+ "validation_id": "nested_table"
+ }
+ }
+ }
+ };
+ expect(controller.getErrorMessages()).to.eql(expectedOneError);
+
+ // Verify subpanel selectColumns table display the error in cell
+ const subPanelErrors = selectColumnsControl[0].querySelectorAll(".properties-validation-message.error.inTable");
+ expect(subPanelErrors.length).to.equal(1);
+
+ // Verify main parent table also display the error in cell
+ const errors = mainTable.querySelectorAll(".properties-validation-message.error.inTable");
+ expect(errors.length).to.equal(1);
+ });
+
+ it("conditions work correctly for a nested someofselect control", () => {
+ const container = wrapper.container;
+ const mainTable = container.querySelector("div[data-id='properties-ci-nested_table']");
+ const editButton = mainTable.querySelectorAll("button.properties-subpanel-button")[0];
+ fireEvent.click(editButton); // Click subpanel edit button on first row
+
+ // verify subpanel shows someofselect control
+ const subPanel = container.querySelectorAll(".properties-editstyle-sub-panel");
+ expect(subPanel).to.have.length(1);
+
+ // Select 'yellow' to trigger the error to show
+ const someofselectControl = subPanel[0].querySelectorAll("div[data-id='properties-someofselect']");
+ expect(someofselectControl.length).to.equal(1);
+ tableUtilsRTL.selectCheckboxes(someofselectControl[0], [2]);
+
+ const expectedSelectedValues = ["orange", "yellow", "green"];
+ expect(controller.getPropertyValue({ name: "nested_table", row: 0, col: 2 })).to.eql(expectedSelectedValues);
+
+ const expectedOneError = {
+ "nested_table": {
+ "0": {
+ "2": {
+ "propertyId": {
+ "col": 2,
+ "name": "nested_table",
+ "row": 0
+ },
+ "required": false,
+ "text": "Cannot select 'orange'",
+ "type": "error",
+ "validation_id": "nested_table"
+ }
+ }
+ }
+ };
+ expect(controller.getErrorMessages()).to.eql(expectedOneError);
+
+ // Verify subpanel someofselect table display the error in table
+ let subPanelErrors = someofselectControl[0].querySelectorAll(".properties-validation-message.error");
+ expect(subPanelErrors.length).to.equal(1);
+
+ // Verify main parent table also display the error in cell
+ let errors = mainTable.querySelectorAll(".properties-validation-message.error.inTable");
+ expect(errors.length).to.equal(1);
+
+ // Deselect 'orange' will remove error from someofselect table and parent table
+ tableUtilsRTL.selectCheckboxes(someofselectControl[0], [1]);
+ const expectedValues = ["yellow", "green"];
+ expect(controller.getPropertyValue({ name: "nested_table", row: 0, col: 2 })).to.eql(expectedValues);
+
+ subPanelErrors = someofselectControl[0].querySelectorAll(".properties-validation-message.error");
+ expect(subPanelErrors.length).to.equal(0);
+ errors = mainTable.querySelectorAll(".properties-validation-message.error.inTable");
+ expect(errors.length).to.equal(0);
+ });
+
+ it("conditions work correctly for a nested structurelisteditor control", () => {
+ const container = wrapper.container;
+ const mainTable = container.querySelector("div[data-id='properties-ci-nested_table']");
+ const editButton = mainTable.querySelectorAll("button.properties-subpanel-button")[0];
+ fireEvent.click(editButton); // Click subpanel edit button on first row
+
+ // verify subpanel shows structurelisteditor control
+ const subPanel = container.querySelectorAll(".properties-editstyle-sub-panel");
+ expect(subPanel).to.have.length(1);
+
+ const structurelisteditorControl = subPanel[0].querySelectorAll("div[data-id='properties-ft-structurelisteditor']");
+ expect(structurelisteditorControl.length).to.equal(1);
+
+ const textInputs = structurelisteditorControl[0].querySelectorAll("input[type='text']");
+ expect(textInputs.length).to.equal(2);
+ const numberInputs = structurelisteditorControl[0].querySelectorAll("input[type='number']");
+ expect(numberInputs.length).to.equal(2);
+
+ const expectedOneError = {
+ "nested_table": {
+ "0": {
+ "4": {
+ "0": {
+ "0": {
+ "propertyId": {
+ "col": 4,
+ "name": "nested_table",
+ "propertyId": {
+ "col": 0,
+ "name": "structurelisteditor",
+ "row": 0
+ },
+ "row": 0
+ },
+ "required": false,
+ "text": "Cannot be 'error'",
+ "type": "error",
+ "validation_id": "nested_table"
+ }
+ },
+ "propertyId": {
+ "col": 4,
+ "name": "nested_table",
+ "propertyId": {
+ "col": 0,
+ "name": "structurelisteditor",
+ "row": 0
+ },
+ "row": 0
+ },
+ "required": false,
+ "text": "Cannot be 'error'",
+ "type": "error",
+ "validation_id": "nested_table"
+ }
+ }
+ }
+ };
+
+ // Modify row 0 textinput to show error
+ fireEvent.change(textInputs[0], { target: { value: "error" } });
+ expect(controller.getErrorMessages()).to.eql(expectedOneError);
+
+ // Verify main parent table also display the error in cell
+ let errors = mainTable.querySelectorAll(".properties-validation-message.error.inTable");
+ expect(errors.length).to.equal(1);
+
+ // Verify structurelistedior also display the error in bottom of table
+ let validationErrors = subPanel[0].querySelectorAll(".properties-validation-message.error");
+ expect(validationErrors.length).to.equal(2); // First one is in cell, the other is on table
+ expect(validationErrors[1].querySelector("span").textContent).to.equal("Cannot be 'error'");
+
+ const expectedTwoErrors = {
+ "nested_table": {
+ "0": {
+ "4": {
+ "0": {
+ "0": {
+ "propertyId": {
+ "col": 4,
+ "name": "nested_table",
+ "propertyId": {
+ "col": 0,
+ "name": "structurelisteditor",
+ "row": 0
+ },
+ "row": 0
+ },
+ "required": false,
+ "text": "Cannot be 'error'",
+ "type": "error",
+ "validation_id": "nested_table"
+ },
+ "1": {
+ "propertyId": {
+ "col": 4,
+ "name": "nested_table",
+ "propertyId": {
+ "col": 1,
+ "name": "structurelisteditor",
+ "row": 0
+ },
+ "row": 0
+ },
+ "required": false,
+ "tableText": "There are 2 error cells. ",
+ "text": "Must be greater than 0",
+ "type": "error",
+ "validation_id": "nested_table"
+ }
+ },
+ "propertyId": {
+ "col": 4,
+ "name": "nested_table",
+ "propertyId": {
+ "col": 1,
+ "name": "structurelisteditor",
+ "row": 0
+ },
+ "row": 0
+ },
+ "required": false,
+ "text": "Must be greater than 0",
+ "type": "error",
+ "validation_id": "nested_table"
+ }
+ }
+ }
+ };
+
+ // Modify row 0 numberinput to show error
+ fireEvent.change(numberInputs[0], { target: { value: 0 } });
+ expect(controller.getErrorMessages()).to.eql(expectedTwoErrors);
+
+ // Verify main table also display the error in cell
+ errors = mainTable.querySelectorAll(".properties-validation-message.error.inTable");
+ expect(errors.length).to.equal(1);
+
+ // Verify structurelisteditor table error gets updated
+ validationErrors = subPanel[0].querySelectorAll(".properties-validation-message.error");
+ expect(validationErrors.length).to.equal(3); // Two errors in the cell, the other is on table
+ expect(validationErrors[2].querySelector("span").textContent).to.equal("There are 2 error cells. ");
+
+ // Remove error from numberinput, table error should be set back to first error
+ fireEvent.change(numberInputs[0], { target: { value: 1 } });
+ expect(controller.getErrorMessages()).to.eql(expectedOneError);
+ validationErrors = subPanel[0].querySelectorAll(".properties-validation-message.error");
+ expect(validationErrors.length).to.equal(2); // First one is in cell, the other is on table
+ expect(validationErrors[1].querySelector("span").textContent).to.equal("Cannot be 'error'");
+ });
+
+ it("default required conditions work correctly for a nested structurelisteditor control", () => {
+ const container = wrapper.container;
+ const mainTable = container.querySelector("div[data-id='properties-ci-nested_table']");
+ const editButton = mainTable.querySelectorAll("button.properties-subpanel-button")[0];
+ fireEvent.click(editButton); // Click subpanel edit button on first row
+
+ // verify subpanel shows structurelisteditor control
+ const subPanel = container.querySelectorAll(".properties-editstyle-sub-panel");
+ expect(subPanel).to.have.length(1);
+
+ const structurelisteditorControl = subPanel[0].querySelectorAll("div[data-id='properties-ft-structurelisteditor']");
+ expect(structurelisteditorControl.length).to.equal(1);
+
+ const textInputs = structurelisteditorControl[0].querySelectorAll("input[type='text']");
+ expect(textInputs.length).to.equal(2);
+ const numberInputs = structurelisteditorControl[0].querySelectorAll("input[type='number']");
+ expect(numberInputs.length).to.equal(2);
+
+ const expectedOneError = {
+ "nested_table": {
+ "0": {
+ "4": {
+ "1": {
+ "0": {
+ "propertyId": {
+ "col": 4,
+ "name": "nested_table",
+ "propertyId": {
+ "col": 0,
+ "name": "structurelisteditor",
+ "row": 1
+ },
+ "row": 0
+ },
+ "required": true,
+ "text": "You must enter a value for textfield.",
+ "type": "error",
+ "validation_id": "required_nested_table[4][0]_186.07303925125697"
+ }
+ },
+ "propertyId": {
+ "col": 4,
+ "name": "nested_table",
+ "propertyId": {
+ "col": 0,
+ "name": "structurelisteditor",
+ "row": 1
+ },
+ "row": 0
+ },
+ "required": true,
+ "text": "You must enter a value for textfield.",
+ "type": "error",
+ "validation_id": "required_nested_table[4][0]_186.07303925125697"
+ }
+ }
+ }
+ };
+
+ // Modify row 1 textinput to show error
+ fireEvent.change(textInputs[1], { target: { value: "" } });
+ expect(controller.getErrorMessages()).to.eql(expectedOneError);
+
+ // Verify main parent table also display the error in cell
+ let errors = mainTable.querySelectorAll(".properties-validation-message.error.inTable");
+ expect(errors.length).to.equal(1);
+
+ // Verify structurelistedior also display the error in bottom of table
+ let validationErrors = subPanel[0].querySelectorAll(".properties-validation-message.error");
+ expect(validationErrors.length).to.equal(2); // First one is in cell, the other is on table
+ expect(validationErrors[1].querySelector("span").textContent).to.equal("You must enter a value for textfield.");
+
+ const expectedTwoErrors = {
+ "nested_table": {
+ "0": {
+ "4": {
+ "0": {
+ "1": {
+ "propertyId": {
+ "col": 4,
+ "name": "nested_table",
+ "propertyId": {
+ "col": 1,
+ "name": "structurelisteditor",
+ "row": 0
+ },
+ "row": 0
+ },
+ "required": true,
+ "text": "You must enter a value for numberfield.",
+ "type": "error",
+ "validation_id": "required_nested_table[4][1]_344.3166233602058"
+ }
+ },
+ "1": {
+ "0": {
+ "propertyId": {
+ "col": 4,
+ "name": "nested_table",
+ "propertyId": {
+ "col": 0,
+ "name": "structurelisteditor",
+ "row": 1
+ },
+ "row": 0
+ },
+ "required": true,
+ "tableText": "There are 2 error cells. ",
+ "text": "You must enter a value for textfield.",
+ "type": "error",
+ "validation_id": "required_nested_table[4][0]_186.07303925125697"
+ }
+ },
+ "propertyId": {
+ "col": 4,
+ "name": "nested_table",
+ "propertyId": {
+ "col": 1,
+ "name": "structurelisteditor",
+ "row": 0
+ },
+ "row": 0
+ },
+ "required": true,
+ "text": "You must enter a value for numberfield.",
+ "type": "error",
+ "validation_id": "required_nested_table[4][1]_344.3166233602058"
+ }
+ }
+ }
+ };
+
+ // Modify row 1 numberinput to show error
+ fireEvent.change(numberInputs[0], { target: { value: null } });
+ expect(controller.getErrorMessages()).to.eql(expectedTwoErrors);
+
+ // Verify main table also display the error in cell
+ errors = mainTable.querySelectorAll(".properties-validation-message.error.inTable");
+ expect(errors.length).to.equal(1);
+
+ // Verify structurelisteditor table error gets updated
+ validationErrors = subPanel[0].querySelectorAll(".properties-validation-message.error");
+ expect(validationErrors.length).to.equal(3); // Two errors in the cell, the other is on table
+ expect(validationErrors[2].querySelector("span").textContent).to.equal("There are 2 error cells. ");
+
+ // Remove error from numberinput, table error should be set back to first error
+ fireEvent.change(numberInputs[0], { target: { value: 1 } });
+ expect(controller.getErrorMessages()).to.eql(expectedOneError);
+ validationErrors = subPanel[0].querySelectorAll(".properties-validation-message.error");
+ expect(validationErrors.length).to.equal(2); // First one is in cell, the other is on table
+ expect(validationErrors[1].querySelector("span").textContent).to.equal("You must enter a value for textfield.");
+ });
+});
diff --git a/canvas_modules/common-canvas/__tests__/common-properties/controls/structurelisteditor-test.js b/canvas_modules/common-canvas/__tests__/common-properties/controls/structurelisteditor-test.js
index 0b32b4b455..b0d4a2d81d 100644
--- a/canvas_modules/common-canvas/__tests__/common-properties/controls/structurelisteditor-test.js
+++ b/canvas_modules/common-canvas/__tests__/common-properties/controls/structurelisteditor-test.js
@@ -620,7 +620,8 @@ describe("StructureListEditor render from paramdef", () => {
"required": false,
"validation_id": "tableerrortest3",
"type": "error",
- "text": "There are 2 error cells. ",
+ "tableText": "There are 2 error cells. ",
+ "text": "checkbox cannot be off"
};
actual = renderedController.getErrorMessage({ name: "inlineEditingTableError" });
diff --git a/canvas_modules/common-canvas/__tests__/test_resources/paramDefs/nestedConditions_paramDef.json b/canvas_modules/common-canvas/__tests__/test_resources/paramDefs/nestedConditions_paramDef.json
new file mode 100644
index 0000000000..53fa69f24b
--- /dev/null
+++ b/canvas_modules/common-canvas/__tests__/test_resources/paramDefs/nestedConditions_paramDef.json
@@ -0,0 +1,422 @@
+{
+ "titleDefinition": {
+ "title": "Nested cells"
+ },
+ "current_parameters": {
+ "nested_table": [
+ [
+ ["list1", "list2"],
+ ["Age", "Na"],
+ ["orange", "green"],
+ [["Sex", "red"], ["BP", "blue"]],
+ [["Hello", 1], ["world", 2]]
+ ]
+ ]
+ },
+ "parameters": [
+ {
+ "id": "nested_table",
+ "type": "array[table_controls]"
+ }
+ ],
+ "complex_types": [
+ {
+ "id": "table_controls",
+ "parameters": [
+ {
+ "id": "list",
+ "type": "array[string]",
+ "required": true
+ },
+ {
+ "id": "select_columns",
+ "type": "array[string]",
+ "role": "column"
+ },
+ {
+ "id": "someofselect",
+ "type": "array[string]",
+ "enum": [
+ "red",
+ "orange",
+ "yellow",
+ "green",
+ "blue",
+ "purple"
+ ]
+ },
+ {
+ "id": "structuretable",
+ "type": "map[string, nestedStructureTable]",
+ "role": "column"
+ },
+ {
+ "id": "structurelisteditor",
+ "type": "array[nestedStructureListEditor]"
+ }
+ ]
+ },
+ {
+ "id": "nestedStructureTable",
+ "parameters": [
+ {
+ "id": "field",
+ "type": "string",
+ "role": "column"
+ },
+ {
+ "id": "oneofselect",
+ "enum": [
+ "red",
+ "orange",
+ "yellow",
+ "green",
+ "blue",
+ "purple"
+ ]
+ }
+ ]
+ },
+ {
+ "id": "nestedStructureListEditor",
+ "parameters": [
+ {
+ "id": "textfield",
+ "type": "string",
+ "required": true
+ },
+ {
+ "id": "numberfield",
+ "type": "integer",
+ "required": true
+ }
+ ]
+ }
+ ],
+ "uihints": {
+ "id": "nested cells test",
+ "icon": "images/default.svg",
+ "label": {
+ "default": "Nested cells test"
+ },
+ "editor_size": "large",
+ "parameter_info": [
+ {
+ "parameter_ref": "nested_table",
+ "label": {
+ "default": "Table within table"
+ },
+ "description": {
+ "default": "This example displays conditions within a nested table.",
+ "placement": "on_panel"
+ }
+ }
+ ],
+ "complex_type_info": [
+ {
+ "complex_type_ref": "table_controls",
+ "parameters": [
+ {
+ "parameter_ref": "list",
+ "width": 10,
+ "label": {
+ "default": "list"
+ },
+ "description": {
+ "default": "Enter 'error' in a cell to display an error",
+ "placement": "on_panel"
+ },
+ "edit_style": "on_panel",
+ "control": "list"
+ },
+ {
+ "parameter_ref": "select_columns",
+ "width": 10,
+ "label": {
+ "default": "selectColumns"
+ },
+ "description": {
+ "default": "Select the column 'K' to display an error",
+ "placement": "on_panel"
+ },
+ "edit_style": "subpanel"
+ },
+ {
+ "parameter_ref": "someofselect",
+ "width": 10,
+ "label": {
+ "default": "someofselect"
+ },
+ "description": {
+ "default": "Select 'orange' below to display an error. It is preselected initially and error will not be shown until the control is modified",
+ "placement": "on_panel"
+ },
+ "edit_style": "subpanel"
+ },
+ {
+ "parameter_ref": "structuretable",
+ "width": 10,
+ "label": {
+ "default": "structuretable"
+ },
+ "description": {
+ "default": "Select 'green' in the second column to display an error",
+ "placement": "on_panel"
+ },
+ "edit_style": "on_panel"
+ },
+ {
+ "parameter_ref": "structurelisteditor",
+ "width": 10,
+ "label": {
+ "default": "structurelisteditor"
+ },
+ "description": {
+ "default": "Enter 'error' in a cell in the first column, or enter a number less than 1 in the second column to display an error. Clearing a cell error will update the table error to show the remaining error present in the table",
+ "placement": "on_panel"
+ },
+ "edit_style": "subpanel"
+ }
+ ]
+ },
+ {
+ "complex_type_ref": "nestedStructureTable",
+ "moveable_rows": true,
+ "parameters": [
+ {
+ "parameter_ref": "field",
+ "label": {
+ "default": "field"
+ }
+ },
+ {
+ "parameter_ref": "oneofselect",
+ "label": {
+ "default": "oneofselect"
+ }
+ }
+ ]
+ },
+ {
+ "complex_type_ref": "nestedStructureListEditor",
+ "moveable_rows": true,
+ "parameters": [
+ {
+ "parameter_ref": "textfield",
+ "label": {
+ "default": "textfield"
+ },
+ "control": "textfield"
+ },
+ {
+ "parameter_ref": "numberfield",
+ "label": {
+ "default": "numberfield"
+ },
+ "control": "numberfield"
+ }
+ ]
+ }
+ ],
+ "group_info": [
+ {
+ "id": "numberfield-table-panels",
+ "label": {
+ "default": "Table"
+ },
+ "type": "controls",
+ "parameter_refs": [
+ "nested_table"
+ ]
+ }
+ ]
+ },
+ "conditions": [
+ {
+ "validation": {
+ "fail_message": {
+ "type": "error",
+ "focus_parameter_ref": "nested_table[0]",
+ "message": {
+ "default": "Cannot be 'error'"
+ }
+ },
+ "evaluate": {
+ "condition": {
+ "parameter_ref": "nested_table[0]",
+ "op": "notEquals",
+ "value": "error"
+ }
+ }
+ }
+ },
+ {
+ "validation": {
+ "fail_message": {
+ "type": "error",
+ "focus_parameter_ref": "nested_table[1]",
+ "message": {
+ "default": "Cannot contain 'K'"
+ }
+ },
+ "evaluate": {
+ "condition": {
+ "parameter_ref": "nested_table[1]",
+ "op": "notContains",
+ "value": "K"
+ }
+ }
+ }
+ },
+ {
+ "validation": {
+ "fail_message": {
+ "type": "error",
+ "focus_parameter_ref": "nested_table[2]",
+ "message": {
+ "default": "Cannot select 'orange'"
+ }
+ },
+ "evaluate": {
+ "condition": {
+ "parameter_ref": "nested_table[2]",
+ "op": "notContains",
+ "value": "orange"
+ }
+ }
+ }
+ },
+ {
+ "validation": {
+ "fail_message": {
+ "type": "error",
+ "focus_parameter_ref": "nested_table[3]",
+ "message": {
+ "default": "Cannot select 'green'"
+ }
+ },
+ "evaluate": {
+ "condition": {
+ "parameter_ref": "nested_table[3][1]",
+ "op": "notEquals",
+ "value": "green"
+ }
+ }
+ }
+ },
+ {
+ "validation": {
+ "fail_message": {
+ "type": "error",
+ "focus_parameter_ref": "nested_table[4][0]",
+ "message": {
+ "default": "Cannot be 'error'"
+ }
+ },
+ "evaluate": {
+ "condition": {
+ "parameter_ref": "nested_table[4][0]",
+ "op": "notEquals",
+ "value": "error"
+ }
+ }
+ }
+ },
+ {
+ "validation": {
+ "fail_message": {
+ "type": "error",
+ "focus_parameter_ref": "nested_table[4][1]",
+ "message": {
+ "default": "Must be greater than 0"
+ }
+ },
+ "evaluate": {
+ "condition": {
+ "parameter_ref": "nested_table[4][1]",
+ "op": "greaterThan",
+ "value": "0"
+ }
+ }
+ }
+ }
+ ],
+ "dataset_metadata": [
+ {
+ "fields": [
+ {
+ "name": "Age",
+ "type": "integer",
+ "metadata": {
+ "description": "",
+ "measure": "range",
+ "modeling_role": "input"
+ }
+ },
+ {
+ "name": "Sex",
+ "type": "string",
+ "metadata": {
+ "description": "",
+ "measure": "ordered_set",
+ "modeling_role": "input"
+ }
+ },
+ {
+ "name": "BP",
+ "type": "string",
+ "metadata": {
+ "description": "",
+ "measure": "discrete",
+ "modeling_role": "input"
+ }
+ },
+ {
+ "name": "Cholesterol",
+ "type": "string",
+ "metadata": {
+ "description": "",
+ "measure": "set",
+ "modeling_role": "input"
+ }
+ },
+ {
+ "name": "Na",
+ "type": "double",
+ "metadata": {
+ "description": "",
+ "measure": "flag",
+ "modeling_role": "input"
+ }
+ },
+ {
+ "name": "K",
+ "type": "double",
+ "metadata": {
+ "description": "",
+ "measure": "collection",
+ "modeling_role": "input"
+ }
+ },
+ {
+ "name": "Drug",
+ "type": "string",
+ "metadata": {
+ "description": "",
+ "measure": "geospatial",
+ "modeling_role": "input"
+ }
+ },
+ {
+ "name": "Ag",
+ "type": "integer",
+ "metadata": {
+ "description": "",
+ "measure": "",
+ "modeling_role": "input"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/canvas_modules/common-canvas/__tests__/test_resources/paramDefs/numberfield_paramDef.json b/canvas_modules/common-canvas/__tests__/test_resources/paramDefs/numberfield_paramDef.json
index 8b32f247dc..134e26a917 100644
--- a/canvas_modules/common-canvas/__tests__/test_resources/paramDefs/numberfield_paramDef.json
+++ b/canvas_modules/common-canvas/__tests__/test_resources/paramDefs/numberfield_paramDef.json
@@ -4,6 +4,7 @@
},
"current_parameters": {
"number_int": 10,
+ "number_int_readonly": 10,
"number_dbl": 11.012,
"number_null": null,
"number_random": 12345,
@@ -28,6 +29,11 @@
"type": "integer",
"required": true
},
+ {
+ "id": "number_int_readonly",
+ "type": "integer",
+ "required": true
+ },
{
"id": "number_dbl",
"type": "double",
@@ -161,7 +167,24 @@
"description": {
"default": "numberfield with parameter value set to '10'"
},
- "class_name": "numberfield-control-class"
+ "class_name": "numberfield-control-class",
+ "helper_text": {
+ "default": "numberfield with parameter value set to '10'"
+ }
+ },
+ {
+ "parameter_ref": "number_int_readonly",
+ "label": {
+ "default": "Integer"
+ },
+ "description": {
+ "default": "Readonly numberfield with parameter value set to '10'"
+ },
+ "class_name": "numberfield-readonly-control-class",
+ "helper_text": {
+ "default": "Readonly numberfield with parameter value set to '10'"
+ },
+ "read_only": true
},
{
"parameter_ref": "number_dbl",
@@ -421,7 +444,8 @@
"number_random",
"number_random_resource_key",
"number_longControlName",
- "number_placeholder"
+ "number_placeholder",
+ "number_int_readonly"
]
},
{
diff --git a/canvas_modules/common-canvas/src/common-properties/components/validation-message/validation-message.jsx b/canvas_modules/common-canvas/src/common-properties/components/validation-message/validation-message.jsx
index 2414ab48f4..7b392f767d 100644
--- a/canvas_modules/common-canvas/src/common-properties/components/validation-message/validation-message.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/components/validation-message/validation-message.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import PropTypes from "prop-types";
import Icon from "./../../../icons/icon.jsx";
import Tooltip from "./../../../tooltip/tooltip.jsx";
import { STATES } from "./../../constants/constants.js";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import classNames from "classnames";
export default class ValidationMessage extends React.Component {
@@ -28,7 +29,13 @@ export default class ValidationMessage extends React.Component {
if (!this.props.messageInfo || (this.props.tableOnly && !this.props.inTable)) {
return null;
}
- const msgText = this.props.inTable ? null : {this.props.messageInfo.text};
+
+ // Check if this is a nested control, and if the messageInfo applies to that specific cell
+ if ((this.props.tableOnly || this.props.inTable) && !doesErrorMessageApplyToCell(this.props.propertyId, this.props.messageInfo)) {
+ return null;
+ }
+
+ const msgText = this.props.inTable ? null : {this.props.messageInfo.tableText || this.props.messageInfo.text};
const icon = (
{}
);
@@ -57,8 +64,11 @@ export default class ValidationMessage extends React.Component {
ValidationMessage.propTypes = {
messageInfo: PropTypes.shape({
text: PropTypes.string,
- type: PropTypes.string
+ tableText: PropTypes.string,
+ type: PropTypes.string,
+ propertyId: PropTypes.object
}),
+ propertyId: PropTypes.object,
state: PropTypes.string,
inTable: PropTypes.bool,
tableOnly: PropTypes.bool
diff --git a/canvas_modules/common-canvas/src/common-properties/constants/constants.js b/canvas_modules/common-canvas/src/common-properties/constants/constants.js
index bdc08fa10d..1fe217714d 100644
--- a/canvas_modules/common-canvas/src/common-properties/constants/constants.js
+++ b/canvas_modules/common-canvas/src/common-properties/constants/constants.js
@@ -153,6 +153,16 @@ export const CONDITION_MESSAGE_TYPE = {
SUCCESS: "success"
};
+export const DEFAULT_ERROR_MESSAGE_KEYS = [
+ "propertyId",
+ "required",
+ "text",
+ "type",
+ "validation_id",
+ "displayError",
+ "tableText"
+];
+
export const SPINNER = "spinner";
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/checkbox/checkbox.jsx b/canvas_modules/common-canvas/src/common-properties/controls/checkbox/checkbox.jsx
index 4a05d70e18..33085876a3 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/checkbox/checkbox.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/checkbox/checkbox.jsx
@@ -20,6 +20,7 @@ import { connect } from "react-redux";
import { isEmpty } from "lodash";
import { Checkbox } from "@carbon/react";
import ValidationMessage from "./../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import * as ControlUtils from "./../../util/control-utils";
import { STATES, CARBON_ICONS } from "./../../constants/constants.js";
import Tooltip from "./../../../tooltip/tooltip.jsx";
@@ -74,7 +75,9 @@ class CheckboxControl extends React.Component {
);
return (
-
);
}
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/checkboxset/checkboxset.jsx b/canvas_modules/common-canvas/src/common-properties/controls/checkboxset/checkboxset.jsx
index 6cda0412d8..89a06bf483 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/checkboxset/checkboxset.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/checkboxset/checkboxset.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import { Checkbox, CheckboxGroup } from "@carbon/react";
import * as ControlUtils from "./../../util/control-utils";
import classNames from "classnames";
import ValidationMessage from "./../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import { v4 as uuid4 } from "uuid";
import { intersection, isEqual } from "lodash";
import { Information } from "@carbon/react/icons";
@@ -132,11 +133,13 @@ class CheckboxsetControl extends React.Component {
readOnly={this.props.readOnly}
aria-label={this.props.control.labelVisible ? null : this.props.control?.label?.text}
>
-
);
}
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/datefield/datefield.jsx b/canvas_modules/common-canvas/src/common-properties/controls/datefield/datefield.jsx
index 578da96d33..e4426650c4 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/datefield/datefield.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/datefield/datefield.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import PropTypes from "prop-types";
import { connect } from "react-redux";
import { TextInput } from "@carbon/react";
import ValidationMessage from "./../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import * as ControlUtils from "./../../util/control-utils";
import { parse, format, isValid } from "date-fns";
import { DEFAULT_DATE_FORMAT, STATES } from "./../../constants/constants.js";
@@ -73,7 +74,7 @@ class DatefieldControl extends React.Component {
return null; // Do not render hidden controls
}
const className = classNames("properties-datefield", "properties-input-control", { "hide": hidden },
- this.props.messageInfo ? this.props.messageInfo.type : null);
+ this.props.messageInfo && doesErrorMessageApplyToCell(this.props.propertyId, this.props.messageInfo) ? this.props.messageInfo.type : null);
const validationProps = ControlUtils.getValidationProps(this.props.messageInfo, this.props.tableControl);
return (
@@ -91,7 +92,7 @@ class DatefieldControl extends React.Component {
readOnly={this.props.readOnly}
aria-label={this.props.control.labelVisible ? null : this.props.control?.label?.text}
/>
-
+
);
}
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/datepicker-range/datepicker-range.jsx b/canvas_modules/common-canvas/src/common-properties/controls/datepicker-range/datepicker-range.jsx
index c384fd0677..ae528aa392 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/datepicker-range/datepicker-range.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/datepicker-range/datepicker-range.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Elyra Authors
+ * Copyright 2023-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import { v4 as uuid4 } from "uuid";
import Tooltip from "./../../../tooltip/tooltip.jsx";
import Icon from "./../../../icons/icon.jsx";
import ValidationMessage from "../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import * as ControlUtils from "../../util/control-utils";
import { getFormattedDate, getISODate } from "../../util/date-utils";
import { STATES, DATEPICKER_TYPE, MESSAGE_KEYS, CARBON_ICONS } from "../../constants/constants.js";
@@ -134,7 +135,7 @@ class DatepickerRangeControl extends React.Component {
endLabel = this.createInfoDesc(endLabel, endDesc, "end");
const className = classNames("properties-datepicker-range", "properties-input-control", { "hide": hidden },
- this.props.messageInfo ? this.props.messageInfo.type : null);
+ this.props.messageInfo && doesErrorMessageApplyToCell(this.props.propertyId, this.props.messageInfo) ? this.props.messageInfo.type : null);
const validationProps = ControlUtils.getValidationProps(this.props.messageInfo, this.props.tableControl);
return (
@@ -172,7 +173,7 @@ class DatepickerRangeControl extends React.Component {
helperText={!this.props.tableControl && endHelperText}
/>
-
+
);
}
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/datepicker/datepicker.jsx b/canvas_modules/common-canvas/src/common-properties/controls/datepicker/datepicker.jsx
index 88f1f9d651..812e98bd7d 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/datepicker/datepicker.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/datepicker/datepicker.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Elyra Authors
+ * Copyright 2023-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@ import { DatePicker, DatePickerInput } from "@carbon/react";
import classNames from "classnames";
import ValidationMessage from "../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import * as ControlUtils from "../../util/control-utils";
import { getFormattedDate, getISODate } from "../../util/date-utils";
import { STATES, DATEPICKER_TYPE } from "../../constants/constants.js";
@@ -70,7 +71,7 @@ class DatepickerControl extends React.Component {
}
const helperText = this.props.controller.getResource(`${this.props.control.name}.helper`, null);
const className = classNames("properties-datepicker", "properties-input-control", { "hide": hidden },
- this.props.messageInfo ? this.props.messageInfo.type : null);
+ this.props.messageInfo && doesErrorMessageApplyToCell(this.props.propertyId, this.props.messageInfo) ? this.props.messageInfo.type : null);
const validationProps = ControlUtils.getValidationProps(this.props.messageInfo, this.props.tableControl);
return (
@@ -96,7 +97,7 @@ class DatepickerControl extends React.Component {
helperText={(!this.props.tableControl && helperText) || this.props.control.helperText}
/>
-
+
);
}
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/dropdown/dropdown.jsx b/canvas_modules/common-canvas/src/common-properties/controls/dropdown/dropdown.jsx
index 382d23c7b8..9db5f10210 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/dropdown/dropdown.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/dropdown/dropdown.jsx
@@ -21,6 +21,7 @@ import { SelectItem, Select, Dropdown, ComboBox } from "@carbon/react";
import { isEqual, isEmpty } from "lodash";
import * as ControlUtils from "./../../util/control-utils";
import ValidationMessage from "./../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import classNames from "classnames";
import * as PropertyUtils from "./../../util/property-utils.js";
import { ControlType } from "./../../constants/form-constants";
@@ -323,10 +324,11 @@ class DropDown extends React.Component {
return (
{dropdownComponent}
-
+
);
}
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/expression/expression.jsx b/canvas_modules/common-canvas/src/common-properties/controls/expression/expression.jsx
index 5a93e154d8..1e08591ed1 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/expression/expression.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/expression/expression.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2024 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -444,7 +444,7 @@ class ExpressionControl extends React.Component {
style={{ height: this.state.expressionEditorHeight, minHeight: minLineHeight }}
aria-disabled={this.props.state === STATES.DISABLED || !this.props.readOnly}
/>
-
+
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/list/list.jsx b/canvas_modules/common-canvas/src/common-properties/controls/list/list.jsx
index 51f620a868..429e45c360 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/list/list.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/list/list.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -191,7 +191,7 @@ class ListControl extends AbstractTable {
{table}
-
+
);
return (
@@ -219,6 +219,7 @@ ListControl.propTypes = {
controller: PropTypes.object.isRequired,
controlItem: PropTypes.element,
rightFlyout: PropTypes.bool,
+ tableControl: PropTypes.bool,
selectedRows: PropTypes.array, // set by redux
state: PropTypes.string, // pass in by redux
value: PropTypes.array, // pass in by redux
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/multiselect/multiselect.jsx b/canvas_modules/common-canvas/src/common-properties/controls/multiselect/multiselect.jsx
index ec0076595a..13fe91b150 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/multiselect/multiselect.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/multiselect/multiselect.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import { connect } from "react-redux";
import { MultiSelect, FilterableMultiSelect } from "@carbon/react";
import * as ControlUtils from "./../../util/control-utils";
import ValidationMessage from "./../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import classNames from "classnames";
import * as PropertyUtils from "./../../util/property-utils.js";
import { MESSAGE_KEYS, STATES } from "./../../constants/constants.js";
@@ -193,10 +194,11 @@ class MultiSelectControl extends React.Component {
return (
{dropdownComponent}
-
+
);
}
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/numberfield/numberfield.jsx b/canvas_modules/common-canvas/src/common-properties/controls/numberfield/numberfield.jsx
index 00deb19d20..a96290a5a7 100755
--- a/canvas_modules/common-canvas/src/common-properties/controls/numberfield/numberfield.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/numberfield/numberfield.jsx
@@ -21,6 +21,7 @@ import { NumberInput, Button } from "@carbon/react";
import ValidationMessage from "./../../components/validation-message";
import * as ControlUtils from "./../../util/control-utils";
import { formatMessage } from "./../../util/property-utils";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import { STATES, MESSAGE_KEYS } from "./../../constants/constants.js";
import classNames from "classnames";
import { ControlType } from "./../../constants/form-constants";
@@ -170,7 +171,7 @@ class NumberfieldControl extends React.Component {
"properties-numberfield",
{ "numberfield-with-number-generator": has(this.props.control, "label.numberGenerator") ? this.props.control.label.numberGenerator : null },
{ "hide": hidden },
- this.props.messageInfo ? this.props.messageInfo.type : null
+ this.props.messageInfo && doesErrorMessageApplyToCell(this.props.propertyId, this.props.messageInfo) ? this.props.messageInfo.type : null
);
const validationProps = ControlUtils.getValidationProps(this.props.messageInfo, this.props.tableControl);
return (
@@ -195,7 +196,7 @@ class NumberfieldControl extends React.Component {
aria-label={this.props.control.labelVisible ? null : this.props.control?.label?.text}
/>
{numberGenerator}
-
+
);
}
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/passwordfield/passwordfield.jsx b/canvas_modules/common-canvas/src/common-properties/controls/passwordfield/passwordfield.jsx
index 4d2f77be4e..f678ef3d27 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/passwordfield/passwordfield.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/passwordfield/passwordfield.jsx
@@ -19,6 +19,7 @@ import PropTypes from "prop-types";
import { connect } from "react-redux";
import { PasswordInput } from "@carbon/react";
import ValidationMessage from "./../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import * as ControlUtils from "./../../util/control-utils";
import { STATES, MESSAGE_KEYS } from "./../../constants/constants.js";
import classNames from "classnames";
@@ -51,7 +52,7 @@ class PasswordControl extends React.Component {
const hidePasswordLabel = this.props.controller.getResource(overrideHidePasswordLabel, defaultHidePasswordLabel);
const value = this.props.value ? this.props.value : "";
const className = classNames("properties-pwdfield", "properties-input-control", { "hide": hidden },
- this.props.messageInfo ? this.props.messageInfo.type : null);
+ this.props.messageInfo && doesErrorMessageApplyToCell(this.props.propertyId, this.props.messageInfo) ? this.props.messageInfo.type : null);
const validationProps = ControlUtils.getValidationProps(this.props.messageInfo, this.props.tableControl);
return (
@@ -71,7 +72,7 @@ class PasswordControl extends React.Component {
helperText={this.props.control.helperText}
aria-label={this.props.control.labelVisible ? null : this.props.control?.label?.text}
/>
-
+
);
}
}
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/radioset/radioset.jsx b/canvas_modules/common-canvas/src/common-properties/controls/radioset/radioset.jsx
index 22c7189004..b623ec1895 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/radioset/radioset.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/radioset/radioset.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import * as ControlUtils from "./../../util/control-utils";
import * as PropertyUtils from "./../../util/property-utils";
import * as ConditionsUtils from "./../../ui-conditions/conditions-utils.js";
import ValidationMessage from "./../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import { RadioButton, RadioButtonGroup } from "@carbon/react";
import classNames from "classnames";
import { MESSAGE_KEYS, STATES, UPDATE_TYPE } from "./../../constants/constants.js";
@@ -234,7 +235,9 @@ class RadiosetControl extends React.Component {
-
{buttons}
-
+
);
}
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/readonly/readonly.jsx b/canvas_modules/common-canvas/src/common-properties/controls/readonly/readonly.jsx
index 9b4502f1e4..fa28bf316e 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/readonly/readonly.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/readonly/readonly.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -144,7 +144,7 @@ class ReadonlyControl extends React.Component {
>
{this.props.tableControl ? null : this.props.controlItem}
{display}
-
+
);
}
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/readonlytable/readonlytable.jsx b/canvas_modules/common-canvas/src/common-properties/controls/readonlytable/readonlytable.jsx
index 73406a93d8..09c99e3d3a 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/readonlytable/readonlytable.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/readonlytable/readonlytable.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -66,7 +66,7 @@ class ReadonlyTableControl extends AbstractTable {
{table}
-
+
);
return (
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/selectcolumns/selectcolumns.jsx b/canvas_modules/common-canvas/src/common-properties/controls/selectcolumns/selectcolumns.jsx
index bcab06849d..fc61e7b23a 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/selectcolumns/selectcolumns.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/selectcolumns/selectcolumns.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -177,7 +177,7 @@ class SelectColumnsControl extends AbstractTable {
{table}
-
+
);
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/slider/slider.jsx b/canvas_modules/common-canvas/src/common-properties/controls/slider/slider.jsx
index e54d1305e0..39d98138c5 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/slider/slider.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/slider/slider.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import { v4 as uuid4 } from "uuid";
import * as ControlUtils from "../../util/control-utils";
import ValidationMessage from "../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import { STATES, MESSAGE_KEYS } from "../../constants/constants";
import { formatMessage } from "./../../util/property-utils";
@@ -51,8 +52,8 @@ class SliderControl extends React.Component {
const step = this.props.control.increment ? this.props.control.increment : 1;
return (
-
-
+
);
}
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/someofselect/someofselect.jsx b/canvas_modules/common-canvas/src/common-properties/controls/someofselect/someofselect.jsx
index 8be5f93d1c..2348743fce 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/someofselect/someofselect.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/someofselect/someofselect.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import { connect } from "react-redux";
import FlexibleTable from "../../components/flexible-table";
import * as ControlUtils from "./../../util/control-utils";
import ValidationMessage from "./../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import classNames from "classnames";
import { isEqual, intersection } from "lodash";
@@ -100,7 +101,7 @@ class SomeofselectControl extends React.Component {
return (
{this.props.controlItem}
-
+
);
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/structureeditor/structureeditor.jsx b/canvas_modules/common-canvas/src/common-properties/controls/structureeditor/structureeditor.jsx
index 0e662f4070..a6902e01cd 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/structureeditor/structureeditor.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/structureeditor/structureeditor.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import { connect } from "react-redux";
import ControlFactory from "../control-factory";
import * as ControlUtils from "./../../util/control-utils";
import ValidationMessage from "./../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import classNames from "classnames";
import { STATES } from "./../../constants/constants.js";
import { cloneDeep } from "lodash";
@@ -149,10 +150,10 @@ class StructureEditorControl extends React.Component {
return (
{controls}
-
+
);
}
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/structurelisteditor/structurelisteditor.jsx b/canvas_modules/common-canvas/src/common-properties/controls/structurelisteditor/structurelisteditor.jsx
index 69adfb0df9..307c5569da 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/structurelisteditor/structurelisteditor.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/structurelisteditor/structurelisteditor.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -59,7 +59,7 @@ class StructurelisteditorControl extends AbstractTable {
{table}
-
+
);
const onPanelContainer = this.getOnPanelContainer(this.props.selectedRows);
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/structuretable/structuretable.jsx b/canvas_modules/common-canvas/src/common-properties/controls/structuretable/structuretable.jsx
index cbcbc91842..92d2def004 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/structuretable/structuretable.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/structuretable/structuretable.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -173,7 +173,7 @@ class StructureTableControl extends AbstractTable {
{table}
-
+
);
const onPanelContainer = this.getOnPanelContainer(this.props.selectedRows);
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/textarea/textarea.jsx b/canvas_modules/common-canvas/src/common-properties/controls/textarea/textarea.jsx
index e3c8c420e4..6439ae72ad 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/textarea/textarea.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/textarea/textarea.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import PropTypes from "prop-types";
import { connect } from "react-redux";
import { TextArea } from "@carbon/react";
import ValidationMessage from "./../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import * as ControlUtils from "./../../util/control-utils";
import { formatMessage } from "./../../util/property-utils";
import { STATES } from "./../../constants/constants.js";
@@ -87,7 +88,7 @@ class TextareaControl extends React.Component {
readOnly={this.props.readOnly}
aria-label={this.props.control.labelVisible ? null : this.props.control?.label?.text}
/>
-
+
);
} else {
textArea = (
@@ -129,11 +130,17 @@ class TextareaControl extends React.Component {
{textArea}
);
}
- const className = classNames("properties-textarea", { "hide": hidden }, this.props.messageInfo ? this.props.messageInfo.type : null);
+ const className = classNames("properties-textarea", { "hide": hidden },
+ this.props.messageInfo && doesErrorMessageApplyToCell(this.props.propertyId, this.props.messageInfo) ? this.props.messageInfo.type : null);
return (
{display}
-
+
);
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/textfield/textfield.jsx b/canvas_modules/common-canvas/src/common-properties/controls/textfield/textfield.jsx
index 92ac45812e..5aa4dff105 100755
--- a/canvas_modules/common-canvas/src/common-properties/controls/textfield/textfield.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/textfield/textfield.jsx
@@ -20,6 +20,7 @@ import { connect } from "react-redux";
import { TextInput } from "@carbon/react";
import ReadonlyControl from "./../readonly";
import ValidationMessage from "./../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import * as ControlUtils from "./../../util/control-utils";
import { formatMessage } from "./../../util/property-utils";
import { STATES } from "./../../constants/constants.js";
@@ -79,7 +80,7 @@ class TextfieldControl extends React.Component {
truncated = value.length !== 0 && value.length !== this.props.value.length;
}
const className = classNames("properties-textfield", "properties-input-control", { "hide": hidden },
- this.props.messageInfo ? this.props.messageInfo.type : null);
+ this.props.messageInfo && doesErrorMessageApplyToCell(this.props.propertyId, this.props.messageInfo) ? this.props.messageInfo.type : null);
let textInput = null;
if (truncated) {
@@ -97,7 +98,7 @@ class TextfieldControl extends React.Component {
tableControl={this.props.tableControl}
/>
{/* // TODO this could conflict with the below ValidationMessage. */}
-
+
);
} else {
const validationProps = ControlUtils.getValidationProps(this.props.messageInfo, this.props.tableControl);
@@ -139,7 +140,7 @@ class TextfieldControl extends React.Component {
return (
{textInput}
-
+
);
}
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/timefield/timefield.jsx b/canvas_modules/common-canvas/src/common-properties/controls/timefield/timefield.jsx
index 23554364d3..8ea5aa3919 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/timefield/timefield.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/timefield/timefield.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import { parse, format, isValid } from "date-fns";
import classNames from "classnames";
import ValidationMessage from "./../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import * as ControlUtils from "./../../util/control-utils";
import { DEFAULT_TIME_FORMAT, STATES } from "./../../constants/constants";
@@ -71,7 +72,7 @@ class TimefieldControl extends React.Component {
return null; // Do not render hidden controls
}
const className = classNames("properties-timefield", "properties-input-control", { "hide": hidden },
- this.props.messageInfo ? this.props.messageInfo.type : null);
+ this.props.messageInfo && doesErrorMessageApplyToCell(this.props.propertyId, this.props.messageInfo) ? this.props.messageInfo.type : null);
const validationProps = ControlUtils.getValidationProps(this.props.messageInfo, this.props.tableControl);
return (
@@ -89,7 +90,7 @@ class TimefieldControl extends React.Component {
readOnly={this.props.readOnly}
aria-label={this.props.control.labelVisible ? null : this.props.control?.label?.text}
/>
-
+
);
}
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/toggle/toggle.jsx b/canvas_modules/common-canvas/src/common-properties/controls/toggle/toggle.jsx
index ee360dea20..37431d5b89 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/toggle/toggle.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/toggle/toggle.jsx
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import PropTypes from "prop-types";
import { connect } from "react-redux";
import { Toggle } from "@carbon/react";
import ValidationMessage from "./../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import * as ControlUtils from "./../../util/control-utils";
import { STATES, MESSAGE_KEYS } from "./../../constants/constants.js";
import classNames from "classnames";
@@ -61,7 +62,8 @@ class ToggleControl extends React.Component {
aria-labelledby={`${this.props.propertyId?.name}-toggle-label`}
readOnly={this.props.readOnly}
/>);
- const className = classNames("properties-toggle", { "hide": hidden }, this.props.messageInfo ? this.props.messageInfo.type : null);
+ const className = classNames("properties-toggle", { "hide": hidden },
+ this.props.messageInfo && doesErrorMessageApplyToCell(this.props.propertyId, this.props.messageInfo) ? this.props.messageInfo.type : null);
return (
{
@@ -74,7 +76,7 @@ class ToggleControl extends React.Component {
)
}
{toggleControl}
-
+
);
diff --git a/canvas_modules/common-canvas/src/common-properties/controls/toggletext/toggletext.jsx b/canvas_modules/common-canvas/src/common-properties/controls/toggletext/toggletext.jsx
index db93a193de..6a22cc1147 100644
--- a/canvas_modules/common-canvas/src/common-properties/controls/toggletext/toggletext.jsx
+++ b/canvas_modules/common-canvas/src/common-properties/controls/toggletext/toggletext.jsx
@@ -19,6 +19,7 @@ import PropTypes from "prop-types";
import { connect } from "react-redux";
import { Button } from "@carbon/react";
import ValidationMessage from "./../../components/validation-message";
+import { doesErrorMessageApplyToCell } from "../../ui-conditions/validation-utils.js";
import * as ControlUtils from "./../../util/control-utils";
import { formatMessage } from "./../../util/property-utils";
import { STATES, MESSAGE_KEYS } from "./../../constants/constants.js";
@@ -90,7 +91,8 @@ class ToggletextControl extends React.Component {
);
}
- const className = classNames("properties-toggletext", { "hide": hidden }, this.props.messageInfo ? this.props.messageInfo.type : null);
+ const className = classNames("properties-toggletext", { "hide": hidden },
+ this.props.messageInfo && doesErrorMessageApplyToCell(this.props.propertyId, this.props.messageInfo) ? this.props.messageInfo.type : null);
return (
{this.props.tableControl ? null : this.props.controlItem}
{button}
-
+
);
}
diff --git a/canvas_modules/common-canvas/src/common-properties/properties-controller.js b/canvas_modules/common-canvas/src/common-properties/properties-controller.js
index 136e53f7f3..593465412a 100644
--- a/canvas_modules/common-canvas/src/common-properties/properties-controller.js
+++ b/canvas_modules/common-canvas/src/common-properties/properties-controller.js
@@ -315,7 +315,9 @@ export default class PropertiesController {
// with the the name and col set from the definition ref.
const baseId = conditionsUtil.getParamRefPropertyId(conditionKey);
// baseId.col and propertyId.col can be undefined
- if (baseId.name === propertyId.name && baseId.col === propertyId.col) {
+ if (baseId.name === propertyId.name && baseId.col === propertyId.col &&
+ (!baseId.propertyId || (baseId.propertyId && baseId.propertyId.col === propertyId.propertyId?.col))
+ ) {
retCond = retCond.concat(conditionDefinitions[conditionKey]);
}
}
diff --git a/canvas_modules/common-canvas/src/common-properties/properties-store.js b/canvas_modules/common-canvas/src/common-properties/properties-store.js
index b6766085b5..c88423fd8a 100644
--- a/canvas_modules/common-canvas/src/common-properties/properties-store.js
+++ b/canvas_modules/common-canvas/src/common-properties/properties-store.js
@@ -15,7 +15,7 @@
*/
import { createStore, combineReducers } from "redux";
-import { has, get, isEqual, cloneDeep } from "lodash";
+import { has, get, isEqual, cloneDeep, difference } from "lodash";
import { setTearsheetState } from "./actions";
import { setPropertyValues, updatePropertyValue, removePropertyValue } from "./actions";
import { setControlStates, updateControlState } from "./actions";
@@ -42,7 +42,7 @@ import wideFlyoutPrimaryButtonDisableReducer from "./reducers/wide-flyout-primar
import propertiesSettingsReducer from "./reducers/properties-settings";
import tearsheetStatesReducer from "./reducers/tearsheet-states";
import * as PropertyUtils from "./util/property-utils.js";
-import { CONDITION_MESSAGE_TYPE, MESSAGE_KEYS } from "./constants/constants.js";
+import { CONDITION_MESSAGE_TYPE, MESSAGE_KEYS, DEFAULT_ERROR_MESSAGE_KEYS } from "./constants/constants.js";
/* eslint max-depth: ["error", 6] */
@@ -275,9 +275,33 @@ export default class PropertiesStore {
if (typeof propertyId.row !== "undefined" && controlMsg) {
controlMsg = controlMsg[propertyId.row.toString()];
if (typeof propertyId.col !== "undefined" && controlMsg) {
- return controlMsg[propertyId.col.toString()]; // return cell message
- }
- if (controlMsg && controlMsg.text) {
+ const cellMessage = controlMsg[propertyId.col.toString()]; // return cell message
+ const cellErrors = cellMessage ? difference(Object.keys(cellMessage), DEFAULT_ERROR_MESSAGE_KEYS) : [];
+ if (cellErrors.length > 0) {
+ delete cellMessage.tableText;
+ let tableReturnMessage = null;
+ let errorMsgCount = 0;
+ let warningMsgCount = 0;
+ cellErrors.forEach((cellError) => {
+ const countedCellMessages = this._countTableCellErrors(cellMessage[cellError]);
+ errorMsgCount += countedCellMessages.errorMsgCount;
+ warningMsgCount += countedCellMessages.warningMsgCount;
+ tableReturnMessage = countedCellMessages.returnMessage;
+ });
+ const tableErrorMsg = this._getTableReturnMessage(errorMsgCount, warningMsgCount, tableReturnMessage, intl);
+ // Only get the updated messages
+ const messages = {};
+ if (tableErrorMsg) {
+ messages.type = tableErrorMsg.type;
+ messages.text = tableErrorMsg.text;
+ if (tableErrorMsg.tableText) {
+ messages.tableText = tableErrorMsg.tableText;
+ }
+ }
+ return Object.assign({}, cellMessage, messages);
+ }
+ return cellMessage;
+ } else if (controlMsg && controlMsg.text) {
return controlMsg;
}
}
@@ -298,8 +322,7 @@ export default class PropertiesStore {
return controlMessage;
}
- // get the table cell level error messages. if more than one cell is in error, return summary message;
- _getTableCellErrors(controlMsg, intl) {
+ _countTableCellErrors(controlMsg) {
let returnMessage = null;
let errorMsgCount = 0;
let warningMsgCount = 0;
@@ -334,13 +357,18 @@ export default class PropertiesStore {
}
}
}
- if ((errorMsgCount + warningMsgCount) !== 1 && returnMessage) {
+ return { errorMsgCount, warningMsgCount, returnMessage };
+ }
+
+ _getTableReturnMessage(errorMsgCount, warningMsgCount, returnMessage, intl) {
+ delete returnMessage?.tableText;
+ if ((errorMsgCount + warningMsgCount) > 1 && returnMessage) {
returnMessage.type = (errorMsgCount > 0) ? CONDITION_MESSAGE_TYPE.ERROR : CONDITION_MESSAGE_TYPE.WARNING;
- returnMessage.text = (errorMsgCount > 0)
+ returnMessage.tableText = (errorMsgCount > 0)
? PropertyUtils.formatMessage(intl,
MESSAGE_KEYS.TABLE_SUMMARY_ERROR, { errorMsgCount: errorMsgCount })
: "";
- returnMessage.text += (warningMsgCount > 0)
+ returnMessage.tableText += (warningMsgCount > 0)
? PropertyUtils.formatMessage(intl,
MESSAGE_KEYS.TABLE_SUMMARY_WARNING, { warningMsgCount: warningMsgCount })
: "";
@@ -348,6 +376,12 @@ export default class PropertiesStore {
return returnMessage;
}
+ // get the table cell level error messages. if more than one cell is in error, return summary message;
+ _getTableCellErrors(controlMsg, intl) {
+ const { errorMsgCount, warningMsgCount, returnMessage } = this._countTableCellErrors(controlMsg);
+ return this._getTableReturnMessage(errorMsgCount, warningMsgCount, returnMessage, intl);
+ }
+
getErrorMessages() {
const state = this.store.getState();
return PropertyUtils.copy(state.errorMessagesReducer);
diff --git a/canvas_modules/common-canvas/src/common-properties/reducers/error-messages.js b/canvas_modules/common-canvas/src/common-properties/reducers/error-messages.js
index 06e4d5f272..2d24c60d20 100644
--- a/canvas_modules/common-canvas/src/common-properties/reducers/error-messages.js
+++ b/canvas_modules/common-canvas/src/common-properties/reducers/error-messages.js
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,8 @@
*/
import { UPDATE_ERROR_MESSAGE, SET_ERROR_MESSAGES, CLEAR_ERROR_MESSAGE } from "../actions";
-import { isEmpty } from "lodash";
+import { DEFAULT_ERROR_MESSAGE_KEYS } from "../constants/constants";
+import { isEmpty, difference } from "lodash";
/* eslint max-depth: ["error", 6] */
/*
@@ -31,19 +32,8 @@ function messages(state = {}, action) {
newState[propertyId.name] = {};
}
if (typeof propertyId.row !== "undefined") {
- const strRow = propertyId.row.toString();
- if (typeof newState[propertyId.name][strRow] === "undefined") {
- newState[propertyId.name][strRow] = {};
- }
- if (typeof propertyId.col !== "undefined") {
- const strCol = propertyId.col.toString();
- if (typeof newState[propertyId.name][strRow][strCol] === "undefined") {
- newState[propertyId.name][strRow][strCol] = {};
- }
- newState[propertyId.name][strRow][strCol] = action.message.value;
- } else {
- newState[propertyId.name][strRow] = Object.assign({}, action.message.value);
- }
+ const newNameState = newState[propertyId.name];
+ updateNestedPropertyValue(propertyId, newNameState, action.message.value);
} else {
newState[propertyId.name] = Object.assign({}, action.message.value);
}
@@ -51,22 +41,30 @@ function messages(state = {}, action) {
}
case CLEAR_ERROR_MESSAGE: {
const newState = state;
- if (newState[action.message.propertyId.name]) {
- if (typeof action.message.propertyId.row !== "undefined") {
- if (typeof action.message.propertyId.col !== "undefined") {
- delete newState[action.message.propertyId.name][action.message.propertyId.row][action.message.propertyId.col];
+ const propertyId = action.message.propertyId;
+ if (newState[propertyId.name]) {
+ if (typeof propertyId.row !== "undefined") {
+ if (typeof propertyId.col !== "undefined") {
+ if (typeof propertyId.propertyId !== "undefined" && typeof propertyId.propertyId.row !== "undefined") { // clear subcell
+ clearNestedPropertyMessage(propertyId.propertyId, newState[propertyId.name][propertyId.row][propertyId.col]);
+ clearCellMessage(propertyId.col, newState[propertyId.name][propertyId.row]);
+ updateParentErrorMessage(propertyId.col, propertyId.row, newState[propertyId.name]);
+ } else {
+ delete newState[propertyId.name][propertyId.row][propertyId.col];
+ }
} else {
- delete newState[action.message.propertyId.name][action.message.propertyId.row];
+ delete newState[propertyId.name][propertyId.row];
}
} else {
- delete newState[action.message.propertyId.name].type;
- delete newState[action.message.propertyId.name].text;
- delete newState[action.message.propertyId.name].validation_id;
- delete newState[action.message.propertyId.name].required;
- delete newState[action.message.propertyId.name].propertyId;
- delete newState[action.message.propertyId.name].displayError;
- if (isEmpty(newState[action.message.propertyId.name])) {
- delete newState[action.message.propertyId.name];
+ delete newState[propertyId.name].type;
+ delete newState[propertyId.name].text;
+ delete newState[propertyId.name].validation_id;
+ delete newState[propertyId.name].required;
+ delete newState[propertyId.name].propertyId;
+ delete newState[propertyId.name].displayError;
+ delete newState[propertyId.name].tableText;
+ if (isEmpty(newState[propertyId.name])) {
+ delete newState[propertyId.name];
}
}
}
@@ -81,4 +79,81 @@ function messages(state = {}, action) {
}
}
+function updateNestedPropertyValue(propertyId, newState, value) {
+ if (typeof propertyId.row !== "undefined") {
+ const strRow = propertyId.row.toString();
+
+ if (typeof newState[strRow] === "undefined") {
+ newState[strRow] = {};
+ } else if (typeof newState[strRow].displayError !== "undefined") {
+ delete newState[strRow].displayError;
+ }
+ delete newState[strRow].tableText;
+
+ if (typeof propertyId.col !== "undefined") {
+ const strCol = propertyId.col.toString();
+ if (typeof newState[strRow][strCol] === "undefined") {
+ newState[strRow][strCol] = {};
+ } else if (typeof newState[strRow][strCol].displayError !== "undefined") {
+ delete newState[strRow][strCol].displayError;
+ }
+ delete newState[strRow][strCol].tableText;
+ newState[strRow][strCol] = Object.assign({}, newState[strRow][strCol], value);
+ if (typeof propertyId.propertyId !== "undefined") {
+ updateNestedPropertyValue(propertyId.propertyId, newState[strRow][strCol], Object.assign({}, value));
+ }
+ } else if (typeof propertyId.propertyId !== "undefined") { // nested structureeditor
+ updateNestedPropertyValue(propertyId.propertyId, newState[strRow], value);
+ } else {
+ newState[strRow] = Object.assign({}, newState[strRow], value);
+ }
+ }
+}
+
+function clearNestedPropertyMessage(propertyId, newState) {
+ if (typeof propertyId.col !== "undefined") {
+ if (typeof propertyId.propertyId !== "undefined" && typeof propertyId.propertyId.row !== "undefined") { // clear subcell
+ clearNestedPropertyMessage(propertyId.propertyId, newState[propertyId.row][propertyId.col]);
+ clearCellMessage(propertyId.col, newState[propertyId.row]);
+ updateParentErrorMessage(propertyId.col, propertyId.row, newState);
+ } else {
+ delete newState[propertyId.row][propertyId.col];
+ clearCellMessage(propertyId.row, newState);
+ }
+ } else {
+ delete newState[propertyId.row];
+ delete newState.tableText;
+ }
+}
+
+// Check that no cells have error before deleting the error from the parent row/col
+function clearCellMessage(col, newState) {
+ const cellErrors = difference(Object.keys(newState[col]), DEFAULT_ERROR_MESSAGE_KEYS);
+ if (cellErrors.length === 0) {
+ delete newState[col];
+ delete newState.tableText;
+ }
+}
+
+// Once an error is cleared from the nested cell, and there is still another error present,
+// ensure that the parent error reflects the error still present, instead of the error that was removed
+function updateParentErrorMessage(errIdx, delIdx, newState) {
+ // delete newState.tableText;
+ const errorState = newState[delIdx];
+ const cellErrors = difference(Object.keys(errorState[errIdx]), DEFAULT_ERROR_MESSAGE_KEYS);
+ if (typeof errorState[errIdx].type !== "undefined") {
+ const remainingCellErrorIdx = cellErrors[0]; // Set the parent error to the first cell
+ const cellError = difference(Object.keys(errorState[errIdx][remainingCellErrorIdx]), DEFAULT_ERROR_MESSAGE_KEYS);
+ if (cellError.length === 0) {
+ const remainingError = errorState[errIdx][remainingCellErrorIdx];
+ delete remainingError.tableText;
+ errorState[errIdx] = Object.assign({}, errorState[errIdx], remainingError);
+ } else { // go deeper to find the nested error
+ const remainingError = errorState[errIdx][remainingCellErrorIdx][cellError[0]];
+ delete remainingError.tableText;
+ errorState[errIdx] = Object.assign({}, errorState[errIdx], remainingError);
+ }
+ }
+}
+
export default messages;
diff --git a/canvas_modules/common-canvas/src/common-properties/ui-conditions/conditions-utils.js b/canvas_modules/common-canvas/src/common-properties/ui-conditions/conditions-utils.js
index e508ac6b7c..b5b030e864 100644
--- a/canvas_modules/common-canvas/src/common-properties/ui-conditions/conditions-utils.js
+++ b/canvas_modules/common-canvas/src/common-properties/ui-conditions/conditions-utils.js
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Elyra Authors
+ * Copyright 2017-2025 Elyra Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -201,6 +201,13 @@ function validateInput(inPropertyId, controller, showErrors = true) {
propertyId.col = 0;
_validateInput(propertyId, controller, control, showErrors);
}
+ } else if (typeof propertyId.row !== "undefined" && control.controlType === "selectcolumns") { // validate each row in the array within a table.
+ for (let rowIndex = 0; rowIndex < controlValue.length; rowIndex++) {
+ const newNestedPropertyId = {};
+ newNestedPropertyId.row = rowIndex;
+ propertyId.propertyId = newNestedPropertyId;
+ _validateInput(propertyId, controller, control, showErrors);
+ }
}
} else {
@@ -549,10 +556,21 @@ function getParamRefPropertyId(paramRef, controlPropertyId) {
const offset = paramRef.indexOf("[");
if (offset > -1) {
baseParam.name = paramRef.substring(0, offset);
- baseParam.col = parseInt(paramRef.substring(offset + 1, paramRef.indexOf("]")), 10);
+ const colOffset = paramRef.substring(offset + 1);
+ baseParam.col = parseInt(colOffset.substring(0, paramRef.indexOf("]")), 10);
+
+ const nestedOffset = colOffset.indexOf("[");
+ if (nestedOffset > -1) {
+ const nestedColOffset = colOffset.substring(nestedOffset + 1);
+ const nestedCol = parseInt(nestedColOffset.substring(0, paramRef.indexOf("]")), 10);
+ baseParam.propertyId = { col: nestedCol };
+ }
}
if (controlPropertyId) {
baseParam.row = controlPropertyId.row;
+ if (controlPropertyId.propertyId) {
+ baseParam.propertyId = controlPropertyId.propertyId;
+ }
}
return baseParam;
}
@@ -710,10 +728,11 @@ function _validateInput(propertyId, controller, control, showErrors) {
errorSet = false;
}
// Before setting an error message for table cell, clear the error message for table (if any)
+ // only if there are no nested propertyId
if (typeof msgPropertyId.row !== "undefined" || typeof msgPropertyId.col !== "undefined") {
const tablePropertyId = controller.convertPropertyId(msgPropertyId.name);
const tableErrorMessage = controller.getErrorMessage(tablePropertyId);
- if (tableErrorMessage !== null) {
+ if (tableErrorMessage !== null && !msgPropertyId.propertyId) {
controller.updateErrorMessage(tablePropertyId, null);
}
}
@@ -723,7 +742,7 @@ function _validateInput(propertyId, controller, control, showErrors) {
if (isError) {
errorSet = true;
}
- } else if ((!isError && !errorSet) || (!isError && errorSet)) {
+ } else if (!isError) {
const msg = controller.getErrorMessage(msgPropertyId, false, false, false);
if (!isEmpty(msg) && (msg.validation_id === errorMessage.validation_id)) {
controller.updateErrorMessage(msgPropertyId, DEFAULT_VALIDATION_MESSAGE);
diff --git a/canvas_modules/common-canvas/src/common-properties/ui-conditions/validation-utils.js b/canvas_modules/common-canvas/src/common-properties/ui-conditions/validation-utils.js
new file mode 100644
index 0000000000..c73066c137
--- /dev/null
+++ b/canvas_modules/common-canvas/src/common-properties/ui-conditions/validation-utils.js
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2025 Elyra Authors
+ *
+ * Licensed 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.
+ */
+
+/**
+* Check if the given propertyId is a nested control, and if the messageInfo applies to that specific cell
+*
+* @param {object} propertyId. required
+* @param {object} messageInfo. required
+*/
+function doesErrorMessageApplyToCell(propertyId, messageInfo) {
+ if (propertyId?.propertyId && messageInfo.propertyId?.propertyId) {
+ const currentCellRow = propertyId?.propertyId?.row;
+ const currentCellCol = propertyId?.propertyId?.col;
+ const errorCellRow = messageInfo[currentCellRow];
+ if (!errorCellRow || (typeof currentCellCol !== "undefined" && typeof errorCellRow[currentCellCol] === "undefined")) {
+ return false;
+ }
+ } else if (typeof propertyId?.index !== "undefined" && messageInfo.propertyId?.propertyId) { // selectColumns
+ const currentCellRow = propertyId.index;
+ const errorCellRow = messageInfo[currentCellRow];
+ if (!errorCellRow) {
+ return false;
+ }
+ }
+ return true;
+}
+
+export {
+ doesErrorMessageApplyToCell
+};
diff --git a/canvas_modules/harness/test_resources/parameterDefs/nestedConditions_paramDef.json b/canvas_modules/harness/test_resources/parameterDefs/nestedConditions_paramDef.json
new file mode 100644
index 0000000000..53fa69f24b
--- /dev/null
+++ b/canvas_modules/harness/test_resources/parameterDefs/nestedConditions_paramDef.json
@@ -0,0 +1,422 @@
+{
+ "titleDefinition": {
+ "title": "Nested cells"
+ },
+ "current_parameters": {
+ "nested_table": [
+ [
+ ["list1", "list2"],
+ ["Age", "Na"],
+ ["orange", "green"],
+ [["Sex", "red"], ["BP", "blue"]],
+ [["Hello", 1], ["world", 2]]
+ ]
+ ]
+ },
+ "parameters": [
+ {
+ "id": "nested_table",
+ "type": "array[table_controls]"
+ }
+ ],
+ "complex_types": [
+ {
+ "id": "table_controls",
+ "parameters": [
+ {
+ "id": "list",
+ "type": "array[string]",
+ "required": true
+ },
+ {
+ "id": "select_columns",
+ "type": "array[string]",
+ "role": "column"
+ },
+ {
+ "id": "someofselect",
+ "type": "array[string]",
+ "enum": [
+ "red",
+ "orange",
+ "yellow",
+ "green",
+ "blue",
+ "purple"
+ ]
+ },
+ {
+ "id": "structuretable",
+ "type": "map[string, nestedStructureTable]",
+ "role": "column"
+ },
+ {
+ "id": "structurelisteditor",
+ "type": "array[nestedStructureListEditor]"
+ }
+ ]
+ },
+ {
+ "id": "nestedStructureTable",
+ "parameters": [
+ {
+ "id": "field",
+ "type": "string",
+ "role": "column"
+ },
+ {
+ "id": "oneofselect",
+ "enum": [
+ "red",
+ "orange",
+ "yellow",
+ "green",
+ "blue",
+ "purple"
+ ]
+ }
+ ]
+ },
+ {
+ "id": "nestedStructureListEditor",
+ "parameters": [
+ {
+ "id": "textfield",
+ "type": "string",
+ "required": true
+ },
+ {
+ "id": "numberfield",
+ "type": "integer",
+ "required": true
+ }
+ ]
+ }
+ ],
+ "uihints": {
+ "id": "nested cells test",
+ "icon": "images/default.svg",
+ "label": {
+ "default": "Nested cells test"
+ },
+ "editor_size": "large",
+ "parameter_info": [
+ {
+ "parameter_ref": "nested_table",
+ "label": {
+ "default": "Table within table"
+ },
+ "description": {
+ "default": "This example displays conditions within a nested table.",
+ "placement": "on_panel"
+ }
+ }
+ ],
+ "complex_type_info": [
+ {
+ "complex_type_ref": "table_controls",
+ "parameters": [
+ {
+ "parameter_ref": "list",
+ "width": 10,
+ "label": {
+ "default": "list"
+ },
+ "description": {
+ "default": "Enter 'error' in a cell to display an error",
+ "placement": "on_panel"
+ },
+ "edit_style": "on_panel",
+ "control": "list"
+ },
+ {
+ "parameter_ref": "select_columns",
+ "width": 10,
+ "label": {
+ "default": "selectColumns"
+ },
+ "description": {
+ "default": "Select the column 'K' to display an error",
+ "placement": "on_panel"
+ },
+ "edit_style": "subpanel"
+ },
+ {
+ "parameter_ref": "someofselect",
+ "width": 10,
+ "label": {
+ "default": "someofselect"
+ },
+ "description": {
+ "default": "Select 'orange' below to display an error. It is preselected initially and error will not be shown until the control is modified",
+ "placement": "on_panel"
+ },
+ "edit_style": "subpanel"
+ },
+ {
+ "parameter_ref": "structuretable",
+ "width": 10,
+ "label": {
+ "default": "structuretable"
+ },
+ "description": {
+ "default": "Select 'green' in the second column to display an error",
+ "placement": "on_panel"
+ },
+ "edit_style": "on_panel"
+ },
+ {
+ "parameter_ref": "structurelisteditor",
+ "width": 10,
+ "label": {
+ "default": "structurelisteditor"
+ },
+ "description": {
+ "default": "Enter 'error' in a cell in the first column, or enter a number less than 1 in the second column to display an error. Clearing a cell error will update the table error to show the remaining error present in the table",
+ "placement": "on_panel"
+ },
+ "edit_style": "subpanel"
+ }
+ ]
+ },
+ {
+ "complex_type_ref": "nestedStructureTable",
+ "moveable_rows": true,
+ "parameters": [
+ {
+ "parameter_ref": "field",
+ "label": {
+ "default": "field"
+ }
+ },
+ {
+ "parameter_ref": "oneofselect",
+ "label": {
+ "default": "oneofselect"
+ }
+ }
+ ]
+ },
+ {
+ "complex_type_ref": "nestedStructureListEditor",
+ "moveable_rows": true,
+ "parameters": [
+ {
+ "parameter_ref": "textfield",
+ "label": {
+ "default": "textfield"
+ },
+ "control": "textfield"
+ },
+ {
+ "parameter_ref": "numberfield",
+ "label": {
+ "default": "numberfield"
+ },
+ "control": "numberfield"
+ }
+ ]
+ }
+ ],
+ "group_info": [
+ {
+ "id": "numberfield-table-panels",
+ "label": {
+ "default": "Table"
+ },
+ "type": "controls",
+ "parameter_refs": [
+ "nested_table"
+ ]
+ }
+ ]
+ },
+ "conditions": [
+ {
+ "validation": {
+ "fail_message": {
+ "type": "error",
+ "focus_parameter_ref": "nested_table[0]",
+ "message": {
+ "default": "Cannot be 'error'"
+ }
+ },
+ "evaluate": {
+ "condition": {
+ "parameter_ref": "nested_table[0]",
+ "op": "notEquals",
+ "value": "error"
+ }
+ }
+ }
+ },
+ {
+ "validation": {
+ "fail_message": {
+ "type": "error",
+ "focus_parameter_ref": "nested_table[1]",
+ "message": {
+ "default": "Cannot contain 'K'"
+ }
+ },
+ "evaluate": {
+ "condition": {
+ "parameter_ref": "nested_table[1]",
+ "op": "notContains",
+ "value": "K"
+ }
+ }
+ }
+ },
+ {
+ "validation": {
+ "fail_message": {
+ "type": "error",
+ "focus_parameter_ref": "nested_table[2]",
+ "message": {
+ "default": "Cannot select 'orange'"
+ }
+ },
+ "evaluate": {
+ "condition": {
+ "parameter_ref": "nested_table[2]",
+ "op": "notContains",
+ "value": "orange"
+ }
+ }
+ }
+ },
+ {
+ "validation": {
+ "fail_message": {
+ "type": "error",
+ "focus_parameter_ref": "nested_table[3]",
+ "message": {
+ "default": "Cannot select 'green'"
+ }
+ },
+ "evaluate": {
+ "condition": {
+ "parameter_ref": "nested_table[3][1]",
+ "op": "notEquals",
+ "value": "green"
+ }
+ }
+ }
+ },
+ {
+ "validation": {
+ "fail_message": {
+ "type": "error",
+ "focus_parameter_ref": "nested_table[4][0]",
+ "message": {
+ "default": "Cannot be 'error'"
+ }
+ },
+ "evaluate": {
+ "condition": {
+ "parameter_ref": "nested_table[4][0]",
+ "op": "notEquals",
+ "value": "error"
+ }
+ }
+ }
+ },
+ {
+ "validation": {
+ "fail_message": {
+ "type": "error",
+ "focus_parameter_ref": "nested_table[4][1]",
+ "message": {
+ "default": "Must be greater than 0"
+ }
+ },
+ "evaluate": {
+ "condition": {
+ "parameter_ref": "nested_table[4][1]",
+ "op": "greaterThan",
+ "value": "0"
+ }
+ }
+ }
+ }
+ ],
+ "dataset_metadata": [
+ {
+ "fields": [
+ {
+ "name": "Age",
+ "type": "integer",
+ "metadata": {
+ "description": "",
+ "measure": "range",
+ "modeling_role": "input"
+ }
+ },
+ {
+ "name": "Sex",
+ "type": "string",
+ "metadata": {
+ "description": "",
+ "measure": "ordered_set",
+ "modeling_role": "input"
+ }
+ },
+ {
+ "name": "BP",
+ "type": "string",
+ "metadata": {
+ "description": "",
+ "measure": "discrete",
+ "modeling_role": "input"
+ }
+ },
+ {
+ "name": "Cholesterol",
+ "type": "string",
+ "metadata": {
+ "description": "",
+ "measure": "set",
+ "modeling_role": "input"
+ }
+ },
+ {
+ "name": "Na",
+ "type": "double",
+ "metadata": {
+ "description": "",
+ "measure": "flag",
+ "modeling_role": "input"
+ }
+ },
+ {
+ "name": "K",
+ "type": "double",
+ "metadata": {
+ "description": "",
+ "measure": "collection",
+ "modeling_role": "input"
+ }
+ },
+ {
+ "name": "Drug",
+ "type": "string",
+ "metadata": {
+ "description": "",
+ "measure": "geospatial",
+ "modeling_role": "input"
+ }
+ },
+ {
+ "name": "Ag",
+ "type": "integer",
+ "metadata": {
+ "description": "",
+ "measure": "",
+ "modeling_role": "input"
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/canvas_modules/harness/test_resources/parameterDefs/numberfield_paramDef.json b/canvas_modules/harness/test_resources/parameterDefs/numberfield_paramDef.json
index 13953acf1f..134e26a917 100644
--- a/canvas_modules/harness/test_resources/parameterDefs/numberfield_paramDef.json
+++ b/canvas_modules/harness/test_resources/parameterDefs/numberfield_paramDef.json
@@ -180,7 +180,7 @@
"description": {
"default": "Readonly numberfield with parameter value set to '10'"
},
- "class_name": "numberfield-control-class",
+ "class_name": "numberfield-readonly-control-class",
"helper_text": {
"default": "Readonly numberfield with parameter value set to '10'"
},