Merge branch 'release' into feat/13412-improve-import-app-flow-ui
This commit is contained in:
commit
7e5332ceb7
2
.github/config.json
vendored
2
.github/config.json
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,209 +1,256 @@
|
|||
{
|
||||
"dsl": {
|
||||
"widgetName": "MainContainer",
|
||||
"backgroundColor": "none",
|
||||
"rightColumn": 1270,
|
||||
"snapColumns": 64,
|
||||
"detachFromLayout": true,
|
||||
"widgetId": "0",
|
||||
"topRow": 0,
|
||||
"bottomRow": 820,
|
||||
"containerStyle": "none",
|
||||
"snapRows": 125,
|
||||
"parentRowSpace": 1,
|
||||
"type": "CANVAS_WIDGET",
|
||||
"canExtend": true,
|
||||
"version": 23,
|
||||
"minHeight": 830,
|
||||
"parentColumnSpace": 1,
|
||||
"dynamicTriggerPathList": [],
|
||||
"dynamicBindingPathList": [],
|
||||
"leftColumn": 0,
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Chart1",
|
||||
"rightColumn": 26,
|
||||
"allowScroll": false,
|
||||
"widgetId": "ypstklohw5",
|
||||
"topRow": 4,
|
||||
"bottomRow": 36,
|
||||
"parentRowSpace": 10,
|
||||
"isVisible": true,
|
||||
"type": "CHART_WIDGET",
|
||||
"version": 1,
|
||||
"parentId": "0",
|
||||
"isLoading": false,
|
||||
"chartData": {
|
||||
"3jzahcrorq": {
|
||||
"seriesName": "Sales",
|
||||
"data": [
|
||||
{
|
||||
"x": "Mon",
|
||||
"y": 10000
|
||||
},
|
||||
{
|
||||
"x": "Tue",
|
||||
"y": 12000
|
||||
},
|
||||
{
|
||||
"x": "Wed",
|
||||
"y": 32000
|
||||
},
|
||||
{
|
||||
"x": "Thu",
|
||||
"y": 28000
|
||||
},
|
||||
{
|
||||
"x": "Fri",
|
||||
"y": 14000
|
||||
},
|
||||
{
|
||||
"x": "Sat",
|
||||
"y": 19000
|
||||
},
|
||||
{
|
||||
"x": "Sun",
|
||||
"y": 36000
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"yAxisName": "Total Order Revenue $",
|
||||
"parentColumnSpace": 19.65625,
|
||||
"chartName": "Last week's revenue",
|
||||
"leftColumn": 2,
|
||||
"xAxisName": "Last Week",
|
||||
"customFusionChartConfig": {
|
||||
"type": "column2d",
|
||||
"dataSource": {
|
||||
"chart": {
|
||||
"caption": "Last week's revenue",
|
||||
"xAxisName": "Last Week",
|
||||
"yAxisName": "Total Order Revenue $",
|
||||
"theme": "fusion"
|
||||
"dsl": {
|
||||
"widgetName": "MainContainer",
|
||||
"backgroundColor": "none",
|
||||
"rightColumn": 1270,
|
||||
"snapColumns": 64,
|
||||
"detachFromLayout": true,
|
||||
"widgetId": "0",
|
||||
"topRow": 0,
|
||||
"bottomRow": 1050,
|
||||
"containerStyle": "none",
|
||||
"snapRows": 125,
|
||||
"parentRowSpace": 1,
|
||||
"type": "CANVAS_WIDGET",
|
||||
"canExtend": true,
|
||||
"version": 58,
|
||||
"minHeight": 830,
|
||||
"parentColumnSpace": 1,
|
||||
"dynamicTriggerPathList": [],
|
||||
"dynamicBindingPathList": [],
|
||||
"leftColumn": 0,
|
||||
"children": [
|
||||
{
|
||||
"labelTextSize": "0.875rem",
|
||||
"boxShadow": "none",
|
||||
"widgetName": "Chart1",
|
||||
"rightColumn": 26,
|
||||
"widgetId": "ypstklohw5",
|
||||
"topRow": 4,
|
||||
"bottomRow": 36,
|
||||
"parentRowSpace": 10,
|
||||
"isVisible": true,
|
||||
"type": "CHART_WIDGET",
|
||||
"version": 1,
|
||||
"parentId": "0",
|
||||
"isLoading": false,
|
||||
"chartData": {
|
||||
"3jzahcrorq": {
|
||||
"seriesName": "Sales",
|
||||
"data": [
|
||||
{
|
||||
"x": "Mon",
|
||||
"y": 10000
|
||||
},
|
||||
"data": [
|
||||
{
|
||||
"label": "Mon",
|
||||
"value": 10000
|
||||
},
|
||||
{
|
||||
"label": "Tue",
|
||||
"value": 12000
|
||||
},
|
||||
{
|
||||
"label": "Wed",
|
||||
"value": 32000
|
||||
},
|
||||
{
|
||||
"label": "Thu",
|
||||
"value": 28000
|
||||
},
|
||||
{
|
||||
"label": "Fri",
|
||||
"value": 14000
|
||||
},
|
||||
{
|
||||
"label": "Sat",
|
||||
"value": 19000
|
||||
},
|
||||
{
|
||||
"label": "Sun",
|
||||
"value": 36000
|
||||
}
|
||||
],
|
||||
"trendlines": [
|
||||
{
|
||||
"line": [
|
||||
{
|
||||
"startvalue": "38000",
|
||||
"valueOnRight": "1",
|
||||
"displayvalue": "Weekly Target"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"chartType": "LINE_CHART"
|
||||
{
|
||||
"x": "Tue",
|
||||
"y": 12000
|
||||
},
|
||||
{
|
||||
"x": "Wed",
|
||||
"y": 32000
|
||||
},
|
||||
{
|
||||
"x": "Thu",
|
||||
"y": 28000
|
||||
},
|
||||
{
|
||||
"x": "Fri",
|
||||
"y": 14000
|
||||
},
|
||||
{
|
||||
"x": "Sat",
|
||||
"y": 19000
|
||||
},
|
||||
{
|
||||
"x": "Sun",
|
||||
"y": 36000
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"backgroundColor": "#FFFFFF",
|
||||
"widgetName": "Container1",
|
||||
"rightColumn": 62,
|
||||
"widgetId": "6y21iarlp4",
|
||||
"containerStyle": "card",
|
||||
"topRow": 11,
|
||||
"bottomRow": 51,
|
||||
"parentRowSpace": 10,
|
||||
"isVisible": true,
|
||||
"type": "CONTAINER_WIDGET",
|
||||
"version": 1,
|
||||
"parentId": "0",
|
||||
"isLoading": false,
|
||||
"parentColumnSpace": 19.65625,
|
||||
"leftColumn": 30,
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Canvas1",
|
||||
"rightColumn": 629,
|
||||
"detachFromLayout": true,
|
||||
"widgetId": "jqasr1uss5",
|
||||
"containerStyle": "none",
|
||||
"topRow": 0,
|
||||
"bottomRow": 400,
|
||||
"parentRowSpace": 1,
|
||||
"isVisible": true,
|
||||
"canExtend": false,
|
||||
"type": "CANVAS_WIDGET",
|
||||
"version": 1,
|
||||
"parentId": "6y21iarlp4",
|
||||
"minHeight": 400,
|
||||
"isLoading": false,
|
||||
"parentColumnSpace": 1,
|
||||
"leftColumn": 0,
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
"yAxisName": "Total Order Revenue $",
|
||||
"parentColumnSpace": 19.65625,
|
||||
"chartName": "Last week's revenue",
|
||||
"leftColumn": 2,
|
||||
"borderRadius": "0px",
|
||||
"xAxisName": "Last Week",
|
||||
"customFusionChartConfig": {
|
||||
"type": "column2d",
|
||||
"dataSource": {
|
||||
"chart": {
|
||||
"caption": "Last week's revenue",
|
||||
"xAxisName": "Last Week",
|
||||
"yAxisName": "Total Order Revenue $",
|
||||
"theme": "fusion"
|
||||
},
|
||||
"data": [
|
||||
{
|
||||
"label": "Mon",
|
||||
"value": 10000
|
||||
},
|
||||
{
|
||||
"label": "Tue",
|
||||
"value": 12000
|
||||
},
|
||||
{
|
||||
"label": "Wed",
|
||||
"value": 32000
|
||||
},
|
||||
{
|
||||
"label": "Thu",
|
||||
"value": 28000
|
||||
},
|
||||
{
|
||||
"label": "Fri",
|
||||
"value": 14000
|
||||
},
|
||||
{
|
||||
"label": "Sat",
|
||||
"value": 19000
|
||||
},
|
||||
{
|
||||
"label": "Sun",
|
||||
"value": 36000
|
||||
}
|
||||
],
|
||||
"trendlines": [
|
||||
{
|
||||
"line": [
|
||||
{
|
||||
"startvalue": "38000",
|
||||
"valueOnRight": "1",
|
||||
"displayvalue": "Weekly Target"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"image": "",
|
||||
"widgetName": "Image1",
|
||||
"rightColumn": 22,
|
||||
"widgetId": "1t50avy6f1",
|
||||
"topRow": 58,
|
||||
"bottomRow": 70,
|
||||
"parentRowSpace": 10,
|
||||
"isVisible": true,
|
||||
"type": "IMAGE_WIDGET",
|
||||
"version": 1,
|
||||
"parentId": "0",
|
||||
"isLoading": false,
|
||||
"maxZoomLevel": 1,
|
||||
"parentColumnSpace": 19.65625,
|
||||
"imageShape": "RECTANGLE",
|
||||
"leftColumn": 6,
|
||||
"defaultImage": "https://res.cloudinary.com/drako999/image/upload/v1589196259/default.png"
|
||||
},
|
||||
{
|
||||
"widgetName": "Button1",
|
||||
"rightColumn": 18,
|
||||
"isDefaultClickDisabled": true,
|
||||
"widgetId": "41wgbhd5vp",
|
||||
"buttonStyle": "PRIMARY_BUTTON",
|
||||
"topRow": 44,
|
||||
"bottomRow": 48,
|
||||
"parentRowSpace": 10,
|
||||
"isVisible": true,
|
||||
"type": "BUTTON_WIDGET",
|
||||
"version": 1,
|
||||
"parentId": "0",
|
||||
"isLoading": false,
|
||||
"parentColumnSpace": 19.65625,
|
||||
"leftColumn": 10,
|
||||
"text": "Submit",
|
||||
"isDisabled": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
"chartType": "LINE_CHART"
|
||||
},
|
||||
{
|
||||
"labelTextSize": "0.875rem",
|
||||
"boxShadow": "none",
|
||||
"backgroundColor": "#FFFFFF",
|
||||
"widgetName": "Container1",
|
||||
"rightColumn": 62,
|
||||
"widgetId": "6y21iarlp4",
|
||||
"containerStyle": "card",
|
||||
"topRow": 11,
|
||||
"bottomRow": 51,
|
||||
"parentRowSpace": 10,
|
||||
"isVisible": true,
|
||||
"type": "CONTAINER_WIDGET",
|
||||
"version": 1,
|
||||
"parentId": "0",
|
||||
"isLoading": false,
|
||||
"parentColumnSpace": 19.65625,
|
||||
"leftColumn": 30,
|
||||
"borderRadius": "0px",
|
||||
"children": [
|
||||
{
|
||||
"labelTextSize": "0.875rem",
|
||||
"boxShadow": "none",
|
||||
"widgetName": "Canvas1",
|
||||
"rightColumn": 629,
|
||||
"detachFromLayout": true,
|
||||
"widgetId": "jqasr1uss5",
|
||||
"containerStyle": "none",
|
||||
"topRow": 0,
|
||||
"bottomRow": 400,
|
||||
"parentRowSpace": 1,
|
||||
"isVisible": true,
|
||||
"canExtend": false,
|
||||
"type": "CANVAS_WIDGET",
|
||||
"version": 1,
|
||||
"parentId": "6y21iarlp4",
|
||||
"minHeight": 400,
|
||||
"isLoading": false,
|
||||
"parentColumnSpace": 1,
|
||||
"leftColumn": 0,
|
||||
"borderRadius": "0px",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"labelTextSize": "0.875rem",
|
||||
"image": "",
|
||||
"boxShadow": "none",
|
||||
"widgetName": "Image1",
|
||||
"rightColumn": 22,
|
||||
"widgetId": "1t50avy6f1",
|
||||
"topRow": 58,
|
||||
"bottomRow": 70,
|
||||
"parentRowSpace": 10,
|
||||
"isVisible": true,
|
||||
"type": "IMAGE_WIDGET",
|
||||
"version": 1,
|
||||
"parentId": "0",
|
||||
"isLoading": false,
|
||||
"maxZoomLevel": 1,
|
||||
"parentColumnSpace": 19.65625,
|
||||
"imageShape": "RECTANGLE",
|
||||
"leftColumn": 6,
|
||||
"borderRadius": "0px",
|
||||
"defaultImage": "https://res.cloudinary.com/drako999/image/upload/v1589196259/default.png"
|
||||
},
|
||||
{
|
||||
"labelTextSize": "0.875rem",
|
||||
"boxShadow": "none",
|
||||
"widgetName": "Button1",
|
||||
"rightColumn": 18,
|
||||
"isDefaultClickDisabled": true,
|
||||
"buttonColor": "#03B365",
|
||||
"widgetId": "41wgbhd5vp",
|
||||
"topRow": 44,
|
||||
"bottomRow": 48,
|
||||
"parentRowSpace": 10,
|
||||
"isVisible": true,
|
||||
"type": "BUTTON_WIDGET",
|
||||
"version": 1,
|
||||
"recaptchaType": "V3",
|
||||
"parentId": "0",
|
||||
"isLoading": false,
|
||||
"parentColumnSpace": 19.65625,
|
||||
"leftColumn": 10,
|
||||
"borderRadius": "0px",
|
||||
"buttonVariant": "PRIMARY",
|
||||
"text": "Submit",
|
||||
"isDisabled": false
|
||||
},
|
||||
{
|
||||
"boxShadow": "none",
|
||||
"widgetName": "Camera1",
|
||||
"isCanvas": false,
|
||||
"displayName": "Camera",
|
||||
"iconSVG": "/static/media/icon.79c0d6de.svg",
|
||||
"topRow": 70,
|
||||
"bottomRow": 103,
|
||||
"parentRowSpace": 10,
|
||||
"type": "CAMERA_WIDGET",
|
||||
"hideCard": false,
|
||||
"mode": "CAMERA",
|
||||
"parentColumnSpace": 12.5625,
|
||||
"leftColumn": 2,
|
||||
"dynamicBindingPathList": [
|
||||
{
|
||||
"key": "borderRadius"
|
||||
}
|
||||
],
|
||||
"isDisabled": false,
|
||||
"key": "bybcx1x9lk",
|
||||
"isMirrored": true,
|
||||
"rightColumn": 27,
|
||||
"widgetId": "wv1qtmzsbm",
|
||||
"isVisible": true,
|
||||
"version": 1,
|
||||
"parentId": "0",
|
||||
"renderMode": "CANVAS",
|
||||
"isLoading": false,
|
||||
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -9,9 +9,8 @@ let homePage = ObjectsRegistry.HomePage,
|
|||
jsEditor = ObjectsRegistry.JSEditor,
|
||||
locator = ObjectsRegistry.CommonLocators;
|
||||
|
||||
describe("AForce - Community Issues page validations", function () {
|
||||
|
||||
before(function () {
|
||||
describe("AForce - Community Issues page validations", function() {
|
||||
before(function() {
|
||||
agHelper.clearLocalStorageCache();
|
||||
});
|
||||
|
||||
|
|
@ -28,231 +27,267 @@ describe("AForce - Community Issues page validations", function () {
|
|||
cy.visit("/applications");
|
||||
homePage.ImportApp("CommunityIssuesExport.json");
|
||||
cy.wait("@importNewApplication").then((interception: any) => {
|
||||
agHelper.Sleep()
|
||||
agHelper.Sleep();
|
||||
const { isPartialImport } = interception.response.body.data;
|
||||
if (isPartialImport) {
|
||||
// should reconnect modal
|
||||
dataSources.ReconnectDataSourcePostgres("AForceDB")
|
||||
dataSources.ReconnectDataSourcePostgres("AForceDB");
|
||||
} else {
|
||||
homePage.AssertImport()
|
||||
homePage.AssertImport();
|
||||
}
|
||||
//Validate table is not empty!
|
||||
table.WaitUntilTableLoad()
|
||||
table.WaitUntilTableLoad();
|
||||
//Validating order of header columns!
|
||||
table.AssertTableHeaderOrder("TypeTitleStatus+1CommentorsVotesAnswerUpVoteStatesupvote_ididgithub_issue_idauthorcreated_atdescriptionlabelsstatelinkupdated_at")
|
||||
table.AssertTableHeaderOrder(
|
||||
"TypeTitleStatus+1CommentorsVotesAnswerUpVoteStatesupvote_ididgithub_issue_idauthorcreated_atdescriptionlabelsstatelinkupdated_at",
|
||||
);
|
||||
//Validating hidden columns:
|
||||
table.AssertHiddenColumns(['States', 'upvote_id', 'id', 'github_issue_id', 'author', 'created_at', 'description', 'labels', 'state', 'link', 'updated_at'])
|
||||
table.AssertHiddenColumns([
|
||||
"States",
|
||||
"upvote_id",
|
||||
"id",
|
||||
"github_issue_id",
|
||||
"author",
|
||||
"created_at",
|
||||
"description",
|
||||
"labels",
|
||||
"state",
|
||||
"link",
|
||||
"updated_at",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it("2. Validate table navigation with Server Side pagination enabled with Default selected row", () => {
|
||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
||||
agHelper.AssertExistingToggleState("serversidepagination", 'checked')
|
||||
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||
agHelper.AssertExistingToggleState("serversidepagination", "checked");
|
||||
|
||||
agHelper.EvaluateExistingPropertyFieldValue("Default Selected Row")
|
||||
.then($selectedRow => {
|
||||
agHelper
|
||||
.EvaluateExistingPropertyFieldValue("Default Selected Row")
|
||||
.then(($selectedRow) => {
|
||||
selectedRow = Number($selectedRow);
|
||||
table.AssertSelectedRow(selectedRow)
|
||||
table.AssertSelectedRow(selectedRow);
|
||||
});
|
||||
|
||||
agHelper.DeployApp()
|
||||
table.WaitUntilTableLoad()
|
||||
agHelper.DeployApp();
|
||||
table.WaitUntilTableLoad();
|
||||
|
||||
//Verify hidden columns are infact hidden in deployed app!
|
||||
table.AssertTableHeaderOrder("TypeTitleStatus+1CommentorsVotesAnswerUpVote")//from case #1
|
||||
table.AssertTableHeaderOrder(
|
||||
"TypeTitleStatus+1CommentorsVotesAnswerUpVote",
|
||||
); //from case #1
|
||||
|
||||
table.AssertSelectedRow(selectedRow)//Assert default selected row
|
||||
table.AssertSelectedRow(selectedRow); //Assert default selected row
|
||||
|
||||
table.AssertPageNumber(1);
|
||||
table.NavigateToNextPage()//page 2
|
||||
agHelper.Sleep(3000)//wait for table navigation to take effect!
|
||||
table.WaitUntilTableLoad()
|
||||
table.AssertSelectedRow(selectedRow)
|
||||
table.NavigateToNextPage(); //page 2
|
||||
agHelper.Sleep(3000); //wait for table navigation to take effect!
|
||||
table.WaitUntilTableLoad();
|
||||
table.AssertSelectedRow(selectedRow);
|
||||
|
||||
table.NavigateToNextPage(); //page 3
|
||||
agHelper.Sleep(3000); //wait for table navigation to take effect!
|
||||
table.WaitForTableEmpty(); //page 3
|
||||
table.NavigateToPreviousPage(); //page 2
|
||||
agHelper.Sleep(3000); //wait for table navigation to take effect!
|
||||
table.WaitUntilTableLoad();
|
||||
table.AssertSelectedRow(selectedRow);
|
||||
|
||||
table.NavigateToNextPage()//page 3
|
||||
agHelper.Sleep(3000)//wait for table navigation to take effect!
|
||||
table.WaitForTableEmpty()//page 3
|
||||
table.NavigateToPreviousPage()//page 2
|
||||
agHelper.Sleep(3000)//wait for table navigation to take effect!
|
||||
table.WaitUntilTableLoad()
|
||||
table.AssertSelectedRow(selectedRow)
|
||||
|
||||
table.NavigateToPreviousPage()//page 1
|
||||
agHelper.Sleep(3000)//wait for table navigation to take effect!
|
||||
table.WaitUntilTableLoad()
|
||||
table.AssertSelectedRow(selectedRow)
|
||||
table.NavigateToPreviousPage(); //page 1
|
||||
agHelper.Sleep(3000); //wait for table navigation to take effect!
|
||||
table.WaitUntilTableLoad();
|
||||
table.AssertSelectedRow(selectedRow);
|
||||
table.AssertPageNumber(1);
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
it("3. Validate table navigation with Server Side pagination disabled with Default selected row selection", () => {
|
||||
|
||||
agHelper.NavigateBacktoEditor()
|
||||
table.WaitUntilTableLoad()
|
||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
||||
agHelper.ToggleOnOrOff('serversidepagination', 'Off')
|
||||
agHelper.DeployApp()
|
||||
table.WaitUntilTableLoad()
|
||||
table.AssertPageNumber(1, 'Off');
|
||||
table.AssertSelectedRow(selectedRow)
|
||||
agHelper.NavigateBacktoEditor()
|
||||
table.WaitUntilTableLoad()
|
||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
||||
agHelper.ToggleOnOrOff('serversidepagination', 'On')
|
||||
|
||||
agHelper.NavigateBacktoEditor();
|
||||
table.WaitUntilTableLoad();
|
||||
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||
agHelper.ToggleOnOrOff("serversidepagination", "Off");
|
||||
agHelper.DeployApp();
|
||||
table.WaitUntilTableLoad();
|
||||
table.AssertPageNumber(1, "Off");
|
||||
table.AssertSelectedRow(selectedRow);
|
||||
agHelper.NavigateBacktoEditor();
|
||||
table.WaitUntilTableLoad();
|
||||
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||
agHelper.ToggleOnOrOff("serversidepagination", "On");
|
||||
});
|
||||
|
||||
it("4. Change Default selected row in table and verify", () => {
|
||||
|
||||
jsEditor.EnterJSContext("Default Selected Row", "1")
|
||||
agHelper.DeployApp()
|
||||
table.WaitUntilTableLoad()
|
||||
jsEditor.EnterJSContext("Default Selected Row", "1");
|
||||
agHelper.DeployApp();
|
||||
table.WaitUntilTableLoad();
|
||||
table.AssertPageNumber(1);
|
||||
table.AssertSelectedRow(1)
|
||||
table.NavigateToNextPage()//page 2
|
||||
table.AssertSelectedRow(1);
|
||||
table.NavigateToNextPage(); //page 2
|
||||
table.AssertPageNumber(2);
|
||||
table.AssertSelectedRow(1)
|
||||
agHelper.NavigateBacktoEditor()
|
||||
table.WaitUntilTableLoad()
|
||||
|
||||
table.AssertSelectedRow(1);
|
||||
agHelper.NavigateBacktoEditor();
|
||||
table.WaitUntilTableLoad();
|
||||
});
|
||||
|
||||
it.skip("5. Verify Default search text in table as per 'Default Search Text' property set + Bug 12228", () => {
|
||||
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||
jsEditor.EnterJSContext("Default Search Text", "Bug", false);
|
||||
agHelper.DeployApp();
|
||||
table.AssertSearchText("Bug");
|
||||
table.WaitUntilTableLoad();
|
||||
table.WaitUntilTableLoad();
|
||||
agHelper.NavigateBacktoEditor();
|
||||
|
||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
||||
jsEditor.EnterJSContext("Default Search Text", "Bug", false)
|
||||
agHelper.DeployApp()
|
||||
table.AssertSearchText('Bug')
|
||||
table.WaitUntilTableLoad()
|
||||
table.WaitUntilTableLoad()
|
||||
agHelper.NavigateBacktoEditor()
|
||||
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||
jsEditor.EnterJSContext("Default Search Text", "Question", false);
|
||||
agHelper.DeployApp();
|
||||
table.AssertSearchText("Question");
|
||||
table.WaitUntilTableLoad();
|
||||
agHelper.NavigateBacktoEditor();
|
||||
table.WaitUntilTableLoad();
|
||||
|
||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
||||
jsEditor.EnterJSContext("Default Search Text", "Question", false)
|
||||
agHelper.DeployApp()
|
||||
table.AssertSearchText('Question')
|
||||
table.WaitUntilTableLoad()
|
||||
agHelper.NavigateBacktoEditor()
|
||||
table.WaitUntilTableLoad()
|
||||
|
||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
||||
jsEditor.EnterJSContext("Default Search Text", "Epic", false)//Bug 12228 - Searching based on hidden column value should not be allowed
|
||||
agHelper.DeployApp()
|
||||
table.AssertSearchText('Epic')
|
||||
table.WaitForTableEmpty()
|
||||
agHelper.NavigateBacktoEditor()
|
||||
table.WaitUntilTableLoad()
|
||||
|
||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
||||
jsEditor.RemoveText('defaultsearchtext')
|
||||
table.WaitUntilTableLoad()
|
||||
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||
jsEditor.EnterJSContext("Default Search Text", "Epic", false); //Bug 12228 - Searching based on hidden column value should not be allowed
|
||||
agHelper.DeployApp();
|
||||
table.AssertSearchText("Epic");
|
||||
table.WaitForTableEmpty();
|
||||
agHelper.NavigateBacktoEditor();
|
||||
table.WaitUntilTableLoad();
|
||||
|
||||
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||
jsEditor.RemoveText("defaultsearchtext");
|
||||
table.WaitUntilTableLoad();
|
||||
});
|
||||
|
||||
it.skip("6. Validate Search table with Client Side Search enabled & disabled", () => {
|
||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
||||
agHelper.AssertExistingToggleState("enableclientsidesearch", 'checked')
|
||||
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||
agHelper.AssertExistingToggleState("enableclientsidesearch", "checked");
|
||||
|
||||
agHelper.DeployApp()
|
||||
table.WaitUntilTableLoad()
|
||||
agHelper.DeployApp();
|
||||
table.WaitUntilTableLoad();
|
||||
|
||||
table.SearchTable('Bug')
|
||||
table.WaitUntilTableLoad()
|
||||
cy.xpath(table._searchBoxCross).click()
|
||||
table.SearchTable("Bug");
|
||||
table.WaitUntilTableLoad();
|
||||
cy.xpath(table._searchBoxCross).click();
|
||||
|
||||
table.SearchTable('Question')
|
||||
table.WaitUntilTableLoad()
|
||||
cy.xpath(table._searchBoxCross).click()
|
||||
table.SearchTable("Question");
|
||||
table.WaitUntilTableLoad();
|
||||
cy.xpath(table._searchBoxCross).click();
|
||||
|
||||
agHelper.NavigateBacktoEditor()
|
||||
table.WaitUntilTableLoad()
|
||||
agHelper.NavigateBacktoEditor();
|
||||
table.WaitUntilTableLoad();
|
||||
|
||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
||||
agHelper.ToggleOnOrOff("enableclientsidesearch", 'Off')
|
||||
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||
agHelper.ToggleOnOrOff("enableclientsidesearch", "Off");
|
||||
|
||||
agHelper.DeployApp()
|
||||
table.WaitUntilTableLoad()
|
||||
agHelper.DeployApp();
|
||||
table.WaitUntilTableLoad();
|
||||
|
||||
table.SearchTable('Bug')
|
||||
table.WaitForTableEmpty()
|
||||
cy.xpath(table._searchBoxCross).click()
|
||||
table.SearchTable("Bug");
|
||||
table.WaitForTableEmpty();
|
||||
cy.xpath(table._searchBoxCross).click();
|
||||
|
||||
table.SearchTable('Question')
|
||||
table.WaitForTableEmpty()
|
||||
cy.xpath(table._searchBoxCross).click()
|
||||
table.SearchTable("Question");
|
||||
table.WaitForTableEmpty();
|
||||
cy.xpath(table._searchBoxCross).click();
|
||||
|
||||
agHelper.NavigateBacktoEditor()
|
||||
table.WaitUntilTableLoad()
|
||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
||||
agHelper.ToggleOnOrOff("enableclientsidesearch", 'On')
|
||||
})
|
||||
agHelper.NavigateBacktoEditor();
|
||||
table.WaitUntilTableLoad();
|
||||
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||
agHelper.ToggleOnOrOff("enableclientsidesearch", "On");
|
||||
});
|
||||
|
||||
it("7. Validate Filter table", () => {
|
||||
agHelper.DeployApp()
|
||||
table.WaitUntilTableLoad()
|
||||
var filterTitle = new Array();
|
||||
agHelper.DeployApp();
|
||||
table.WaitUntilTableLoad();
|
||||
|
||||
//One filter
|
||||
table.OpenNFilterTable("Type", "is exactly", "Bug")
|
||||
table.ReadTableRowColumnData(0, 1).then(($cellData) => {
|
||||
expect($cellData).to.eq("[Bug]: Postgres queries unable to execute with more than 9 placeholders");
|
||||
});
|
||||
table.ReadTableRowColumnData(2, 1).then(($cellData) => {
|
||||
expect($cellData).to.eq("[Bug]: Input updates with default values are not captured");
|
||||
});
|
||||
table.RemoveFilterNVerify("Question", true, false)
|
||||
table.OpenNFilterTable("Type", "is exactly", "Bug");
|
||||
for (let i = 0; i < 3; i++) {
|
||||
table.ReadTableRowColumnData(i, 0, 200).then(($cellData) => {
|
||||
expect($cellData).to.eq("Bug");
|
||||
});
|
||||
}
|
||||
table.RemoveFilterNVerify("Question", true, false);
|
||||
|
||||
//Two filters - OR
|
||||
table.OpenNFilterTable("Type", "starts with", "Trouble")
|
||||
table.ReadTableRowColumnData(0, 0).then(($cellData) => {
|
||||
expect($cellData).to.eq("Troubleshooting");
|
||||
table.OpenNFilterTable("Type", "starts with", "Trouble");
|
||||
for (let i = 0; i < 5; i++) {
|
||||
table.ReadTableRowColumnData(i, 0, 200).then(($cellData) => {
|
||||
expect($cellData).to.eq("Troubleshooting");
|
||||
});
|
||||
}
|
||||
|
||||
table.OpenNFilterTable("Title", "contains", "query", "OR", 1);
|
||||
table.ReadTableRowColumnData(1, 0, 200).then(($cellData) => {
|
||||
expect($cellData).to.be.oneOf(["Troubleshooting", "Question"]);
|
||||
});
|
||||
|
||||
for (let i = 0; i < 8; i++) {
|
||||
table.ReadTableRowColumnData(i, 1, 100).then(($cellData) => {
|
||||
if ($cellData.toLowerCase().includes("query"))
|
||||
filterTitle.push($cellData);
|
||||
});
|
||||
}
|
||||
cy.wrap(filterTitle).as("filterTitleText"); // alias it for later
|
||||
cy.get("@filterTitleText")
|
||||
.its("length")
|
||||
.should("eq", 2);
|
||||
|
||||
table.RemoveFilterNVerify("Question", true, false);
|
||||
|
||||
//Two filters - AND
|
||||
table.OpenNFilterTable("Votes", "greater than", "2");
|
||||
table.ReadTableRowColumnData(0, 1).then(($cellData) => {
|
||||
expect($cellData).to.eq("Renew expired SSL certificate on a self-hosted instance");
|
||||
expect($cellData).to.eq("Combine queries from different datasources");
|
||||
});
|
||||
|
||||
table.OpenNFilterTable("Title", "contains", "query", 'OR', 1)
|
||||
table.ReadTableRowColumnData(1, 0).then(($cellData) => {
|
||||
expect($cellData).to.be.oneOf(['Troubleshooting','Question'])
|
||||
table.OpenNFilterTable("Title", "contains", "button", "AND", 1);
|
||||
table.ReadTableRowColumnData(0, 1).then(($cellData) => {
|
||||
expect($cellData).to.eq(
|
||||
"Change the video in the video player with a button click",
|
||||
);
|
||||
});
|
||||
table.ReadTableRowColumnData(6, 1).then(($cellData) => {
|
||||
expect($cellData).to.eq("Run storeValue commands before a Query.run()");
|
||||
});
|
||||
table.RemoveFilterNVerify("Question", true, false)
|
||||
|
||||
//Two filters - AND
|
||||
table.OpenNFilterTable("Votes", "greater than", "3")
|
||||
table.ReadTableRowColumnData(1, 1).then(($cellData) => {
|
||||
expect($cellData).to.eq("Combine queries from different datasources");
|
||||
});
|
||||
|
||||
table.OpenNFilterTable("Title", "contains", "button", 'AND', 1)
|
||||
table.ReadTableRowColumnData(0, 1).then(($cellData) => {
|
||||
expect($cellData).to.eq("Change the video in the video player with a button click");
|
||||
});
|
||||
table.RemoveFilterNVerify("Question", true, false)
|
||||
})
|
||||
table.RemoveFilterNVerify("Question", true, false);
|
||||
});
|
||||
|
||||
it("8. Validate Adding a New issue from Add Modal", () => {
|
||||
// agHelper.DeployApp()
|
||||
// table.WaitUntilTableLoad()
|
||||
|
||||
cy.get(table._addIcon).closest('div').click()
|
||||
agHelper.AssertElementPresence(locator._modal)
|
||||
agHelper.SelectFromDropDown('Suggestion', 't--modal-widget')
|
||||
cy.get(table._addIcon)
|
||||
.closest("div")
|
||||
.click();
|
||||
agHelper.AssertElementPresence(locator._modal);
|
||||
agHelper.SelectFromDropDown("Suggestion", "t--modal-widget");
|
||||
|
||||
cy.get(locator._inputWidgetv1InDeployed).eq(3).type("Adding Title Suggestion via script")
|
||||
cy.get(locator._textAreainputWidgetv1InDeployed).eq(1).type("Adding Description Suggestion via script")
|
||||
cy.get(locator._inputWidgetv1InDeployed).eq(4).type("https://github.com/appsmithorg/appsmith/issues/12532")
|
||||
agHelper.SelectFromMultiSelect(['Epic', 'Task'], 1)
|
||||
cy.xpath(table._visibleTextSpan('Labels')).click()
|
||||
cy.get(locator._inputWidgetv1InDeployed).eq(5).type("https://release.app.appsmith.com/applications/62486d45ab307a026918639e/pages/62486d45ab307a02691863a7")
|
||||
agHelper.SelectFromMultiSelect(['Documented', 'Needs App'], 1, true, 'multiselectwidget')
|
||||
cy.get(locator._inputWidgetv1InDeployed)
|
||||
.eq(3)
|
||||
.type("Adding Title Suggestion via script");
|
||||
cy.get(locator._textAreainputWidgetv1InDeployed)
|
||||
.eq(1)
|
||||
.type("Adding Description Suggestion via script");
|
||||
cy.get(locator._inputWidgetv1InDeployed)
|
||||
.eq(4)
|
||||
.type("https://github.com/appsmithorg/appsmith/issues/12532");
|
||||
agHelper.SelectFromMultiSelect(["Epic", "Task"], 1);
|
||||
cy.xpath(table._visibleTextSpan("Labels")).click();
|
||||
cy.get(locator._inputWidgetv1InDeployed)
|
||||
.eq(5)
|
||||
.type(
|
||||
"https://release.app.appsmith.com/applications/62486d45ab307a026918639e/pages/62486d45ab307a02691863a7",
|
||||
);
|
||||
agHelper.SelectFromMultiSelect(
|
||||
["Documented", "Needs App"],
|
||||
1,
|
||||
true,
|
||||
"multiselectwidget",
|
||||
);
|
||||
|
||||
agHelper.ClickButton('Confirm')
|
||||
agHelper.Sleep(3000)
|
||||
table.SearchTable('Suggestion', 2)
|
||||
table.WaitUntilTableLoad()
|
||||
agHelper.ClickButton("Confirm");
|
||||
agHelper.Sleep(3000);
|
||||
table.SearchTable("Suggestion", 2);
|
||||
table.WaitUntilTableLoad();
|
||||
|
||||
table.ReadTableRowColumnData(0, 0, 1000).then((cellData) => {
|
||||
expect(cellData).to.be.equal("Suggestion");
|
||||
|
|
@ -261,22 +296,32 @@ describe("AForce - Community Issues page validations", function () {
|
|||
table.ReadTableRowColumnData(0, 1, 1000).then((cellData) => {
|
||||
expect(cellData).to.be.equal("Adding Title Suggestion via script");
|
||||
});
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
it("9. Validate Updating issue from Details tab", () => {
|
||||
|
||||
agHelper.AssertElementAbsence(locator._widgetInDeployed('tabswidget'))
|
||||
table.SelectTableRow(0)
|
||||
agHelper.AssertElementPresence(locator._widgetInDeployed('tabswidget'))
|
||||
agHelper.GetNClick(locator._inputWidgetv1InDeployed).type("-updating title")
|
||||
agHelper.GetNClick(locator._textAreainputWidgetv1InDeployed).type("-updating desc")
|
||||
agHelper.GetNClick(locator._inputWidgetv1InDeployed, 1).type("-updating issue link")
|
||||
agHelper.SelectFromDropDown('Troubleshooting', 't--widget-tabswidget')
|
||||
agHelper.SelectFromMultiSelect(['Epic', 'Task'], 0, false)
|
||||
agHelper.SelectFromMultiSelect(['High', 'Dependencies'], 0, true)
|
||||
agHelper.SelectFromDropDown('[Bug] TypeError: o is undefined', 't--widget-tabswidget', 1)
|
||||
agHelper.GetNClick(locator._inputWidgetv1InDeployed, 2).type("-updating answer link")
|
||||
agHelper.AssertElementAbsence(locator._widgetInDeployed("tabswidget"));
|
||||
table.SelectTableRow(0);
|
||||
agHelper.AssertElementPresence(locator._widgetInDeployed("tabswidget"));
|
||||
agHelper
|
||||
.GetNClick(locator._inputWidgetv1InDeployed)
|
||||
.type("-updating title");
|
||||
agHelper
|
||||
.GetNClick(locator._textAreainputWidgetv1InDeployed)
|
||||
.type("-updating desc");
|
||||
agHelper
|
||||
.GetNClick(locator._inputWidgetv1InDeployed, 1)
|
||||
.type("-updating issue link");
|
||||
agHelper.SelectFromDropDown("Troubleshooting", "t--widget-tabswidget");
|
||||
agHelper.SelectFromMultiSelect(["Epic", "Task"], 0, false);
|
||||
agHelper.SelectFromMultiSelect(["High", "Dependencies"], 0, true);
|
||||
agHelper.SelectFromDropDown(
|
||||
"[Bug] TypeError: o is undefined",
|
||||
"t--widget-tabswidget",
|
||||
1,
|
||||
);
|
||||
agHelper
|
||||
.GetNClick(locator._inputWidgetv1InDeployed, 2)
|
||||
.type("-updating answer link");
|
||||
|
||||
//cy.get("body").tab().type("{enter}")
|
||||
|
||||
|
|
@ -288,34 +333,41 @@ describe("AForce - Community Issues page validations", function () {
|
|||
// key: 'Enter',
|
||||
// })
|
||||
|
||||
|
||||
//agHelper.Sleep(2000)
|
||||
//cy.get("body").type("{enter}")
|
||||
|
||||
agHelper.RemoveMultiSelectItems(['Documented', 'Needs App'])
|
||||
agHelper.RemoveMultiSelectItems(["Documented", "Needs App"]);
|
||||
|
||||
//agHelper.SelectFromMultiSelect(['Documented', 'Needs App', 'App Built'], 0, false, 'multiselectwidget')
|
||||
agHelper.SelectFromMultiSelect(['Needs Product'], 0, true, 'multiselectwidget')
|
||||
agHelper.ClickButton('Save')
|
||||
agHelper.SelectFromMultiSelect(
|
||||
["Needs Product"],
|
||||
0,
|
||||
true,
|
||||
"multiselectwidget",
|
||||
);
|
||||
agHelper.ClickButton("Save");
|
||||
|
||||
table.ReadTableRowColumnData(0, 0, 1000).then((cellData) => {
|
||||
expect(cellData).to.be.equal("Troubleshooting");
|
||||
});
|
||||
|
||||
table.ReadTableRowColumnData(0, 1, 1000).then((cellData) => {
|
||||
expect(cellData).to.be.equal("Adding Title Suggestion via script-updating title");
|
||||
expect(cellData).to.be.equal(
|
||||
"Adding Title Suggestion via script-updating title",
|
||||
);
|
||||
});
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
it("10. Validate Deleting the newly created issue", () => {
|
||||
agHelper.AssertElementAbsence(locator._widgetInDeployed('tabswidget'))
|
||||
table.SelectTableRow(0)
|
||||
agHelper.AssertElementPresence(locator._widgetInDeployed('tabswidget'))
|
||||
agHelper.Sleep()
|
||||
cy.get(table._trashIcon).closest('div').click()
|
||||
agHelper.AssertElementAbsence(locator._widgetInDeployed('tabswidget'))
|
||||
table.WaitForTableEmpty()
|
||||
agHelper.AssertElementAbsence(locator._widgetInDeployed("tabswidget"));
|
||||
table.SelectTableRow(0);
|
||||
agHelper.AssertElementPresence(locator._widgetInDeployed("tabswidget"));
|
||||
agHelper.Sleep();
|
||||
cy.get(table._trashIcon)
|
||||
.closest("div")
|
||||
.click();
|
||||
agHelper.AssertElementAbsence(locator._widgetInDeployed("tabswidget"));
|
||||
table.WaitForTableEmpty();
|
||||
|
||||
//2nd search is not working, hence commenting below
|
||||
// cy.xpath(table._searchBoxCross).click()
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ describe("Validate API request body panel", () => {
|
|||
paste: true,
|
||||
completeReplace: true,
|
||||
toRun: false,
|
||||
shouldNavigate: true,
|
||||
shouldCreateNewJSObj: true,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ describe("Validate JSObjects binding to Input widget", () => {
|
|||
paste: false,
|
||||
completeReplace: false,
|
||||
toRun: true,
|
||||
shouldNavigate: true,
|
||||
shouldCreateNewJSObj: true,
|
||||
});
|
||||
ee.expandCollapseEntity("WIDGETS"); //to expand widgets
|
||||
ee.expandCollapseEntity("Form1");
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ describe("Validate JSObj binding to Table widget", () => {
|
|||
paste: false,
|
||||
completeReplace: false,
|
||||
toRun: true,
|
||||
shouldNavigate: true,
|
||||
shouldCreateNewJSObj: true,
|
||||
});
|
||||
cy.get("@jsObjName").then((jsObj) => {
|
||||
jsName = jsObj;
|
||||
|
|
|
|||
|
|
@ -319,7 +319,7 @@ showAlert("Wonderful! all apis executed", "success")).catch(() => showAlert("Ple
|
|||
return Promise.any([this.func2(), this.func3(), this.func1()]).then((value) => showAlert("Resolved promise is:" + value))
|
||||
}
|
||||
}`,
|
||||
{ paste: true, completeReplace: true, toRun: true, shouldNavigate: true },
|
||||
{ paste: true, completeReplace: true, toRun: true, shouldCreateNewJSObj: true },
|
||||
);
|
||||
ee.SelectEntityByName("Button1", "WIDGETS");
|
||||
cy.get("@jsObjName").then((jsObjName) => {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ describe("JS Function Execution", function() {
|
|||
paste: true,
|
||||
completeReplace: true,
|
||||
toRun: false,
|
||||
shouldNavigate: true,
|
||||
shouldCreateNewJSObj: true,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ describe("JS Function Execution", function() {
|
|||
paste: true,
|
||||
completeReplace: true,
|
||||
toRun: false,
|
||||
shouldNavigate: true,
|
||||
shouldCreateNewJSObj: true,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -41,8 +41,8 @@ describe("JS Function Execution", function() {
|
|||
});
|
||||
|
||||
it("3. Prioritizes parse errors that render JS Object invalid over function execution parse errors in debugger callouts", function() {
|
||||
const JSObjectWithFunctionExecutionParseErrors = `export default {
|
||||
myFun1 :()=>{
|
||||
const JSObjectWithFunctionExecutionParseErrors = `export default {
|
||||
myFun1 :()=>{
|
||||
return f
|
||||
}
|
||||
}`;
|
||||
|
|
@ -58,7 +58,7 @@ describe("JS Function Execution", function() {
|
|||
paste: true,
|
||||
completeReplace: true,
|
||||
toRun: true,
|
||||
shouldNavigate: true,
|
||||
shouldCreateNewJSObj: true,
|
||||
});
|
||||
|
||||
// Assert presence of function execution parse error callout
|
||||
|
|
@ -69,7 +69,7 @@ describe("JS Function Execution", function() {
|
|||
paste: true,
|
||||
completeReplace: true,
|
||||
toRun: false,
|
||||
shouldNavigate: false,
|
||||
shouldCreateNewJSObj: false,
|
||||
});
|
||||
|
||||
// Assert presence of parse error callout (entire JS Object is invalid)
|
||||
|
|
|
|||
|
|
@ -33,5 +33,17 @@ describe("Widget Grouping", function() {
|
|||
.should("have.length", 2);
|
||||
cy.get(`@group`).find(`.t--draggable-buttonwidget`);
|
||||
cy.get(`@group`).find(`.t--draggable-imagewidget`);
|
||||
|
||||
// verify the position so that the camera widget is still below the newly grouped container
|
||||
cy.get(`.t--widget-containerwidget`)
|
||||
.eq(1)
|
||||
.then((element) => {
|
||||
const elementTop = parseFloat(element.css("top"));
|
||||
const elementHeight = parseFloat(element.css("height"));
|
||||
const containerBottom = (elementTop + elementHeight).toString() + "px";
|
||||
cy.get(`.t--widget-camerawidget`)
|
||||
.invoke("attr", "style")
|
||||
.should("contain", `top: ${containerBottom}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ describe("Validate basic operations on Entity explorer JSEditor structure", () =
|
|||
|
||||
it("5. Validate Move JSObject", function() {
|
||||
const newPageId = "Page2";
|
||||
agHelper.AddNewPage();
|
||||
ee.AddNewPage();
|
||||
ee.AssertEntityPresenceInExplorer(newPageId);
|
||||
ee.SelectEntityByName(pageId);
|
||||
ee.expandCollapseEntity("QUERIES/JS");
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ describe("JSObjects OnLoad Actions tests", function() {
|
|||
paste: true,
|
||||
completeReplace: true,
|
||||
toRun: false,
|
||||
shouldNavigate: true,
|
||||
shouldCreateNewJSObj: true,
|
||||
},
|
||||
);
|
||||
jsEditor.EnableDisableAsyncFuncSettings("getId", false, true); //Only before calling confirmation is enabled by User here
|
||||
|
|
@ -189,7 +189,7 @@ describe("JSObjects OnLoad Actions tests", function() {
|
|||
paste: true,
|
||||
completeReplace: true,
|
||||
toRun: false,
|
||||
shouldNavigate: true,
|
||||
shouldCreateNewJSObj: true,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -251,7 +251,7 @@ describe("JSObjects OnLoad Actions tests", function() {
|
|||
paste: true,
|
||||
completeReplace: true,
|
||||
toRun: false,
|
||||
shouldNavigate: true,
|
||||
shouldCreateNewJSObj: true,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -440,7 +440,7 @@ describe("JSObjects OnLoad Actions tests", function() {
|
|||
paste: true,
|
||||
completeReplace: true,
|
||||
toRun: false,
|
||||
shouldNavigate: true,
|
||||
shouldCreateNewJSObj: true,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -481,7 +481,7 @@ describe("JSObjects OnLoad Actions tests", function() {
|
|||
// `{{` +
|
||||
// jsObjName +
|
||||
// `.callCountry();
|
||||
// showAlert('Your country is: ' + getCountry.data[0].country, 'info')}}`,
|
||||
// Select1.selectedOptionValue? showAlert('Your country is: ' + getCountry.data[0].country, 'info'): null`,
|
||||
// true,
|
||||
// true,
|
||||
// );
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break",
|
|||
paste: true,
|
||||
completeReplace: false,
|
||||
toRun: false,
|
||||
shouldNavigate: true,
|
||||
shouldCreateNewJSObj: true,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@
|
|||
"evaluatedType": ".t--CodeEditor-evaluatedValue > div:first-of-type pre",
|
||||
"evaluatedCurrentValue": "div:last-of-type .t--CodeEditor-evaluatedValue > div:last-of-type pre",
|
||||
"entityExplorersearch": "#entity-explorer-search",
|
||||
"searchEntityInExplorer": "#search-entity",
|
||||
"entitySearchResult": ".t--entity-name:contains('",
|
||||
"saveStatusContainer": ".t--save-status-container",
|
||||
"saveStatusIsSaving": "t--save-status-is-saving",
|
||||
|
|
@ -156,9 +157,9 @@
|
|||
"debugErrorMsg": ".t--debugger-message",
|
||||
"tableButtonVariant": ".t--property-control-buttonvariant .bp3-popover-target",
|
||||
"debuggerLabel": "span.debugger-label",
|
||||
"debuggerContextMenu":".t--debugger-contextual-error-menu",
|
||||
"debuggerContextMenu": ".t--debugger-contextual-error-menu",
|
||||
"cyclicDependencyError": "//div[@class='Toastify']//span[contains(text(),'Cyclic dependency found while evaluating')]",
|
||||
"openDocumentationfromErrorTab":"//span[@name='book-line']",
|
||||
"openDocumentationfromErrorTab": "//span[@name='book-line']",
|
||||
"appNameDeployMenu": ".t--app-name-menu-deploy-parent",
|
||||
"appNameDeployMenuPublish": ".t--app-name-menu-deploy",
|
||||
"appNameDeployMenuCurrentVersion": ".t--app-name-menu-deploy-current-version",
|
||||
|
|
@ -178,4 +179,4 @@
|
|||
"saveThemeBtn": ".t--save-theme-btn",
|
||||
"selectThemeBackBtn": ".t--theme-select-back-btn",
|
||||
"themeAppBorderRadiusBtn": ".t--theme-appBorderRadius"
|
||||
}
|
||||
}
|
||||
|
|
@ -134,17 +134,6 @@ export class AggregateHelper {
|
|||
localStorage.setItem("inDeployedMode", "true");
|
||||
}
|
||||
|
||||
public AddNewPage() {
|
||||
cy.get(this.locator._newPage)
|
||||
.first()
|
||||
.click();
|
||||
cy.wait("@createPage").should(
|
||||
"have.nested.property",
|
||||
"response.body.responseMeta.status",
|
||||
201,
|
||||
);
|
||||
}
|
||||
|
||||
public ValidateToastMessage(text: string, length = 1) {
|
||||
cy.get(this.locator._toastMsg)
|
||||
.should("have.length", length)
|
||||
|
|
@ -409,6 +398,17 @@ export class AggregateHelper {
|
|||
.wait(500);
|
||||
}
|
||||
|
||||
public GetNClickByContains(
|
||||
selector: string,
|
||||
containsText: string,
|
||||
index = 0,
|
||||
) {
|
||||
cy.get(selector)
|
||||
.contains(containsText)
|
||||
.eq(index)
|
||||
.click().wait(200);
|
||||
}
|
||||
|
||||
public ToggleOnOrOff(propertyName: string, toggle: "On" | "Off") {
|
||||
if (toggle == "On") {
|
||||
cy.get(this.locator._propertyToggle(propertyName))
|
||||
|
|
@ -605,7 +605,11 @@ export class AggregateHelper {
|
|||
locator.should("not.exist");
|
||||
}
|
||||
|
||||
public GetText(selector: string, textOrValue : 'text'| 'val' = 'text', index = 0) {
|
||||
public GetText(
|
||||
selector: string,
|
||||
textOrValue: "text" | "val" = "text",
|
||||
index = 0,
|
||||
) {
|
||||
let locator = selector.startsWith("//")
|
||||
? cy.xpath(selector)
|
||||
: cy.get(selector);
|
||||
|
|
|
|||
|
|
@ -34,26 +34,28 @@ export class DataSources {
|
|||
_reconnectModal = "div.reconnect-datasource-modal";
|
||||
_activeDSListReconnectModal = (dbName: string) =>
|
||||
"//div[contains(@class, 't--ds-list')]//span[text()='" + dbName + "']";
|
||||
_apiQueryBtn = ".t--run-query";
|
||||
_runQueryBtn = ".t--run-query";
|
||||
_newDatabases = "#new-datasources";
|
||||
_selectDatasourceDropdown = "[data-cy=t--datasource-dropdown]";
|
||||
_datasourceDropdownOption = "[data-cy=t--datasource-dropdown-option]";
|
||||
_selectTableDropdown = "[data-cy=t--table-dropdown]";
|
||||
_tableDropdownOption = ".bp3-popover-content .t--dropdown-option";
|
||||
_generatePageBtn = "[data-cy=t--generate-page-form-submit]";
|
||||
|
||||
public NavigateToDSAdd() {
|
||||
public CreatePlugIn(pluginName: string) {
|
||||
cy.get(this._createNewPlgin(pluginName)).trigger("click");
|
||||
}
|
||||
|
||||
public NavigateToDSCreateNew() {
|
||||
cy.get(this._addNewDataSource)
|
||||
.last()
|
||||
.scrollIntoView()
|
||||
.should("be.visible")
|
||||
.click({ force: true });
|
||||
}
|
||||
|
||||
public CreatePlugIn(pluginName: string) {
|
||||
cy.get(this._createNewPlgin(pluginName)).click();
|
||||
}
|
||||
|
||||
public NavigateToDSCreateNew() {
|
||||
this.NavigateToDSAdd();
|
||||
cy.get(this._dsCreateNewTab)
|
||||
.should("be.visible")
|
||||
.click({ force: true });
|
||||
cy.get(this.locator._loading).should("not.exist");
|
||||
// cy.get(this._dsCreateNewTab)
|
||||
// .should("be.visible")
|
||||
// .click({ force: true });
|
||||
cy.get(this._newDatabases).should("be.visible");
|
||||
}
|
||||
|
||||
public FillPostgresDSForm(shouldAddTrailingSpaces = false) {
|
||||
|
|
@ -73,6 +75,18 @@ export class DataSources {
|
|||
cy.get(this._password).type(datasourceFormData["postgres-password"]);
|
||||
}
|
||||
|
||||
public FillMongoDSForm(shouldAddTrailingSpaces = false) {
|
||||
const hostAddress = shouldAddTrailingSpaces
|
||||
? datasourceFormData["mongo-host"] + " "
|
||||
: datasourceFormData["mongo-host"];
|
||||
cy.get(this._host).type(hostAddress);
|
||||
cy.get(this._port).type(datasourceFormData["mongo-port"].toString());
|
||||
cy.get(this._sectionAuthentication).click();
|
||||
cy.get(this._databaseName)
|
||||
.clear()
|
||||
.type(datasourceFormData["mongo-databaseName"]);
|
||||
}
|
||||
|
||||
public TestSaveDatasource(expectedRes = true) {
|
||||
this.TestDatasource(expectedRes);
|
||||
this.SaveDatasource();
|
||||
|
|
@ -94,7 +108,7 @@ export class DataSources {
|
|||
}
|
||||
|
||||
public NavigateToActiveDSQueryPane(datasourceName: string) {
|
||||
this.NavigateToDSAdd();
|
||||
this.NavigateToDSCreateNew();
|
||||
this.agHelper.GetNClick(this.locator._activeTab);
|
||||
cy.get(this._datasourceCard)
|
||||
.contains(datasourceName)
|
||||
|
|
@ -145,7 +159,7 @@ export class DataSources {
|
|||
}
|
||||
|
||||
RunQuery() {
|
||||
cy.get(this._apiQueryBtn).click({ force: true });
|
||||
cy.get(this._runQueryBtn).click({ force: true });
|
||||
this.agHelper.ValidateNetworkExecutionSuccess("@postExecute");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,69 +1,88 @@
|
|||
import { ObjectsRegistry } from "../Objects/Registry"
|
||||
import { ObjectsRegistry } from "../Objects/Registry";
|
||||
|
||||
export class EntityExplorer {
|
||||
public agHelper = ObjectsRegistry.AggregateHelper;
|
||||
public locator = ObjectsRegistry.CommonLocators;
|
||||
|
||||
public agHelper = ObjectsRegistry.AggregateHelper
|
||||
public locator = ObjectsRegistry.CommonLocators;
|
||||
public SelectEntityByName(
|
||||
entityNameinLeftSidebar: string,
|
||||
section: "WIDGETS" | "QUERIES/JS" | "DATASOURCES" | "" = "",
|
||||
) {
|
||||
if (section) this.expandCollapseEntity(section); //to expand respective section
|
||||
cy.xpath(this.locator._entityNameInExplorer(entityNameinLeftSidebar))
|
||||
.last()
|
||||
.click({ multiple: true });
|
||||
this.agHelper.Sleep();
|
||||
}
|
||||
|
||||
public SelectEntityByName(entityNameinLeftSidebar: string, section: 'WIDGETS' | 'QUERIES/JS' | 'DATASOURCES' | '' = '') {
|
||||
if (section)
|
||||
this.expandCollapseEntity(section)//to expand respective section
|
||||
cy.xpath(this.locator._entityNameInExplorer(entityNameinLeftSidebar))
|
||||
.last()
|
||||
.click({ multiple: true })
|
||||
this.agHelper.Sleep()
|
||||
public AddNewPage() {
|
||||
cy.get(this.locator._newPage)
|
||||
.first()
|
||||
.click();
|
||||
this.agHelper.ValidateNetworkStatus("@createPage", 201);
|
||||
}
|
||||
|
||||
public NavigateToSwitcher(navigationTab: "explorer" | "widgets") {
|
||||
cy.get(this.locator._openNavigationTab(navigationTab)).click();
|
||||
}
|
||||
|
||||
public AssertEntityPresenceInExplorer(entityNameinLeftSidebar: string) {
|
||||
cy.xpath(
|
||||
this.locator._entityNameInExplorer(entityNameinLeftSidebar),
|
||||
).should("have.length", 1);
|
||||
}
|
||||
|
||||
public AssertEntityAbsenceInExplorer(entityNameinLeftSidebar: string) {
|
||||
cy.xpath(
|
||||
this.locator._entityNameInExplorer(entityNameinLeftSidebar),
|
||||
).should("not.exist");
|
||||
}
|
||||
|
||||
public expandCollapseEntity(entityName: string, expand = true) {
|
||||
cy.xpath(this.locator._expandCollapseArrow(entityName))
|
||||
.invoke("attr", "name")
|
||||
.then((arrow) => {
|
||||
if (expand && arrow == "arrow-right")
|
||||
cy.xpath(this.locator._expandCollapseArrow(entityName))
|
||||
.trigger("click", { multiple: true })
|
||||
.wait(1000);
|
||||
else if (!expand && arrow == "arrow-down")
|
||||
cy.xpath(this.locator._expandCollapseArrow(entityName))
|
||||
.trigger("click", { multiple: true })
|
||||
.wait(1000);
|
||||
else this.agHelper.Sleep(500);
|
||||
});
|
||||
}
|
||||
|
||||
public ActionContextMenuByEntityName(
|
||||
entityNameinLeftSidebar: string,
|
||||
action = "Delete",
|
||||
subAction = "",
|
||||
) {
|
||||
this.agHelper.Sleep();
|
||||
cy.xpath(this.locator._contextMenu(entityNameinLeftSidebar))
|
||||
.last()
|
||||
.click({ force: true });
|
||||
cy.xpath(this.locator._contextMenuItem(action)).click({ force: true });
|
||||
this.agHelper.Sleep(500);
|
||||
if (subAction) {
|
||||
cy.xpath(this.locator._contextMenuItem(subAction)).click({ force: true });
|
||||
this.agHelper.Sleep(500);
|
||||
}
|
||||
}
|
||||
|
||||
public NavigateToSwitcher(navigationTab: 'explorer' | 'widgets') {
|
||||
cy.get(this.locator._openNavigationTab(navigationTab)).click()
|
||||
}
|
||||
|
||||
public AssertEntityPresenceInExplorer(entityNameinLeftSidebar: string) {
|
||||
cy.xpath(this.locator._entityNameInExplorer(entityNameinLeftSidebar))
|
||||
.should("have.length", 1);
|
||||
}
|
||||
|
||||
public AssertEntityAbsenceInExplorer(entityNameinLeftSidebar: string) {
|
||||
cy.xpath(this.locator._entityNameInExplorer(entityNameinLeftSidebar)).should('not.exist');
|
||||
}
|
||||
|
||||
public expandCollapseEntity(entityName: string, expand = true) {
|
||||
cy.xpath(this.locator._expandCollapseArrow(entityName)).invoke('attr', 'name').then((arrow) => {
|
||||
if (expand && arrow == 'arrow-right')
|
||||
cy.xpath(this.locator._expandCollapseArrow(entityName)).trigger('click', { multiple: true }).wait(1000);
|
||||
else if (!expand && arrow == 'arrow-down')
|
||||
cy.xpath(this.locator._expandCollapseArrow(entityName)).trigger('click', { multiple: true }).wait(1000);
|
||||
else
|
||||
this.agHelper.Sleep(500)
|
||||
})
|
||||
}
|
||||
|
||||
public ActionContextMenuByEntityName(entityNameinLeftSidebar: string, action = "Delete", subAction = "") {
|
||||
this.agHelper.Sleep();
|
||||
cy.xpath(this.locator._contextMenu(entityNameinLeftSidebar))
|
||||
.last()
|
||||
.click({ force: true });
|
||||
cy.xpath(this.locator._contextMenuItem(action))
|
||||
.click({ force: true })
|
||||
this.agHelper.Sleep(500)
|
||||
if (subAction) {
|
||||
cy.xpath(this.locator._contextMenuItem(subAction))
|
||||
.click({ force: true })
|
||||
this.agHelper.Sleep(500)
|
||||
}
|
||||
}
|
||||
|
||||
public DragDropWidgetNVerify(widgetType: string, x: number, y: number) {
|
||||
this.NavigateToSwitcher('widgets')
|
||||
this.agHelper.Sleep()
|
||||
cy.get(this.locator._widgetPageIcon(widgetType)).first()
|
||||
.trigger("dragstart", { force: true })
|
||||
.trigger("mousemove", x, y, { force: true });
|
||||
cy.get(this.locator._dropHere)
|
||||
.trigger("mousemove", x, y, { eventConstructor: "MouseEvent" })
|
||||
.trigger("mousemove", x, y, { eventConstructor: "MouseEvent" })
|
||||
.trigger("mouseup", x, y, { eventConstructor: "MouseEvent" });
|
||||
this.agHelper.AssertAutoSave()//settling time for widget on canvas!
|
||||
cy.get(this.locator._widgetInCanvas(widgetType)).should('exist')
|
||||
}
|
||||
public DragDropWidgetNVerify(widgetType: string, x: number, y: number) {
|
||||
this.NavigateToSwitcher("widgets");
|
||||
this.agHelper.Sleep();
|
||||
cy.get(this.locator._widgetPageIcon(widgetType))
|
||||
.first()
|
||||
.trigger("dragstart", { force: true })
|
||||
.trigger("mousemove", x, y, { force: true });
|
||||
cy.get(this.locator._dropHere)
|
||||
.trigger("mousemove", x, y, { eventConstructor: "MouseEvent" })
|
||||
.trigger("mousemove", x, y, { eventConstructor: "MouseEvent" })
|
||||
.trigger("mouseup", x, y, { eventConstructor: "MouseEvent" });
|
||||
this.agHelper.AssertAutoSave(); //settling time for widget on canvas!
|
||||
cy.get(this.locator._widgetInCanvas(widgetType)).should("exist");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export class HomePage {
|
|||
private _editAppName = "bp3-editable-text-editing"
|
||||
private _appMenu = ".t--editor-appname-menu-portal .bp3-menu-item"
|
||||
private _buildFromScratchActionCard = ".t--BuildFromScratch"
|
||||
private _buildFromDataTableActionCard = ".t--GenerateCRUDPage"
|
||||
_buildFromDataTableActionCard = ".t--GenerateCRUDPage"
|
||||
private _selectRole = "//span[text()='Select a role']/ancestor::div"
|
||||
private _searchInput = "input[type='text']"
|
||||
_appHoverIcon = (action: string) => ".t--application-" + action + "-link"
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ export interface ICreateJSObjectOptions {
|
|||
paste: boolean;
|
||||
completeReplace: boolean;
|
||||
toRun: boolean;
|
||||
shouldNavigate: boolean;
|
||||
shouldCreateNewJSObj: boolean;
|
||||
}
|
||||
const DEFAULT_CREATE_JS_OBJECT_OPTIONS = {
|
||||
paste: true,
|
||||
completeReplace: false,
|
||||
toRun: true,
|
||||
shouldNavigate: true,
|
||||
shouldCreateNewJSObj: true,
|
||||
};
|
||||
|
||||
export class JSEditor {
|
||||
|
|
@ -82,7 +82,7 @@ export class JSEditor {
|
|||
//#endregion
|
||||
|
||||
//#region Page functions
|
||||
public NavigateToJSEditor() {
|
||||
public NavigateToNewJSEditor() {
|
||||
cy.get(this.locator._createNew)
|
||||
.last()
|
||||
.click({ force: true });
|
||||
|
|
@ -107,9 +107,9 @@ export class JSEditor {
|
|||
JSCode: string,
|
||||
options: ICreateJSObjectOptions = DEFAULT_CREATE_JS_OBJECT_OPTIONS,
|
||||
) {
|
||||
const { completeReplace, paste, shouldNavigate, toRun } = options;
|
||||
const { completeReplace, paste, shouldCreateNewJSObj, toRun } = options;
|
||||
|
||||
shouldNavigate && this.NavigateToJSEditor();
|
||||
shouldCreateNewJSObj && this.NavigateToNewJSEditor();
|
||||
if (!completeReplace) {
|
||||
cy.get(this.locator._codeMirrorTextArea)
|
||||
.first()
|
||||
|
|
@ -377,7 +377,7 @@ export class JSEditor {
|
|||
this.agHelper.GetNClick(this._codeTab);
|
||||
}
|
||||
/**
|
||||
*
|
||||
*
|
||||
There are two types of parse errors in the JS Editor
|
||||
1. Parse errors that render the JS Object invalid and all functions unrunnable
|
||||
2. Parse errors within functions that throw errors when executing those functions
|
||||
|
|
|
|||
|
|
@ -313,7 +313,7 @@ Cypress.Commands.add("SearchApp", (appname) => {
|
|||
});
|
||||
|
||||
Cypress.Commands.add("SearchEntity", (apiname1, apiname2) => {
|
||||
cy.get(commonlocators.entityExplorersearch)
|
||||
cy.get(commonlocators.searchEntityInExplorer)
|
||||
.clear({ force: true })
|
||||
.type(apiname1, { force: true });
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
|
|
@ -329,7 +329,7 @@ Cypress.Commands.add("SearchEntity", (apiname1, apiname2) => {
|
|||
|
||||
Cypress.Commands.add("GlobalSearchEntity", (apiname1, dontAssertVisibility) => {
|
||||
// entity explorer search will be hidden
|
||||
cy.get(commonlocators.entityExplorersearch)
|
||||
cy.get(commonlocators.searchEntityInExplorer)
|
||||
.clear({ force: true })
|
||||
.type(apiname1, { force: true });
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
|
|
@ -413,7 +413,7 @@ Cypress.Commands.add("CheckAndUnfoldWidgets", () => {
|
|||
});
|
||||
|
||||
Cypress.Commands.add("SearchEntityandOpen", (apiname1) => {
|
||||
cy.get(commonlocators.entityExplorersearch)
|
||||
cy.get(commonlocators.searchEntityInExplorer)
|
||||
.clear({ force: true })
|
||||
.type(apiname1, { force: true });
|
||||
cy.CheckAndUnfoldWidgets();
|
||||
|
|
@ -432,7 +432,7 @@ Cypress.Commands.add("SearchEntityandOpen", (apiname1) => {
|
|||
});
|
||||
|
||||
Cypress.Commands.add("SearchEntityAndUnfold", (apiname1) => {
|
||||
cy.get(commonlocators.entityExplorersearch)
|
||||
cy.get(commonlocators.searchEntityInExplorer)
|
||||
.clear({ force: true })
|
||||
.type(apiname1, { force: true });
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
|
|
@ -451,7 +451,7 @@ Cypress.Commands.add("SearchEntityAndUnfold", (apiname1) => {
|
|||
|
||||
Cypress.Commands.add("OpenBindings", (apiname1) => {
|
||||
cy.wait(500);
|
||||
cy.get(commonlocators.entityExplorersearch)
|
||||
cy.get(commonlocators.searchEntityInExplorer)
|
||||
.clear({ force: true })
|
||||
.type(apiname1, { force: true });
|
||||
cy.CheckAndUnfoldWidgets();
|
||||
|
|
|
|||
|
|
@ -7,22 +7,17 @@ import React, {
|
|||
useMemo,
|
||||
} from "react";
|
||||
import classNames from "classnames";
|
||||
import history from "utils/history";
|
||||
import * as Sentry from "@sentry/react";
|
||||
import { PanelStack } from "@blueprintjs/core";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
import PerformanceTracker, {
|
||||
PerformanceTransactionName,
|
||||
} from "utils/PerformanceTracker";
|
||||
import { AppState } from "reducers";
|
||||
import {
|
||||
getFirstTimeUserOnboardingComplete,
|
||||
getIsFirstTimeUserOnboardingEnabled,
|
||||
} from "selectors/onboardingSelectors";
|
||||
import Explorer from "pages/Editor/Explorer";
|
||||
import Switcher from "components/ads/Switcher";
|
||||
import { trimQueryString } from "utils/helpers";
|
||||
import AppComments from "comments/AppComments/AppComments";
|
||||
import { setExplorerActiveAction } from "actions/explorerActions";
|
||||
import {
|
||||
|
|
@ -33,14 +28,10 @@ import { tailwindLayers } from "constants/Layers";
|
|||
import TooltipComponent from "components/ads/Tooltip";
|
||||
import { previewModeSelector } from "selectors/editorSelectors";
|
||||
import useHorizontalResize from "utils/hooks/useHorizontalResize";
|
||||
import { forceOpenWidgetPanel } from "actions/widgetSidebarActions";
|
||||
import { toggleInOnboardingWidgetSelection } from "actions/onboardingActions";
|
||||
import OnboardingStatusbar from "pages/Editor/FirstTimeUserOnboarding/Statusbar";
|
||||
import Pages from "pages/Editor/Explorer/Pages";
|
||||
import { Colors } from "constants/Colors";
|
||||
import { EntityProperties } from "pages/Editor/Explorer/Entity/EntityProperties";
|
||||
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
||||
import { builderURL } from "RouteBuilder";
|
||||
|
||||
type Props = {
|
||||
width: number;
|
||||
|
|
@ -58,38 +49,12 @@ export const EntityExplorerSidebar = memo((props: Props) => {
|
|||
const enableFirstTimeUserOnboarding = useSelector(
|
||||
getIsFirstTimeUserOnboardingEnabled,
|
||||
);
|
||||
const isFirstTimeUserOnboardingEnabled = useSelector(
|
||||
getIsFirstTimeUserOnboardingEnabled,
|
||||
);
|
||||
const resizer = useHorizontalResize(
|
||||
sidebarRef,
|
||||
props.onWidthChange,
|
||||
props.onDragEnd,
|
||||
);
|
||||
const switches = [
|
||||
{
|
||||
id: "explorer",
|
||||
text: "Explorer",
|
||||
action: () => dispatch(forceOpenWidgetPanel(false)),
|
||||
},
|
||||
{
|
||||
id: "widgets",
|
||||
text: "Widgets",
|
||||
action: () => {
|
||||
!(trimQueryString(builderURL()) === window.location.pathname) &&
|
||||
history.push(builderURL());
|
||||
setTimeout(() => dispatch(forceOpenWidgetPanel(true)), 0);
|
||||
if (isFirstTimeUserOnboardingEnabled) {
|
||||
dispatch(toggleInOnboardingWidgetSelection(true));
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
const [activeSwitch, setActiveSwitch] = useState(switches[0]);
|
||||
const [tooltipIsOpen, setTooltipIsOpen] = useState(false);
|
||||
const isForceOpenWidgetPanel = useSelector(
|
||||
(state: AppState) => state.ui.onBoarding.forceOpenWidgetPanel,
|
||||
);
|
||||
const isFirstTimeUserOnboardingComplete = useSelector(
|
||||
getFirstTimeUserOnboardingComplete,
|
||||
);
|
||||
|
|
@ -98,14 +63,6 @@ export const EntityExplorerSidebar = memo((props: Props) => {
|
|||
PerformanceTracker.stopTracking();
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isForceOpenWidgetPanel) {
|
||||
setActiveSwitch(switches[1]);
|
||||
} else {
|
||||
setActiveSwitch(switches[0]);
|
||||
}
|
||||
}, [isForceOpenWidgetPanel]);
|
||||
|
||||
// registering event listeners
|
||||
useEffect(() => {
|
||||
document.addEventListener("mousemove", onMouseMove);
|
||||
|
|
@ -191,7 +148,7 @@ export const EntityExplorerSidebar = memo((props: Props) => {
|
|||
<div
|
||||
className={classNames({
|
||||
[`js-entity-explorer t--entity-explorer transform transition-all flex h-full duration-400 border-r border-gray-200 ${tailwindLayers.entityExplorer}`]: true,
|
||||
"relative ": pinned && !isPreviewMode,
|
||||
relative: pinned && !isPreviewMode,
|
||||
"-translate-x-full": (!pinned && !active) || isPreviewMode,
|
||||
"shadow-xl": !pinned,
|
||||
fixed: !pinned || isPreviewMode,
|
||||
|
|
@ -199,7 +156,7 @@ export const EntityExplorerSidebar = memo((props: Props) => {
|
|||
>
|
||||
{/* SIDEBAR */}
|
||||
<div
|
||||
className="flex flex-col p-0 overflow-y-auto bg-white t--sidebar min-w-48 max-w-96 group"
|
||||
className="flex flex-col p-0 bg-white t--sidebar min-w-48 max-w-96 group"
|
||||
ref={sidebarRef}
|
||||
style={{ width: props.width }}
|
||||
>
|
||||
|
|
@ -209,19 +166,8 @@ export const EntityExplorerSidebar = memo((props: Props) => {
|
|||
<Pages />
|
||||
{/* Popover that contains the bindings info */}
|
||||
<EntityProperties />
|
||||
{/* SWITCHER */}
|
||||
<div
|
||||
className={`px-3 mt-1 py-2 border-t border-b border-[${Colors.Gallery}]`}
|
||||
>
|
||||
<Switcher activeObj={activeSwitch} switches={switches} />
|
||||
</div>
|
||||
<PanelStack
|
||||
className="flex-grow"
|
||||
initialPanel={{
|
||||
component: Explorer,
|
||||
}}
|
||||
showPanelHeader={false}
|
||||
/>
|
||||
{/* Contains entity explorer & widgets library along with a switcher*/}
|
||||
<Explorer />
|
||||
<AppComments />
|
||||
</div>
|
||||
{/* RESIZER */}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
export const ENTITY_EXPLORER_SEARCH_ID = "entity-explorer-search";
|
||||
export const WIDGETS_SEARCH_ID = "#widgets-search";
|
||||
export const SEARCH_ENTITY = "search-entity";
|
||||
|
|
|
|||
|
|
@ -220,11 +220,11 @@ export const Entity = forwardRef(
|
|||
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
useEffect(() => {
|
||||
if (props.isDefaultExpanded) {
|
||||
if (props.isDefaultExpanded || props.searchKeyword) {
|
||||
open(true);
|
||||
props.onToggle && props.onToggle(true);
|
||||
}
|
||||
}, [props.isDefaultExpanded]);
|
||||
}, [props.isDefaultExpanded, props.searchKeyword]);
|
||||
useEffect(() => {
|
||||
if (!props.searchKeyword && !props.isDefaultExpanded) {
|
||||
open(false);
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@ import React, {
|
|||
import styled from "styled-components";
|
||||
import Divider from "components/editorComponents/Divider";
|
||||
import Search from "./ExplorerSearch";
|
||||
import { NonIdealState, Classes, IPanelProps } from "@blueprintjs/core";
|
||||
import WidgetSidebar from "../WidgetSidebar";
|
||||
import history from "utils/history";
|
||||
import { NonIdealState, Classes } from "@blueprintjs/core";
|
||||
import JSDependencies from "./JSDependencies";
|
||||
import PerformanceTracker, {
|
||||
PerformanceTransactionName,
|
||||
|
|
@ -29,6 +27,8 @@ import Datasources from "./Datasources";
|
|||
import Files from "./Files";
|
||||
import ExplorerWidgetGroup from "./Widgets/WidgetGroup";
|
||||
import { builderURL } from "RouteBuilder";
|
||||
import history from "utils/history";
|
||||
import { SEARCH_ENTITY } from "constants/Explorer";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
height: 100%;
|
||||
|
|
@ -71,7 +71,7 @@ const StyledDivider = styled(Divider)`
|
|||
border-bottom-color: #f0f0f0;
|
||||
`;
|
||||
|
||||
function EntityExplorer(props: IPanelProps) {
|
||||
function EntityExplorer({ isActive }: { isActive: boolean }) {
|
||||
const dispatch = useDispatch();
|
||||
const [searchKeyword, setSearchKeyword] = useState("");
|
||||
const searchInputRef: MutableRefObject<HTMLInputElement | null> = useRef(
|
||||
|
|
@ -86,15 +86,13 @@ function EntityExplorer(props: IPanelProps) {
|
|||
getIsFirstTimeUserOnboardingEnabled,
|
||||
);
|
||||
const noResults = false;
|
||||
const { openPanel } = props;
|
||||
const showWidgetsSidebar = useCallback(() => {
|
||||
history.push(builderURL());
|
||||
openPanel({ component: WidgetSidebar });
|
||||
dispatch(forceOpenWidgetPanel(true));
|
||||
if (isFirstTimeUserOnboardingEnabled) {
|
||||
dispatch(toggleInOnboardingWidgetSelection(true));
|
||||
}
|
||||
}, [openPanel, isFirstTimeUserOnboardingEnabled]);
|
||||
}, [isFirstTimeUserOnboardingEnabled]);
|
||||
|
||||
/**
|
||||
* filter entitites
|
||||
|
|
@ -112,10 +110,14 @@ function EntityExplorer(props: IPanelProps) {
|
|||
};
|
||||
|
||||
return (
|
||||
<Wrapper className={"relative"} ref={explorerRef}>
|
||||
<Wrapper
|
||||
className={`relative overflow-y-auto ${isActive ? "" : "hidden"}`}
|
||||
ref={explorerRef}
|
||||
>
|
||||
{/* SEARCH */}
|
||||
<Search
|
||||
clear={clearSearchInput}
|
||||
id={SEARCH_ENTITY}
|
||||
isHidden
|
||||
onChange={search}
|
||||
ref={searchInputRef}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ export const ExplorerSearch = forwardRef(
|
|||
autoFocus?: boolean;
|
||||
isHidden?: boolean;
|
||||
onChange?: (e: any) => void;
|
||||
id?: string;
|
||||
},
|
||||
ref: Ref<HTMLInputElement>,
|
||||
) => {
|
||||
|
|
@ -62,7 +63,7 @@ export const ExplorerSearch = forwardRef(
|
|||
autoComplete="off"
|
||||
autoFocus
|
||||
className="flex-grow py-2 text-gray-800 bg-transparent placeholder-trueGray-500"
|
||||
id={ENTITY_EXPLORER_SEARCH_ID}
|
||||
id={props.id || ENTITY_EXPLORER_SEARCH_ID}
|
||||
onBlur={() => setFocussed(false)}
|
||||
onChange={onChange}
|
||||
onFocus={() => setFocussed(true)}
|
||||
|
|
@ -78,8 +79,7 @@ export const ExplorerSearch = forwardRef(
|
|||
</div>
|
||||
<div
|
||||
className={classNames({
|
||||
"border-b border-primary-500 transition-all duration-400 absolute bottom-0": true,
|
||||
"w-0": !focussed,
|
||||
"border-b border-primary-500 absolute bottom-0": true,
|
||||
"w-full": focussed,
|
||||
})}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,23 +1,73 @@
|
|||
import { IPanelProps } from "@blueprintjs/core";
|
||||
import React from "react";
|
||||
import { useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { toggleInOnboardingWidgetSelection } from "actions/onboardingActions";
|
||||
import { forceOpenWidgetPanel } from "actions/widgetSidebarActions";
|
||||
import { Switcher } from "components/ads";
|
||||
import { Colors } from "constants/Colors";
|
||||
import { tailwindLayers } from "constants/Layers";
|
||||
import React, { useEffect, useMemo, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { AppState } from "reducers";
|
||||
import { builderURL } from "RouteBuilder";
|
||||
import { getIsFirstTimeUserOnboardingEnabled } from "selectors/onboardingSelectors";
|
||||
import { trimQueryString } from "utils/helpers";
|
||||
import history from "utils/history";
|
||||
import WidgetSidebar from "../WidgetSidebar";
|
||||
import EntityExplorer from "./EntityExplorer";
|
||||
|
||||
const isForceOpenWidgetPanelSelector = (state: AppState) =>
|
||||
const selectForceOpenWidgetPanel = (state: AppState) =>
|
||||
state.ui.onBoarding.forceOpenWidgetPanel;
|
||||
|
||||
function ExplorerContent(props: IPanelProps) {
|
||||
const isForceOpenWidgetPanel = useSelector(isForceOpenWidgetPanelSelector);
|
||||
useEffect(() => {
|
||||
if (isForceOpenWidgetPanel) {
|
||||
props.openPanel({ component: WidgetSidebar });
|
||||
}
|
||||
}, [isForceOpenWidgetPanel]);
|
||||
function ExplorerContent() {
|
||||
const dispatch = useDispatch();
|
||||
const isFirstTimeUserOnboardingEnabled = useSelector(
|
||||
getIsFirstTimeUserOnboardingEnabled,
|
||||
);
|
||||
const switches = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: "explorer",
|
||||
text: "Explorer",
|
||||
action: () => dispatch(forceOpenWidgetPanel(false)),
|
||||
},
|
||||
{
|
||||
id: "widgets",
|
||||
text: "Widgets",
|
||||
action: () => {
|
||||
!(trimQueryString(builderURL()) === window.location.pathname) &&
|
||||
history.push(builderURL());
|
||||
dispatch(forceOpenWidgetPanel(true));
|
||||
if (isFirstTimeUserOnboardingEnabled) {
|
||||
dispatch(toggleInOnboardingWidgetSelection(true));
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
dispatch,
|
||||
forceOpenWidgetPanel,
|
||||
isFirstTimeUserOnboardingEnabled,
|
||||
toggleInOnboardingWidgetSelection,
|
||||
],
|
||||
);
|
||||
const [activeSwitch, setActiveSwitch] = useState(switches[0]);
|
||||
const openWidgetPanel = useSelector(selectForceOpenWidgetPanel);
|
||||
|
||||
return <EntityExplorer {...props} />;
|
||||
useEffect(() => {
|
||||
setActiveSwitch(switches[openWidgetPanel ? 1 : 0]);
|
||||
}, [openWidgetPanel]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex-1 flex flex-col overflow-hidden ${tailwindLayers.entityExplorer}`}
|
||||
>
|
||||
<div
|
||||
className={`flex-shrink-0 px-3 mt-1 py-2 border-t border-b border-[${Colors.Gallery}]`}
|
||||
>
|
||||
<Switcher activeObj={activeSwitch} switches={switches} />
|
||||
</div>
|
||||
<WidgetSidebar isActive={activeSwitch.id === "widgets"} />
|
||||
<EntityExplorer isActive={activeSwitch.id === "explorer"} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ExplorerContent;
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ import {
|
|||
ONBOARDING_STATUS_STEPS_THIRD_ALT,
|
||||
} from "@appsmith/constants/messages";
|
||||
import { getTypographyByKey } from "constants/DefaultTheme";
|
||||
|
||||
import { Colors } from "constants/Colors";
|
||||
import { onboardingCheckListUrl } from "RouteBuilder";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,16 @@
|
|||
import React, { useRef, useEffect, useState } from "react";
|
||||
import React, { useRef, useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import WidgetCard from "./WidgetCard";
|
||||
import { getWidgetCards } from "selectors/editorSelectors";
|
||||
import { IPanelProps } from "@blueprintjs/core";
|
||||
import ExplorerSearch from "./Explorer/ExplorerSearch";
|
||||
import { debounce } from "lodash";
|
||||
import produce from "immer";
|
||||
import { useLocation } from "react-router";
|
||||
|
||||
import {
|
||||
createMessage,
|
||||
WIDGET_SIDEBAR_CAPTION,
|
||||
} from "@appsmith/constants/messages";
|
||||
import { matchBuilderPath } from "constants/routes";
|
||||
import { AppState } from "reducers";
|
||||
|
||||
function WidgetSidebar(props: IPanelProps) {
|
||||
const location = useLocation();
|
||||
function WidgetSidebar({ isActive }: { isActive: boolean }) {
|
||||
const cards = useSelector(getWidgetCards);
|
||||
const [filteredCards, setFilteredCards] = useState(cards);
|
||||
const searchInputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
|
@ -33,17 +27,6 @@ function WidgetSidebar(props: IPanelProps) {
|
|||
}
|
||||
setFilteredCards(filteredCards);
|
||||
};
|
||||
const isForceOpenWidgetPanel = useSelector(
|
||||
(state: AppState) => state.ui.onBoarding.forceOpenWidgetPanel,
|
||||
);
|
||||
|
||||
const onCanvas = matchBuilderPath(window.location.pathname);
|
||||
|
||||
useEffect(() => {
|
||||
if (!onCanvas || isForceOpenWidgetPanel === false) {
|
||||
props.closePanel();
|
||||
}
|
||||
}, [onCanvas, location, isForceOpenWidgetPanel]);
|
||||
|
||||
/**
|
||||
* filter widgets
|
||||
|
|
@ -64,7 +47,9 @@ function WidgetSidebar(props: IPanelProps) {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col overflow-hidden">
|
||||
<div
|
||||
className={`flex flex-col overflow-hidden ${isActive ? "" : "hidden"}`}
|
||||
>
|
||||
<ExplorerSearch
|
||||
autoFocus
|
||||
clear={clearSearchInput}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import {
|
|||
} from "./StyledComponents";
|
||||
import { getCurrentUser as refreshCurrentUser } from "actions/authActions";
|
||||
import { getAppsmithConfigs } from "@appsmith/configs";
|
||||
import { ANONYMOUS_USERNAME } from "constants/userConstants";
|
||||
const { disableLoginForm } = getAppsmithConfigs();
|
||||
|
||||
const ForgotPassword = styled.a`
|
||||
|
|
@ -73,6 +74,8 @@ function General() {
|
|||
dispatch(refreshCurrentUser());
|
||||
}, []);
|
||||
|
||||
if (user?.email === ANONYMOUS_USERNAME) return null;
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<FieldWrapper>
|
||||
|
|
|
|||
|
|
@ -128,7 +128,12 @@ import {
|
|||
collisionCheckPostReflow,
|
||||
getBottomRowAfterReflow,
|
||||
} from "utils/reflowHookUtils";
|
||||
import { PrevReflowState, ReflowDirection, SpaceMap } from "reflow/reflowTypes";
|
||||
import {
|
||||
GridProps,
|
||||
PrevReflowState,
|
||||
ReflowDirection,
|
||||
SpaceMap,
|
||||
} from "reflow/reflowTypes";
|
||||
import { WidgetSpace } from "constants/CanvasEditorConstants";
|
||||
import { reflow } from "reflow";
|
||||
import { getBottomMostRow } from "reflow/reflowUtils";
|
||||
|
|
@ -845,7 +850,6 @@ export function calculateNewWidgetPosition(
|
|||
* @param copiedTotalWidth total width of the copied widgets
|
||||
* @param copiedTopMostRow top row of the top most copied widget
|
||||
* @param copiedLeftMostColumn left column of the left most copied widget
|
||||
* @param shouldGroup boolean to indicate if the user is grouping instead of pasting
|
||||
* @returns
|
||||
*/
|
||||
const getNewPositions = function*(
|
||||
|
|
@ -854,11 +858,7 @@ const getNewPositions = function*(
|
|||
copiedTotalWidth: number,
|
||||
copiedTopMostRow: number,
|
||||
copiedLeftMostColumn: number,
|
||||
shouldGroup: boolean,
|
||||
) {
|
||||
// if it is grouping instead of pasting then skip the pasting logic
|
||||
if (shouldGroup) return {};
|
||||
|
||||
const selectedWidgetIDs: string[] = yield select(getSelectedWidgets);
|
||||
const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets);
|
||||
const {
|
||||
|
|
@ -1187,16 +1187,18 @@ function* pasteWidgetSaga(
|
|||
let widgets: CanvasWidgetsReduxState = canvasWidgets;
|
||||
const selectedWidget: FlattenedWidgetProps<undefined> = yield getSelectedWidgetWhenPasting();
|
||||
|
||||
let reflowedMovementMap,
|
||||
bottomMostRow: number | undefined,
|
||||
gridProps: GridProps | undefined,
|
||||
newPastingPositionMap: SpaceMap | undefined,
|
||||
canvasId;
|
||||
|
||||
let pastingIntoWidgetId: string = yield getParentWidgetIdForPasting(
|
||||
canvasWidgets,
|
||||
selectedWidget,
|
||||
);
|
||||
|
||||
let isThereACollision: boolean = yield isSelectedWidgetsColliding(
|
||||
widgets,
|
||||
copiedWidgetGroups,
|
||||
pastingIntoWidgetId,
|
||||
);
|
||||
let isThereACollision = false;
|
||||
|
||||
// if this is true, selected widgets will be grouped in container
|
||||
if (shouldGroup) {
|
||||
|
|
@ -1204,7 +1206,6 @@ function* pasteWidgetSaga(
|
|||
pastingIntoWidgetId = yield getParentWidgetIdForGrouping(
|
||||
widgets,
|
||||
copiedWidgetGroups,
|
||||
pastingIntoWidgetId,
|
||||
);
|
||||
widgets = yield filterOutSelectedWidgets(
|
||||
copiedWidgetGroups[0].parentId,
|
||||
|
|
@ -1216,10 +1217,20 @@ function* pasteWidgetSaga(
|
|||
pastingIntoWidgetId,
|
||||
);
|
||||
|
||||
copiedWidgetGroups = yield groupWidgetsIntoContainer(
|
||||
//while grouping, the container around the selected widgets will increase by 2 rows,
|
||||
//hence if there are any widgets in that path then we reflow those widgets
|
||||
// If there are already widgets inside the selection box even before grouping
|
||||
//then we will have to move it down to the bottom most row
|
||||
({
|
||||
bottomMostRow,
|
||||
copiedWidgetGroups,
|
||||
gridProps,
|
||||
reflowedMovementMap,
|
||||
} = yield groupWidgetsIntoContainer(
|
||||
copiedWidgetGroups,
|
||||
pastingIntoWidgetId,
|
||||
);
|
||||
isThereACollision,
|
||||
));
|
||||
} else if (isCopiedModalWidget(copiedWidgetGroups, widgets)) {
|
||||
pastingIntoWidgetId = MAIN_CONTAINER_WIDGET_ID;
|
||||
}
|
||||
|
|
@ -1242,25 +1253,27 @@ function* pasteWidgetSaga(
|
|||
widgets,
|
||||
);
|
||||
|
||||
// new pasting positions, the variables are undefined if the positions cannot be calculated,
|
||||
// then it pastes the regular way at the bottom of the canvas
|
||||
const {
|
||||
bottomMostRow,
|
||||
canvasId,
|
||||
gridProps,
|
||||
newPastingPositionMap,
|
||||
reflowedMovementMap,
|
||||
}: NewPastePositionVariables = yield call(
|
||||
getNewPositions,
|
||||
copiedWidgetGroups,
|
||||
action.payload.mouseLocation,
|
||||
copiedTotalWidth,
|
||||
topMostWidget.topRow,
|
||||
leftMostWidget.leftColumn,
|
||||
shouldGroup,
|
||||
);
|
||||
// skip new position calculation if grouping
|
||||
if (!shouldGroup) {
|
||||
// new pasting positions, the variables are undefined if the positions cannot be calculated,
|
||||
// then it pastes the regular way at the bottom of the canvas
|
||||
({
|
||||
bottomMostRow,
|
||||
canvasId,
|
||||
gridProps,
|
||||
newPastingPositionMap,
|
||||
reflowedMovementMap,
|
||||
} = yield call(
|
||||
getNewPositions,
|
||||
copiedWidgetGroups,
|
||||
action.payload.mouseLocation,
|
||||
copiedTotalWidth,
|
||||
topMostWidget.topRow,
|
||||
leftMostWidget.leftColumn,
|
||||
));
|
||||
|
||||
if (canvasId) pastingIntoWidgetId = canvasId;
|
||||
if (canvasId) pastingIntoWidgetId = canvasId;
|
||||
}
|
||||
|
||||
yield all(
|
||||
copiedWidgetGroups.map((copiedWidgets) =>
|
||||
|
|
|
|||
|
|
@ -33,15 +33,24 @@ import {
|
|||
import { getNextEntityName } from "utils/AppsmithUtils";
|
||||
import WidgetFactory from "utils/WidgetFactory";
|
||||
import { getParentWithEnhancementFn } from "./WidgetEnhancementHelpers";
|
||||
import { OccupiedSpace } from "constants/CanvasEditorConstants";
|
||||
import { OccupiedSpace, WidgetSpace } from "constants/CanvasEditorConstants";
|
||||
import { areIntersecting } from "utils/WidgetPropsUtils";
|
||||
import { GridProps, ReflowedSpaceMap, SpaceMap } from "reflow/reflowTypes";
|
||||
import {
|
||||
GridProps,
|
||||
PrevReflowState,
|
||||
ReflowDirection,
|
||||
ReflowedSpaceMap,
|
||||
SpaceMap,
|
||||
} from "reflow/reflowTypes";
|
||||
import {
|
||||
getBaseWidgetClassName,
|
||||
getSlidingCanvasName,
|
||||
getStickyCanvasName,
|
||||
POSITIONED_WIDGET,
|
||||
} from "constants/componentClassNameConstants";
|
||||
import { getWidgetSpacesSelectorForContainer } from "selectors/editorSelectors";
|
||||
import { reflow } from "reflow";
|
||||
import { getBottomRowAfterReflow } from "utils/reflowHookUtils";
|
||||
|
||||
export interface CopiedWidgetGroup {
|
||||
widgetId: string;
|
||||
|
|
@ -992,6 +1001,7 @@ export function isDropTarget(type: WidgetType, includeCanvasWidget = false) {
|
|||
export const groupWidgetsIntoContainer = function*(
|
||||
copiedWidgetGroups: CopiedWidgetGroup[],
|
||||
pastingIntoWidgetId: string,
|
||||
isThereACollision: boolean,
|
||||
) {
|
||||
const containerWidgetId = generateReactKey();
|
||||
const evalTree: DataTree = yield select(getDataTree);
|
||||
|
|
@ -1006,6 +1016,7 @@ export const groupWidgetsIntoContainer = function*(
|
|||
"CANVAS_WIDGET",
|
||||
evalTree,
|
||||
);
|
||||
let reflowedMovementMap, bottomMostRow, gridProps;
|
||||
const {
|
||||
bottomMostWidget,
|
||||
leftMostWidget,
|
||||
|
|
@ -1018,8 +1029,12 @@ export const groupWidgetsIntoContainer = function*(
|
|||
(w) => w.widgetId === copiedWidgetGroup.widgetId,
|
||||
),
|
||||
);
|
||||
|
||||
//calculating parentColumnSpace because the values stored inside widget DSL are not entirely reliable
|
||||
const parentColumnSpace =
|
||||
copiedWidgetGroups[0].list[0].parentColumnSpace || 1;
|
||||
getParentColumnSpace(canvasWidgets, pastingIntoWidgetId) ||
|
||||
copiedWidgetGroups[0].list[0].parentColumnSpace ||
|
||||
1;
|
||||
|
||||
const boundary = {
|
||||
top: _.minBy(copiedWidgets, (copiedWidget) => copiedWidget?.topRow),
|
||||
|
|
@ -1124,13 +1139,73 @@ export const groupWidgetsIntoContainer = function*(
|
|||
|
||||
const flatList = _.flattenDeep(list);
|
||||
|
||||
return [
|
||||
{
|
||||
list: [newContainerWidget, newCanvasWidget, ...flatList],
|
||||
widgetId: newContainerWidget.widgetId,
|
||||
parentId: pastingIntoWidgetId,
|
||||
},
|
||||
];
|
||||
// if there are no collision already then reflow the below widgets by 2 rows.
|
||||
if (!isThereACollision) {
|
||||
const widgetSpacesSelector = getWidgetSpacesSelectorForContainer(
|
||||
pastingIntoWidgetId,
|
||||
);
|
||||
const widgetSpaces: WidgetSpace[] = yield select(widgetSpacesSelector) ||
|
||||
[];
|
||||
|
||||
const copiedWidgetIds = copiedWidgets
|
||||
.map((widget) => widget?.widgetId)
|
||||
.filter((id) => !!id);
|
||||
|
||||
// filter out copiedWidgets from occupied spaces
|
||||
const widgetOccupiedSpaces = widgetSpaces.filter(
|
||||
(widgetSpace) => copiedWidgetIds.indexOf(widgetSpace.id) === -1,
|
||||
);
|
||||
|
||||
// create the object of the new container in the form of OccupiedSpace
|
||||
const containerSpace = {
|
||||
id: "1",
|
||||
left: newContainerWidget.leftColumn,
|
||||
top: newContainerWidget.topRow,
|
||||
right: newContainerWidget.rightColumn,
|
||||
bottom: newContainerWidget.bottomRow,
|
||||
};
|
||||
|
||||
gridProps = {
|
||||
parentColumnSpace,
|
||||
parentRowSpace: GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
||||
maxGridColumns: GridDefaults.DEFAULT_GRID_COLUMNS,
|
||||
};
|
||||
|
||||
//get movement map of reflowed widgets
|
||||
const { movementMap } = reflow(
|
||||
[containerSpace],
|
||||
[containerSpace],
|
||||
widgetOccupiedSpaces,
|
||||
ReflowDirection.BOTTOM,
|
||||
gridProps,
|
||||
true,
|
||||
false,
|
||||
{ prevSpacesMap: {} } as PrevReflowState,
|
||||
);
|
||||
|
||||
reflowedMovementMap = movementMap;
|
||||
|
||||
//get the new calculated bottom row
|
||||
bottomMostRow = getBottomRowAfterReflow(
|
||||
reflowedMovementMap,
|
||||
containerSpace.bottom,
|
||||
widgetOccupiedSpaces,
|
||||
gridProps,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
reflowedMovementMap,
|
||||
bottomMostRow,
|
||||
gridProps,
|
||||
copiedWidgetGroups: [
|
||||
{
|
||||
list: [newContainerWidget, newCanvasWidget, ...flatList],
|
||||
widgetId: newContainerWidget.widgetId,
|
||||
parentId: pastingIntoWidgetId,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1225,27 +1300,20 @@ export const isSelectedWidgetsColliding = function*(
|
|||
widget.parentId === pastingIntoWidgetId && widget.type !== "MODAL_WIDGET",
|
||||
);
|
||||
|
||||
let isColliding = false;
|
||||
|
||||
for (let i = 0; i < widgetsArray.length; i++) {
|
||||
const widget = widgetsArray[i];
|
||||
|
||||
if (
|
||||
widget.bottomRow + 2 < topMostWidget.topRow ||
|
||||
widget.topRow > bottomMostWidget.bottomRow
|
||||
) {
|
||||
isColliding = false;
|
||||
} else if (
|
||||
widget.rightColumn < leftMostWidget.leftColumn ||
|
||||
widget.leftColumn > rightMostWidget.rightColumn
|
||||
) {
|
||||
isColliding = false;
|
||||
} else {
|
||||
!(
|
||||
widget.leftColumn >= rightMostWidget.rightColumn ||
|
||||
widget.rightColumn <= leftMostWidget.leftColumn ||
|
||||
widget.topRow >= bottomMostWidget.bottomRow ||
|
||||
widget.bottomRow <= topMostWidget.topRow
|
||||
)
|
||||
)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return isColliding;
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1351,8 +1419,8 @@ export const getParentBottomRowAfterAddingWidget = (
|
|||
export function* getParentWidgetIdForGrouping(
|
||||
widgets: CanvasWidgetsReduxState,
|
||||
copiedWidgetGroups: CopiedWidgetGroup[],
|
||||
pastingIntoWidgetId: string,
|
||||
) {
|
||||
const pastingIntoWidgetId = copiedWidgetGroups[0]?.parentId;
|
||||
const widgetIds = copiedWidgetGroups.map(
|
||||
(widgetGroup) => widgetGroup.widgetId,
|
||||
);
|
||||
|
|
@ -1445,6 +1513,33 @@ export function purgeOrphanedDynamicPaths(widget: WidgetProps) {
|
|||
return widget;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param canvasWidgets
|
||||
* @param pastingIntoWidgetId
|
||||
* @returns
|
||||
*/
|
||||
export function getParentColumnSpace(
|
||||
canvasWidgets: CanvasWidgetsReduxState,
|
||||
pastingIntoWidgetId: string,
|
||||
) {
|
||||
const containerId = getContainerIdForCanvas(pastingIntoWidgetId);
|
||||
|
||||
const containerWidget = canvasWidgets[containerId];
|
||||
const canvasDOM = document.querySelector(
|
||||
`#${getSlidingCanvasName(pastingIntoWidgetId)}`,
|
||||
);
|
||||
|
||||
if (!canvasDOM || !containerWidget) return;
|
||||
|
||||
const rect = canvasDOM.getBoundingClientRect();
|
||||
|
||||
// get Grid values such as snapRowSpace and snapColumnSpace
|
||||
const { snapGrid } = getSnappedGrid(containerWidget, rect.width);
|
||||
|
||||
return snapGrid?.snapColumnSpace;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function to extend the lodash's get function to check
|
||||
* paths which have dots in it's key
|
||||
|
|
|
|||
|
|
@ -62,7 +62,8 @@ describe("#parse", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("returns unmodified schema when existing field's value in data source changes to null/undefined", () => {
|
||||
it("returns unmodified schema when existing field's value in data source changes to null and back", () => {
|
||||
// Get the initial schema
|
||||
const initialSchema = SchemaParser.parse(widgetName, {
|
||||
currSourceData: testData.initialDataset.dataSource,
|
||||
schema: {},
|
||||
|
|
@ -71,45 +72,390 @@ describe("#parse", () => {
|
|||
|
||||
expect(initialSchema).toEqual(testData.initialDataset.schemaOutput);
|
||||
|
||||
// With null field
|
||||
const nulledDataSource = klona(testData.initialDataset.dataSource);
|
||||
set(nulledDataSource, "dob", null);
|
||||
// Set all keys to null
|
||||
const nulledSourceData = klona(testData.initialDataset.dataSource);
|
||||
set(nulledSourceData, "name", null);
|
||||
set(nulledSourceData, "age", null);
|
||||
set(nulledSourceData, "dob", null);
|
||||
set(nulledSourceData, "boolean", null);
|
||||
set(nulledSourceData, "hobbies", null);
|
||||
set(nulledSourceData, "%%", null);
|
||||
set(nulledSourceData, "हिन्दि", null);
|
||||
set(nulledSourceData, "education", null);
|
||||
set(nulledSourceData, "address", null);
|
||||
|
||||
const expectedNulledSchema = klona(initialSchema);
|
||||
set(expectedNulledSchema, "__root_schema__.children.dob.sourceData", null);
|
||||
set(expectedNulledSchema, "__root_schema__.sourceData.dob", null);
|
||||
// Set the sourceData entry in each SchemaItem to null (only property that changes)
|
||||
const expectedSchema = klona(initialSchema);
|
||||
set(expectedSchema, "__root_schema__.children.name.sourceData", null);
|
||||
set(expectedSchema, "__root_schema__.sourceData.name", null);
|
||||
set(expectedSchema, "__root_schema__.children.age.sourceData", null);
|
||||
set(expectedSchema, "__root_schema__.sourceData.age", null);
|
||||
set(expectedSchema, "__root_schema__.children.dob.sourceData", null);
|
||||
set(expectedSchema, "__root_schema__.sourceData.dob", null);
|
||||
set(expectedSchema, "__root_schema__.children.boolean.sourceData", null);
|
||||
set(expectedSchema, "__root_schema__.sourceData.boolean", null);
|
||||
set(expectedSchema, "__root_schema__.children.hobbies.sourceData", null);
|
||||
set(expectedSchema, "__root_schema__.sourceData.hobbies", null);
|
||||
set(expectedSchema, "__root_schema__.children.education.sourceData", null);
|
||||
set(expectedSchema, "__root_schema__.sourceData.education", null);
|
||||
set(expectedSchema, "__root_schema__.children.__.sourceData", null);
|
||||
set(expectedSchema, "__root_schema__.sourceData['%%']", null);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.xn__j2bd4cyac6f.sourceData",
|
||||
null,
|
||||
);
|
||||
set(expectedSchema, "__root_schema__.sourceData.हिन्दि", null);
|
||||
set(expectedSchema, "__root_schema__.children.address.sourceData", null);
|
||||
set(expectedSchema, "__root_schema__.sourceData.address", null);
|
||||
|
||||
const schemaWithNulledField = SchemaParser.parse(widgetName, {
|
||||
currSourceData: nulledDataSource,
|
||||
// Parse with the nulled sourceData
|
||||
const schemaWithNullKeys = SchemaParser.parse(widgetName, {
|
||||
currSourceData: nulledSourceData,
|
||||
schema: initialSchema,
|
||||
fieldThemeStylesheets: testData.fieldThemeStylesheets,
|
||||
});
|
||||
|
||||
expect(schemaWithNulledField).toEqual(expectedNulledSchema);
|
||||
expect(schemaWithNullKeys).toEqual(expectedSchema);
|
||||
|
||||
// With undefined field
|
||||
const undefinedDataSource = klona(nulledDataSource);
|
||||
set(undefinedDataSource, "boolean", undefined);
|
||||
|
||||
const expectedUndefinedSchema = klona(expectedNulledSchema);
|
||||
set(
|
||||
expectedUndefinedSchema,
|
||||
"__root_schema__.children.boolean.sourceData",
|
||||
undefined,
|
||||
);
|
||||
set(
|
||||
expectedUndefinedSchema,
|
||||
"__root_schema__.sourceData.boolean",
|
||||
undefined,
|
||||
);
|
||||
|
||||
const schemaWithUndefinedField = SchemaParser.parse(widgetName, {
|
||||
currSourceData: undefinedDataSource,
|
||||
schema: schemaWithNulledField,
|
||||
/**
|
||||
* Parse with initial sourceData to check if previous schema with null sourceData
|
||||
* can still retain the schema structure
|
||||
*/
|
||||
const schemaWithRevertedData = SchemaParser.parse(widgetName, {
|
||||
currSourceData: testData.initialDataset.dataSource,
|
||||
schema: schemaWithNullKeys,
|
||||
fieldThemeStylesheets: testData.fieldThemeStylesheets,
|
||||
});
|
||||
|
||||
expect(schemaWithUndefinedField).toEqual(expectedUndefinedSchema);
|
||||
expect(schemaWithRevertedData).toEqual(
|
||||
testData.initialDataset.schemaOutput,
|
||||
);
|
||||
});
|
||||
|
||||
it("returns unmodified schema when existing fields value in data source changes to undefined and back", () => {
|
||||
// Get the initial schema
|
||||
const initialSchema = SchemaParser.parse(widgetName, {
|
||||
currSourceData: testData.initialDataset.dataSource,
|
||||
schema: {},
|
||||
fieldThemeStylesheets: testData.fieldThemeStylesheets,
|
||||
});
|
||||
|
||||
expect(initialSchema).toEqual(testData.initialDataset.schemaOutput);
|
||||
|
||||
// Set all keys to undefined
|
||||
const undefinedDataSource = klona(testData.initialDataset.dataSource);
|
||||
set(undefinedDataSource, "name", undefined);
|
||||
set(undefinedDataSource, "age", undefined);
|
||||
set(undefinedDataSource, "dob", undefined);
|
||||
set(undefinedDataSource, "boolean", undefined);
|
||||
set(undefinedDataSource, "hobbies", undefined);
|
||||
set(undefinedDataSource, "%%", undefined);
|
||||
set(undefinedDataSource, "हिन्दि", undefined);
|
||||
set(undefinedDataSource, "education", undefined);
|
||||
set(undefinedDataSource, "address", undefined);
|
||||
|
||||
// Set the sourceData entry in each SchemaItem to undefined (only property that changes)
|
||||
const expectedSchema = klona(initialSchema);
|
||||
set(expectedSchema, "__root_schema__.children.name.sourceData", undefined);
|
||||
set(expectedSchema, "__root_schema__.sourceData.name", undefined);
|
||||
set(expectedSchema, "__root_schema__.children.age.sourceData", undefined);
|
||||
set(expectedSchema, "__root_schema__.sourceData.age", undefined);
|
||||
set(expectedSchema, "__root_schema__.children.dob.sourceData", undefined);
|
||||
set(expectedSchema, "__root_schema__.sourceData.dob", undefined);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.boolean.sourceData",
|
||||
undefined,
|
||||
);
|
||||
set(expectedSchema, "__root_schema__.sourceData.boolean", undefined);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.hobbies.sourceData",
|
||||
undefined,
|
||||
);
|
||||
set(expectedSchema, "__root_schema__.sourceData.hobbies", undefined);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.education.sourceData",
|
||||
undefined,
|
||||
);
|
||||
set(expectedSchema, "__root_schema__.sourceData.education", undefined);
|
||||
set(expectedSchema, "__root_schema__.children.__.sourceData", undefined);
|
||||
set(expectedSchema, "__root_schema__.sourceData['%%']", undefined);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.xn__j2bd4cyac6f.sourceData",
|
||||
undefined,
|
||||
);
|
||||
set(expectedSchema, "__root_schema__.sourceData.हिन्दि", undefined);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.address.sourceData",
|
||||
undefined,
|
||||
);
|
||||
set(expectedSchema, "__root_schema__.sourceData.address", undefined);
|
||||
|
||||
// Parse with the undefined sourceData keys
|
||||
const schemaWithUndefinedKeys = SchemaParser.parse(widgetName, {
|
||||
currSourceData: undefinedDataSource,
|
||||
schema: initialSchema,
|
||||
fieldThemeStylesheets: testData.fieldThemeStylesheets,
|
||||
});
|
||||
|
||||
expect(schemaWithUndefinedKeys).toEqual(expectedSchema);
|
||||
|
||||
/**
|
||||
* Parse with initial sourceData to check if previous schema with null sourceData
|
||||
* can still retain the schema structure
|
||||
*/
|
||||
const schemaWithRevertedData = SchemaParser.parse(widgetName, {
|
||||
currSourceData: testData.initialDataset.dataSource,
|
||||
schema: schemaWithUndefinedKeys,
|
||||
fieldThemeStylesheets: testData.fieldThemeStylesheets,
|
||||
});
|
||||
|
||||
expect(schemaWithRevertedData).toEqual(
|
||||
testData.initialDataset.schemaOutput,
|
||||
);
|
||||
});
|
||||
|
||||
it("returns unmodified schema when existing inner field's value in data source changes to null and back", () => {
|
||||
// Get the initial schema
|
||||
const initialSchema = SchemaParser.parse(widgetName, {
|
||||
currSourceData: testData.initialDataset.dataSource,
|
||||
schema: {},
|
||||
fieldThemeStylesheets: testData.fieldThemeStylesheets,
|
||||
});
|
||||
|
||||
expect(initialSchema).toEqual(testData.initialDataset.schemaOutput);
|
||||
|
||||
// Set all keys to null
|
||||
const nulledSourceData = klona(testData.initialDataset.dataSource);
|
||||
set(nulledSourceData, "address.Line1", null);
|
||||
set(nulledSourceData, "address.city", null);
|
||||
set(nulledSourceData, "education[0].college", null);
|
||||
set(nulledSourceData, "education[0].number", null);
|
||||
set(nulledSourceData, "education[0].graduationDate", null);
|
||||
set(nulledSourceData, "education[0].boolean", null);
|
||||
|
||||
// Set the sourceData entry in each SchemaItem to null (only property that changes)
|
||||
const expectedSchema = klona(initialSchema);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.address.children.Line1.sourceData",
|
||||
null,
|
||||
);
|
||||
set(expectedSchema, "__root_schema__.sourceData.address.Line1", null);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.address.children.city.sourceData",
|
||||
null,
|
||||
);
|
||||
set(expectedSchema, "__root_schema__.sourceData.address.city", null);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.education.children.__array_item__.children.college.sourceData",
|
||||
null,
|
||||
);
|
||||
set(expectedSchema, "__root_schema__.children.address.sourceData", {
|
||||
Line1: null,
|
||||
city: null,
|
||||
});
|
||||
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.sourceData.education[0].college",
|
||||
null,
|
||||
);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.education.children.__array_item__.children.number.sourceData",
|
||||
null,
|
||||
);
|
||||
set(expectedSchema, "__root_schema__.sourceData.education[0].number", null);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.education.children.__array_item__.children.graduationDate.sourceData",
|
||||
null,
|
||||
);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.sourceData.education[0].graduationDate",
|
||||
null,
|
||||
);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.education.children.__array_item__.children.boolean.sourceData",
|
||||
null,
|
||||
);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.sourceData.education[0].boolean",
|
||||
null,
|
||||
);
|
||||
set(expectedSchema, "__root_schema__.children.education.sourceData", [
|
||||
{
|
||||
college: null,
|
||||
number: null,
|
||||
graduationDate: null,
|
||||
boolean: null,
|
||||
},
|
||||
]);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.education.children.__array_item__.sourceData",
|
||||
{
|
||||
college: null,
|
||||
number: null,
|
||||
graduationDate: null,
|
||||
boolean: null,
|
||||
},
|
||||
);
|
||||
|
||||
// Parse with the nulled sourceData
|
||||
const schemaWithNullKeys = SchemaParser.parse(widgetName, {
|
||||
currSourceData: nulledSourceData,
|
||||
schema: initialSchema,
|
||||
fieldThemeStylesheets: testData.fieldThemeStylesheets,
|
||||
});
|
||||
|
||||
expect(schemaWithNullKeys).toEqual(expectedSchema);
|
||||
|
||||
/**
|
||||
* Parse with initial sourceData to check if previous schema with null sourceData
|
||||
* can still retain the schema structure
|
||||
*/
|
||||
const schemaWithRevertedData = SchemaParser.parse(widgetName, {
|
||||
currSourceData: testData.initialDataset.dataSource,
|
||||
schema: schemaWithNullKeys,
|
||||
fieldThemeStylesheets: testData.fieldThemeStylesheets,
|
||||
});
|
||||
|
||||
expect(schemaWithRevertedData).toEqual(
|
||||
testData.initialDataset.schemaOutput,
|
||||
);
|
||||
});
|
||||
|
||||
it("returns unmodified schema when existing inner field's value in data source changes to undefined and back", () => {
|
||||
// Get the initial schema
|
||||
const initialSchema = SchemaParser.parse(widgetName, {
|
||||
currSourceData: testData.initialDataset.dataSource,
|
||||
schema: {},
|
||||
fieldThemeStylesheets: testData.fieldThemeStylesheets,
|
||||
});
|
||||
|
||||
expect(initialSchema).toEqual(testData.initialDataset.schemaOutput);
|
||||
|
||||
// Set all keys to undefined
|
||||
const undefinedSourceData = klona(testData.initialDataset.dataSource);
|
||||
set(undefinedSourceData, "address.Line1", undefined);
|
||||
set(undefinedSourceData, "address.city", undefined);
|
||||
set(undefinedSourceData, "education[0].college", undefined);
|
||||
set(undefinedSourceData, "education[0].number", undefined);
|
||||
set(undefinedSourceData, "education[0].graduationDate", undefined);
|
||||
set(undefinedSourceData, "education[0].boolean", undefined);
|
||||
|
||||
// Set the sourceData entry in each SchemaItem to undefined (only property that changes)
|
||||
const expectedSchema = klona(initialSchema);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.address.children.Line1.sourceData",
|
||||
undefined,
|
||||
);
|
||||
set(expectedSchema, "__root_schema__.sourceData.address.Line1", undefined);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.address.children.city.sourceData",
|
||||
undefined,
|
||||
);
|
||||
set(expectedSchema, "__root_schema__.sourceData.address.city", undefined);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.education.children.__array_item__.children.college.sourceData",
|
||||
undefined,
|
||||
);
|
||||
set(expectedSchema, "__root_schema__.children.address.sourceData", {
|
||||
Line1: undefined,
|
||||
city: undefined,
|
||||
});
|
||||
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.sourceData.education[0].college",
|
||||
undefined,
|
||||
);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.education.children.__array_item__.children.number.sourceData",
|
||||
undefined,
|
||||
);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.sourceData.education[0].number",
|
||||
undefined,
|
||||
);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.education.children.__array_item__.children.graduationDate.sourceData",
|
||||
undefined,
|
||||
);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.sourceData.education[0].graduationDate",
|
||||
undefined,
|
||||
);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.education.children.__array_item__.children.boolean.sourceData",
|
||||
undefined,
|
||||
);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.sourceData.education[0].boolean",
|
||||
undefined,
|
||||
);
|
||||
set(expectedSchema, "__root_schema__.children.education.sourceData", [
|
||||
{
|
||||
college: undefined,
|
||||
number: undefined,
|
||||
graduationDate: undefined,
|
||||
boolean: undefined,
|
||||
},
|
||||
]);
|
||||
set(
|
||||
expectedSchema,
|
||||
"__root_schema__.children.education.children.__array_item__.sourceData",
|
||||
{
|
||||
college: undefined,
|
||||
number: undefined,
|
||||
graduationDate: undefined,
|
||||
boolean: undefined,
|
||||
},
|
||||
);
|
||||
|
||||
// Parse with the undefined sourceData
|
||||
const schemaWithUndefinedKeys = SchemaParser.parse(widgetName, {
|
||||
currSourceData: undefinedSourceData,
|
||||
schema: initialSchema,
|
||||
fieldThemeStylesheets: testData.fieldThemeStylesheets,
|
||||
});
|
||||
|
||||
expect(schemaWithUndefinedKeys).toEqual(expectedSchema);
|
||||
|
||||
/**
|
||||
* Parse with initial sourceData to check if previous schema with undefined sourceData
|
||||
* can still retain the schema structure
|
||||
*/
|
||||
const schemaWithRevertedData = SchemaParser.parse(widgetName, {
|
||||
currSourceData: testData.initialDataset.dataSource,
|
||||
schema: schemaWithUndefinedKeys,
|
||||
fieldThemeStylesheets: testData.fieldThemeStylesheets,
|
||||
});
|
||||
|
||||
expect(schemaWithRevertedData).toEqual(
|
||||
testData.initialDataset.schemaOutput,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -27,11 +27,10 @@ import {
|
|||
} from "./constants";
|
||||
import { getFieldStylesheet } from "./helper";
|
||||
|
||||
type Obj = Record<string, any>;
|
||||
type JSON = Obj | Obj[];
|
||||
type Obj = Record<string, unknown>;
|
||||
|
||||
type ParserOptions = {
|
||||
currSourceData?: JSON | string;
|
||||
currSourceData?: unknown;
|
||||
fieldThemeStylesheets?: FieldThemeStylesheet;
|
||||
fieldType?: FieldType;
|
||||
isCustomField?: boolean;
|
||||
|
|
@ -60,11 +59,15 @@ type GetKeysFromSchemaOptions = {
|
|||
};
|
||||
|
||||
type ParseOptions = {
|
||||
currSourceData?: JSON;
|
||||
currSourceData?: unknown;
|
||||
schema?: Schema;
|
||||
fieldThemeStylesheets?: FieldThemeStylesheet;
|
||||
};
|
||||
|
||||
function isObject(val: unknown): val is Obj {
|
||||
return typeof val === "object" && !Array.isArray(val) && val !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* This method takes in array of object and squishes every object in the
|
||||
|
|
@ -604,7 +607,7 @@ class SchemaParser {
|
|||
|
||||
// This method deals with the conversion of array data to a schema
|
||||
static convertArrayToSchema = ({
|
||||
currSourceData = [],
|
||||
currSourceData,
|
||||
fieldThemeStylesheets,
|
||||
prevSchema = {},
|
||||
sourceDataPath,
|
||||
|
|
@ -612,7 +615,12 @@ class SchemaParser {
|
|||
...rest
|
||||
}: Omit<ParserOptions, "identifier">): Schema => {
|
||||
const schema = klona(prevSchema);
|
||||
const currData = normalizeArrayValue(currSourceData as any[]);
|
||||
|
||||
if (!Array.isArray(currSourceData)) {
|
||||
return schema;
|
||||
}
|
||||
|
||||
const currData = normalizeArrayValue(currSourceData);
|
||||
|
||||
const prevDataType = schema[ARRAY_ITEM_KEY]?.dataType;
|
||||
const currDataType = dataTypeFor(currData);
|
||||
|
|
@ -644,7 +652,7 @@ class SchemaParser {
|
|||
|
||||
// This method deals with the conversion of object data to a schema
|
||||
static convertObjectToSchema = ({
|
||||
currSourceData = {},
|
||||
currSourceData,
|
||||
prevSchema = {},
|
||||
sourceDataPath,
|
||||
widgetName,
|
||||
|
|
@ -654,8 +662,10 @@ class SchemaParser {
|
|||
const origIdentifierToIdentifierMap = mapOriginalIdentifierToSanitizedIdentifier(
|
||||
schema,
|
||||
);
|
||||
const currObj = currSourceData as Obj;
|
||||
|
||||
if (!isObject(currSourceData)) {
|
||||
return schema;
|
||||
}
|
||||
const customFieldAccessors = getKeysFromSchema(prevSchema, ["accessor"], {
|
||||
onlyCustomFieldKeys: true,
|
||||
});
|
||||
|
|
@ -683,7 +693,7 @@ class SchemaParser {
|
|||
modifiedKeys.forEach((modifiedKey) => {
|
||||
const identifier = origIdentifierToIdentifierMap[modifiedKey];
|
||||
const prevSchemaItem = klona(schema[identifier]);
|
||||
const currData = currObj[modifiedKey];
|
||||
const currData = currSourceData[modifiedKey];
|
||||
const prevData = prevSchemaItem.sourceData;
|
||||
const currDataType = dataTypeFor(currData);
|
||||
const prevDataType = schema[identifier].dataType;
|
||||
|
|
@ -743,7 +753,7 @@ class SchemaParser {
|
|||
newKeys.forEach((newKey) => {
|
||||
const schemaItem = SchemaParser.getSchemaItemFor(newKey, {
|
||||
...rest,
|
||||
currSourceData: currObj[newKey],
|
||||
currSourceData: currSourceData[newKey],
|
||||
sourceDataPath: getSourcePath(newKey, sourceDataPath),
|
||||
identifier: sanitizeSchemaItemKey(newKey, schema),
|
||||
widgetName,
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ class PhoneInputWidget extends BaseInputWidget<
|
|||
propertyName: "defaultText",
|
||||
label: "Default Text",
|
||||
controlType: "INPUT_TEXT",
|
||||
placeholderText: "John Doe",
|
||||
placeholderText: "(000) 000-0000",
|
||||
isBindProperty: true,
|
||||
isTriggerProperty: false,
|
||||
validation: {
|
||||
|
|
@ -115,7 +115,7 @@ class PhoneInputWidget extends BaseInputWidget<
|
|||
fn: defaultValueValidation,
|
||||
expected: {
|
||||
type: "string",
|
||||
example: `000 0000`,
|
||||
example: `(000) 000-0000`,
|
||||
autocompleteDataType: AutocompleteDataType.STRING,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -89,13 +89,7 @@ public class ApplicationControllerCE extends BaseController<ApplicationService,
|
|||
public Mono<ResponseDTO<Boolean>> publish(@PathVariable String defaultApplicationId,
|
||||
@RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName) {
|
||||
return applicationPageService.publish(defaultApplicationId, branchName, true)
|
||||
.flatMap(application ->
|
||||
// This event should parallel a similar event sent from the client, so we want it to be sent by the
|
||||
// controller and not the service method.
|
||||
applicationPageService.sendApplicationPublishedEvent(application)
|
||||
// This will only be called when the publishing was successful, so we can always return `true` here.
|
||||
.thenReturn(new ResponseDTO<>(HttpStatus.OK.value(), true, null))
|
||||
);
|
||||
.thenReturn(new ResponseDTO<>(HttpStatus.OK.value(), true, null));
|
||||
}
|
||||
|
||||
@PutMapping("/{defaultApplicationId}/page/{defaultPageId}/makeDefault")
|
||||
|
|
|
|||
|
|
@ -51,8 +51,6 @@ public interface ApplicationPageServiceCE {
|
|||
|
||||
void generateAndSetPagePolicies(Application application, PageDTO page);
|
||||
|
||||
Mono<Void> sendApplicationPublishedEvent(Application application);
|
||||
|
||||
Mono<ApplicationPagesDTO> reorderPage(String applicationId, String pageId, Integer order, String branchName);
|
||||
|
||||
Mono<Application> deleteApplicationByResource(Application application);
|
||||
|
|
|
|||
|
|
@ -892,7 +892,7 @@ public class ApplicationPageServiceCEImpl implements ApplicationPageServiceCE {
|
|||
application -> themeService.publishTheme(application.getId())
|
||||
);
|
||||
|
||||
Flux<NewPage> publishApplicationAndPages = applicationMono
|
||||
Mono<List<NewPage>> publishApplicationAndPages = applicationMono
|
||||
//Return all the pages in the Application
|
||||
.flatMap(application -> {
|
||||
List<ApplicationPage> pages = application.getPages();
|
||||
|
|
@ -952,10 +952,11 @@ public class ApplicationPageServiceCEImpl implements ApplicationPageServiceCE {
|
|||
page.setPublishedPage(page.getUnpublishedPage());
|
||||
return page;
|
||||
}))
|
||||
.flatMap(newPageService::save)
|
||||
.collectList()
|
||||
.flatMapMany(newPageService::saveAll);
|
||||
.cache(); // caching as we'll need this to send analytics attributes after publishing the app
|
||||
|
||||
Flux<NewAction> publishedActionsFlux = newActionService
|
||||
Mono<List<NewAction>> publishedActionsListMono = newActionService
|
||||
.findAllByApplicationIdAndViewMode(applicationId, false, MANAGE_ACTIONS, null)
|
||||
.flatMap(newAction -> {
|
||||
// If the action was deleted in edit mode, now this document can be safely archived
|
||||
|
|
@ -967,10 +968,11 @@ public class ApplicationPageServiceCEImpl implements ApplicationPageServiceCE {
|
|||
newAction.setPublishedAction(newAction.getUnpublishedAction());
|
||||
return Mono.just(newAction);
|
||||
})
|
||||
.flatMap(newActionService::save)
|
||||
.collectList()
|
||||
.flatMapMany(newActionService::saveAll);
|
||||
.cache(); // caching as we'll need this to send analytics attributes after publishing the app
|
||||
|
||||
Flux<ActionCollection> publishedCollectionsFlux = actionCollectionService
|
||||
Mono<List<ActionCollection>> publishedActionCollectionsListMono = actionCollectionService
|
||||
.findAllByApplicationIdAndViewMode(applicationId, false, MANAGE_ACTIONS, null)
|
||||
.flatMap(collection -> {
|
||||
// If the collection was deleted in edit mode, now this can be safely deleted from the repository
|
||||
|
|
@ -982,16 +984,42 @@ public class ApplicationPageServiceCEImpl implements ApplicationPageServiceCE {
|
|||
collection.setPublishedCollection(collection.getUnpublishedCollection());
|
||||
return Mono.just(collection);
|
||||
})
|
||||
.collectList()
|
||||
.flatMapMany(actionCollectionService::saveAll);
|
||||
.flatMap(actionCollectionService::save)
|
||||
.collectList();
|
||||
|
||||
return Mono.when(
|
||||
publishApplicationAndPages.collectList(),
|
||||
publishedActionsFlux.collectList(),
|
||||
publishedCollectionsFlux,
|
||||
publishApplicationAndPages,
|
||||
publishedActionsListMono,
|
||||
publishedActionCollectionsListMono,
|
||||
publishThemeMono
|
||||
)
|
||||
.then(applicationMono);
|
||||
.then(sendApplicationPublishedEvent(publishApplicationAndPages, publishedActionsListMono, publishedActionCollectionsListMono, applicationId));
|
||||
}
|
||||
|
||||
private Mono<Application> sendApplicationPublishedEvent(Mono<List<NewPage>> publishApplicationAndPages,
|
||||
Mono<List<NewAction>> publishedActionsFlux,
|
||||
Mono<List<ActionCollection>> publishedActionsCollectionFlux,
|
||||
String applicationId) {
|
||||
return Mono.zip(
|
||||
publishApplicationAndPages,
|
||||
publishedActionsFlux,
|
||||
publishedActionsCollectionFlux,
|
||||
// not using existing applicationMono because we need the latest Application after published
|
||||
applicationService.findById(applicationId, MANAGE_APPLICATIONS)
|
||||
)
|
||||
.flatMap(objects -> {
|
||||
Application application = objects.getT4();
|
||||
Map<String, Object> extraProperties = new HashMap<>();
|
||||
extraProperties.put("pageCount", objects.getT1().size());
|
||||
extraProperties.put("queryCount", objects.getT2().size());
|
||||
extraProperties.put("actionCollectionCount", objects.getT3().size());
|
||||
extraProperties.put("appId", defaultIfNull(application.getId(), ""));
|
||||
extraProperties.put("appName", defaultIfNull(application.getName(), ""));
|
||||
extraProperties.put("orgId", defaultIfNull(application.getOrganizationId(), ""));
|
||||
extraProperties.put("publishedAt", defaultIfNull(application.getLastDeployedAt(), ""));
|
||||
|
||||
return analyticsService.sendObjectEvent(AnalyticsEvents.PUBLISH_APPLICATION, application, extraProperties);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1001,34 +1029,6 @@ public class ApplicationPageServiceCEImpl implements ApplicationPageServiceCE {
|
|||
.map(responseUtils::updateApplicationWithDefaultResources);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> sendApplicationPublishedEvent(Application application) {
|
||||
if (!analyticsService.isActive()) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
return sessionUserService.getCurrentUser()
|
||||
.flatMap(user -> {
|
||||
int publishedPageCount = 0;
|
||||
if(application.getPublishedPages() != null) {
|
||||
publishedPageCount = application.getPublishedPages().size();
|
||||
}
|
||||
|
||||
analyticsService.sendEvent(
|
||||
AnalyticsEvents.PUBLISH_APPLICATION.getEventName(),
|
||||
user.getUsername(),
|
||||
Map.of(
|
||||
"appId", defaultIfNull(application.getId(), ""),
|
||||
"appName", defaultIfNull(application.getName(), ""),
|
||||
"orgId", defaultIfNull(application.getOrganizationId(), ""),
|
||||
"pageCount", publishedPageCount + "",
|
||||
"publishedAt", defaultIfNull(application.getLastDeployedAt(), "")
|
||||
)
|
||||
);
|
||||
return Mono.empty();
|
||||
});
|
||||
}
|
||||
|
||||
/** This function walks through all the pages and reorders them and updates the order as per the user preference.
|
||||
* A page can be moved up or down from the current position and accordingly the order of the remaining page changes.
|
||||
* @param defaultAppId The id of the Application
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user