From 79a7979839dfc71206438955b0a6066bd35203de Mon Sep 17 00:00:00 2001 From: Hetu Nandu Date: Tue, 5 Nov 2024 17:00:51 +0530 Subject: [PATCH 1/7] fix: Paddings and scrolls in Plugin Action Editor (#37203) ## Description Updates the padding and fixes the unnecessary scrolls renders in the new Plugin Action Editor flow Fixes #31573 Fixes #37214 ## Automation /ok-to-test tags="@tag.Datasource" ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [x] No ## Summary by CodeRabbit - **New Features** - Introduced a new `TabbedViewContainer` for improved layout in the `CommonEditorForm`. - Added support for binary file uploads in the `PostBodyData` component. - **Improvements** - Enhanced padding and border behavior in the `Section` component based on the new `withoutPadding` attribute. - Adjusted minimum height for the `.CodeMirror` class in the `PostBodyData` component. - Improved layout and styling in the `InfoFields` component with new styled components. - Updated tooltip content handling in the `DatasourceConfig` component for better clarity on invalid entries. - Refined layout behavior in the `CommonEditorForm` with the addition of the `TabbedViewContainer`. - Enhanced layout in the `EmbeddedDatasourcePathField` component with updated styling. - **Bug Fixes** - No new bug fixes reported. - **Refactor** - Streamlined the `RequestTabs` component by integrating styles directly into the `Tabs` component. - Refined handling of datasource URLs in the `EmbeddedDatasourcePathField` component. - Updated styling and structure in the `KeyValueFieldArray` component for improved layout. --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../components/ActionForm/Section/index.tsx | 6 +- .../ActionForm/Section/styles.module.css | 15 ++++- .../components/ApiEditor/PostBodyData.tsx | 1 - .../CommonEditorForm/CommonEditorForm.tsx | 8 ++- .../CommonEditorForm/InfoFields.tsx | 18 ++---- .../CommonEditorForm/RequestTabs.tsx | 60 ++++++++++--------- .../components/DatasourceConfig.tsx | 5 +- .../EmbeddedDatasourcePathField.tsx | 5 +- .../components/CommonEditorForm/styles.ts | 12 ++++ .../components/GraphQLEditor/PostBodyData.tsx | 4 +- .../form/fields/KeyValueFieldArray.tsx | 6 +- .../Editor/APIEditor/CommonEditorForm.tsx | 1 + .../JSEditor/JSEditorForm/JSEditorForm.tsx | 2 +- 13 files changed, 81 insertions(+), 62 deletions(-) create mode 100644 app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/styles.ts diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/ActionForm/Section/index.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/components/ActionForm/Section/index.tsx index 7b664e11960..d47fd7f0616 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionForm/components/ActionForm/Section/index.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/ActionForm/Section/index.tsx @@ -4,7 +4,7 @@ import styles from "./styles.module.css"; interface SectionProps extends React.HTMLAttributes { children: React.ReactNode; - isStandalone?: boolean; + withoutPadding?: boolean; isFullWidth?: boolean; } @@ -12,7 +12,7 @@ const Section: React.FC = ({ children, className, isFullWidth = false, - isStandalone = false, + withoutPadding = false, ...props }) => { const classNames = clsx(styles.section, className); @@ -21,7 +21,7 @@ const Section: React.FC = ({
{children} diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/ActionForm/Section/styles.module.css b/app/client/src/PluginActionEditor/components/PluginActionForm/components/ActionForm/Section/styles.module.css index fb03da7ffbd..ac73ba3c3bc 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionForm/components/ActionForm/Section/styles.module.css +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/ActionForm/Section/styles.module.css @@ -6,11 +6,22 @@ max-width: 800px; justify-content: center; - &[data-standalone="false"] { + &[data-withoutPadding="true"] { + padding: 0; + } + + /* We do not want padding above the first section */ + &[data-withoutPadding="false"]:first-child { + padding-bottom: var(--ads-v2-spaces-6); + } + + /* All other sections expect first will have padding top and bottom */ + &[data-withoutPadding="false"]:not(:first-child) { padding-block: var(--ads-v2-spaces-6); } - &[data-standalone="false"]:not(:last-child) { + /* We will also render a border below sections expect for the last section */ + &[data-withoutPadding="false"]:not(:last-child) { border-bottom: 1px solid var(--ads-v2-color-border); } diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/ApiEditor/PostBodyData.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/components/ApiEditor/PostBodyData.tsx index de373520ece..cee26fa6075 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionForm/components/ApiEditor/PostBodyData.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/ApiEditor/PostBodyData.tsx @@ -28,7 +28,6 @@ import { Select, Option } from "@appsmith/ads"; const PostBodyContainer = styled.div` display: flex; flex-direction: column; - padding: 12px 0px 0px; background-color: var(--ads-v2-color-bg); height: 100%; gap: var(--ads-v2-spaces-4); diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/CommonEditorForm.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/CommonEditorForm.tsx index a9ae550c0ec..802a5294572 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/CommonEditorForm.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/CommonEditorForm.tsx @@ -29,7 +29,13 @@ const CommonEditorForm = (props: Props) => { } = useGetFormActionValues(); return ( - + -
+ +
-
- +
+ - +
); } diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/RequestTabs.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/RequestTabs.tsx index f3e0adb9065..62b22a18b6c 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/RequestTabs.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/RequestTabs.tsx @@ -22,13 +22,9 @@ const SettingsWrapper = styled.div` padding: 0; } `; -const TabsListWrapper = styled.div` - padding: 0 var(--ads-v2-spaces-7); -`; const StyledTabPanel = styled(TabPanel)` height: calc(100% - 50px); overflow: auto; - padding: 0 var(--ads-v2-spaces-7); `; export function RequestTabs(props: { @@ -61,31 +57,37 @@ export function RequestTabs(props: { ); return ( - - - - {Object.values(API_EDITOR_TABS) - .filter((tab) => { - return !(!props.showSettings && tab === API_EDITOR_TABS.SETTINGS); - }) - .map((tab) => ( - - {createMessage(API_EDITOR_TAB_TITLES[tab])} - - ))} - - + + + {Object.values(API_EDITOR_TABS) + .filter((tab) => { + return !(!props.showSettings && tab === API_EDITOR_TABS.SETTINGS); + }) + .map((tab) => ( + + {createMessage(API_EDITOR_TAB_TITLES[tab])} + + ))} + props.theme.spaces[4]}px - ${(props) => props.theme.spaces[14]}px 0 0; -`; +const KeyValueFlexContainer = styled.div``; const FormRowWithLabel = styled(FormRow)` flex-wrap: wrap; diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/components/EmbeddedDatasourcePathField.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/components/EmbeddedDatasourcePathField.tsx index 136c51b0cce..87615cbf7f2 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/components/EmbeddedDatasourcePathField.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/components/EmbeddedDatasourcePathField.tsx @@ -92,6 +92,7 @@ const DatasourceContainer = styled.div` position: relative; align-items: center; height: 36px; + gap: var(--ads-v2-spaces-4); .t--datasource-editor { background-color: var(--ads-v2-color-bg); .cm-s-duotone-light.CodeMirror { @@ -101,10 +102,6 @@ const DatasourceContainer = styled.div` z-index: ${Indices.Layer5}; } } - - .t--store-as-datasource { - margin-left: 10px; - } `; const hintContainerStyles: React.CSSProperties = { diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/styles.ts b/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/styles.ts new file mode 100644 index 00000000000..a60145281e9 --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/CommonEditorForm/styles.ts @@ -0,0 +1,12 @@ +import styled from "styled-components"; + +export const RequestMethodSelectContainer = styled.div` + width: 100px; + .ads-v2-select > .rc-select-selector { + min-width: 100px; + } +`; + +export const DatasourcePathFieldContainer = styled.div` + width: 100%; +`; diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/GraphQLEditor/PostBodyData.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/components/GraphQLEditor/PostBodyData.tsx index c66aa404858..aaabc25d70d 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionForm/components/GraphQLEditor/PostBodyData.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/GraphQLEditor/PostBodyData.tsx @@ -15,7 +15,7 @@ import FormLabel from "components/editorComponents/FormLabel"; const PostBodyContainer = styled.div` &&&& .CodeMirror { height: auto; - min-height: 250px; + min-height: 150px; } `; @@ -43,7 +43,7 @@ function PostBodyData(props: Props) { return ( -
+
Query diff --git a/app/client/src/components/editorComponents/form/fields/KeyValueFieldArray.tsx b/app/client/src/components/editorComponents/form/fields/KeyValueFieldArray.tsx index 8e5e514dc38..c30513169b5 100644 --- a/app/client/src/components/editorComponents/form/fields/KeyValueFieldArray.tsx +++ b/app/client/src/components/editorComponents/form/fields/KeyValueFieldArray.tsx @@ -33,7 +33,7 @@ const KeyValueStackContainer = styled.div` // `; const FormRowWithLabel = styled(FormRow)` flex-wrap: wrap; - margin-bottom: ${(props) => props.theme.spaces[2] - 1}px; + margin-bottom: var(--ads-v2-spaces-3); ${FormLabel} { width: 100%; } @@ -52,7 +52,7 @@ const Flex = styled.div<{ size: number }>` ${(props) => props.size === 3 ? ` - margin-left: 5px; + margin-left: var(--ads-v2-spaces-3); ` : null}; `; @@ -81,7 +81,7 @@ const DynamicTextFieldWithDropdownWrapper = styled.div` const DynamicDropdownFieldWrapper = styled.div` position: relative; - margin-left: 5px; + margin-left: var(--ads-v2-spaces-3); border-color: var(--ads-v2-color-border); color: var(--ads-v2-color-fg); diff --git a/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx b/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx index 10e04d269f3..a024ab6b386 100644 --- a/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx +++ b/app/client/src/pages/Editor/APIEditor/CommonEditorForm.tsx @@ -101,6 +101,7 @@ const TabbedViewContainer = styled.div` overflow: auto; position: relative; height: 100%; + padding: 0 var(--ads-v2-spaces-7); `; const Wrapper = styled.div` diff --git a/app/client/src/pages/Editor/JSEditor/JSEditorForm/JSEditorForm.tsx b/app/client/src/pages/Editor/JSEditor/JSEditorForm/JSEditorForm.tsx index 9b6f1323940..b7ab3ad4844 100644 --- a/app/client/src/pages/Editor/JSEditor/JSEditorForm/JSEditorForm.tsx +++ b/app/client/src/pages/Editor/JSEditor/JSEditorForm/JSEditorForm.tsx @@ -57,7 +57,7 @@ export const JSEditorForm = (props: Props) => { } return ( - + Date: Tue, 5 Nov 2024 17:11:02 +0530 Subject: [PATCH 2/7] test: fixed datepicker skipped tests set 2 (#37126) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated common method to suite both v1 and v2 Skipped test updated locator change handled EE PR: https://github.com/appsmithorg/appsmith-ee/pull/5477 /ok-to-test tags="@tag.All" > [!TIP] > 🟒 🟒 🟒 All cypress tests have passed! πŸŽ‰ πŸŽ‰ πŸŽ‰ > Workflow run: > Commit: 56ecbce50bef806998c83ffe585482a32eff120b > Cypress dashboard. > Tags: `@tag.All` > Spec: >
Tue, 05 Nov 2024 06:59:10 UTC ## Summary by CodeRabbit - **New Features** - Enhanced date selection process in DatePicker widget with improved test interactions. - Introduced a new structured JSON layout for the user interface, including multiple interactive widgets. - **Bug Fixes** - Activated previously skipped tests to ensure comprehensive validation of DatePicker functionality. - **Documentation** - Updated locators for form widgets to improve selector accuracy and functionality. - **Chores** - Refined various Cypress commands related to widget interactions for better reliability and performance. --------- Co-authored-by: β€œNandanAnantharamu” <β€œnandan@thinkify.io”> --- .../Binding/DatePicker_Text_spec.js | 24 +- .../Widgets/Datepicker/DatePicker2_spec.js | 2 +- .../Datepicker/DatePicker_With_Switch_spec.js | 21 +- app/client/cypress/fixtures/uiBindnewDsl.json | 540 ++++++++++++++++++ app/client/cypress/locators/FormWidgets.json | 3 +- app/client/cypress/locators/Widgets.json | 4 +- app/client/cypress/support/commands.js | 14 +- app/client/cypress/support/widgetCommands.js | 2 +- 8 files changed, 577 insertions(+), 33 deletions(-) create mode 100644 app/client/cypress/fixtures/uiBindnewDsl.json diff --git a/app/client/cypress/e2e/Regression/ClientSide/Binding/DatePicker_Text_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Binding/DatePicker_Text_spec.js index 037e637f8cd..52c699902ba 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Binding/DatePicker_Text_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Binding/DatePicker_Text_spec.js @@ -14,7 +14,7 @@ describe( _.agHelper.AddDsl("uiBindDsl"); }); // Skipping tests due to issue - https://www.notion.so/appsmith/f353d8c6bd664f79ad858a42010cdfc8?v=f04cde23f6424aeb9d5a6e389cd172bd&p=0717892d43684c40bae4e2c87b8308cb&pm=s - it.skip("1. DatePicker-Text, Validate selectedDate functionality", function () { + it("1. DatePicker-Text, Validate selectedDate functionality", function () { /** * Bind DatePicker1 to Text for "selectedDate" */ @@ -25,9 +25,8 @@ describe( * Set the Calender for today's date in DatePicker1 */ cy.openPropertyPane("datepickerwidget"); - cy.get(formWidgetsPage.defaultDate).click(); - cy.ClearDateFooter(); - cy.SetDateToToday(); + cy.get(formWidgetsPage.datepickerWidget).first().click(); + cy.get(formWidgetsPage.datepickerFooter).contains("Today").click(); cy.getDate(1, "YYYY-MM-DD").then((date) => { cy.log("retured date" + date); @@ -43,15 +42,15 @@ describe( cy.get(publishPage.datepickerWidget + commonlocators.inputField) .eq(0) .click(); - cy.ClearDateFooter(); - cy.setDate(1, "ddd MMM DD YYYY"); + cy.SetDateToToday(); + cy.setDate(1, "ddd MMM DD YYYY", "v1"); cy.get(commonlocators.labelTextStyle).should("contain", nextDay); }); cy.get(commonlocators.backToEditor).click(); }); - it.skip("2. DatePicker1-text: Change the date in DatePicker1 and Validate the same in text widget", function () { + it("2. DatePicker1-text: Change the date in DatePicker1 and Validate the same in text widget", function () { cy.openPropertyPane("textwidget"); /** @@ -75,9 +74,9 @@ describe( */ cy.openPropertyPane("datepickerwidget"); cy.get(formWidgetsPage.defaultDate).click(); - cy.ClearDateFooter(); - cy.setDate(1, "ddd MMM DD YYYY"); - // cy.get(commonlocators.onDateSelectedField).click(); + cy.get(formWidgetsPage.dayPickerToday).click(); + cy.get(formWidgetsPage.defaultDate).click(); + cy.setDate(1); /** *Validate the date in text widget @@ -89,7 +88,7 @@ describe( }); }); - it.skip("3. Validate the Date is not changed in DatePicker2", function () { + it("3. Validate the Date is not changed in DatePicker2", function () { cy.log("dateDp2:" + dateDp2); cy.get(formWidgetsPage.datepickerWidget + commonlocators.inputField) .eq(1) @@ -124,7 +123,7 @@ describe( _.deployMode.NavigateBacktoEditor(); }); - it.skip("5. Checks if on deselection of date triggers the onDateSelected action or not.", function () { + it("5. Checks if on deselection of date triggers the onDateSelected action or not.", function () { /** * bind datepicker to show a message "Hello" on date selected */ @@ -135,7 +134,6 @@ describe( * checking if on selecting the date triggers the message */ cy.get(formWidgetsPage.datepickerWidget).first().click(); - cy.ClearDateFooter(); cy.SetDateToToday(); cy.get(commonlocators.toastmsg).contains("hello"); diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Datepicker/DatePicker2_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Datepicker/DatePicker2_spec.js index a84f5c06692..065f0c7a9e4 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Datepicker/DatePicker2_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Datepicker/DatePicker2_spec.js @@ -22,7 +22,7 @@ describe( it("DatePicker-Date Name validation", function () { // changing the date to today cy.get(formWidgetsPage.defaultDate).click(); - cy.SetDateToToday(); + cy.get(formWidgetsPage.dayPickerToday).click(); //changing the Button Name cy.widgetText( diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Datepicker/DatePicker_With_Switch_spec.js b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Datepicker/DatePicker_With_Switch_spec.js index d48e5ce34c9..f5257949830 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/Datepicker/DatePicker_With_Switch_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/Datepicker/DatePicker_With_Switch_spec.js @@ -28,11 +28,14 @@ describe( cy.closePropertyPane(); }); // Skipping tests due to issue - https://www.notion.so/appsmith/f353d8c6bd664f79ad858a42010cdfc8?v=f04cde23f6424aeb9d5a6e389cd172bd&p=0717892d43684c40bae4e2c87b8308cb&pm=s - it.skip("Date Widget with Reset widget being switch widget", function () { + it("Date Widget with Reset widget being switch widget", function () { EditorNavigation.SelectEntityByName("DatePicker1", EntityType.Widget); + cy.get(formWidgetsPage.datePickerInput).click(); + _.agHelper.GetNClick(widgetsPage.todayText); + cy.get(formWidgetsPage.defaultDate).click(); + _.agHelper.GetNClick(".ads-v2-datepicker__calender-today"); cy.get(formWidgetsPage.defaultDate).click(); - cy.SetDateToToday(); cy.setDate(1, "ddd MMM DD YYYY"); const nextDay = dayjs().format("DD/MM/YYYY"); cy.log(nextDay); @@ -51,18 +54,10 @@ describe( cy.get(widgetsPage.switchWidgetInactive).should("be.visible"); }); - it.skip("DatePicker-Date change and validate switch widget status", function () { + it("DatePicker-Date change and validate switch widget status", function () { cy.get(widgetsPage.datepickerInput).click({ force: true }); - cy.SetDateToToday(); - cy.get(widgetsPage.switchWidgetActive).should("be.visible"); - cy.get(".t--toast-action span") - .last() - .invoke("text") - .then((text) => { - const toasttext = text; - cy.log(toasttext); - expect(text.trim()).to.equal(toasttext.trim()); - }); + _.agHelper.GetNClick(widgetsPage.todayText); + _.agHelper.AssertClassExists(".bp3-switch", "t--switch-widget-active"); }); }, ); diff --git a/app/client/cypress/fixtures/uiBindnewDsl.json b/app/client/cypress/fixtures/uiBindnewDsl.json new file mode 100644 index 00000000000..6d45729acce --- /dev/null +++ b/app/client/cypress/fixtures/uiBindnewDsl.json @@ -0,0 +1,540 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 600, + "containerStyle": "none", + "snapRows": 124, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 90, + "minHeight": 1292, + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "boxShadow": "none", + "widgetName": "Container1", + "topRow": 0, + "bottomRow": 60, + "parentRowSpace": 38, + "type": "CONTAINER_WIDGET", + "parentColumnSpace": 75.25, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "labelTextSize": "0.875rem", + "boxShadow": "none", + "backgroundColor": "#FFFFFF", + "widgetName": "utncsu66ty", + "rightColumn": 2408, + "orientation": "VERTICAL", + "snapColumns": 16, + "detachFromLayout": true, + "widgetId": "acizsl94my", + "containerStyle": "none", + "topRow": 0, + "bottomRow": 380, + "parentRowSpace": 1, + "isVisible": true, + "type": "CANVAS_WIDGET", + "canExtend": false, + "version": 1, + "isLoading": false, + "parentColumnSpace": 1, + "leftColumn": 0, + "dynamicBindingPathList": [], + "borderRadius": "0px", + "children": [ + { + "boxShadow": "none", + "widgetName": "Text5", + "topRow": 28, + "bottomRow": 32, + "parentRowSpace": 38, + "type": "TEXT_WIDGET", + "parentColumnSpace": 62.484375, + "overflow": "NONE", + "fontFamily": "System Default", + "dynamicTriggerPathList": [], + "leftColumn": 16, + "dynamicBindingPathList": [ + { + "key": "text" + } + ], + "text": "{{DatePicker1.selectedDate}}", + "labelTextSize": "0.875rem", + "rightColumn": 32, + "textAlign": "LEFT", + "dynamicHeight": "FIXED", + "widgetId": "t7c7i4gv0f", + "isVisible": true, + "fontStyle": "BOLD", + "version": 1, + "textColor": "#231F20", + "parentId": "acizsl94my", + "isLoading": false, + "borderRadius": "0px", + "maxDynamicHeight": 9000, + "fontSize": "0.875rem", + "minDynamicHeight": 4 + }, + { + "needsErrorInfo": false, + "boxShadow": "none", + "mobileBottomRow": 12, + "widgetName": "DatePicker1", + "minDate": "1920-12-31T18:30:00.000Z", + "dateFormat": "YYYY-MM-DD HH:mm", + "topRow": 5, + "bottomRow": 12, + "shortcuts": false, + "parentRowSpace": 10, + "labelWidth": 5, + "type": "DATE_PICKER_WIDGET2", + "mobileRightColumn": 30, + "animateLoading": true, + "parentColumnSpace": 7.26171875, + "leftColumn": 10, + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + } + ], + "labelPosition": "Top", + "isDisabled": false, + "key": "frcocd42ul", + "labelTextSize": "0.875rem", + "isRequired": false, + "defaultDate": "2024-10-24T07:39:07.659Z", + "rightColumn": 30, + "dynamicHeight": "FIXED", + "widgetId": "pwo88q4udx", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "minWidth": 450, + "isVisible": true, + "datePickerType": "DATE_PICKER", + "label": "Label", + "version": 2, + "parentId": "acizsl94my", + "labelAlignment": "left", + "renderMode": "CANVAS", + "isLoading": false, + "mobileTopRow": 5, + "timePrecision": "minute", + "responsiveBehavior": "fill", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "mobileLeftColumn": 10, + "maxDynamicHeight": 9000, + "firstDayOfWeek": 0, + "closeOnSelection": true, + "maxDate": "2121-12-31T18:29:00.000Z", + "minDynamicHeight": 4 + }, + { + "needsErrorInfo": false, + "boxShadow": "none", + "mobileBottomRow": 22, + "widgetName": "DatePicker2", + "minDate": "1920-12-31T18:30:00.000Z", + "dateFormat": "YYYY-MM-DD HH:mm", + "topRow": 14, + "bottomRow": 21, + "shortcuts": false, + "parentRowSpace": 10, + "labelWidth": 5, + "type": "DATE_PICKER_WIDGET2", + "mobileRightColumn": 29, + "animateLoading": true, + "parentColumnSpace": 7.26171875, + "leftColumn": 10, + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + } + ], + "labelPosition": "Top", + "isDisabled": false, + "key": "frcocd42ul", + "labelTextSize": "0.875rem", + "isRequired": false, + "defaultDate": "2024-10-24T07:39:07.659Z", + "rightColumn": 30, + "dynamicHeight": "FIXED", + "widgetId": "gwcu42z6yn", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "minWidth": 450, + "isVisible": true, + "datePickerType": "DATE_PICKER", + "label": "Label", + "version": 2, + "parentId": "acizsl94my", + "labelAlignment": "left", + "renderMode": "CANVAS", + "isLoading": false, + "mobileTopRow": 15, + "timePrecision": "minute", + "responsiveBehavior": "fill", + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "mobileLeftColumn": 9, + "maxDynamicHeight": 9000, + "firstDayOfWeek": 0, + "closeOnSelection": true, + "maxDate": "2121-12-31T18:29:00.000Z", + "minDynamicHeight": 4 + } + ] + } + ], + "labelTextSize": "0.875rem", + "backgroundColor": "#FFFFFF", + "rightColumn": 56, + "orientation": "VERTICAL", + "snapColumns": 16, + "dynamicHeight": "FIXED", + "widgetId": "mp429a7dl3", + "containerStyle": "card", + "isVisible": true, + "version": 1, + "isLoading": false, + "borderRadius": "0px", + "maxDynamicHeight": 9000, + "minDynamicHeight": 4 + }, + { + "boxShadow": "none", + "widgetName": "Modal1", + "topRow": 0, + "bottomRow": 0, + "parentRowSpace": 1, + "type": "MODAL_WIDGET", + "shouldScrollContents": true, + "parentColumnSpace": 1, + "leftColumn": 0, + "dynamicBindingPathList": [], + "children": [ + { + "boxShadow": "none", + "widgetName": "Canvas1", + "topRow": 0, + "bottomRow": 240, + "parentRowSpace": 1, + "canExtend": true, + "type": "CANVAS_WIDGET", + "shouldScrollContents": false, + "minHeight": 0, + "parentColumnSpace": 1, + "leftColumn": 0, + "dynamicBindingPathList": [], + "children": [ + { + "labelTextSize": "0.875rem", + "isRequired": false, + "boxShadow": "none", + "widgetName": "Icon1", + "rightColumn": 64, + "onClick": "{{closeModal(Modal1.name)}}", + "iconName": "cross", + "buttonColor": "#2E3D49", + "widgetId": "t3sjfihdb1", + "topRow": 0, + "bottomRow": 4, + "isVisible": true, + "type": "ICON_BUTTON_WIDGET", + "version": 1, + "parentId": "cwamdbv44c", + "isLoading": false, + "leftColumn": 60, + "dynamicBindingPathList": [], + "borderRadius": "0px", + "buttonVariant": "TERTIARY", + "iconSize": 24 + }, + { + "boxShadow": "none", + "widgetName": "Text4", + "dynamicPropertyPathList": [ + { + "key": "fontSize" + } + ], + "topRow": 0, + "bottomRow": 4, + "type": "TEXT_WIDGET", + "overflow": "NONE", + "fontFamily": "System Default", + "leftColumn": 0, + "dynamicBindingPathList": [], + "text": "Modal Title", + "labelTextSize": "0.875rem", + "rightColumn": 60, + "dynamicHeight": "FIXED", + "widgetId": "x893ud3zjh", + "isVisible": true, + "fontStyle": "BOLD", + "version": 1, + "textColor": "#231F20", + "parentId": "cwamdbv44c", + "isLoading": false, + "borderRadius": "0px", + "maxDynamicHeight": 9000, + "fontSize": "1.5rem", + "minDynamicHeight": 4 + }, + { + "labelTextSize": "0.875rem", + "boxShadow": "none", + "widgetName": "Button2", + "rightColumn": 52, + "isDefaultClickDisabled": true, + "buttonColor": "#03B365", + "widgetId": "q9snwskqan", + "topRow": 16, + "bottomRow": 20, + "isVisible": true, + "type": "BUTTON_WIDGET", + "version": 1, + "recaptchaType": "V3", + "parentId": "cwamdbv44c", + "isLoading": false, + "leftColumn": 40, + "dynamicBindingPathList": [], + "borderRadius": "0px", + "buttonVariant": "PRIMARY", + "text": "Cancel", + "isDisabled": false + }, + { + "labelTextSize": "0.875rem", + "boxShadow": "none", + "widgetName": "Button3", + "rightColumn": 64, + "isDefaultClickDisabled": true, + "buttonColor": "#03B365", + "widgetId": "tufuj2kdpz", + "topRow": 16, + "bottomRow": 20, + "isVisible": true, + "type": "BUTTON_WIDGET", + "version": 1, + "recaptchaType": "V3", + "parentId": "cwamdbv44c", + "isLoading": false, + "leftColumn": 52, + "dynamicBindingPathList": [], + "borderRadius": "0px", + "buttonVariant": "PRIMARY", + "text": "Confirm", + "isDisabled": false + } + ], + "isDisabled": false, + "labelTextSize": "0.875rem", + "rightColumn": 0, + "detachFromLayout": true, + "widgetId": "cwamdbv44c", + "isVisible": true, + "version": 1, + "parentId": "bx9a2jg08o", + "blueprint": { + "view": [ + { + "type": "ICON_WIDGET", + "position": { + "left": 15, + "top": 0 + }, + "size": { + "rows": 1, + "cols": 1 + }, + "props": { + "iconName": "cross", + "iconSize": 24, + "color": "#040627" + } + }, + { + "type": "TEXT_WIDGET", + "position": { + "left": 0, + "top": 0 + }, + "size": { + "rows": 1, + "cols": 15 + }, + "props": { + "text": "Modal Title", + "textStyle": "HEADING" + } + }, + { + "type": "BUTTON_WIDGET", + "position": { + "left": 10, + "top": 4 + }, + "size": { + "rows": 1, + "cols": 3 + }, + "props": { + "text": "Cancel", + "buttonStyle": "SECONDARY_BUTTON" + } + }, + { + "type": "BUTTON_WIDGET", + "position": { + "left": 13, + "top": 4 + }, + "size": { + "rows": 1, + "cols": 3 + }, + "props": { + "text": "Confirm", + "buttonStyle": "PRIMARY_BUTTON" + } + } + ], + "operations": [ + { + "type": "MODIFY_PROPS" + } + ] + }, + "isLoading": false, + "borderRadius": "0px" + } + ], + "height": 240, + "labelTextSize": "0.875rem", + "rightColumn": 0, + "detachFromLayout": true, + "dynamicHeight": "FIXED", + "widgetId": "bx9a2jg08o", + "isVisible": false, + "canOutsideClickClose": true, + "canEscapeKeyClose": true, + "version": 2, + "parentId": "0", + "blueprint": { + "view": [ + { + "type": "CANVAS_WIDGET", + "position": { + "left": 0, + "top": 0 + }, + "props": { + "detachFromLayout": true, + "canExtend": true, + "isVisible": true, + "isDisabled": false, + "shouldScrollContents": false, + "children": [], + "blueprint": { + "view": [ + { + "type": "ICON_WIDGET", + "position": { + "left": 15, + "top": 0 + }, + "size": { + "rows": 1, + "cols": 1 + }, + "props": { + "iconName": "cross", + "iconSize": 24, + "color": "#040627" + } + }, + { + "type": "TEXT_WIDGET", + "position": { + "left": 0, + "top": 0 + }, + "size": { + "rows": 1, + "cols": 15 + }, + "props": { + "text": "Modal Title", + "textStyle": "HEADING" + } + }, + { + "type": "BUTTON_WIDGET", + "position": { + "left": 10, + "top": 4 + }, + "size": { + "rows": 1, + "cols": 3 + }, + "props": { + "text": "Cancel", + "buttonStyle": "SECONDARY_BUTTON" + } + }, + { + "type": "BUTTON_WIDGET", + "position": { + "left": 13, + "top": 4 + }, + "size": { + "rows": 1, + "cols": 3 + }, + "props": { + "text": "Confirm", + "buttonStyle": "PRIMARY_BUTTON" + } + } + ], + "operations": [ + { + "type": "MODIFY_PROPS" + } + ] + } + } + } + ] + }, + "isLoading": false, + "borderRadius": "0px", + "maxDynamicHeight": 9000, + "width": 456, + "minDynamicHeight": 4 + } + ] + }, + "layoutOnLoadActions": [], + "layoutOnLoadActionErrors": [], + "actionUpdates": [], + "messages": [] +} diff --git a/app/client/cypress/locators/FormWidgets.json b/app/client/cypress/locators/FormWidgets.json index db603de7b8c..43462381c2d 100644 --- a/app/client/cypress/locators/FormWidgets.json +++ b/app/client/cypress/locators/FormWidgets.json @@ -78,5 +78,6 @@ "minDateTextArea" : ".t--property-control-mindate .CodeMirror textarea", "minDateInput" : ".t--property-control-mindate .ads-v2-input__input-section-input", "datePickerInput": ".t--widget-datepickerwidget2 .bp3-input", - "dayPickerNextButton": ".DayPicker-NavButton--next" + "dayPickerNextButton": ".DayPicker-NavButton--next", + "dayPickerToday": ".ads-v2-datepicker__calender-today" } diff --git a/app/client/cypress/locators/Widgets.json b/app/client/cypress/locators/Widgets.json index 92b161d141b..4dcb4ce330b 100644 --- a/app/client/cypress/locators/Widgets.json +++ b/app/client/cypress/locators/Widgets.json @@ -234,5 +234,7 @@ "propertyPaneSaveButton": ".t--property-pane-section-collapse-savebutton", "firstEditInput":"[data-colindex=0][data-rowindex=0] .t--inlined-cell-editor input.bp3-input", "cellControlSwitch" : ".t--property-control-cellwrapping .ads-v2-switch", - "propertyControlLabel" : ".t--property-control-label" + "propertyControlLabel" : ".t--property-control-label", + "todayText": "span:contains('Today')", + "dayPickerToday": ".DayPicker-Day--today" } diff --git a/app/client/cypress/support/commands.js b/app/client/cypress/support/commands.js index d922a6f902f..c088b90d868 100644 --- a/app/client/cypress/support/commands.js +++ b/app/client/cypress/support/commands.js @@ -526,9 +526,17 @@ Cypress.Commands.add("getDate", (date, dateFormate) => { return eDate; }); -Cypress.Commands.add("setDate", (date) => { - const expDate = dayjs().add(date, "days").format("dddd, MMMM Do, YYYY"); - cy.get(`.react-datepicker__day[aria-label^="Choose ${expDate}"]`).click(); +Cypress.Commands.add("setDate", (date, dateFormate, ver = "v2") => { + if (ver == "v2") { + const expDate = dayjs().add(date, "days").format("dddd, MMMM D"); + cy.get(`.react-datepicker__day[aria-label^="Choose ${expDate}"]`) + .first() + .click(); + } else if (ver == "v1") { + const expDate = dayjs().add(date, "days").format(dateFormate); + const sel = `.DayPicker-Day[aria-label=\"${expDate}\"]`; + cy.get(sel).click(); + } }); Cypress.Commands.add("validateDisableWidget", (widgetCss, disableCss) => { diff --git a/app/client/cypress/support/widgetCommands.js b/app/client/cypress/support/widgetCommands.js index fd0772748f6..23d1d828f6a 100644 --- a/app/client/cypress/support/widgetCommands.js +++ b/app/client/cypress/support/widgetCommands.js @@ -837,7 +837,7 @@ Cypress.Commands.add("selectWidgetForReset", (value) => { }); Cypress.Commands.add("SetDateToToday", () => { - cy.get(".react-datepicker .react-datepicker__day--today").click({ + cy.get("button:contains('Today')").click({ force: true, }); agHelper.AssertAutoSave(); From db713e946760e14c89a399527fe3357bf9a7b7aa Mon Sep 17 00:00:00 2001 From: Hetu Nandu Date: Tue, 5 Nov 2024 18:29:26 +0530 Subject: [PATCH 3/7] chore: [Plugin Action Editor] Query Form evaluation (#37224) ## Description Adds the Query and Google Sheets initialisation based on Google Sheets ref: https://github.com/appsmithorg/appsmith/blob/release/app/client/src/pages/Editor/QueryEditor/Editor.tsx#L292-L309 https://github.com/appsmithorg/appsmith/blob/release/app/client/src/pages/Editor/QueryEditor/Editor.tsx#L148-L156 Query ref: https://github.com/appsmithorg/appsmith/blob/release/app/client/src/pages/Editor/QueryEditor/Editor.tsx#L131-L137 > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="@tag.Datasource" ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No ## Summary by CodeRabbit - **New Features** - Introduced a new `withoutPadding` property for better control over section padding. - Added support for binary data uploads in the `PostBodyData` component. - New styled components created for enhanced layout management. - `httpsMethods` property added to the `CommonFormPropsWithExtraParams` interface. - **Bug Fixes** - Improved tooltip clarity for invalid data in the `ImportedKeyValue` component. - **Refactor** - Simplified component structures and styling, enhancing layout management across several components. - **Chores** - Removed unused imports and streamlined component logic in the `UQIEditorForm`. --- .../components/UQIEditor/UQIEditorForm.tsx | 33 +++++----- .../components/UQIEditor/hooks/useFormData.ts | 22 +++++++ .../useGoogleSheetsSetDefaultProperty.ts | 60 +++++++++++++++++++ .../UQIEditor/hooks/useInitFormEvaluation.ts | 21 +++++++ 4 files changed, 118 insertions(+), 18 deletions(-) create mode 100644 app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/hooks/useFormData.ts create mode 100644 app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/hooks/useGoogleSheetsSetDefaultProperty.ts create mode 100644 app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/hooks/useInitFormEvaluation.ts diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/UQIEditorForm.tsx b/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/UQIEditorForm.tsx index fdee4e73717..44750c91be8 100644 --- a/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/UQIEditorForm.tsx +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/UQIEditorForm.tsx @@ -2,36 +2,33 @@ import React from "react"; import FormRender from "./FormRender"; import { usePluginActionContext } from "../../../../PluginActionContext"; import { QUERY_EDITOR_FORM_NAME } from "ee/constants/forms"; -import { getFormValues, reduxForm } from "redux-form"; -import type { QueryAction, SaaSAction } from "entities/Action"; -import { useSelector } from "react-redux"; -import { getFormEvaluationState } from "selectors/formSelectors"; +import { reduxForm } from "redux-form"; import { Flex } from "@appsmith/ads"; +import { useGoogleSheetsSetDefaultProperty } from "./hooks/useGoogleSheetsSetDefaultProperty"; +import { useFormData } from "./hooks/useFormData"; +import { useInitFormEvaluation } from "./hooks/useInitFormEvaluation"; const UQIEditorForm = () => { - const { editorConfig, plugin } = usePluginActionContext(); + const { + editorConfig, + plugin: { uiComponent }, + } = usePluginActionContext(); - const formData = useSelector(getFormValues(QUERY_EDITOR_FORM_NAME)) as - | QueryAction - | SaaSAction; + useInitFormEvaluation(); - const formEvaluation = useSelector(getFormEvaluationState); + // Set default values for Google Sheets + useGoogleSheetsSetDefaultProperty(); - let formEvaluationState = {}; - - // Fetching evaluations state only once the formData is populated - if (!!formData) { - formEvaluationState = formEvaluation[formData.id]; - } + const { data, evaluationState } = useFormData(); return ( ); diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/hooks/useFormData.ts b/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/hooks/useFormData.ts new file mode 100644 index 00000000000..61f798cc093 --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/hooks/useFormData.ts @@ -0,0 +1,22 @@ +import { useSelector } from "react-redux"; +import { getFormValues } from "redux-form"; +import { QUERY_EDITOR_FORM_NAME } from "ee/constants/forms"; +import type { QueryAction, SaaSAction } from "entities/Action"; +import { getFormEvaluationState } from "selectors/formSelectors"; + +export const useFormData = () => { + const data = useSelector(getFormValues(QUERY_EDITOR_FORM_NAME)) as + | QueryAction + | SaaSAction; + + const formEvaluation = useSelector(getFormEvaluationState); + + let evaluationState = {}; + + // Fetching evaluations state only once the formData is populated + if (!!data) { + evaluationState = formEvaluation[data.id]; + } + + return { data, evaluationState }; +}; diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/hooks/useGoogleSheetsSetDefaultProperty.ts b/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/hooks/useGoogleSheetsSetDefaultProperty.ts new file mode 100644 index 00000000000..b888239e3aa --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/hooks/useGoogleSheetsSetDefaultProperty.ts @@ -0,0 +1,60 @@ +import { useEffect } from "react"; +import { PluginPackageName } from "entities/Action"; +import { merge } from "lodash"; +import { getConfigInitialValues } from "components/formControls/utils"; +import { diff, type Diff } from "deep-diff"; +import { getPathAndValueFromActionDiffObject } from "utils/getPathAndValueFromActionDiffObject"; +import { setActionProperty } from "actions/pluginActionActions"; +import { usePluginActionContext } from "../../../../../PluginActionContext"; +import { useDispatch } from "react-redux"; + +export const useGoogleSheetsSetDefaultProperty = () => { + const { + action, + editorConfig, + plugin: { packageName }, + settingsConfig, + } = usePluginActionContext(); + + const dispatch = useDispatch(); + + useEffect( + function setDefaultValuesForGoogleSheets() { + if (packageName === PluginPackageName.GOOGLE_SHEETS) { + const initialValues = {}; + + merge( + initialValues, + getConfigInitialValues(editorConfig as Record[]), + ); + + merge( + initialValues, + getConfigInitialValues(settingsConfig as Record[]), + ); + + // initialValues contains merge of action, editorConfig, settingsConfig and will be passed to redux form + merge(initialValues, action); + + // @ts-expect-error: Types are not available + const actionObjectDiff: undefined | Diff[] = + diff(action, initialValues); + + const { path = "", value = "" } = { + ...getPathAndValueFromActionDiffObject(actionObjectDiff), + }; + + if (value && path) { + dispatch( + setActionProperty({ + actionId: action.id, + propertyName: path, + value, + }), + ); + } + } + }, + [action, dispatch, editorConfig, packageName, settingsConfig], + ); +}; diff --git a/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/hooks/useInitFormEvaluation.ts b/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/hooks/useInitFormEvaluation.ts new file mode 100644 index 00000000000..5c52cdfa89d --- /dev/null +++ b/app/client/src/PluginActionEditor/components/PluginActionForm/components/UQIEditor/hooks/useInitFormEvaluation.ts @@ -0,0 +1,21 @@ +import { useEffect } from "react"; +import { initFormEvaluations } from "actions/evaluationActions"; +import { useDispatch } from "react-redux"; +import { usePluginActionContext } from "../../../../../PluginActionContext"; + +export const useInitFormEvaluation = () => { + const dispatch = useDispatch(); + + const { + action: { baseId }, + editorConfig, + settingsConfig, + } = usePluginActionContext(); + + useEffect( + function formEvaluationInit() { + dispatch(initFormEvaluations(editorConfig, settingsConfig, baseId)); + }, + [baseId, dispatch, editorConfig, settingsConfig], + ); +}; From 4e188275125ea6f67d959381302eda6283da374e Mon Sep 17 00:00:00 2001 From: Trisha Anand Date: Tue, 5 Nov 2024 19:19:26 +0530 Subject: [PATCH 4/7] fix: Only trigger restart if current user is an instance admin. (#37227) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description > [!TIP] > _Add a TL;DR when the description is longer than 500 words or extremely technical (helps the content, marketing, and DevRel team)._ > > _Please also include relevant motivation and context. List any dependencies that are required for this change. Add links to Notion, Figma or any other documents that might be relevant to the PR._ Fixes #`Issue Number` _or_ Fixes `Issue URL` > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="@tag.Sanity" ### :mag: Cypress test results > [!TIP] > 🟒 🟒 🟒 All cypress tests have passed! πŸŽ‰ πŸŽ‰ πŸŽ‰ > Workflow run: > Commit: 4fa5ebcd78bc8206f9ce3ac5641d9e0605df25f0 > Cypress dashboard. > Tags: `@tag.Sanity` > Spec: >
Tue, 05 Nov 2024 10:24:09 UTC ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [ ] No ## Summary by CodeRabbit - **New Features** - Enhanced user context processing during the restart operation. - Improved error handling and configuration for sending test emails. - **Bug Fixes** - Addressed issues related to connection testing and email sending with detailed error messages. --- .../java/com/appsmith/server/solutions/ce/EnvManagerCEImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/EnvManagerCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/EnvManagerCEImpl.java index e21a319193c..af74b26950e 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/EnvManagerCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/EnvManagerCEImpl.java @@ -725,7 +725,7 @@ public Mono verifyCurrentUserIsSuper() { @Override public Mono restart() { - return verifyCurrentUserIsSuper().then(restartWithoutAclCheck()); + return verifyCurrentUserIsSuper().flatMap(user -> restartWithoutAclCheck()); } /** From 0288f5b9efd963bcd000e85bcfcfa23c0eedbbd5 Mon Sep 17 00:00:00 2001 From: Rahul Barwal Date: Wed, 6 Nov 2024 10:27:57 +0530 Subject: [PATCH 5/7] fix: Enhance URL handling in table by rendering URL column types with `` tag. (#37179) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Problem URLs in table were not being rendered as links, resulting in inconsistent user experience(missing context menus. Root cause URLs were rendered in `
` instead of ``, making the component lack links related features.. Solution This PR handles... - Rendering URLs as links in BasicCell for a better user experience. - Adding specific types for column properties for more robust data validation and type checking. - Adding unit tests for BasicCell functionality to ensure accurate rendering and behavior. - Simplifies the AutoToolTipComponent by removing unncessary `LinkWrapper` component Fixes #24769 _or_ Fixes `Issue URL` > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="@tag.Table" ### :mag: Cypress test results > [!TIP] > 🟒 🟒 🟒 All cypress tests have passed! πŸŽ‰ πŸŽ‰ πŸŽ‰ > Workflow run: > Commit: b7c5d176b35407923a120bb19e40252e3a61b628 > Cypress dashboard. > Tags: `@tag.Table` > Spec: >
Tue, 05 Nov 2024 10:23:38 UTC ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [x] No ## Summary by CodeRabbit ## Release Notes - **New Features** - Enhanced type safety for `compactMode` and `columnType` properties across various components. - Improved rendering logic in the `AutoToolTipComponent` for better control based on `columnType`. - Optimized rendering in the `BasicCell` component using `useMemo`. - **Bug Fixes** - Resolved inconsistencies in type definitions for `BasicCell`, `PlainTextCell`, and `SelectCell` components. - Updated tooltip behavior in the `AutoToolTipComponent` to ensure accurate rendering. - **Tests** - Introduced a new test suite for the `BasicCell` component, ensuring proper rendering and interaction behaviors. - Refined test cases for the `AutoToolTipComponent` to verify accurate rendering under various conditions. - Updated test case for URL column verification to check attributes directly instead of navigation. --- .../TableV2/TableV2_Url_Column_spec.ts | 29 +++++---- .../TableWidgetV2/component/Constants.ts | 2 +- .../cellComponents/AutoToolTipComponent.tsx | 62 +++++++------------ .../AutoTooltipComponent.test.tsx | 21 +------ .../cellComponents/BasicCell.test.tsx | 56 +++++++++++++++++ .../component/cellComponents/BasicCell.tsx | 25 ++++++-- .../cellComponents/PlainTextCell.tsx | 2 +- .../component/cellComponents/SelectCell.tsx | 3 +- 8 files changed, 120 insertions(+), 80 deletions(-) create mode 100644 app/client/src/widgets/TableWidgetV2/component/cellComponents/BasicCell.test.tsx diff --git a/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/TableV2_Url_Column_spec.ts b/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/TableV2_Url_Column_spec.ts index f866def606d..e906d531598 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/TableV2_Url_Column_spec.ts +++ b/app/client/cypress/e2e/Regression/ClientSide/Widgets/TableV2/TableV2_Url_Column_spec.ts @@ -20,18 +20,23 @@ describe( table.ReadTableRowColumnData(3, 0, "v2").then(($cellData) => { expect($cellData).to.eq("Profile pic"); }); - table.AssertURLColumnNavigation( - 0, - 0, - "https://randomuser.me/api/portraits/med/women/39.jpg", - "v2", - ); - table.AssertURLColumnNavigation( - 3, - 0, - "https://randomuser.me/api/portraits/med/men/52.jpg", - "v2", - ); + + agHelper + .GetElement(`${table._tableRowColumnData(0, 0, "v2")} a`) + .should( + "have.attr", + "href", + "https://randomuser.me/api/portraits/med/women/39.jpg", + ) + .should("have.attr", "target", "_blank"); + agHelper + .GetElement(`${table._tableRowColumnData(3, 0, "v2")} a`) + .should( + "have.attr", + "href", + "https://randomuser.me/api/portraits/med/men/52.jpg", + ) + .should("have.attr", "target", "_blank"); }); }, ); diff --git a/app/client/src/widgets/TableWidgetV2/component/Constants.ts b/app/client/src/widgets/TableWidgetV2/component/Constants.ts index c4a3d0eceb5..182eb402073 100644 --- a/app/client/src/widgets/TableWidgetV2/component/Constants.ts +++ b/app/client/src/widgets/TableWidgetV2/component/Constants.ts @@ -546,7 +546,7 @@ export enum IMAGE_VERTICAL_ALIGN { } export interface BaseCellComponentProps { - compactMode: string; + compactMode: CompactMode; isHidden: boolean; allowCellWrapping?: boolean; horizontalAlignment?: CellAlignment; diff --git a/app/client/src/widgets/TableWidgetV2/component/cellComponents/AutoToolTipComponent.tsx b/app/client/src/widgets/TableWidgetV2/component/cellComponents/AutoToolTipComponent.tsx index 86b3c41caec..8b9d30e99e6 100644 --- a/app/client/src/widgets/TableWidgetV2/component/cellComponents/AutoToolTipComponent.tsx +++ b/app/client/src/widgets/TableWidgetV2/component/cellComponents/AutoToolTipComponent.tsx @@ -137,7 +137,7 @@ interface Props { children: React.ReactNode; title: string; tableWidth?: number; - columnType?: string; + columnType?: ColumnTypes; className?: string; compactMode?: string; allowCellWrapping?: boolean; @@ -152,38 +152,6 @@ interface Props { isCellDisabled?: boolean; } -function LinkWrapper(props: Props) { - const content = useToolTip(props.children, props.title); - - return ( - ) => { - e.stopPropagation(); - window.open(props.url, "_blank"); - }} - textColor={props.textColor} - textSize={props.textSize} - verticalAlignment={props.verticalAlignment} - > -
{content}
- - - -
- ); -} - export function AutoToolTipComponent(props: Props) { const content = useToolTip( props.children, @@ -191,12 +159,27 @@ export function AutoToolTipComponent(props: Props) { props.columnType === ColumnTypes.BUTTON, ); - if (props.columnType === ColumnTypes.URL && props.title) { - return ; - } + let contentToRender; + + switch (props.columnType) { + case ColumnTypes.BUTTON: + if (props.title) { + return content; + } - if (props.columnType === ColumnTypes.BUTTON && props.title) { - return content; + break; + case ColumnTypes.URL: + contentToRender = ( + <> +
{content}
+ + + + + ); + break; + default: + contentToRender = content; } return ( @@ -212,12 +195,13 @@ export function AutoToolTipComponent(props: Props) { isCellDisabled={props.isCellDisabled} isCellVisible={props.isCellVisible} isHidden={props.isHidden} + isHyperLink={props.columnType === ColumnTypes.URL} isTextType textColor={props.textColor} textSize={props.textSize} verticalAlignment={props.verticalAlignment} > - {content} + {contentToRender} ); diff --git a/app/client/src/widgets/TableWidgetV2/component/cellComponents/AutoTooltipComponent.test.tsx b/app/client/src/widgets/TableWidgetV2/component/cellComponents/AutoTooltipComponent.test.tsx index 8a9a5721c71..bf46ff76952 100644 --- a/app/client/src/widgets/TableWidgetV2/component/cellComponents/AutoTooltipComponent.test.tsx +++ b/app/client/src/widgets/TableWidgetV2/component/cellComponents/AutoTooltipComponent.test.tsx @@ -54,7 +54,7 @@ test("does not show tooltip for non-button types", () => { test("handles empty tooltip", () => { const { getByText } = render( - + , ); @@ -86,25 +86,6 @@ test("does not show tooltip for non-truncated text", () => { expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); }); -test("opens a new tab for URL column type when clicked", () => { - const openSpy = jest.spyOn(window, "open").mockImplementation(() => null); - - render( - - Go to Google - , - ); - - fireEvent.click(screen.getByText("Go to Google")); - expect(openSpy).toHaveBeenCalledWith("https://www.google.com", "_blank"); - - openSpy.mockRestore(); -}); - describe("isButtonTextTruncated", () => { function mockElementWidths( offsetWidth: number, diff --git a/app/client/src/widgets/TableWidgetV2/component/cellComponents/BasicCell.test.tsx b/app/client/src/widgets/TableWidgetV2/component/cellComponents/BasicCell.test.tsx new file mode 100644 index 00000000000..ee37e656b95 --- /dev/null +++ b/app/client/src/widgets/TableWidgetV2/component/cellComponents/BasicCell.test.tsx @@ -0,0 +1,56 @@ +import React from "react"; +import { render, screen, fireEvent } from "@testing-library/react"; +import "@testing-library/jest-dom"; +import { BasicCell, type PropType } from "./BasicCell"; +import { ColumnTypes } from "widgets/TableWidgetV2/constants"; +import { CompactModeTypes } from "widgets/TableWidget/component/Constants"; + +describe("BasicCell Component", () => { + const defaultProps: PropType = { + value: "Test Value", + onEdit: jest.fn(), + isCellEditable: false, + hasUnsavedChanges: false, + columnType: ColumnTypes.TEXT, + url: "", + compactMode: CompactModeTypes.DEFAULT, + isHidden: false, + isCellVisible: true, + accentColor: "", + tableWidth: 100, + disabledEditIcon: false, + disabledEditIconMessage: "", + }; + + it("renders the value", () => { + render(); + expect(screen.getByText("Test Value")).toBeInTheDocument(); + }); + + it("renders a link when columnType is URL", () => { + render( + , + ); + const link = screen.getByText("Test Value"); + + expect(link).toBeInTheDocument(); + expect(link).toHaveAttribute("href", "http://example.com"); + }); + + it("calls onEdit when double-clicked", () => { + render(); + fireEvent.doubleClick(screen.getByText("Test Value")); + expect(defaultProps.onEdit).toHaveBeenCalled(); + }); + + it("forwards ref to the div element", () => { + const ref = React.createRef(); + + render(); + expect(ref.current).toBeInstanceOf(HTMLDivElement); + }); +}); diff --git a/app/client/src/widgets/TableWidgetV2/component/cellComponents/BasicCell.tsx b/app/client/src/widgets/TableWidgetV2/component/cellComponents/BasicCell.tsx index 08d9efe8901..9b2431cf860 100644 --- a/app/client/src/widgets/TableWidgetV2/component/cellComponents/BasicCell.tsx +++ b/app/client/src/widgets/TableWidgetV2/component/cellComponents/BasicCell.tsx @@ -1,12 +1,13 @@ import type { Ref } from "react"; -import React, { useCallback } from "react"; +import React, { useCallback, useMemo } from "react"; import { Tooltip } from "@blueprintjs/core"; import styled from "styled-components"; -import type { BaseCellComponentProps } from "../Constants"; +import type { BaseCellComponentProps, CompactMode } from "../Constants"; import { TABLE_SIZES } from "../Constants"; import { TooltipContentWrapper } from "../TableStyledWrappers"; import AutoToolTipComponent from "./AutoToolTipComponent"; import { importSvg } from "@appsmith/ads-old"; +import { ColumnTypes } from "widgets/TableWidgetV2/constants"; const EditIcon = importSvg( async () => import("assets/icons/control/edit-variant1.svg"), @@ -55,7 +56,7 @@ const Content = styled.div` const StyledEditIcon = styled.div<{ accentColor?: string; backgroundColor?: string; - compactMode: string; + compactMode: CompactMode; disabledEditIcon: boolean; }>` position: absolute; @@ -74,12 +75,12 @@ const StyledEditIcon = styled.div<{ } `; -type PropType = BaseCellComponentProps & { +export type PropType = BaseCellComponentProps & { accentColor: string; // TODO: Fix this the next time the file is edited // eslint-disable-next-line @typescript-eslint/no-explicit-any value: any; - columnType: string; + columnType: ColumnTypes; tableWidth: number; isCellEditable?: boolean; isCellEditMode?: boolean; @@ -128,6 +129,18 @@ export const BasicCell = React.forwardRef( }, [onEdit, disabledEditIcon, isCellEditable], ); + const contentToRender = useMemo(() => { + switch (columnType) { + case ColumnTypes.URL: + return ( + + {value} + + ); + default: + return value; + } + }, [columnType, url, value]); return ( - {value} + {contentToRender} {isCellEditable && ( Date: Wed, 6 Nov 2024 10:17:22 +0300 Subject: [PATCH 6/7] fix: remove gap between tabs and list (#37242) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Remove a small gap that revealed a slice of action bar. Fixes #37220 ## Automation /ok-to-test tags="@tag.IDE" ### :mag: Cypress test results > [!TIP] > 🟒 🟒 🟒 All cypress tests have passed! πŸŽ‰ πŸŽ‰ πŸŽ‰ > Workflow run: > Commit: 87b7ed85bb9e45820839f495471a3a9ba1dffec9 > Cypress dashboard. > Tags: `@tag.IDE` > Spec: >
Tue, 05 Nov 2024 17:13:44 UTC ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [x] No ## Summary by CodeRabbit - **Style** - Updated the positioning and padding of the List component for improved layout. - Adjusted the styling of the ListContainer for better visual alignment. --- app/client/src/pages/Editor/IDE/EditorTabs/List.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/client/src/pages/Editor/IDE/EditorTabs/List.tsx b/app/client/src/pages/Editor/IDE/EditorTabs/List.tsx index 0884b47f778..e73b42762e1 100644 --- a/app/client/src/pages/Editor/IDE/EditorTabs/List.tsx +++ b/app/client/src/pages/Editor/IDE/EditorTabs/List.tsx @@ -8,6 +8,9 @@ import ListQuery from "../EditorPane/Query/List"; import ListJSObjects from "../EditorPane/JS/List"; const ListContainer = styled(Flex)` + position: absolute; + top: 32px; + padding-top: 4px; & .t--entity-item { grid-template-columns: 0 auto 1fr auto auto auto auto auto; height: 32px; @@ -24,7 +27,6 @@ export const List = () => { return ( Date: Wed, 6 Nov 2024 12:55:11 +0530 Subject: [PATCH 7/7] fix: skipped unnecessary code for js object updates (#37125) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR is in continuation to [PR](https://github.com/appsmithorg/appsmith/pull/37062) which we had merged earlier, In previous, we skipped the redundant calls to update page layout when updating each js object action, as we already have a call for [updating the page layout for actionCollection](https://github.com/appsmithorg/appsmith/blob/27bdeb92b6a0b27e6afcbfe80a5cb0705c0812ac/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/LayoutCollectionServiceCEImpl.java#L411) Since we have skipped the update page layout for each js action, we no longer need the code part after this, which basically fetches page data from DB and updates the `errorReports` in actionDTO based on layout `layoutOnLoadActionErrors`. This PR skips this unnecessary part too for each js action as we do [set the errorReport for actionCollection in the end](https://github.com/appsmithorg/appsmith/blob/27bdeb92b6a0b27e6afcbfe80a5cb0705c0812ac/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/LayoutCollectionServiceCEImpl.java#L430) ### Will this have any impact on error messages shown to user? In order to understand this, checked out the frontend code to see if errorReports from individual js action is getting consumed on updating js object, looks like [it is not](https://github.com/appsmithorg/appsmith/blob/e7e3d5e00290919c1df0767fdefed67458ec3cc9/app/client/src/sagas/JSPaneSagas.ts#L316), so we can safely remove this piece of code. However this points to existing bug in the code (as errorReports is not even getting consumed from actionCollection), which is, when there is cyclic dependency created for js object with a widget, we don't get any toast message. Since this is existing issue which is not caused by any of the above PR implementations, creating separate issue for it and tracking it [here](https://github.com/appsmithorg/appsmith/issues/37129) Fixes #37114 _or_ Fixes `Issue URL` > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="@tag.JS, @tag.JS" ### :mag: Cypress test results > [!TIP] > 🟒 🟒 🟒 All cypress tests have passed! πŸŽ‰ πŸŽ‰ πŸŽ‰ > Workflow run: > Commit: 9fbde996545451c1a12736e0b8a9252ad4ab69bd > Cypress dashboard. > Tags: `@tag.JS, @tag.JS` > Spec: >
Wed, 06 Nov 2024 06:50:05 UTC ## Communication Should the DevRel and Marketing teams inform users about this change? - [ ] Yes - [x] No ## Summary by CodeRabbit - **New Features** - Streamlined layout update process for actions, enhancing performance and clarity. - **Bug Fixes** - Improved test reliability by monitoring interactions with the layout service and handling cyclic dependencies. - **Documentation** - Updated comments to clarify the logic behind layout updates for JS actions. - **Tests** - Enhanced test descriptions and assertions for better clarity and validation of method interactions, including cyclic dependency scenarios. --------- Co-authored-by: β€œsneha122” <β€œsneha@appsmith.com”> --- .../ce/LayoutActionServiceCEImpl.java | 64 ++++++++++--------- .../services/ActionCollectionServiceTest.java | 45 +++++++++++-- 2 files changed, 74 insertions(+), 35 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/LayoutActionServiceCEImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/LayoutActionServiceCEImpl.java index a68913540f9..0efe0e9e4f3 100755 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/LayoutActionServiceCEImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/LayoutActionServiceCEImpl.java @@ -249,36 +249,40 @@ protected Mono updateActionBasedOnContextType(NewAction newAction, Ac String pageId = newAction.getUnpublishedAction().getPageId(); action.setApplicationId(null); action.setPageId(null); - return updateSingleAction(newAction.getId(), action) - .name(UPDATE_SINGLE_ACTION) - .tap(Micrometer.observation(observationRegistry)) - .flatMap(updatedAction -> { - // Update page layout is skipped for JS actions here because when JSobject is updated, we first - // update all actions, action - // collection and then we update the page layout, hence updating page layout with each action update - // is not required here - if (action.getPluginType() != PluginType.JS) { - return updateLayoutService - .updatePageLayoutsByPageId(pageId) - .name(UPDATE_PAGE_LAYOUT_BY_PAGE_ID) - .tap(Micrometer.observation(observationRegistry)) - .thenReturn(updatedAction); - } - return Mono.just(updatedAction); - }) - .zipWhen(actionDTO -> newPageService.findPageById(pageId, pagePermission.getEditPermission(), false)) - .map(tuple2 -> { - ActionDTO actionDTO = tuple2.getT1(); - PageDTO pageDTO = tuple2.getT2(); - // redundant check - if (pageDTO.getLayouts().size() > 0) { - actionDTO.setErrorReports(pageDTO.getLayouts().get(0).getLayoutOnLoadActionErrors()); - } - log.debug( - "Update action based on context type completed, returning actionDTO with action id: {}", - actionDTO != null ? actionDTO.getId() : null); - return actionDTO; - }); + + // Update page layout is skipped for JS actions here because when JSobject is updated, we first + // update all actions, action + // collection and then we update the page layout, hence updating page layout with each action update + // is not required here + if (action.getPluginType() == PluginType.JS) { + return updateSingleAction(newAction.getId(), action) + .name(UPDATE_SINGLE_ACTION) + .tap(Micrometer.observation(observationRegistry)); + } else { + return updateSingleAction(newAction.getId(), action) + .name(UPDATE_SINGLE_ACTION) + .tap(Micrometer.observation(observationRegistry)) + .flatMap(updatedAction -> updateLayoutService + .updatePageLayoutsByPageId(pageId) + .name(UPDATE_PAGE_LAYOUT_BY_PAGE_ID) + .tap(Micrometer.observation(observationRegistry)) + .thenReturn(updatedAction)) + .zipWhen( + actionDTO -> newPageService.findPageById(pageId, pagePermission.getEditPermission(), false)) + .map(tuple2 -> { + ActionDTO actionDTO = tuple2.getT1(); + PageDTO pageDTO = tuple2.getT2(); + // redundant check + if (pageDTO.getLayouts().size() > 0) { + actionDTO.setErrorReports( + pageDTO.getLayouts().get(0).getLayoutOnLoadActionErrors()); + } + log.debug( + "Update action based on context type completed, returning actionDTO with action id: {}", + actionDTO != null ? actionDTO.getId() : null); + return actionDTO; + }); + } } @Override diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ActionCollectionServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ActionCollectionServiceTest.java index f201b0a3bff..c21a0f49589 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ActionCollectionServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/ActionCollectionServiceTest.java @@ -26,6 +26,7 @@ import com.appsmith.server.dtos.PluginWorkspaceDTO; import com.appsmith.server.dtos.RefactorEntityNameDTO; import com.appsmith.server.dtos.WorkspacePluginStatus; +import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.helpers.MockPluginExecutor; import com.appsmith.server.helpers.PluginExecutorHelper; import com.appsmith.server.layouts.UpdateLayoutService; @@ -73,6 +74,7 @@ import static com.appsmith.server.constants.FieldName.VIEWER; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; @SpringBootTest @Slf4j @@ -708,7 +710,8 @@ public void testDeleteActionCollection_afterApplicationPublish_clearsActionColle @Test @WithUserDetails(value = "api_user") - public void testUpdateUnpublishedActionCollection_withValidCollection_callsPageLayoutOnlyOnce() { + public void + testUpdateUnpublishedActionCollection_withValidCollection_callsPageLayoutOnlyOnceAndAssertCyclicDependencyError() { Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(pluginExecutor)); Mockito.when(pluginExecutor.getHintMessages(Mockito.any(), Mockito.any())) .thenReturn(Mono.zip(Mono.just(new HashSet<>()), Mono.just(new HashSet<>()))); @@ -727,7 +730,7 @@ public void testUpdateUnpublishedActionCollection_withValidCollection_callsPageL ActionDTO action1 = new ActionDTO(); action1.setName("testAction1"); action1.setActionConfiguration(new ActionConfiguration()); - action1.getActionConfiguration().setBody("mockBody"); + action1.getActionConfiguration().setBody("initial body"); action1.getActionConfiguration().setIsValid(false); ActionDTO action2 = new ActionDTO(); @@ -744,15 +747,35 @@ public void testUpdateUnpublishedActionCollection_withValidCollection_callsPageL actionCollectionDTO.setActions(List.of(action1, action2, action3)); + Layout layout = testPage.getLayouts().get(0); + ArrayList dslList = (ArrayList) layout.getDsl().get("children"); + JSONObject tableDsl = (JSONObject) dslList.get(0); + tableDsl.put("tableData", "{{testCollection1.testAction1.data}}"); + JSONArray temp2 = new JSONArray(); + temp2.add(new JSONObject(Map.of("key", "tableData"))); + tableDsl.put("dynamicBindingPathList", temp2); + JSONArray temp3 = new JSONArray(); + temp3.add(new JSONObject(Map.of("key", "tableData"))); + tableDsl.put("dynamicPropertyPathList", temp3); + layout.getDsl().put("widgetName", "MainContainer"); + + testPage.setLayouts(List.of(layout)); + PageDTO updatedPage = + newPageService.updatePage(testPage.getId(), testPage).block(); + + // Create Js object ActionCollectionDTO createdActionCollectionDTO = layoutCollectionService.createCollection(actionCollectionDTO).block(); assert createdActionCollectionDTO != null; assert createdActionCollectionDTO.getId() != null; String createdActionCollectionId = createdActionCollectionDTO.getId(); - applicationPageService.publish(testApp.getId(), true).block(); - - actionCollectionDTO.getActions().get(0).getActionConfiguration().setBody("updatedBody"); + // Update JS object to create cyclic dependency + actionCollectionDTO.getActions().stream() + .filter(action -> "testAction1".equals(action.getName())) + .findFirst() + .ifPresent(action -> + action.getActionConfiguration().setBody("function () {\n return Table1.tableData;\n}")); final Mono updatedActionCollectionDTO = layoutCollectionService.updateUnpublishedActionCollection( @@ -766,6 +789,18 @@ public void testUpdateUnpublishedActionCollection_withValidCollection_callsPageL // collection as expected Mockito.verify(updateLayoutService, Mockito.times(2)) .updatePageLayoutsByPageId(Mockito.anyString()); + + assertEquals(1, actionCollectionDTO1.getErrorReports().size()); + assertEquals( + AppsmithError.CYCLICAL_DEPENDENCY_ERROR.getAppErrorCode(), + actionCollectionDTO1.getErrorReports().get(0).getCode()); + + // Iterate over each action and assert that errorReports is null as action collection already has + // error reports + // it's not required in each action + actionCollectionDTO + .getActions() + .forEach(action -> assertNull(action.getErrorReports(), "Error reports should be null")); }) .verifyComplete(); }