diff --git a/app/client/cypress/e2e/Regression/ClientSide/Binding/ChartText_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Binding/ChartText_spec.js index 38f9eec51bc5..f44989ff37fb 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Binding/ChartText_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Binding/ChartText_spec.js @@ -28,8 +28,7 @@ describe( EditorNavigation.SelectEntityByName("Chart1", EntityType.Widget, {}, [ "Container1", ]); - cy.get(viewWidgetsPage.chartType).last().click({ force: true }); - cy.get(".t--dropdown-option").children().contains("Column chart").click(); + cy.selectDropdownValue(viewWidgetsPage.chartType, "Column chart"); cy.get( ".t--property-control-charttype span.rc-select-selection-item span", ) diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Button/ButtonGroup_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Button/ButtonGroup_spec.js index 1e64e83a5c58..20e2b13be2d2 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Button/ButtonGroup_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Button/ButtonGroup_spec.js @@ -82,19 +82,13 @@ describe( it("Update Placement and Verify buttons alignments", function () { // check first button placement - cy.selectDropdownValue( - ".t--property-control-placement .rc-select-selection-item", - "Between", - ); + cy.selectDropdownValue(".t--property-control-placement", "Between"); // 1st btn cy.get(firstButton) .last() .should("have.css", "justify-content", "space-between"); // update dropdown value - cy.selectDropdownValue( - ".t--property-control-placement .rc-select-selection-item", - "Start", - ); + cy.selectDropdownValue(".t--property-control-placement", "Start"); cy.get(firstButton).last().should("have.css", "justify-content", "start"); // other button style stay same cy.get(menuButton).should("have.css", "justify-content", "center"); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/CurrencyInput/CurrencyInput_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/CurrencyInput/CurrencyInput_spec.js index b4fdfe4d25cf..61209355e178 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/CurrencyInput/CurrencyInput_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/CurrencyInput/CurrencyInput_spec.js @@ -27,12 +27,13 @@ describe( it("2. should check for type of value and widget", () => { cy.openPropertyPane(widgetName); - cy.get(".t--property-control-currency").click(); - cy.get(".t--property-control-currency").type("usd"); + cy.openSelectDropdown(".t--property-control-currency"); + cy.searchSelectDropdown("usd"); cy.selectDropdownValue( - ".t--property-control-currency input", + ".t--property-control-currency", "USD - US Dollar", ); + function enterAndTest(text, expected) { cy.get(widgetInput).clear(); cy.wait(300); @@ -53,7 +54,7 @@ describe( }); cy.openPropertyPane(widgetName); - cy.selectDropdownValue(".t--property-control-decimalsallowed input", "1"); + cy.selectDropdownValue(".t--property-control-decimalsallowed", "1"); [ //[input, {{CurrencyInput1.text}}:{{CurrencyInput1.value}}:{{CurrencyInput1.isValid}}:{{typeof CurrencyInput1.text}}:{{typeof CurrencyInput1.value}}:{{CurrencyInput1.countryCode}}:{{CurrencyInput1.currencyCode}}] @@ -66,7 +67,7 @@ describe( }); cy.openPropertyPane(widgetName); - cy.selectDropdownValue(".t--property-control-decimalsallowed input", "2"); + cy.selectDropdownValue(".t--property-control-decimalsallowed", "2"); [ //[input, {{CurrencyInput1.text}}:{{CurrencyInput1.value}}:{{CurrencyInput1.isValid}}:{{typeof CurrencyInput1.text}}:{{typeof CurrencyInput1.value}}:{{CurrencyInput1.countryCode}}:{{CurrencyInput1.currencyCode}}] @@ -80,10 +81,10 @@ describe( cy.get(".currency-change-dropdown-trigger").should("contain", "$"); cy.openPropertyPane(widgetName); - cy.get(".t--property-control-currency").click(); - cy.get(".t--property-control-currency").type("ind"); + cy.openSelectDropdown(".t--property-control-currency"); + cy.searchSelectDropdown("ind"); cy.selectDropdownValue( - ".t--property-control-currency input", + ".t--property-control-currency", "INR - Indian Rupee", ); enterAndTest("100.22", "100.22:100.22:true:string:number:IN:INR"); @@ -100,12 +101,13 @@ describe( .last() .click(); enterAndTest("100.22", "100.22:100.22:true:string:number:GB:GBP"); + enterAndTest("100.22", "100.22:100.22:true:string:number:GB:GBP"); cy.get(".t--input-currency-change").should("contain", "£"); }); it("3. should accept 0 decimal option", () => { cy.openPropertyPane(widgetName); - cy.selectDropdownValue(".t--property-control-decimalsallowed input", "0"); + cy.selectDropdownValue(".t--property-control-decimalsallowed", "0"); cy.closePropertyPane(); cy.wait(500); cy.openPropertyPane(widgetName); @@ -142,7 +144,7 @@ describe( `{{CurrencyInput1.text}}:{{CurrencyInput1.value}}`, ); cy.openPropertyPane(widgetName); - cy.selectDropdownValue(".t--property-control-decimalsallowed input", "0"); + cy.selectDropdownValue(".t--property-control-decimalsallowed", "0"); [ //[input, {{CurrencyInput1.text}}:{{CurrencyInput1.value}}] @@ -157,7 +159,7 @@ describe( }); cy.openPropertyPane(widgetName); - cy.selectDropdownValue(".t--property-control-decimalsallowed input", "1"); + cy.selectDropdownValue(".t--property-control-decimalsallowed", "1"); [ //[input, {{CurrencyInput1.text}}:{{CurrencyInput1.value}}] ["100", "100:100"], @@ -171,7 +173,7 @@ describe( }); cy.openPropertyPane(widgetName); - cy.selectDropdownValue(".t--property-control-decimalsallowed input", "2"); + cy.selectDropdownValue(".t--property-control-decimalsallowed", "2"); [ //[input, {{CurrencyInput1.text}}:{{CurrencyInput1.value}}] ["100", "100:100"], @@ -205,7 +207,7 @@ describe( } cy.openPropertyPane(widgetName); - cy.selectDropdownValue(".t--property-control-decimalsallowed input", "0"); + cy.selectDropdownValue(".t--property-control-decimalsallowed", "0"); [ //[input, expected] @@ -221,7 +223,7 @@ describe( }); cy.openPropertyPane(widgetName); - cy.selectDropdownValue(".t--property-control-decimalsallowed input", "1"); + cy.selectDropdownValue(".t--property-control-decimalsallowed", "1"); [ //[input, expected] ["100", "100"], @@ -238,7 +240,7 @@ describe( }); cy.openPropertyPane(widgetName); - cy.selectDropdownValue(".t--property-control-decimalsallowed input", "2"); + cy.selectDropdownValue(".t--property-control-decimalsallowed", "2"); [ //[input, expected] ["100", "100"], diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Filepicker/FilePickerV2_CSV_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Filepicker/FilePickerV2_CSV_spec.js index 975f23e23fb7..115872c0877b 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Filepicker/FilePickerV2_CSV_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Filepicker/FilePickerV2_CSV_spec.js @@ -29,7 +29,7 @@ describe( ); agHelper.AssertText( - commonlocators.filePickerDataFormat, + `${commonlocators.filePickerDataFormat} .rc-select-selection-item .ads-v2-text`, "text", "Array of Objects (CSV, XLS(X), JSON, TSV)", ); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Input/Inputv2_inside_List_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Input/Inputv2_inside_List_spec.js index 291ece7961e1..a69e464b3256 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Input/Inputv2_inside_List_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Input/Inputv2_inside_List_spec.js @@ -52,13 +52,13 @@ describe( it("3. Validate DataType - NUMBER can be entered into Input widget", () => { cy.openPropertyPane(widgetName); - cy.selectDropdownValue(".t--property-control-datatype input", "Number"); + cy.selectDropdownValue(".t--property-control-datatype", "Number"); cy.get(".t--property-control-required label") .last() .click({ force: true }); - cy.selectDropdownValue(".t--property-control-datatype input", "Number"); + cy.selectDropdownValue(".t--property-control-datatype", "Number"); [ { input: "invalid", @@ -101,7 +101,7 @@ describe( it("4. Validate DataType - PASSWORD can be entered into Input widget", () => { cy.openPropertyPane(widgetName); - cy.selectDropdownValue(".t--property-control-datatype input", "Password"); + cy.selectDropdownValue(".t--property-control-datatype", "Password"); [ { input: "test", @@ -136,7 +136,7 @@ describe( it("5. Validate DataType - EMAIL can be entered into Input widget", () => { cy.openPropertyPane(widgetName); - cy.selectDropdownValue(".t--property-control-datatype input", "Email"); + cy.selectDropdownValue(".t--property-control-datatype", "Email"); cy.get(".t--property-control-required label") .last() diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Input/Inputv2_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Input/Inputv2_spec.js index 3c9f53e826d6..d3bf2895157e 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Input/Inputv2_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Input/Inputv2_spec.js @@ -27,7 +27,7 @@ describe( cy.wait(300); cy.get(widgetInput).should("contain.value", ""); - cy.selectDropdownValue(".t--property-control-datatype input", "Number"); + cy.selectDropdownValue(".t--property-control-datatype", "Number"); cy.get(widgetInput).clear(); cy.get(widgetInput).type("1.0010{enter}"); //Clicking enter submits the form here @@ -37,7 +37,7 @@ describe( it("3. Validate DataType - TEXT can be entered into Input widget", () => { cy.selectDropdownValue( - ".t--property-control-datatype input", + ".t--property-control-datatype", "Single-line text", ); [ @@ -112,7 +112,7 @@ describe( it("4. Validate DataType - NUMBER can be entered into Input widget", () => { cy.openPropertyPane(widgetName); - cy.selectDropdownValue(".t--property-control-datatype input", "Number"); + cy.selectDropdownValue(".t--property-control-datatype", "Number"); [ { input: "invalid", @@ -157,7 +157,7 @@ describe( .last() .click({ force: true }); - cy.selectDropdownValue(".t--property-control-datatype input", "Number"); + cy.selectDropdownValue(".t--property-control-datatype", "Number"); [ { input: "invalid", @@ -200,7 +200,7 @@ describe( it("5. Validate DataType - PASSWORD can be entered into Input widget", () => { cy.openPropertyPane(widgetName); - cy.selectDropdownValue(".t--property-control-datatype input", "Password"); + cy.selectDropdownValue(".t--property-control-datatype", "Password"); [ { input: "test", @@ -273,7 +273,7 @@ describe( it("6. Validate DataType - EMAIL can be entered into Input widget", () => { cy.openPropertyPane(widgetName); - cy.selectDropdownValue(".t--property-control-datatype input", "Email"); + cy.selectDropdownValue(".t--property-control-datatype", "Email"); [ { input: "test", @@ -361,7 +361,7 @@ describe( it("8. onSubmit should be triggered with the whole input value", () => { cy.openPropertyPane(widgetName); cy.selectDropdownValue( - ".t--property-control-datatype input", + ".t--property-control-datatype", "Single-line text", ); cy.get(".t--property-control-required label") @@ -415,7 +415,7 @@ describe( "anotherText:anotherText:true", ); - cy.selectDropdownValue(".t--property-control-datatype input", "Number"); + cy.selectDropdownValue(".t--property-control-datatype", "Number"); cy.updateCodeInput(".t--property-control-defaultvalue", `{{1}}`); // wait for evaluations @@ -439,7 +439,7 @@ describe( // Init isDirty cy.openPropertyPane(widgetName); cy.selectDropdownValue( - ".t--property-control-datatype input", + ".t--property-control-datatype", "Single-line text", ); cy.updateCodeInput(".t--property-control-defaultvalue", "a"); @@ -490,7 +490,7 @@ describe( cy.get(widgetInput).should("have.attr", "autocomplete", "off"); //select a non email or password option - cy.selectDropdownValue(".t--property-control-datatype input", "text"); + cy.selectDropdownValue(".t--property-control-datatype", "text"); //autofill toggle should not be present as this restores autofill to be enabled cy.get(".t--property-control-allowautofill input").should("not.exist"); //autocomplete attribute should not be present in the text widget diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/PhoneInput/Phone_input_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/PhoneInput/Phone_input_spec.js index a539adac574c..cba5e7fdf376 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/PhoneInput/Phone_input_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/PhoneInput/Phone_input_spec.js @@ -2,17 +2,7 @@ import * as _ from "../../../../../support/Objects/ObjectsCore"; const widgetName = "phoneinputwidget"; const widgetInput = `.t--widget-${widgetName} input`; -const searchAndSelectOption = (optionValue) => { - cy.get(".t--property-control-defaultcountrycode input") - .last() - .scrollIntoView() - .click({ force: true }) - .type(optionValue.substring(0, 3)); - cy.get(".t--dropdown-option") - .children() - .contains(optionValue) - .click({ force: true }); -}; + describe( "Phone input widget - ", { tags: ["@tag.Widget", "@tag.PhoneInput", "@tag.Binding"] }, @@ -21,7 +11,7 @@ describe( _.agHelper.AddDsl("emptyDSL"); }); - it("1. Add new dropdown widget", () => { + it("1. Add new phone input widget", () => { cy.dragAndDropToCanvas(widgetName, { x: 300, y: 300 }); cy.get(`.t--widget-${widgetName}`).should("exist"); cy.dragAndDropToCanvas("textwidget", { x: 300, y: 500 }); @@ -39,7 +29,12 @@ describe( cy.get(".t--widget-textwidget").should("contain", "(999) 999-9999:US:+1"); cy.openPropertyPane(widgetName); - searchAndSelectOption("Afghanistan (+93)"); + cy.openSelectDropdown(".t--property-control-defaultcountrycode"); + cy.searchSelectDropdown("Afg"); + cy.selectDropdownValue( + ".t--property-control-defaultcountrycode", + "Afghanistan (+93)", + ); cy.get(`.t--widget-${widgetName} input`).clear(); cy.wait(500); cy.get(`.t--widget-${widgetName} input`).type("1234567890"); @@ -62,14 +57,24 @@ describe( cy.get(".t--property-control-enableformatting label") .last() .click({ force: true }); - searchAndSelectOption("United States / Canada (+1)"); + cy.openSelectDropdown(".t--property-control-defaultcountrycode"); + cy.searchSelectDropdown("United States / Canada"); + cy.selectDropdownValue( + ".t--property-control-defaultcountrycode", + "United States / Canada (+1)", + ); cy.get(`.t--widget-${widgetName} input`).clear(); cy.wait(500); cy.get(`.t--widget-${widgetName} input`).type("9999999999"); cy.get(".t--widget-textwidget").should("contain", "9999999999:US:+1"); cy.openPropertyPane(widgetName); - searchAndSelectOption("India (+91)"); + cy.openSelectDropdown(".t--property-control-defaultcountrycode"); + cy.searchSelectDropdown("India"); + cy.selectDropdownValue( + ".t--property-control-defaultcountrycode", + "India (+91)", + ); cy.get(`.t--widget-${widgetName} input`).clear(); cy.wait(500); cy.get(`.t--widget-${widgetName} input`).type("1234567890"); @@ -131,13 +136,11 @@ describe( // Select the Currency dropdown option from property pane // and enter a value that has space and returns 0 results - cy.get(".t--property-control-defaultcountrycode input") - .first() - .click() - .type("AFDB (+93)"); + cy.openSelectDropdown(".t--property-control-defaultcountrycode"); + cy.searchSelectDropdown("AFDB"); // assert that the dropdown is still option - cy.get(".t--property-control-defaultcountrycode input").should( + cy.get(".ads-v2-select__dropdown .rc-select-item-empty").should( "be.visible", ); }); diff --git a/app/client/cypress/locators/ViewWidgets.json b/app/client/cypress/locators/ViewWidgets.json index f12eadfc456e..e868e0a384ee 100644 --- a/app/client/cypress/locators/ViewWidgets.json +++ b/app/client/cypress/locators/ViewWidgets.json @@ -1,7 +1,7 @@ { "imageWidget": ".t--draggable-imagewidget", "chartWidget": ".t--draggable-chartwidget", - "chartType": ".t--property-control-charttype input", + "chartType": ".t--property-control-charttype", "chartTypeText": ".t--property-control-charttype", "mapType": ".t--property-control-maptype .rc-select-selection-item", "destin": ".appsmith_widget_01ewdomru7", @@ -36,4 +36,4 @@ "pickMyLocation": ".t--draggable-mapwidget div[title='Pick My Location']", "mapChartEntityLabels": ".t--draggable-mapchartwidget text", "listWidget": ".t--draggable-listwidget" -} +} \ No newline at end of file diff --git a/app/client/cypress/locators/Widgets.json b/app/client/cypress/locators/Widgets.json index 4dcb4ce330bf..3ebeed9d3f15 100644 --- a/app/client/cypress/locators/Widgets.json +++ b/app/client/cypress/locators/Widgets.json @@ -5,7 +5,7 @@ "multiSelectWidget": ".t--draggable-multiselectwidgetv2", "togglebutton": "input[type='checkbox']", "showStepArrowsToggleCheckBox": ".t--property-control-showsteparrows input[type='checkbox']", - "inputPropsDataType": ".t--property-control-datatype input", + "inputPropsDataType": ".t--property-control-datatype", "inputdatatypeplaceholder": ".t--property-control-placeholder", "buttonWidget": ".t--draggable-buttonwidget", "buttonColor": ".t--property-control-buttoncolor [data-testid='t--color-picker-input']", @@ -31,7 +31,7 @@ "labelColor": ".t--property-control-labelcolor input", "inputval": ".t--draggable-inputwidgetv2 span.t--widget-name", "dataclass": ".bp3-input", - "datatype": ".t--property-control-datatype input", + "datatype": ".t--property-control-datatype", "rowHeight": ".t--property-control-defaultrowheight .rc-select-selection-search-input ", "innertext": ".t--draggable-inputwidgetv2 input", "defaultinput": ".t--property-control-defaultinput", @@ -232,9 +232,9 @@ "counterclockwise": ".t--property-control-counterclockwise input[type='checkbox']", "serversideFilteringInput": ".t--property-control-serversidefiltering input[type='checkbox']", "propertyPaneSaveButton": ".t--property-pane-section-collapse-savebutton", - "firstEditInput":"[data-colindex=0][data-rowindex=0] .t--inlined-cell-editor input.bp3-input", - "cellControlSwitch" : ".t--property-control-cellwrapping .ads-v2-switch", - "propertyControlLabel" : ".t--property-control-label", + "firstEditInput": "[data-colindex=0][data-rowindex=0] .t--inlined-cell-editor input.bp3-input", + "cellControlSwitch": ".t--property-control-cellwrapping .ads-v2-switch", + "propertyControlLabel": ".t--property-control-label", "todayText": "span:contains('Today')", "dayPickerToday": ".DayPicker-Day--today" -} +} \ No newline at end of file diff --git a/app/client/cypress/locators/commonlocators.json b/app/client/cypress/locators/commonlocators.json index ccdf52e4853f..e6750c2d4e93 100644 --- a/app/client/cypress/locators/commonlocators.json +++ b/app/client/cypress/locators/commonlocators.json @@ -126,12 +126,12 @@ "filePickerRemoveButton": ".uppy-Dashboard-Item-action--remove", "AddMoreFiles": ".uppy-DashboardContent-addMoreCaption", "filePickerOnFilesSelected": ".t--property-control-onfilesselected", - "dataType": ".t--property-control-datatype input", - "recaptchaVersion": ".t--property-control-googlerecaptchaversion input", + "dataType": ".t--property-control-datatype", + "recaptchaVersion": ".t--property-control-googlerecaptchaversion", "recaptchaVersionText": ".t--property-control-googlerecaptchaversion span.rc-select-selection-item span", - "filePickerDataFormat": ".t--property-control-dataformat .rc-select-selection-item", + "filePickerDataFormat": ".t--property-control-dataformat", "helperText": ".t--property-control-helperText", - "jsonFormFieldType": ".t--property-control-fieldtype input", + "jsonFormFieldType": ".t--property-control-fieldtype", "jsonFormAddNewCustomFieldBtn": ".t--property-control-fieldconfiguration .t--add-column-btn", "evaluateMsg": ".t--evaluatedPopup-error", "globalSearchModal": "[data-testid='t--global-search-modal']", @@ -207,7 +207,7 @@ "fixed": "Fixed", "autoHeight": "Auto Height", "autoHeightWithLimits": "Auto Height with limits", - "heightDropdown": "[data-guided-tour-iid='dynamicHeight'] input", + "heightDropdown": "[data-guided-tour-iid='dynamicHeight']", "minHeight": "minheight\\(inrows\\)", "maxHeight": "maxheight\\(inrows\\)", "overlayMin": "[data-testid='t--auto-height-overlay-min']", @@ -248,4 +248,4 @@ "downloadFileType": "button[class*='t--open-dropdown-Select-file-type'] > span:first-of-type", "listToggle": "[data-testid='t--list-toggle']", "showBindingsMenu": "//*[@id='entity-properties-container']" -} +} \ No newline at end of file diff --git a/app/client/cypress/support/Pages/AggregateHelper.ts b/app/client/cypress/support/Pages/AggregateHelper.ts index 466b32c26381..970192e1d9cb 100644 --- a/app/client/cypress/support/Pages/AggregateHelper.ts +++ b/app/client/cypress/support/Pages/AggregateHelper.ts @@ -575,12 +575,14 @@ export class AggregateHelper { }); } - public WaitUntilEleAppear(selector: string) { + // Note: isVisible is required in case where item exists but is not visible ( hidden by css ), + // For e.g - search input in select widget is not visible, + public WaitUntilEleAppear(selector: string, isVisible = true) { cy.waitUntil( () => this.GetElement(selector) .should("exist") - .should("be.visible") + .should(isVisible ? "be.visible" : "not.be.visible") .its("length") .should("be.gte", 1), { diff --git a/app/client/cypress/support/Pages/GitSync.ts b/app/client/cypress/support/Pages/GitSync.ts index db8baad24ff3..2d405a639687 100644 --- a/app/client/cypress/support/Pages/GitSync.ts +++ b/app/client/cypress/support/Pages/GitSync.ts @@ -450,10 +450,16 @@ export class GitSync { public CheckMergeConflicts(destinationBranch: string) { this.agHelper.AssertElementExist(this.locators.quickActionsPullBtn); this.agHelper.GetNClick(this.locators.quickActionsMergeBtn); - this.agHelper.WaitUntilEleAppear(this.locators.opsMergeBranchSelectMenu); + this.agHelper.WaitUntilEleAppear( + this.locators.opsMergeBranchSelectMenu, + false, + ); this.agHelper.WaitUntilEleDisappear(this.locators.opsMergeLoader); this.assertHelper.AssertNetworkStatus("@getBranch", 200); - this.agHelper.WaitUntilEleAppear(this.locators.opsMergeBranchSelectMenu); + this.agHelper.WaitUntilEleAppear( + this.locators.opsMergeBranchSelectMenu, + false, + ); this.agHelper.GetNClick(this.locators.opsMergeBranchSelectMenu, 0, true); this.agHelper.AssertContains(destinationBranch); this.agHelper.GetNClickByContains( diff --git a/app/client/cypress/support/widgetCommands.js b/app/client/cypress/support/widgetCommands.js index 23d1d828f6a4..9e42a7b359af 100644 --- a/app/client/cypress/support/widgetCommands.js +++ b/app/client/cypress/support/widgetCommands.js @@ -86,14 +86,45 @@ Cypress.Commands.add("selectDateFormat", (value) => { .click({ force: true }); }); +Cypress.Commands.add("openSelectDropdown", (element) => { + let isDropdownAlreadyOpen = false; + + cy.get(element) + .invoke("html") + .then((html) => { + if (html.includes("rc-select-open")) { + isDropdownAlreadyOpen = true; + } + }) + .then(() => { + if (!isDropdownAlreadyOpen) { + cy.get(element).last().scrollIntoView().click({ force: true }); + cy.get(`${element} .rc-select-selection-search-input`) + .last() + .click({ force: true }); + } + }); +}); + Cypress.Commands.add("selectDropdownValue", (element, value) => { - cy.get(element).last().scrollIntoView().click({ force: true }); + cy.openSelectDropdown(element); + cy.get(".t--dropdown-option") .children() .contains(value) .click({ force: true }); }); +Cypress.Commands.add("searchSelectDropdown", (value) => { + cy.get(".ads-v2-select__dropdown .ads-v2-input__input-section-input").click(); + cy.get(".ads-v2-select__dropdown .ads-v2-input__input-section-input").should( + "have.focus", + ); + cy.get(".ads-v2-select__dropdown .ads-v2-input__input-section-input").type( + value, + ); +}); + Cypress.Commands.add("assertDateFormat", () => { cy.get(".t--draggable-datepickerwidget2 input") .first() diff --git a/app/client/package.json b/app/client/package.json index 1a4652a73863..a3d793937fa9 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -387,4 +387,4 @@ "@types/react": "^17.0.2", "postcss": "8.4.31" } -} +} \ No newline at end of file diff --git a/app/client/packages/design-system/ads/src/Checkbox/Checkbox.tsx b/app/client/packages/design-system/ads/src/Checkbox/Checkbox.tsx index 8e4a63887352..6e2d6cb2ccfa 100644 --- a/app/client/packages/design-system/ads/src/Checkbox/Checkbox.tsx +++ b/app/client/packages/design-system/ads/src/Checkbox/Checkbox.tsx @@ -27,7 +27,7 @@ function Checkbox(props: CheckboxProps) { isFocusVisible={isFocusVisible} isIndeterminate={isIndeterminate} > - {children} + {children} diff --git a/app/client/packages/design-system/ads/src/Select/Select.mdx b/app/client/packages/design-system/ads/src/Select/Select.mdx index 3a939f2ba29f..a1d5ee249586 100644 --- a/app/client/packages/design-system/ads/src/Select/Select.mdx +++ b/app/client/packages/design-system/ads/src/Select/Select.mdx @@ -51,6 +51,10 @@ Select allows users to make single or multiple selections from a list of options +### Grouping with Checkbox in options + + + ## Best practices The select component should: diff --git a/app/client/packages/design-system/ads/src/Select/Select.stories.tsx b/app/client/packages/design-system/ads/src/Select/Select.stories.tsx index e0daa015fd0f..9cda90bb397e 100644 --- a/app/client/packages/design-system/ads/src/Select/Select.stories.tsx +++ b/app/client/packages/design-system/ads/src/Select/Select.stories.tsx @@ -1,9 +1,10 @@ import React, { useState } from "react"; -import { Select, Option } from "./Select"; +import { Select, Option, OptGroup } from "./Select"; import { Icon } from "../Icon"; import { Checkbox } from "../Checkbox"; import type { SelectProps } from "./Select.types"; import type { StoryObj } from "@storybook/react"; +import type { DefaultOptionType } from "rc-select/lib/Select"; export default { title: "ADS/Components/Select", @@ -970,3 +971,75 @@ export function SelectWithCheckbox() { ); } + +const groupOptions = [ + { + label: "Group 1", + options: [ + { + label: "Option 1", + value: "value 11", + }, + { + label: "Very long label to force a line break in the option", + value: "Very long label to force a line break in the option", + }, + ], + }, + { + label: "Group 2", + options: Array.from({ length: 1000 }, (_, i) => ({ + label: `Option ${i + 1}`, + value: `value ${i + 1}`, + })), + }, +]; + +export function SelectWithCheckboxAndGroup() { + const [selectedOptions, setSelectedOptions] = useState( + [], + ); + + return ( + + ); +} diff --git a/app/client/packages/design-system/ads/src/Select/Select.tsx b/app/client/packages/design-system/ads/src/Select/Select.tsx index e591525d9adb..958624dceb23 100644 --- a/app/client/packages/design-system/ads/src/Select/Select.tsx +++ b/app/client/packages/design-system/ads/src/Select/Select.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useRef, useState } from "react"; import RCSelect, { Option as RCOption, OptGroup as RCOptGroup, @@ -12,6 +12,7 @@ import { SelectClassName, SelectDropdownClassName } from "./Select.constants"; import { Tag } from "../Tag"; import type { SelectProps } from "./Select.types"; import { Spinner } from "../Spinner"; +import { SearchInput } from "../SearchInput"; /* TODO: @@ -29,15 +30,20 @@ function Select(props: SelectProps) { isLoading = false, isMultiSelect, isValid, - maxTagCount = 2, + maxTagCount = isMultiSelect + ? props.value?.length > 1 + ? "responsive" + : 1 + : undefined, maxTagPlaceholder, - maxTagTextLength = 5, placeholder = "Please select an option", showSearch = false, size = "md", virtual = false, ...rest } = props; + const searchRef = useRef(null); + const [searchValue, setSearchValue] = useState(""); const getMaxTagPlaceholder = (omittedValues: any[]) => { return `+${omittedValues.length}`; @@ -51,6 +57,24 @@ function Select(props: SelectProps) { return ; } + const handleDropdownVisibleChange = (open: boolean) => { + if (open) { + // this is a hack to get the search input to focus when the dropdown is opened + // the reason is, rc-select does not support putting the search input in the dropdown + // and rc-select focus its native searchinput element on dropdown open, but we need to focus the search input + // so we use a timeout to focus the search input after the dropdown is opened + setTimeout(() => { + if (!searchRef.current) return; + + searchRef.current?.focus(); + }, 200); + + return; + } + + setSearchValue(""); + }; + return ( { + return ( +
+ {showSearch && ( + + )} +
{menu}
+
+ ); + }} inputIcon={} maxTagCount={maxTagCount} maxTagPlaceholder={maxTagPlaceholder || getMaxTagPlaceholder} - maxTagTextLength={maxTagTextLength} menuItemSelectedIcon="" - mode={isMultiSelect ? "multiple" : undefined} + mode={isMultiSelect ? "tags" : undefined} + onDropdownVisibleChange={handleDropdownVisibleChange} placeholder={placeholder} + searchValue={searchValue} showArrow - showSearch={showSearch} tagRender={(props) => { + if (rest.tagRender) { + return rest.tagRender(props); + } + const { closable, label, onClose } = props; return ( - + {label} ); diff --git a/app/client/packages/design-system/ads/src/Select/rc-styles.css b/app/client/packages/design-system/ads/src/Select/rc-styles.css index fe486dddb05d..fd6d8cf9c978 100644 --- a/app/client/packages/design-system/ads/src/Select/rc-styles.css +++ b/app/client/packages/design-system/ads/src/Select/rc-styles.css @@ -4,10 +4,12 @@ width: 100px; position: relative; } + .rc-select-disabled, .rc-select-disabled input { cursor: not-allowed; } + .rc-select-show-arrow.rc-select-loading .rc-select-arrow-icon::after { box-sizing: border-box; width: 12px; @@ -20,27 +22,35 @@ margin-top: 4px; animation: rcSelectLoadingIcon 0.5s infinite; } + .rc-select .rc-select-selection-placeholder { opacity: 0.4; pointer-events: none; } + .rc-select .rc-select-selection-search-input { appearance: none; + opacity: 0; } + .rc-select .rc-select-selection-search-input::-webkit-search-cancel-button { display: none; appearance: none; } + .rc-select-single .rc-select-selector { display: flex; position: relative; } + .rc-select-single .rc-select-selector .rc-select-selection-search { width: 100%; } + .rc-select-single .rc-select-selector .rc-select-selection-search-input { width: 100%; } + .rc-select-single .rc-select-selector .rc-select-selection-item, .rc-select-single .rc-select-selector .rc-select-selection-placeholder { position: absolute; @@ -48,6 +58,7 @@ left: 3px; pointer-events: none; } + /* Selects the currently selected item when in the input box*/ .rc-select-single .rc-select-selector .rc-select-selection-item, .rc-select-single .rc-select-selector .rc-select-selection-placeholder, @@ -55,6 +66,7 @@ overflow: hidden; text-overflow: ellipsis; } + .rc-select-single:not(.rc-select-customize-input) .rc-select-selector .rc-select-selection-search-input { @@ -63,12 +75,14 @@ width: 100%; padding: 0; } + .rc-select-multiple .rc-select-selector { display: flex; flex-wrap: wrap; padding: 1px; border: 1px solid #000; } + .rc-select-multiple .rc-select-selector .rc-select-selection-item { flex: none; background: #bbb; @@ -76,28 +90,34 @@ margin-right: 2px; padding: 0 8px; } + .rc-select-multiple .rc-select-selector .rc-select-selection-item-disabled { cursor: not-allowed; opacity: 0.5; } + .rc-select-multiple .rc-select-selector .rc-select-selection-overflow { display: flex; flex-wrap: wrap; width: 100%; } + .rc-select-multiple .rc-select-selector .rc-select-selection-overflow-item { flex: none; max-width: 100%; } + .rc-select-multiple .rc-select-selector .rc-select-selection-search { position: relative; max-width: 100%; } + .rc-select-multiple .rc-select-selector .rc-select-selection-search-input, .rc-select-multiple .rc-select-selector .rc-select-selection-search-mirror { padding: 1px; font-family: var(--ads-v2-font-family); } + .rc-select-multiple .rc-select-selector .rc-select-selection-search-mirror { position: absolute; z-index: 999; @@ -107,29 +127,35 @@ top: 0; visibility: hidden; } + .rc-select-multiple .rc-select-selector .rc-select-selection-search-input { border: none; outline: none; background: rgba(255, 0, 0, 0.2); width: 100%; } + .rc-select-allow-clear.rc-select-multiple .rc-select-selector { padding-right: 20px; } + .rc-select-allow-clear .rc-select-clear { position: absolute; right: 20px; top: 0; } + .rc-select-show-arrow.rc-select-multiple .rc-select-selector { padding-right: 20px; } + .rc-select-show-arrow .rc-select-arrow { pointer-events: none; position: absolute; right: 5px; top: 0; } + .rc-select-show-arrow .rc-select-arrow-icon::after { content: ""; border: 5px solid transparent; @@ -145,60 +171,75 @@ position: absolute; background: #fff; } + .rc-select-dropdown-hidden { display: none; } + .rc-select-item { font-size: 16px; line-height: 1.5; padding: 4px 16px; } + .rc-select-item-group { color: #999; font-weight: bold; font-size: 80%; } + .rc-select-item-option { position: relative; } + .rc-select-item-option-grouped { padding-left: 24px; } + .rc-select-item-option .rc-select-item-option-state { position: absolute; right: 0; top: 4px; pointer-events: none; } + .rc-select-item-option-active { background: #ddd; } + .rc-select-item-option-disabled { color: #999; } + .rc-select-item-empty { text-align: center; color: #999; } + .rc-select-selection__choice-zoom { transition: all 0.3s; } + .rc-select-selection__choice-zoom-appear { opacity: 0; transform: scale(0.5); } + .rc-select-selection__choice-zoom-appear.rc-select-selection__choice-zoom-appear-active { opacity: 1; transform: scale(1); } + .rc-select-selection__choice-zoom-leave { opacity: 1; transform: scale(1); } + .rc-select-selection__choice-zoom-leave.rc-select-selection__choice-zoom-leave-active { opacity: 0; transform: scale(0.5); } + .rc-select-dropdown-slide-up-enter, .rc-select-dropdown-slide-up-appear { animation-duration: 0.3s; @@ -208,6 +249,7 @@ animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1); animation-play-state: paused; } + .rc-select-dropdown-slide-up-leave { animation-duration: 0.3s; animation-fill-mode: both; @@ -216,6 +258,7 @@ animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34); animation-play-state: paused; } + .rc-select-dropdown-slide-up-enter.rc-select-dropdown-slide-up-enter-active.rc-select-dropdown-placement-bottomLeft, .rc-select-dropdown-slide-up-appear.rc-select-dropdown-slide-up-appear-active.rc-select-dropdown-placement-bottomLeft, .rc-select-dropdown-slide-up-enter.rc-select-dropdown-slide-up-enter-active.rc-select-dropdown-placement-bottomRight, @@ -223,11 +266,13 @@ animation-name: rcSelectDropdownSlideUpIn; animation-play-state: running; } + .rc-select-dropdown-slide-up-leave.rc-select-dropdown-slide-up-leave-active.rc-select-dropdown-placement-bottomLeft, .rc-select-dropdown-slide-up-leave.rc-select-dropdown-slide-up-leave-active.rc-select-dropdown-placement-bottomRight { animation-name: rcSelectDropdownSlideUpOut; animation-play-state: running; } + .rc-select-dropdown-slide-up-enter.rc-select-dropdown-slide-up-enter-active.rc-select-dropdown-placement-topLeft, .rc-select-dropdown-slide-up-appear.rc-select-dropdown-slide-up-appear-active.rc-select-dropdown-placement-topLeft, .rc-select-dropdown-slide-up-enter.rc-select-dropdown-slide-up-enter-active.rc-select-dropdown-placement-topRight, @@ -235,71 +280,84 @@ animation-name: rcSelectDropdownSlideDownIn; animation-play-state: running; } + .rc-select-dropdown-slide-up-leave.rc-select-dropdown-slide-up-leave-active.rc-select-dropdown-placement-topLeft, .rc-select-dropdown-slide-up-leave.rc-select-dropdown-slide-up-leave-active.rc-select-dropdown-placement-topRight { animation-name: rcSelectDropdownSlideDownOut; animation-play-state: running; } + .rc-virtual-list-scrollbar { width: 5px !important; height: 5px !important; } + .rc-virtual-list-scrollbar .rc-virtual-list-scrollbar-thumb { background-color: var(--ads-v2-color-bg-emphasis) !important; border-radius: 36px; } + @keyframes rcSelectDropdownSlideUpIn { 0% { opacity: 0; transform-origin: 0% 0%; transform: scaleY(0); } + 100% { opacity: 1; transform-origin: 0% 0%; transform: scaleY(1); } } + @keyframes rcSelectDropdownSlideUpOut { 0% { opacity: 1; transform-origin: 0% 0%; transform: scaleY(1); } + 100% { opacity: 0; transform-origin: 0% 0%; transform: scaleY(0); } } + @keyframes rcSelectDropdownSlideDownIn { 0% { transform: scaleY(0); transform-origin: 100% 100%; opacity: 0; } + 100% { transform: scaleY(1); transform-origin: 100% 100%; opacity: 1; } } + @keyframes rcSelectDropdownSlideDownOut { 0% { transform: scaleY(1); transform-origin: 100% 100%; opacity: 1; } + 100% { transform: scaleY(0); transform-origin: 100% 100%; opacity: 0; } } + @keyframes rcSelectLoadingIcon { 0% { transform: rotate(0); } + 100% { transform: rotate(360deg); } diff --git a/app/client/packages/design-system/ads/src/Select/styles.css b/app/client/packages/design-system/ads/src/Select/styles.css index 723192f68ac2..9210c3bedcb0 100644 --- a/app/client/packages/design-system/ads/src/Select/styles.css +++ b/app/client/packages/design-system/ads/src/Select/styles.css @@ -19,7 +19,7 @@ } .ads-v2-select.rc-select-show-search * { - cursor: text; + cursor: unset; } /* size sm */ @@ -187,7 +187,10 @@ display: flex; font-size: 12px; align-items: center; - background: var(--ads-v2-colors-control-pill-default-bg); + background: var(--ads-v2-colors-content-surface-info-bg); + border: 1px solid var(--ads-v2-colors-content-surface-info-border); + color: var(--ads-v2-colors-content-label-info-fg); + line-height: normal; } /* typing space */ @@ -208,11 +211,10 @@ border-radius: var(--ads-v2-border-radius); border: 1px solid var(--ads-v2-colors-content-container-default-border); box-shadow: var(--ads-v2-shadow-popovers); - padding: var(--ads-v2-spaces-2); + padding: 0; animation-duration: 400ms; animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1); will-change: transform, opacity; - overflow: auto; z-index: 1001; pointer-events: auto; } @@ -226,15 +228,29 @@ --select-option-height: 36px; padding: var(--select-option-padding); - margin-bottom: var(--ads-v2-spaces-1); + margin-inline: var(--ads-v2-spaces-2); border-radius: var(--ads-v2-border-radius); cursor: pointer; - /* TODO: remove !important after WDS fix their issue in tree select */ - background-color: var(--select-option-color-bg) !important; position: relative; color: var(--ads-v2-colors-content-label-default-fg); min-height: var(--select-option-height); box-sizing: border-box; + display: flex; + align-items: center; + background-color: var(--select-option-color-bg); +} + +.ads-v2-select__dropdown .rc-virtual-list { + padding-top: var(--ads-v2-spaces-2); + padding-bottom: var(--ads-v2-spaces-2); +} + +/* if the dropdown first item is a group, dont add padding-top to virtual list */ +.ads-v2-select__dropdown:has( + .rc-select-item:first-child:is(.rc-select-item-group) + ) + .rc-virtual-list { + padding-top: 0; } /* Option group */ @@ -249,8 +265,6 @@ border-radius: var(--ads-v2-border-radius); font-weight: 500; font-size: var(--ads-v2-font-size-4); - /* TODO: remove !important after WDS fix their issue in tree select */ - background-color: var(--select-option-color-bg) !important; position: relative; color: var(--ads-v2-colors-content-label-default-fg); min-height: var(--select-option-height); @@ -267,12 +281,9 @@ --select-option-height: 36px; padding: var(--select-option-padding); - padding-left: var(--ads-v2-spaces-5); - margin-bottom: var(--ads-v2-spaces-1); + margin-left: var(--ads-v2-spaces-2); border-radius: var(--ads-v2-border-radius); cursor: pointer; - /* TODO: remove !important after WDS fix their issue in tree select */ - background-color: var(--select-option-color-bg) !important; position: relative; color: var(--ads-v2-colors-content-label-default-fg); min-height: var(--select-option-height); @@ -306,6 +317,18 @@ font-size: var(--select-option-font-size); overflow: hidden; overflow-wrap: break-word; + width: 100%; +} + +.ads-v2-select__dropdown + .rc-select-item.rc-select-item-option + .rc-select-item-option-content + > label + > span { + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; } /* Option hover */ @@ -314,6 +337,13 @@ .rc-select-item.rc-select-item-option.rc-select-item-option-active { --select-option-color-bg: var(--ads-v2-colors-content-surface-hover-bg); outline: none; + --select-option-color-bg: var(--ads-v2-colors-content-surface-hover-bg); +} + +/* selected */ +.ads-v2-select__dropdown + .rc-select-item.rc-select-item-option.rc-select-item-option-selected { + --select-option-color-bg: var(--ads-v2-colors-content-surface-active-bg); } /* Option focus */ @@ -328,11 +358,6 @@ outline-offset: var(--ads-v2-offset-outline); } -/* Option active */ -.ads-v2-select__dropdown .rc-select-item.rc-select-item-option:active { - --select-option-color-bg: var(--ads-v2-colors-content-surface-active-bg); -} - /* Option disabled */ .ads-v2-select__dropdown .rc-select-item.rc-select-item-option.rc-select-item-option-disabled { @@ -348,8 +373,42 @@ color: var(--ads-v2-colors-content-helper-default-fg); } -/* Selected option */ -.ads-v2-select__dropdown - .rc-select-item.rc-select-item-option.rc-select-item-option-selected { - --select-option-color-bg: var(--ads-v2-colors-content-surface-active-bg); +/* search input */ +.ads-v2-select__dropdown .ads-v2-search-input input { + border-top: none; + border-left: none; + border-right: none; + border-bottom: 1px solid var(--ads-v2-color-border-emphasis); + border-radius: 0; + outline: none !important; +} + +/* this is required because we want to set the max width of first tag around 60% of the container width */ +.ads-v2-select .rc-select-selector { + container-type: inline-size; +} + +/* tags */ +.ads-v2-select + .rc-select-selection-overflow-item:first-child + .ads-v2-tag + > span { + max-width: calc(60cqw - 16px); +} + +.ads-v2-select .rc-select-selection-overflow-item .ads-v2-tag > span { + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; + max-width: calc(100% - 16px); +} + +.ads-v2-select .rc-select-selection-overflow-item-suffix { + display: none; +} + +.ads-v2-select__dropdown .rc-virtual-list { + padding-top: var(--ads-v2-spaces-2); + padding-bottom: var(--ads-v2-spaces-2); } diff --git a/app/client/packages/design-system/ads/src/Tag/Tag.styles.tsx b/app/client/packages/design-system/ads/src/Tag/Tag.styles.tsx index 19ca1304417f..fc786df527b1 100644 --- a/app/client/packages/design-system/ads/src/Tag/Tag.styles.tsx +++ b/app/client/packages/design-system/ads/src/Tag/Tag.styles.tsx @@ -78,6 +78,10 @@ export const StyledTag = styled.span<{ display: flex; align-items: center; + & > span { + line-height: normal; + } + ${({ isClosed }) => isClosed && `display: none;`} `; @@ -85,9 +89,8 @@ export const StyledButton = styled(Button)` --button-color-fg: var(--tag-color-fg); --button-color-bg: inherit; - margin-left: var(--ads-v2-spaces-1); + margin-left: var(--ads-v2-spaces-2); position: relative; - top: 1px; // align with text cursor: pointer; &:hover:not([data-disabled="true"]):not([data-loading="true"]) { diff --git a/app/client/packages/design-system/ads/src/Tag/Tag.tsx b/app/client/packages/design-system/ads/src/Tag/Tag.tsx index de430670ecc9..d2b13f51a3ab 100644 --- a/app/client/packages/design-system/ads/src/Tag/Tag.tsx +++ b/app/client/packages/design-system/ads/src/Tag/Tag.tsx @@ -24,7 +24,7 @@ function Tag({ }; return ( - + {children} @@ -36,6 +36,7 @@ function Tag({ isIconButton kind="tertiary" onClick={closeHandler} + onMouseDown={(e) => e.stopPropagation()} size="sm" startIcon="close-line" /> diff --git a/app/client/packages/design-system/ads/src/Tag/Tag.types.tsx b/app/client/packages/design-system/ads/src/Tag/Tag.types.tsx index 5416ff7ec369..ade342c078f6 100644 --- a/app/client/packages/design-system/ads/src/Tag/Tag.types.tsx +++ b/app/client/packages/design-system/ads/src/Tag/Tag.types.tsx @@ -4,7 +4,7 @@ import type { Sizes } from "../__config__/types"; export type TagSizes = Extract; // TODO: Update this to include "Kind" from __config__/types -export type TagKind = "neutral" | "special" | "premium"; +export type TagKind = "neutral" | "special" | "premium" | "info"; export type TagProps = { /** the size of the tag */ diff --git a/app/client/src/components/editorComponents/WidgetQueryGeneratorForm/CommonControls/TableOrSpreadsheetDropdown/index.tsx b/app/client/src/components/editorComponents/WidgetQueryGeneratorForm/CommonControls/TableOrSpreadsheetDropdown/index.tsx index 801e23a135f4..ae9d6a68032d 100644 --- a/app/client/src/components/editorComponents/WidgetQueryGeneratorForm/CommonControls/TableOrSpreadsheetDropdown/index.tsx +++ b/app/client/src/components/editorComponents/WidgetQueryGeneratorForm/CommonControls/TableOrSpreadsheetDropdown/index.tsx @@ -35,10 +35,6 @@ function TableOrSpreadsheetDropdown() {