diff --git a/canvas_modules/common-canvas/__tests__/common-properties/controls/selectcolumn-test.js b/canvas_modules/common-canvas/__tests__/common-properties/controls/selectcolumn-test.js index 51ad27c633..c62fcf5a8c 100644 --- a/canvas_modules/common-canvas/__tests__/common-properties/controls/selectcolumn-test.js +++ b/canvas_modules/common-canvas/__tests__/common-properties/controls/selectcolumn-test.js @@ -22,6 +22,7 @@ import Controller from "../../../src/common-properties/properties-controller"; import propertyUtils from "../../_utils_/property-utils"; import selectcolumnParamDef from "../../test_resources/paramDefs/selectcolumn_paramDef.json"; +import selectcolumnEmptyListParamDef from "../../test_resources/paramDefs/selectcolumn_emptylist_paramDef.json"; import selectcolumnMultiInputParamDef from "../../test_resources/paramDefs/selectcolumn_multiInput_paramDef.json"; const emptyValueIndicator = "..."; @@ -148,7 +149,8 @@ describe("selectcolumn control renders correctly", () => { expect(dropdownList).to.be.length(4); expect(dropdownList.at(0).text()).to.equal(emptyValueIndicator); }); - it("should have '...' as first selected option when fields is empty", () => { + it("should have 'No options available' as first selected option when fields is empty", () => { + const defaultEmptyListPlaceholder = "No options available"; controller.setDatasetMetadata([]); controller.setPropertyValues( { "targetField": null } @@ -162,7 +164,7 @@ describe("selectcolumn control renders correctly", () => { /> ); let dropdownWrapper = wrapper.find("div[data-id='properties-targetField']"); - expect(dropdownWrapper.find("button > span").text()).to.equal(emptyValueIndicator); + expect(dropdownWrapper.find("button > span").text()).to.equal(defaultEmptyListPlaceholder); // open the dropdown const dropdownButton = dropdownWrapper.find("button"); dropdownButton.simulate("click"); @@ -170,7 +172,7 @@ describe("selectcolumn control renders correctly", () => { dropdownWrapper = wrapper.find("div[data-id='properties-targetField']"); const dropdownList = dropdownWrapper.find("div.bx--list-box__menu-item"); expect(dropdownList).to.be.length(1); - expect(dropdownList.at(0).text()).to.equal(emptyValueIndicator); + expect(dropdownList.at(0).text()).to.equal(defaultEmptyListPlaceholder); }); it("should allow empty string to be set as valid field in selectcolumn control", () => { @@ -602,3 +604,29 @@ describe("selectcolumn classnames appear correctly", () => { expect(wrapper.find(".table-subpanel-selectcolumn-control-class")).to.have.length(1); }); }); + +describe("Empty list selectcolumn control with default and custom placeholder text", () => { + let wrapper; + beforeEach(() => { + const renderedObject = propertyUtils.flyoutEditorForm(selectcolumnEmptyListParamDef); + wrapper = renderedObject.wrapper; + }); + afterEach(() => { + wrapper.unmount(); + }); + it("should have default placeholder text when fields is empty", () => { + // No resource_key added for field2_panel property + const dropdownWrapper = wrapper.find("div[data-id='properties-field2_panel']"); + expect(dropdownWrapper.find("button > span").text()).to.equal("No options available"); + // Verify dropdown is enabled + expect(dropdownWrapper.find("Dropdown").props()).to.have.property("disabled", false); + }); + + it("should have custom placeholder text when fields is empty and selectcolumn control should be disabled", () => { + // "field1_panel.emptyList.placeholder" resource key is added in paramDef + const dropdownWrapper = wrapper.find("div[data-id='properties-field1_panel']"); + expect(dropdownWrapper.find("button > span").text()).to.equal("Empty list placeholder text"); + // Verify dropdown is disabled + expect(dropdownWrapper.find("Dropdown").props()).to.have.property("disabled", true); + }); +}); diff --git a/canvas_modules/common-canvas/__tests__/common-properties/controls/selectschema-test.js b/canvas_modules/common-canvas/__tests__/common-properties/controls/selectschema-test.js index 75e8fade12..736e672237 100644 --- a/canvas_modules/common-canvas/__tests__/common-properties/controls/selectschema-test.js +++ b/canvas_modules/common-canvas/__tests__/common-properties/controls/selectschema-test.js @@ -25,7 +25,7 @@ import selectschemaParamDef from "../../test_resources/paramDefs/selectschema_pa const controller = new Controller(); -const emptyValueIndicator = "..."; +const emptyValueIndicator = "No options available"; const control = { "name": "selectschema", @@ -64,7 +64,7 @@ describe("selectschema renders correctly", () => { expect(wrapper.prop("controller")).to.equal(controller); expect(wrapper.prop("propertyId")).to.equal(propertyId); }); - it("should have '...' as first selected option", () => { + it("should have 'No options available' as first selected option for empty list", () => { controller.setPropertyValues( { "test-selectschema": null } ); diff --git a/canvas_modules/common-canvas/__tests__/common-properties/controls/structureeditor-test.js b/canvas_modules/common-canvas/__tests__/common-properties/controls/structureeditor-test.js index 386b44bbbd..1b35e2acde 100644 --- a/canvas_modules/common-canvas/__tests__/common-properties/controls/structureeditor-test.js +++ b/canvas_modules/common-canvas/__tests__/common-properties/controls/structureeditor-test.js @@ -176,7 +176,8 @@ describe("structureeditor control renders correctly", () => { expect(dropdownList.at(0).text()).to.equal(emptyValueIndicator); }); - it("should have '...' as first selected option when fields is empty", () => { + it("should have 'No options available' as first selected option when fields is empty", () => { + const defaultEmptyListPlaceholder = "No options available"; controller.setDatasetMetadata([]); controller.setPropertyValues( { "group-o-fields": null } @@ -191,7 +192,7 @@ describe("structureeditor control renders correctly", () => { ); let dropdownWrapper = wrapper.find("div[data-id='properties-group-o-fields_0']"); - expect(dropdownWrapper.find("button > span").text()).to.equal(emptyValueIndicator); + expect(dropdownWrapper.find("button > span").text()).to.equal(defaultEmptyListPlaceholder); // open the dropdown const dropdownButton = dropdownWrapper.find("button"); dropdownButton.simulate("click"); @@ -199,7 +200,7 @@ describe("structureeditor control renders correctly", () => { dropdownWrapper = wrapper.find("div[data-id='properties-group-o-fields_0']"); const dropdownList = dropdownWrapper.find("div.bx--list-box__menu-item"); expect(dropdownList).to.be.length(1); - expect(dropdownList.at(0).text()).to.equal(emptyValueIndicator); + expect(dropdownList.at(0).text()).to.equal(defaultEmptyListPlaceholder); }); it("should allow empty string to be set as valid field in structureeditor control", () => { diff --git a/canvas_modules/common-canvas/__tests__/test_resources/paramDefs/selectcolumn_emptylist_paramDef.json b/canvas_modules/common-canvas/__tests__/test_resources/paramDefs/selectcolumn_emptylist_paramDef.json new file mode 100644 index 0000000000..48948090a6 --- /dev/null +++ b/canvas_modules/common-canvas/__tests__/test_resources/paramDefs/selectcolumn_emptylist_paramDef.json @@ -0,0 +1,75 @@ +{ + "titleDefinition": { + "title": "Select Column Empty list" + }, + "current_parameters": {}, + "parameters": [ + { + "id": "field1_panel", + "type": "string", + "role": "column", + "required": true + }, + { + "id": "field2_panel", + "type": "string", + "role": "column", + "required": true + } + ], + "complex_types": [], + "uihints": { + "id": "selectcolumn", + "icon": "images/default.svg", + "label": { + "default": "Select Column" + }, + "parameter_info": [ + { + "parameter_ref": "field1_panel", + "label": { + "default": "Field1 Panel" + }, + "description": { + "default": "Disabled selectcolumn with empty list and custom placeholder text." + } + }, + { + "parameter_ref": "field2_panel", + "label": { + "default": "Field2 Panel" + }, + "description": { + "default": "selectcolumn with empty list and default placeholder text." + } + } + ], + "complex_type_info": [], + "group_info": [ + { + "id": "selectcolumn-values", + "label": { + "default": "Values" + }, + "type": "panels", + "group_info": [ + { + "id": "selectcolumn-values1", + "label": { + "default": "Values" + }, + "type": "columnSelection", + "parameter_refs": [ + "field1_panel", + "field2_panel" + ] + } + ] + } + ] + }, + "conditions": [], + "resources": { + "field1_panel.emptyList.placeholder": "Empty list placeholder text" + } +} diff --git a/canvas_modules/common-canvas/locales/common-properties/locales/en.json b/canvas_modules/common-canvas/locales/common-properties/locales/en.json index de04274a29..69f4fc6e3d 100644 --- a/canvas_modules/common-canvas/locales/common-properties/locales/en.json +++ b/canvas_modules/common-canvas/locales/common-properties/locales/en.json @@ -102,5 +102,6 @@ "virtualizedTable.row.checkbox.label": "Select row {row_index} from {table_label}", "properties.empty.table.text": "To begin, click \"{button_label}\"", "label.indicator.required": "(required)", - "label.indicator.optional": "(optional)" + "label.indicator.optional": "(optional)", + "emptyList.placeholder": "No options available" } diff --git a/canvas_modules/common-canvas/locales/common-properties/locales/eo.json b/canvas_modules/common-canvas/locales/common-properties/locales/eo.json index 4aca483bd8..f5df0bc961 100644 --- a/canvas_modules/common-canvas/locales/common-properties/locales/eo.json +++ b/canvas_modules/common-canvas/locales/common-properties/locales/eo.json @@ -102,5 +102,6 @@ "virtualizedTable.row.checkbox.label": "[Esperanto~Select row {row_index} from {table_label}~~~~~~~~~~eo]", "properties.empty.table.text": "[Esperanto~To begin, click \"{button_label}\"~~~~~eo]", "label.indicator.required": "[Esperanto~(required)~~~~~~eo]", - "label.indicator.optional": "[Esperanto~(optional)~~~~~~eo]" + "label.indicator.optional": "[Esperanto~(optional)~~~~~~eo]", + "emptyList.placeholder": "[Esperanto~No options available~~~~~~eo]" } 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 9b8c9dbccf..a9c58aaddb 100644 --- a/canvas_modules/common-canvas/src/common-properties/constants/constants.js +++ b/canvas_modules/common-canvas/src/common-properties/constants/constants.js @@ -114,7 +114,8 @@ export const MESSAGE_KEYS = { SHOW_PASSWORD_TOOLTIP: "passwordShow.tooltip", HIDE_PASSWORD_TOOLTIP: "passwordHide.tooltip", LABEL_INDICATOR_REQUIRED: "label.indicator.required", - LABEL_INDICATOR_OPTIONAL: "label.indicator.optional" + LABEL_INDICATOR_OPTIONAL: "label.indicator.optional", + EMPTY_LIST_PLACEHOLDER: "emptyList.placeholder" }; export const TRUNCATE_LIMIT = 10000; 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 b1069918e1..3c890e452a 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 @@ -18,7 +18,7 @@ import React from "react"; import PropTypes from "prop-types"; import { connect } from "react-redux"; import { SelectItem, Select, Dropdown, ComboBox } from "carbon-components-react"; -import { isEqual } from "lodash"; +import { isEqual, isEmpty } from "lodash"; import * as ControlUtils from "./../../util/control-utils"; import ValidationMessage from "./../../components/validation-message"; import classNames from "classnames"; @@ -30,11 +30,22 @@ import { formatMessage } from "./../../util/property-utils"; class DropDown extends React.Component { constructor(props) { super(props); + this.reactIntl = props.controller.getReactIntl(); this.emptyLabel = "..."; - if (props.control.additionalText) { + this.disableEmptyListDropdown = false; + if (isEmpty(props.controlOpts)) { + // For empty dropdown, get placeholder text from resources + const overrideEmptyListPlaceholder = `${this.props.control.name}.emptyList.placeholder`; + const defaultEmptyListPlaceholder = formatMessage(this.reactIntl, MESSAGE_KEYS.EMPTY_LIST_PLACEHOLDER); + this.emptyLabel = props.controller.getResource(overrideEmptyListPlaceholder, defaultEmptyListPlaceholder); + // Disable empty dropdown when [property_id].emptyList.placeholder is set in resources + if (this.emptyLabel !== defaultEmptyListPlaceholder) { + this.disableEmptyListDropdown = true; + } + } else if (props.control.additionalText) { + // For non-empty dropdown, get placeholder text from place_holder_text in parameter_info this.emptyLabel = props.control.additionalText; } - this.reactIntl = props.controller.getReactIntl(); this.id = ControlUtils.getControlId(this.props.propertyId); this.handleChange = this.handleChange.bind(this); this.handleComboOnChange = this.handleComboOnChange.bind(this); @@ -205,7 +216,7 @@ class DropDown extends React.Component { hideLabel inline labelText={this.props.control.label ? this.props.control.label.text : ""} - disabled={this.props.state === STATES.DISABLED} + disabled={this.props.state === STATES.DISABLED || this.disableEmptyListDropdown} onChange={this.handleChange} value={selection} light={this.props.controller.getLight() && this.props.control.light} @@ -217,7 +228,7 @@ class DropDown extends React.Component { {...validationProps} ariaLabel={this.props.control.label ? this.props.control.label.text : ""} id={`${ControlUtils.getDataId(this.props.propertyId)}-dropdown`} - disabled={this.props.state === STATES.DISABLED} + disabled={this.props.state === STATES.DISABLED || this.disableEmptyListDropdown} placeholder={dropDown.selectedOption.label} selectedItem={dropDown.selectedOption.label} items={dropDown.options} @@ -231,7 +242,7 @@ class DropDown extends React.Component { dropdownComponent = ( +

When dataset_metadata is not provided, + selectschema control will display default placeholder text "No options available". + This placeholder text can be customized by setting [parameter_id].emptyList.placeholder in resources section. + When custom empty list placeholder text is provided, common-properties will disable the empty list control.

+
+
+ + {this.renderRightFlyoutButton(SELECTSCHEMA_EMPTY_PROPS_INFO)} +
+
+
+								{this.jsonReplacer(SELECTSCHEMA_EMPTY_PROPS_INFO.parameterDef, "all")}
+							
+
+

selectcolumn

@@ -1441,6 +1462,25 @@ class CommonPropertiesComponents extends React.Component {
+

When dataset_metadata is not provided, + selectcolumn control will display default placeholder text "No options available". + This placeholder text can be customized by setting [parameter_id].emptyList.placeholder in resources section. + When custom empty list placeholder text is provided, common-properties will disable the empty list control.

+
+
+ + {this.renderRightFlyoutButton(SELECTCOLUMN_EMPTY_PROPS_INFO)} +
+
+
+								{this.jsonReplacer(SELECTCOLUMN_EMPTY_PROPS_INFO.parameterDef, "all")}
+							
+
+

If multiple input schemas are supported by a given node type, then the type of the parameter for the selectColumn control must be of type object, and the role must be set to column. The selectcolumn control will display all the fields from both schemas, diff --git a/canvas_modules/harness/src/client/constants/properties-documentation-constants.js b/canvas_modules/harness/src/client/constants/properties-documentation-constants.js index 4b10bbe019..45a8f9122a 100644 --- a/canvas_modules/harness/src/client/constants/properties-documentation-constants.js +++ b/canvas_modules/harness/src/client/constants/properties-documentation-constants.js @@ -2820,6 +2820,63 @@ _defineConstant("SELECTSCHEMA_PROPS_INFO", { ] } }); +_defineConstant("SELECTSCHEMA_EMPTY_PROPS_INFO", { + "title": "SelectSchema Title", + "parameterDef": { + "titleDefinition": { + "title": "Control: empty list selectschema", + "editable": false + }, + "current_parameters": { + }, + "parameters": [ + { + "id": "selectschema" + }, + { + "id": "selectschemaEmptyPlaceholder" + } + ], + "uihints": { + "id": "selectschemaList", + "parameter_info": [ + { + "parameter_ref": "selectschema", + "label": { + "default": "SelectSchema Empty list Control" + }, + "description": { + "default": "selectschema control without dataset_metadata." + }, + "control": "selectschema" + }, + { + "parameter_ref": "selectschemaEmptyPlaceholder", + "label": { + "default": "Disabled SelectSchema Empty list Control with custom placeholder" + }, + "description": { + "default": "selectschema control without dataset_metadata. Showing custom placeholder text. This control should be disabled." + }, + "control": "selectschema" + } + ], + "group_info": [ + { + "id": "SelectSchema Control", + "type": "controls", + "parameter_refs": [ + "selectschema", + "selectschemaEmptyPlaceholder" + ] + } + ] + }, + "resources": { + "selectschemaEmptyPlaceholder.emptyList.placeholder": "Custom empty list placeholder text" + } + } +}); _defineConstant("SELECTCOLUMN_PROPS_INFO", { "title": "Select Column Title", "parameterDef": { @@ -2868,6 +2925,68 @@ _defineConstant("SELECTCOLUMN_PROPS_INFO", { ] } }); +_defineConstant("SELECTCOLUMN_EMPTY_PROPS_INFO", { + "title": "Select Column Title", + "parameterDef": { + "titleDefinition": { + "title": "Control: empty selectcolumn", + "editable": false + }, + "current_parameters": { + "selectcolumn": "" + }, + "parameters": [ + { + "id": "selectcolumn", + "type": "string", + "default": "", + "role": "column" + }, + { + "id": "selectcolumnEmptyPlaceholder", + "type": "string", + "default": "", + "role": "column" + } + ], + "uihints": { + "id": "selectcolumn", + "parameter_info": [ + { + "parameter_ref": "selectcolumn", + "label": { + "default": "Select Column Empty list Control" + }, + "description": { + "default": "selectcolumn control without dataset_metadata." + } + }, + { + "parameter_ref": "selectcolumnEmptyPlaceholder", + "label": { + "default": "Disabled Empty list Control with custom placeholder" + }, + "description": { + "default": "selectcolumn control without dataset_metadata. Showing custom placeholder text. This control should be disabled." + } + } + ], + "group_info": [ + { + "id": "SelectColumn Control", + "type": "controls", + "parameter_refs": [ + "selectcolumn", + "selectcolumnEmptyPlaceholder" + ] + } + ] + }, + "resources": { + "selectcolumnEmptyPlaceholder.emptyList.placeholder": "Custom empty list placeholder text" + } + } +}); _defineConstant("SELECTCOLUMN_MULTI_INPUT_PROPS_INFO", { "title": "Select Column Title", "parameterDef": { diff --git a/canvas_modules/harness/test_resources/parameterDefs/selectcolumn_emptylist_paramDef.json b/canvas_modules/harness/test_resources/parameterDefs/selectcolumn_emptylist_paramDef.json new file mode 100644 index 0000000000..48948090a6 --- /dev/null +++ b/canvas_modules/harness/test_resources/parameterDefs/selectcolumn_emptylist_paramDef.json @@ -0,0 +1,75 @@ +{ + "titleDefinition": { + "title": "Select Column Empty list" + }, + "current_parameters": {}, + "parameters": [ + { + "id": "field1_panel", + "type": "string", + "role": "column", + "required": true + }, + { + "id": "field2_panel", + "type": "string", + "role": "column", + "required": true + } + ], + "complex_types": [], + "uihints": { + "id": "selectcolumn", + "icon": "images/default.svg", + "label": { + "default": "Select Column" + }, + "parameter_info": [ + { + "parameter_ref": "field1_panel", + "label": { + "default": "Field1 Panel" + }, + "description": { + "default": "Disabled selectcolumn with empty list and custom placeholder text." + } + }, + { + "parameter_ref": "field2_panel", + "label": { + "default": "Field2 Panel" + }, + "description": { + "default": "selectcolumn with empty list and default placeholder text." + } + } + ], + "complex_type_info": [], + "group_info": [ + { + "id": "selectcolumn-values", + "label": { + "default": "Values" + }, + "type": "panels", + "group_info": [ + { + "id": "selectcolumn-values1", + "label": { + "default": "Values" + }, + "type": "columnSelection", + "parameter_refs": [ + "field1_panel", + "field2_panel" + ] + } + ] + } + ] + }, + "conditions": [], + "resources": { + "field1_panel.emptyList.placeholder": "Empty list placeholder text" + } +}