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
|
|
@ -7,13 +7,13 @@
|
||||||
"detachFromLayout": true,
|
"detachFromLayout": true,
|
||||||
"widgetId": "0",
|
"widgetId": "0",
|
||||||
"topRow": 0,
|
"topRow": 0,
|
||||||
"bottomRow": 820,
|
"bottomRow": 1050,
|
||||||
"containerStyle": "none",
|
"containerStyle": "none",
|
||||||
"snapRows": 125,
|
"snapRows": 125,
|
||||||
"parentRowSpace": 1,
|
"parentRowSpace": 1,
|
||||||
"type": "CANVAS_WIDGET",
|
"type": "CANVAS_WIDGET",
|
||||||
"canExtend": true,
|
"canExtend": true,
|
||||||
"version": 23,
|
"version": 58,
|
||||||
"minHeight": 830,
|
"minHeight": 830,
|
||||||
"parentColumnSpace": 1,
|
"parentColumnSpace": 1,
|
||||||
"dynamicTriggerPathList": [],
|
"dynamicTriggerPathList": [],
|
||||||
|
|
@ -21,9 +21,10 @@
|
||||||
"leftColumn": 0,
|
"leftColumn": 0,
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
|
"labelTextSize": "0.875rem",
|
||||||
|
"boxShadow": "none",
|
||||||
"widgetName": "Chart1",
|
"widgetName": "Chart1",
|
||||||
"rightColumn": 26,
|
"rightColumn": 26,
|
||||||
"allowScroll": false,
|
|
||||||
"widgetId": "ypstklohw5",
|
"widgetId": "ypstklohw5",
|
||||||
"topRow": 4,
|
"topRow": 4,
|
||||||
"bottomRow": 36,
|
"bottomRow": 36,
|
||||||
|
|
@ -72,6 +73,7 @@
|
||||||
"parentColumnSpace": 19.65625,
|
"parentColumnSpace": 19.65625,
|
||||||
"chartName": "Last week's revenue",
|
"chartName": "Last week's revenue",
|
||||||
"leftColumn": 2,
|
"leftColumn": 2,
|
||||||
|
"borderRadius": "0px",
|
||||||
"xAxisName": "Last Week",
|
"xAxisName": "Last Week",
|
||||||
"customFusionChartConfig": {
|
"customFusionChartConfig": {
|
||||||
"type": "column2d",
|
"type": "column2d",
|
||||||
|
|
@ -128,6 +130,8 @@
|
||||||
"chartType": "LINE_CHART"
|
"chartType": "LINE_CHART"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"labelTextSize": "0.875rem",
|
||||||
|
"boxShadow": "none",
|
||||||
"backgroundColor": "#FFFFFF",
|
"backgroundColor": "#FFFFFF",
|
||||||
"widgetName": "Container1",
|
"widgetName": "Container1",
|
||||||
"rightColumn": 62,
|
"rightColumn": 62,
|
||||||
|
|
@ -143,8 +147,11 @@
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"parentColumnSpace": 19.65625,
|
"parentColumnSpace": 19.65625,
|
||||||
"leftColumn": 30,
|
"leftColumn": 30,
|
||||||
|
"borderRadius": "0px",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
|
"labelTextSize": "0.875rem",
|
||||||
|
"boxShadow": "none",
|
||||||
"widgetName": "Canvas1",
|
"widgetName": "Canvas1",
|
||||||
"rightColumn": 629,
|
"rightColumn": 629,
|
||||||
"detachFromLayout": true,
|
"detachFromLayout": true,
|
||||||
|
|
@ -162,12 +169,15 @@
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"parentColumnSpace": 1,
|
"parentColumnSpace": 1,
|
||||||
"leftColumn": 0,
|
"leftColumn": 0,
|
||||||
|
"borderRadius": "0px",
|
||||||
"children": []
|
"children": []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"labelTextSize": "0.875rem",
|
||||||
"image": "",
|
"image": "",
|
||||||
|
"boxShadow": "none",
|
||||||
"widgetName": "Image1",
|
"widgetName": "Image1",
|
||||||
"rightColumn": 22,
|
"rightColumn": 22,
|
||||||
"widgetId": "1t50avy6f1",
|
"widgetId": "1t50avy6f1",
|
||||||
|
|
@ -183,26 +193,63 @@
|
||||||
"parentColumnSpace": 19.65625,
|
"parentColumnSpace": 19.65625,
|
||||||
"imageShape": "RECTANGLE",
|
"imageShape": "RECTANGLE",
|
||||||
"leftColumn": 6,
|
"leftColumn": 6,
|
||||||
|
"borderRadius": "0px",
|
||||||
"defaultImage": "https://res.cloudinary.com/drako999/image/upload/v1589196259/default.png"
|
"defaultImage": "https://res.cloudinary.com/drako999/image/upload/v1589196259/default.png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"labelTextSize": "0.875rem",
|
||||||
|
"boxShadow": "none",
|
||||||
"widgetName": "Button1",
|
"widgetName": "Button1",
|
||||||
"rightColumn": 18,
|
"rightColumn": 18,
|
||||||
"isDefaultClickDisabled": true,
|
"isDefaultClickDisabled": true,
|
||||||
|
"buttonColor": "#03B365",
|
||||||
"widgetId": "41wgbhd5vp",
|
"widgetId": "41wgbhd5vp",
|
||||||
"buttonStyle": "PRIMARY_BUTTON",
|
|
||||||
"topRow": 44,
|
"topRow": 44,
|
||||||
"bottomRow": 48,
|
"bottomRow": 48,
|
||||||
"parentRowSpace": 10,
|
"parentRowSpace": 10,
|
||||||
"isVisible": true,
|
"isVisible": true,
|
||||||
"type": "BUTTON_WIDGET",
|
"type": "BUTTON_WIDGET",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
|
"recaptchaType": "V3",
|
||||||
"parentId": "0",
|
"parentId": "0",
|
||||||
"isLoading": false,
|
"isLoading": false,
|
||||||
"parentColumnSpace": 19.65625,
|
"parentColumnSpace": 19.65625,
|
||||||
"leftColumn": 10,
|
"leftColumn": 10,
|
||||||
|
"borderRadius": "0px",
|
||||||
|
"buttonVariant": "PRIMARY",
|
||||||
"text": "Submit",
|
"text": "Submit",
|
||||||
"isDisabled": false
|
"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}}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ let homePage = ObjectsRegistry.HomePage,
|
||||||
locator = ObjectsRegistry.CommonLocators;
|
locator = ObjectsRegistry.CommonLocators;
|
||||||
|
|
||||||
describe("AForce - Community Issues page validations", function() {
|
describe("AForce - Community Issues page validations", function() {
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
agHelper.clearLocalStorageCache();
|
agHelper.clearLocalStorageCache();
|
||||||
});
|
});
|
||||||
|
|
@ -28,231 +27,267 @@ describe("AForce - Community Issues page validations", function () {
|
||||||
cy.visit("/applications");
|
cy.visit("/applications");
|
||||||
homePage.ImportApp("CommunityIssuesExport.json");
|
homePage.ImportApp("CommunityIssuesExport.json");
|
||||||
cy.wait("@importNewApplication").then((interception: any) => {
|
cy.wait("@importNewApplication").then((interception: any) => {
|
||||||
agHelper.Sleep()
|
agHelper.Sleep();
|
||||||
const { isPartialImport } = interception.response.body.data;
|
const { isPartialImport } = interception.response.body.data;
|
||||||
if (isPartialImport) {
|
if (isPartialImport) {
|
||||||
// should reconnect modal
|
// should reconnect modal
|
||||||
dataSources.ReconnectDataSourcePostgres("AForceDB")
|
dataSources.ReconnectDataSourcePostgres("AForceDB");
|
||||||
} else {
|
} else {
|
||||||
homePage.AssertImport()
|
homePage.AssertImport();
|
||||||
}
|
}
|
||||||
//Validate table is not empty!
|
//Validate table is not empty!
|
||||||
table.WaitUntilTableLoad()
|
table.WaitUntilTableLoad();
|
||||||
//Validating order of header columns!
|
//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:
|
//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", () => {
|
it("2. Validate table navigation with Server Side pagination enabled with Default selected row", () => {
|
||||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||||
agHelper.AssertExistingToggleState("serversidepagination", 'checked')
|
agHelper.AssertExistingToggleState("serversidepagination", "checked");
|
||||||
|
|
||||||
agHelper.EvaluateExistingPropertyFieldValue("Default Selected Row")
|
agHelper
|
||||||
.then($selectedRow => {
|
.EvaluateExistingPropertyFieldValue("Default Selected Row")
|
||||||
|
.then(($selectedRow) => {
|
||||||
selectedRow = Number($selectedRow);
|
selectedRow = Number($selectedRow);
|
||||||
table.AssertSelectedRow(selectedRow)
|
table.AssertSelectedRow(selectedRow);
|
||||||
});
|
});
|
||||||
|
|
||||||
agHelper.DeployApp()
|
agHelper.DeployApp();
|
||||||
table.WaitUntilTableLoad()
|
table.WaitUntilTableLoad();
|
||||||
|
|
||||||
//Verify hidden columns are infact hidden in deployed app!
|
//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.AssertPageNumber(1);
|
||||||
table.NavigateToNextPage()//page 2
|
table.NavigateToNextPage(); //page 2
|
||||||
agHelper.Sleep(3000)//wait for table navigation to take effect!
|
agHelper.Sleep(3000); //wait for table navigation to take effect!
|
||||||
table.WaitUntilTableLoad()
|
table.WaitUntilTableLoad();
|
||||||
table.AssertSelectedRow(selectedRow)
|
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
|
table.NavigateToPreviousPage(); //page 1
|
||||||
agHelper.Sleep(3000)//wait for table navigation to take effect!
|
agHelper.Sleep(3000); //wait for table navigation to take effect!
|
||||||
table.WaitForTableEmpty()//page 3
|
table.WaitUntilTableLoad();
|
||||||
table.NavigateToPreviousPage()//page 2
|
table.AssertSelectedRow(selectedRow);
|
||||||
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);
|
table.AssertPageNumber(1);
|
||||||
|
});
|
||||||
})
|
|
||||||
|
|
||||||
it("3. Validate table navigation with Server Side pagination disabled with Default selected row selection", () => {
|
it("3. Validate table navigation with Server Side pagination disabled with Default selected row selection", () => {
|
||||||
|
agHelper.NavigateBacktoEditor();
|
||||||
agHelper.NavigateBacktoEditor()
|
table.WaitUntilTableLoad();
|
||||||
table.WaitUntilTableLoad()
|
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
agHelper.ToggleOnOrOff("serversidepagination", "Off");
|
||||||
agHelper.ToggleOnOrOff('serversidepagination', 'Off')
|
agHelper.DeployApp();
|
||||||
agHelper.DeployApp()
|
table.WaitUntilTableLoad();
|
||||||
table.WaitUntilTableLoad()
|
table.AssertPageNumber(1, "Off");
|
||||||
table.AssertPageNumber(1, 'Off');
|
table.AssertSelectedRow(selectedRow);
|
||||||
table.AssertSelectedRow(selectedRow)
|
agHelper.NavigateBacktoEditor();
|
||||||
agHelper.NavigateBacktoEditor()
|
table.WaitUntilTableLoad();
|
||||||
table.WaitUntilTableLoad()
|
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
agHelper.ToggleOnOrOff("serversidepagination", "On");
|
||||||
agHelper.ToggleOnOrOff('serversidepagination', 'On')
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("4. Change Default selected row in table and verify", () => {
|
it("4. Change Default selected row in table and verify", () => {
|
||||||
|
jsEditor.EnterJSContext("Default Selected Row", "1");
|
||||||
jsEditor.EnterJSContext("Default Selected Row", "1")
|
agHelper.DeployApp();
|
||||||
agHelper.DeployApp()
|
table.WaitUntilTableLoad();
|
||||||
table.WaitUntilTableLoad()
|
|
||||||
table.AssertPageNumber(1);
|
table.AssertPageNumber(1);
|
||||||
table.AssertSelectedRow(1)
|
table.AssertSelectedRow(1);
|
||||||
table.NavigateToNextPage()//page 2
|
table.NavigateToNextPage(); //page 2
|
||||||
table.AssertPageNumber(2);
|
table.AssertPageNumber(2);
|
||||||
table.AssertSelectedRow(1)
|
table.AssertSelectedRow(1);
|
||||||
agHelper.NavigateBacktoEditor()
|
agHelper.NavigateBacktoEditor();
|
||||||
table.WaitUntilTableLoad()
|
table.WaitUntilTableLoad();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip("5. Verify Default search text in table as per 'Default Search Text' property set + Bug 12228", () => {
|
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')
|
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||||
jsEditor.EnterJSContext("Default Search Text", "Bug", false)
|
jsEditor.EnterJSContext("Default Search Text", "Question", false);
|
||||||
agHelper.DeployApp()
|
agHelper.DeployApp();
|
||||||
table.AssertSearchText('Bug')
|
table.AssertSearchText("Question");
|
||||||
table.WaitUntilTableLoad()
|
table.WaitUntilTableLoad();
|
||||||
table.WaitUntilTableLoad()
|
agHelper.NavigateBacktoEditor();
|
||||||
agHelper.NavigateBacktoEditor()
|
table.WaitUntilTableLoad();
|
||||||
|
|
||||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||||
jsEditor.EnterJSContext("Default Search Text", "Question", false)
|
jsEditor.EnterJSContext("Default Search Text", "Epic", false); //Bug 12228 - Searching based on hidden column value should not be allowed
|
||||||
agHelper.DeployApp()
|
agHelper.DeployApp();
|
||||||
table.AssertSearchText('Question')
|
table.AssertSearchText("Epic");
|
||||||
table.WaitUntilTableLoad()
|
table.WaitForTableEmpty();
|
||||||
agHelper.NavigateBacktoEditor()
|
agHelper.NavigateBacktoEditor();
|
||||||
table.WaitUntilTableLoad()
|
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.RemoveText("defaultsearchtext");
|
||||||
|
table.WaitUntilTableLoad();
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip("6. Validate Search table with Client Side Search enabled & disabled", () => {
|
it.skip("6. Validate Search table with Client Side Search enabled & disabled", () => {
|
||||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||||
agHelper.AssertExistingToggleState("enableclientsidesearch", 'checked')
|
agHelper.AssertExistingToggleState("enableclientsidesearch", "checked");
|
||||||
|
|
||||||
agHelper.DeployApp()
|
agHelper.DeployApp();
|
||||||
table.WaitUntilTableLoad()
|
table.WaitUntilTableLoad();
|
||||||
|
|
||||||
table.SearchTable('Bug')
|
table.SearchTable("Bug");
|
||||||
table.WaitUntilTableLoad()
|
table.WaitUntilTableLoad();
|
||||||
cy.xpath(table._searchBoxCross).click()
|
cy.xpath(table._searchBoxCross).click();
|
||||||
|
|
||||||
table.SearchTable('Question')
|
table.SearchTable("Question");
|
||||||
table.WaitUntilTableLoad()
|
table.WaitUntilTableLoad();
|
||||||
cy.xpath(table._searchBoxCross).click()
|
cy.xpath(table._searchBoxCross).click();
|
||||||
|
|
||||||
agHelper.NavigateBacktoEditor()
|
agHelper.NavigateBacktoEditor();
|
||||||
table.WaitUntilTableLoad()
|
table.WaitUntilTableLoad();
|
||||||
|
|
||||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||||
agHelper.ToggleOnOrOff("enableclientsidesearch", 'Off')
|
agHelper.ToggleOnOrOff("enableclientsidesearch", "Off");
|
||||||
|
|
||||||
agHelper.DeployApp()
|
agHelper.DeployApp();
|
||||||
table.WaitUntilTableLoad()
|
table.WaitUntilTableLoad();
|
||||||
|
|
||||||
table.SearchTable('Bug')
|
table.SearchTable("Bug");
|
||||||
table.WaitForTableEmpty()
|
table.WaitForTableEmpty();
|
||||||
cy.xpath(table._searchBoxCross).click()
|
cy.xpath(table._searchBoxCross).click();
|
||||||
|
|
||||||
table.SearchTable('Question')
|
table.SearchTable("Question");
|
||||||
table.WaitForTableEmpty()
|
table.WaitForTableEmpty();
|
||||||
cy.xpath(table._searchBoxCross).click()
|
cy.xpath(table._searchBoxCross).click();
|
||||||
|
|
||||||
agHelper.NavigateBacktoEditor()
|
agHelper.NavigateBacktoEditor();
|
||||||
table.WaitUntilTableLoad()
|
table.WaitUntilTableLoad();
|
||||||
ee.SelectEntityByName("Table1", 'WIDGETS')
|
ee.SelectEntityByName("Table1", "WIDGETS");
|
||||||
agHelper.ToggleOnOrOff("enableclientsidesearch", 'On')
|
agHelper.ToggleOnOrOff("enableclientsidesearch", "On");
|
||||||
})
|
});
|
||||||
|
|
||||||
it("7. Validate Filter table", () => {
|
it("7. Validate Filter table", () => {
|
||||||
agHelper.DeployApp()
|
var filterTitle = new Array();
|
||||||
table.WaitUntilTableLoad()
|
agHelper.DeployApp();
|
||||||
|
table.WaitUntilTableLoad();
|
||||||
|
|
||||||
//One filter
|
//One filter
|
||||||
table.OpenNFilterTable("Type", "is exactly", "Bug")
|
table.OpenNFilterTable("Type", "is exactly", "Bug");
|
||||||
table.ReadTableRowColumnData(0, 1).then(($cellData) => {
|
for (let i = 0; i < 3; i++) {
|
||||||
expect($cellData).to.eq("[Bug]: Postgres queries unable to execute with more than 9 placeholders");
|
table.ReadTableRowColumnData(i, 0, 200).then(($cellData) => {
|
||||||
|
expect($cellData).to.eq("Bug");
|
||||||
});
|
});
|
||||||
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.RemoveFilterNVerify("Question", true, false)
|
|
||||||
|
|
||||||
//Two filters - OR
|
//Two filters - OR
|
||||||
table.OpenNFilterTable("Type", "starts with", "Trouble")
|
table.OpenNFilterTable("Type", "starts with", "Trouble");
|
||||||
table.ReadTableRowColumnData(0, 0).then(($cellData) => {
|
for (let i = 0; i < 5; i++) {
|
||||||
|
table.ReadTableRowColumnData(i, 0, 200).then(($cellData) => {
|
||||||
expect($cellData).to.eq("Troubleshooting");
|
expect($cellData).to.eq("Troubleshooting");
|
||||||
});
|
});
|
||||||
table.ReadTableRowColumnData(0, 1).then(($cellData) => {
|
}
|
||||||
expect($cellData).to.eq("Renew expired SSL certificate on a self-hosted instance");
|
|
||||||
|
table.OpenNFilterTable("Title", "contains", "query", "OR", 1);
|
||||||
|
table.ReadTableRowColumnData(1, 0, 200).then(($cellData) => {
|
||||||
|
expect($cellData).to.be.oneOf(["Troubleshooting", "Question"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
table.OpenNFilterTable("Title", "contains", "query", 'OR', 1)
|
for (let i = 0; i < 8; i++) {
|
||||||
table.ReadTableRowColumnData(1, 0).then(($cellData) => {
|
table.ReadTableRowColumnData(i, 1, 100).then(($cellData) => {
|
||||||
expect($cellData).to.be.oneOf(['Troubleshooting','Question'])
|
if ($cellData.toLowerCase().includes("query"))
|
||||||
|
filterTitle.push($cellData);
|
||||||
});
|
});
|
||||||
table.ReadTableRowColumnData(6, 1).then(($cellData) => {
|
}
|
||||||
expect($cellData).to.eq("Run storeValue commands before a Query.run()");
|
cy.wrap(filterTitle).as("filterTitleText"); // alias it for later
|
||||||
});
|
cy.get("@filterTitleText")
|
||||||
table.RemoveFilterNVerify("Question", true, false)
|
.its("length")
|
||||||
|
.should("eq", 2);
|
||||||
|
|
||||||
|
table.RemoveFilterNVerify("Question", true, false);
|
||||||
|
|
||||||
//Two filters - AND
|
//Two filters - AND
|
||||||
table.OpenNFilterTable("Votes", "greater than", "3")
|
table.OpenNFilterTable("Votes", "greater than", "2");
|
||||||
table.ReadTableRowColumnData(1, 1).then(($cellData) => {
|
table.ReadTableRowColumnData(0, 1).then(($cellData) => {
|
||||||
expect($cellData).to.eq("Combine queries from different datasources");
|
expect($cellData).to.eq("Combine queries from different datasources");
|
||||||
});
|
});
|
||||||
|
|
||||||
table.OpenNFilterTable("Title", "contains", "button", 'AND', 1)
|
table.OpenNFilterTable("Title", "contains", "button", "AND", 1);
|
||||||
table.ReadTableRowColumnData(0, 1).then(($cellData) => {
|
table.ReadTableRowColumnData(0, 1).then(($cellData) => {
|
||||||
expect($cellData).to.eq("Change the video in the video player with a button click");
|
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", () => {
|
it("8. Validate Adding a New issue from Add Modal", () => {
|
||||||
// agHelper.DeployApp()
|
// agHelper.DeployApp()
|
||||||
// table.WaitUntilTableLoad()
|
// table.WaitUntilTableLoad()
|
||||||
|
|
||||||
cy.get(table._addIcon).closest('div').click()
|
cy.get(table._addIcon)
|
||||||
agHelper.AssertElementPresence(locator._modal)
|
.closest("div")
|
||||||
agHelper.SelectFromDropDown('Suggestion', 't--modal-widget')
|
.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._inputWidgetv1InDeployed)
|
||||||
cy.get(locator._textAreainputWidgetv1InDeployed).eq(1).type("Adding Description Suggestion via script")
|
.eq(3)
|
||||||
cy.get(locator._inputWidgetv1InDeployed).eq(4).type("https://github.com/appsmithorg/appsmith/issues/12532")
|
.type("Adding Title Suggestion via script");
|
||||||
agHelper.SelectFromMultiSelect(['Epic', 'Task'], 1)
|
cy.get(locator._textAreainputWidgetv1InDeployed)
|
||||||
cy.xpath(table._visibleTextSpan('Labels')).click()
|
.eq(1)
|
||||||
cy.get(locator._inputWidgetv1InDeployed).eq(5).type("https://release.app.appsmith.com/applications/62486d45ab307a026918639e/pages/62486d45ab307a02691863a7")
|
.type("Adding Description Suggestion via script");
|
||||||
agHelper.SelectFromMultiSelect(['Documented', 'Needs App'], 1, true, 'multiselectwidget')
|
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.ClickButton("Confirm");
|
||||||
agHelper.Sleep(3000)
|
agHelper.Sleep(3000);
|
||||||
table.SearchTable('Suggestion', 2)
|
table.SearchTable("Suggestion", 2);
|
||||||
table.WaitUntilTableLoad()
|
table.WaitUntilTableLoad();
|
||||||
|
|
||||||
table.ReadTableRowColumnData(0, 0, 1000).then((cellData) => {
|
table.ReadTableRowColumnData(0, 0, 1000).then((cellData) => {
|
||||||
expect(cellData).to.be.equal("Suggestion");
|
expect(cellData).to.be.equal("Suggestion");
|
||||||
|
|
@ -261,22 +296,32 @@ describe("AForce - Community Issues page validations", function () {
|
||||||
table.ReadTableRowColumnData(0, 1, 1000).then((cellData) => {
|
table.ReadTableRowColumnData(0, 1, 1000).then((cellData) => {
|
||||||
expect(cellData).to.be.equal("Adding Title Suggestion via script");
|
expect(cellData).to.be.equal("Adding Title Suggestion via script");
|
||||||
});
|
});
|
||||||
|
});
|
||||||
})
|
|
||||||
|
|
||||||
it("9. Validate Updating issue from Details tab", () => {
|
it("9. Validate Updating issue from Details tab", () => {
|
||||||
|
agHelper.AssertElementAbsence(locator._widgetInDeployed("tabswidget"));
|
||||||
agHelper.AssertElementAbsence(locator._widgetInDeployed('tabswidget'))
|
table.SelectTableRow(0);
|
||||||
table.SelectTableRow(0)
|
agHelper.AssertElementPresence(locator._widgetInDeployed("tabswidget"));
|
||||||
agHelper.AssertElementPresence(locator._widgetInDeployed('tabswidget'))
|
agHelper
|
||||||
agHelper.GetNClick(locator._inputWidgetv1InDeployed).type("-updating title")
|
.GetNClick(locator._inputWidgetv1InDeployed)
|
||||||
agHelper.GetNClick(locator._textAreainputWidgetv1InDeployed).type("-updating desc")
|
.type("-updating title");
|
||||||
agHelper.GetNClick(locator._inputWidgetv1InDeployed, 1).type("-updating issue link")
|
agHelper
|
||||||
agHelper.SelectFromDropDown('Troubleshooting', 't--widget-tabswidget')
|
.GetNClick(locator._textAreainputWidgetv1InDeployed)
|
||||||
agHelper.SelectFromMultiSelect(['Epic', 'Task'], 0, false)
|
.type("-updating desc");
|
||||||
agHelper.SelectFromMultiSelect(['High', 'Dependencies'], 0, true)
|
agHelper
|
||||||
agHelper.SelectFromDropDown('[Bug] TypeError: o is undefined', 't--widget-tabswidget', 1)
|
.GetNClick(locator._inputWidgetv1InDeployed, 1)
|
||||||
agHelper.GetNClick(locator._inputWidgetv1InDeployed, 2).type("-updating answer link")
|
.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}")
|
//cy.get("body").tab().type("{enter}")
|
||||||
|
|
||||||
|
|
@ -288,34 +333,41 @@ describe("AForce - Community Issues page validations", function () {
|
||||||
// key: 'Enter',
|
// key: 'Enter',
|
||||||
// })
|
// })
|
||||||
|
|
||||||
|
|
||||||
//agHelper.Sleep(2000)
|
//agHelper.Sleep(2000)
|
||||||
//cy.get("body").type("{enter}")
|
//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(['Documented', 'Needs App', 'App Built'], 0, false, 'multiselectwidget')
|
||||||
agHelper.SelectFromMultiSelect(['Needs Product'], 0, true, 'multiselectwidget')
|
agHelper.SelectFromMultiSelect(
|
||||||
agHelper.ClickButton('Save')
|
["Needs Product"],
|
||||||
|
0,
|
||||||
|
true,
|
||||||
|
"multiselectwidget",
|
||||||
|
);
|
||||||
|
agHelper.ClickButton("Save");
|
||||||
|
|
||||||
table.ReadTableRowColumnData(0, 0, 1000).then((cellData) => {
|
table.ReadTableRowColumnData(0, 0, 1000).then((cellData) => {
|
||||||
expect(cellData).to.be.equal("Troubleshooting");
|
expect(cellData).to.be.equal("Troubleshooting");
|
||||||
});
|
});
|
||||||
|
|
||||||
table.ReadTableRowColumnData(0, 1, 1000).then((cellData) => {
|
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", () => {
|
it("10. Validate Deleting the newly created issue", () => {
|
||||||
agHelper.AssertElementAbsence(locator._widgetInDeployed('tabswidget'))
|
agHelper.AssertElementAbsence(locator._widgetInDeployed("tabswidget"));
|
||||||
table.SelectTableRow(0)
|
table.SelectTableRow(0);
|
||||||
agHelper.AssertElementPresence(locator._widgetInDeployed('tabswidget'))
|
agHelper.AssertElementPresence(locator._widgetInDeployed("tabswidget"));
|
||||||
agHelper.Sleep()
|
agHelper.Sleep();
|
||||||
cy.get(table._trashIcon).closest('div').click()
|
cy.get(table._trashIcon)
|
||||||
agHelper.AssertElementAbsence(locator._widgetInDeployed('tabswidget'))
|
.closest("div")
|
||||||
table.WaitForTableEmpty()
|
.click();
|
||||||
|
agHelper.AssertElementAbsence(locator._widgetInDeployed("tabswidget"));
|
||||||
|
table.WaitForTableEmpty();
|
||||||
|
|
||||||
//2nd search is not working, hence commenting below
|
//2nd search is not working, hence commenting below
|
||||||
// cy.xpath(table._searchBoxCross).click()
|
// cy.xpath(table._searchBoxCross).click()
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ describe("Validate API request body panel", () => {
|
||||||
paste: true,
|
paste: true,
|
||||||
completeReplace: true,
|
completeReplace: true,
|
||||||
toRun: false,
|
toRun: false,
|
||||||
shouldNavigate: true,
|
shouldCreateNewJSObj: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ describe("Validate JSObjects binding to Input widget", () => {
|
||||||
paste: false,
|
paste: false,
|
||||||
completeReplace: false,
|
completeReplace: false,
|
||||||
toRun: true,
|
toRun: true,
|
||||||
shouldNavigate: true,
|
shouldCreateNewJSObj: true,
|
||||||
});
|
});
|
||||||
ee.expandCollapseEntity("WIDGETS"); //to expand widgets
|
ee.expandCollapseEntity("WIDGETS"); //to expand widgets
|
||||||
ee.expandCollapseEntity("Form1");
|
ee.expandCollapseEntity("Form1");
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ describe("Validate JSObj binding to Table widget", () => {
|
||||||
paste: false,
|
paste: false,
|
||||||
completeReplace: false,
|
completeReplace: false,
|
||||||
toRun: true,
|
toRun: true,
|
||||||
shouldNavigate: true,
|
shouldCreateNewJSObj: true,
|
||||||
});
|
});
|
||||||
cy.get("@jsObjName").then((jsObj) => {
|
cy.get("@jsObjName").then((jsObj) => {
|
||||||
jsName = 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))
|
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");
|
ee.SelectEntityByName("Button1", "WIDGETS");
|
||||||
cy.get("@jsObjName").then((jsObjName) => {
|
cy.get("@jsObjName").then((jsObjName) => {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ describe("JS Function Execution", function() {
|
||||||
paste: true,
|
paste: true,
|
||||||
completeReplace: true,
|
completeReplace: true,
|
||||||
toRun: false,
|
toRun: false,
|
||||||
shouldNavigate: true,
|
shouldCreateNewJSObj: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -33,7 +33,7 @@ describe("JS Function Execution", function() {
|
||||||
paste: true,
|
paste: true,
|
||||||
completeReplace: true,
|
completeReplace: true,
|
||||||
toRun: false,
|
toRun: false,
|
||||||
shouldNavigate: true,
|
shouldCreateNewJSObj: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -58,7 +58,7 @@ describe("JS Function Execution", function() {
|
||||||
paste: true,
|
paste: true,
|
||||||
completeReplace: true,
|
completeReplace: true,
|
||||||
toRun: true,
|
toRun: true,
|
||||||
shouldNavigate: true,
|
shouldCreateNewJSObj: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Assert presence of function execution parse error callout
|
// Assert presence of function execution parse error callout
|
||||||
|
|
@ -69,7 +69,7 @@ describe("JS Function Execution", function() {
|
||||||
paste: true,
|
paste: true,
|
||||||
completeReplace: true,
|
completeReplace: true,
|
||||||
toRun: false,
|
toRun: false,
|
||||||
shouldNavigate: false,
|
shouldCreateNewJSObj: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Assert presence of parse error callout (entire JS Object is invalid)
|
// Assert presence of parse error callout (entire JS Object is invalid)
|
||||||
|
|
|
||||||
|
|
@ -33,5 +33,17 @@ describe("Widget Grouping", function() {
|
||||||
.should("have.length", 2);
|
.should("have.length", 2);
|
||||||
cy.get(`@group`).find(`.t--draggable-buttonwidget`);
|
cy.get(`@group`).find(`.t--draggable-buttonwidget`);
|
||||||
cy.get(`@group`).find(`.t--draggable-imagewidget`);
|
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() {
|
it("5. Validate Move JSObject", function() {
|
||||||
const newPageId = "Page2";
|
const newPageId = "Page2";
|
||||||
agHelper.AddNewPage();
|
ee.AddNewPage();
|
||||||
ee.AssertEntityPresenceInExplorer(newPageId);
|
ee.AssertEntityPresenceInExplorer(newPageId);
|
||||||
ee.SelectEntityByName(pageId);
|
ee.SelectEntityByName(pageId);
|
||||||
ee.expandCollapseEntity("QUERIES/JS");
|
ee.expandCollapseEntity("QUERIES/JS");
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ describe("JSObjects OnLoad Actions tests", function() {
|
||||||
paste: true,
|
paste: true,
|
||||||
completeReplace: true,
|
completeReplace: true,
|
||||||
toRun: false,
|
toRun: false,
|
||||||
shouldNavigate: true,
|
shouldCreateNewJSObj: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
jsEditor.EnableDisableAsyncFuncSettings("getId", false, true); //Only before calling confirmation is enabled by User here
|
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,
|
paste: true,
|
||||||
completeReplace: true,
|
completeReplace: true,
|
||||||
toRun: false,
|
toRun: false,
|
||||||
shouldNavigate: true,
|
shouldCreateNewJSObj: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -251,7 +251,7 @@ describe("JSObjects OnLoad Actions tests", function() {
|
||||||
paste: true,
|
paste: true,
|
||||||
completeReplace: true,
|
completeReplace: true,
|
||||||
toRun: false,
|
toRun: false,
|
||||||
shouldNavigate: true,
|
shouldCreateNewJSObj: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -440,7 +440,7 @@ describe("JSObjects OnLoad Actions tests", function() {
|
||||||
paste: true,
|
paste: true,
|
||||||
completeReplace: true,
|
completeReplace: true,
|
||||||
toRun: false,
|
toRun: false,
|
||||||
shouldNavigate: true,
|
shouldCreateNewJSObj: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -481,7 +481,7 @@ describe("JSObjects OnLoad Actions tests", function() {
|
||||||
// `{{` +
|
// `{{` +
|
||||||
// jsObjName +
|
// jsObjName +
|
||||||
// `.callCountry();
|
// `.callCountry();
|
||||||
// showAlert('Your country is: ' + getCountry.data[0].country, 'info')}}`,
|
// Select1.selectedOptionValue? showAlert('Your country is: ' + getCountry.data[0].country, 'info'): null`,
|
||||||
// true,
|
// true,
|
||||||
// true,
|
// true,
|
||||||
// );
|
// );
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break",
|
||||||
paste: true,
|
paste: true,
|
||||||
completeReplace: false,
|
completeReplace: false,
|
||||||
toRun: false,
|
toRun: false,
|
||||||
shouldNavigate: true,
|
shouldCreateNewJSObj: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@
|
||||||
"evaluatedType": ".t--CodeEditor-evaluatedValue > div:first-of-type pre",
|
"evaluatedType": ".t--CodeEditor-evaluatedValue > div:first-of-type pre",
|
||||||
"evaluatedCurrentValue": "div:last-of-type .t--CodeEditor-evaluatedValue > div:last-of-type pre",
|
"evaluatedCurrentValue": "div:last-of-type .t--CodeEditor-evaluatedValue > div:last-of-type pre",
|
||||||
"entityExplorersearch": "#entity-explorer-search",
|
"entityExplorersearch": "#entity-explorer-search",
|
||||||
|
"searchEntityInExplorer": "#search-entity",
|
||||||
"entitySearchResult": ".t--entity-name:contains('",
|
"entitySearchResult": ".t--entity-name:contains('",
|
||||||
"saveStatusContainer": ".t--save-status-container",
|
"saveStatusContainer": ".t--save-status-container",
|
||||||
"saveStatusIsSaving": "t--save-status-is-saving",
|
"saveStatusIsSaving": "t--save-status-is-saving",
|
||||||
|
|
|
||||||
|
|
@ -134,17 +134,6 @@ export class AggregateHelper {
|
||||||
localStorage.setItem("inDeployedMode", "true");
|
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) {
|
public ValidateToastMessage(text: string, length = 1) {
|
||||||
cy.get(this.locator._toastMsg)
|
cy.get(this.locator._toastMsg)
|
||||||
.should("have.length", length)
|
.should("have.length", length)
|
||||||
|
|
@ -409,6 +398,17 @@ export class AggregateHelper {
|
||||||
.wait(500);
|
.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") {
|
public ToggleOnOrOff(propertyName: string, toggle: "On" | "Off") {
|
||||||
if (toggle == "On") {
|
if (toggle == "On") {
|
||||||
cy.get(this.locator._propertyToggle(propertyName))
|
cy.get(this.locator._propertyToggle(propertyName))
|
||||||
|
|
@ -605,7 +605,11 @@ export class AggregateHelper {
|
||||||
locator.should("not.exist");
|
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("//")
|
let locator = selector.startsWith("//")
|
||||||
? cy.xpath(selector)
|
? cy.xpath(selector)
|
||||||
: cy.get(selector);
|
: cy.get(selector);
|
||||||
|
|
|
||||||
|
|
@ -34,26 +34,28 @@ export class DataSources {
|
||||||
_reconnectModal = "div.reconnect-datasource-modal";
|
_reconnectModal = "div.reconnect-datasource-modal";
|
||||||
_activeDSListReconnectModal = (dbName: string) =>
|
_activeDSListReconnectModal = (dbName: string) =>
|
||||||
"//div[contains(@class, 't--ds-list')]//span[text()='" + dbName + "']";
|
"//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)
|
cy.get(this._addNewDataSource)
|
||||||
.last()
|
.last()
|
||||||
.scrollIntoView()
|
.scrollIntoView()
|
||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
}
|
// cy.get(this._dsCreateNewTab)
|
||||||
|
// .should("be.visible")
|
||||||
public CreatePlugIn(pluginName: string) {
|
// .click({ force: true });
|
||||||
cy.get(this._createNewPlgin(pluginName)).click();
|
cy.get(this._newDatabases).should("be.visible");
|
||||||
}
|
|
||||||
|
|
||||||
public NavigateToDSCreateNew() {
|
|
||||||
this.NavigateToDSAdd();
|
|
||||||
cy.get(this._dsCreateNewTab)
|
|
||||||
.should("be.visible")
|
|
||||||
.click({ force: true });
|
|
||||||
cy.get(this.locator._loading).should("not.exist");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public FillPostgresDSForm(shouldAddTrailingSpaces = false) {
|
public FillPostgresDSForm(shouldAddTrailingSpaces = false) {
|
||||||
|
|
@ -73,6 +75,18 @@ export class DataSources {
|
||||||
cy.get(this._password).type(datasourceFormData["postgres-password"]);
|
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) {
|
public TestSaveDatasource(expectedRes = true) {
|
||||||
this.TestDatasource(expectedRes);
|
this.TestDatasource(expectedRes);
|
||||||
this.SaveDatasource();
|
this.SaveDatasource();
|
||||||
|
|
@ -94,7 +108,7 @@ export class DataSources {
|
||||||
}
|
}
|
||||||
|
|
||||||
public NavigateToActiveDSQueryPane(datasourceName: string) {
|
public NavigateToActiveDSQueryPane(datasourceName: string) {
|
||||||
this.NavigateToDSAdd();
|
this.NavigateToDSCreateNew();
|
||||||
this.agHelper.GetNClick(this.locator._activeTab);
|
this.agHelper.GetNClick(this.locator._activeTab);
|
||||||
cy.get(this._datasourceCard)
|
cy.get(this._datasourceCard)
|
||||||
.contains(datasourceName)
|
.contains(datasourceName)
|
||||||
|
|
@ -145,7 +159,7 @@ export class DataSources {
|
||||||
}
|
}
|
||||||
|
|
||||||
RunQuery() {
|
RunQuery() {
|
||||||
cy.get(this._apiQueryBtn).click({ force: true });
|
cy.get(this._runQueryBtn).click({ force: true });
|
||||||
this.agHelper.ValidateNetworkExecutionSuccess("@postExecute");
|
this.agHelper.ValidateNetworkExecutionSuccess("@postExecute");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,88 @@
|
||||||
import { ObjectsRegistry } from "../Objects/Registry"
|
import { ObjectsRegistry } from "../Objects/Registry";
|
||||||
|
|
||||||
export class EntityExplorer {
|
export class EntityExplorer {
|
||||||
|
public agHelper = ObjectsRegistry.AggregateHelper;
|
||||||
public agHelper = ObjectsRegistry.AggregateHelper
|
|
||||||
public locator = ObjectsRegistry.CommonLocators;
|
public locator = ObjectsRegistry.CommonLocators;
|
||||||
|
|
||||||
public SelectEntityByName(entityNameinLeftSidebar: string, section: 'WIDGETS' | 'QUERIES/JS' | 'DATASOURCES' | '' = '') {
|
public SelectEntityByName(
|
||||||
if (section)
|
entityNameinLeftSidebar: string,
|
||||||
this.expandCollapseEntity(section)//to expand respective section
|
section: "WIDGETS" | "QUERIES/JS" | "DATASOURCES" | "" = "",
|
||||||
|
) {
|
||||||
|
if (section) this.expandCollapseEntity(section); //to expand respective section
|
||||||
cy.xpath(this.locator._entityNameInExplorer(entityNameinLeftSidebar))
|
cy.xpath(this.locator._entityNameInExplorer(entityNameinLeftSidebar))
|
||||||
.last()
|
.last()
|
||||||
.click({ multiple: true })
|
.click({ multiple: true });
|
||||||
this.agHelper.Sleep()
|
this.agHelper.Sleep();
|
||||||
}
|
}
|
||||||
|
|
||||||
public NavigateToSwitcher(navigationTab: 'explorer' | 'widgets') {
|
public AddNewPage() {
|
||||||
cy.get(this.locator._openNavigationTab(navigationTab)).click()
|
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) {
|
public AssertEntityPresenceInExplorer(entityNameinLeftSidebar: string) {
|
||||||
cy.xpath(this.locator._entityNameInExplorer(entityNameinLeftSidebar))
|
cy.xpath(
|
||||||
.should("have.length", 1);
|
this.locator._entityNameInExplorer(entityNameinLeftSidebar),
|
||||||
|
).should("have.length", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AssertEntityAbsenceInExplorer(entityNameinLeftSidebar: string) {
|
public AssertEntityAbsenceInExplorer(entityNameinLeftSidebar: string) {
|
||||||
cy.xpath(this.locator._entityNameInExplorer(entityNameinLeftSidebar)).should('not.exist');
|
cy.xpath(
|
||||||
|
this.locator._entityNameInExplorer(entityNameinLeftSidebar),
|
||||||
|
).should("not.exist");
|
||||||
}
|
}
|
||||||
|
|
||||||
public expandCollapseEntity(entityName: string, expand = true) {
|
public expandCollapseEntity(entityName: string, expand = true) {
|
||||||
cy.xpath(this.locator._expandCollapseArrow(entityName)).invoke('attr', 'name').then((arrow) => {
|
cy.xpath(this.locator._expandCollapseArrow(entityName))
|
||||||
if (expand && arrow == 'arrow-right')
|
.invoke("attr", "name")
|
||||||
cy.xpath(this.locator._expandCollapseArrow(entityName)).trigger('click', { multiple: true }).wait(1000);
|
.then((arrow) => {
|
||||||
else if (!expand && arrow == 'arrow-down')
|
if (expand && arrow == "arrow-right")
|
||||||
cy.xpath(this.locator._expandCollapseArrow(entityName)).trigger('click', { multiple: true }).wait(1000);
|
cy.xpath(this.locator._expandCollapseArrow(entityName))
|
||||||
else
|
.trigger("click", { multiple: true })
|
||||||
this.agHelper.Sleep(500)
|
.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 = "") {
|
public ActionContextMenuByEntityName(
|
||||||
|
entityNameinLeftSidebar: string,
|
||||||
|
action = "Delete",
|
||||||
|
subAction = "",
|
||||||
|
) {
|
||||||
this.agHelper.Sleep();
|
this.agHelper.Sleep();
|
||||||
cy.xpath(this.locator._contextMenu(entityNameinLeftSidebar))
|
cy.xpath(this.locator._contextMenu(entityNameinLeftSidebar))
|
||||||
.last()
|
.last()
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
cy.xpath(this.locator._contextMenuItem(action))
|
cy.xpath(this.locator._contextMenuItem(action)).click({ force: true });
|
||||||
.click({ force: true })
|
this.agHelper.Sleep(500);
|
||||||
this.agHelper.Sleep(500)
|
|
||||||
if (subAction) {
|
if (subAction) {
|
||||||
cy.xpath(this.locator._contextMenuItem(subAction))
|
cy.xpath(this.locator._contextMenuItem(subAction)).click({ force: true });
|
||||||
.click({ force: true })
|
this.agHelper.Sleep(500);
|
||||||
this.agHelper.Sleep(500)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DragDropWidgetNVerify(widgetType: string, x: number, y: number) {
|
public DragDropWidgetNVerify(widgetType: string, x: number, y: number) {
|
||||||
this.NavigateToSwitcher('widgets')
|
this.NavigateToSwitcher("widgets");
|
||||||
this.agHelper.Sleep()
|
this.agHelper.Sleep();
|
||||||
cy.get(this.locator._widgetPageIcon(widgetType)).first()
|
cy.get(this.locator._widgetPageIcon(widgetType))
|
||||||
|
.first()
|
||||||
.trigger("dragstart", { force: true })
|
.trigger("dragstart", { force: true })
|
||||||
.trigger("mousemove", x, y, { force: true });
|
.trigger("mousemove", x, y, { force: true });
|
||||||
cy.get(this.locator._dropHere)
|
cy.get(this.locator._dropHere)
|
||||||
.trigger("mousemove", x, y, { eventConstructor: "MouseEvent" })
|
.trigger("mousemove", x, y, { eventConstructor: "MouseEvent" })
|
||||||
.trigger("mousemove", x, y, { eventConstructor: "MouseEvent" })
|
.trigger("mousemove", x, y, { eventConstructor: "MouseEvent" })
|
||||||
.trigger("mouseup", x, y, { eventConstructor: "MouseEvent" });
|
.trigger("mouseup", x, y, { eventConstructor: "MouseEvent" });
|
||||||
this.agHelper.AssertAutoSave()//settling time for widget on canvas!
|
this.agHelper.AssertAutoSave(); //settling time for widget on canvas!
|
||||||
cy.get(this.locator._widgetInCanvas(widgetType)).should('exist')
|
cy.get(this.locator._widgetInCanvas(widgetType)).should("exist");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ export class HomePage {
|
||||||
private _editAppName = "bp3-editable-text-editing"
|
private _editAppName = "bp3-editable-text-editing"
|
||||||
private _appMenu = ".t--editor-appname-menu-portal .bp3-menu-item"
|
private _appMenu = ".t--editor-appname-menu-portal .bp3-menu-item"
|
||||||
private _buildFromScratchActionCard = ".t--BuildFromScratch"
|
private _buildFromScratchActionCard = ".t--BuildFromScratch"
|
||||||
private _buildFromDataTableActionCard = ".t--GenerateCRUDPage"
|
_buildFromDataTableActionCard = ".t--GenerateCRUDPage"
|
||||||
private _selectRole = "//span[text()='Select a role']/ancestor::div"
|
private _selectRole = "//span[text()='Select a role']/ancestor::div"
|
||||||
private _searchInput = "input[type='text']"
|
private _searchInput = "input[type='text']"
|
||||||
_appHoverIcon = (action: string) => ".t--application-" + action + "-link"
|
_appHoverIcon = (action: string) => ".t--application-" + action + "-link"
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ export interface ICreateJSObjectOptions {
|
||||||
paste: boolean;
|
paste: boolean;
|
||||||
completeReplace: boolean;
|
completeReplace: boolean;
|
||||||
toRun: boolean;
|
toRun: boolean;
|
||||||
shouldNavigate: boolean;
|
shouldCreateNewJSObj: boolean;
|
||||||
}
|
}
|
||||||
const DEFAULT_CREATE_JS_OBJECT_OPTIONS = {
|
const DEFAULT_CREATE_JS_OBJECT_OPTIONS = {
|
||||||
paste: true,
|
paste: true,
|
||||||
completeReplace: false,
|
completeReplace: false,
|
||||||
toRun: true,
|
toRun: true,
|
||||||
shouldNavigate: true,
|
shouldCreateNewJSObj: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class JSEditor {
|
export class JSEditor {
|
||||||
|
|
@ -82,7 +82,7 @@ export class JSEditor {
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region Page functions
|
//#region Page functions
|
||||||
public NavigateToJSEditor() {
|
public NavigateToNewJSEditor() {
|
||||||
cy.get(this.locator._createNew)
|
cy.get(this.locator._createNew)
|
||||||
.last()
|
.last()
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
|
|
@ -107,9 +107,9 @@ export class JSEditor {
|
||||||
JSCode: string,
|
JSCode: string,
|
||||||
options: ICreateJSObjectOptions = DEFAULT_CREATE_JS_OBJECT_OPTIONS,
|
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) {
|
if (!completeReplace) {
|
||||||
cy.get(this.locator._codeMirrorTextArea)
|
cy.get(this.locator._codeMirrorTextArea)
|
||||||
.first()
|
.first()
|
||||||
|
|
|
||||||
|
|
@ -313,7 +313,7 @@ Cypress.Commands.add("SearchApp", (appname) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add("SearchEntity", (apiname1, apiname2) => {
|
Cypress.Commands.add("SearchEntity", (apiname1, apiname2) => {
|
||||||
cy.get(commonlocators.entityExplorersearch)
|
cy.get(commonlocators.searchEntityInExplorer)
|
||||||
.clear({ force: true })
|
.clear({ force: true })
|
||||||
.type(apiname1, { force: true });
|
.type(apiname1, { force: true });
|
||||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||||
|
|
@ -329,7 +329,7 @@ Cypress.Commands.add("SearchEntity", (apiname1, apiname2) => {
|
||||||
|
|
||||||
Cypress.Commands.add("GlobalSearchEntity", (apiname1, dontAssertVisibility) => {
|
Cypress.Commands.add("GlobalSearchEntity", (apiname1, dontAssertVisibility) => {
|
||||||
// entity explorer search will be hidden
|
// entity explorer search will be hidden
|
||||||
cy.get(commonlocators.entityExplorersearch)
|
cy.get(commonlocators.searchEntityInExplorer)
|
||||||
.clear({ force: true })
|
.clear({ force: true })
|
||||||
.type(apiname1, { force: true });
|
.type(apiname1, { force: true });
|
||||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||||
|
|
@ -413,7 +413,7 @@ Cypress.Commands.add("CheckAndUnfoldWidgets", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add("SearchEntityandOpen", (apiname1) => {
|
Cypress.Commands.add("SearchEntityandOpen", (apiname1) => {
|
||||||
cy.get(commonlocators.entityExplorersearch)
|
cy.get(commonlocators.searchEntityInExplorer)
|
||||||
.clear({ force: true })
|
.clear({ force: true })
|
||||||
.type(apiname1, { force: true });
|
.type(apiname1, { force: true });
|
||||||
cy.CheckAndUnfoldWidgets();
|
cy.CheckAndUnfoldWidgets();
|
||||||
|
|
@ -432,7 +432,7 @@ Cypress.Commands.add("SearchEntityandOpen", (apiname1) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add("SearchEntityAndUnfold", (apiname1) => {
|
Cypress.Commands.add("SearchEntityAndUnfold", (apiname1) => {
|
||||||
cy.get(commonlocators.entityExplorersearch)
|
cy.get(commonlocators.searchEntityInExplorer)
|
||||||
.clear({ force: true })
|
.clear({ force: true })
|
||||||
.type(apiname1, { force: true });
|
.type(apiname1, { force: true });
|
||||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||||
|
|
@ -451,7 +451,7 @@ Cypress.Commands.add("SearchEntityAndUnfold", (apiname1) => {
|
||||||
|
|
||||||
Cypress.Commands.add("OpenBindings", (apiname1) => {
|
Cypress.Commands.add("OpenBindings", (apiname1) => {
|
||||||
cy.wait(500);
|
cy.wait(500);
|
||||||
cy.get(commonlocators.entityExplorersearch)
|
cy.get(commonlocators.searchEntityInExplorer)
|
||||||
.clear({ force: true })
|
.clear({ force: true })
|
||||||
.type(apiname1, { force: true });
|
.type(apiname1, { force: true });
|
||||||
cy.CheckAndUnfoldWidgets();
|
cy.CheckAndUnfoldWidgets();
|
||||||
|
|
|
||||||
|
|
@ -7,22 +7,17 @@ import React, {
|
||||||
useMemo,
|
useMemo,
|
||||||
} from "react";
|
} from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import history from "utils/history";
|
|
||||||
import * as Sentry from "@sentry/react";
|
import * as Sentry from "@sentry/react";
|
||||||
import { PanelStack } from "@blueprintjs/core";
|
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
|
||||||
import PerformanceTracker, {
|
import PerformanceTracker, {
|
||||||
PerformanceTransactionName,
|
PerformanceTransactionName,
|
||||||
} from "utils/PerformanceTracker";
|
} from "utils/PerformanceTracker";
|
||||||
import { AppState } from "reducers";
|
|
||||||
import {
|
import {
|
||||||
getFirstTimeUserOnboardingComplete,
|
getFirstTimeUserOnboardingComplete,
|
||||||
getIsFirstTimeUserOnboardingEnabled,
|
getIsFirstTimeUserOnboardingEnabled,
|
||||||
} from "selectors/onboardingSelectors";
|
} from "selectors/onboardingSelectors";
|
||||||
import Explorer from "pages/Editor/Explorer";
|
import Explorer from "pages/Editor/Explorer";
|
||||||
import Switcher from "components/ads/Switcher";
|
|
||||||
import { trimQueryString } from "utils/helpers";
|
|
||||||
import AppComments from "comments/AppComments/AppComments";
|
import AppComments from "comments/AppComments/AppComments";
|
||||||
import { setExplorerActiveAction } from "actions/explorerActions";
|
import { setExplorerActiveAction } from "actions/explorerActions";
|
||||||
import {
|
import {
|
||||||
|
|
@ -33,14 +28,10 @@ import { tailwindLayers } from "constants/Layers";
|
||||||
import TooltipComponent from "components/ads/Tooltip";
|
import TooltipComponent from "components/ads/Tooltip";
|
||||||
import { previewModeSelector } from "selectors/editorSelectors";
|
import { previewModeSelector } from "selectors/editorSelectors";
|
||||||
import useHorizontalResize from "utils/hooks/useHorizontalResize";
|
import useHorizontalResize from "utils/hooks/useHorizontalResize";
|
||||||
import { forceOpenWidgetPanel } from "actions/widgetSidebarActions";
|
|
||||||
import { toggleInOnboardingWidgetSelection } from "actions/onboardingActions";
|
|
||||||
import OnboardingStatusbar from "pages/Editor/FirstTimeUserOnboarding/Statusbar";
|
import OnboardingStatusbar from "pages/Editor/FirstTimeUserOnboarding/Statusbar";
|
||||||
import Pages from "pages/Editor/Explorer/Pages";
|
import Pages from "pages/Editor/Explorer/Pages";
|
||||||
import { Colors } from "constants/Colors";
|
|
||||||
import { EntityProperties } from "pages/Editor/Explorer/Entity/EntityProperties";
|
import { EntityProperties } from "pages/Editor/Explorer/Entity/EntityProperties";
|
||||||
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
|
||||||
import { builderURL } from "RouteBuilder";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
width: number;
|
width: number;
|
||||||
|
|
@ -58,38 +49,12 @@ export const EntityExplorerSidebar = memo((props: Props) => {
|
||||||
const enableFirstTimeUserOnboarding = useSelector(
|
const enableFirstTimeUserOnboarding = useSelector(
|
||||||
getIsFirstTimeUserOnboardingEnabled,
|
getIsFirstTimeUserOnboardingEnabled,
|
||||||
);
|
);
|
||||||
const isFirstTimeUserOnboardingEnabled = useSelector(
|
|
||||||
getIsFirstTimeUserOnboardingEnabled,
|
|
||||||
);
|
|
||||||
const resizer = useHorizontalResize(
|
const resizer = useHorizontalResize(
|
||||||
sidebarRef,
|
sidebarRef,
|
||||||
props.onWidthChange,
|
props.onWidthChange,
|
||||||
props.onDragEnd,
|
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 [tooltipIsOpen, setTooltipIsOpen] = useState(false);
|
||||||
const isForceOpenWidgetPanel = useSelector(
|
|
||||||
(state: AppState) => state.ui.onBoarding.forceOpenWidgetPanel,
|
|
||||||
);
|
|
||||||
const isFirstTimeUserOnboardingComplete = useSelector(
|
const isFirstTimeUserOnboardingComplete = useSelector(
|
||||||
getFirstTimeUserOnboardingComplete,
|
getFirstTimeUserOnboardingComplete,
|
||||||
);
|
);
|
||||||
|
|
@ -98,14 +63,6 @@ export const EntityExplorerSidebar = memo((props: Props) => {
|
||||||
PerformanceTracker.stopTracking();
|
PerformanceTracker.stopTracking();
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isForceOpenWidgetPanel) {
|
|
||||||
setActiveSwitch(switches[1]);
|
|
||||||
} else {
|
|
||||||
setActiveSwitch(switches[0]);
|
|
||||||
}
|
|
||||||
}, [isForceOpenWidgetPanel]);
|
|
||||||
|
|
||||||
// registering event listeners
|
// registering event listeners
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.addEventListener("mousemove", onMouseMove);
|
document.addEventListener("mousemove", onMouseMove);
|
||||||
|
|
@ -191,7 +148,7 @@ export const EntityExplorerSidebar = memo((props: Props) => {
|
||||||
<div
|
<div
|
||||||
className={classNames({
|
className={classNames({
|
||||||
[`js-entity-explorer t--entity-explorer transform transition-all flex h-full duration-400 border-r border-gray-200 ${tailwindLayers.entityExplorer}`]: true,
|
[`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,
|
"-translate-x-full": (!pinned && !active) || isPreviewMode,
|
||||||
"shadow-xl": !pinned,
|
"shadow-xl": !pinned,
|
||||||
fixed: !pinned || isPreviewMode,
|
fixed: !pinned || isPreviewMode,
|
||||||
|
|
@ -199,7 +156,7 @@ export const EntityExplorerSidebar = memo((props: Props) => {
|
||||||
>
|
>
|
||||||
{/* SIDEBAR */}
|
{/* SIDEBAR */}
|
||||||
<div
|
<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}
|
ref={sidebarRef}
|
||||||
style={{ width: props.width }}
|
style={{ width: props.width }}
|
||||||
>
|
>
|
||||||
|
|
@ -209,19 +166,8 @@ export const EntityExplorerSidebar = memo((props: Props) => {
|
||||||
<Pages />
|
<Pages />
|
||||||
{/* Popover that contains the bindings info */}
|
{/* Popover that contains the bindings info */}
|
||||||
<EntityProperties />
|
<EntityProperties />
|
||||||
{/* SWITCHER */}
|
{/* Contains entity explorer & widgets library along with a switcher*/}
|
||||||
<div
|
<Explorer />
|
||||||
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}
|
|
||||||
/>
|
|
||||||
<AppComments />
|
<AppComments />
|
||||||
</div>
|
</div>
|
||||||
{/* RESIZER */}
|
{/* RESIZER */}
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
export const ENTITY_EXPLORER_SEARCH_ID = "entity-explorer-search";
|
export const ENTITY_EXPLORER_SEARCH_ID = "entity-explorer-search";
|
||||||
export const WIDGETS_SEARCH_ID = "#widgets-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 */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (props.isDefaultExpanded) {
|
if (props.isDefaultExpanded || props.searchKeyword) {
|
||||||
open(true);
|
open(true);
|
||||||
props.onToggle && props.onToggle(true);
|
props.onToggle && props.onToggle(true);
|
||||||
}
|
}
|
||||||
}, [props.isDefaultExpanded]);
|
}, [props.isDefaultExpanded, props.searchKeyword]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!props.searchKeyword && !props.isDefaultExpanded) {
|
if (!props.searchKeyword && !props.isDefaultExpanded) {
|
||||||
open(false);
|
open(false);
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,7 @@ import React, {
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import Divider from "components/editorComponents/Divider";
|
import Divider from "components/editorComponents/Divider";
|
||||||
import Search from "./ExplorerSearch";
|
import Search from "./ExplorerSearch";
|
||||||
import { NonIdealState, Classes, IPanelProps } from "@blueprintjs/core";
|
import { NonIdealState, Classes } from "@blueprintjs/core";
|
||||||
import WidgetSidebar from "../WidgetSidebar";
|
|
||||||
import history from "utils/history";
|
|
||||||
import JSDependencies from "./JSDependencies";
|
import JSDependencies from "./JSDependencies";
|
||||||
import PerformanceTracker, {
|
import PerformanceTracker, {
|
||||||
PerformanceTransactionName,
|
PerformanceTransactionName,
|
||||||
|
|
@ -29,6 +27,8 @@ import Datasources from "./Datasources";
|
||||||
import Files from "./Files";
|
import Files from "./Files";
|
||||||
import ExplorerWidgetGroup from "./Widgets/WidgetGroup";
|
import ExplorerWidgetGroup from "./Widgets/WidgetGroup";
|
||||||
import { builderURL } from "RouteBuilder";
|
import { builderURL } from "RouteBuilder";
|
||||||
|
import history from "utils/history";
|
||||||
|
import { SEARCH_ENTITY } from "constants/Explorer";
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
@ -71,7 +71,7 @@ const StyledDivider = styled(Divider)`
|
||||||
border-bottom-color: #f0f0f0;
|
border-bottom-color: #f0f0f0;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function EntityExplorer(props: IPanelProps) {
|
function EntityExplorer({ isActive }: { isActive: boolean }) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [searchKeyword, setSearchKeyword] = useState("");
|
const [searchKeyword, setSearchKeyword] = useState("");
|
||||||
const searchInputRef: MutableRefObject<HTMLInputElement | null> = useRef(
|
const searchInputRef: MutableRefObject<HTMLInputElement | null> = useRef(
|
||||||
|
|
@ -86,15 +86,13 @@ function EntityExplorer(props: IPanelProps) {
|
||||||
getIsFirstTimeUserOnboardingEnabled,
|
getIsFirstTimeUserOnboardingEnabled,
|
||||||
);
|
);
|
||||||
const noResults = false;
|
const noResults = false;
|
||||||
const { openPanel } = props;
|
|
||||||
const showWidgetsSidebar = useCallback(() => {
|
const showWidgetsSidebar = useCallback(() => {
|
||||||
history.push(builderURL());
|
history.push(builderURL());
|
||||||
openPanel({ component: WidgetSidebar });
|
|
||||||
dispatch(forceOpenWidgetPanel(true));
|
dispatch(forceOpenWidgetPanel(true));
|
||||||
if (isFirstTimeUserOnboardingEnabled) {
|
if (isFirstTimeUserOnboardingEnabled) {
|
||||||
dispatch(toggleInOnboardingWidgetSelection(true));
|
dispatch(toggleInOnboardingWidgetSelection(true));
|
||||||
}
|
}
|
||||||
}, [openPanel, isFirstTimeUserOnboardingEnabled]);
|
}, [isFirstTimeUserOnboardingEnabled]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* filter entitites
|
* filter entitites
|
||||||
|
|
@ -112,10 +110,14 @@ function EntityExplorer(props: IPanelProps) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper className={"relative"} ref={explorerRef}>
|
<Wrapper
|
||||||
|
className={`relative overflow-y-auto ${isActive ? "" : "hidden"}`}
|
||||||
|
ref={explorerRef}
|
||||||
|
>
|
||||||
{/* SEARCH */}
|
{/* SEARCH */}
|
||||||
<Search
|
<Search
|
||||||
clear={clearSearchInput}
|
clear={clearSearchInput}
|
||||||
|
id={SEARCH_ENTITY}
|
||||||
isHidden
|
isHidden
|
||||||
onChange={search}
|
onChange={search}
|
||||||
ref={searchInputRef}
|
ref={searchInputRef}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ export const ExplorerSearch = forwardRef(
|
||||||
autoFocus?: boolean;
|
autoFocus?: boolean;
|
||||||
isHidden?: boolean;
|
isHidden?: boolean;
|
||||||
onChange?: (e: any) => void;
|
onChange?: (e: any) => void;
|
||||||
|
id?: string;
|
||||||
},
|
},
|
||||||
ref: Ref<HTMLInputElement>,
|
ref: Ref<HTMLInputElement>,
|
||||||
) => {
|
) => {
|
||||||
|
|
@ -62,7 +63,7 @@ export const ExplorerSearch = forwardRef(
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
autoFocus
|
autoFocus
|
||||||
className="flex-grow py-2 text-gray-800 bg-transparent placeholder-trueGray-500"
|
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)}
|
onBlur={() => setFocussed(false)}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onFocus={() => setFocussed(true)}
|
onFocus={() => setFocussed(true)}
|
||||||
|
|
@ -78,8 +79,7 @@ export const ExplorerSearch = forwardRef(
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className={classNames({
|
className={classNames({
|
||||||
"border-b border-primary-500 transition-all duration-400 absolute bottom-0": true,
|
"border-b border-primary-500 absolute bottom-0": true,
|
||||||
"w-0": !focussed,
|
|
||||||
"w-full": focussed,
|
"w-full": focussed,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,73 @@
|
||||||
import { IPanelProps } from "@blueprintjs/core";
|
import { toggleInOnboardingWidgetSelection } from "actions/onboardingActions";
|
||||||
import React from "react";
|
import { forceOpenWidgetPanel } from "actions/widgetSidebarActions";
|
||||||
import { useEffect } from "react";
|
import { Switcher } from "components/ads";
|
||||||
import { useSelector } from "react-redux";
|
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 { 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 WidgetSidebar from "../WidgetSidebar";
|
||||||
import EntityExplorer from "./EntityExplorer";
|
import EntityExplorer from "./EntityExplorer";
|
||||||
|
|
||||||
const isForceOpenWidgetPanelSelector = (state: AppState) =>
|
const selectForceOpenWidgetPanel = (state: AppState) =>
|
||||||
state.ui.onBoarding.forceOpenWidgetPanel;
|
state.ui.onBoarding.forceOpenWidgetPanel;
|
||||||
|
|
||||||
function ExplorerContent(props: IPanelProps) {
|
function ExplorerContent() {
|
||||||
const isForceOpenWidgetPanel = useSelector(isForceOpenWidgetPanelSelector);
|
const dispatch = useDispatch();
|
||||||
useEffect(() => {
|
const isFirstTimeUserOnboardingEnabled = useSelector(
|
||||||
if (isForceOpenWidgetPanel) {
|
getIsFirstTimeUserOnboardingEnabled,
|
||||||
props.openPanel({ component: WidgetSidebar });
|
);
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
}, [isForceOpenWidgetPanel]);
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
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;
|
export default ExplorerContent;
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ import {
|
||||||
ONBOARDING_STATUS_STEPS_THIRD_ALT,
|
ONBOARDING_STATUS_STEPS_THIRD_ALT,
|
||||||
} from "@appsmith/constants/messages";
|
} from "@appsmith/constants/messages";
|
||||||
import { getTypographyByKey } from "constants/DefaultTheme";
|
import { getTypographyByKey } from "constants/DefaultTheme";
|
||||||
|
|
||||||
import { Colors } from "constants/Colors";
|
import { Colors } from "constants/Colors";
|
||||||
import { onboardingCheckListUrl } from "RouteBuilder";
|
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 { useSelector } from "react-redux";
|
||||||
import WidgetCard from "./WidgetCard";
|
import WidgetCard from "./WidgetCard";
|
||||||
import { getWidgetCards } from "selectors/editorSelectors";
|
import { getWidgetCards } from "selectors/editorSelectors";
|
||||||
import { IPanelProps } from "@blueprintjs/core";
|
|
||||||
import ExplorerSearch from "./Explorer/ExplorerSearch";
|
import ExplorerSearch from "./Explorer/ExplorerSearch";
|
||||||
import { debounce } from "lodash";
|
import { debounce } from "lodash";
|
||||||
import produce from "immer";
|
import produce from "immer";
|
||||||
import { useLocation } from "react-router";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createMessage,
|
createMessage,
|
||||||
WIDGET_SIDEBAR_CAPTION,
|
WIDGET_SIDEBAR_CAPTION,
|
||||||
} from "@appsmith/constants/messages";
|
} from "@appsmith/constants/messages";
|
||||||
import { matchBuilderPath } from "constants/routes";
|
|
||||||
import { AppState } from "reducers";
|
|
||||||
|
|
||||||
function WidgetSidebar(props: IPanelProps) {
|
function WidgetSidebar({ isActive }: { isActive: boolean }) {
|
||||||
const location = useLocation();
|
|
||||||
const cards = useSelector(getWidgetCards);
|
const cards = useSelector(getWidgetCards);
|
||||||
const [filteredCards, setFilteredCards] = useState(cards);
|
const [filteredCards, setFilteredCards] = useState(cards);
|
||||||
const searchInputRef = useRef<HTMLInputElement | null>(null);
|
const searchInputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
@ -33,17 +27,6 @@ function WidgetSidebar(props: IPanelProps) {
|
||||||
}
|
}
|
||||||
setFilteredCards(filteredCards);
|
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
|
* filter widgets
|
||||||
|
|
@ -64,7 +47,9 @@ function WidgetSidebar(props: IPanelProps) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col overflow-hidden">
|
<div
|
||||||
|
className={`flex flex-col overflow-hidden ${isActive ? "" : "hidden"}`}
|
||||||
|
>
|
||||||
<ExplorerSearch
|
<ExplorerSearch
|
||||||
autoFocus
|
autoFocus
|
||||||
clear={clearSearchInput}
|
clear={clearSearchInput}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import {
|
||||||
} from "./StyledComponents";
|
} from "./StyledComponents";
|
||||||
import { getCurrentUser as refreshCurrentUser } from "actions/authActions";
|
import { getCurrentUser as refreshCurrentUser } from "actions/authActions";
|
||||||
import { getAppsmithConfigs } from "@appsmith/configs";
|
import { getAppsmithConfigs } from "@appsmith/configs";
|
||||||
|
import { ANONYMOUS_USERNAME } from "constants/userConstants";
|
||||||
const { disableLoginForm } = getAppsmithConfigs();
|
const { disableLoginForm } = getAppsmithConfigs();
|
||||||
|
|
||||||
const ForgotPassword = styled.a`
|
const ForgotPassword = styled.a`
|
||||||
|
|
@ -73,6 +74,8 @@ function General() {
|
||||||
dispatch(refreshCurrentUser());
|
dispatch(refreshCurrentUser());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
if (user?.email === ANONYMOUS_USERNAME) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<FieldWrapper>
|
<FieldWrapper>
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,12 @@ import {
|
||||||
collisionCheckPostReflow,
|
collisionCheckPostReflow,
|
||||||
getBottomRowAfterReflow,
|
getBottomRowAfterReflow,
|
||||||
} from "utils/reflowHookUtils";
|
} from "utils/reflowHookUtils";
|
||||||
import { PrevReflowState, ReflowDirection, SpaceMap } from "reflow/reflowTypes";
|
import {
|
||||||
|
GridProps,
|
||||||
|
PrevReflowState,
|
||||||
|
ReflowDirection,
|
||||||
|
SpaceMap,
|
||||||
|
} from "reflow/reflowTypes";
|
||||||
import { WidgetSpace } from "constants/CanvasEditorConstants";
|
import { WidgetSpace } from "constants/CanvasEditorConstants";
|
||||||
import { reflow } from "reflow";
|
import { reflow } from "reflow";
|
||||||
import { getBottomMostRow } from "reflow/reflowUtils";
|
import { getBottomMostRow } from "reflow/reflowUtils";
|
||||||
|
|
@ -845,7 +850,6 @@ export function calculateNewWidgetPosition(
|
||||||
* @param copiedTotalWidth total width of the copied widgets
|
* @param copiedTotalWidth total width of the copied widgets
|
||||||
* @param copiedTopMostRow top row of the top most copied widget
|
* @param copiedTopMostRow top row of the top most copied widget
|
||||||
* @param copiedLeftMostColumn left column of the left 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
|
* @returns
|
||||||
*/
|
*/
|
||||||
const getNewPositions = function*(
|
const getNewPositions = function*(
|
||||||
|
|
@ -854,11 +858,7 @@ const getNewPositions = function*(
|
||||||
copiedTotalWidth: number,
|
copiedTotalWidth: number,
|
||||||
copiedTopMostRow: number,
|
copiedTopMostRow: number,
|
||||||
copiedLeftMostColumn: 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 selectedWidgetIDs: string[] = yield select(getSelectedWidgets);
|
||||||
const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets);
|
const canvasWidgets: CanvasWidgetsReduxState = yield select(getWidgets);
|
||||||
const {
|
const {
|
||||||
|
|
@ -1187,16 +1187,18 @@ function* pasteWidgetSaga(
|
||||||
let widgets: CanvasWidgetsReduxState = canvasWidgets;
|
let widgets: CanvasWidgetsReduxState = canvasWidgets;
|
||||||
const selectedWidget: FlattenedWidgetProps<undefined> = yield getSelectedWidgetWhenPasting();
|
const selectedWidget: FlattenedWidgetProps<undefined> = yield getSelectedWidgetWhenPasting();
|
||||||
|
|
||||||
|
let reflowedMovementMap,
|
||||||
|
bottomMostRow: number | undefined,
|
||||||
|
gridProps: GridProps | undefined,
|
||||||
|
newPastingPositionMap: SpaceMap | undefined,
|
||||||
|
canvasId;
|
||||||
|
|
||||||
let pastingIntoWidgetId: string = yield getParentWidgetIdForPasting(
|
let pastingIntoWidgetId: string = yield getParentWidgetIdForPasting(
|
||||||
canvasWidgets,
|
canvasWidgets,
|
||||||
selectedWidget,
|
selectedWidget,
|
||||||
);
|
);
|
||||||
|
|
||||||
let isThereACollision: boolean = yield isSelectedWidgetsColliding(
|
let isThereACollision = false;
|
||||||
widgets,
|
|
||||||
copiedWidgetGroups,
|
|
||||||
pastingIntoWidgetId,
|
|
||||||
);
|
|
||||||
|
|
||||||
// if this is true, selected widgets will be grouped in container
|
// if this is true, selected widgets will be grouped in container
|
||||||
if (shouldGroup) {
|
if (shouldGroup) {
|
||||||
|
|
@ -1204,7 +1206,6 @@ function* pasteWidgetSaga(
|
||||||
pastingIntoWidgetId = yield getParentWidgetIdForGrouping(
|
pastingIntoWidgetId = yield getParentWidgetIdForGrouping(
|
||||||
widgets,
|
widgets,
|
||||||
copiedWidgetGroups,
|
copiedWidgetGroups,
|
||||||
pastingIntoWidgetId,
|
|
||||||
);
|
);
|
||||||
widgets = yield filterOutSelectedWidgets(
|
widgets = yield filterOutSelectedWidgets(
|
||||||
copiedWidgetGroups[0].parentId,
|
copiedWidgetGroups[0].parentId,
|
||||||
|
|
@ -1216,10 +1217,20 @@ function* pasteWidgetSaga(
|
||||||
pastingIntoWidgetId,
|
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,
|
copiedWidgetGroups,
|
||||||
pastingIntoWidgetId,
|
pastingIntoWidgetId,
|
||||||
);
|
isThereACollision,
|
||||||
|
));
|
||||||
} else if (isCopiedModalWidget(copiedWidgetGroups, widgets)) {
|
} else if (isCopiedModalWidget(copiedWidgetGroups, widgets)) {
|
||||||
pastingIntoWidgetId = MAIN_CONTAINER_WIDGET_ID;
|
pastingIntoWidgetId = MAIN_CONTAINER_WIDGET_ID;
|
||||||
}
|
}
|
||||||
|
|
@ -1242,25 +1253,27 @@ function* pasteWidgetSaga(
|
||||||
widgets,
|
widgets,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// skip new position calculation if grouping
|
||||||
|
if (!shouldGroup) {
|
||||||
// new pasting positions, the variables are undefined if the positions cannot be calculated,
|
// 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
|
// then it pastes the regular way at the bottom of the canvas
|
||||||
const {
|
({
|
||||||
bottomMostRow,
|
bottomMostRow,
|
||||||
canvasId,
|
canvasId,
|
||||||
gridProps,
|
gridProps,
|
||||||
newPastingPositionMap,
|
newPastingPositionMap,
|
||||||
reflowedMovementMap,
|
reflowedMovementMap,
|
||||||
}: NewPastePositionVariables = yield call(
|
} = yield call(
|
||||||
getNewPositions,
|
getNewPositions,
|
||||||
copiedWidgetGroups,
|
copiedWidgetGroups,
|
||||||
action.payload.mouseLocation,
|
action.payload.mouseLocation,
|
||||||
copiedTotalWidth,
|
copiedTotalWidth,
|
||||||
topMostWidget.topRow,
|
topMostWidget.topRow,
|
||||||
leftMostWidget.leftColumn,
|
leftMostWidget.leftColumn,
|
||||||
shouldGroup,
|
));
|
||||||
);
|
|
||||||
|
|
||||||
if (canvasId) pastingIntoWidgetId = canvasId;
|
if (canvasId) pastingIntoWidgetId = canvasId;
|
||||||
|
}
|
||||||
|
|
||||||
yield all(
|
yield all(
|
||||||
copiedWidgetGroups.map((copiedWidgets) =>
|
copiedWidgetGroups.map((copiedWidgets) =>
|
||||||
|
|
|
||||||
|
|
@ -33,15 +33,24 @@ import {
|
||||||
import { getNextEntityName } from "utils/AppsmithUtils";
|
import { getNextEntityName } from "utils/AppsmithUtils";
|
||||||
import WidgetFactory from "utils/WidgetFactory";
|
import WidgetFactory from "utils/WidgetFactory";
|
||||||
import { getParentWithEnhancementFn } from "./WidgetEnhancementHelpers";
|
import { getParentWithEnhancementFn } from "./WidgetEnhancementHelpers";
|
||||||
import { OccupiedSpace } from "constants/CanvasEditorConstants";
|
import { OccupiedSpace, WidgetSpace } from "constants/CanvasEditorConstants";
|
||||||
import { areIntersecting } from "utils/WidgetPropsUtils";
|
import { areIntersecting } from "utils/WidgetPropsUtils";
|
||||||
import { GridProps, ReflowedSpaceMap, SpaceMap } from "reflow/reflowTypes";
|
import {
|
||||||
|
GridProps,
|
||||||
|
PrevReflowState,
|
||||||
|
ReflowDirection,
|
||||||
|
ReflowedSpaceMap,
|
||||||
|
SpaceMap,
|
||||||
|
} from "reflow/reflowTypes";
|
||||||
import {
|
import {
|
||||||
getBaseWidgetClassName,
|
getBaseWidgetClassName,
|
||||||
getSlidingCanvasName,
|
getSlidingCanvasName,
|
||||||
getStickyCanvasName,
|
getStickyCanvasName,
|
||||||
POSITIONED_WIDGET,
|
POSITIONED_WIDGET,
|
||||||
} from "constants/componentClassNameConstants";
|
} from "constants/componentClassNameConstants";
|
||||||
|
import { getWidgetSpacesSelectorForContainer } from "selectors/editorSelectors";
|
||||||
|
import { reflow } from "reflow";
|
||||||
|
import { getBottomRowAfterReflow } from "utils/reflowHookUtils";
|
||||||
|
|
||||||
export interface CopiedWidgetGroup {
|
export interface CopiedWidgetGroup {
|
||||||
widgetId: string;
|
widgetId: string;
|
||||||
|
|
@ -992,6 +1001,7 @@ export function isDropTarget(type: WidgetType, includeCanvasWidget = false) {
|
||||||
export const groupWidgetsIntoContainer = function*(
|
export const groupWidgetsIntoContainer = function*(
|
||||||
copiedWidgetGroups: CopiedWidgetGroup[],
|
copiedWidgetGroups: CopiedWidgetGroup[],
|
||||||
pastingIntoWidgetId: string,
|
pastingIntoWidgetId: string,
|
||||||
|
isThereACollision: boolean,
|
||||||
) {
|
) {
|
||||||
const containerWidgetId = generateReactKey();
|
const containerWidgetId = generateReactKey();
|
||||||
const evalTree: DataTree = yield select(getDataTree);
|
const evalTree: DataTree = yield select(getDataTree);
|
||||||
|
|
@ -1006,6 +1016,7 @@ export const groupWidgetsIntoContainer = function*(
|
||||||
"CANVAS_WIDGET",
|
"CANVAS_WIDGET",
|
||||||
evalTree,
|
evalTree,
|
||||||
);
|
);
|
||||||
|
let reflowedMovementMap, bottomMostRow, gridProps;
|
||||||
const {
|
const {
|
||||||
bottomMostWidget,
|
bottomMostWidget,
|
||||||
leftMostWidget,
|
leftMostWidget,
|
||||||
|
|
@ -1018,8 +1029,12 @@ export const groupWidgetsIntoContainer = function*(
|
||||||
(w) => w.widgetId === copiedWidgetGroup.widgetId,
|
(w) => w.widgetId === copiedWidgetGroup.widgetId,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//calculating parentColumnSpace because the values stored inside widget DSL are not entirely reliable
|
||||||
const parentColumnSpace =
|
const parentColumnSpace =
|
||||||
copiedWidgetGroups[0].list[0].parentColumnSpace || 1;
|
getParentColumnSpace(canvasWidgets, pastingIntoWidgetId) ||
|
||||||
|
copiedWidgetGroups[0].list[0].parentColumnSpace ||
|
||||||
|
1;
|
||||||
|
|
||||||
const boundary = {
|
const boundary = {
|
||||||
top: _.minBy(copiedWidgets, (copiedWidget) => copiedWidget?.topRow),
|
top: _.minBy(copiedWidgets, (copiedWidget) => copiedWidget?.topRow),
|
||||||
|
|
@ -1124,13 +1139,73 @@ export const groupWidgetsIntoContainer = function*(
|
||||||
|
|
||||||
const flatList = _.flattenDeep(list);
|
const flatList = _.flattenDeep(list);
|
||||||
|
|
||||||
return [
|
// 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],
|
list: [newContainerWidget, newCanvasWidget, ...flatList],
|
||||||
widgetId: newContainerWidget.widgetId,
|
widgetId: newContainerWidget.widgetId,
|
||||||
parentId: pastingIntoWidgetId,
|
parentId: pastingIntoWidgetId,
|
||||||
},
|
},
|
||||||
];
|
],
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1225,27 +1300,20 @@ export const isSelectedWidgetsColliding = function*(
|
||||||
widget.parentId === pastingIntoWidgetId && widget.type !== "MODAL_WIDGET",
|
widget.parentId === pastingIntoWidgetId && widget.type !== "MODAL_WIDGET",
|
||||||
);
|
);
|
||||||
|
|
||||||
let isColliding = false;
|
|
||||||
|
|
||||||
for (let i = 0; i < widgetsArray.length; i++) {
|
for (let i = 0; i < widgetsArray.length; i++) {
|
||||||
const widget = widgetsArray[i];
|
const widget = widgetsArray[i];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
widget.bottomRow + 2 < topMostWidget.topRow ||
|
!(
|
||||||
widget.topRow > bottomMostWidget.bottomRow
|
widget.leftColumn >= rightMostWidget.rightColumn ||
|
||||||
) {
|
widget.rightColumn <= leftMostWidget.leftColumn ||
|
||||||
isColliding = false;
|
widget.topRow >= bottomMostWidget.bottomRow ||
|
||||||
} else if (
|
widget.bottomRow <= topMostWidget.topRow
|
||||||
widget.rightColumn < leftMostWidget.leftColumn ||
|
)
|
||||||
widget.leftColumn > rightMostWidget.rightColumn
|
)
|
||||||
) {
|
|
||||||
isColliding = false;
|
|
||||||
} else {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return isColliding;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1351,8 +1419,8 @@ export const getParentBottomRowAfterAddingWidget = (
|
||||||
export function* getParentWidgetIdForGrouping(
|
export function* getParentWidgetIdForGrouping(
|
||||||
widgets: CanvasWidgetsReduxState,
|
widgets: CanvasWidgetsReduxState,
|
||||||
copiedWidgetGroups: CopiedWidgetGroup[],
|
copiedWidgetGroups: CopiedWidgetGroup[],
|
||||||
pastingIntoWidgetId: string,
|
|
||||||
) {
|
) {
|
||||||
|
const pastingIntoWidgetId = copiedWidgetGroups[0]?.parentId;
|
||||||
const widgetIds = copiedWidgetGroups.map(
|
const widgetIds = copiedWidgetGroups.map(
|
||||||
(widgetGroup) => widgetGroup.widgetId,
|
(widgetGroup) => widgetGroup.widgetId,
|
||||||
);
|
);
|
||||||
|
|
@ -1445,6 +1513,33 @@ export function purgeOrphanedDynamicPaths(widget: WidgetProps) {
|
||||||
return widget;
|
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
|
* Function to extend the lodash's get function to check
|
||||||
* paths which have dots in it's key
|
* 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, {
|
const initialSchema = SchemaParser.parse(widgetName, {
|
||||||
currSourceData: testData.initialDataset.dataSource,
|
currSourceData: testData.initialDataset.dataSource,
|
||||||
schema: {},
|
schema: {},
|
||||||
|
|
@ -71,45 +72,390 @@ describe("#parse", () => {
|
||||||
|
|
||||||
expect(initialSchema).toEqual(testData.initialDataset.schemaOutput);
|
expect(initialSchema).toEqual(testData.initialDataset.schemaOutput);
|
||||||
|
|
||||||
// With null field
|
// Set all keys to null
|
||||||
const nulledDataSource = klona(testData.initialDataset.dataSource);
|
const nulledSourceData = klona(testData.initialDataset.dataSource);
|
||||||
set(nulledDataSource, "dob", null);
|
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 the sourceData entry in each SchemaItem to null (only property that changes)
|
||||||
set(expectedNulledSchema, "__root_schema__.children.dob.sourceData", null);
|
const expectedSchema = klona(initialSchema);
|
||||||
set(expectedNulledSchema, "__root_schema__.sourceData.dob", null);
|
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, {
|
// Parse with the nulled sourceData
|
||||||
currSourceData: nulledDataSource,
|
const schemaWithNullKeys = SchemaParser.parse(widgetName, {
|
||||||
|
currSourceData: nulledSourceData,
|
||||||
schema: initialSchema,
|
schema: initialSchema,
|
||||||
fieldThemeStylesheets: testData.fieldThemeStylesheets,
|
fieldThemeStylesheets: testData.fieldThemeStylesheets,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(schemaWithNulledField).toEqual(expectedNulledSchema);
|
expect(schemaWithNullKeys).toEqual(expectedSchema);
|
||||||
|
|
||||||
// With undefined field
|
/**
|
||||||
const undefinedDataSource = klona(nulledDataSource);
|
* Parse with initial sourceData to check if previous schema with null sourceData
|
||||||
set(undefinedDataSource, "boolean", undefined);
|
* can still retain the schema structure
|
||||||
|
*/
|
||||||
const expectedUndefinedSchema = klona(expectedNulledSchema);
|
const schemaWithRevertedData = SchemaParser.parse(widgetName, {
|
||||||
set(
|
currSourceData: testData.initialDataset.dataSource,
|
||||||
expectedUndefinedSchema,
|
schema: schemaWithNullKeys,
|
||||||
"__root_schema__.children.boolean.sourceData",
|
|
||||||
undefined,
|
|
||||||
);
|
|
||||||
set(
|
|
||||||
expectedUndefinedSchema,
|
|
||||||
"__root_schema__.sourceData.boolean",
|
|
||||||
undefined,
|
|
||||||
);
|
|
||||||
|
|
||||||
const schemaWithUndefinedField = SchemaParser.parse(widgetName, {
|
|
||||||
currSourceData: undefinedDataSource,
|
|
||||||
schema: schemaWithNulledField,
|
|
||||||
fieldThemeStylesheets: testData.fieldThemeStylesheets,
|
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";
|
} from "./constants";
|
||||||
import { getFieldStylesheet } from "./helper";
|
import { getFieldStylesheet } from "./helper";
|
||||||
|
|
||||||
type Obj = Record<string, any>;
|
type Obj = Record<string, unknown>;
|
||||||
type JSON = Obj | Obj[];
|
|
||||||
|
|
||||||
type ParserOptions = {
|
type ParserOptions = {
|
||||||
currSourceData?: JSON | string;
|
currSourceData?: unknown;
|
||||||
fieldThemeStylesheets?: FieldThemeStylesheet;
|
fieldThemeStylesheets?: FieldThemeStylesheet;
|
||||||
fieldType?: FieldType;
|
fieldType?: FieldType;
|
||||||
isCustomField?: boolean;
|
isCustomField?: boolean;
|
||||||
|
|
@ -60,11 +59,15 @@ type GetKeysFromSchemaOptions = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type ParseOptions = {
|
type ParseOptions = {
|
||||||
currSourceData?: JSON;
|
currSourceData?: unknown;
|
||||||
schema?: Schema;
|
schema?: Schema;
|
||||||
fieldThemeStylesheets?: FieldThemeStylesheet;
|
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
|
* 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
|
// This method deals with the conversion of array data to a schema
|
||||||
static convertArrayToSchema = ({
|
static convertArrayToSchema = ({
|
||||||
currSourceData = [],
|
currSourceData,
|
||||||
fieldThemeStylesheets,
|
fieldThemeStylesheets,
|
||||||
prevSchema = {},
|
prevSchema = {},
|
||||||
sourceDataPath,
|
sourceDataPath,
|
||||||
|
|
@ -612,7 +615,12 @@ class SchemaParser {
|
||||||
...rest
|
...rest
|
||||||
}: Omit<ParserOptions, "identifier">): Schema => {
|
}: Omit<ParserOptions, "identifier">): Schema => {
|
||||||
const schema = klona(prevSchema);
|
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 prevDataType = schema[ARRAY_ITEM_KEY]?.dataType;
|
||||||
const currDataType = dataTypeFor(currData);
|
const currDataType = dataTypeFor(currData);
|
||||||
|
|
@ -644,7 +652,7 @@ class SchemaParser {
|
||||||
|
|
||||||
// This method deals with the conversion of object data to a schema
|
// This method deals with the conversion of object data to a schema
|
||||||
static convertObjectToSchema = ({
|
static convertObjectToSchema = ({
|
||||||
currSourceData = {},
|
currSourceData,
|
||||||
prevSchema = {},
|
prevSchema = {},
|
||||||
sourceDataPath,
|
sourceDataPath,
|
||||||
widgetName,
|
widgetName,
|
||||||
|
|
@ -654,8 +662,10 @@ class SchemaParser {
|
||||||
const origIdentifierToIdentifierMap = mapOriginalIdentifierToSanitizedIdentifier(
|
const origIdentifierToIdentifierMap = mapOriginalIdentifierToSanitizedIdentifier(
|
||||||
schema,
|
schema,
|
||||||
);
|
);
|
||||||
const currObj = currSourceData as Obj;
|
|
||||||
|
|
||||||
|
if (!isObject(currSourceData)) {
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
const customFieldAccessors = getKeysFromSchema(prevSchema, ["accessor"], {
|
const customFieldAccessors = getKeysFromSchema(prevSchema, ["accessor"], {
|
||||||
onlyCustomFieldKeys: true,
|
onlyCustomFieldKeys: true,
|
||||||
});
|
});
|
||||||
|
|
@ -683,7 +693,7 @@ class SchemaParser {
|
||||||
modifiedKeys.forEach((modifiedKey) => {
|
modifiedKeys.forEach((modifiedKey) => {
|
||||||
const identifier = origIdentifierToIdentifierMap[modifiedKey];
|
const identifier = origIdentifierToIdentifierMap[modifiedKey];
|
||||||
const prevSchemaItem = klona(schema[identifier]);
|
const prevSchemaItem = klona(schema[identifier]);
|
||||||
const currData = currObj[modifiedKey];
|
const currData = currSourceData[modifiedKey];
|
||||||
const prevData = prevSchemaItem.sourceData;
|
const prevData = prevSchemaItem.sourceData;
|
||||||
const currDataType = dataTypeFor(currData);
|
const currDataType = dataTypeFor(currData);
|
||||||
const prevDataType = schema[identifier].dataType;
|
const prevDataType = schema[identifier].dataType;
|
||||||
|
|
@ -743,7 +753,7 @@ class SchemaParser {
|
||||||
newKeys.forEach((newKey) => {
|
newKeys.forEach((newKey) => {
|
||||||
const schemaItem = SchemaParser.getSchemaItemFor(newKey, {
|
const schemaItem = SchemaParser.getSchemaItemFor(newKey, {
|
||||||
...rest,
|
...rest,
|
||||||
currSourceData: currObj[newKey],
|
currSourceData: currSourceData[newKey],
|
||||||
sourceDataPath: getSourcePath(newKey, sourceDataPath),
|
sourceDataPath: getSourcePath(newKey, sourceDataPath),
|
||||||
identifier: sanitizeSchemaItemKey(newKey, schema),
|
identifier: sanitizeSchemaItemKey(newKey, schema),
|
||||||
widgetName,
|
widgetName,
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ class PhoneInputWidget extends BaseInputWidget<
|
||||||
propertyName: "defaultText",
|
propertyName: "defaultText",
|
||||||
label: "Default Text",
|
label: "Default Text",
|
||||||
controlType: "INPUT_TEXT",
|
controlType: "INPUT_TEXT",
|
||||||
placeholderText: "John Doe",
|
placeholderText: "(000) 000-0000",
|
||||||
isBindProperty: true,
|
isBindProperty: true,
|
||||||
isTriggerProperty: false,
|
isTriggerProperty: false,
|
||||||
validation: {
|
validation: {
|
||||||
|
|
@ -115,7 +115,7 @@ class PhoneInputWidget extends BaseInputWidget<
|
||||||
fn: defaultValueValidation,
|
fn: defaultValueValidation,
|
||||||
expected: {
|
expected: {
|
||||||
type: "string",
|
type: "string",
|
||||||
example: `000 0000`,
|
example: `(000) 000-0000`,
|
||||||
autocompleteDataType: AutocompleteDataType.STRING,
|
autocompleteDataType: AutocompleteDataType.STRING,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -89,13 +89,7 @@ public class ApplicationControllerCE extends BaseController<ApplicationService,
|
||||||
public Mono<ResponseDTO<Boolean>> publish(@PathVariable String defaultApplicationId,
|
public Mono<ResponseDTO<Boolean>> publish(@PathVariable String defaultApplicationId,
|
||||||
@RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName) {
|
@RequestHeader(name = FieldName.BRANCH_NAME, required = false) String branchName) {
|
||||||
return applicationPageService.publish(defaultApplicationId, branchName, true)
|
return applicationPageService.publish(defaultApplicationId, branchName, true)
|
||||||
.flatMap(application ->
|
.thenReturn(new ResponseDTO<>(HttpStatus.OK.value(), true, null));
|
||||||
// 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))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{defaultApplicationId}/page/{defaultPageId}/makeDefault")
|
@PutMapping("/{defaultApplicationId}/page/{defaultPageId}/makeDefault")
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,6 @@ public interface ApplicationPageServiceCE {
|
||||||
|
|
||||||
void generateAndSetPagePolicies(Application application, PageDTO page);
|
void generateAndSetPagePolicies(Application application, PageDTO page);
|
||||||
|
|
||||||
Mono<Void> sendApplicationPublishedEvent(Application application);
|
|
||||||
|
|
||||||
Mono<ApplicationPagesDTO> reorderPage(String applicationId, String pageId, Integer order, String branchName);
|
Mono<ApplicationPagesDTO> reorderPage(String applicationId, String pageId, Integer order, String branchName);
|
||||||
|
|
||||||
Mono<Application> deleteApplicationByResource(Application application);
|
Mono<Application> deleteApplicationByResource(Application application);
|
||||||
|
|
|
||||||
|
|
@ -892,7 +892,7 @@ public class ApplicationPageServiceCEImpl implements ApplicationPageServiceCE {
|
||||||
application -> themeService.publishTheme(application.getId())
|
application -> themeService.publishTheme(application.getId())
|
||||||
);
|
);
|
||||||
|
|
||||||
Flux<NewPage> publishApplicationAndPages = applicationMono
|
Mono<List<NewPage>> publishApplicationAndPages = applicationMono
|
||||||
//Return all the pages in the Application
|
//Return all the pages in the Application
|
||||||
.flatMap(application -> {
|
.flatMap(application -> {
|
||||||
List<ApplicationPage> pages = application.getPages();
|
List<ApplicationPage> pages = application.getPages();
|
||||||
|
|
@ -952,10 +952,11 @@ public class ApplicationPageServiceCEImpl implements ApplicationPageServiceCE {
|
||||||
page.setPublishedPage(page.getUnpublishedPage());
|
page.setPublishedPage(page.getUnpublishedPage());
|
||||||
return page;
|
return page;
|
||||||
}))
|
}))
|
||||||
|
.flatMap(newPageService::save)
|
||||||
.collectList()
|
.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)
|
.findAllByApplicationIdAndViewMode(applicationId, false, MANAGE_ACTIONS, null)
|
||||||
.flatMap(newAction -> {
|
.flatMap(newAction -> {
|
||||||
// If the action was deleted in edit mode, now this document can be safely archived
|
// 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());
|
newAction.setPublishedAction(newAction.getUnpublishedAction());
|
||||||
return Mono.just(newAction);
|
return Mono.just(newAction);
|
||||||
})
|
})
|
||||||
|
.flatMap(newActionService::save)
|
||||||
.collectList()
|
.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)
|
.findAllByApplicationIdAndViewMode(applicationId, false, MANAGE_ACTIONS, null)
|
||||||
.flatMap(collection -> {
|
.flatMap(collection -> {
|
||||||
// If the collection was deleted in edit mode, now this can be safely deleted from the repository
|
// 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());
|
collection.setPublishedCollection(collection.getUnpublishedCollection());
|
||||||
return Mono.just(collection);
|
return Mono.just(collection);
|
||||||
})
|
})
|
||||||
.collectList()
|
.flatMap(actionCollectionService::save)
|
||||||
.flatMapMany(actionCollectionService::saveAll);
|
.collectList();
|
||||||
|
|
||||||
return Mono.when(
|
return Mono.when(
|
||||||
publishApplicationAndPages.collectList(),
|
publishApplicationAndPages,
|
||||||
publishedActionsFlux.collectList(),
|
publishedActionsListMono,
|
||||||
publishedCollectionsFlux,
|
publishedActionCollectionsListMono,
|
||||||
publishThemeMono
|
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
|
@Override
|
||||||
|
|
@ -1001,34 +1029,6 @@ public class ApplicationPageServiceCEImpl implements ApplicationPageServiceCE {
|
||||||
.map(responseUtils::updateApplicationWithDefaultResources);
|
.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.
|
/** 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.
|
* 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
|
* @param defaultAppId The id of the Application
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user