feat: Integrate one click binding to sourceData of select and multi select widget (#25750)

## Description
We have changed the property control of sourceData of select and multi
select widget to dropdowns which has one click-binding items.

#### PR fixes following issue(s)
Fixes https://github.com/appsmithorg/appsmith/issues/24780

#### Type of change

- New feature (non-breaking change which adds functionality)

## Testing
>
#### How Has This Been Tested?
> Please describe the tests that you ran to verify your changes. Also
list any relevant details for your test configuration.
> Delete anything that is not relevant
- [x] Manual
- [ ] Cypress
>
>
#### Test Plan
>(https://github.com/appsmithorg/TestSmith/issues/2472)
>
>
#### Issues raised during DP testing
>
(https://github.com/appsmithorg/appsmith/pull/25750#issuecomment-1665077044)
>
>
>
## Checklist:
#### Dev activity
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag


#### QA activity:
- [ ] [Speedbreak
features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-)
have been covered
- [x] Test plan covers all impacted features and [areas of
interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-)
- [x] Test plan has been peer reviewed by project stakeholders and other
QA members
- [x] Manually tested functionality on DP
- [ ] We had an implementation alignment call with stakeholders post QA
Round 2
- [ ] Cypress test cases have been added and approved by SDET/manual QA
- [ ] Added `Test Plan Approved` label after Cypress tests were reviewed
- [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
This commit is contained in:
balajisoundar 2023-08-10 10:51:19 +05:30 committed by GitHub
parent 0d533dab90
commit 727d30ad92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 982 additions and 243 deletions

View File

@ -2,6 +2,7 @@ const testdata = require("../../../../fixtures/testdata.json");
import { import {
entityExplorer, entityExplorer,
agHelper, agHelper,
propPane,
} from "../../../../support/Objects/ObjectsCore"; } from "../../../../support/Objects/ObjectsCore";
describe("Binding the multiple widgets and validating default data", function () { describe("Binding the multiple widgets and validating default data", function () {
@ -11,6 +12,7 @@ describe("Binding the multiple widgets and validating default data", function ()
it("1. Dropdown widget test with invalid binding value", function () { it("1. Dropdown widget test with invalid binding value", function () {
entityExplorer.SelectEntityByName("Dropdown1"); entityExplorer.SelectEntityByName("Dropdown1");
propPane.ToggleJSMode("sourcedata");
cy.testJsontext("sourcedata", JSON.stringify(testdata.defaultdataBinding)); cy.testJsontext("sourcedata", JSON.stringify(testdata.defaultdataBinding));
cy.evaluateErrorMessage(testdata.dropdownErrorMsg); cy.evaluateErrorMessage(testdata.dropdownErrorMsg);
//Table widget test with invalid binding value //Table widget test with invalid binding value

View File

@ -20,6 +20,7 @@ describe("Binding the multiple widgets and validating default data", function ()
); );
//Dropdown widget test with default value from table widget //Dropdown widget test with default value from table widget
entityExplorer.SelectEntityByName("Dropdown1"); entityExplorer.SelectEntityByName("Dropdown1");
propPane.ToggleJSMode("sourcedata");
cy.testJsontext( cy.testJsontext(
"sourcedata", "sourcedata",
JSON.stringify(testdata.deafultDropDownWidget), JSON.stringify(testdata.deafultDropDownWidget),

View File

@ -147,7 +147,10 @@ describe("excludeForAirgap", "Widget property navigation", () => {
); );
_.agHelper.GetNClick(OneClickBindingLocator.searchableColumn); _.agHelper.GetNClick(OneClickBindingLocator.searchableColumn);
_.agHelper.GetNClick( _.agHelper.GetNClick(
OneClickBindingLocator.searchableColumnDropdownOption(), OneClickBindingLocator.columnDropdownOption(
"searchableColumn",
"imdb_id",
),
); );
_.agHelper.GetNClick(OneClickBindingLocator.connectData); _.agHelper.GetNClick(OneClickBindingLocator.connectData);

View File

@ -0,0 +1,97 @@
import oneClickBindingLocator from "../../../../../locators/OneClickBindingLocator";
import { OneClickBinding } from "../spec_utility";
import {
agHelper,
entityExplorer,
dataSources,
draggableWidgets,
assertHelper,
propPane,
} from "../../../../../support/Objects/ObjectsCore";
import formWidgetsPage from "../../../../../locators/FormWidgets.json";
import widgetsPage from "../../../../../locators/Widgets.json";
import commonlocators from "../../../../../locators/commonlocators.json";
const oneClickBinding = new OneClickBinding();
describe("Table widget one click binding feature", () => {
it("should check that queries are created and bound to table widget properly", () => {
entityExplorer.DragDropWidgetNVerify(
draggableWidgets.MULTISELECT,
450,
200,
);
entityExplorer.NavigateToSwitcher("Explorer");
dataSources.CreateDataSource("Mongo");
cy.get("@dsName").then((dsName) => {
entityExplorer.NavigateToSwitcher("Widgets");
entityExplorer.SelectEntityByName("MultiSelect1", "Widgets");
oneClickBinding.ChooseAndAssertForm(`${dsName}`, dsName, "netflix", {
label: "name",
value: "director",
});
});
agHelper.GetNClick(oneClickBindingLocator.connectData);
assertHelper.AssertNetworkStatus("@postExecute");
agHelper.Sleep(2000);
entityExplorer.DragDropWidgetNVerify(draggableWidgets.TEXT, 450, 500);
propPane.UpdatePropertyFieldValue(
"Text",
`{{MultiSelect1.selectedOptionLabels.toString()}}:{{MultiSelect1.selectedOptionValues.toString()}}`,
);
[
{
label: "I Care a Lot",
text: "I Care a Lot:J Blakeson",
},
{
label: "tick, tick...BOOM!",
text: "I Care a Lot,tick, tick...BOOM!:J Blakeson,Lin-Manuel Miranda",
},
{
label: "Munich The Edge of War",
text: "I Care a Lot,tick, tick...BOOM!,Munich The Edge of War:J Blakeson,Lin-Manuel Miranda,Christian Schwochow",
},
].forEach((d) => {
cy.get(formWidgetsPage.multiSelectWidget)
.find(".rc-select-selector")
.click({
force: true,
});
cy.get(".rc-select-item").contains(d.label).click({
force: true,
});
cy.get(commonlocators.TextInside).first().should("have.text", d.text);
});
agHelper.Sleep(2000);
cy.get(formWidgetsPage.multiselectWidgetv2search)
.first()
.focus({ force: true } as any)
.type("Haunting", { force: true });
assertHelper.AssertNetworkStatus("@postExecute");
agHelper.Sleep(2000);
cy.get(
".rc-select-item-option:contains('The Haunting of Hill House')",
).should("have.length", 1);
cy.get(".rc-select-item")
.contains("The Haunting of Hill House")
.should("exist");
});
});

View File

@ -0,0 +1,104 @@
import oneClickBindingLocator from "../../../../../locators/OneClickBindingLocator";
import { OneClickBinding } from "../spec_utility";
import {
agHelper,
entityExplorer,
dataSources,
draggableWidgets,
assertHelper,
propPane,
} from "../../../../../support/Objects/ObjectsCore";
import formWidgetsPage from "../../../../../locators/FormWidgets.json";
import widgetsPage from "../../../../../locators/Widgets.json";
import commonlocators from "../../../../../locators/commonlocators.json";
const oneClickBinding = new OneClickBinding();
describe("Table widget one click binding feature", () => {
it("should check that queries are created and bound to table widget properly", () => {
entityExplorer.DragDropWidgetNVerify(
draggableWidgets.MULTISELECT,
450,
200,
);
entityExplorer.NavigateToSwitcher("Explorer");
dataSources.CreateDataSource("Postgres");
cy.get("@dsName").then((dsName) => {
entityExplorer.NavigateToSwitcher("Widgets");
entityExplorer.SelectEntityByName("MultiSelect1", "Widgets");
oneClickBinding.ChooseAndAssertForm(
`${dsName}`,
dsName,
"public.employees",
{
label: "first_name",
value: "last_name",
},
);
});
agHelper.GetNClick(oneClickBindingLocator.connectData);
assertHelper.AssertNetworkStatus("@postExecute");
agHelper.Sleep(2000);
entityExplorer.DragDropWidgetNVerify(draggableWidgets.TEXT, 450, 500);
propPane.UpdatePropertyFieldValue(
"Text",
`{{MultiSelect1.selectedOptionLabels.toString()}}:{{MultiSelect1.selectedOptionValues.toString()}}`,
);
[
{
label: "Andrew",
text: "Andrew:Fuller",
},
{
label: "Janet",
text: "Andrew,Janet:Fuller,Leverling",
},
{
label: "Margaret",
text: "Andrew,Janet,Margaret:Fuller,Leverling,Peacock",
},
].forEach((d) => {
cy.get(formWidgetsPage.multiSelectWidget)
.find(".rc-select-selector")
.click({
force: true,
});
cy.get(".rc-select-item").contains(d.label).click({
force: true,
});
cy.get(commonlocators.TextInside).first().should("have.text", d.text);
});
agHelper.Sleep(2000);
cy.get(formWidgetsPage.multiSelectWidget)
.find(".rc-select-selector")
.click({
force: true,
});
cy.get(formWidgetsPage.multiselectwidgetv2)
.find(".rc-select-selection-search-input")
.first()
.focus({ force: true } as any)
.type("Anne", { force: true });
assertHelper.AssertNetworkStatus("@postExecute");
agHelper.Sleep(2000);
cy.get(".rc-select-item").contains("Anne").should("exist");
});
});

View File

@ -99,23 +99,17 @@ describe("excludeForAirgap", "One click binding control", () => {
propPane.ToggleJSMode("Table data", false); propPane.ToggleJSMode("Table data", false);
oneClickBinding.ChooseAndAssertForm( oneClickBinding.ChooseAndAssertForm("Users", "Users", "public.users", {
"Users", searchableColumn: "gender",
"Users", });
"public.users",
"gender",
);
propPane.MoveToTab("Style"); propPane.MoveToTab("Style");
propPane.MoveToTab("Content"); propPane.MoveToTab("Content");
oneClickBinding.ChooseAndAssertForm( oneClickBinding.ChooseAndAssertForm("sample Movies", "movies", "movies", {
"sample Movies", searchableColumn: "status",
"movies", });
"movies",
"status",
);
entityExplorer.NavigateToSwitcher("Explorer"); entityExplorer.NavigateToSwitcher("Explorer");
dataSources.NavigateToDSCreateNew(); dataSources.NavigateToDSCreateNew();

View File

@ -0,0 +1,98 @@
import oneClickBindingLocator from "../../../../../locators/OneClickBindingLocator";
import { OneClickBinding } from "../spec_utility";
import {
agHelper,
entityExplorer,
dataSources,
draggableWidgets,
assertHelper,
propPane,
} from "../../../../../support/Objects/ObjectsCore";
import formWidgetsPage from "../../../../../locators/FormWidgets.json";
import widgetsPage from "../../../../../locators/Widgets.json";
import commonlocators from "../../../../../locators/commonlocators.json";
const oneClickBinding = new OneClickBinding();
describe("Table widget one click binding feature", () => {
it("should check that queries are created and bound to table widget properly", () => {
entityExplorer.DragDropWidgetNVerify(draggableWidgets.SELECT, 450, 200);
entityExplorer.NavigateToSwitcher("Explorer");
dataSources.CreateDataSource("Mongo");
cy.get("@dsName").then((dsName) => {
entityExplorer.NavigateToSwitcher("Widgets");
entityExplorer.SelectEntityByName("Select1", "Widgets");
oneClickBinding.ChooseAndAssertForm(`${dsName}`, dsName, "netflix", {
label: "name",
value: "director",
});
});
agHelper.GetNClick(oneClickBindingLocator.connectData);
assertHelper.AssertNetworkStatus("@postExecute");
agHelper.Sleep(2000);
entityExplorer.DragDropWidgetNVerify(draggableWidgets.TEXT, 450, 500);
propPane.UpdatePropertyFieldValue(
"Text",
`{{Select1.selectedOptionLabel}}:{{Select1.selectedOptionValue}}`,
);
[
{
label: "I Care a Lot",
text: "I Care a Lot:J Blakeson",
},
{
label: "tick, tick...BOOM!",
text: "tick, tick...BOOM!:Lin-Manuel Miranda",
},
{
label: "Munich The Edge of War",
text: "Munich The Edge of War:Christian Schwochow",
},
].forEach((d) => {
cy.get(formWidgetsPage.selectWidget)
.find(widgetsPage.dropdownSingleSelect)
.click({
force: true,
});
cy.get(commonlocators.singleSelectWidgetMenuItem)
.contains(d.label)
.click({
force: true,
});
cy.get(commonlocators.TextInside).first().should("have.text", d.text);
});
cy.get(formWidgetsPage.selectWidget)
.find(widgetsPage.dropdownSingleSelect)
.click({
force: true,
});
cy.get(commonlocators.selectInputSearch).type("I Care a Lot");
assertHelper.AssertNetworkStatus("@postExecute");
agHelper.Sleep(2000);
cy.get(".select-popover-wrapper .menu-item-link")
.children()
.should("have.length", 1);
agHelper.AssertElementExist(
commonlocators.singleSelectWidgetMenuItem + `:contains(I Care a Lot)`,
);
});
});

View File

@ -0,0 +1,103 @@
import oneClickBindingLocator from "../../../../../locators/OneClickBindingLocator";
import { OneClickBinding } from "../spec_utility";
import {
agHelper,
entityExplorer,
dataSources,
draggableWidgets,
assertHelper,
propPane,
} from "../../../../../support/Objects/ObjectsCore";
import formWidgetsPage from "../../../../../locators/FormWidgets.json";
import widgetsPage from "../../../../../locators/Widgets.json";
import commonlocators from "../../../../../locators/commonlocators.json";
const oneClickBinding = new OneClickBinding();
describe("Table widget one click binding feature", () => {
it("should check that queries are created and bound to table widget properly", () => {
entityExplorer.DragDropWidgetNVerify(draggableWidgets.SELECT, 450, 200);
entityExplorer.NavigateToSwitcher("Explorer");
dataSources.CreateDataSource("Postgres");
cy.get("@dsName").then((dsName) => {
entityExplorer.NavigateToSwitcher("Widgets");
entityExplorer.SelectEntityByName("Select1", "Widgets");
oneClickBinding.ChooseAndAssertForm(
`${dsName}`,
dsName,
"public.employees",
{
label: "first_name",
value: "last_name",
},
);
});
agHelper.GetNClick(oneClickBindingLocator.connectData);
assertHelper.AssertNetworkStatus("@postExecute");
agHelper.Sleep(2000);
entityExplorer.DragDropWidgetNVerify(draggableWidgets.TEXT, 450, 500);
propPane.UpdatePropertyFieldValue(
"Text",
`{{Select1.selectedOptionLabel}}:{{Select1.selectedOptionValue}}`,
);
[
{
label: "Andrew",
text: "Andrew:Fuller",
},
{
label: "Janet",
text: "Janet:Leverling",
},
{
label: "Margaret",
text: "Margaret:Peacock",
},
].forEach((d) => {
cy.get(formWidgetsPage.selectWidget)
.find(widgetsPage.dropdownSingleSelect)
.click({
force: true,
});
cy.get(commonlocators.singleSelectWidgetMenuItem)
.contains(d.label)
.click({
force: true,
});
cy.get(commonlocators.TextInside).first().should("have.text", d.text);
});
cy.get(formWidgetsPage.selectWidget)
.find(widgetsPage.dropdownSingleSelect)
.click({
force: true,
});
cy.get(commonlocators.selectInputSearch).type("Anne");
assertHelper.AssertNetworkStatus("@postExecute");
agHelper.Sleep(2000);
cy.get(".select-popover-wrapper .menu-item-link")
.children()
.should("have.length", 1);
agHelper.AssertElementExist(
commonlocators.singleSelectWidgetMenuItem + `:contains(Anne)`,
);
});
});

View File

@ -25,12 +25,9 @@ describe("one click binding mongodb datasource", function () {
cy.get("@dsName").then((dsName) => { cy.get("@dsName").then((dsName) => {
entityExplorer.SelectEntityByName("Table1", "Widgets"); entityExplorer.SelectEntityByName("Table1", "Widgets");
oneClickBinding.ChooseAndAssertForm( oneClickBinding.ChooseAndAssertForm(`${dsName}`, dsName, "netflix", {
`${dsName}`, searchableColumn: "creator",
dsName, });
"netflix",
"creator",
);
}); });
agHelper.GetNClick(oneClickBindingLocator.connectData); agHelper.GetNClick(oneClickBindingLocator.connectData);

View File

@ -23,12 +23,9 @@ describe.skip("Table widget one click binding feature", () => {
entityExplorer.SelectEntityByName("Table1", "Widgets"); entityExplorer.SelectEntityByName("Table1", "Widgets");
oneClickBinding.ChooseAndAssertForm( oneClickBinding.ChooseAndAssertForm(`${dsName}`, dsName, "configs", {
`${dsName}`, searchableColumn: "configName",
dsName, });
"configs",
"configName",
);
}); });
agHelper.GetNClick(oneClickBindingLocator.connectData); agHelper.GetNClick(oneClickBindingLocator.connectData);

View File

@ -28,7 +28,9 @@ describe("Table widget one click binding feature", () => {
`${dsName}`, `${dsName}`,
dsName, dsName,
"public.employees", "public.employees",
"first_name", {
searchableColumn: "first_name",
},
); );
}); });

View File

@ -6,7 +6,7 @@ export class OneClickBinding {
source?: string, source?: string,
selectedSource?: any, selectedSource?: any,
table?: string, table?: string,
column?: string, column: Record<string, string> = {},
) { ) {
agHelper.GetNClick(oneClickBindingLocator.datasourceDropdownSelector); agHelper.GetNClick(oneClickBindingLocator.datasourceDropdownSelector);
@ -37,17 +37,19 @@ export class OneClickBinding {
oneClickBindingLocator.tableOrSpreadsheetSelectedOption(table), oneClickBindingLocator.tableOrSpreadsheetSelectedOption(table),
); );
agHelper.AssertElementExist(oneClickBindingLocator.searchableColumn); Object.entries(column).forEach(([key, value]) => {
agHelper.AssertElementExist((oneClickBindingLocator as any)[key]);
agHelper.GetNClick(oneClickBindingLocator.searchableColumn); agHelper.GetNClick((oneClickBindingLocator as any)[key]);
agHelper.GetNClick( agHelper.GetNClick(
oneClickBindingLocator.searchableColumnDropdownOption(column), oneClickBindingLocator.columnDropdownOption(key, value),
); );
agHelper.AssertElementExist( agHelper.AssertElementExist(
oneClickBindingLocator.searchableColumnSelectedOption(column), oneClickBindingLocator.columnSelectedOption(key, value),
); );
});
agHelper.AssertElementExist(oneClickBindingLocator.connectData); agHelper.AssertElementExist(oneClickBindingLocator.connectData);

View File

@ -17,6 +17,7 @@ describe("Dropdown Widget Functionality", function () {
it("should check that empty value is allowed in options", () => { it("should check that empty value is allowed in options", () => {
cy.openPropertyPane("selectwidget"); cy.openPropertyPane("selectwidget");
_.propPane.ToggleJSMode("sourcedata");
cy.updateCodeInput( cy.updateCodeInput(
".t--property-control-sourcedata", ".t--property-control-sourcedata",
`[ `[
@ -35,16 +36,16 @@ describe("Dropdown Widget Functionality", function () {
]`, ]`,
); );
_.propPane.ToggleJSMode("label"); _.propPane.ToggleJSMode("label key");
cy.updateCodeInput( cy.updateCodeInput(
".t--property-control-wrapper.t--property-control-label", ".t--property-control-wrapper.t--property-control-labelkey",
`label`, `label`,
); );
_.propPane.ToggleJSMode("value"); _.propPane.ToggleJSMode("value key");
cy.updateCodeInput(".t--property-control-value", `value`); cy.updateCodeInput(".t--property-control-valuekey", `value`);
cy.get(".t--property-control-value .t--codemirror-has-error").should( cy.get(".t--property-control-valuekey .t--codemirror-has-error").should(
"not.exist", "not.exist",
); );
}); });
@ -68,7 +69,7 @@ describe("Dropdown Widget Functionality", function () {
} }
]`, ]`,
); );
cy.get(".t--property-control-value .t--codemirror-has-error").should( cy.get(".t--property-control-valuekey .t--codemirror-has-error").should(
"exist", "exist",
); );
}); });
@ -91,9 +92,7 @@ describe("Dropdown Widget Functionality", function () {
}]`, }]`,
); );
cy.updateCodeInput(".t--property-control-defaultselectedvalue", "BLUE"); cy.updateCodeInput(".t--property-control-defaultselectedvalue", "BLUE");
cy.get(".t--property-control-value .t--codemirror-has-error").should( cy.get(".t--property-key .t--codemirror-has-error").should("not.exist");
"not.exist",
);
cy.get( cy.get(
".t--property-control-defaultselectedvalue .t--codemirror-has-error", ".t--property-control-defaultselectedvalue .t--codemirror-has-error",
).should("not.exist"); ).should("not.exist");

View File

@ -21,6 +21,8 @@ describe("Select Widgets", function () {
100, 100,
); );
_.propPane.ToggleJSMode("sourcedata");
_.propPane.UpdatePropertyFieldValue( _.propPane.UpdatePropertyFieldValue(
"Source Data", "Source Data",
`{{[{ `{{[{
@ -29,14 +31,14 @@ describe("Select Widgets", function () {
}]}}`, }]}}`,
); );
_.propPane.ToggleJSMode("label"); _.propPane.ToggleJSMode("label key");
cy.updateCodeInput( cy.updateCodeInput(
".t--property-control-wrapper.t--property-control-label", ".t--property-control-wrapper.t--property-control-labelkey",
`label`, `label`,
); );
_.propPane.ToggleJSMode("value"); _.propPane.ToggleJSMode("value key");
cy.updateCodeInput(".t--property-control-value", `value`); cy.updateCodeInput(".t--property-control-valuekey", `value`);
_.propPane.UpdatePropertyFieldValue( _.propPane.UpdatePropertyFieldValue(
"Default selected values", "Default selected values",
@ -50,6 +52,8 @@ describe("Select Widgets", function () {
_.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.SELECT, 250, 300); _.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.SELECT, 250, 300);
_.propPane.ToggleJSMode("sourcedata");
_.propPane.UpdatePropertyFieldValue( _.propPane.UpdatePropertyFieldValue(
"Source Data", "Source Data",
`{{[{ `{{[{
@ -58,14 +62,14 @@ describe("Select Widgets", function () {
}]}}`, }]}}`,
); );
_.propPane.ToggleJSMode("label"); _.propPane.ToggleJSMode("label key");
cy.updateCodeInput( cy.updateCodeInput(
".t--property-control-wrapper.t--property-control-label", ".t--property-control-wrapper.t--property-control-labelkey",
`label`, `label`,
); );
_.propPane.ToggleJSMode("value"); _.propPane.ToggleJSMode("value key");
cy.updateCodeInput(".t--property-control-value", `value`); cy.updateCodeInput(".t--property-control-valuekey", `value`);
_.propPane.UpdatePropertyFieldValue( _.propPane.UpdatePropertyFieldValue(
"Default selected value", "Default selected value",

View File

@ -12,6 +12,7 @@ describe("MultiSelect Widget Functionality", function () {
_.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.MULTISELECT); _.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.MULTISELECT);
//should check that empty value is allowed in options", () => { //should check that empty value is allowed in options", () => {
cy.openPropertyPane("multiselectwidgetv2"); cy.openPropertyPane("multiselectwidgetv2");
_.propPane.ToggleJSMode("sourcedata");
cy.updateCodeInput( cy.updateCodeInput(
".t--property-control-sourcedata", ".t--property-control-sourcedata",
`[ `[
@ -30,16 +31,16 @@ describe("MultiSelect Widget Functionality", function () {
]`, ]`,
); );
_.propPane.ToggleJSMode("label"); _.propPane.ToggleJSMode("labelkey");
cy.updateCodeInput( cy.updateCodeInput(
".t--property-control-wrapper.t--property-control-label", ".t--property-control-wrapper.t--property-control-labelkey",
`label`, `label`,
); );
_.propPane.ToggleJSMode("value"); _.propPane.ToggleJSMode("valuekey");
cy.updateCodeInput(".t--property-control-value", `value`); cy.updateCodeInput(".t--property-control-valuekey", `value`);
cy.get(".t--property-control-value .t--codemirror-has-error").should( cy.get(".t--property-control-valuekey .t--codemirror-has-error").should(
"not.exist", "not.exist",
); );
}); });
@ -63,7 +64,7 @@ describe("MultiSelect Widget Functionality", function () {
} }
]`, ]`,
); );
cy.get(".t--property-control-value .t--codemirror-has-error").should( cy.get(".t--property-control-valuekey .t--codemirror-has-error").should(
"exist", "exist",
); );
}); });
@ -96,7 +97,7 @@ describe("MultiSelect Widget Functionality", function () {
} }
]`, ]`,
); );
cy.get(".t--property-control-value .t--codemirror-has-error").should( cy.get(".t--property-control-valuekey .t--codemirror-has-error").should(
"not.exist", "not.exist",
); );
cy.get( cy.get(
@ -135,7 +136,7 @@ describe("MultiSelect Widget Functionality", function () {
"RED" "RED"
]`, ]`,
); );
cy.get(".t--property-control-value .t--codemirror-has-error").should( cy.get(".t--property-control-valuekey .t--codemirror-has-error").should(
"not.exist", "not.exist",
); );
cy.get( cy.get(

View File

@ -18,19 +18,20 @@ describe("MultiSelect Widget Functionality", function () {
it("1. Selects value with invalid default value", () => { it("1. Selects value with invalid default value", () => {
cy.openPropertyPane("multiselectwidgetv2"); cy.openPropertyPane("multiselectwidgetv2");
_.propPane.ToggleJSMode("sourcedata");
_.propPane.UpdatePropertyFieldValue( _.propPane.UpdatePropertyFieldValue(
"Source Data", "Source Data",
JSON.stringify(data.input), JSON.stringify(data.input),
); );
_.propPane.ToggleJSMode("label"); _.propPane.ToggleJSMode("labelkey");
cy.updateCodeInput( cy.updateCodeInput(
".t--property-control-wrapper.t--property-control-label", ".t--property-control-wrapper.t--property-control-labelkey",
`label`, `label`,
); );
_.propPane.ToggleJSMode("value"); _.propPane.ToggleJSMode("valuekey");
cy.updateCodeInput(".t--property-control-value", `value`); cy.updateCodeInput(".t--property-control-valuekey", `value`);
_.propPane.UpdatePropertyFieldValue( _.propPane.UpdatePropertyFieldValue(
"Default selected values", "Default selected values",

View File

@ -17,6 +17,7 @@ describe("MultiSelect Widget Functionality", function () {
}); });
it("1. Add new multiselect widget", () => { it("1. Add new multiselect widget", () => {
_.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.MULTISELECT); _.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.MULTISELECT);
_.propPane.ToggleJSMode("sourcedata");
_.propPane.UpdatePropertyFieldValue( _.propPane.UpdatePropertyFieldValue(
"Source Data", "Source Data",
`[ `[
@ -35,14 +36,14 @@ describe("MultiSelect Widget Functionality", function () {
]`, ]`,
); );
_.propPane.ToggleJSMode("label"); _.propPane.ToggleJSMode("labelkey");
cy.updateCodeInput( cy.updateCodeInput(
".t--property-control-wrapper.t--property-control-label", ".t--property-control-wrapper.t--property-control-labelkey",
`label`, `label`,
); );
_.propPane.ToggleJSMode("value"); _.propPane.ToggleJSMode("valuekey");
cy.updateCodeInput(".t--property-control-value", `value`); cy.updateCodeInput(".t--property-control-valuekey", `value`);
_.propPane.UpdatePropertyFieldValue( _.propPane.UpdatePropertyFieldValue(
"Default selected values", "Default selected values",

View File

@ -20,6 +20,8 @@ describe("Select Widget Functionality", function () {
); );
_.agHelper.AssertElementExist(".t--widget-multiselectwidgetv2"); _.agHelper.AssertElementExist(".t--widget-multiselectwidgetv2");
_.propPane.ToggleJSMode("sourcedata");
_.propPane.UpdatePropertyFieldValue( _.propPane.UpdatePropertyFieldValue(
"Source Data", "Source Data",
`[ `[
@ -39,12 +41,12 @@ describe("Select Widget Functionality", function () {
]`, ]`,
); );
_.agHelper.GetNClick(_.propPane._selectPropDropdown("label")); _.agHelper.GetNClick(_.propPane._selectPropDropdown("labelkey"));
["1", "2", "3"].forEach((d) => { ["1", "2", "3"].forEach((d) => {
_.agHelper.AssertElementExist(_.propPane._dropDownValue(d)); _.agHelper.AssertElementExist(_.propPane._dropDownValue(d));
}); });
_.agHelper.GetNClick(_.propPane._selectPropDropdown("value"), 0, true); _.agHelper.GetNClick(_.propPane._selectPropDropdown("valuekey"), 0, true);
["1", "2", "3"].forEach((d) => { ["1", "2", "3"].forEach((d) => {
_.agHelper.AssertElementExist(_.propPane._dropDownValue(d)); _.agHelper.AssertElementExist(_.propPane._dropDownValue(d));
}); });

View File

@ -16,6 +16,8 @@ describe("Select Widget Functionality", function () {
_.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.SELECT, 450, 200); _.entityExplorer.DragDropWidgetNVerify(_.draggableWidgets.SELECT, 450, 200);
_.agHelper.AssertElementExist(".t--widget-selectwidget"); _.agHelper.AssertElementExist(".t--widget-selectwidget");
_.propPane.ToggleJSMode("sourcedata");
_.propPane.UpdatePropertyFieldValue( _.propPane.UpdatePropertyFieldValue(
"Source Data", "Source Data",
`[ `[
@ -35,12 +37,12 @@ describe("Select Widget Functionality", function () {
]`, ]`,
); );
_.agHelper.GetNClick(_.propPane._selectPropDropdown("label")); _.agHelper.GetNClick(_.propPane._selectPropDropdown("labelkey"));
["1", "2", "3"].forEach((d) => { ["1", "2", "3"].forEach((d) => {
_.agHelper.AssertElementExist(_.propPane._dropDownValue(d)); _.agHelper.AssertElementExist(_.propPane._dropDownValue(d));
}); });
_.agHelper.GetNClick(_.propPane._selectPropDropdown("value"), 0, true); _.agHelper.GetNClick(_.propPane._selectPropDropdown("valuekey"), 0, true);
["1", "2", "3"].forEach((d) => { ["1", "2", "3"].forEach((d) => {
_.agHelper.AssertElementExist(_.propPane._dropDownValue(d)); _.agHelper.AssertElementExist(_.propPane._dropDownValue(d));
}); });

View File

@ -20,6 +20,7 @@ describe("Select Widget Functionality", function () {
cy.get(explorer.addWidget).click(); cy.get(explorer.addWidget).click();
cy.dragAndDropToCanvas("selectwidget", { x: 300, y: 300 }); cy.dragAndDropToCanvas("selectwidget", { x: 300, y: 300 });
cy.get(".t--widget-selectwidget").should("exist"); cy.get(".t--widget-selectwidget").should("exist");
_.propPane.ToggleJSMode("sourcedata");
cy.updateCodeInput( cy.updateCodeInput(
".t--property-control-sourcedata", ".t--property-control-sourcedata",
`[ `[
@ -38,14 +39,14 @@ describe("Select Widget Functionality", function () {
]`, ]`,
); );
_.propPane.ToggleJSMode("label"); _.propPane.ToggleJSMode("labelkey");
cy.updateCodeInput( cy.updateCodeInput(
".t--property-control-wrapper.t--property-control-label", ".t--property-control-wrapper.t--property-control-labelkey",
`label`, `label`,
); );
_.propPane.ToggleJSMode("value"); _.propPane.ToggleJSMode("valuekey");
cy.updateCodeInput(".t--property-control-value", `value`); cy.updateCodeInput(".t--property-control-valuekey", `value`);
cy.updateCodeInput( cy.updateCodeInput(
".t--property-control-defaultselectedvalue", ".t--property-control-defaultselectedvalue",

View File

@ -20,6 +20,7 @@ describe("Select Widget Functionality", function () {
it("should check that virtualization works well", () => { it("should check that virtualization works well", () => {
cy.openPropertyPane("selectwidget"); cy.openPropertyPane("selectwidget");
_.propPane.ToggleJSMode("sourcedata");
cy.updateCodeInput( cy.updateCodeInput(
".t--property-control-sourcedata", ".t--property-control-sourcedata",
`[ `[
@ -50,14 +51,14 @@ describe("Select Widget Functionality", function () {
]`, ]`,
); );
_.propPane.ToggleJSMode("label"); _.propPane.ToggleJSMode("labelkey");
cy.updateCodeInput( cy.updateCodeInput(
".t--property-control-wrapper.t--property-control-label", ".t--property-control-wrapper.t--property-control-labelkey",
`label`, `label`,
); );
_.propPane.ToggleJSMode("value"); _.propPane.ToggleJSMode("valuekey");
cy.updateCodeInput(".t--property-control-value", `value`); cy.updateCodeInput(".t--property-control-valuekey", `value`);
cy.get(".t--property-control-value .t--codemirror-has-error").should( cy.get(".t--property-control-value .t--codemirror-has-error").should(
"not.exist", "not.exist",

View File

@ -304,6 +304,7 @@ describe("JSObjects OnLoad Actions tests", function () {
//jsEditor.EnableDisableAsyncFuncSettings("callCountry", false, true); Bug # 13826 //jsEditor.EnableDisableAsyncFuncSettings("callCountry", false, true); Bug # 13826
entityExplorer.SelectEntityByName("Select1", "Widgets"); entityExplorer.SelectEntityByName("Select1", "Widgets");
propPane.ToggleJSMode("sourcedata");
propPane.UpdatePropertyFieldValue( propPane.UpdatePropertyFieldValue(
"Source Data", "Source Data",
`{{ getCitiesList.data.map((row) => { `{{ getCitiesList.data.map((row) => {

View File

@ -18,19 +18,20 @@ describe("Bug #10784 - Passing params from JS to SQL query should not break", ()
before(() => { before(() => {
entityExplorer.DragDropWidgetNVerify(draggableWidgets.BUTTON, 100, 100); entityExplorer.DragDropWidgetNVerify(draggableWidgets.BUTTON, 100, 100);
entityExplorer.DragDropWidgetNVerify(draggableWidgets.SELECT, 500, 100); entityExplorer.DragDropWidgetNVerify(draggableWidgets.SELECT, 500, 100);
propPane.ToggleJSMode("sourcedata");
propPane.UpdatePropertyFieldValue( propPane.UpdatePropertyFieldValue(
"Source Data", "Source Data",
`[\n {\n \"label\": \"7\",\n \"value\": \"7\"\n },\n {\n \"label\": \"8\",\n \"value\": \"8\"\n },\n {\n \"label\": \"9\",\n \"value\": \"9\"\n }\n]`, `[\n {\n \"label\": \"7\",\n \"value\": \"7\"\n },\n {\n \"label\": \"8\",\n \"value\": \"8\"\n },\n {\n \"label\": \"9\",\n \"value\": \"9\"\n }\n]`,
); );
propPane.ToggleJSMode("label"); propPane.ToggleJSMode("labelkey");
(cy as any).updateCodeInput( (cy as any).updateCodeInput(
".t--property-control-wrapper.t--property-control-label", ".t--property-control-wrapper.t--property-control-labelkey",
`label`, `label`,
); );
propPane.ToggleJSMode("value"); propPane.ToggleJSMode("valuekey");
(cy as any).updateCodeInput(".t--property-control-value", `value`); (cy as any).updateCodeInput(".t--property-control-valuekey", `value`);
propPane.UpdatePropertyFieldValue( propPane.UpdatePropertyFieldValue(
"Default selected value", "Default selected value",

View File

@ -150,7 +150,9 @@ describe("Validate MsSQL connection & basic querying with UI flows", () => {
it.skip("3.One click binding - should check that queries are created and bound to table widget properly", () => { it.skip("3.One click binding - should check that queries are created and bound to table widget properly", () => {
entityExplorer.DragDropWidgetNVerify(draggableWidgets.TABLE, 450, 200); entityExplorer.DragDropWidgetNVerify(draggableWidgets.TABLE, 450, 200);
oneClickBinding.ChooseAndAssertForm(dsName, dsName, "Simpsons", "title"); oneClickBinding.ChooseAndAssertForm(dsName, dsName, "Simpsons", {
searchableColumn: "title",
});
agHelper.GetNClick(oneClickBindingLocator.connectData); agHelper.GetNClick(oneClickBindingLocator.connectData);

View File

@ -42,6 +42,7 @@
"sourceData": "", "sourceData": "",
"optionLabel": "label", "optionLabel": "label",
"optionValue": "value", "optionValue": "value",
"dynamicPropertyPathList": [{"key": "sourceData"}],
"widgetName": "Dropdown1", "widgetName": "Dropdown1",
"defaultOptionValue": { "defaultOptionValue": {
"value":"VEG", "value":"VEG",

View File

@ -147,6 +147,7 @@
"bottomRow": 23, "bottomRow": 23,
"parentId": "anq2not518", "parentId": "anq2not518",
"dynamicBindingPathList": [], "dynamicBindingPathList": [],
"dynamicPropertyPathList": [{"key": "sourceData"}],
"dynamicTriggerPathList": [] "dynamicTriggerPathList": []
} }
], ],

View File

@ -140,6 +140,7 @@
"sourceData": "[\n {\n \"label\": \"Blue\",\n \"value\": 0\n },\n {\n \"label\": \"Green\",\n \"value\": \"GREEN\"\n },\n {\n \"label\": \"Red\",\n \"value\": \"RED\"\n },\n\t{\n \"label\": \"Red2\",\n \"value\": \"\"\n }\n]", "sourceData": "[\n {\n \"label\": \"Blue\",\n \"value\": 0\n },\n {\n \"label\": \"Green\",\n \"value\": \"GREEN\"\n },\n {\n \"label\": \"Red\",\n \"value\": \"RED\"\n },\n\t{\n \"label\": \"Red2\",\n \"value\": \"\"\n }\n]",
"optionLabel": "label", "optionLabel": "label",
"optionValue": "value", "optionValue": "value",
"dynamicPropertyPathList": [{"key": "sourceData"}],
"placeholderText": "Select option", "placeholderText": "Select option",
"isDisabled": false, "isDisabled": false,
"key": "ber0u80gjl", "key": "ber0u80gjl",

View File

@ -137,6 +137,7 @@
"sourceData": "", "sourceData": "",
"optionLabel": "label", "optionLabel": "label",
"optionValue": "value", "optionValue": "value",
"dynamicPropertyPathList": [{"key": "sourceData"}],
"isDisabled": false "isDisabled": false
}, },
{ {

View File

@ -95,6 +95,7 @@
"sourceData": "", "sourceData": "",
"optionLabel": "label", "optionLabel": "label",
"optionValue": "value", "optionValue": "value",
"dynamicPropertyPathList": [{"key": "sourceData"}],
"widgetName": "Dropdown1", "widgetName": "Dropdown1",
"type": "SELECT_WIDGET", "type": "SELECT_WIDGET",
"isLoading": false, "isLoading": false,
@ -135,6 +136,7 @@
"leftColumn": 10, "leftColumn": 10,
"optionLable": "label", "optionLable": "label",
"optionValue": "value", "optionValue": "value",
"dynamicPropertyPathList": [{"key": "sourceData"}],
"sourceData": [ "sourceData": [
{ {
"label": "Hashirama Senju", "label": "Hashirama Senju",

View File

@ -4,6 +4,7 @@
"dropdownWidget": ".t--draggable-selectwidget", "dropdownWidget": ".t--draggable-selectwidget",
"menuButtonWidget": ".t--draggable-menubuttonwidget", "menuButtonWidget": ".t--draggable-menubuttonwidget",
"multiselectwidgetv2": ".t--draggable-multiselectwidgetv2", "multiselectwidgetv2": ".t--draggable-multiselectwidgetv2",
"multiselectWidgetv2search": ".multi-select-dropdown .bp3-input",
"multiselecttreeWidget": ".t--draggable-multiselecttreewidget", "multiselecttreeWidget": ".t--draggable-multiselecttreewidget",
"singleselecttreeWidget": ".t--draggable-singleselecttreewidget", "singleselecttreeWidget": ".t--draggable-singleselecttreewidget",
"dropdownSelectionType": ".t--property-control-selectiontype .bp3-popover-target", "dropdownSelectionType": ".t--property-control-selectiontype .bp3-popover-target",

View File

@ -34,16 +34,6 @@ export default {
`[data-testid="t--one-click-binding-table-selector"] .rc-select-selection-item${ `[data-testid="t--one-click-binding-table-selector"] .rc-select-selection-item${
table ? `:contains(${table})` : "" table ? `:contains(${table})` : ""
}`, }`,
searchableColumn:
'[data-testId="t--one-click-binding-column-searchableColumn"]',
searchableColumnDropdownOption: (column?: string) =>
`[data-testId='t--one-click-binding-column-searchableColumn--column']${
column ? `:contains(${column})` : ""
}`,
searchableColumnSelectedOption: (column?: string) =>
`[data-testId="t--one-click-binding-column-searchableColumn"] .rc-select-selection-item${
column ? `:contains(${column})` : ""
}`,
validTableRowData: validTableRowData:
'.t--widget-tablewidgetv2 [role="rowgroup"] [role="button"]', '.t--widget-tablewidgetv2 [role="rowgroup"] [role="button"]',
tableError: (error: string) => tableError: (error: string) =>
@ -52,4 +42,16 @@ export default {
dayViewFromDate: ".DayPicker-Day", dayViewFromDate: ".DayPicker-Day",
loadMore: "[data-testId='t--one-click-binding-datasource--load-more']", loadMore: "[data-testId='t--one-click-binding-datasource--load-more']",
datasourceSearch: `[data-testId="t--one-click-binding-datasource--search"]`, datasourceSearch: `[data-testId="t--one-click-binding-datasource--search"]`,
searchableColumn:
'[data-testId="t--one-click-binding-column-searchableColumn"]',
label: '[data-testId="t--one-click-binding-column-label"]',
value: '[data-testId="t--one-click-binding-column-value"]',
columnDropdownOption: (column: string, value?: string) =>
`[data-testId='t--one-click-binding-column-${column}--column']${
value ? `:contains(${value})` : ""
}`,
columnSelectedOption: (column: string, value?: string) =>
`[data-testId="t--one-click-binding-column-${column}"] .rc-select-selection-item${
value ? `:contains(${value})` : ""
}`,
}; };

View File

@ -54,11 +54,22 @@ export default abstract class GSheets extends BaseQueryGenerator {
const { select } = widgetConfig; const { select } = widgetConfig;
if (select && formConfig.sheetName) { if (select && formConfig.sheetName) {
return { const queryPayload: any = {
type: QUERY_TYPE.SELECT, type: QUERY_TYPE.SELECT,
name: `Find_${removeSpecialChars(formConfig.sheetName)}`, name: `Find_${removeSpecialChars(formConfig.sheetName)}`,
formData: { formData: {
where: { ...this.buildBasicConfig(
COMMAND_TYPES.FIND,
formConfig.tableName,
formConfig.sheetName,
formConfig.tableHeaderIndex,
),
},
dynamicBindingPathList: [],
};
if (select["where"]) {
queryPayload.formData.where = {
data: { data: {
children: [ children: [
{ {
@ -68,40 +79,42 @@ export default abstract class GSheets extends BaseQueryGenerator {
}, },
], ],
}, },
}, };
sortBy: {
queryPayload.dynamicBindingPathList.push({
key: "formData.where.data",
});
}
if (select["sortOrder"] && select["orderBy"]) {
queryPayload.formData.sortBy = {
data: [ data: [
{ {
column: `{{${select["orderBy"]}}}`, column: `{{${select["orderBy"]}}}`,
order: select["sortOrder"], order: select["sortOrder"],
}, },
], ],
}, };
pagination: {
queryPayload.dynamicBindingPathList.push({
key: "formData.sortBy.data",
});
}
if (select["limit"] && select["offset"]) {
queryPayload.formData.pagination = {
data: { data: {
limit: `{{${select["limit"]}}}`, limit: `{{${select["limit"]}}}`,
offset: `{{${select["offset"]}}}`, offset: `{{${select["offset"]}}}`,
}, },
},
...this.buildBasicConfig(
COMMAND_TYPES.FIND,
formConfig.tableName,
formConfig.sheetName,
formConfig.tableHeaderIndex,
),
},
dynamicBindingPathList: [
{
key: "formData.where.data",
},
{
key: "formData.sortBy.data",
},
{
key: "formData.pagination.data",
},
],
}; };
queryPayload.dynamicBindingPathList.push({
key: "formData.pagination.data",
});
}
return queryPayload;
} }
} }

View File

@ -77,7 +77,7 @@ describe("Mongo WidgetQueryGenerator", () => {
data: "{{data_table.pageSize}}", data: "{{data_table.pageSize}}",
}, },
query: { query: {
data: '{{{ title: {$regex: data_table.searchText||""} }}}', data: `{{{ title: {$regex: data_table.searchText||"", '$options' : 'i'} }}}`,
}, },
skip: { skip: {
data: "{{(data_table.pageNo - 1) * data_table.pageSize}}", data: "{{(data_table.pageNo - 1) * data_table.pageSize}}",
@ -152,7 +152,7 @@ describe("Mongo WidgetQueryGenerator", () => {
data: "{{data_table.pageSize}}", data: "{{data_table.pageSize}}",
}, },
query: { query: {
data: '{{{ title: {$regex: data_table.searchText||""} }}}', data: `{{{ title: {$regex: data_table.searchText||"", '$options' : 'i'} }}}`,
}, },
skip: { skip: {
data: "{{(data_table.pageNo - 1) * data_table.pageSize}}", data: "{{(data_table.pageNo - 1) * data_table.pageSize}}",

View File

@ -30,39 +30,66 @@ export default abstract class MongoDB extends BaseQueryGenerator {
const { select } = widgetConfig; const { select } = widgetConfig;
if (select) { if (select) {
return { const queryPayload: any = {
type: QUERY_TYPE.SELECT, type: QUERY_TYPE.SELECT,
name: `Find_${removeSpecialChars(formConfig.tableName)}`, name: `Find_${removeSpecialChars(formConfig.tableName)}`,
formData: { formData: {
find: { find: {
skip: { data: `{{${select["offset"]}}}` }, skip: { data: "" },
query: { query: {
data: formConfig.searchableColumn data: "",
? `{{{ ${formConfig.searchableColumn}: {$regex: ${select["where"]}} }}}`
: "",
}, },
sort: { sort: {
data: `{{ ${select["orderBy"]} ? { [${select["orderBy"]}]: ${select["sortOrder"]} ? 1 : -1 } : {}}}`, data: "",
},
limit: {
data: "",
}, },
limit: { data: `{{${select["limit"]}}}` },
}, },
...this.buildBasicConfig(COMMAND_TYPES.FIND, formConfig.tableName), ...this.buildBasicConfig(COMMAND_TYPES.FIND, formConfig.tableName),
}, },
dynamicBindingPathList: [ dynamicBindingPathList: [],
{
key: "formData.find.skip.data",
},
{
key: "formData.find.query.data",
},
{
key: "formData.find.sort.data",
},
{
key: "formData.find.limit.data",
},
],
}; };
if (select["offset"]) {
queryPayload.formData.find.skip = { data: `{{${select["offset"]}}}` };
queryPayload.dynamicBindingPathList.push({
key: "formData.find.skip.data",
});
}
if (formConfig.searchableColumn) {
queryPayload.formData.find.query = {
data: formConfig.searchableColumn
? `{{{ ${formConfig.searchableColumn}: {$regex: ${select["where"]}, '$options' : 'i'} }}}`
: "",
};
queryPayload.dynamicBindingPathList.push({
key: "formData.find.query.data",
});
}
if (select["orderBy"] && select["sortOrder"]) {
queryPayload.formData.find.sort = {
data: `{{ ${select["orderBy"]} ? { [${select["orderBy"]}]: ${select["sortOrder"]} ? 1 : -1 } : {}}}`,
};
queryPayload.dynamicBindingPathList.push({
key: "formData.find.sort.data",
});
}
if (select["limit"]) {
queryPayload.formData.find.limit = { data: `{{${select["limit"]}}}` };
queryPayload.dynamicBindingPathList.push({
key: "formData.find.limit.data",
});
}
return queryPayload;
} }
} }

View File

@ -18,11 +18,11 @@ export type WidgetQueryGenerationFormConfig = {
export type WidgetQueryGenerationConfig = { export type WidgetQueryGenerationConfig = {
select?: { select?: {
limit: string; limit?: string;
offset: string; offset?: string;
where: string; where?: string;
orderBy: string; orderBy?: string;
sortOrder: string; sortOrder?: string;
}; };
create?: { create?: {
value: string; value: string;
@ -31,7 +31,7 @@ export type WidgetQueryGenerationConfig = {
value: string; value: string;
where?: string; where?: string;
}; };
totalRecord: boolean; totalRecord?: boolean;
}; };
export enum QUERY_TYPE { export enum QUERY_TYPE {

View File

@ -284,6 +284,7 @@ function getWidgetProps(
{ key: "sourceData" }, { key: "sourceData" },
{ key: "defaultOptionValue" }, { key: "defaultOptionValue" },
], ],
dynamicPropertyPathList: [{ key: "sourceData" }],
}, },
}; };
case "TEXT_WIDGET": case "TEXT_WIDGET":

View File

@ -106,6 +106,7 @@ export function useDatasource(searchText: string) {
onSourceClose, onSourceClose,
propertyName, propertyName,
propertyValue, propertyValue,
sampleData,
updateConfig, updateConfig,
widgetId, widgetId,
} = useContext(WidgetQueryGeneratorFormContext); } = useContext(WidgetQueryGeneratorFormContext);
@ -321,7 +322,7 @@ export function useDatasource(searchText: string) {
const { pageId: currentPageId } = useParams<ExplorerURLParams>(); const { pageId: currentPageId } = useParams<ExplorerURLParams>();
const otherOptions = useMemo(() => { const otherOptions = useMemo(() => {
return [ const options = [
{ {
icon: <Icon name="plus" size="md" />, icon: <Icon name="plus" size="md" />,
id: "Connect new datasource", id: "Connect new datasource",
@ -350,7 +351,35 @@ export function useDatasource(searchText: string) {
}, },
}, },
]; ];
}, [currentPageId, history, propertyName]);
if (sampleData) {
options.push({
icon: <Icon name="code" size="md" />,
id: "Sample data",
label: "Sample data",
value: "Sample data",
onSelect: () => {
addBinding(sampleData, false);
updateConfig({
datasource: "",
datasourcePluginType: "",
datasourcePluginName: "",
datasourceConnectionMode: "",
});
AnalyticsUtil.logEvent("BIND_OTHER_ACTIONS", {
widgetName: widget.widgetName,
widgetType: widget.type,
propertyName: propertyName,
selectedAction: "Sample data",
});
},
});
}
return options;
}, [currentPageId, history, propertyName, sampleData, addBinding]);
const queries = useSelector(getActionsForCurrentPage); const queries = useSelector(getActionsForCurrentPage);
@ -439,16 +468,20 @@ export function useDatasource(searchText: string) {
]; ];
}, [searchText, datasourceOptions, otherOptions, queryOptions]); }, [searchText, datasourceOptions, otherOptions, queryOptions]);
return { const selected = useMemo(() => {
datasourceOptions: filteredDatasourceOptions,
otherOptions,
selected: (() => {
let source; let source;
if (config.datasource) { if (config.datasource) {
source = datasourceOptions.find( source = datasourceOptions.find(
(option) => option.id === config.datasource, (option) => option.id === config.datasource,
); );
} else if (
sampleData ===
(typeof propertyValue === "string"
? propertyValue
: JSON.stringify(propertyValue, null, 2))
) {
source = otherOptions.find((option) => option.value === "Sample data");
} else if (propertyValue) { } else if (propertyValue) {
source = queryOptions.find((option) => option.value === propertyValue); source = queryOptions.find((option) => option.value === propertyValue);
} }
@ -463,7 +496,19 @@ export function useDatasource(searchText: string) {
} else { } else {
return <Placeholder>Connect data</Placeholder>; return <Placeholder>Connect data</Placeholder>;
} }
})(), }, [
config,
datasourceOptions,
sampleData,
propertyValue,
otherOptions,
queryOptions,
]);
return {
datasourceOptions: filteredDatasourceOptions,
otherOptions,
selected,
queryOptions: filteredQueryOptions, queryOptions: filteredQueryOptions,
isSourceOpen, isSourceOpen,
onSourceClose, onSourceClose,

View File

@ -1,7 +1,7 @@
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import type { AppState } from "@appsmith/reducers"; import type { AppState } from "@appsmith/reducers";
import { PluginPackageName } from "entities/Action"; import { PluginPackageName } from "entities/Action";
import { useContext } from "react"; import { useContext, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { getWidget } from "sagas/selectors"; import { getWidget } from "sagas/selectors";
import { getPluginPackageFromDatasourceId } from "selectors/entitiesSelector"; import { getPluginPackageFromDatasourceId } from "selectors/entitiesSelector";
@ -14,7 +14,7 @@ import { useColumns } from "../WidgetSpecificControls/ColumnDropdown/useColumns"
export function useConnectData() { export function useConnectData() {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { config, propertyName, widgetId } = useContext( const { aliases, config, propertyName, widgetId } = useContext(
WidgetQueryGeneratorFormContext, WidgetQueryGeneratorFormContext,
); );
@ -27,16 +27,30 @@ export function useConnectData() {
); );
const onClick = () => { const onClick = () => {
const searchableColumn = (() => {
if (config.searchableColumn) {
return config.searchableColumn;
} else {
const alias = aliases?.find((d) => d.isSearcheable)?.name;
return alias && config.alias[alias];
}
})();
const payload = { const payload = {
tableName: config.table, tableName: config.table,
sheetName: config.sheet, sheetName: config.sheet,
datasourceId: config.datasource, datasourceId: config.datasource,
widgetId: widgetId, widgetId: widgetId,
tableHeaderIndex: config.tableHeaderIndex, tableHeaderIndex: config.tableHeaderIndex,
searchableColumn: config.searchableColumn, searchableColumn,
columns: columns.map((column) => column.name), columns: columns.map((column) => column.name),
primaryColumn, primaryColumn,
connectionMode: config.datasourceConnectionMode, connectionMode: config.datasourceConnectionMode,
aliases: Object.entries(config.alias).map(([key, value]) => ({
name: key,
alias: value,
})),
}; };
dispatch({ dispatch({
@ -54,6 +68,7 @@ export function useConnectData() {
additionalData: { additionalData: {
dataTableName: config.table, dataTableName: config.table,
searchableColumn: config.searchableColumn, searchableColumn: config.searchableColumn,
alias: config.alias,
}, },
}); });
}; };
@ -64,10 +79,17 @@ export function useConnectData() {
const show = !!config.datasource; const show = !!config.datasource;
const disabled = const disabled = useMemo(() => {
return (
!config.table || !config.table ||
(selectedDatasourcePluginPackageName === PluginPackageName.GOOGLE_SHEETS && (selectedDatasourcePluginPackageName ===
!isValidGsheetConfig(config)); PluginPackageName.GOOGLE_SHEETS &&
!isValidGsheetConfig(config)) ||
aliases?.some((alias) => {
return alias.isRequired && !config.alias[alias.name];
})
);
}, [config, aliases]);
return { return {
show, show,

View File

@ -6,6 +6,7 @@ import { ErrorMessage, Label, SelectWrapper } from "../../styles";
import { useColumns } from "./useColumns"; import { useColumns } from "./useColumns";
type Props = { type Props = {
id: string;
alias: string; alias: string;
label: string; label: string;
onSelect: () => void; onSelect: () => void;
@ -29,7 +30,7 @@ function ColumnDropdown(props: Props) {
<Label>{props.label}</Label> <Label>{props.label}</Label>
<Select <Select
allowClear allowClear
data-testId={`t--one-click-binding-column-${props.alias}`} data-testId={`t--one-click-binding-column-${props.id}`}
dropdownStyle={{ dropdownStyle={{
minWidth: "350px", minWidth: "350px",
maxHeight: "300px", maxHeight: "300px",
@ -51,7 +52,7 @@ function ColumnDropdown(props: Props) {
{options.map((option) => { {options.map((option) => {
return ( return (
<Option <Option
data-testId={`t--one-click-binding-column-${props.alias}--column`} data-testId={`t--one-click-binding-column-${props.id}--column`}
key={option.id} key={option.id}
value={option.value} value={option.value}
> >

View File

@ -3,7 +3,6 @@ import type { AppState } from "@appsmith/reducers";
import { Icon } from "design-system"; import { Icon } from "design-system";
import { PluginPackageName } from "entities/Action"; import { PluginPackageName } from "entities/Action";
import { get, isArray } from "lodash"; import { get, isArray } from "lodash";
import { ALLOWED_SEARCH_DATATYPE } from "pages/Editor/GeneratePage/components/constants";
import { useCallback, useContext, useMemo } from "react"; import { useCallback, useContext, useMemo } from "react";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { import {
@ -70,14 +69,7 @@ export function useColumns(alias: string) {
}; };
}); });
} else if (isArray(columns)) { } else if (isArray(columns)) {
return columns return columns.map((column: any) => {
.filter((column: any) => {
return (
column.type &&
ALLOWED_SEARCH_DATATYPE.includes(column.type.toLowerCase())
);
})
.map((column: any) => {
return { return {
id: column.name, id: column.name,
label: column.name, label: column.name,

View File

@ -1,37 +1,48 @@
import React from "react"; import React from "react";
import ColumnDropdown from "./ColumnDropdown"; import ColumnDropdown from "./ColumnDropdown";
import { noop } from "lodash"; import { noop } from "lodash";
import type { Alias } from "../types";
type Props = { type Props = {
hasSearchableColumn?: boolean; hasSearchableColumn?: boolean;
hasAliasPicker?: boolean; aliases?: Alias[];
aliases?: string[];
}; };
export default function WidgetSpecificControls(props: Props) { export default function WidgetSpecificControls(props: Props) {
let searchableColumn = null; let searchableColumn = null;
let aliasPicker = null; let aliases = null;
if (props.hasSearchableColumn) { if (props.hasSearchableColumn) {
searchableColumn = ( searchableColumn = (
<ColumnDropdown <ColumnDropdown
alias="searchableColumn" alias="searchableColumn"
id="searchableColumn"
label="Select a searchable column" label="Select a searchable column"
onSelect={noop} onSelect={noop}
/> />
); );
} }
if (props.hasAliasPicker && props.aliases) { if (props.aliases?.length) {
aliasPicker = props.aliases.map((alias) => { aliases = props.aliases.map(({ name }) => {
<ColumnDropdown alias={`alias.${alias}`} label={alias} onSelect={noop} />; const label = name.slice(0, 1).toUpperCase() + name.slice(1);
return (
<ColumnDropdown
alias={`alias.${name}`}
id={name}
key={name}
label={label}
onSelect={noop}
/>
);
}); });
} }
return ( return (
<> <>
{searchableColumn} {searchableColumn}
{aliasPicker} {aliases}
</> </>
); );
} }

View File

@ -14,6 +14,7 @@ import {
getOneClickBindingConfigForWidget, getOneClickBindingConfigForWidget,
} from "selectors/oneClickBindingSelectors"; } from "selectors/oneClickBindingSelectors";
import { updateOneClickBindingOptionsVisibility } from "actions/oneClickBindingActions"; import { updateOneClickBindingOptionsVisibility } from "actions/oneClickBindingActions";
import type { Alias } from "./types";
type WidgetQueryGeneratorFormContextType = { type WidgetQueryGeneratorFormContextType = {
widgetId: string; widgetId: string;
@ -39,6 +40,8 @@ type WidgetQueryGeneratorFormContextType = {
onSourceClose: () => void; onSourceClose: () => void;
errorMsg: string; errorMsg: string;
expectedType: string; expectedType: string;
sampleData: string;
aliases: Alias[];
}; };
const DEFAULT_CONFIG_VALUE = { const DEFAULT_CONFIG_VALUE = {
@ -64,6 +67,8 @@ const DEFAULT_CONTEXT_VALUE = {
errorMsg: "", errorMsg: "",
propertyName: "", propertyName: "",
expectedType: "", expectedType: "",
sampleData: "",
aliases: [],
}; };
export const WidgetQueryGeneratorFormContext = export const WidgetQueryGeneratorFormContext =
@ -78,6 +83,9 @@ type Props = {
widgetId: string; widgetId: string;
errorMsg: string; errorMsg: string;
expectedType: string; expectedType: string;
aliases: Alias[];
searchableColumn: boolean;
sampleData: string;
}; };
function WidgetQueryGeneratorForm(props: Props) { function WidgetQueryGeneratorForm(props: Props) {
@ -86,11 +94,13 @@ function WidgetQueryGeneratorForm(props: Props) {
const [pristine, setPristine] = useState(true); const [pristine, setPristine] = useState(true);
const { const {
aliases,
errorMsg, errorMsg,
expectedType, expectedType,
onUpdate, onUpdate,
propertyPath, propertyPath,
propertyValue, propertyValue,
sampleData,
widgetId, widgetId,
} = props; } = props;
@ -197,6 +207,8 @@ function WidgetQueryGeneratorForm(props: Props) {
errorMsg, errorMsg,
propertyName: propertyPath, propertyName: propertyPath,
expectedType, expectedType,
sampleData,
aliases,
}; };
}, [ }, [
config, config,
@ -208,6 +220,8 @@ function WidgetQueryGeneratorForm(props: Props) {
onSourceClose, onSourceClose,
errorMsg, errorMsg,
propertyPath, propertyPath,
sampleData,
aliases,
]); ]);
useEffect(() => { useEffect(() => {
@ -221,7 +235,10 @@ function WidgetQueryGeneratorForm(props: Props) {
<WidgetQueryGeneratorFormContext.Provider value={contextValue}> <WidgetQueryGeneratorFormContext.Provider value={contextValue}>
<CommonControls /> <CommonControls />
<DatasourceSpecificControls /> <DatasourceSpecificControls />
<WidgetSpecificControls hasSearchableColumn /> <WidgetSpecificControls
aliases={props.aliases}
hasSearchableColumn={props.searchableColumn}
/>
<ConnectData /> <ConnectData />
</WidgetQueryGeneratorFormContext.Provider> </WidgetQueryGeneratorFormContext.Provider>
</Wrapper> </Wrapper>

View File

@ -6,3 +6,9 @@ export interface DropdownOptionType {
onSelect?: (value: string, option: DropdownOptionType) => void; onSelect?: (value: string, option: DropdownOptionType) => void;
data?: any; data?: any;
} }
export interface Alias {
name: string;
isSearcheable?: boolean;
isRequired?: boolean;
}

View File

@ -1,6 +1,7 @@
import WidgetQueryGeneratorForm from "components/editorComponents/WidgetQueryGeneratorForm"; import WidgetQueryGeneratorForm from "components/editorComponents/WidgetQueryGeneratorForm";
import type { Alias } from "components/editorComponents/WidgetQueryGeneratorForm/types";
import React from "react"; import React from "react";
import type { ControlData, ControlProps } from "./BaseControl"; import type { ControlProps } from "./BaseControl";
import BaseControl from "./BaseControl"; import BaseControl from "./BaseControl";
class OneClickBindingControl extends BaseControl<OneClickBindingControlProps> { class OneClickBindingControl extends BaseControl<OneClickBindingControlProps> {
constructor(props: OneClickBindingControlProps) { constructor(props: OneClickBindingControlProps) {
@ -15,9 +16,15 @@ class OneClickBindingControl extends BaseControl<OneClickBindingControlProps> {
* Commenting out as we're not able to switch between the js modes without value being overwritten * Commenting out as we're not able to switch between the js modes without value being overwritten
* with default value by platform * with default value by platform
*/ */
static canDisplayValueInUI(config: ControlData, value: any): boolean { static canDisplayValueInUI(
// {{query1.data}} config: OneClickBindingControlProps,
return /^{{[^.]*\.data}}$/gi.test(value); value: any,
): boolean {
// {{query1.data}} || sample data
return (
/^{{[^.]*\.data}}$/gi.test(value) ||
config.controlConfig?.sampleData === value
);
} }
static shouldValidateValueOnDynamicPropertyOff() { static shouldValidateValueOnDynamicPropertyOff() {
@ -52,11 +59,14 @@ class OneClickBindingControl extends BaseControl<OneClickBindingControlProps> {
public render() { public render() {
return ( return (
<WidgetQueryGeneratorForm <WidgetQueryGeneratorForm
aliases={this.props.controlConfig.aliases}
errorMsg={this.getErrorMessage()} errorMsg={this.getErrorMessage()}
expectedType={this.props.expected?.autocompleteDataType || ""} expectedType={this.props.expected?.autocompleteDataType || ""}
onUpdate={this.onUpdatePropertyValue} onUpdate={this.onUpdatePropertyValue}
propertyPath={this.props.propertyName} propertyPath={this.props.propertyName}
propertyValue={this.props.propertyValue} propertyValue={this.props.propertyValue}
sampleData={this.props.controlConfig.sampleData}
searchableColumn={this.props.controlConfig.searchableColumn}
widgetId={this.props.widgetProperties.widgetId} widgetId={this.props.widgetProperties.widgetId}
/> />
); );
@ -65,4 +75,10 @@ class OneClickBindingControl extends BaseControl<OneClickBindingControlProps> {
export default OneClickBindingControl; export default OneClickBindingControl;
export type OneClickBindingControlProps = ControlProps; export type OneClickBindingControlProps = ControlProps & {
controlConfig: {
aliases: Alias[];
searchableColumn: boolean;
sampleData: string;
};
};

View File

@ -219,7 +219,11 @@ function* BindWidgetToDatasource(
data: `{{${queryNameMap[QUERY_TYPE.SELECT]}.data}}`, data: `{{${queryNameMap[QUERY_TYPE.SELECT]}.data}}`,
run: `{{ run: `{{
${queryNameMap[QUERY_TYPE.SELECT]}.run(); ${queryNameMap[QUERY_TYPE.SELECT]}.run();
${queryNameMap[QUERY_TYPE.TOTAL_RECORD]}.run(); ${
createdQueryNames.includes(queryNameMap[QUERY_TYPE.TOTAL_RECORD])
? queryNameMap[QUERY_TYPE.TOTAL_RECORD] + ".run()"
: ""
}
}}`, }}`,
}; };
} }

View File

@ -6,6 +6,11 @@ import { DynamicHeight } from "utils/WidgetFeatures";
import IconSVG from "./icon.svg"; import IconSVG from "./icon.svg";
import Widget from "./widget"; import Widget from "./widget";
import { WIDGET_TAGS } from "constants/WidgetConstants"; import { WIDGET_TAGS } from "constants/WidgetConstants";
import type { WidgetProps } from "widgets/BaseWidget";
import type {
WidgetQueryConfig,
WidgetQueryGenerationFormConfig,
} from "WidgetQueryGenerators/types";
export const CONFIG = { export const CONFIG = {
features: { features: {
@ -60,6 +65,22 @@ export const CONFIG = {
autocompleteDefinitions: Widget.getAutocompleteDefinitions(), autocompleteDefinitions: Widget.getAutocompleteDefinitions(),
setterConfig: Widget.getSetterConfig(), setterConfig: Widget.getSetterConfig(),
}, },
methods: {
getQueryGenerationConfig: (widgetProps: WidgetProps) => {
return Widget.getQueryGenerationConfig(widgetProps);
},
getPropertyUpdatesForQueryBinding: (
queryConfig: WidgetQueryConfig,
widget: WidgetProps,
formConfig: WidgetQueryGenerationFormConfig,
) => {
return Widget.getPropertyUpdatesForQueryBinding(
queryConfig,
widget,
formConfig,
);
},
},
autoLayout: { autoLayout: {
disabledPropsDefaults: { disabledPropsDefaults: {
labelPosition: LabelPosition.Top, labelPosition: LabelPosition.Top,

View File

@ -35,11 +35,46 @@ import {
getLabelValueKeyOptions, getLabelValueKeyOptions,
valueKeyValidation, valueKeyValidation,
} from "./propertyUtils"; } from "./propertyUtils";
import type {
WidgetQueryConfig,
WidgetQueryGenerationFormConfig,
} from "WidgetQueryGenerators/types";
class MultiSelectWidget extends BaseWidget< class MultiSelectWidget extends BaseWidget<
MultiSelectWidgetProps, MultiSelectWidgetProps,
WidgetState WidgetState
> { > {
static getQueryGenerationConfig(widget: WidgetProps) {
return {
select: {
where: `${widget.widgetName}.filterText`,
},
};
}
static getPropertyUpdatesForQueryBinding(
queryConfig: WidgetQueryConfig,
widget: WidgetProps,
formConfig: WidgetQueryGenerationFormConfig,
) {
let modify;
if (queryConfig.select) {
modify = {
sourceData: queryConfig.select.data,
optionLabel: formConfig.aliases.find((d) => d.name === "label")?.alias,
optionValue: formConfig.aliases.find((d) => d.name === "value")?.alias,
defaultOptionValue: "",
serverSideFiltering: true,
onFilterUpdate: queryConfig.select.run,
};
}
return {
modify,
};
}
static getAutocompleteDefinitions(): AutocompletionDefinitions { static getAutocompleteDefinitions(): AutocompletionDefinitions {
return { return {
"!doc": "!doc":
@ -78,11 +113,33 @@ class MultiSelectWidget extends BaseWidget<
"Takes in an array of objects to display options. Bind data from an API using {{}}", "Takes in an array of objects to display options. Bind data from an API using {{}}",
propertyName: "sourceData", propertyName: "sourceData",
label: "Source Data", label: "Source Data",
controlType: "INPUT_TEXT", controlType: "ONE_CLICK_BINDING_CONTROL",
controlConfig: {
aliases: [
{
name: "label",
isSearcheable: true,
isRequired: true,
},
{
name: "value",
isRequired: true,
},
],
sampleData: JSON.stringify(
[
{ name: "Blue", code: "BLUE" },
{ name: "Green", code: "GREEN" },
{ name: "Red", code: "RED" },
],
null,
2,
),
},
isJSConvertible: true,
placeholderText: '[{ "label": "Option1", "value": "Option2" }]', placeholderText: '[{ "label": "Option1", "value": "Option2" }]',
isBindProperty: true, isBindProperty: true,
isTriggerProperty: false, isTriggerProperty: false,
isJSConvertible: false,
validation: { validation: {
type: ValidationTypes.ARRAY, type: ValidationTypes.ARRAY,
params: { params: {
@ -98,9 +155,10 @@ class MultiSelectWidget extends BaseWidget<
EvaluationSubstitutionType.SMART_SUBSTITUTE, EvaluationSubstitutionType.SMART_SUBSTITUTE,
}, },
{ {
helpText: "Sets the label of the option", helpText:
"Choose or set a field from source data as the display label",
propertyName: "optionLabel", propertyName: "optionLabel",
label: "Label", label: "Label key",
controlType: "DROP_DOWN", controlType: "DROP_DOWN",
customJSControl: "WRAPPED_CODE_EDITOR", customJSControl: "WRAPPED_CODE_EDITOR",
controlConfig: { controlConfig: {
@ -130,9 +188,9 @@ class MultiSelectWidget extends BaseWidget<
additionalAutoComplete: getLabelValueAdditionalAutocompleteData, additionalAutoComplete: getLabelValueAdditionalAutocompleteData,
}, },
{ {
helpText: "Sets the value of the option", helpText: "Choose or set a field from source data as the value",
propertyName: "optionValue", propertyName: "optionValue",
label: "Value", label: "Value key",
controlType: "DROP_DOWN", controlType: "DROP_DOWN",
customJSControl: "WRAPPED_CODE_EDITOR", customJSControl: "WRAPPED_CODE_EDITOR",
controlConfig: { controlConfig: {

View File

@ -3,11 +3,15 @@ import { LabelPosition } from "components/constants";
import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants"; import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants";
import { ResponsiveBehavior } from "utils/autoLayout/constants"; import { ResponsiveBehavior } from "utils/autoLayout/constants";
import { DynamicHeight } from "utils/WidgetFeatures"; import { DynamicHeight } from "utils/WidgetFeatures";
import IconSVG from "./icon.svg"; import IconSVG from "./icon.svg";
import Widget from "./widget"; import Widget from "./widget";
import type { SnipingModeProperty, PropertyUpdates } from "widgets/constants"; import type { SnipingModeProperty, PropertyUpdates } from "widgets/constants";
import { WIDGET_TAGS } from "constants/WidgetConstants"; import { WIDGET_TAGS } from "constants/WidgetConstants";
import type { WidgetProps } from "widgets/BaseWidget";
import type {
WidgetQueryConfig,
WidgetQueryGenerationFormConfig,
} from "WidgetQueryGenerators/types";
export const CONFIG = { export const CONFIG = {
features: { features: {
@ -62,6 +66,20 @@ export const CONFIG = {
setterConfig: Widget.getSetterConfig(), setterConfig: Widget.getSetterConfig(),
}, },
methods: { methods: {
getQueryGenerationConfig: (widgetProps: WidgetProps) => {
return Widget.getQueryGenerationConfig(widgetProps);
},
getPropertyUpdatesForQueryBinding: (
queryConfig: WidgetQueryConfig,
widget: WidgetProps,
formConfig: WidgetQueryGenerationFormConfig,
) => {
return Widget.getPropertyUpdatesForQueryBinding(
queryConfig,
widget,
formConfig,
);
},
getSnipingModeUpdates: ( getSnipingModeUpdates: (
propValueMap: SnipingModeProperty, propValueMap: SnipingModeProperty,
): PropertyUpdates[] => { ): PropertyUpdates[] => {

View File

@ -36,12 +36,47 @@ import {
getLabelValueKeyOptions, getLabelValueKeyOptions,
valueKeyValidation, valueKeyValidation,
} from "./propertyUtils"; } from "./propertyUtils";
import type {
WidgetQueryConfig,
WidgetQueryGenerationFormConfig,
} from "WidgetQueryGenerators/types";
class SelectWidget extends BaseWidget<SelectWidgetProps, WidgetState> { class SelectWidget extends BaseWidget<SelectWidgetProps, WidgetState> {
constructor(props: SelectWidgetProps) { constructor(props: SelectWidgetProps) {
super(props); super(props);
} }
static getQueryGenerationConfig(widget: WidgetProps) {
return {
select: {
where: `${widget.widgetName}.filterText`,
},
};
}
static getPropertyUpdatesForQueryBinding(
queryConfig: WidgetQueryConfig,
widget: WidgetProps,
formConfig: WidgetQueryGenerationFormConfig,
) {
let modify;
if (queryConfig.select) {
modify = {
sourceData: queryConfig.select.data,
optionLabel: formConfig.aliases.find((d) => d.name === "label")?.alias,
optionValue: formConfig.aliases.find((d) => d.name === "value")?.alias,
defaultOptionValue: "",
serverSideFiltering: true,
onFilterUpdate: queryConfig.select.run,
};
}
return {
modify,
};
}
static getAutocompleteDefinitions(): AutocompletionDefinitions { static getAutocompleteDefinitions(): AutocompletionDefinitions {
return { return {
"!doc": "!doc":
@ -79,7 +114,30 @@ class SelectWidget extends BaseWidget<SelectWidgetProps, WidgetState> {
"Takes in an array of objects to display options. Bind data from an API using {{}}", "Takes in an array of objects to display options. Bind data from an API using {{}}",
propertyName: "sourceData", propertyName: "sourceData",
label: "Source Data", label: "Source Data",
controlType: "INPUT_TEXT", controlType: "ONE_CLICK_BINDING_CONTROL",
controlConfig: {
aliases: [
{
name: "label",
isSearcheable: true,
isRequired: true,
},
{
name: "value",
isRequired: true,
},
],
sampleData: JSON.stringify(
[
{ name: "Blue", code: "BLUE" },
{ name: "Green", code: "GREEN" },
{ name: "Red", code: "RED" },
],
null,
2,
),
},
isJSConvertible: true,
placeholderText: '[{ "label": "label1", "value": "value1" }]', placeholderText: '[{ "label": "label1", "value": "value1" }]',
isBindProperty: true, isBindProperty: true,
isTriggerProperty: false, isTriggerProperty: false,
@ -98,9 +156,10 @@ class SelectWidget extends BaseWidget<SelectWidgetProps, WidgetState> {
EvaluationSubstitutionType.SMART_SUBSTITUTE, EvaluationSubstitutionType.SMART_SUBSTITUTE,
}, },
{ {
helpText: "Sets the label of the option", helpText:
"Choose or set a field from source data as the display label",
propertyName: "optionLabel", propertyName: "optionLabel",
label: "Label", label: "Label key",
controlType: "DROP_DOWN", controlType: "DROP_DOWN",
customJSControl: "WRAPPED_CODE_EDITOR", customJSControl: "WRAPPED_CODE_EDITOR",
controlConfig: { controlConfig: {
@ -131,9 +190,9 @@ class SelectWidget extends BaseWidget<SelectWidgetProps, WidgetState> {
additionalAutoComplete: getLabelValueAdditionalAutocompleteData, additionalAutoComplete: getLabelValueAdditionalAutocompleteData,
}, },
{ {
helpText: "Sets the value of the option", helpText: "Choose or set a field from source data as the value",
propertyName: "optionValue", propertyName: "optionValue",
label: "Value", label: "Value key",
controlType: "DROP_DOWN", controlType: "DROP_DOWN",
customJSControl: "WRAPPED_CODE_EDITOR", customJSControl: "WRAPPED_CODE_EDITOR",
controlConfig: { controlConfig: {

View File

@ -30,6 +30,9 @@ export default [
propertyName: "tableData", propertyName: "tableData",
label: "Table data", label: "Table data",
controlType: "ONE_CLICK_BINDING_CONTROL", controlType: "ONE_CLICK_BINDING_CONTROL",
controlConfig: {
searchableColumn: true,
},
placeholderText: '[{ "name": "John" }]', placeholderText: '[{ "name": "John" }]',
inputType: "ARRAY", inputType: "ARRAY",
isBindProperty: true, isBindProperty: true,