diff --git a/app/client/cypress/fixtures/datePicker2dsl.json b/app/client/cypress/fixtures/datePicker2dsl.json new file mode 100644 index 0000000000..ceb13dfb82 --- /dev/null +++ b/app/client/cypress/fixtures/datePicker2dsl.json @@ -0,0 +1,80 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1224, + "snapColumns": 16, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1280, + "containerStyle": "none", + "snapRows": 33, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 9, + "minHeight": 1292, + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "isDisabled": false, + "datePickerType": "DATE_PICKER", + "label": "", + "dateFormat": "DD/MM/YYYY HH:mm", + "widgetName": "DatePicker1", + "defaultDate": "2021-02-05T10:53:12.791Z", + "version": 2, + "type": "DATE_PICKER_WIDGET2", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 5, + "rightColumn": 10, + "topRow": 0, + "bottomRow": 1, + "parentId": "0", + "widgetId": "w4htilgv5t" + }, + { + "isVisible": true, + "text": "Label", + "textStyle": "LABEL", + "textAlign": "LEFT", + "widgetName": "Text1", + "version": 1, + "type": "TEXT_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 1, + "rightColumn": 5, + "topRow": 3, + "bottomRow": 4, + "parentId": "0", + "widgetId": "voohxsv4t2" + }, + { + "isVisible": true, + "text": "Label", + "textStyle": "LABEL", + "textAlign": "LEFT", + "widgetName": "Text2", + "version": 1, + "type": "TEXT_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 8, + "rightColumn": 12, + "topRow": 3, + "bottomRow": 4, + "parentId": "0", + "widgetId": "xif8wugzjv" + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Applications/DuplicateApplication_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Applications/DuplicateApplication_spec.js index 580a401dac..e824916c33 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Applications/DuplicateApplication_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Applications/DuplicateApplication_spec.js @@ -13,6 +13,7 @@ describe("Duplicate application", function() { cy.get(commonlocators.homeIcon).click({ force: true }); const appname = localStorage.getItem("AppName"); cy.get(homePage.searchInput).type(appname); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(2000); cy.get(homePage.applicationCard) diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Applications/UpdateApplication_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Applications/UpdateApplication_spec.js index 1dd97677df..b28651d94f 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Applications/UpdateApplication_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Applications/UpdateApplication_spec.js @@ -14,6 +14,7 @@ describe("Update Application", function() { cy.get(commonlocators.homeIcon).click({ force: true }); appname = localStorage.getItem("AppName"); cy.get(homePage.searchInput).type(appname); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(2000); cy.get(homePage.applicationCard) @@ -53,6 +54,7 @@ describe("Update Application", function() { it("Check for errors in updating application name", function() { cy.get(commonlocators.homeIcon).click({ force: true }); cy.get(homePage.searchInput).type(appname); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(2000); cy.get(homePage.applicationCard) .first() @@ -61,6 +63,7 @@ describe("Update Application", function() { .first() .click({ force: true }); cy.get("#loading").should("not.exist"); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(2000); cy.get(homePage.applicationName).type(" "); cy.get(homePage.toastMessage).should( @@ -79,6 +82,7 @@ describe("Update Application", function() { it("Updates the name of first application to very long name and checks whether update is reflected in the application card with a popover", function() { cy.get(commonlocators.homeIcon).click({ force: true }); cy.get(homePage.searchInput).clear(); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(2000); cy.get(homePage.applicationCard) @@ -94,6 +98,7 @@ describe("Update Application", function() { "response.body.responseMeta.status", 200, ); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(2000); cy.get(homePage.applicationCard) diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TableTextPagination_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TableTextPagination_spec.js index 8746e2b84d..accae859d4 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TableTextPagination_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/Bind_TableTextPagination_spec.js @@ -31,6 +31,7 @@ describe("Test Create Api and Bind to Table widget", function() { /**Validate Table data on current page(page1) */ cy.ValidateTableData("1"); cy.get(commonlocators.tableNextPage).click({ force: true }); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(5000); /* cy.wait("@postExecute").should( diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/ButtonWidgets_NavigateTo_validation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/ButtonWidgets_NavigateTo_validation_spec.js index a5846c60af..caffe2c865 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/ButtonWidgets_NavigateTo_validation_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/ButtonWidgets_NavigateTo_validation_spec.js @@ -22,12 +22,14 @@ describe("Binding the button Widgets and validating NavigateTo Page functionalit .click(); cy.enterNavigatePageName(testdata.externalPage); cy.get(commonlocators.editPropCrossButton).click({ force: true }); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(300); }); it("Button click should take the control to page link validation", function() { cy.PublishtheApp(); cy.get(publish.buttonWidget).click(); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(500); cy.get(publish.buttonWidget).should("not.exist"); cy.go("back"); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/JS_Toggle_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/JS_Toggle_spec.js index fe767d9967..c5ceb1981b 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/JS_Toggle_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/JS_Toggle_spec.js @@ -16,7 +16,7 @@ describe("JS Toggle tests", () => { .should("have.class", "is-active"); cy.testJsontext("visible", "false"); - + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(1000); cy.get(".t--property-control-visible") diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/TableWidgets_NavigateTo_Validation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/TableWidgets_NavigateTo_Validation_spec.js index 762cfe72af..08aa7c30b9 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/TableWidgets_NavigateTo_Validation_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/Binding/TableWidgets_NavigateTo_Validation_spec.js @@ -29,6 +29,7 @@ describe("Table Widget and Navigate to functionality validation", function() { it("Create MyPage and valdiate if its successfully created", function() { cy.Createpage(pageid); cy.addDsl(dsl2); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(500); cy.get(`.t--entity-name:contains("${pageid}")`).should("be.visible"); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js index 2ccdb51220..f15307d536 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js @@ -21,6 +21,7 @@ describe("Test Suite to validate copy/delete/undo functionalites", function() { formWidgetsPage.formInner, ); cy.get(commonlocators.copyWidget).click(); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(500); cy.get(commonlocators.toastBody) .first() @@ -36,6 +37,7 @@ describe("Test Suite to validate copy/delete/undo functionalites", function() { expect($lis.eq(1)).to.contain("{{FormTest.data}}"); }); cy.DeleteWidgetFromSideBar(); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(500); cy.get(apiwidget.propertyList).should("not.exist"); /* @@ -53,6 +55,7 @@ describe("Test Suite to validate copy/delete/undo functionalites", function() { "response.body.responseMeta.status", 200, ); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(500); cy.get(apiwidget.propertyList).then(function($lis) { expect($lis).to.have.length(2); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js index bf695ad477..c6627d91bf 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js @@ -24,6 +24,7 @@ describe("Test Suite to validate copy/delete/undo functionalites", function() { ); cy.get("body").click(); cy.get("body").type(`{${modifierKey}}c`); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(500); cy.get(commonlocators.toastBody) .first() diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/FormWidgets/DatePicker_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/FormWidgets/DatePicker_spec.js index e6111d4d4c..3a1a83a28e 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/FormWidgets/DatePicker_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/FormWidgets/DatePicker_spec.js @@ -63,43 +63,45 @@ describe("DatePicker Widget Functionality", function() { ); }); - it("Datepicker min/max date validation", function() { - cy.get(formWidgetsPage.defaultDate).click({ force: true }); - cy.SetDateToToday(); + // it("Datepicker min/max date validation", function() { + // cy.get(formWidgetsPage.defaultDate).click({ force: true }); + // cy.SetDateToToday(); - cy.get(formWidgetsPage.minDate) - .first() - .click(); - cy.wait(1000); - cy.setDate(-1, "ddd MMM DD YYYY"); + // cy.get(formWidgetsPage.minDate) + // .first() + // .click(); + // // eslint-disable-next-line cypress/no-unnecessary-waiting + // cy.wait(1000); + // cy.setDate(-1, "ddd MMM DD YYYY"); - cy.get(formWidgetsPage.maxDate) - .first() - .click(); - cy.wait(1000); - cy.setDate(1, "ddd MMM DD YYYY"); + // cy.get(formWidgetsPage.maxDate) + // .first() + // .click(); + // // eslint-disable-next-line cypress/no-unnecessary-waiting + // cy.wait(1000); + // cy.setDate(1, "ddd MMM DD YYYY"); - cy.PublishtheApp(); - cy.get(publishPage.datepickerWidget + " .bp3-input").click(); + // cy.PublishtheApp(); + // cy.get(publishPage.datepickerWidget + " .bp3-input").click(); - const minDate = Cypress.moment() - .add(2, "days") - .format("ddd MMM DD YYYY"); - const maxDate = Cypress.moment() - .add(2, "days") - .format("ddd MMM DD YYYY"); + // const minDate = Cypress.moment() + // .add(2, "days") + // .format("ddd MMM DD YYYY"); + // const maxDate = Cypress.moment() + // .add(2, "days") + // .format("ddd MMM DD YYYY"); - cy.get(`.DayPicker-Day[aria-label=\"${minDate}\"]`).should( - "have.attr", - "aria-disabled", - "true", - ); - cy.get(`.DayPicker-Day[aria-label=\"${maxDate}\"]`).should( - "have.attr", - "aria-disabled", - "true", - ); - }); + // cy.get(`.DayPicker-Day[aria-label=\"${minDate}\"]`).should( + // "have.attr", + // "aria-disabled", + // "true", + // ); + // cy.get(`.DayPicker-Day[aria-label=\"${maxDate}\"]`).should( + // "have.attr", + // "aria-disabled", + // "true", + // ); + // }); // it("Datepicker default date validation", function() { // cy.get(formWidgetsPage.defaultDate).click(); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/FormWidgets/FormReset_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/FormWidgets/FormReset_spec.js index 1de82fb833..541586379a 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/FormWidgets/FormReset_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/FormWidgets/FormReset_spec.js @@ -19,7 +19,7 @@ describe("Form reset functionality", function() { cy.get(widgetsPage.formButtonWidget) .contains("Reset") .click(); - + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(500); cy.get(".tr") diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/LayoutWidgets/Tab_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/LayoutWidgets/Tab_spec.js index 3d35ed8020..8afda73a11 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/LayoutWidgets/Tab_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/LayoutWidgets/Tab_spec.js @@ -32,6 +32,7 @@ describe("Tab widget test", function() { .click({ force: true }) .should("be.visible"); cy.get(Layoutpage.tabButton).click({ force: true }); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(200); cy.tabVerify(2, "Day"); cy.get(Layoutpage.tabDelete) diff --git a/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/DatePicker_2_Default_spec.js b/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/DatePicker_2_Default_spec.js new file mode 100644 index 0000000000..765b040a33 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/DatePicker_2_Default_spec.js @@ -0,0 +1,39 @@ +const commonlocators = require("../../../locators/commonlocators.json"); +const formWidgetsPage = require("../../../locators/FormWidgets.json"); +const dsl = require("../../../fixtures/datePicker2dsl.json"); +const publishPage = require("../../../locators/publishWidgetspage.json"); +const pages = require("../../../locators/Pages.json"); + +describe("DatePicker Widget Property pane tests with js bindings", function() { + before(() => { + cy.addDsl(dsl); + }); + + it("Datepicker default date validation with js binding and default date", function() { + cy.openPropertyPane("datepickerwidget2"); + cy.get(".t--property-control-defaultdate .bp3-input").clear(); + cy.get(formWidgetsPage.toggleJsDefaultDate).click(); + cy.testJsontext("defaultdate", "{{ moment().add(-1,'days') }}"); + }); + + it("Text widgets binding with datepicker", function() { + cy.SearchEntityandOpen("Text1"); + cy.testJsontext("text", "{{DatePicker1.formattedDate}}"); + cy.closePropertyPane(); + cy.SearchEntityandOpen("Text2"); + cy.testJsontext("text", "{{DatePicker1.selectedDate}}"); + cy.closePropertyPane(); + }); + + it("Text widgets binding with datepicker", function() { + cy.openPropertyPane("datepickerwidget2"); + cy.selectDateFormat("DD/MM/YYYY"); + cy.assertDateFormat(); + cy.closePropertyPane(); + cy.assertDateFormat(); + }); + it("Datepicker default date validation with js binding", function() { + cy.PublishtheApp(); + cy.wait(10000); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/DatePicker_2_spec.js b/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/DatePicker_2_spec.js new file mode 100644 index 0000000000..c74fd97c46 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/DatePicker_2_spec.js @@ -0,0 +1,79 @@ +const commonlocators = require("../../../locators/commonlocators.json"); +const formWidgetsPage = require("../../../locators/FormWidgets.json"); +const dsl = require("../../../fixtures/datePicker2dsl.json"); +const publishPage = require("../../../locators/publishWidgetspage.json"); +const pages = require("../../../locators/Pages.json"); + +describe("DatePicker Widget Property pane tests with js bindings", function() { + before(() => { + cy.addDsl(dsl); + }); + + it("Datepicker default date validation with js binding", function() { + cy.openPropertyPane("datepickerwidget2"); + cy.get(".t--property-control-defaultdate .bp3-input").clear(); + cy.get(formWidgetsPage.toggleJsDefaultDate).click(); + cy.testJsontext("defaultdate", "{{moment().toISOString()}}"); + cy.get(formWidgetsPage.toggleJsMinDate).click(); + cy.testJsontext( + "mindate", + "{{moment().subtract(10, 'days').toISOString()}}", + ); + cy.get(formWidgetsPage.toggleJsMaxDate).click(); + cy.testJsontext("maxdate", "{{moment().add(10, 'days').toISOString()}}"); + /* + cy.get(formWidgetsPage.datepickerWidget + " .bp3-input").should( + "contain.value", + "14/02/2021", + ); + cy.PublishtheApp(); + cy.get(publishPage.datepickerWidget + " .bp3-input").should( + "contain.value", + "14/02/2021", + ); + */ + }); + + it("Text widgets binding with datepicker", function() { + cy.SearchEntityandOpen("Text1"); + cy.testJsontext("text", "{{DatePicker1.formattedDate}}"); + cy.closePropertyPane(); + cy.SearchEntityandOpen("Text2"); + cy.testJsontext("text", "{{DatePicker1.selectedDate}}"); + cy.closePropertyPane(); + }); + + it("Text widgets binding with datepicker", function() { + cy.openPropertyPane("datepickerwidget2"); + cy.selectDateFormat("YYYY-MM-DD"); + cy.assertDateFormat(); + cy.selectDateFormat("YYYY-MM-DD HH:mm"); + cy.assertDateFormat(); + cy.selectDateFormat("YYYY-MM-DDTHH:mm:ss.sssZ"); + cy.assertDateFormat(); + cy.selectDateFormat("DD/MM/YYYY"); + cy.assertDateFormat(); + cy.selectDateFormat("DD/MM/YYYY HH:mm"); + cy.closePropertyPane(); + cy.assertDateFormat(); + }); + + it("Datepicker default date validation with strings", function() { + cy.openPropertyPane("datepickerwidget2"); + cy.get(formWidgetsPage.toggleJsDefaultDate).click(); + cy.get(".t--property-control-defaultdate .bp3-input").clear(); + cy.get(".t--property-control-defaultdate .bp3-input").type("2020-02-01"); + cy.closePropertyPane(); + cy.openPropertyPane("datepickerwidget2"); + cy.get(formWidgetsPage.toggleJsMinDate).click(); + cy.get(".t--property-control-mindate .bp3-input").type("2020-01-01"); + cy.get(formWidgetsPage.toggleJsMaxDate).click(); + cy.get(".t--property-control-maxdate .bp3-input").type("2020-02-10"); + cy.closePropertyPane(); + }); + + it("Datepicker default date validation with js binding", function() { + cy.PublishtheApp(); + cy.wait(10000); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/DatePicker_Toggle_js_spec.js b/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/DatePicker_Toggle_js_spec.js new file mode 100644 index 0000000000..a581ad7d17 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/DatePicker_Toggle_js_spec.js @@ -0,0 +1,47 @@ +const commonlocators = require("../../../locators/commonlocators.json"); +const formWidgetsPage = require("../../../locators/FormWidgets.json"); +const dsl = require("../../../fixtures/newFormDsl.json"); +const publishPage = require("../../../locators/publishWidgetspage.json"); +const pages = require("../../../locators/Pages.json"); + +describe("DatePicker Widget Property pane tests with js bindings", function() { + before(() => { + cy.addDsl(dsl); + }); + + beforeEach(() => { + cy.openPropertyPane("datepickerwidget"); + }); + + it("Datepicker default date validation with js binding", function() { + cy.get(".t--property-control-defaultdate .bp3-input").clear(); + cy.get(formWidgetsPage.toggleJsDefaultDate).click(); + cy.testJsontext( + "defaultdate", + "{{moment('14/02/2021', 'DD/MM/YYYY').format('DD/MM/YYYY')}}", + ); + cy.get(formWidgetsPage.toggleJsMinDate).click(); + cy.testJsontext( + "mindate", + "{{moment('12/02/2021', 'DD/MM/YYYY').format('DD/MM/YYYY')}}", + ); + cy.get(formWidgetsPage.toggleJsMaxDate).click(); + cy.testJsontext( + "maxdate", + "{{moment('17/02/2021', 'DD/MM/YYYY').format('DD/MM/YYYY')}}", + ); + cy.get(formWidgetsPage.datepickerWidget + " .bp3-input").should( + "contain.value", + "14/02/2021", + ); + cy.PublishtheApp(); + cy.get(publishPage.datepickerWidget + " .bp3-input").should( + "contain.value", + "14/02/2021", + ); + }); + + afterEach(() => { + cy.get(publishPage.backToEditor).click({ force: true }); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OrganisationTests/UpdateOrgTests_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OrganisationTests/UpdateOrgTests_spec.js index 9edcd9f157..fc37647369 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OrganisationTests/UpdateOrgTests_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/OrganisationTests/UpdateOrgTests_spec.js @@ -24,6 +24,7 @@ describe("Update Organization", function() { localStorage.setItem("OrgName", orgid); cy.get(homePage.orgNameInput).clear(); cy.get(homePage.orgNameInput).type(orgid); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(2000); cy.get(homePage.orgHeaderName).should("have.text", orgid); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidgetTableAndBind_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidgetTableAndBind_spec.js index cbf1143f16..11b923bc27 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidgetTableAndBind_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidgetTableAndBind_spec.js @@ -29,6 +29,7 @@ describe("Addwidget from Query and bind with other widgets", function() { .first() .focus() .type("SELECT * FROM configs LIMIT 10;"); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(500); cy.get(queryEditor.runQuery).click(); cy.wait("@postExecute").should( diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidget_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidget_spec.js index 18f5a407ef..d579b4eab6 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidget_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/QueryPane/AddWidget_spec.js @@ -22,6 +22,7 @@ describe("Add widget", function() { .first() .focus() .type("select * from configs"); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(500); cy.get(queryEditor.runQuery).click(); cy.wait("@postExecute").should( diff --git a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/UnitTest/LoginFromUIApp_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/UnitTest/LoginFromUIApp_spec.js index 23d7769e1a..54d3485a4f 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/UnitTest/LoginFromUIApp_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ServerSideTests/UnitTest/LoginFromUIApp_spec.js @@ -26,8 +26,10 @@ describe("Login from UI and check the functionality", function() { cy.LogintoApp(Cypress.env("USERNAME"), Cypress.env("PASSWORD")); cy.get(homePage.profileMenu).click(); cy.get(homePage.signOutIcon).click(); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(500); cy.get(homePage.headerAppSmithLogo).click(); + // eslint-disable-next-line cypress/no-unnecessary-waiting cy.wait(500); cy.url().should("include", "user/login"); }); diff --git a/app/client/cypress/locators/FormWidgets.json b/app/client/cypress/locators/FormWidgets.json index 673ae44d1a..84d613b30e 100644 --- a/app/client/cypress/locators/FormWidgets.json +++ b/app/client/cypress/locators/FormWidgets.json @@ -1,37 +1,36 @@ { - "checkboxWidget": ".t--draggable-checkboxwidget", - "dropdownWidget": ".t--draggable-dropdownwidget", - "dropdownSelectionType": ".t--property-control-selectiontype", - "radioWidget": ".t--draggable-radiogroupwidget", - "radioOnSelectionChangeDropdown": ".t--property-control-onselectionchange", - "nextDayBtn": ".DayPicker-Day[aria-selected='true'] + div.DayPicker-Day", - "datepickerWidget": ".t--draggable-datepickerwidget", - "defaultDate": ".t--property-control-defaultdate input", - "minDate": ".t--property-control-mindate input", - "maxDate": ".t--property-control-maxdate input", - "filepickerWidget": ".t--draggable-filepickerwidget", - "formWidget": ".t--draggable-formwidget", - "richTextEditorWidget": ".t--draggable-richtexteditorwidget", - "richEditorOnTextChange": ".t--property-control-ontextchange", - "optionvalue": ".t--property-control-defaultselectedvalue .kDwnRc", - "defselected": ".CodeMirror textarea", - "dropdowninner": ".bp3-button > .bp3-button-text", - "Textinput": ".t--property-control-options .CodeMirror-code", - "labelvalue": ".t--draggable-dropdownwidget label", - "dropdownInput": ".bp3-tag-input-values", - "labelradio": ".t--draggable-radiogroupwidget label", - "deleteradiovalue": ".t--property-control-options mask", - "inputRadio": ".t--draggable-radiogroupwidget input", - "defaultSelect": ".t--property-control-defaultselectedvalue .CodeMirror-code", - "formInner": ".t--draggable-formwidget span.t--widget-name", - "radioInput": ".t--draggable-radiogroupwidget span.t--widget-name", - "radioAddButton": ".t--property-control-options button", - "formD": "div[type='FORM_WIDGET']", - "datepickerFooter": ".bp3-datepicker-footer span", - "disableJs":".t--property-control-disable input[type='checkbox']", - "switchWidget": ".t--draggable-switchwidget", - "toggleJsDefaultDate": ".t--property-control-defaultdate .t--js-toggle", - "toggleJsMinDate": ".t--property-control-mindate .t--js-toggle", - "toggleJsMaxDate": ".t--property-control-maxdate .t--js-toggle" - - } + "checkboxWidget": ".t--draggable-checkboxwidget", + "dropdownWidget": ".t--draggable-dropdownwidget", + "dropdownSelectionType": ".t--property-control-selectiontype", + "radioWidget": ".t--draggable-radiogroupwidget", + "radioOnSelectionChangeDropdown": ".t--property-control-onselectionchange", + "nextDayBtn": ".DayPicker-Day[aria-selected='true'] + div.DayPicker-Day", + "datepickerWidget": ".t--draggable-datepickerwidget", + "defaultDate": ".t--property-control-defaultdate input", + "minDate": ".t--property-control-mindate input", + "maxDate": ".t--property-control-maxdate input", + "filepickerWidget": ".t--draggable-filepickerwidget", + "formWidget": ".t--draggable-formwidget", + "richTextEditorWidget": ".t--draggable-richtexteditorwidget", + "richEditorOnTextChange": ".t--property-control-ontextchange", + "optionvalue": ".t--property-control-defaultselectedvalue .kDwnRc", + "defselected": ".CodeMirror textarea", + "dropdowninner": ".bp3-button > .bp3-button-text", + "Textinput": ".t--property-control-options .CodeMirror-code", + "labelvalue": ".t--draggable-dropdownwidget label", + "dropdownInput": ".bp3-tag-input-values", + "labelradio": ".t--draggable-radiogroupwidget label", + "deleteradiovalue": ".t--property-control-options mask", + "inputRadio": ".t--draggable-radiogroupwidget input", + "defaultSelect": ".t--property-control-defaultselectedvalue .CodeMirror-code", + "formInner": ".t--draggable-formwidget span.t--widget-name", + "radioInput": ".t--draggable-radiogroupwidget span.t--widget-name", + "radioAddButton": ".t--property-control-options button", + "formD": "div[type='FORM_WIDGET']", + "datepickerFooter": ".bp3-datepicker-footer span", + "disableJs": ".t--property-control-disable input[type='checkbox']", + "switchWidget": ".t--draggable-switchwidget", + "toggleJsDefaultDate": ".t--property-control-defaultdate .t--js-toggle", + "toggleJsMinDate": ".t--property-control-mindate .t--js-toggle", + "toggleJsMaxDate": ".t--property-control-maxdate .t--js-toggle" +} diff --git a/app/client/cypress/support/commands.js b/app/client/cypress/support/commands.js index 3bc45ff9f0..3b193b59ba 100644 --- a/app/client/cypress/support/commands.js +++ b/app/client/cypress/support/commands.js @@ -690,6 +690,37 @@ Cypress.Commands.add("switchToAPIInputTab", () => { .click({ force: true }); }); +Cypress.Commands.add("selectDateFormat", (value) => { + cy.get(".t--property-control-dateformat button") + .first() + .click({ force: true }); + cy.get("ul.bp3-menu") + .children() + .contains(value) + .click(); +}); + +Cypress.Commands.add("assertDateFormat", () => { + cy.get(".t--draggable-datepickerwidget2 input") + .first() + .invoke("attr", "value") + .then((text) => { + const firstTxt = text; + cy.log("date time : ", firstTxt); + cy.get(commonlocators.labelTextStyle) + .first() + .should("contain", firstTxt); + cy.get(commonlocators.labelTextStyle) + .last() + .invoke("text") + .then((text) => { + const secondText = text; + cy.log("date time : ", secondText); + expect(firstTxt).not.to.equal(secondText); + }); + }); +}); + Cypress.Commands.add("selectPaginationType", (option) => { cy.xpath(option).click({ force: true }); }); diff --git a/app/client/src/components/designSystems/appsmith/TableComponent/CascadeFields.tsx b/app/client/src/components/designSystems/appsmith/TableComponent/CascadeFields.tsx index 755573d9bc..5770579ccc 100644 --- a/app/client/src/components/designSystems/appsmith/TableComponent/CascadeFields.tsx +++ b/app/client/src/components/designSystems/appsmith/TableComponent/CascadeFields.tsx @@ -8,7 +8,7 @@ import { ControlIcons } from "icons/ControlIcons"; import { AnyStyledComponent } from "styled-components"; import { Skin } from "constants/DefaultTheme"; import AutoToolTipComponent from "components/designSystems/appsmith/TableComponent/AutoToolTipComponent"; -import DatePickerComponent from "components/designSystems/blueprint/DatePickerComponent"; +import DatePickerComponent from "components/designSystems/blueprint/DatePickerComponent2"; import { OperatorTypes, Condition, diff --git a/app/client/src/components/designSystems/blueprint/DatePickerComponent.tsx b/app/client/src/components/designSystems/blueprint/DatePickerComponent.tsx index 4d1e95c631..e0fac3e121 100644 --- a/app/client/src/components/designSystems/blueprint/DatePickerComponent.tsx +++ b/app/client/src/components/designSystems/blueprint/DatePickerComponent.tsx @@ -1,6 +1,10 @@ import React from "react"; import styled from "styled-components"; -import { getBorderCSSShorthand, labelStyle } from "constants/DefaultTheme"; +import { + getBorderCSSShorthand, + labelStyle, + IntentColors, +} from "constants/DefaultTheme"; import { ControlGroup, Classes, Label } from "@blueprintjs/core"; import { ComponentProps } from "components/designSystems/appsmith/BaseComponent"; import { DateInput } from "@blueprintjs/datetime"; @@ -11,24 +15,30 @@ import { WIDGET_PADDING } from "constants/WidgetConstants"; import { TimePrecision } from "@blueprintjs/datetime"; import { Colors } from "constants/Colors"; import { ISO_DATE_FORMAT } from "constants/WidgetValidation"; +import ErrorTooltip from "components/editorComponents/ErrorTooltip"; +import { DATE_WIDGET_DEFAULT_VALIDATION_ERROR } from "constants/messages"; -const StyledControlGroup = styled(ControlGroup)` +const StyledControlGroup = styled(ControlGroup)<{ isValid: boolean }>` &&& { .${Classes.INPUT} { box-shadow: none; color: ${Colors.OXFORD_BLUE}; font-size: ${(props) => props.theme.fontSizes[3]}px; border: ${(props) => getBorderCSSShorthand(props.theme.borders[2])}; + border-color: ${(props) => + !props.isValid ? IntentColors.danger : Colors.GEYSER_LIGHT}; border-radius: 0; width: 100%; height: inherit; align-items: center; &:active { - border-color: ${Colors.HIT_GRAY}; + border-color: ${(props) => + !props.isValid ? IntentColors.danger : Colors.HIT_GRAY}; } &:focus { border: ${(props) => getBorderCSSShorthand(props.theme.borders[2])}; - border-color: #80bdff; + border-color: ${(props) => + !props.isValid ? IntentColors.danger : "#80bdff"}; outline: 0; box-shadow: 0 0 0 0.1rem rgba(0, 123, 255, 0.25); } @@ -95,10 +105,17 @@ class DatePickerComponent extends React.Component< .clone() .set({ month: 11, date: 31, year: year + 20 }) .toDate(); - + const isValid = this.state.selectedDate + ? this.isValidDate(this.parseDate(this.state.selectedDate)) + : true; + const value = + isValid && this.state.selectedDate + ? this.parseDate(this.state.selectedDate) + : null; return ( { e.stopPropagation(); }} @@ -115,29 +132,58 @@ class DatePickerComponent extends React.Component< )} { - + + + } ); } + isValidDate = (date: Date): boolean => { + let isValid = true; + const dateFormat = this.props.dateFormat || ISO_DATE_FORMAT; + const parsedCurrentDate = moment(date); + if (this.props.minDate) { + const parsedMinDate = moment(this.props.minDate, dateFormat); + if ( + this.props.minDate && + parsedMinDate.isValid() && + parsedCurrentDate.isBefore(parsedMinDate) + ) { + isValid = false; + } + } + if (this.props.maxDate) { + const parsedMaxDate = moment(this.props.maxDate, dateFormat); + if ( + isValid && + this.props.maxDate && + parsedMaxDate.isValid() && + parsedCurrentDate.isAfter(parsedMaxDate) + ) { + isValid = false; + } + } + return isValid; + }; + formatDate = (date: Date): string => { const dateFormat = this.props.dateFormat || ISO_DATE_FORMAT; return moment(date).format(dateFormat); @@ -165,9 +211,6 @@ class DatePickerComponent extends React.Component< const date = selectedDate ? this.formatDate(selectedDate) : ""; this.setState({ selectedDate: date }); - // if date is null ( if date is cleared ), don't call onDateSelected - if (!selectedDate) return false; - onDateSelected(date); } }; diff --git a/app/client/src/components/designSystems/blueprint/DatePickerComponent2.tsx b/app/client/src/components/designSystems/blueprint/DatePickerComponent2.tsx new file mode 100644 index 0000000000..a3d2cb7178 --- /dev/null +++ b/app/client/src/components/designSystems/blueprint/DatePickerComponent2.tsx @@ -0,0 +1,236 @@ +import React from "react"; +import styled from "styled-components"; +import { labelStyle, IntentColors } from "constants/DefaultTheme"; +import { ControlGroup, Classes, Label } from "@blueprintjs/core"; +import { ComponentProps } from "components/designSystems/appsmith/BaseComponent"; +import { DateInput } from "@blueprintjs/datetime"; +import moment from "moment-timezone"; +import "../../../../node_modules/@blueprintjs/datetime/lib/css/blueprint-datetime.css"; +import { DatePickerType } from "widgets/DatePickerWidget"; +import { WIDGET_PADDING } from "constants/WidgetConstants"; +import { TimePrecision } from "@blueprintjs/datetime"; +import { Colors } from "constants/Colors"; +import { ISO_DATE_FORMAT } from "constants/WidgetValidation"; +import ErrorTooltip from "components/editorComponents/ErrorTooltip"; +import { DATE_WIDGET_DEFAULT_VALIDATION_ERROR } from "constants/messages"; + +const StyledControlGroup = styled(ControlGroup)<{ isValid: boolean }>` + &&& { + .${Classes.INPUT} { + box-shadow: none; + border: 1px solid; + border-color: ${(props) => + !props.isValid ? IntentColors.danger : Colors.GEYSER_LIGHT}; + border-radius: ${(props) => props.theme.radii[1]}px; + width: 100%; + height: inherit; + align-items: center; + &:active { + border-color: ${(props) => + !props.isValid ? IntentColors.danger : Colors.HIT_GRAY}; + } + &:focus { + border-color: ${(props) => + !props.isValid ? IntentColors.danger : Colors.MYSTIC}; + } + } + .${Classes.INPUT_GROUP} { + display: block; + margin: 0; + } + .${Classes.CONTROL_GROUP} { + justify-content: flex-start; + } + label { + ${labelStyle} + flex: 0 1 30%; + margin: 7px ${WIDGET_PADDING * 2}px 0 0; + text-align: right; + align-self: flex-start; + max-width: calc(30% - ${WIDGET_PADDING}px); + } + } + &&& { + input { + border: 1px solid; + border-color: ${(props) => + !props.isValid ? IntentColors.danger : Colors.HIT_GRAY}; + border-radius: ${(props) => props.theme.radii[1]}px; + box-shadow: none; + color: ${Colors.OXFORD_BLUE}; + font-size: ${(props) => props.theme.fontSizes[3]}px; + } + } +`; + +class DatePickerComponent extends React.Component< + DatePickerComponentProps, + DatePickerComponentState +> { + constructor(props: DatePickerComponentProps) { + super(props); + this.state = { + selectedDate: props.selectedDate, + }; + } + + componentDidUpdate(prevProps: DatePickerComponentProps) { + if ( + this.props.selectedDate !== this.state.selectedDate && + !moment(this.props.selectedDate).isSame( + moment(prevProps.selectedDate), + "seconds", + ) + ) { + this.setState({ selectedDate: this.props.selectedDate }); + } + } + + getValidDate = (date: string, format: string) => { + const _date = moment(date, format); + return _date.isValid() ? _date.toDate() : undefined; + }; + + render() { + const now = moment(); + const year = now.get("year"); + const minDate = this.props.minDate + ? new Date(this.props.minDate) + : now + .clone() + .set({ month: 0, date: 1, year: year - 100 }) + .toDate(); + const maxDate = this.props.maxDate + ? new Date(this.props.maxDate) + : now + .clone() + .set({ month: 11, date: 31, year: year + 20 }) + .toDate(); + const isValid = this.state.selectedDate + ? this.isValidDate(new Date(this.state.selectedDate)) + : true; + const value = + isValid && this.state.selectedDate + ? new Date(this.state.selectedDate) + : null; + return ( + { + e.stopPropagation(); + }} + > + {this.props.label && ( + + )} + { + + + + } + + ); + } + + isValidDate = (date: Date): boolean => { + let isValid = true; + const parsedCurrentDate = moment(date); + if (this.props.minDate) { + const parsedMinDate = moment(this.props.minDate); + if ( + this.props.minDate && + parsedMinDate.isValid() && + parsedCurrentDate.isBefore(parsedMinDate) + ) { + isValid = false; + } + } + if (this.props.maxDate) { + const parsedMaxDate = moment(this.props.maxDate); + if ( + isValid && + this.props.maxDate && + parsedMaxDate.isValid() && + parsedCurrentDate.isAfter(parsedMaxDate) + ) { + isValid = false; + } + } + return isValid; + }; + + formatDate = (date: Date): string => { + const dateFormat = this.props.dateFormat || ISO_DATE_FORMAT; + return moment(date).format(dateFormat); + }; + + parseDate = (dateStr: string): Date => { + const date = moment(dateStr); + + if (date.isValid()) return moment(dateStr).toDate(); + else return moment().toDate(); + }; + + /** + * checks if selelectedDate is null or not, + * sets state and calls props onDateSelected + * if its null, don't call onDateSelected + * + * @param selectedDate + */ + onDateSelected = (selectedDate: Date, isUserChange: boolean) => { + if (isUserChange) { + const { onDateSelected } = this.props; + + const date = selectedDate ? selectedDate.toISOString() : ""; + this.setState({ selectedDate: date }); + + onDateSelected(date); + } + }; +} + +interface DatePickerComponentProps extends ComponentProps { + label: string; + dateFormat: string; + enableTimePicker?: boolean; + selectedDate?: string; + minDate?: string; + maxDate?: string; + timezone?: string; + datePickerType: DatePickerType; + isDisabled: boolean; + onDateSelected: (selectedDate: string) => void; + isLoading: boolean; +} + +interface DatePickerComponentState { + selectedDate?: string; +} + +export default DatePickerComponent; diff --git a/app/client/src/components/propertyControls/DatePickerControl.tsx b/app/client/src/components/propertyControls/DatePickerControl.tsx index 6796c3e736..259417ffa6 100644 --- a/app/client/src/components/propertyControls/DatePickerControl.tsx +++ b/app/client/src/components/propertyControls/DatePickerControl.tsx @@ -5,8 +5,6 @@ import moment from "moment-timezone"; import styled from "styled-components"; import { TimePrecision } from "@blueprintjs/datetime"; import { WidgetProps } from "widgets/BaseWidget"; -import { Toaster } from "components/ads/Toast"; -import { Variant } from "components/ads/common"; import { ISO_DATE_FORMAT } from "constants/WidgetValidation"; const DatePickerControlWrapper = styled.div<{ isValid: boolean }>` @@ -63,18 +61,22 @@ class DatePickerControl extends BaseControl< } render() { + const version = this.props.widgetProperties.version; const dateFormat = - this.props.widgetProperties.dateFormat || ISO_DATE_FORMAT; + version === 2 + ? ISO_DATE_FORMAT + : this.props.widgetProperties.dateFormat || ISO_DATE_FORMAT; const isValid = this.state.selectedDate ? this.validateDate(moment(this.state.selectedDate, dateFormat).toDate()) : true; - const maxDate = - this.props.widgetProperties?.evaluatedValues?.maxDate ?? this.maxDate; - const minDate = - this.props.widgetProperties?.evaluatedValues?.minDate ?? this.minDate; - + const value = + this.props.propertyValue && isValid + ? version === 2 + ? new Date(this.props.propertyValue) + : this.parseDate(this.props.propertyValue) + : null; return ( - + ); @@ -117,10 +105,13 @@ class DatePickerControl extends BaseControl< */ onDateSelected = (date: Date, isUserChange: boolean): void => { if (isUserChange) { - const selectedDate = date ? this.formatDate(date) : undefined; + const selectedDate = date + ? this.props.widgetProperties.version === 2 + ? date.toISOString() + : this.formatDate(date) + : undefined; const isValid = this.validateDate(date); if (!isValid) return; - // if everything is ok, put date in state this.setState({ selectedDate: selectedDate }); this.updateProperty(this.props.propertyName, selectedDate); @@ -128,73 +119,14 @@ class DatePickerControl extends BaseControl< }; /** - * checks: - * 1. if max date is greater than the default date - * 2. if default date is in range of min and max date + * checks if date is of valid date format */ validateDate = (date: Date): boolean => { const dateFormat = - this.props.widgetProperties.dateFormat || ISO_DATE_FORMAT; - const parsedSelectedDate = moment(date, dateFormat); - //validate defaultDate if both minDate and maxDate is already selected - if (this.props.propertyName === "defaultDate") { - if ( - parsedSelectedDate.isValid() && - this.props.widgetProperties?.evaluatedValues?.minDate && - this.props.widgetProperties?.evaluatedValues?.maxDate - ) { - const parsedMinDate = moment( - this.props.widgetProperties.evaluatedValues.minDate, - dateFormat, - ); - const parsedMaxDate = moment( - this.props.widgetProperties.evaluatedValues.maxDate, - dateFormat, - ); - if ( - parsedSelectedDate.isBefore(parsedMinDate) || - parsedSelectedDate.isAfter(parsedMaxDate) - ) { - return false; - } - } - } - if (this.props.widgetProperties?.evaluatedValues?.value) { - const parsedWidgetDate = moment( - this.props.widgetProperties.evaluatedValues.value, - dateFormat, - ); - // checking if widget date is after min date - if (this.props.propertyName === "minDate") { - if ( - parsedSelectedDate.isValid() && - parsedWidgetDate.isBefore(parsedSelectedDate) - ) { - Toaster.show({ - text: "Min date cannot be greater than current widget value.", - variant: Variant.danger, - }); - - return false; - } - } - - // checking if widget date is before max date - if (this.props.propertyName === "maxDate") { - if ( - parsedSelectedDate.isValid() && - parsedWidgetDate.isAfter(parsedSelectedDate) - ) { - Toaster.show({ - text: "Max date cannot be less than current widget value.", - variant: Variant.danger, - }); - - return false; - } - } - } - return true; + this.props.widgetProperties.version === 2 + ? ISO_DATE_FORMAT + : this.props.widgetProperties.dateFormat || ISO_DATE_FORMAT; + return date ? moment(date, dateFormat).isValid() : true; }; formatDate = (date: Date): string => { @@ -205,7 +137,9 @@ class DatePickerControl extends BaseControl< parseDate = (dateStr: string): Date => { const dateFormat = - this.props.widgetProperties.dateFormat || ISO_DATE_FORMAT; + this.props.widgetProperties.version === 2 + ? ISO_DATE_FORMAT + : this.props.widgetProperties.dateFormat || ISO_DATE_FORMAT; const date = moment(dateStr, dateFormat); if (date.isValid()) return moment(dateStr, dateFormat).toDate(); diff --git a/app/client/src/constants/FieldExpectedValue.ts b/app/client/src/constants/FieldExpectedValue.ts index 99580f40c3..eed49ad87e 100644 --- a/app/client/src/constants/FieldExpectedValue.ts +++ b/app/client/src/constants/FieldExpectedValue.ts @@ -24,6 +24,13 @@ const FIELD_VALUES: Record< isDisabled: "boolean", // onDateSelected: "Function Call", }, + DATE_PICKER_WIDGET2: { + defaultDate: "string", //TODO:Vicky validate this property + isRequired: "boolean", + isVisible: "boolean", + isDisabled: "boolean", + // onDateSelected: "Function Call", + }, TABLE_WIDGET: { tableData: "Array", serverSidePaginationEnabled: "boolean", diff --git a/app/client/src/constants/HelpConstants.ts b/app/client/src/constants/HelpConstants.ts index 6e1020b27f..43fe688253 100644 --- a/app/client/src/constants/HelpConstants.ts +++ b/app/client/src/constants/HelpConstants.ts @@ -27,6 +27,10 @@ export const HelpMap = { path: "/widget-reference/datepicker", searchKey: "DatePicker", }, + DATE_PICKER_WIDGET2: { + path: "/widget-reference/datepicker", + searchKey: "DatePicker", + }, TABLE_WIDGET: { path: "/widget-reference/table", searchKey: "Table", diff --git a/app/client/src/constants/WidgetConstants.tsx b/app/client/src/constants/WidgetConstants.tsx index 40f8e4b1d9..5c3123eddf 100644 --- a/app/client/src/constants/WidgetConstants.tsx +++ b/app/client/src/constants/WidgetConstants.tsx @@ -5,6 +5,7 @@ export enum WidgetTypes { INPUT_WIDGET = "INPUT_WIDGET", CONTAINER_WIDGET = "CONTAINER_WIDGET", DATE_PICKER_WIDGET = "DATE_PICKER_WIDGET", + DATE_PICKER_WIDGET2 = "DATE_PICKER_WIDGET2", TABLE_WIDGET = "TABLE_WIDGET", DROP_DOWN_WIDGET = "DROP_DOWN_WIDGET", CHECKBOX_WIDGET = "CHECKBOX_WIDGET", diff --git a/app/client/src/constants/WidgetValidation.ts b/app/client/src/constants/WidgetValidation.ts index 1b5a26bc16..d22315b7dc 100644 --- a/app/client/src/constants/WidgetValidation.ts +++ b/app/client/src/constants/WidgetValidation.ts @@ -42,7 +42,7 @@ export type Validator = ( dataTree?: DataTree, ) => ValidationResponse; -export const ISO_DATE_FORMAT = "YYYY-MM-DDTHH:mm:ss.Z"; +export const ISO_DATE_FORMAT = "YYYY-MM-DDTHH:mm:ss.sssZ"; export const JAVASCRIPT_KEYWORDS = { true: "true", diff --git a/app/client/src/constants/messages.ts b/app/client/src/constants/messages.ts index 9ca3f8cf96..5f95ddc1fb 100644 --- a/app/client/src/constants/messages.ts +++ b/app/client/src/constants/messages.ts @@ -138,6 +138,8 @@ export const FORGOT_PASSWORD_PAGE_LOGIN_LINK = "Back to Sign In"; export const ADD_API_TO_PAGE_SUCCESS_MESSAGE = "Api added to page."; export const INPUT_WIDGET_DEFAULT_VALIDATION_ERROR = "Invalid input"; +export const DATE_WIDGET_DEFAULT_VALIDATION_ERROR = "Date out of range"; + export const AUTOFIT_ALL_COLUMNS = "Autofit all columns"; export const AUTOFIT_THIS_COLUMN = "Autofit this column"; export const AUTOFIT_COLUMN = "Autofit column"; diff --git a/app/client/src/entities/Widget/utils.test.ts b/app/client/src/entities/Widget/utils.test.ts index 8e89f01311..d7402546db 100644 --- a/app/client/src/entities/Widget/utils.test.ts +++ b/app/client/src/entities/Widget/utils.test.ts @@ -29,6 +29,7 @@ describe("getAllPathsFromPropertyConfig", () => { isLoading: false, horizontalAlignment: "LEFT", parentColumnSpace: 74, + version: 1, dynamicTriggerPathList: [ { key: "primaryColumns.status.onClick", diff --git a/app/client/src/icons/WidgetIcons.tsx b/app/client/src/icons/WidgetIcons.tsx index 7eaa30f489..c95ff4fdaa 100644 --- a/app/client/src/icons/WidgetIcons.tsx +++ b/app/client/src/icons/WidgetIcons.tsx @@ -51,7 +51,7 @@ export const WidgetIcons: { ), - DATE_PICKER_WIDGET: (props: IconProps) => ( + DATE_PICKER_WIDGET2: (props: IconProps) => ( diff --git a/app/client/src/mockResponses/WidgetConfigResponse.tsx b/app/client/src/mockResponses/WidgetConfigResponse.tsx index f2d841b45b..1e88df7137 100644 --- a/app/client/src/mockResponses/WidgetConfigResponse.tsx +++ b/app/client/src/mockResponses/WidgetConfigResponse.tsx @@ -14,6 +14,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { isDisabled: false, isVisible: true, isDefaultClickDisabled: true, + version: 1, }, TEXT_WIDGET: { text: "Label", @@ -22,6 +23,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { rows: 1, columns: 4, widgetName: "Text", + version: 1, }, RICH_TEXT_EDITOR_WIDGET: { defaultText: "This is the initial content of the editor", @@ -31,6 +33,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { isVisible: true, widgetName: "RichTextEditor", isDefaultClickDisabled: true, + version: 1, }, IMAGE_WIDGET: { defaultImage: @@ -41,6 +44,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { rows: 3, columns: 4, widgetName: "Image", + version: 1, }, INPUT_WIDGET: { inputType: "TEXT", @@ -48,6 +52,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { label: "", columns: 5, widgetName: "Input", + version: 1, resetOnSubmit: true, }, SWITCH_WIDGET: { @@ -57,11 +62,13 @@ const WidgetConfigResponse: WidgetConfigReducerState = { defaultSwitchState: true, widgetName: "Switch", alignWidget: "LEFT", + version: 1, }, ICON_WIDGET: { widgetName: "Icon", rows: 1, columns: 1, + version: 1, }, CONTAINER_WIDGET: { backgroundColor: "#FFFFFF", @@ -84,6 +91,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { }, ], }, + version: 1, }, DATE_PICKER_WIDGET: { isDisabled: false, @@ -94,14 +102,26 @@ const WidgetConfigResponse: WidgetConfigReducerState = { columns: 5, widgetName: "DatePicker", defaultDate: moment().format("DD/MM/YYYY HH:mm"), + version: 1, + }, + DATE_PICKER_WIDGET2: { + isDisabled: false, + datePickerType: "DATE_PICKER", + rows: 1, + label: "", + dateFormat: "DD/MM/YYYY HH:mm", + columns: 5, + widgetName: "DatePicker", + defaultDate: moment().toISOString(), + version: 2, }, - VIDEO_WIDGET: { rows: 7, columns: 7, widgetName: "Video", url: "https://www.youtube.com/watch?v=mzqK0QIZRLs", autoPlay: false, + version: 1, }, TABLE_WIDGET: { rows: 7, @@ -137,6 +157,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { orderAmount: 19.99, }, ], + version: 1, }, DROP_DOWN_WIDGET: { rows: 1, @@ -150,6 +171,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { ], widgetName: "Dropdown", defaultOptionValue: "VEG", + version: 1, }, CHECKBOX_WIDGET: { rows: 1, @@ -157,6 +179,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { label: "Label", defaultCheckedState: true, widgetName: "Checkbox", + version: 1, alignWidget: "LEFT", }, RADIO_GROUP_WIDGET: { @@ -169,6 +192,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { ], defaultOptionValue: "M", widgetName: "RadioGroup", + version: 1, }, ALERT_WIDGET: { alertType: "NOTIFICATION", @@ -178,6 +202,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { header: "", message: "", widgetName: "Alert", + version: 1, }, FILE_PICKER_WIDGET: { rows: 1, @@ -188,6 +213,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { maxFileSize: 5, widgetName: "FilePicker", isDefaultClickDisabled: true, + version: 1, }, TABS_WIDGET: { rows: 7, @@ -224,6 +250,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { }, ], }, + version: 1, }, MODAL_WIDGET: { rows: 6, @@ -235,6 +262,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { shouldScrollContents: true, widgetName: "Modal", children: [], + version: 1, blueprint: { view: [ { @@ -247,6 +275,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { isDisabled: false, shouldScrollContents: false, children: [], + version: 1, blueprint: { view: [ { @@ -257,6 +286,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { iconName: "cross", iconSize: 24, color: "#040627", + version: 1, }, }, { @@ -266,6 +296,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { props: { text: "Modal Title", textStyle: "HEADING", + version: 1, }, }, { @@ -275,6 +306,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { props: { text: "Cancel", buttonStyle: "SECONDARY_BUTTON", + version: 1, }, }, { @@ -284,6 +316,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { props: { text: "Confirm", buttonStyle: "PRIMARY_BUTTON", + version: 1, }, }, ], @@ -322,6 +355,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { rows: 0, columns: 0, widgetName: "Canvas", + version: 1, }, CHART_WIDGET: { rows: 8, @@ -330,6 +364,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { chartType: "LINE_CHART", chartName: "Sales on working days", allowHorizontalScroll: false, + version: 1, chartData: [ { seriesName: "Sales", @@ -374,6 +409,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { widgetName: "FormButton", text: "Submit", isDefaultClickDisabled: true, + version: 1, }, FORM_WIDGET: { rows: 13, @@ -391,6 +427,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { canExtend: false, detachFromLayout: true, children: [], + version: 1, blueprint: { view: [ { @@ -400,6 +437,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { props: { text: "Form", textStyle: "HEADING", + version: 1, }, }, { @@ -411,6 +449,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { buttonStyle: "PRIMARY_BUTTON", disabledWhenInvalid: true, resetFormOnClick: true, + version: 1, }, }, { @@ -422,6 +461,7 @@ const WidgetConfigResponse: WidgetConfigReducerState = { buttonStyle: "SECONDARY_BUTTON", disabledWhenInvalid: false, resetFormOnClick: true, + version: 1, }, }, ], @@ -443,12 +483,14 @@ const WidgetConfigResponse: WidgetConfigReducerState = { allowZoom: true, mapCenter: { lat: -34.397, long: 150.644 }, defaultMarkers: [{ lat: -34.397, long: 150.644, title: "Test A" }], + version: 1, }, SKELETON_WIDGET: { isLoading: true, rows: 1, columns: 1, widgetName: "Skeleton", + version: 1, }, }, configVersion: 1, diff --git a/app/client/src/mockResponses/WidgetSidebarResponse.tsx b/app/client/src/mockResponses/WidgetSidebarResponse.tsx index dc8e13878f..205bb38231 100644 --- a/app/client/src/mockResponses/WidgetSidebarResponse.tsx +++ b/app/client/src/mockResponses/WidgetSidebarResponse.tsx @@ -29,7 +29,7 @@ const WidgetSidebarResponse: WidgetCardProps[] = [ key: generateReactKey(), }, { - type: "DATE_PICKER_WIDGET", + type: "DATE_PICKER_WIDGET2", widgetCardName: "DatePicker", key: generateReactKey(), }, diff --git a/app/client/src/reducers/entityReducers/widgetConfigReducer.tsx b/app/client/src/reducers/entityReducers/widgetConfigReducer.tsx index 4a8d29f747..6b327b2db9 100644 --- a/app/client/src/reducers/entityReducers/widgetConfigReducer.tsx +++ b/app/client/src/reducers/entityReducers/widgetConfigReducer.tsx @@ -9,6 +9,7 @@ import { ImageWidgetProps } from "widgets/ImageWidget"; import { InputWidgetProps } from "widgets/InputWidget"; import { RichTextEditorWidgetProps } from "widgets/RichTextEditorWidget"; import { DatePickerWidgetProps } from "../../widgets/DatePickerWidget"; +import { DatePickerWidget2Props } from "../../widgets/DatePickerWidget2"; import { TableWidgetProps } from "../../widgets/TableWidget/TableWidgetConstants"; import { DropdownWidgetProps } from "../../widgets/DropdownWidget"; import { CheckboxWidgetProps } from "../../widgets/CheckboxWidget"; @@ -59,6 +60,7 @@ export interface WidgetConfigReducerState { CONTAINER_WIDGET: Partial> & WidgetConfigProps; DATE_PICKER_WIDGET: Partial & WidgetConfigProps; + DATE_PICKER_WIDGET2: Partial & WidgetConfigProps; TABLE_WIDGET: Partial & WidgetConfigProps; VIDEO_WIDGET: Partial & WidgetConfigProps; DROP_DOWN_WIDGET: Partial & WidgetConfigProps; diff --git a/app/client/src/sagas/WidgetOperationSagas.tsx b/app/client/src/sagas/WidgetOperationSagas.tsx index 333bc1b9e9..e99f01529f 100644 --- a/app/client/src/sagas/WidgetOperationSagas.tsx +++ b/app/client/src/sagas/WidgetOperationSagas.tsx @@ -143,6 +143,7 @@ function* getChildWidgetProps( parentColumnSpace, widgetName, widgetProps, + restDefaultConfig.version, ); widget.widgetId = newWidgetId; @@ -1411,6 +1412,7 @@ function* addTableWidgetFromQuerySaga(action: ReduxAction) { parentRowSpace: GridDefaults.DEFAULT_GRID_ROW_HEIGHT, parentColumnSpace: 1, isLoading: false, + version: 1, props: { tableData: `{{${queryName}.data}}`, dynamicBindingPathList: [{ key: "tableData" }], diff --git a/app/client/src/utils/WidgetPropsUtils.tsx b/app/client/src/utils/WidgetPropsUtils.tsx index b490956241..f2a28a3717 100644 --- a/app/client/src/utils/WidgetPropsUtils.tsx +++ b/app/client/src/utils/WidgetPropsUtils.tsx @@ -259,6 +259,18 @@ const dynamicPathListMigration = ( return currentDSL; }; +const addVersionNumberMigration = ( + currentDSL: ContainerWidgetProps, +) => { + if (currentDSL.children && currentDSL.children.length) { + currentDSL.children = currentDSL.children.map(addVersionNumberMigration); + } + if (currentDSL.version === undefined) { + currentDSL.version = 1; + } + return currentDSL; +}; + const canvasNameConflictMigration = ( currentDSL: ContainerWidgetProps, props = { counter: 1 }, @@ -367,6 +379,11 @@ const transformDSL = (currentDSL: ContainerWidgetProps) => { currentDSL.version = 10; } + if (currentDSL.version === 10) { + currentDSL = addVersionNumberMigration(currentDSL); + currentDSL.version = 11; + } + return currentDSL; }; @@ -590,6 +607,7 @@ export const generateWidgetProps = ( widgetId: string; renderMode: RenderMode; } & Partial, + version: number, ): ContainerWidgetProps => { if (parent) { const sizes = { @@ -611,6 +629,7 @@ export const generateWidgetProps = ( ...sizes, ...others, parentId: parent.widgetId, + version, }; delete props.rows; delete props.columns; diff --git a/app/client/src/utils/WidgetRegistry.tsx b/app/client/src/utils/WidgetRegistry.tsx index 2fced6e1e0..5e09d10a2a 100644 --- a/app/client/src/utils/WidgetRegistry.tsx +++ b/app/client/src/utils/WidgetRegistry.tsx @@ -71,6 +71,10 @@ import DatePickerWidget, { DatePickerWidgetProps, ProfiledDatePickerWidget, } from "widgets/DatePickerWidget"; +import DatePickerWidget2, { + DatePickerWidget2Props, + ProfiledDatePickerWidget2, +} from "widgets/DatePickerWidget2"; import FormWidget, { ProfiledFormWidget } from "widgets/FormWidget"; import FormButtonWidget, { FormButtonWidgetProps, @@ -286,6 +290,20 @@ export default class WidgetBuilderRegistry { DatePickerWidget.getMetaPropertiesMap(), DatePickerWidget.getPropertyPaneConfig(), ); + WidgetFactory.registerWidgetBuilder( + "DATE_PICKER_WIDGET2", + { + buildWidget(widgetData: DatePickerWidget2Props): JSX.Element { + return ; + }, + }, + DatePickerWidget2.getPropertyValidationMap(), + DatePickerWidget2.getDerivedPropertiesMap(), + DatePickerWidget2.getTriggerPropertyMap(), + DatePickerWidget2.getDefaultPropertiesMap(), + DatePickerWidget2.getMetaPropertiesMap(), + DatePickerWidget.getPropertyPaneConfig(), + ); WidgetFactory.registerWidgetBuilder( "TABS_WIDGET", { diff --git a/app/client/src/utils/autocomplete/EntityDefinitions.ts b/app/client/src/utils/autocomplete/EntityDefinitions.ts index 0dd1e5dc84..cb25204611 100644 --- a/app/client/src/utils/autocomplete/EntityDefinitions.ts +++ b/app/client/src/utils/autocomplete/EntityDefinitions.ts @@ -124,6 +124,15 @@ export const entityDefinitions = { selectedDate: "string", isDisabled: "bool", }, + DATE_PICKER_WIDGET2: { + "!doc": + "Datepicker is used to capture the date and time from a user. It can be used to filter data base on the input date range as well as to capture personal information such as date of birth", + "!url": "https://docs.appsmith.com/widget-reference/datepicker", + isVisible: isVisible, + selectedDate: "string", + formattedDate: "string", + isDisabled: "bool", + }, CHECKBOX_WIDGET: { "!doc": "Checkbox is a simple UI widget you can use when you want users to make a binary choice", diff --git a/app/client/src/utils/autocomplete/dataTreeTypeDefCreator.test.ts b/app/client/src/utils/autocomplete/dataTreeTypeDefCreator.test.ts index ea0eda774a..853bb76e67 100644 --- a/app/client/src/utils/autocomplete/dataTreeTypeDefCreator.test.ts +++ b/app/client/src/utils/autocomplete/dataTreeTypeDefCreator.test.ts @@ -24,6 +24,7 @@ describe("dataTreeTypeDefCreator", () => { topRow: 1, bottomRow: 2, isLoading: false, + version: 1, bindingPaths: { defaultText: true, }, diff --git a/app/client/src/utils/migrations/TableWidget.test.ts b/app/client/src/utils/migrations/TableWidget.test.ts index ed4d0524c3..57291b80a7 100644 --- a/app/client/src/utils/migrations/TableWidget.test.ts +++ b/app/client/src/utils/migrations/TableWidget.test.ts @@ -44,6 +44,7 @@ const input1: ContainerWidgetProps = { widgetId: "fs785w9gcy", dynamicBindingPathList: [], renderMode: "CANVAS", + version: 1, }, ], }; @@ -105,6 +106,7 @@ const input2: ContainerWidgetProps = { }, ], renderMode: "CANVAS", + version: 1, }, ], }; @@ -157,6 +159,7 @@ const input3: ContainerWidgetProps = { onRowSelected: "{{showAlert('test','success')}}", onSearchTextChanged: "{{showAlert('fail','error')}}", renderMode: "CANVAS", + version: 1, }, ], }; @@ -293,6 +296,7 @@ const output1 = { horizontalAlignment: "LEFT", verticalAlignment: "CENTER", renderMode: "CANVAS", + version: 1, }, ], }; @@ -474,6 +478,7 @@ const output2 = { horizontalAlignment: "LEFT", verticalAlignment: "CENTER", renderMode: "CANVAS", + version: 1, }, ], }; @@ -616,6 +621,7 @@ const output3 = { horizontalAlignment: "LEFT", verticalAlignment: "CENTER", renderMode: "CANVAS", + version: 1, }, ], }; diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index e497898ad0..2ae2da6702 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -319,6 +319,7 @@ export interface WidgetBaseProps { widgetName: string; parentId: string; renderMode: RenderMode; + version: number; } export type WidgetRowCols = { diff --git a/app/client/src/widgets/DatePickerWidget2.tsx b/app/client/src/widgets/DatePickerWidget2.tsx new file mode 100644 index 0000000000..3ba8f2fe6d --- /dev/null +++ b/app/client/src/widgets/DatePickerWidget2.tsx @@ -0,0 +1,223 @@ +import React from "react"; +import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget"; +import { WidgetType } from "constants/WidgetConstants"; +import { EventType } from "constants/ActionConstants"; +import DatePickerComponent from "components/designSystems/blueprint/DatePickerComponent2"; +import { + WidgetPropertyValidationType, + BASE_WIDGET_VALIDATION, +} from "utils/WidgetValidation"; +import { VALIDATION_TYPES } from "constants/WidgetValidation"; +import { + DerivedPropertiesMap, + TriggerPropertiesMap, +} from "utils/WidgetFactory"; +import * as Sentry from "@sentry/react"; +import withMeta, { WithMeta } from "./MetaHOC"; + +class DatePickerWidget extends BaseWidget { + static getPropertyPaneConfig() { + return [ + { + sectionName: "General", + children: [ + { + propertyName: "defaultDate", + label: "Default Date", + helpText: + "Sets the default date of the widget. The date is updated if the default date changes", + controlType: "DATE_PICKER", + placeholderText: "Enter Default Date", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + }, + { + helpText: "Sets the format of the selected date", + propertyName: "dateFormat", + label: "Date Format", + controlType: "DROP_DOWN", + isJSConvertible: true, + options: [ + { + label: "YYYY-MM-DD", + value: "YYYY-MM-DD", + }, + { + label: "YYYY-MM-DD HH:mm", + value: "YYYY-MM-DD HH:mm", + }, + { + label: "YYYY-MM-DDTHH:mm:ss.sssZ", + value: "YYYY-MM-DDTHH:mm:ss.sssZ", + }, + { + label: "DD/MM/YYYY", + value: "DD/MM/YYYY", + }, + { + label: "DD/MM/YYYY HH:mm", + value: "DD/MM/YYYY HH:mm", + }, + ], + isBindProperty: true, + isTriggerProperty: false, + }, + { + propertyName: "isRequired", + label: "Required", + helpText: "Makes input to the widget mandatory", + controlType: "SWITCH", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + }, + { + propertyName: "isVisible", + label: "Visible", + helpText: "Controls the visibility of the widget", + controlType: "SWITCH", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + }, + { + propertyName: "isDisabled", + label: "Disabled", + helpText: "Disables input to this widget", + controlType: "SWITCH", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + }, + { + propertyName: "minDate", + label: "Min Date", + helpText: "Defines the min date for this widget", + controlType: "DATE_PICKER", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + }, + { + propertyName: "maxDate", + label: "Max Date", + helpText: "Defines the max date for this widget", + controlType: "DATE_PICKER", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: false, + }, + ], + }, + { + sectionName: "Actions", + children: [ + { + propertyName: "onDateSelected", + label: "onDateSelected", + controlType: "ACTION_SELECTOR", + isJSConvertible: true, + isBindProperty: true, + isTriggerProperty: true, + }, + ], + }, + ]; + } + + static getPropertyValidationMap(): WidgetPropertyValidationType { + return { + ...BASE_WIDGET_VALIDATION, + defaultDate: VALIDATION_TYPES.DEFAULT_DATE, + timezone: VALIDATION_TYPES.TEXT, + enableTimePicker: VALIDATION_TYPES.BOOLEAN, + dateFormat: VALIDATION_TYPES.TEXT, + label: VALIDATION_TYPES.TEXT, + datePickerType: VALIDATION_TYPES.TEXT, + maxDate: VALIDATION_TYPES.DATE, + minDate: VALIDATION_TYPES.DATE, + isRequired: VALIDATION_TYPES.BOOLEAN, + // onDateSelected: VALIDATION_TYPES.ACTION_SELECTOR, + // onDateRangeSelected: VALIDATION_TYPES.ACTION_SELECTOR, + }; + } + + static getDerivedPropertiesMap(): DerivedPropertiesMap { + return { + isValid: `{{ this.isRequired ? !!this.selectedDate : true }}`, + selectedDate: `{{ this.value ? moment(this.value).toISOString() : (this.defaultDate ? moment(this.defaultDate).toISOString() : "")}}`, + formattedDate: `{{ this.value ? moment(this.value).format(this.dateFormat) : (this.defaultDate ? moment(this.defaultDate).format(this.dateFormat) : "")}}`, + }; + } + + static getTriggerPropertyMap(): TriggerPropertiesMap { + return { + onDateSelected: true, + }; + } + + static getDefaultPropertiesMap(): Record { + return { + value: "defaultDate", + }; + } + + static getMetaPropertiesMap(): Record { + return { + value: undefined, + }; + } + + getPageView() { + return ( + + ); + } + + onDateSelected = (selectedDate: string) => { + this.props.updateWidgetMetaProperty("value", selectedDate, { + dynamicString: this.props.onDateSelected, + event: { + type: EventType.ON_DATE_SELECTED, + }, + }); + }; + + getWidgetType(): WidgetType { + return "DATE_PICKER_WIDGET2"; + } +} + +export type DatePickerType = "DATE_PICKER" | "DATE_RANGE_PICKER"; + +export interface DatePickerWidget2Props extends WidgetProps, WithMeta { + defaultDate: string; + selectedDate: string; + formattedDate: string; + isDisabled: boolean; + dateFormat: string; + label: string; + datePickerType: DatePickerType; + onDateSelected?: string; + onDateRangeSelected?: string; + maxDate: string; + minDate: string; + isRequired?: boolean; +} + +export default DatePickerWidget; +export const ProfiledDatePickerWidget2 = Sentry.withProfiler( + withMeta(DatePickerWidget), +); diff --git a/app/client/src/workers/evaluation.test.ts b/app/client/src/workers/evaluation.test.ts index 00d3b4d9f6..64265180d9 100644 --- a/app/client/src/workers/evaluation.test.ts +++ b/app/client/src/workers/evaluation.test.ts @@ -289,6 +289,33 @@ const WIDGET_CONFIG_MAP: WidgetTypeConfigMap = { }, metaProperties: {}, }, + DATE_PICKER_WIDGET2: { + validations: { + isLoading: "BOOLEAN", + isVisible: "BOOLEAN", + isDisabled: "BOOLEAN", + defaultDate: "DATE", + timezone: "TEXT", + enableTimePicker: "BOOLEAN", + dateFormat: "TEXT", + label: "TEXT", + datePickerType: "TEXT", + maxDate: "DATE", + minDate: "DATE", + isRequired: "BOOLEAN", + }, + defaultProperties: { + selectedDate: "defaultDate", + }, + derivedProperties: { + isValid: "{{ this.isRequired ? !!this.selectedDate : true }}", + value: "{{ this.selectedDate }}", + }, + triggerProperties: { + onDateSelected: true, + }, + metaProperties: {}, + }, TABS_WIDGET: { validations: { tabs: "TABS_DATA", @@ -447,6 +474,7 @@ const BASE_WIDGET: DataTreeWidget = { topRow: 0, type: WidgetTypes.SKELETON_WIDGET, parentId: "0", + version: 1, bindingPaths: {}, triggerPaths: {}, ENTITY_TYPE: ENTITY_TYPE.WIDGET, diff --git a/app/client/src/workers/validations.ts b/app/client/src/workers/validations.ts index af27b932ea..6a595e7075 100644 --- a/app/client/src/workers/validations.ts +++ b/app/client/src/workers/validations.ts @@ -391,7 +391,10 @@ export const VALIDATORS: Record = { dateString: string, props: WidgetProps, ): ValidationResponse => { - const dateFormat = props.dateFormat ? props.dateFormat : ISO_DATE_FORMAT; + const dateFormat = + props.version === 2 + ? ISO_DATE_FORMAT + : props.dateFormat || ISO_DATE_FORMAT; if (dateString === undefined) { return { @@ -421,39 +424,22 @@ export const VALIDATORS: Record = { dateString: string, props: WidgetProps, ): ValidationResponse => { - const dateFormat = props.dateFormat ? props.dateFormat : ISO_DATE_FORMAT; + const dateFormat = + props.version === 2 + ? ISO_DATE_FORMAT + : props.dateFormat || ISO_DATE_FORMAT; if (dateString === undefined) { return { isValid: false, parsed: "", message: - `${WIDGET_TYPE_VALIDATION_ERROR}: Date ` + props.dateFormat - ? props.dateFormat + `${WIDGET_TYPE_VALIDATION_ERROR}: Date ` + dateFormat + ? dateFormat : "", }; } const parsedCurrentDate = moment(dateString, dateFormat); - let isValid = parsedCurrentDate.isValid(); - const parsedMinDate = moment(props.minDate, dateFormat); - const parsedMaxDate = moment(props.maxDate, dateFormat); - - // checking for max/min date range - if (isValid) { - if ( - parsedMinDate.isValid() && - parsedCurrentDate.isBefore(parsedMinDate) - ) { - isValid = false; - } - - if ( - isValid && - parsedMaxDate.isValid() && - parsedCurrentDate.isAfter(parsedMaxDate) - ) { - isValid = false; - } - } + const isValid = parsedCurrentDate.isValid(); if (!isValid) { return { isValid: isValid, @@ -471,14 +457,17 @@ export const VALIDATORS: Record = { dateString: string, props: WidgetProps, ): ValidationResponse => { - const dateFormat = props.dateFormat ? props.dateFormat : ISO_DATE_FORMAT; + const dateFormat = + props.version === 2 + ? ISO_DATE_FORMAT + : props.dateFormat || ISO_DATE_FORMAT; if (dateString === undefined) { return { isValid: false, parsed: "", message: - `${WIDGET_TYPE_VALIDATION_ERROR}: Date ` + props.dateFormat - ? props.dateFormat + `${WIDGET_TYPE_VALIDATION_ERROR}: Date ` + dateFormat + ? dateFormat : "", }; } @@ -517,14 +506,17 @@ export const VALIDATORS: Record = { dateString: string, props: WidgetProps, ): ValidationResponse => { - const dateFormat = props.dateFormat ? props.dateFormat : ISO_DATE_FORMAT; + const dateFormat = + props.version === 2 + ? ISO_DATE_FORMAT + : props.dateFormat || ISO_DATE_FORMAT; if (dateString === undefined) { return { isValid: false, parsed: "", message: - `${WIDGET_TYPE_VALIDATION_ERROR}: Date ` + props.dateFormat - ? props.dateFormat + `${WIDGET_TYPE_VALIDATION_ERROR}: Date ` + dateFormat + ? dateFormat : "", }; }