Merge branch 'feature/acl' into fix/invite-users
This commit is contained in:
commit
7c3d18b7f6
|
|
@ -10,5 +10,7 @@
|
||||||
"overwrite": false,
|
"overwrite": false,
|
||||||
"html": true,
|
"html": true,
|
||||||
"json": false
|
"json": false
|
||||||
}
|
},
|
||||||
|
"viewportHeight": 900,
|
||||||
|
"viewportWidth": 1400
|
||||||
}
|
}
|
||||||
|
|
|
||||||
186
app/client/cypress/fixtures/ChartTextDsl.json
Normal file
186
app/client/cypress/fixtures/ChartTextDsl.json
Normal file
|
|
@ -0,0 +1,186 @@
|
||||||
|
{
|
||||||
|
"dsl": {
|
||||||
|
"widgetName": "MainContainer",
|
||||||
|
"backgroundColor": "none",
|
||||||
|
"rightColumn": 1224,
|
||||||
|
"snapColumns": 16,
|
||||||
|
"detachFromLayout": true,
|
||||||
|
"widgetId": "0",
|
||||||
|
"topRow": 0,
|
||||||
|
"bottomRow": 1292,
|
||||||
|
"containerStyle": "none",
|
||||||
|
"snapRows": 33,
|
||||||
|
"parentRowSpace": 1,
|
||||||
|
"type": "CANVAS_WIDGET",
|
||||||
|
"canExtend": true,
|
||||||
|
"dynamicBindings": {},
|
||||||
|
"version": 4,
|
||||||
|
"minHeight": 1292,
|
||||||
|
"parentColumnSpace": 1,
|
||||||
|
"leftColumn": 0,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"backgroundColor": "#FFFFFF",
|
||||||
|
"widgetName": "Container1",
|
||||||
|
"type": "CONTAINER_WIDGET",
|
||||||
|
"containerStyle": "card",
|
||||||
|
"isVisible": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"parentColumnSpace": 75.25,
|
||||||
|
"parentRowSpace": 38,
|
||||||
|
"dynamicBindings": {},
|
||||||
|
"leftColumn": 0,
|
||||||
|
"rightColumn": 8,
|
||||||
|
"topRow": 0,
|
||||||
|
"bottomRow": 9,
|
||||||
|
"snapColumns": 16,
|
||||||
|
"orientation": "VERTICAL",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"widgetName": "kydabisaxj",
|
||||||
|
"type": "CANVAS_WIDGET",
|
||||||
|
"containerStyle": "none",
|
||||||
|
"isVisible": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"parentColumnSpace": 1,
|
||||||
|
"parentRowSpace": 1,
|
||||||
|
"leftColumn": 0,
|
||||||
|
"rightColumn": 602,
|
||||||
|
"topRow": 0,
|
||||||
|
"bottomRow": 342,
|
||||||
|
"snapColumns": 16,
|
||||||
|
"orientation": "VERTICAL",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"isVisible": true,
|
||||||
|
"widgetName": "Chart1",
|
||||||
|
"chartType": "LINE_CHART",
|
||||||
|
"chartName": "Sales on working days",
|
||||||
|
"allowHorizontalScroll": false,
|
||||||
|
"chartData": "[{\"seriesName\":\"\",\"data\":\"\"}]",
|
||||||
|
"xAxisName": "Last Week",
|
||||||
|
"yAxisName": "Total Order Revenue $",
|
||||||
|
"type": "CHART_WIDGET",
|
||||||
|
"isLoading": false,
|
||||||
|
"parentColumnSpace": 34.6875,
|
||||||
|
"parentRowSpace": 38,
|
||||||
|
"leftColumn": 5,
|
||||||
|
"rightColumn": 11,
|
||||||
|
"topRow": 0,
|
||||||
|
"bottomRow": 8,
|
||||||
|
"parentId": "56c5odk5ic",
|
||||||
|
"widgetId": "64jukpgbzh",
|
||||||
|
"dynamicBindings": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"widgetId": "56c5odk5ic",
|
||||||
|
"detachFromLayout": true,
|
||||||
|
"canExtend": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"widgetId": "kzlk5ductp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"backgroundColor": "#FFFFFF",
|
||||||
|
"widgetName": "Container3",
|
||||||
|
"type": "CONTAINER_WIDGET",
|
||||||
|
"containerStyle": "card",
|
||||||
|
"isVisible": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"parentColumnSpace": 75.25,
|
||||||
|
"parentRowSpace": 38,
|
||||||
|
"dynamicBindings": {},
|
||||||
|
"leftColumn": 0,
|
||||||
|
"rightColumn": 16,
|
||||||
|
"topRow": 9,
|
||||||
|
"bottomRow": 23,
|
||||||
|
"snapColumns": 16,
|
||||||
|
"orientation": "VERTICAL",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"widgetName": "za6o0unktq",
|
||||||
|
"type": "CANVAS_WIDGET",
|
||||||
|
"containerStyle": "none",
|
||||||
|
"isVisible": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"parentColumnSpace": 1,
|
||||||
|
"parentRowSpace": 1,
|
||||||
|
"leftColumn": 0,
|
||||||
|
"rightColumn": 1204,
|
||||||
|
"topRow": 0,
|
||||||
|
"bottomRow": 532,
|
||||||
|
"snapColumns": 16,
|
||||||
|
"orientation": "VERTICAL",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"isVisible": true,
|
||||||
|
"text": "Label",
|
||||||
|
"textStyle": "LABEL",
|
||||||
|
"textAlign": "LEFT",
|
||||||
|
"widgetName": "Text1",
|
||||||
|
"type": "TEXT_WIDGET",
|
||||||
|
"isLoading": false,
|
||||||
|
"parentColumnSpace": 71.75,
|
||||||
|
"parentRowSpace": 38,
|
||||||
|
"leftColumn": 3,
|
||||||
|
"rightColumn": 7,
|
||||||
|
"topRow": 2,
|
||||||
|
"bottomRow": 3,
|
||||||
|
"parentId": "d2i9xsy2fk",
|
||||||
|
"widgetId": "u210slvpsz",
|
||||||
|
"dynamicBindings": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"widgetId": "d2i9xsy2fk",
|
||||||
|
"detachFromLayout": true,
|
||||||
|
"canExtend": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"widgetId": "t2a7se9pxe"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"backgroundColor": "#FFFFFF",
|
||||||
|
"widgetName": "Container4",
|
||||||
|
"type": "CONTAINER_WIDGET",
|
||||||
|
"containerStyle": "card",
|
||||||
|
"isVisible": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"parentColumnSpace": 75.25,
|
||||||
|
"parentRowSpace": 38,
|
||||||
|
"dynamicBindings": {},
|
||||||
|
"leftColumn": 8,
|
||||||
|
"rightColumn": 16,
|
||||||
|
"topRow": 0,
|
||||||
|
"bottomRow": 9,
|
||||||
|
"snapColumns": 16,
|
||||||
|
"orientation": "VERTICAL",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"widgetName": "cli9vgw4yj",
|
||||||
|
"type": "CANVAS_WIDGET",
|
||||||
|
"containerStyle": "none",
|
||||||
|
"isVisible": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"parentColumnSpace": 1,
|
||||||
|
"parentRowSpace": 1,
|
||||||
|
"leftColumn": 0,
|
||||||
|
"rightColumn": 602,
|
||||||
|
"topRow": 0,
|
||||||
|
"bottomRow": 342,
|
||||||
|
"snapColumns": 16,
|
||||||
|
"orientation": "VERTICAL",
|
||||||
|
"children": [],
|
||||||
|
"widgetId": "qzniae78ab",
|
||||||
|
"detachFromLayout": true,
|
||||||
|
"canExtend": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"widgetId": "gtsbf2q08n"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"layoutOnLoadActions": []
|
||||||
|
}
|
||||||
|
|
@ -152,5 +152,20 @@
|
||||||
"productName": "Avocado Panini",
|
"productName": "Avocado Panini",
|
||||||
"orderAmount": 7.99
|
"orderAmount": 7.99
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"chartInputValidate": [
|
||||||
|
{
|
||||||
|
"x": "Test1",
|
||||||
|
"y": 5400
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": "Test2",
|
||||||
|
"y": 10000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": "Test3",
|
||||||
|
"y": 1000
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Chartval":["Test1", "Test2", "Test3"]
|
||||||
}
|
}
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
"responsetext2": "qui est esse",
|
"responsetext2": "qui est esse",
|
||||||
"baseUrl3": "https://reqres.in",
|
"baseUrl3": "https://reqres.in",
|
||||||
"methods2": "api/users/2",
|
"methods2": "api/users/2",
|
||||||
|
"invalidPath": "api/users/a",
|
||||||
"responsetext3": "Josh M Krantz",
|
"responsetext3": "Josh M Krantz",
|
||||||
"postUrl": "https://reqres.in",
|
"postUrl": "https://reqres.in",
|
||||||
"deleteUrl": "",
|
"deleteUrl": "",
|
||||||
|
|
|
||||||
|
|
@ -148,39 +148,34 @@
|
||||||
"chartType": "BAR_CHART",
|
"chartType": "BAR_CHART",
|
||||||
"chartName": "App Sign Up",
|
"chartName": "App Sign Up",
|
||||||
"allowHorizontalScroll": false,
|
"allowHorizontalScroll": false,
|
||||||
"chartData": [
|
"singleChartData": [
|
||||||
{
|
{
|
||||||
"seriesName": "",
|
"x": "Mon",
|
||||||
"data": [
|
"y": 10000
|
||||||
{
|
},
|
||||||
"x": "Mon",
|
{
|
||||||
"y": 10000
|
"x": "Tue",
|
||||||
},
|
"y": 12000
|
||||||
{
|
},
|
||||||
"x": "Tue",
|
{
|
||||||
"y": 12000
|
"x": "Wed",
|
||||||
},
|
"y": 32000
|
||||||
{
|
},
|
||||||
"x": "Wed",
|
{
|
||||||
"y": 32000
|
"x": "Thu",
|
||||||
},
|
"y": 28000
|
||||||
{
|
},
|
||||||
"x": "Thu",
|
{
|
||||||
"y": 28000
|
"x": "Fri",
|
||||||
},
|
"y": 14000
|
||||||
{
|
},
|
||||||
"x": "Fri",
|
{
|
||||||
"y": 14000
|
"x": "Sat",
|
||||||
},
|
"y": 19000
|
||||||
{
|
},
|
||||||
"x": "Sat",
|
{
|
||||||
"y": 19000
|
"x": "Sun",
|
||||||
},
|
"y": 36000
|
||||||
{
|
|
||||||
"x": "Sun",
|
|
||||||
"y": 36000
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"xAxisName": "Last Week",
|
"xAxisName": "Last Week",
|
||||||
|
|
@ -265,4 +260,4 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"layoutOnLoadActions": []
|
"layoutOnLoadActions": []
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ describe("API Panel Test Functionality", function() {
|
||||||
cy.ClearSearch();
|
cy.ClearSearch();
|
||||||
cy.SearchAPIandClick("SecondAPI");
|
cy.SearchAPIandClick("SecondAPI");
|
||||||
//invalid api end point check
|
//invalid api end point check
|
||||||
cy.EditSourceDetail(testdata.baseUrl3, testdata.methods2);
|
cy.EditSourceDetail(testdata.baseUrl3, testdata.invalidPath);
|
||||||
cy.ResponseStatusCheck("404 NOT_FOUND");
|
cy.ResponseStatusCheck("404 NOT_FOUND");
|
||||||
cy.DeleteAPI();
|
cy.DeleteAPI();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ const testdata = require("../../../fixtures/testdata.json");
|
||||||
describe("API Panel Test Functionality ", function() {
|
describe("API Panel Test Functionality ", function() {
|
||||||
it("Test Search API fetaure", function() {
|
it("Test Search API fetaure", function() {
|
||||||
cy.log("Login Successful");
|
cy.log("Login Successful");
|
||||||
cy.viewport("macbook-15"); //To avoid screen Resize issues
|
|
||||||
cy.NavigateToAPI_Panel();
|
cy.NavigateToAPI_Panel();
|
||||||
cy.log("Navigation to API Panel screen successful");
|
cy.log("Navigation to API Panel screen successful");
|
||||||
cy.CreateAPI("FirstAPI");
|
cy.CreateAPI("FirstAPI");
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ const testdata = require("../../../fixtures/testdata.json");
|
||||||
describe("API Panel Test Functionality ", function() {
|
describe("API Panel Test Functionality ", function() {
|
||||||
it("Test API copy/Move/delete feature", function() {
|
it("Test API copy/Move/delete feature", function() {
|
||||||
cy.log("Login Successful");
|
cy.log("Login Successful");
|
||||||
cy.viewport("macbook-15"); //To avoid screen Resize issues
|
|
||||||
cy.NavigateToAPI_Panel();
|
cy.NavigateToAPI_Panel();
|
||||||
cy.log("Navigation to API Panel screen successful");
|
cy.log("Navigation to API Panel screen successful");
|
||||||
cy.CreateAPI("FirstAPI");
|
cy.CreateAPI("FirstAPI");
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
const commonlocators = require("../../../locators/commonlocators.json");
|
||||||
|
const viewWidgetsPage = require("../../../locators/ViewWidgets.json");
|
||||||
|
const publish = require("../../../locators/publishWidgetspage.json");
|
||||||
|
const dsl = require("../../../fixtures/ChartTextDsl.json");
|
||||||
|
|
||||||
|
describe("Text-Chart Binding Functionality", function() {
|
||||||
|
before(() => {
|
||||||
|
cy.addDsl(dsl);
|
||||||
|
});
|
||||||
|
it("Text-Chart Binding Functionality View", function() {
|
||||||
|
cy.openPropertyPane("textwidget");
|
||||||
|
cy.testJsontext("text", JSON.stringify(this.data.chartInputValidate));
|
||||||
|
cy.get(commonlocators.TextInside).should(
|
||||||
|
"have.text",
|
||||||
|
JSON.stringify(this.data.chartInputValidate),
|
||||||
|
);
|
||||||
|
cy.closePropertyPane();
|
||||||
|
cy.openPropertyPane("chartwidget");
|
||||||
|
cy.get(viewWidgetsPage.chartType)
|
||||||
|
.find(commonlocators.dropdownbuttonclick)
|
||||||
|
.click({ force: true })
|
||||||
|
.get(commonlocators.dropdownmenu)
|
||||||
|
.children()
|
||||||
|
.contains("Column Chart")
|
||||||
|
.click();
|
||||||
|
cy.get(viewWidgetsPage.chartType)
|
||||||
|
.find(commonlocators.menuSelection)
|
||||||
|
.should("have.text", "Column Chart");
|
||||||
|
cy.testJsontext("chartdata", "{{Text1.text}}");
|
||||||
|
cy.closePropertyPane();
|
||||||
|
const labels = [
|
||||||
|
this.data.Chartval[0],
|
||||||
|
this.data.Chartval[1],
|
||||||
|
this.data.Chartval[2],
|
||||||
|
];
|
||||||
|
[0, 1, 2].forEach(k => {
|
||||||
|
cy.get(viewWidgetsPage.rectangleChart)
|
||||||
|
.eq(k)
|
||||||
|
.trigger("mousemove", { force: true });
|
||||||
|
cy.get(viewWidgetsPage.Chartlabel)
|
||||||
|
.eq(k)
|
||||||
|
.should("have.text", labels[k]);
|
||||||
|
});
|
||||||
|
cy.PublishtheApp();
|
||||||
|
});
|
||||||
|
it("Text-Chart Binding Functionality Publish", function() {
|
||||||
|
cy.get(publish.chartCanvasVal).should("be.visible");
|
||||||
|
cy.get(publish.chartWidget).should("have.css", "opacity", "1");
|
||||||
|
const labels = [
|
||||||
|
this.data.Chartval[0],
|
||||||
|
this.data.Chartval[1],
|
||||||
|
this.data.Chartval[2],
|
||||||
|
];
|
||||||
|
[0, 1, 2].forEach(k => {
|
||||||
|
cy.get(publish.rectChart)
|
||||||
|
.eq(k)
|
||||||
|
.trigger("mousemove", { force: true });
|
||||||
|
cy.get(publish.chartLab)
|
||||||
|
.eq(k)
|
||||||
|
.should("have.text", labels[k]);
|
||||||
|
});
|
||||||
|
cy.get(commonlocators.TextInside).should(
|
||||||
|
"have.text",
|
||||||
|
JSON.stringify(this.data.chartInputValidate),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// put your clean up code if any
|
||||||
|
});
|
||||||
|
|
@ -6,13 +6,85 @@ describe("Text-Table Binding Functionality", function() {
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.addDsl(dsl);
|
cy.addDsl(dsl);
|
||||||
});
|
});
|
||||||
it("Text-Table Binding Functionality For Username", function() {
|
it("Text-Table Binding Functionality For Id", function() {
|
||||||
cy.openPropertyPane("tablewidget");
|
cy.openPropertyPane("tablewidget");
|
||||||
/**
|
/**
|
||||||
* @param(Index) Provide index value to select the row.
|
* @param(Index) Provide index value to select the row.
|
||||||
*/
|
*/
|
||||||
cy.isSelectRow(1);
|
cy.isSelectRow(1);
|
||||||
cy.openPropertyPane("textwidget");
|
cy.openPropertyPane("textwidget");
|
||||||
|
cy.testJsontext("text", "{{Table1.selectedRow.id}}");
|
||||||
|
/**
|
||||||
|
* @param{Row Index} Provide the row index
|
||||||
|
* @param(Column Index) Provide column index
|
||||||
|
*/
|
||||||
|
cy.readTabledata("1", "0").then(tabData => {
|
||||||
|
const tabValue = tabData;
|
||||||
|
cy.get(commonlocators.TextInside).should("have.text", tabValue);
|
||||||
|
cy.PublishtheApp();
|
||||||
|
cy.isSelectRow(1);
|
||||||
|
cy.readTabledataPublish("1", "0").then(tabDataP => {
|
||||||
|
const tabValueP = tabDataP;
|
||||||
|
cy.get(commonlocators.TextInside).should("have.text", tabValueP);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("Text-Table Binding Functionality For Email", function() {
|
||||||
|
cy.get(publish.backToEditor)
|
||||||
|
.first()
|
||||||
|
.click();
|
||||||
|
cy.isSelectRow(2);
|
||||||
|
cy.openPropertyPane("textwidget");
|
||||||
|
cy.testJsontext("text", "{{Table1.selectedRow.email}}");
|
||||||
|
/**
|
||||||
|
* @param{Row Index} Provide the row index
|
||||||
|
* @param(Column Index) Provide column index
|
||||||
|
*/
|
||||||
|
cy.readTabledata("2", "1").then(tabData => {
|
||||||
|
const tabValue = tabData;
|
||||||
|
cy.get(commonlocators.TextInside).should("have.text", tabValue);
|
||||||
|
cy.PublishtheApp();
|
||||||
|
cy.isSelectRow(2);
|
||||||
|
cy.readTabledataPublish("2", "1").then(tabDataP => {
|
||||||
|
const tabValueP = tabDataP;
|
||||||
|
cy.get(commonlocators.TextInside).should("have.text", tabValueP);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("Text-Table Binding Functionality For Total Length", function() {
|
||||||
|
cy.get(publish.backToEditor)
|
||||||
|
.first()
|
||||||
|
.click();
|
||||||
|
cy.pageNo();
|
||||||
|
cy.openPropertyPane("textwidget");
|
||||||
|
cy.testJsontext("text", "{{Table1.pageSize}}");
|
||||||
|
cy.get(commonlocators.TableRow)
|
||||||
|
.find(".tr")
|
||||||
|
.then(listing => {
|
||||||
|
const listingCount = listing.length.toString();
|
||||||
|
cy.get(commonlocators.TextInside).should("have.text", listingCount);
|
||||||
|
cy.PublishtheApp();
|
||||||
|
cy.pageNo();
|
||||||
|
cy.get(publish.tableLength)
|
||||||
|
.find(".tr")
|
||||||
|
.then(listing => {
|
||||||
|
const listingCountP = listing.length.toString();
|
||||||
|
cy.get(commonlocators.TextInside).should(
|
||||||
|
"have.text",
|
||||||
|
listingCountP,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("Text-Table Binding Functionality For Username", function() {
|
||||||
|
cy.get(publish.backToEditor)
|
||||||
|
.first()
|
||||||
|
.click();
|
||||||
|
/**
|
||||||
|
* @param(Index) Provide index value to select the row.
|
||||||
|
*/
|
||||||
|
cy.isSelectRow(1);
|
||||||
|
cy.openPropertyPane("textwidget");
|
||||||
cy.testJsontext("text", JSON.stringify(this.data.textfun));
|
cy.testJsontext("text", JSON.stringify(this.data.textfun));
|
||||||
/**
|
/**
|
||||||
* @param{Row Index} Provide the row index
|
* @param{Row Index} Provide the row index
|
||||||
|
|
|
||||||
|
|
@ -25,14 +25,14 @@ describe("Input Widget Functionality", function() {
|
||||||
cy.get(widgetsPage.innertext)
|
cy.get(widgetsPage.innertext)
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
.type(this.data.para);
|
.type(this.data.para);
|
||||||
cy.get(publish.inputWidget + " " + "input")
|
cy.get(widgetsPage.inputWidget + " " + "input")
|
||||||
.invoke("attr", "value")
|
.invoke("attr", "value")
|
||||||
.should("contain", this.data.para);
|
.should("contain", this.data.para);
|
||||||
cy.openPropertyPane("inputwidget");
|
cy.openPropertyPane("inputwidget");
|
||||||
cy.get(widgetsPage.defaultInput)
|
cy.get(widgetsPage.defaultInput)
|
||||||
.type(this.data.command)
|
.type(this.data.command)
|
||||||
.type(this.data.defaultdata);
|
.type(this.data.defaultdata);
|
||||||
cy.get(publish.inputWidget + " " + "input")
|
cy.get(widgetsPage.inputWidget + " " + "input")
|
||||||
.invoke("attr", "value")
|
.invoke("attr", "value")
|
||||||
.should("contain", this.data.defaultdata);
|
.should("contain", this.data.defaultdata);
|
||||||
cy.get(widgetsPage.placeholder)
|
cy.get(widgetsPage.placeholder)
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,16 @@ describe("Table Widget Functionality", function() {
|
||||||
cy.widgetText("Table1", widgetsPage.tableWidget, commonlocators.tableInner);
|
cy.widgetText("Table1", widgetsPage.tableWidget, commonlocators.tableInner);
|
||||||
cy.testJsontext("tabledata", JSON.stringify(this.data.TableInput));
|
cy.testJsontext("tabledata", JSON.stringify(this.data.TableInput));
|
||||||
cy.wait("@updateLayout");
|
cy.wait("@updateLayout");
|
||||||
cy.ExportVerify(commonlocators.pdfSupport, "PDF Export");
|
// cy.ExportVerify(commonlocators.pdfSupport, "PDF Export");
|
||||||
cy.ExportVerify(commonlocators.ExcelSupport, "Excel Export");
|
// cy.ExportVerify(commonlocators.ExcelSupport, "Excel Export");
|
||||||
cy.ExportVerify(commonlocators.csvSupport, "CSV Export");
|
// cy.ExportVerify(commonlocators.csvSupport, "CSV Export");
|
||||||
cy.get(widgetsPage.ColumnAction).click({ force: true });
|
cy.get(widgetsPage.ColumnAction).click({ force: true });
|
||||||
cy.readTabledata("1", "5").then(tabData => {
|
// cy.readTabledata("1", "5").then(tabData => {
|
||||||
const tabValue = tabData;
|
// const tabValue = tabData;
|
||||||
expect(tabValue).to.be.equal("Action");
|
// expect(tabValue).to.be.equal("Action");
|
||||||
cy.log("the value is" + tabValue);
|
// cy.log("the value is" + tabValue);
|
||||||
});
|
// });
|
||||||
cy.pageNo(2).should("be.visible");
|
cy.pageNo();
|
||||||
cy.openPropertyPane("tablewidget");
|
cy.openPropertyPane("tablewidget");
|
||||||
cy.get(widgetsPage.tableOnRowSelected)
|
cy.get(widgetsPage.tableOnRowSelected)
|
||||||
.get(commonlocators.dropdownSelectButton)
|
.get(commonlocators.dropdownSelectButton)
|
||||||
|
|
@ -56,31 +56,33 @@ describe("Table Widget Functionality", function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it("Table Widget Functionality To Verify The PageNo", function() {
|
it("Table Widget Functionality To Verify The PageNo", function() {
|
||||||
cy.pageNo(2).should("be.visible");
|
cy.pageNo();
|
||||||
cy.get(publish.backToEditor).click();
|
cy.get(publish.backToEditor)
|
||||||
});
|
.first()
|
||||||
it("Table Widget Functionality To Verify The Extension Support", function() {
|
.click();
|
||||||
cy.openPropertyPane("tablewidget");
|
|
||||||
cy.togglebar(commonlocators.pdfSupport);
|
|
||||||
cy.PublishtheApp();
|
|
||||||
cy.get(publish.tableWidget + " " + "button").should(
|
|
||||||
"contain",
|
|
||||||
"PDF Export",
|
|
||||||
);
|
|
||||||
cy.get(publish.backToEditor).click();
|
|
||||||
cy.openPropertyPane("tablewidget");
|
|
||||||
cy.togglebarDisable(commonlocators.pdfSupport);
|
|
||||||
cy.togglebar(commonlocators.ExcelSupport);
|
|
||||||
cy.PublishtheApp();
|
|
||||||
cy.get(publish.tableWidget + " " + "button").should(
|
|
||||||
"not.contain",
|
|
||||||
"PDF Export",
|
|
||||||
);
|
|
||||||
cy.get(publish.tableWidget + " " + "button").should(
|
|
||||||
"contain",
|
|
||||||
"Excel Export",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
// it("Table Widget Functionality To Verify The Extension Support", function() {
|
||||||
|
// cy.openPropertyPane("tablewidget");
|
||||||
|
// cy.togglebar(commonlocators.pdfSupport);
|
||||||
|
// cy.PublishtheApp();
|
||||||
|
// cy.get(publish.tableWidget + " " + "button").should(
|
||||||
|
// "contain",
|
||||||
|
// "PDF Export",
|
||||||
|
// );
|
||||||
|
// cy.get(publish.backToEditor).click();
|
||||||
|
// cy.openPropertyPane("tablewidget");
|
||||||
|
// cy.togglebarDisable(commonlocators.pdfSupport);
|
||||||
|
// cy.togglebar(commonlocators.ExcelSupport);
|
||||||
|
// cy.PublishtheApp();
|
||||||
|
// cy.get(publish.tableWidget + " " + "button").should(
|
||||||
|
// "not.contain",
|
||||||
|
// "PDF Export",
|
||||||
|
// );
|
||||||
|
// cy.get(publish.tableWidget + " " + "button").should(
|
||||||
|
// "contain",
|
||||||
|
// "Excel Export",
|
||||||
|
// );
|
||||||
|
// });
|
||||||
});
|
});
|
||||||
Cypress.on("test:after:run", attributes => {
|
Cypress.on("test:after:run", attributes => {
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
const dsl = require("../../../fixtures/commondsl.json");
|
const dsl = require("../../../fixtures/commondsl.json");
|
||||||
const pages = require("../../../locators/Pages.json");
|
const pages = require("../../../locators/Pages.json");
|
||||||
const dynamicInputLocators = require("../../../locators/DynamicInput.json");
|
const dynamicInputLocators = require("../../../locators/DynamicInput.json");
|
||||||
|
const apiwidget = require("../../../locators/apiWidgetslocator.json");
|
||||||
|
|
||||||
describe("Dynamic input autocomplete", () => {
|
describe("Dynamic input autocomplete", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
@ -52,4 +53,22 @@ describe("Dynamic input autocomplete", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it("opens current value popup", () => {
|
||||||
|
// Test on widgets pane
|
||||||
|
cy.get(pages.widgetsEditor).click();
|
||||||
|
cy.openPropertyPane("buttonwidget");
|
||||||
|
cy.get(dynamicInputLocators.input)
|
||||||
|
.first()
|
||||||
|
.focus();
|
||||||
|
cy.assertEvaluatedValuePopup("string");
|
||||||
|
|
||||||
|
// Test on api pane
|
||||||
|
cy.NavigateToAPI_Panel();
|
||||||
|
cy.get(apiwidget.createapi).click({ force: true });
|
||||||
|
cy.wait("@createNewApi");
|
||||||
|
cy.xpath(apiwidget.headerValue)
|
||||||
|
.first()
|
||||||
|
.focus();
|
||||||
|
cy.assertEvaluatedValuePopup("string");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ describe("Create and Delete App Functionality", function() {
|
||||||
it("Delete App Functionality", function() {
|
it("Delete App Functionality", function() {
|
||||||
cy.log("appname: " + localStorage.getItem("AppName"));
|
cy.log("appname: " + localStorage.getItem("AppName"));
|
||||||
const appname = localStorage.getItem("AppName");
|
const appname = localStorage.getItem("AppName");
|
||||||
cy.openPropertyPane("textwidget");
|
|
||||||
cy.DeleteApp(appname);
|
cy.DeleteApp(appname);
|
||||||
cy.wait("@deleteApplication");
|
cy.wait("@deleteApplication");
|
||||||
cy.get("@deleteApplication").should("have.property", "status", 200);
|
cy.get("@deleteApplication").should("have.property", "status", 200);
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
const commonlocators = require("../../../locators/commonlocators.json");
|
const commonlocators = require("../../../locators/commonlocators.json");
|
||||||
const viewWidgetsPage = require("../../../locators/ViewWidgets.json");
|
const viewWidgetsPage = require("../../../locators/ViewWidgets.json");
|
||||||
const dsl = require("../../../fixtures/Mapdsl.json");
|
const dsl = require("../../../fixtures/Mapdsl.json");
|
||||||
|
const publishPage = require("../../../locators/publishWidgetspage.json");
|
||||||
|
|
||||||
describe("Map Widget Functionality", function() {
|
describe("Map Widget Functionality", function() {
|
||||||
beforeEach(() => {
|
before(() => {
|
||||||
cy.addDsl(dsl);
|
cy.addDsl(dsl);
|
||||||
});
|
});
|
||||||
it("Map Widget Functionality", function() {
|
|
||||||
|
this.beforeEach(() => {
|
||||||
cy.openPropertyPane("mapwidget");
|
cy.openPropertyPane("mapwidget");
|
||||||
|
});
|
||||||
|
it("Map Widget Functionality", function() {
|
||||||
/**
|
/**
|
||||||
* @param{Text} Random Text
|
* @param{Text} Random Text
|
||||||
* @param{MapWidget}Mouseover
|
* @param{MapWidget}Mouseover
|
||||||
|
|
@ -35,18 +39,88 @@ describe("Map Widget Functionality", function() {
|
||||||
cy.get(viewWidgetsPage.zoomLevel)
|
cy.get(viewWidgetsPage.zoomLevel)
|
||||||
.eq(1)
|
.eq(1)
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
|
|
||||||
cy.get(viewWidgetsPage.mapSearch).should("be.visible");
|
|
||||||
cy.get(viewWidgetsPage.mapSearch)
|
|
||||||
.invoke("attr", "placeholder")
|
|
||||||
.should("contain", "Enter location to search");
|
|
||||||
cy.get(viewWidgetsPage.mapSearch)
|
cy.get(viewWidgetsPage.mapSearch)
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
.clear()
|
.clear()
|
||||||
.type(this.data.location2)
|
.type(this.data.location2)
|
||||||
.type("{enter}");
|
.type("{enter}");
|
||||||
});
|
});
|
||||||
afterEach(() => {
|
it("Map-Enable Location,Map search and Create Marker Property Validation", function() {
|
||||||
// put your clean up code if any
|
/**
|
||||||
|
* Enable the Search Location checkbox and Validate the same in editor mode
|
||||||
|
*/
|
||||||
|
cy.CheckWidgetProperties(commonlocators.enableSearchLocCheckbox);
|
||||||
|
cy.get(viewWidgetsPage.mapSearch).should("be.visible");
|
||||||
|
cy.get(viewWidgetsPage.mapSearch)
|
||||||
|
.invoke("attr", "placeholder")
|
||||||
|
.should("contain", "Enter location to search");
|
||||||
|
/**
|
||||||
|
* Enable the Pick Location checkbox and Validate the same in editor mode
|
||||||
|
*/
|
||||||
|
cy.CheckWidgetProperties(commonlocators.enablePickLocCheckbox);
|
||||||
|
cy.get(viewWidgetsPage.pickMyLocation).should("exist");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the Createnew Marker checkbox and Validate the same in editor mode
|
||||||
|
*/
|
||||||
|
cy.CheckWidgetProperties(commonlocators.enableCreateMarkerCheckbox);
|
||||||
|
/**
|
||||||
|
* Validation will be added when create marker fun is working fine
|
||||||
|
*/
|
||||||
|
|
||||||
|
cy.PublishtheApp();
|
||||||
|
/**
|
||||||
|
* Publish mode Validation
|
||||||
|
*/
|
||||||
|
cy.get(publishPage.mapSearch).should("be.visible");
|
||||||
|
cy.get(publishPage.mapSearch)
|
||||||
|
.invoke("attr", "placeholder")
|
||||||
|
.should("contain", "Enter location to search");
|
||||||
|
cy.get(publishPage.pickMyLocation).should("exist");
|
||||||
|
cy.get(publishPage.backToEditor).click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Map-Disable Location, Mapsearch and Create Marker Property Validation", function() {
|
||||||
|
/**
|
||||||
|
* Disable the Search Location checkbox and Validate the same in editor mode
|
||||||
|
*/
|
||||||
|
cy.UncheckWidgetProperties(commonlocators.enableSearchLocCheckbox);
|
||||||
|
cy.get(viewWidgetsPage.mapSearch).should("not.be.visible");
|
||||||
|
/**
|
||||||
|
* Disable the Pick Location checkbox and Validate the same in editor mode
|
||||||
|
*/
|
||||||
|
cy.UncheckWidgetProperties(commonlocators.enablePickLocCheckbox);
|
||||||
|
cy.get(viewWidgetsPage.pickMyLocation).should("not.exist");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable the Createnew Marker checkbox and Validate the same in editor mode
|
||||||
|
*/
|
||||||
|
cy.UncheckWidgetProperties(commonlocators.enableCreateMarkerCheckbox);
|
||||||
|
/**
|
||||||
|
* Validation will be added when create marker fun is working fine
|
||||||
|
*/
|
||||||
|
|
||||||
|
cy.PublishtheApp();
|
||||||
|
/**
|
||||||
|
* Publish mode Validation
|
||||||
|
*/
|
||||||
|
cy.get(publishPage.mapSearch).should("not.be.visible");
|
||||||
|
cy.get(publishPage.pickMyLocation).should("not.exist");
|
||||||
|
cy.get(publishPage.backToEditor).click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Map-Check Visible field Validation", function() {
|
||||||
|
//Check the disableed checkbox and Validate
|
||||||
|
cy.CheckWidgetProperties(commonlocators.visibleCheckbox);
|
||||||
|
cy.PublishtheApp();
|
||||||
|
cy.get(publishPage.mapWidget).should("be.visible");
|
||||||
|
cy.get(publishPage.backToEditor).click();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Map-Unckeck Visible field Validation", function() {
|
||||||
|
//Uncheck the disabled checkbox and validate
|
||||||
|
cy.UncheckWidgetProperties(commonlocators.visibleCheckbox);
|
||||||
|
cy.PublishtheApp();
|
||||||
|
cy.get(publishPage.mapWidget).should("not.be.visible");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
"input": ".CodeMirror textarea",
|
"input": ".CodeMirror textarea",
|
||||||
"hints": "ul.CodeMirror-hints"
|
"hints": "ul.CodeMirror-hints",
|
||||||
|
"evaluatedValue": ".t--CodeEditor-evaluatedValue"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,5 +27,6 @@
|
||||||
"createMarker": ".t--property-control-createnewmarker [type='checkbox']",
|
"createMarker": ".t--property-control-createnewmarker [type='checkbox']",
|
||||||
"zoomLevel": ".t--property-control-zoomlevel svg",
|
"zoomLevel": ".t--property-control-zoomlevel svg",
|
||||||
"defaultImage": ".t--property-control-defaultimage .CodeMirror-code",
|
"defaultImage": ".t--property-control-defaultimage .CodeMirror-code",
|
||||||
"Chartlabel": ".t--draggable-chartwidget g:nth-child(5) text"
|
"Chartlabel": ".t--draggable-chartwidget g:nth-child(5) text",
|
||||||
|
"pickMyLocation": ".t--draggable-mapwidget div[title='Pick My Location']"
|
||||||
}
|
}
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
"popover": ".bp3-popover-target >div>svg",
|
"popover": ".bp3-popover-target >div>svg",
|
||||||
"moveTo": ".single-select >div:contains('Move to')",
|
"moveTo": ".single-select >div:contains('Move to')",
|
||||||
"copyTo": ".single-select >div:contains('Copy to')",
|
"copyTo": ".single-select >div:contains('Copy to')",
|
||||||
"home": ".single-select >div:contains('Home')",
|
"home": ".single-select >div:contains('Page1')",
|
||||||
"delete": ".single-select >div:contains('Delete')",
|
"delete": ".single-select >div:contains('Delete')",
|
||||||
"path": ".t--path >div textarea",
|
"path": ".t--path >div textarea",
|
||||||
"editResourceUrl": ".t--dataSourceField input",
|
"editResourceUrl": ".t--dataSourceField input",
|
||||||
|
|
@ -33,4 +33,4 @@
|
||||||
"panigationPrevUrl": ".t--apiFormPaginationPrev div>textarea",
|
"panigationPrevUrl": ".t--apiFormPaginationPrev div>textarea",
|
||||||
"TestNextUrl": ".t--apiFormPaginationNextTest",
|
"TestNextUrl": ".t--apiFormPaginationNextTest",
|
||||||
"TestPreUrl": ".t--apiFormPaginationPrevTest"
|
"TestPreUrl": ".t--apiFormPaginationPrevTest"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
"editWidgetName": ".bp3-editable-text",
|
"editWidgetName": ".bp3-editable-text",
|
||||||
"dropDownIcon": ".t--property-control-textstyle span.bp3-icon-chevron-down",
|
"dropDownIcon": ".t--property-control-textstyle span.bp3-icon-chevron-down",
|
||||||
"onDateSelectedField": ".t--property-control-ondateselected",
|
"onDateSelectedField": ".t--property-control-ondateselected",
|
||||||
"TableRow": ".t--draggable-tablewidget .e-gridcontent.e-lib.e-droppable",
|
"TableRow": ".t--draggable-tablewidget .tbody",
|
||||||
"Disablejs": ".t--property-control-disabled",
|
"Disablejs": ".t--property-control-disabled",
|
||||||
"requiredjs": ".t--property-control-required",
|
"requiredjs": ".t--property-control-required",
|
||||||
"horizontalScroll": ".t--property-control-allowhorizontalscroll input",
|
"horizontalScroll": ".t--property-control-allowhorizontalscroll input",
|
||||||
|
|
@ -45,5 +45,8 @@
|
||||||
"disabledBtn": " button[disabled='disabled']",
|
"disabledBtn": " button[disabled='disabled']",
|
||||||
"inputField": " .bp3-input",
|
"inputField": " .bp3-input",
|
||||||
"csvSupport": ".t--property-control-csvexport input",
|
"csvSupport": ".t--property-control-csvexport input",
|
||||||
"backToEditor": ".bp3-icon.bp3-icon-chevron-left + span.bp3-button-text"
|
"backToEditor": ".t--back-to-editor",
|
||||||
}
|
"enableSearchLocCheckbox": ".t--property-control-enablesearchlocation input",
|
||||||
|
"enablePickLocCheckbox": ".t--property-control-enablepicklocation input",
|
||||||
|
"enableCreateMarkerCheckbox": ".t--property-control-createnewmarker input"
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
"textWidget": ".t--widget-textwidget",
|
"textWidget": ".t--widget-textwidget",
|
||||||
"richTextEditorWidget": ".t--widget-richtexteditorwidget",
|
"richTextEditorWidget": ".t--widget-richtexteditorwidget",
|
||||||
"datepickerWidget": ".t--widget-datepickerwidget",
|
"datepickerWidget": ".t--widget-datepickerwidget",
|
||||||
"backToEditor": ".bp3-icon.bp3-icon-chevron-left + span.bp3-button-text",
|
"backToEditor": ".t--back-to-editor",
|
||||||
"inputWidget": ".t--widget-inputwidget",
|
"inputWidget": ".t--widget-inputwidget",
|
||||||
"checkboxWidget": ".t--widget-checkboxwidget",
|
"checkboxWidget": ".t--widget-checkboxwidget",
|
||||||
"radioWidget": ".t--widget-radiogroupwidget",
|
"radioWidget": ".t--widget-radiogroupwidget",
|
||||||
|
|
@ -12,7 +12,14 @@
|
||||||
"dropdownWidget": ".t--widget-dropdownwidget",
|
"dropdownWidget": ".t--widget-dropdownwidget",
|
||||||
"tabWidget": ".t--widget-tabswidget",
|
"tabWidget": ".t--widget-tabswidget",
|
||||||
"chartWidget": ".t--widget-chartwidget",
|
"chartWidget": ".t--widget-chartwidget",
|
||||||
"horizontalTab": ".t--widget-chartwidget g[class='raphael-group-104-axis-Line-group'] rect",
|
"horizontalTab": ".t--widget-chartwidget g[class*='-scrollContainer'] rect",
|
||||||
"tableWidget": ".t--widget-tablewidget",
|
"tableWidget": ".t--widget-tablewidget",
|
||||||
"tableLength": ".t--widget-tablewidget .e-gridcontent.e-lib.e-droppable"
|
"chartCanvasVal": ".t--widget-chartwidget g[class*='-canvas'] rect ",
|
||||||
|
"mapWidget": ".t--widget-mapwidget",
|
||||||
|
"tableLength": ".t--widget-tablewidget .tbody",
|
||||||
|
"mapSearch": ".t--widget-mapwidget input",
|
||||||
|
"pickMyLocation": ".t--widget-mapwidget div[title='Pick My Location']",
|
||||||
|
"rectChart":".t--widget-chartwidget g rect",
|
||||||
|
"chartLab":".t--widget-chartwidget g:nth-child(5) text"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ const LayoutPage = require("../locators/Layout.json");
|
||||||
const formWidgetsPage = require("../locators/FormWidgets.json");
|
const formWidgetsPage = require("../locators/FormWidgets.json");
|
||||||
const ApiEditor = require("../locators/ApiEditor.json");
|
const ApiEditor = require("../locators/ApiEditor.json");
|
||||||
const apiwidget = require("../locators/apiWidgetslocator.json");
|
const apiwidget = require("../locators/apiWidgetslocator.json");
|
||||||
|
const dynamicInputLocators = require("../locators/DynamicInput.json");
|
||||||
let pageidcopy = " ";
|
let pageidcopy = " ";
|
||||||
|
|
||||||
Cypress.Commands.add("CreateApp", appname => {
|
Cypress.Commands.add("CreateApp", appname => {
|
||||||
|
|
@ -184,7 +185,8 @@ Cypress.Commands.add("enterDatasourceAndPath", (datasource, path) => {
|
||||||
cy.xpath(apiwidget.autoSuggest)
|
cy.xpath(apiwidget.autoSuggest)
|
||||||
.first()
|
.first()
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
cy.get(apiwidget.path)
|
cy.get(apiwidget.editResourceUrl)
|
||||||
|
.first()
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
.type(path, { parseSpecialCharSequences: false });
|
.type(path, { parseSpecialCharSequences: false });
|
||||||
});
|
});
|
||||||
|
|
@ -210,16 +212,17 @@ Cypress.Commands.add(
|
||||||
Cypress.Commands.add("EditSourceDetail", (baseUrl, v1method) => {
|
Cypress.Commands.add("EditSourceDetail", (baseUrl, v1method) => {
|
||||||
cy.get(apiwidget.editResourceUrl)
|
cy.get(apiwidget.editResourceUrl)
|
||||||
.first()
|
.first()
|
||||||
.clear()
|
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
.type(baseUrl);
|
.clear()
|
||||||
|
.type(`{backspace}${baseUrl}`);
|
||||||
cy.xpath(apiwidget.autoSuggest)
|
cy.xpath(apiwidget.autoSuggest)
|
||||||
.first()
|
.first()
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
cy.get(ApiEditor.ApiRunBtn).scrollIntoView();
|
cy.get(ApiEditor.ApiRunBtn).scrollIntoView();
|
||||||
cy.get(apiwidget.path)
|
cy.get(apiwidget.editResourceUrl)
|
||||||
|
.first()
|
||||||
.focus()
|
.focus()
|
||||||
.type("{selectall}{backspace}api/users/2")
|
.type(v1method)
|
||||||
.should("have.value", v1method);
|
.should("have.value", v1method);
|
||||||
cy.SaveAPI();
|
cy.SaveAPI();
|
||||||
});
|
});
|
||||||
|
|
@ -947,6 +950,10 @@ Cypress.Commands.add("openPropertyPane", widgetType => {
|
||||||
.click();
|
.click();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add("closePropertyPane", () => {
|
||||||
|
cy.get(commonlocators.editPropCrossButton).click();
|
||||||
|
});
|
||||||
|
|
||||||
Cypress.Commands.add("createApi", (url, parameters) => {
|
Cypress.Commands.add("createApi", (url, parameters) => {
|
||||||
cy.get("@createNewApi").then(response => {
|
cy.get("@createNewApi").then(response => {
|
||||||
cy.get(ApiEditor.ApiNameField).should("be.visible");
|
cy.get(ApiEditor.ApiNameField).should("be.visible");
|
||||||
|
|
@ -961,7 +968,7 @@ Cypress.Commands.add("createApi", (url, parameters) => {
|
||||||
cy.contains(url).click({
|
cy.contains(url).click({
|
||||||
force: true,
|
force: true,
|
||||||
});
|
});
|
||||||
cy.get(".CodeMirror.CodeMirror-empty textarea")
|
cy.get(apiwidget.editResourceUrl)
|
||||||
.first()
|
.first()
|
||||||
.click({ force: true })
|
.click({ force: true })
|
||||||
.type(parameters, { force: true });
|
.type(parameters, { force: true });
|
||||||
|
|
@ -972,16 +979,13 @@ Cypress.Commands.add("createApi", (url, parameters) => {
|
||||||
|
|
||||||
Cypress.Commands.add("isSelectRow", index => {
|
Cypress.Commands.add("isSelectRow", index => {
|
||||||
cy.get(
|
cy.get(
|
||||||
'.e-gridcontent.e-lib.e-droppable td[index="' +
|
'.tbody .td[data-rowindex="' + index + '"][data-colindex="' + 0 + '"]',
|
||||||
index +
|
|
||||||
'"][aria-colindex="' +
|
|
||||||
index +
|
|
||||||
'"]',
|
|
||||||
).click({ force: true });
|
).click({ force: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add("readTabledata", (rowNum, colNum) => {
|
Cypress.Commands.add("readTabledata", (rowNum, colNum) => {
|
||||||
const selector = `.t--draggable-tablewidget .e-gridcontent.e-lib.e-droppable td[index=${rowNum}][aria-colindex=${colNum}]`;
|
// const selector = `.t--draggable-tablewidget .e-gridcontent.e-lib.e-droppable td[index=${rowNum}][aria-colindex=${colNum}]`;
|
||||||
|
const selector = `.t--draggable-tablewidget .tbody .td[data-rowindex=${rowNum}][data-colindex=${colNum}] div`;
|
||||||
const tabVal = cy.get(selector).invoke("text");
|
const tabVal = cy.get(selector).invoke("text");
|
||||||
return tabVal;
|
return tabVal;
|
||||||
});
|
});
|
||||||
|
|
@ -1002,10 +1006,9 @@ Cypress.Commands.add("setDate", (date, dateFormate) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add("pageNo", index => {
|
Cypress.Commands.add("pageNo", index => {
|
||||||
cy.get(".e-pagercontainer a")
|
cy.get(".page-item")
|
||||||
.eq(index)
|
.first()
|
||||||
.click({ force: true })
|
.click({ force: true });
|
||||||
.should("be.visible");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add("pageNoValidate", index => {
|
Cypress.Commands.add("pageNoValidate", index => {
|
||||||
|
|
@ -1106,7 +1109,18 @@ Cypress.Commands.add("ExportVerify", (togglecss, name) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add("readTabledataPublish", (rowNum, colNum) => {
|
Cypress.Commands.add("readTabledataPublish", (rowNum, colNum) => {
|
||||||
const selector = `.t--widget-tablewidget .e-gridcontent.e-lib.e-droppable td[index=${rowNum}][aria-colindex=${colNum}]`;
|
// const selector = `.t--widget-tablewidget .e-gridcontent.e-lib.e-droppable td[index=${rowNum}][aria-colindex=${colNum}]`;
|
||||||
|
const selector = `.t--widget-tablewidget .tbody .td[data-rowindex=${rowNum}][data-colindex=${colNum}] div`;
|
||||||
const tabVal = cy.get(selector).invoke("text");
|
const tabVal = cy.get(selector).invoke("text");
|
||||||
return tabVal;
|
return tabVal;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add("assertEvaluatedValuePopup", expectedType => {
|
||||||
|
cy.get(dynamicInputLocators.evaluatedValue)
|
||||||
|
.should("be.visible")
|
||||||
|
.children("p")
|
||||||
|
.should("contain.text", "Expected type:")
|
||||||
|
.should("contain.text", "Current Value:")
|
||||||
|
.siblings("pre")
|
||||||
|
.should("have.text", expectedType);
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ let appId;
|
||||||
import "./commands";
|
import "./commands";
|
||||||
before(function() {
|
before(function() {
|
||||||
console.log("**** Got Cypress base URL as: ", process.env.CYPRESS_BASE_URL);
|
console.log("**** Got Cypress base URL as: ", process.env.CYPRESS_BASE_URL);
|
||||||
cy.viewport("macbook-15");
|
|
||||||
cy.startServerAndRoutes();
|
cy.startServerAndRoutes();
|
||||||
cy.LogintoApp(loginData.username, loginData.password);
|
cy.LogintoApp(loginData.username, loginData.password);
|
||||||
// cy.SearchApp(inputData.appname)
|
// cy.SearchApp(inputData.appname)
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,10 @@ server {
|
||||||
proxy_pass http://localhost:3000;
|
proxy_pass http://localhost:3000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /f {
|
||||||
|
proxy_pass https://cdn.optimizely.com/;
|
||||||
|
}
|
||||||
|
|
||||||
location /api {
|
location /api {
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
proxy_set_header X-Forwarded-Host $host;
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
|
|
@ -58,6 +62,10 @@ server {
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://localhost:3000;
|
proxy_pass http://localhost:3000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /f {
|
||||||
|
proxy_pass https://cdn.optimizely.com/;
|
||||||
|
}
|
||||||
|
|
||||||
location /api {
|
location /api {
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,10 @@ server {
|
||||||
proxy_pass http://host.docker.internal:3000;
|
proxy_pass http://host.docker.internal:3000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /f {
|
||||||
|
proxy_pass https://cdn.optimizely.com/;
|
||||||
|
}
|
||||||
|
|
||||||
location /api {
|
location /api {
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
proxy_set_header X-Forwarded-Host $host;
|
proxy_set_header X-Forwarded-Host $host;
|
||||||
|
|
@ -56,6 +60,10 @@ server {
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://host.docker.internal:3000;
|
proxy_pass http://host.docker.internal:3000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /f {
|
||||||
|
proxy_pass https://cdn.optimizely.com/;
|
||||||
|
}
|
||||||
|
|
||||||
location /api {
|
location /api {
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,12 @@
|
||||||
force = true
|
force = true
|
||||||
headers = { X-Forwarded-Host = "APP_HOST_PLACEHOLDER", X-Forwarded-Proto = "https" }
|
headers = { X-Forwarded-Host = "APP_HOST_PLACEHOLDER", X-Forwarded-Proto = "https" }
|
||||||
|
|
||||||
|
[[redirects]]
|
||||||
|
from = "/f/*"
|
||||||
|
to = "https://cdn.optimizely.com/:splat"
|
||||||
|
status = 200
|
||||||
|
force = true
|
||||||
|
|
||||||
# This must be the last redirect in the chain because it's a catch-all
|
# This must be the last redirect in the chain because it's a catch-all
|
||||||
[[redirects]]
|
[[redirects]]
|
||||||
from = "/*"
|
from = "/*"
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
"@types/react-instantsearch-dom": "^6.3.0",
|
"@types/react-instantsearch-dom": "^6.3.0",
|
||||||
"@types/react-redux": "^7.0.1",
|
"@types/react-redux": "^7.0.1",
|
||||||
"@types/react-router-dom": "^5.1.2",
|
"@types/react-router-dom": "^5.1.2",
|
||||||
|
"@types/react-table": "^7.0.13",
|
||||||
"@types/styled-components": "^4.1.8",
|
"@types/styled-components": "^4.1.8",
|
||||||
"@types/tinycolor2": "^1.4.2",
|
"@types/tinycolor2": "^1.4.2",
|
||||||
"@uppy/core": "^1.8.2",
|
"@uppy/core": "^1.8.2",
|
||||||
|
|
@ -90,6 +91,7 @@
|
||||||
"react-scripts": "^3.3.0",
|
"react-scripts": "^3.3.0",
|
||||||
"react-select": "^3.0.8",
|
"react-select": "^3.0.8",
|
||||||
"react-spring": "^8.0.27",
|
"react-spring": "^8.0.27",
|
||||||
|
"react-table": "^7.0.0",
|
||||||
"react-tabs": "^3.0.0",
|
"react-tabs": "^3.0.0",
|
||||||
"react-toastify": "^5.5.0",
|
"react-toastify": "^5.5.0",
|
||||||
"react-transition-group": "^4.3.0",
|
"react-transition-group": "^4.3.0",
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,19 @@
|
||||||
import { RestAction, PaginationField, ActionResponse } from "api/ActionAPI";
|
import { PaginationField, ActionResponse } from "api/ActionAPI";
|
||||||
import {
|
import {
|
||||||
ReduxActionTypes,
|
ReduxActionTypes,
|
||||||
ReduxAction,
|
ReduxAction,
|
||||||
ReduxActionErrorTypes,
|
ReduxActionErrorTypes,
|
||||||
} from "constants/ReduxActionConstants";
|
} from "constants/ReduxActionConstants";
|
||||||
|
import { Action, RestAction } from "entities/Action";
|
||||||
|
|
||||||
export const createActionRequest = (payload: Partial<RestAction>) => {
|
export const createActionRequest = (payload: Partial<Action>) => {
|
||||||
return {
|
return {
|
||||||
type: ReduxActionTypes.CREATE_ACTION_INIT,
|
type: ReduxActionTypes.CREATE_ACTION_INIT,
|
||||||
payload,
|
payload,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createActionSuccess = (payload: RestAction) => {
|
export const createActionSuccess = (payload: Action) => {
|
||||||
return {
|
return {
|
||||||
type: ReduxActionTypes.CREATE_ACTION_SUCCESS,
|
type: ReduxActionTypes.CREATE_ACTION_SUCCESS,
|
||||||
payload,
|
payload,
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,16 @@ export const createNewApiAction = (
|
||||||
payload: { pageId },
|
payload: { pageId },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setDatasourceFieldText = (
|
||||||
|
apiId: string,
|
||||||
|
value: string,
|
||||||
|
): ReduxAction<{ apiId: string; value: string }> => {
|
||||||
|
return {
|
||||||
|
type: ReduxActionTypes.SET_DATASOURCE_FIELD_TEXT,
|
||||||
|
payload: { apiId, value },
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const setExtraFormData = (
|
export const setExtraFormData = (
|
||||||
apiId: string,
|
apiId: string,
|
||||||
extraformData: {},
|
extraformData: {},
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,12 @@ export const initDatasourcePane = (
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const storeAsDatasource = () => {
|
||||||
|
return {
|
||||||
|
type: ReduxActionTypes.STORE_AS_DATASOURCE_INIT,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
createDatasource,
|
createDatasource,
|
||||||
fetchDatasources,
|
fetchDatasources,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { ReduxActionTypes, ReduxAction } from "constants/ReduxActionConstants";
|
import { ReduxActionTypes, ReduxAction } from "constants/ReduxActionConstants";
|
||||||
import { RestAction } from "api/ActionAPI";
|
import { RestAction } from "entities/Action";
|
||||||
|
|
||||||
export const createQueryRequest = (payload: Partial<RestAction>) => {
|
export const createQueryRequest = (payload: Partial<RestAction>) => {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
import API, { HttpMethod } from "./Api";
|
import API from "./Api";
|
||||||
import { ApiResponse, GenericApiResponse, ResponseMeta } from "./ApiResponses";
|
import { ApiResponse, GenericApiResponse, ResponseMeta } from "./ApiResponses";
|
||||||
import {
|
import {
|
||||||
APIRequest,
|
APIRequest,
|
||||||
DEFAULT_EXECUTE_ACTION_TIMEOUT_MS,
|
DEFAULT_EXECUTE_ACTION_TIMEOUT_MS,
|
||||||
} from "constants/ApiConstants";
|
} from "constants/ApiConstants";
|
||||||
import { AxiosPromise } from "axios";
|
import { AxiosPromise } from "axios";
|
||||||
import { Datasource } from "./DatasourcesApi";
|
import { RestAction } from "entities/Action";
|
||||||
import { PaginationType } from "pages/Editor/APIEditor/Pagination";
|
|
||||||
|
|
||||||
export interface CreateActionRequest<T> extends APIRequest {
|
export interface CreateActionRequest<T> extends APIRequest {
|
||||||
datasourceId: string;
|
datasourceId: string;
|
||||||
|
|
@ -19,21 +18,9 @@ export interface UpdateActionRequest<T> extends CreateActionRequest<T> {
|
||||||
actionId: string;
|
actionId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface APIConfig {
|
|
||||||
datasourceId: string;
|
|
||||||
pageId: string;
|
|
||||||
name: string;
|
|
||||||
requestHeaders: Record<string, string>;
|
|
||||||
httpMethod: HttpMethod;
|
|
||||||
path: string;
|
|
||||||
body: JSON;
|
|
||||||
queryParams: Record<string, string>;
|
|
||||||
actionId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Property {
|
export interface Property {
|
||||||
key: string;
|
key: string;
|
||||||
value: string;
|
value?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BodyFormData {
|
export interface BodyFormData {
|
||||||
|
|
@ -45,17 +32,6 @@ export interface BodyFormData {
|
||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface APIConfigRequest {
|
|
||||||
headers: Property[];
|
|
||||||
httpMethod: string;
|
|
||||||
path: string;
|
|
||||||
body: JSON | string | Record<string, any> | null;
|
|
||||||
queryParameters: Property[];
|
|
||||||
paginationType: PaginationType;
|
|
||||||
bodyFormData: BodyFormData[];
|
|
||||||
timeoutInMillisecond: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface QueryConfig {
|
export interface QueryConfig {
|
||||||
queryString: string;
|
queryString: string;
|
||||||
}
|
}
|
||||||
|
|
@ -65,45 +41,6 @@ export interface ActionCreateUpdateResponse extends ApiResponse {
|
||||||
jsonPathKeys: Record<string, string>;
|
jsonPathKeys: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RestAction {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
datasource:
|
|
||||||
| Pick<Datasource, "id">
|
|
||||||
| Omit<Datasource, "id">
|
|
||||||
| Partial<Datasource>;
|
|
||||||
pluginType?: string;
|
|
||||||
pageId: string;
|
|
||||||
actionConfiguration: Partial<APIConfigRequest>;
|
|
||||||
jsonPathKeys: string[];
|
|
||||||
cacheResponse?: string;
|
|
||||||
pluginId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RapidApiAction {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
datasource: Pick<Datasource, "id"> | Omit<Datasource, "id">;
|
|
||||||
pluginType: string;
|
|
||||||
pageId: string;
|
|
||||||
actionConfiguration: Partial<APIConfigRequest>;
|
|
||||||
jsonPathKeys: string[];
|
|
||||||
cacheResponse?: string;
|
|
||||||
templateId: string;
|
|
||||||
proverId: string;
|
|
||||||
provider: ProviderInfo;
|
|
||||||
pluginId: string;
|
|
||||||
documentation: { text: string };
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ProviderInfo {
|
|
||||||
name: string;
|
|
||||||
imageUrl: string;
|
|
||||||
url: string;
|
|
||||||
description: string;
|
|
||||||
credentialSteps: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PaginationField = "PREV" | "NEXT";
|
export type PaginationField = "PREV" | "NEXT";
|
||||||
|
|
||||||
export interface ExecuteActionRequest extends APIRequest {
|
export interface ExecuteActionRequest extends APIRequest {
|
||||||
|
|
|
||||||
|
|
@ -63,12 +63,15 @@ export interface CreatePageResponse extends ApiResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FetchPageListResponse extends ApiResponse {
|
export interface FetchPageListResponse extends ApiResponse {
|
||||||
data: Array<{
|
data: {
|
||||||
id: string;
|
pages: Array<{
|
||||||
name: string;
|
id: string;
|
||||||
isDefault: boolean;
|
name: string;
|
||||||
layouts: Array<PageLayout>;
|
isDefault: boolean;
|
||||||
}>;
|
layouts: Array<PageLayout>;
|
||||||
|
}>;
|
||||||
|
organizationId: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeletePageRequest {
|
export interface DeletePageRequest {
|
||||||
|
|
|
||||||
3
app/client/src/assets/icons/menu/storage.svg
Normal file
3
app/client/src/assets/icons/menu/storage.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="20" height="16" viewBox="0 0 20 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M0 16H20V12H0V16ZM2 13H4V15H2V13ZM0 0V4H20V0H0ZM4 3H2V1H4V3ZM0 10H20V6H0V10ZM2 7H4V9H2V7Z" fill="#F4F4F4"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 220 B |
|
|
@ -1,7 +1,9 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import Creatable from "react-select/creatable";
|
import Select, { InputActionMeta } from "react-select";
|
||||||
import { WrappedFieldInputProps, WrappedFieldMetaProps } from "redux-form";
|
import { WrappedFieldInputProps, WrappedFieldMetaProps } from "redux-form";
|
||||||
|
|
||||||
import { theme } from "constants/DefaultTheme";
|
import { theme } from "constants/DefaultTheme";
|
||||||
|
import { SelectComponents } from "react-select/src/components";
|
||||||
|
|
||||||
type DropdownProps = {
|
type DropdownProps = {
|
||||||
options: Array<{
|
options: Array<{
|
||||||
|
|
@ -12,9 +14,12 @@ type DropdownProps = {
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
input: WrappedFieldInputProps;
|
input: WrappedFieldInputProps;
|
||||||
meta: WrappedFieldMetaProps;
|
meta: WrappedFieldMetaProps;
|
||||||
|
components: SelectComponents<any>;
|
||||||
onCreateOption: (inputValue: string) => void;
|
onCreateOption: (inputValue: string) => void;
|
||||||
formatCreateLabel?: (value: string) => React.ReactNode;
|
formatCreateLabel?: (value: string) => React.ReactNode;
|
||||||
noOptionsMessage?: (obj: { inputValue: string }) => string;
|
noOptionsMessage?: (obj: { inputValue: string }) => string;
|
||||||
|
inputValue?: string;
|
||||||
|
onInputChange: (value: string, actionMeta: InputActionMeta) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectStyles = {
|
const selectStyles = {
|
||||||
|
|
@ -22,7 +27,7 @@ const selectStyles = {
|
||||||
...provided,
|
...provided,
|
||||||
color: "#a3b3bf",
|
color: "#a3b3bf",
|
||||||
}),
|
}),
|
||||||
singleValue: (provided: any) => ({
|
multiValue: (provided: any) => ({
|
||||||
...provided,
|
...provided,
|
||||||
backgroundColor: "rgba(104,113,239,0.1)",
|
backgroundColor: "rgba(104,113,239,0.1)",
|
||||||
border: "1px solid rgba(104, 113, 239, 0.5)",
|
border: "1px solid rgba(104, 113, 239, 0.5)",
|
||||||
|
|
@ -34,9 +39,15 @@ const selectStyles = {
|
||||||
display: "inline-block",
|
display: "inline-block",
|
||||||
transform: "none",
|
transform: "none",
|
||||||
}),
|
}),
|
||||||
|
multiValueRemove: () => {
|
||||||
|
return {
|
||||||
|
display: "none",
|
||||||
|
};
|
||||||
|
},
|
||||||
container: (styles: any) => ({
|
container: (styles: any) => ({
|
||||||
...styles,
|
...styles,
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
zIndex: "5",
|
||||||
}),
|
}),
|
||||||
control: (styles: any, state: any) => ({
|
control: (styles: any, state: any) => ({
|
||||||
...styles,
|
...styles,
|
||||||
|
|
@ -69,23 +80,34 @@ class CreatableDropdown extends React.Component<DropdownProps> {
|
||||||
placeholder,
|
placeholder,
|
||||||
options,
|
options,
|
||||||
isLoading,
|
isLoading,
|
||||||
onCreateOption,
|
|
||||||
input,
|
input,
|
||||||
formatCreateLabel,
|
|
||||||
noOptionsMessage,
|
noOptionsMessage,
|
||||||
|
components,
|
||||||
|
inputValue,
|
||||||
|
onInputChange,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const optionalProps: Partial<DropdownProps> = {};
|
const optionalProps: Partial<DropdownProps> = {};
|
||||||
if (formatCreateLabel) optionalProps.formatCreateLabel = formatCreateLabel;
|
|
||||||
if (noOptionsMessage) optionalProps.noOptionsMessage = noOptionsMessage;
|
if (noOptionsMessage) optionalProps.noOptionsMessage = noOptionsMessage;
|
||||||
|
if (components) optionalProps.components = components;
|
||||||
|
if (inputValue) optionalProps.inputValue = inputValue;
|
||||||
|
if (onInputChange) optionalProps.onInputChange = onInputChange;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Creatable
|
<Select
|
||||||
|
isMulti
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
options={options}
|
options={options}
|
||||||
styles={selectStyles}
|
styles={selectStyles}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onCreateOption={onCreateOption}
|
|
||||||
{...input}
|
{...input}
|
||||||
onChange={value => input.onChange(value)}
|
onChange={value => {
|
||||||
|
const formattedValue = value;
|
||||||
|
if (formattedValue && formattedValue.length > 1) {
|
||||||
|
formattedValue.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
input.onChange(formattedValue);
|
||||||
|
}}
|
||||||
onBlur={() => input.value}
|
onBlur={() => input.value}
|
||||||
isClearable
|
isClearable
|
||||||
{...optionalProps}
|
{...optionalProps}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,483 @@
|
||||||
|
import React from "react";
|
||||||
|
import { ColumnAction } from "components/propertyControls/ColumnActionSelectorControl";
|
||||||
|
import Table from "./Table";
|
||||||
|
import { RenderMode, RenderModes } from "constants/WidgetConstants";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { getMenuOptions, renderActions, renderCell } from "./TableUtilities";
|
||||||
|
|
||||||
|
interface ReactTableComponentState {
|
||||||
|
trigger: number;
|
||||||
|
columnIndex: number;
|
||||||
|
pageSize: number;
|
||||||
|
action: string;
|
||||||
|
columnName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReactTableColumnProps {
|
||||||
|
Header: string;
|
||||||
|
accessor: string;
|
||||||
|
width: number;
|
||||||
|
minWidth: number;
|
||||||
|
draggable: boolean;
|
||||||
|
isHidden?: boolean;
|
||||||
|
Cell: (props: any) => JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ColumnMenuOptionProps {
|
||||||
|
content: string | JSX.Element;
|
||||||
|
closeOnClick?: boolean;
|
||||||
|
isSelected?: boolean;
|
||||||
|
columnAccessor?: string;
|
||||||
|
id?: string;
|
||||||
|
category?: boolean;
|
||||||
|
options?: ColumnMenuSubOptionProps[];
|
||||||
|
onClick?: (isSelected: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ColumnMenuSubOptionProps {
|
||||||
|
content: string;
|
||||||
|
isSelected: boolean;
|
||||||
|
closeOnClick: boolean;
|
||||||
|
onClick: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReactTableComponentProps {
|
||||||
|
widgetId: string;
|
||||||
|
isDisabled?: boolean;
|
||||||
|
isVisible?: boolean;
|
||||||
|
renderMode: RenderMode;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
pageSize: number;
|
||||||
|
tableData: object[];
|
||||||
|
columnOrder?: string[];
|
||||||
|
disableDrag: (disable: boolean) => void;
|
||||||
|
onRowClick: (rowData: object, rowIndex: number) => void;
|
||||||
|
onCommandClick: (dynamicTrigger: string) => void;
|
||||||
|
updatePageNo: Function;
|
||||||
|
updateHiddenColumns: Function;
|
||||||
|
resetSelectedRowIndex: Function;
|
||||||
|
nextPageClick: Function;
|
||||||
|
prevPageClick: Function;
|
||||||
|
pageNo: number;
|
||||||
|
serverSidePaginationEnabled: boolean;
|
||||||
|
columnActions?: ColumnAction[];
|
||||||
|
selectedRowIndex: number;
|
||||||
|
hiddenColumns?: string[];
|
||||||
|
columnNameMap?: { [key: string]: string };
|
||||||
|
columnTypeMap?: {
|
||||||
|
[key: string]: {
|
||||||
|
type: string;
|
||||||
|
format: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
columnSizeMap?: { [key: string]: number };
|
||||||
|
updateColumnType: Function;
|
||||||
|
updateColumnName: Function;
|
||||||
|
handleResizeColumn: Function;
|
||||||
|
handleReorderColumn: Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ReactTableComponent extends React.Component<
|
||||||
|
ReactTableComponentProps,
|
||||||
|
ReactTableComponentState
|
||||||
|
> {
|
||||||
|
private dragged = -1;
|
||||||
|
constructor(props: ReactTableComponentProps) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
trigger: 0,
|
||||||
|
columnIndex: -1,
|
||||||
|
action: "",
|
||||||
|
columnName: "",
|
||||||
|
pageSize: props.pageSize,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.mountEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps: ReactTableComponentProps) {
|
||||||
|
if (this.props.pageSize !== prevProps.pageSize) {
|
||||||
|
this.setState({ pageSize: this.props.pageSize });
|
||||||
|
}
|
||||||
|
this.mountEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
mountEvents() {
|
||||||
|
const headers = Array.prototype.slice.call(
|
||||||
|
document.querySelectorAll(
|
||||||
|
`#table${this.props.widgetId} .draggable-header`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
const columns = this.getTableColumns();
|
||||||
|
headers.forEach((header, i) => {
|
||||||
|
header.setAttribute("draggable", true);
|
||||||
|
|
||||||
|
header.ondragstart = (e: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
header.style =
|
||||||
|
"background: #efefef; border-radius: 4px; z-index: 100; width: 100%; text-overflow: none; overflow: none;";
|
||||||
|
e.stopPropagation();
|
||||||
|
this.dragged = i;
|
||||||
|
};
|
||||||
|
|
||||||
|
header.ondrag = (e: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
header.ondragend = (e: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
header.style = "";
|
||||||
|
e.stopPropagation();
|
||||||
|
setTimeout(() => (this.dragged = -1), 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
// the dropped header
|
||||||
|
header.ondragover = (e: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
if (i !== this.dragged && this.dragged !== -1) {
|
||||||
|
if (this.dragged > i) {
|
||||||
|
header.parentElement.className = "th header-reorder highlight-left";
|
||||||
|
} else if (this.dragged < i) {
|
||||||
|
header.parentElement.className =
|
||||||
|
"th header-reorder highlight-right";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
header.ondragenter = (e: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
if (i !== this.dragged && this.dragged !== -1) {
|
||||||
|
if (this.dragged > i) {
|
||||||
|
header.parentElement.className = "th header-reorder highlight-left";
|
||||||
|
} else if (this.dragged < i) {
|
||||||
|
header.parentElement.className =
|
||||||
|
"th header-reorder highlight-right";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
header.ondragleave = (e: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
header.parentElement.className = "th header-reorder";
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
header.ondrop = (e: React.DragEvent<HTMLDivElement>) => {
|
||||||
|
header.style = "";
|
||||||
|
header.parentElement.className = "th header-reorder";
|
||||||
|
if (i !== this.dragged && this.dragged !== -1) {
|
||||||
|
e.preventDefault();
|
||||||
|
let columnOrder = this.props.columnOrder;
|
||||||
|
if (columnOrder === undefined) {
|
||||||
|
columnOrder = this.props.tableData.length
|
||||||
|
? Object.keys(this.props.tableData[0])
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
const draggedColumn = columns[this.dragged].accessor;
|
||||||
|
columnOrder.splice(this.dragged, 1);
|
||||||
|
columnOrder.splice(i, 0, draggedColumn);
|
||||||
|
this.props.handleReorderColumn(columnOrder);
|
||||||
|
this.setState({ trigger: Math.random() });
|
||||||
|
} else {
|
||||||
|
this.dragged = -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getTableColumns = () => {
|
||||||
|
const tableData: object[] = this.props.tableData;
|
||||||
|
let columns: ReactTableColumnProps[] = [];
|
||||||
|
const hiddenColumns: ReactTableColumnProps[] = [];
|
||||||
|
if (tableData.length) {
|
||||||
|
const row = tableData[0];
|
||||||
|
for (const i in row) {
|
||||||
|
const columnName: string =
|
||||||
|
this.props.columnNameMap && this.props.columnNameMap[i]
|
||||||
|
? this.props.columnNameMap[i]
|
||||||
|
: i;
|
||||||
|
const columnType: { type: string; format?: string } =
|
||||||
|
this.props.columnTypeMap && this.props.columnTypeMap[i]
|
||||||
|
? this.props.columnTypeMap[i]
|
||||||
|
: { type: "text" };
|
||||||
|
const columnSize: number =
|
||||||
|
this.props.columnSizeMap && this.props.columnSizeMap[i]
|
||||||
|
? this.props.columnSizeMap[i]
|
||||||
|
: 150;
|
||||||
|
const isHidden =
|
||||||
|
!!this.props.hiddenColumns && this.props.hiddenColumns.includes(i);
|
||||||
|
const columnData = {
|
||||||
|
Header: columnName,
|
||||||
|
accessor: i,
|
||||||
|
width: columnSize,
|
||||||
|
minWidth: 60,
|
||||||
|
draggable: true,
|
||||||
|
isHidden: false,
|
||||||
|
Cell: (props: any) => {
|
||||||
|
return renderCell(
|
||||||
|
props.cell.value,
|
||||||
|
columnType.type,
|
||||||
|
isHidden,
|
||||||
|
columnType.format,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (isHidden) {
|
||||||
|
columnData.isHidden = true;
|
||||||
|
hiddenColumns.push(columnData);
|
||||||
|
} else {
|
||||||
|
columns.push(columnData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
columns = this.reorderColumns(columns);
|
||||||
|
if (this.props.columnActions?.length) {
|
||||||
|
columns.push({
|
||||||
|
Header: "Actions",
|
||||||
|
accessor: "actions",
|
||||||
|
width: 150,
|
||||||
|
minWidth: 60,
|
||||||
|
draggable: true,
|
||||||
|
Cell: () => {
|
||||||
|
return renderActions({
|
||||||
|
columnActions: this.props.columnActions,
|
||||||
|
onCommandClick: this.props.onCommandClick,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
hiddenColumns.length &&
|
||||||
|
this.props.renderMode === RenderModes.CANVAS
|
||||||
|
) {
|
||||||
|
columns = columns.concat(hiddenColumns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return columns;
|
||||||
|
};
|
||||||
|
|
||||||
|
reorderColumns = (columns: ReactTableColumnProps[]) => {
|
||||||
|
const columnOrder = this.props.columnOrder || [];
|
||||||
|
const reorderedColumns = [];
|
||||||
|
const reorderedFlagMap: { [key: string]: boolean } = {};
|
||||||
|
for (let index = 0; index < columns.length; index++) {
|
||||||
|
const accessor = columnOrder[index];
|
||||||
|
if (accessor) {
|
||||||
|
const column = columns.filter((col: ReactTableColumnProps) => {
|
||||||
|
return col.accessor === accessor;
|
||||||
|
});
|
||||||
|
if (column.length && !reorderedFlagMap[column[0].accessor]) {
|
||||||
|
reorderedColumns.push(column[0]);
|
||||||
|
reorderedFlagMap[column[0].accessor] = true;
|
||||||
|
} else if (!reorderedFlagMap[columns[index].accessor]) {
|
||||||
|
reorderedColumns.push(columns[index]);
|
||||||
|
reorderedFlagMap[columns[index].accessor] = true;
|
||||||
|
}
|
||||||
|
} else if (!reorderedFlagMap[columns[index].accessor]) {
|
||||||
|
reorderedColumns.push(columns[index]);
|
||||||
|
reorderedFlagMap[columns[index].accessor] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (reorderedColumns.length < columns.length) {
|
||||||
|
for (let index = 0; index < columns.length; index++) {
|
||||||
|
if (!reorderedFlagMap[columns[index].accessor]) {
|
||||||
|
reorderedColumns.push(columns[index]);
|
||||||
|
reorderedFlagMap[columns[index].accessor] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reorderedColumns;
|
||||||
|
};
|
||||||
|
|
||||||
|
showMenu = (columnIndex: number) => {
|
||||||
|
this.setState({ columnIndex: columnIndex, action: "" });
|
||||||
|
};
|
||||||
|
|
||||||
|
getColumnMenu = () => {
|
||||||
|
const { columnIndex } = this.state;
|
||||||
|
let columnType = "";
|
||||||
|
let columnId = "";
|
||||||
|
let format = "";
|
||||||
|
if (columnIndex !== -1) {
|
||||||
|
const columns = this.getTableColumns();
|
||||||
|
const column = columns[columnIndex];
|
||||||
|
columnId = column.accessor;
|
||||||
|
columnType =
|
||||||
|
this.props.columnTypeMap && this.props.columnTypeMap[columnId]
|
||||||
|
? this.props.columnTypeMap[columnId].type
|
||||||
|
: "";
|
||||||
|
format =
|
||||||
|
this.props.columnTypeMap && this.props.columnTypeMap[columnId]
|
||||||
|
? this.props.columnTypeMap[columnId].format
|
||||||
|
: "";
|
||||||
|
}
|
||||||
|
const isColumnHidden = !!(
|
||||||
|
this.props.hiddenColumns && this.props.hiddenColumns.includes(columnId)
|
||||||
|
);
|
||||||
|
const columnMenuOptions: ColumnMenuOptionProps[] = getMenuOptions({
|
||||||
|
columnAccessor: columnId,
|
||||||
|
isColumnHidden,
|
||||||
|
columnType,
|
||||||
|
format,
|
||||||
|
hideColumn: this.hideColumn,
|
||||||
|
updateColumnType: this.updateColumnType,
|
||||||
|
handleUpdateCurrencySymbol: this.handleUpdateCurrencySymbol,
|
||||||
|
handleDateFormatUpdate: this.handleDateFormatUpdate,
|
||||||
|
updateAction: (action: string) => this.setState({ action: action }),
|
||||||
|
});
|
||||||
|
return columnMenuOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
hideColumn = (isColumnHidden: boolean) => {
|
||||||
|
const columns = this.getTableColumns();
|
||||||
|
const columnIndex = this.state.columnIndex;
|
||||||
|
const column = columns[columnIndex];
|
||||||
|
let hiddenColumns = this.props.hiddenColumns || [];
|
||||||
|
if (!isColumnHidden) {
|
||||||
|
hiddenColumns.push(column.accessor);
|
||||||
|
const columnOrder = this.props.columnOrder || [];
|
||||||
|
if (columnOrder.includes(column.accessor)) {
|
||||||
|
columnOrder.splice(columnOrder.indexOf(column.accessor), 1);
|
||||||
|
this.props.handleReorderColumn(columnOrder);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hiddenColumns = hiddenColumns.filter(item => {
|
||||||
|
return item !== column.accessor;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.props.updateHiddenColumns(hiddenColumns);
|
||||||
|
this.setState({ columnIndex: -1 });
|
||||||
|
};
|
||||||
|
|
||||||
|
updateColumnType = (columnType: string) => {
|
||||||
|
const columns = this.getTableColumns();
|
||||||
|
const columnIndex = this.state.columnIndex;
|
||||||
|
const column = columns[columnIndex];
|
||||||
|
const columnTypeMap = this.props.columnTypeMap || {};
|
||||||
|
columnTypeMap[column.accessor] = {
|
||||||
|
type: columnType,
|
||||||
|
format: "",
|
||||||
|
};
|
||||||
|
this.props.updateColumnType(columnTypeMap);
|
||||||
|
this.setState({ action: "", columnIndex: -1 });
|
||||||
|
};
|
||||||
|
|
||||||
|
onColumnNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
this.setState({ columnName: event.target.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
onKeyPress = (key: string) => {
|
||||||
|
if (key === "Enter") {
|
||||||
|
this.handleColumnNameUpdate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleColumnNameUpdate = () => {
|
||||||
|
const columns = this.getTableColumns();
|
||||||
|
const columnIndex = this.state.columnIndex;
|
||||||
|
const columnName = this.state.columnName;
|
||||||
|
const column = columns[columnIndex];
|
||||||
|
const columnNameMap = this.props.columnNameMap || {};
|
||||||
|
columnNameMap[column.accessor] = columnName;
|
||||||
|
this.props.updateColumnName(columnNameMap);
|
||||||
|
this.setState({
|
||||||
|
columnIndex: -1,
|
||||||
|
columnName: "",
|
||||||
|
action: "",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handleUpdateCurrencySymbol = (currencySymbol: string) => {
|
||||||
|
const columns = this.getTableColumns();
|
||||||
|
const columnIndex = this.state.columnIndex;
|
||||||
|
const column = columns[columnIndex];
|
||||||
|
const columnTypeMap = this.props.columnTypeMap || {};
|
||||||
|
columnTypeMap[column.accessor] = {
|
||||||
|
type: "currency",
|
||||||
|
format: currencySymbol,
|
||||||
|
};
|
||||||
|
this.props.updateColumnType(columnTypeMap);
|
||||||
|
this.setState({
|
||||||
|
columnIndex: -1,
|
||||||
|
action: "",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handleDateFormatUpdate = (dateFormat: string) => {
|
||||||
|
const columns = this.getTableColumns();
|
||||||
|
const columnIndex = this.state.columnIndex;
|
||||||
|
const column = columns[columnIndex];
|
||||||
|
const columnTypeMap = this.props.columnTypeMap || {};
|
||||||
|
columnTypeMap[column.accessor] = {
|
||||||
|
type: "date",
|
||||||
|
format: dateFormat,
|
||||||
|
};
|
||||||
|
this.props.updateColumnType(columnTypeMap);
|
||||||
|
this.setState({
|
||||||
|
columnIndex: -1,
|
||||||
|
action: "",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
handleResizeColumn = (columnIndex: number, columnWidth: string) => {
|
||||||
|
const columns = this.getTableColumns();
|
||||||
|
const column = columns[columnIndex];
|
||||||
|
const columnSizeMap = this.props.columnSizeMap || {};
|
||||||
|
const width = Number(columnWidth.split("px")[0]);
|
||||||
|
columnSizeMap[column.accessor] = width;
|
||||||
|
this.props.handleResizeColumn(columnSizeMap);
|
||||||
|
};
|
||||||
|
|
||||||
|
selectTableRow = (
|
||||||
|
row: { original: object; index: number },
|
||||||
|
isSelected: boolean,
|
||||||
|
) => {
|
||||||
|
if (!isSelected) {
|
||||||
|
this.props.onRowClick(row.original, row.index);
|
||||||
|
} else {
|
||||||
|
this.props.resetSelectedRowIndex();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const columns = this.getTableColumns();
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
width={this.props.width}
|
||||||
|
height={this.props.height}
|
||||||
|
pageSize={this.state.pageSize || 1}
|
||||||
|
widgetId={this.props.widgetId}
|
||||||
|
columns={columns}
|
||||||
|
data={this.props.tableData}
|
||||||
|
showMenu={this.showMenu}
|
||||||
|
displayColumnActions={this.props.renderMode === RenderModes.CANVAS}
|
||||||
|
columnNameMap={this.props.columnNameMap}
|
||||||
|
columnMenuOptions={this.getColumnMenu()}
|
||||||
|
columnIndex={this.state.columnIndex}
|
||||||
|
columnAction={this.state.action}
|
||||||
|
onColumnNameChange={this.onColumnNameChange}
|
||||||
|
handleColumnNameUpdate={this.handleColumnNameUpdate}
|
||||||
|
handleResizeColumn={_.debounce(this.handleResizeColumn, 300)}
|
||||||
|
selectTableRow={this.selectTableRow}
|
||||||
|
pageNo={this.props.pageNo - 1}
|
||||||
|
updatePageNo={this.props.updatePageNo}
|
||||||
|
nextPageClick={() => {
|
||||||
|
this.props.nextPageClick();
|
||||||
|
}}
|
||||||
|
prevPageClick={() => {
|
||||||
|
this.props.prevPageClick();
|
||||||
|
}}
|
||||||
|
onKeyPress={this.onKeyPress}
|
||||||
|
serverSidePaginationEnabled={this.props.serverSidePaginationEnabled}
|
||||||
|
selectedRowIndex={this.props.selectedRowIndex}
|
||||||
|
disableDrag={() => {
|
||||||
|
this.props.disableDrag(true);
|
||||||
|
}}
|
||||||
|
enableDrag={() => {
|
||||||
|
this.props.disableDrag(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReactTableComponent;
|
||||||
274
app/client/src/components/designSystems/appsmith/Table.tsx
Normal file
274
app/client/src/components/designSystems/appsmith/Table.tsx
Normal file
|
|
@ -0,0 +1,274 @@
|
||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
useTable,
|
||||||
|
usePagination,
|
||||||
|
useFlexLayout,
|
||||||
|
useResizeColumns,
|
||||||
|
useRowSelect,
|
||||||
|
} from "react-table";
|
||||||
|
import { Icon, InputGroup } from "@blueprintjs/core";
|
||||||
|
import {
|
||||||
|
TableWrapper,
|
||||||
|
PaginationWrapper,
|
||||||
|
PaginationItemWrapper,
|
||||||
|
} from "./TableStyledWrappers";
|
||||||
|
import {
|
||||||
|
ReactTableColumnProps,
|
||||||
|
ColumnMenuOptionProps,
|
||||||
|
} from "./ReactTableComponent";
|
||||||
|
import { TableColumnMenuPopup } from "./TableColumnMenu";
|
||||||
|
|
||||||
|
interface TableProps {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
pageSize: number;
|
||||||
|
widgetId: string;
|
||||||
|
columns: ReactTableColumnProps[];
|
||||||
|
data: object[];
|
||||||
|
showMenu: (columnIndex: number) => void;
|
||||||
|
displayColumnActions: boolean;
|
||||||
|
columnNameMap?: { [key: string]: string };
|
||||||
|
columnMenuOptions: ColumnMenuOptionProps[];
|
||||||
|
columnIndex: number;
|
||||||
|
columnAction: string;
|
||||||
|
onColumnNameChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
handleColumnNameUpdate: () => void;
|
||||||
|
handleResizeColumn: Function;
|
||||||
|
selectTableRow: (
|
||||||
|
row: { original: object; index: number },
|
||||||
|
isSelected: boolean,
|
||||||
|
) => void;
|
||||||
|
pageNo: number;
|
||||||
|
updatePageNo: Function;
|
||||||
|
nextPageClick: () => void;
|
||||||
|
prevPageClick: () => void;
|
||||||
|
onKeyPress: (key: string) => void;
|
||||||
|
serverSidePaginationEnabled: boolean;
|
||||||
|
selectedRowIndex: number;
|
||||||
|
disableDrag: () => void;
|
||||||
|
enableDrag: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Table = (props: TableProps) => {
|
||||||
|
const { data, columns } = props;
|
||||||
|
const defaultColumn = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
minWidth: 30,
|
||||||
|
width: 150,
|
||||||
|
maxWidth: 400,
|
||||||
|
}),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const pageCount = Math.ceil(data.length / props.pageSize);
|
||||||
|
const currentPageIndex = props.pageNo < pageCount ? props.pageNo : 0;
|
||||||
|
const {
|
||||||
|
getTableProps,
|
||||||
|
getTableBodyProps,
|
||||||
|
headerGroups,
|
||||||
|
prepareRow,
|
||||||
|
page,
|
||||||
|
pageOptions,
|
||||||
|
} = useTable(
|
||||||
|
{
|
||||||
|
columns,
|
||||||
|
data,
|
||||||
|
defaultColumn,
|
||||||
|
initialState: {
|
||||||
|
pageIndex: currentPageIndex,
|
||||||
|
pageSize: props.pageSize,
|
||||||
|
},
|
||||||
|
manualPagination: true,
|
||||||
|
pageCount,
|
||||||
|
},
|
||||||
|
useFlexLayout,
|
||||||
|
useResizeColumns,
|
||||||
|
usePagination,
|
||||||
|
useRowSelect,
|
||||||
|
);
|
||||||
|
let startIndex = currentPageIndex * props.pageSize;
|
||||||
|
let endIndex = startIndex + props.pageSize;
|
||||||
|
if (props.serverSidePaginationEnabled) {
|
||||||
|
startIndex = 0;
|
||||||
|
endIndex = data.length;
|
||||||
|
}
|
||||||
|
const subPage = page.slice(startIndex, endIndex);
|
||||||
|
const selectedRowIndex = props.selectedRowIndex;
|
||||||
|
return (
|
||||||
|
<TableWrapper
|
||||||
|
width={props.width}
|
||||||
|
height={props.height}
|
||||||
|
id={`table${props.widgetId}`}
|
||||||
|
>
|
||||||
|
<div className="tableWrap">
|
||||||
|
<div {...getTableProps()} className="table">
|
||||||
|
<div onMouseOver={props.disableDrag} onMouseLeave={props.enableDrag}>
|
||||||
|
{headerGroups.map((headerGroup: any, index: number) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
{...headerGroup.getHeaderGroupProps()}
|
||||||
|
className="tr"
|
||||||
|
>
|
||||||
|
{headerGroup.headers.map((column: any, columnIndex: number) => {
|
||||||
|
if (column.isResizing) {
|
||||||
|
props.handleResizeColumn(
|
||||||
|
columnIndex,
|
||||||
|
column.getHeaderProps().style.width,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={columnIndex}
|
||||||
|
{...column.getHeaderProps()}
|
||||||
|
className="th header-reorder"
|
||||||
|
>
|
||||||
|
{props.columnIndex === columnIndex &&
|
||||||
|
props.columnAction === "rename_column" && (
|
||||||
|
<InputGroup
|
||||||
|
placeholder="Enter Column Name"
|
||||||
|
onChange={props.onColumnNameChange}
|
||||||
|
onKeyPress={event => props.onKeyPress(event.key)}
|
||||||
|
type="text"
|
||||||
|
defaultValue={
|
||||||
|
props.columnNameMap &&
|
||||||
|
props.columnNameMap[column.id]
|
||||||
|
? props.columnNameMap[column.id]
|
||||||
|
: column.id
|
||||||
|
}
|
||||||
|
className="input-group"
|
||||||
|
onBlur={() => props.handleColumnNameUpdate()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{(props.columnIndex !== columnIndex ||
|
||||||
|
(props.columnIndex === columnIndex &&
|
||||||
|
"rename_column" !== props.columnAction)) && (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
!column.isHidden
|
||||||
|
? "draggable-header"
|
||||||
|
: "hidden-header"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{column.render("Header")}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{props.displayColumnActions && (
|
||||||
|
<div className="column-menu">
|
||||||
|
<TableColumnMenuPopup
|
||||||
|
showMenu={props.showMenu}
|
||||||
|
columnMenuOptions={props.columnMenuOptions}
|
||||||
|
columnIndex={columnIndex}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
{...column.getResizerProps()}
|
||||||
|
className={`resizer ${
|
||||||
|
column.isResizing ? "isResizing" : ""
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div {...getTableBodyProps()} className="tbody">
|
||||||
|
{subPage.map((row, index) => {
|
||||||
|
prepareRow(row);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
{...row.getRowProps()}
|
||||||
|
className={
|
||||||
|
"tr" +
|
||||||
|
`${row.index === selectedRowIndex ? " selected-row" : ""}`
|
||||||
|
}
|
||||||
|
onClick={() => {
|
||||||
|
row.toggleRowSelected();
|
||||||
|
props.selectTableRow(row, row.index === selectedRowIndex);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{row.cells.map((cell, cellIndex) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={cellIndex}
|
||||||
|
{...cell.getCellProps()}
|
||||||
|
className="td"
|
||||||
|
data-rowindex={index}
|
||||||
|
data-colindex={cellIndex}
|
||||||
|
>
|
||||||
|
{cell.render("Cell")}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{props.serverSidePaginationEnabled && (
|
||||||
|
<PaginationWrapper>
|
||||||
|
<PaginationItemWrapper
|
||||||
|
disabled={false}
|
||||||
|
onClick={() => {
|
||||||
|
props.prevPageClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon="chevron-left" iconSize={16} color="#A1ACB3" />
|
||||||
|
</PaginationItemWrapper>
|
||||||
|
<PaginationItemWrapper selected className="page-item">
|
||||||
|
{props.pageNo + 1}
|
||||||
|
</PaginationItemWrapper>
|
||||||
|
<PaginationItemWrapper
|
||||||
|
disabled={false}
|
||||||
|
onClick={() => {
|
||||||
|
props.nextPageClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon="chevron-right" iconSize={16} color="#A1ACB3" />
|
||||||
|
</PaginationItemWrapper>
|
||||||
|
</PaginationWrapper>
|
||||||
|
)}
|
||||||
|
{!props.serverSidePaginationEnabled && (
|
||||||
|
<PaginationWrapper>
|
||||||
|
<PaginationItemWrapper
|
||||||
|
disabled={currentPageIndex === 0}
|
||||||
|
onClick={() => {
|
||||||
|
const pageNo = currentPageIndex > 0 ? currentPageIndex - 1 : 0;
|
||||||
|
props.updatePageNo(pageNo + 1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon="chevron-left" iconSize={16} color="#A1ACB3" />
|
||||||
|
</PaginationItemWrapper>
|
||||||
|
{pageOptions.map((pageNumber: number, index: number) => {
|
||||||
|
return (
|
||||||
|
<PaginationItemWrapper
|
||||||
|
key={index}
|
||||||
|
selected={pageNumber === currentPageIndex}
|
||||||
|
onClick={() => {
|
||||||
|
props.updatePageNo(pageNumber + 1);
|
||||||
|
}}
|
||||||
|
className="page-item"
|
||||||
|
>
|
||||||
|
{index + 1}
|
||||||
|
</PaginationItemWrapper>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<PaginationItemWrapper
|
||||||
|
disabled={currentPageIndex === pageCount - 1}
|
||||||
|
onClick={() => {
|
||||||
|
const pageNo =
|
||||||
|
currentPageIndex < pageCount - 1 ? currentPageIndex + 1 : 0;
|
||||||
|
props.updatePageNo(pageNo + 1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon icon="chevron-right" iconSize={16} color="#A1ACB3" />
|
||||||
|
</PaginationItemWrapper>
|
||||||
|
</PaginationWrapper>
|
||||||
|
)}
|
||||||
|
</TableWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Table;
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
Classes,
|
||||||
|
PopoverInteractionKind,
|
||||||
|
Icon,
|
||||||
|
Position,
|
||||||
|
} from "@blueprintjs/core";
|
||||||
|
import {
|
||||||
|
DropDownWrapper,
|
||||||
|
OptionWrapper,
|
||||||
|
IconOptionWrapper,
|
||||||
|
} from "./TableStyledWrappers";
|
||||||
|
import {
|
||||||
|
ColumnMenuSubOptionProps,
|
||||||
|
ColumnMenuOptionProps,
|
||||||
|
} from "./ReactTableComponent";
|
||||||
|
|
||||||
|
interface TableColumnMenuPopup {
|
||||||
|
showMenu: (index: number) => void;
|
||||||
|
columnIndex: number;
|
||||||
|
columnMenuOptions: ColumnMenuOptionProps[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TableColumnMenuPopup = (props: TableColumnMenuPopup) => {
|
||||||
|
return (
|
||||||
|
<Popover
|
||||||
|
minimal
|
||||||
|
usePortal
|
||||||
|
enforceFocus={false}
|
||||||
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
|
position={Position.BOTTOM}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
icon="more"
|
||||||
|
iconSize={12}
|
||||||
|
color="#A1ACB3"
|
||||||
|
onClick={e => {
|
||||||
|
props.showMenu(props.columnIndex);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<DropDownWrapper>
|
||||||
|
{props.columnMenuOptions.map(
|
||||||
|
(option: ColumnMenuOptionProps, index: number) => (
|
||||||
|
<OptionWrapper
|
||||||
|
key={index}
|
||||||
|
onClick={() => {
|
||||||
|
if (option.onClick) {
|
||||||
|
option.onClick(!!option.isSelected);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
option.closeOnClick
|
||||||
|
? Classes.POPOVER_DISMISS
|
||||||
|
: option.category
|
||||||
|
? "non-selectable"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
selected={!!option.isSelected}
|
||||||
|
>
|
||||||
|
{!option.options && <div>{option.content}</div>}
|
||||||
|
{option.options && (
|
||||||
|
<Popover
|
||||||
|
minimal
|
||||||
|
usePortal
|
||||||
|
enforceFocus={false}
|
||||||
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
|
position={Position.BOTTOM_RIGHT}
|
||||||
|
className="column-type"
|
||||||
|
>
|
||||||
|
<IconOptionWrapper>{option.content}</IconOptionWrapper>
|
||||||
|
<DropDownWrapper>
|
||||||
|
{option.options.map(
|
||||||
|
(item: ColumnMenuSubOptionProps, itemIndex: number) => (
|
||||||
|
<OptionWrapper
|
||||||
|
key={itemIndex}
|
||||||
|
onClick={item.onClick}
|
||||||
|
className={
|
||||||
|
item.closeOnClick ? Classes.POPOVER_DISMISS : ""
|
||||||
|
}
|
||||||
|
selected={!!item.isSelected}
|
||||||
|
>
|
||||||
|
{item.content}
|
||||||
|
</OptionWrapper>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</DropDownWrapper>
|
||||||
|
</Popover>
|
||||||
|
)}
|
||||||
|
</OptionWrapper>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</DropDownWrapper>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,254 @@
|
||||||
|
import styled from "styled-components";
|
||||||
|
import { Colors } from "constants/Colors";
|
||||||
|
|
||||||
|
export const TableWrapper = styled.div<{ width: number; height: number }>`
|
||||||
|
width: ${props => props.width - 5}px;
|
||||||
|
height: ${props => props.height - 5}px;
|
||||||
|
background: white;
|
||||||
|
border: 1px solid ${Colors.GEYSER_LIGHT};
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-direction: column;
|
||||||
|
.tableWrap {
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
border-bottom: 1px solid ${Colors.GEYSER_LIGHT};
|
||||||
|
}
|
||||||
|
.table {
|
||||||
|
border-spacing: 0;
|
||||||
|
color: ${Colors.BLUE_BAYOUX};
|
||||||
|
position: relative;
|
||||||
|
.thead {
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
.tbody {
|
||||||
|
overflow: scroll;
|
||||||
|
height: ${props => props.height - 5 - 102}px;
|
||||||
|
}
|
||||||
|
.tr {
|
||||||
|
:last-child {
|
||||||
|
.td {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:nth-child(even) {
|
||||||
|
background: ${Colors.ATHENS_GRAY_DARKER};
|
||||||
|
}
|
||||||
|
&.selected-row {
|
||||||
|
background: ${Colors.ATHENS_GRAY};
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
background: ${Colors.ATHENS_GRAY};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.th,
|
||||||
|
.td {
|
||||||
|
margin: 0;
|
||||||
|
padding: 9px 10px;
|
||||||
|
border-bottom: 1px solid ${Colors.GEYSER_LIGHT};
|
||||||
|
border-right: 1px solid ${Colors.GEYSER_LIGHT};
|
||||||
|
position: relative;
|
||||||
|
:last-child {
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
.resizer {
|
||||||
|
display: inline-block;
|
||||||
|
width: 10px;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
transform: translateX(50%);
|
||||||
|
z-index: 1;
|
||||||
|
${"" /* prevents from scrolling while dragging on touch devices */}
|
||||||
|
touch-action:none;
|
||||||
|
&.isResizing {
|
||||||
|
cursor: isResizing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.th {
|
||||||
|
padding: 0 10px 0 0;
|
||||||
|
height: 52px;
|
||||||
|
line-height: 52px;
|
||||||
|
background: ${Colors.ATHENS_GRAY_DARKER};
|
||||||
|
}
|
||||||
|
.td {
|
||||||
|
height: 52px;
|
||||||
|
line-height: 52px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.draggable-header,
|
||||||
|
.hidden-header {
|
||||||
|
width: 100%;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
color: ${Colors.OXFORD_BLUE};
|
||||||
|
font-weight: 500;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
.draggable-header {
|
||||||
|
cursor: pointer;
|
||||||
|
&.reorder-line {
|
||||||
|
width: 1px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.hidden-header {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
.column-menu {
|
||||||
|
cursor: pointer;
|
||||||
|
height: 52px;
|
||||||
|
line-height: 52px;
|
||||||
|
}
|
||||||
|
.th {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
&.highlight-left {
|
||||||
|
border-left: 2px solid ${Colors.GREEN};
|
||||||
|
}
|
||||||
|
&.highlight-right {
|
||||||
|
border-right: 2px solid ${Colors.GREEN};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.input-group {
|
||||||
|
height: 52px;
|
||||||
|
line-height: 52px;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const DropDownWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: white;
|
||||||
|
z-index: 1;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid ${Colors.ATHENS_GRAY};
|
||||||
|
box-shadow: 0px 2px 4px rgba(67, 70, 74, 0.14);
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const OptionWrapper = styled.div<{ selected: boolean }>`
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 32px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 8px;
|
||||||
|
color: ${props => (props.selected ? Colors.WHITE : Colors.OXFORD_BLUE)};
|
||||||
|
font-size: 14px;
|
||||||
|
min-width: 200px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 3px 0;
|
||||||
|
background: ${props => (props.selected ? Colors.GREEN : Colors.WHITE)};
|
||||||
|
&:hover {
|
||||||
|
background: ${props => (props.selected ? Colors.GREEN : Colors.POLAR)};
|
||||||
|
}
|
||||||
|
.column-type {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
&.non-selectable {
|
||||||
|
color: ${Colors.GRAY};
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const IconOptionWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const PaginationWrapper = styled.div`
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const PaginationItemWrapper = styled.div<{
|
||||||
|
disabled?: boolean;
|
||||||
|
selected?: boolean;
|
||||||
|
}>`
|
||||||
|
background: ${props => (props.disabled ? Colors.ATHENS_GRAY : Colors.WHITE)};
|
||||||
|
border: 1px solid
|
||||||
|
${props => (props.selected ? Colors.GREEN : Colors.GEYSER_LIGHT)};
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0 0 0 8px;
|
||||||
|
pointer-events: ${props => props.disabled && "none"};
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
border-color: ${Colors.GREEN};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const MenuColumnWrapper = styled.div<{ selected: boolean }>`
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
background: ${props => props.selected && Colors.GREEN};
|
||||||
|
position: relative;
|
||||||
|
.title {
|
||||||
|
color: ${props => (props.selected ? Colors.WHITE : Colors.OXFORD_BLUE)};
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.sub-menu {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const ActionWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 10px 5px 0 0;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 5px;
|
||||||
|
height: 32px;
|
||||||
|
color: ${Colors.WHITE};
|
||||||
|
background: ${Colors.GREEN};
|
||||||
|
border-radius: 4px;
|
||||||
|
letter-spacing: -0.03em;
|
||||||
|
font-weight: bold;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const CellWrapper = styled.div<{ isHidden: boolean }>`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: normal;
|
||||||
|
opacity: ${props => (props.isHidden ? "0.6" : "1")};
|
||||||
|
.image-cell {
|
||||||
|
width: 40px;
|
||||||
|
height: 32px;
|
||||||
|
margin: 0 5px 0 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
video {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
@ -0,0 +1,384 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Icon } from "@blueprintjs/core";
|
||||||
|
import moment from "moment-timezone";
|
||||||
|
import {
|
||||||
|
MenuColumnWrapper,
|
||||||
|
CellWrapper,
|
||||||
|
ActionWrapper,
|
||||||
|
} from "./TableStyledWrappers";
|
||||||
|
import { ColumnAction } from "components/propertyControls/ColumnActionSelectorControl";
|
||||||
|
import { ColumnMenuOptionProps } from "./ReactTableComponent";
|
||||||
|
|
||||||
|
interface MenuOptionProps {
|
||||||
|
columnAccessor?: string;
|
||||||
|
isColumnHidden: boolean;
|
||||||
|
columnType: string;
|
||||||
|
format?: string;
|
||||||
|
hideColumn: (isColumnHidden: boolean) => void;
|
||||||
|
updateAction: (action: string) => void;
|
||||||
|
updateColumnType: (columnType: string) => void;
|
||||||
|
handleUpdateCurrencySymbol: (currencySymbol: string) => void;
|
||||||
|
handleDateFormatUpdate: (dateFormat: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getMenuOptions = (props: MenuOptionProps) => {
|
||||||
|
const basicOptions: ColumnMenuOptionProps[] = [
|
||||||
|
{
|
||||||
|
content: "Rename a Column",
|
||||||
|
closeOnClick: true,
|
||||||
|
id: "rename_column",
|
||||||
|
onClick: () => {
|
||||||
|
props.updateAction("rename_column");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: props.isColumnHidden ? "Show Column" : "Hide Column",
|
||||||
|
closeOnClick: true,
|
||||||
|
id: "hide_column",
|
||||||
|
onClick: () => {
|
||||||
|
props.hideColumn(props.isColumnHidden);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (props.columnAccessor && props.columnAccessor === "actions") {
|
||||||
|
return basicOptions;
|
||||||
|
}
|
||||||
|
const columnMenuOptions: ColumnMenuOptionProps[] = [
|
||||||
|
...basicOptions,
|
||||||
|
{
|
||||||
|
content: "Select a Data Type",
|
||||||
|
id: "change_column_type",
|
||||||
|
category: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: (
|
||||||
|
<MenuColumnWrapper selected={props.columnType === "image"}>
|
||||||
|
<Icon
|
||||||
|
icon="media"
|
||||||
|
iconSize={12}
|
||||||
|
color={props.columnType === "image" ? "#ffffff" : "#2E3D49"}
|
||||||
|
/>
|
||||||
|
<div className="title">Image</div>
|
||||||
|
</MenuColumnWrapper>
|
||||||
|
),
|
||||||
|
closeOnClick: true,
|
||||||
|
isSelected: props.columnType === "image",
|
||||||
|
onClick: (isSelected: boolean) => {
|
||||||
|
if (isSelected) {
|
||||||
|
props.updateColumnType("");
|
||||||
|
} else {
|
||||||
|
props.updateColumnType("image");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: (
|
||||||
|
<MenuColumnWrapper selected={props.columnType === "video"}>
|
||||||
|
<Icon
|
||||||
|
icon="video"
|
||||||
|
iconSize={12}
|
||||||
|
color={props.columnType === "video" ? "#ffffff" : "#2E3D49"}
|
||||||
|
/>
|
||||||
|
<div className="title">Video</div>
|
||||||
|
</MenuColumnWrapper>
|
||||||
|
),
|
||||||
|
isSelected: props.columnType === "video",
|
||||||
|
closeOnClick: true,
|
||||||
|
onClick: (isSelected: boolean) => {
|
||||||
|
if (isSelected) {
|
||||||
|
props.updateColumnType("");
|
||||||
|
} else {
|
||||||
|
props.updateColumnType("video");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: (
|
||||||
|
<MenuColumnWrapper selected={props.columnType === "text"}>
|
||||||
|
<Icon
|
||||||
|
icon="label"
|
||||||
|
iconSize={12}
|
||||||
|
color={props.columnType === "text" ? "#ffffff" : "#2E3D49"}
|
||||||
|
/>
|
||||||
|
<div className="title">Text</div>
|
||||||
|
</MenuColumnWrapper>
|
||||||
|
),
|
||||||
|
closeOnClick: true,
|
||||||
|
isSelected: props.columnType === "text",
|
||||||
|
onClick: (isSelected: boolean) => {
|
||||||
|
if (isSelected) {
|
||||||
|
props.updateColumnType("");
|
||||||
|
} else {
|
||||||
|
props.updateColumnType("text");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: (
|
||||||
|
<MenuColumnWrapper selected={props.columnType === "currency"}>
|
||||||
|
<Icon
|
||||||
|
icon="dollar"
|
||||||
|
iconSize={12}
|
||||||
|
color={props.columnType === "currency" ? "#ffffff" : "#2E3D49"}
|
||||||
|
/>
|
||||||
|
<div className="title">Currency</div>
|
||||||
|
<Icon
|
||||||
|
className="sub-menu"
|
||||||
|
icon="chevron-right"
|
||||||
|
iconSize={16}
|
||||||
|
color={props.columnType === "currency" ? "#ffffff" : "#2E3D49"}
|
||||||
|
/>
|
||||||
|
</MenuColumnWrapper>
|
||||||
|
),
|
||||||
|
closeOnClick: false,
|
||||||
|
isSelected: props.columnType === "currency",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
content: "USD - $",
|
||||||
|
isSelected: props.format === "$",
|
||||||
|
closeOnClick: true,
|
||||||
|
onClick: () => {
|
||||||
|
props.handleUpdateCurrencySymbol("$");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "INR - ₹",
|
||||||
|
isSelected: props.format === "₹",
|
||||||
|
closeOnClick: true,
|
||||||
|
onClick: () => {
|
||||||
|
props.handleUpdateCurrencySymbol("₹");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "GBP - £",
|
||||||
|
isSelected: props.format === "£",
|
||||||
|
closeOnClick: true,
|
||||||
|
onClick: () => {
|
||||||
|
props.handleUpdateCurrencySymbol("£");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "AUD - A$",
|
||||||
|
isSelected: props.format === "A$",
|
||||||
|
closeOnClick: true,
|
||||||
|
onClick: () => {
|
||||||
|
props.handleUpdateCurrencySymbol("A$");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "EUR - €",
|
||||||
|
isSelected: props.format === "€",
|
||||||
|
closeOnClick: true,
|
||||||
|
onClick: () => {
|
||||||
|
props.handleUpdateCurrencySymbol("€");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "SGD - S$",
|
||||||
|
isSelected: props.format === "S$",
|
||||||
|
closeOnClick: true,
|
||||||
|
onClick: () => {
|
||||||
|
props.handleUpdateCurrencySymbol("S$");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "CAD - C$",
|
||||||
|
isSelected: props.format === "C$",
|
||||||
|
closeOnClick: true,
|
||||||
|
onClick: () => {
|
||||||
|
props.handleUpdateCurrencySymbol("C$");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: (
|
||||||
|
<MenuColumnWrapper selected={props.columnType === "date"}>
|
||||||
|
<Icon
|
||||||
|
icon="calendar"
|
||||||
|
iconSize={12}
|
||||||
|
color={props.columnType === "date" ? "#ffffff" : "#2E3D49"}
|
||||||
|
/>
|
||||||
|
<div className="title">Date</div>
|
||||||
|
<Icon
|
||||||
|
className="sub-menu"
|
||||||
|
icon="chevron-right"
|
||||||
|
iconSize={16}
|
||||||
|
color={props.columnType === "date" ? "#ffffff" : "#2E3D49"}
|
||||||
|
/>
|
||||||
|
</MenuColumnWrapper>
|
||||||
|
),
|
||||||
|
closeOnClick: false,
|
||||||
|
isSelected: props.columnType === "date",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
content: "MM-DD-YY",
|
||||||
|
isSelected: props.format === "MM-DD-YY",
|
||||||
|
closeOnClick: true,
|
||||||
|
onClick: () => {
|
||||||
|
props.handleDateFormatUpdate("MM-DD-YY");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "DD-MM-YY",
|
||||||
|
isSelected: props.format === "DD-MM-YY",
|
||||||
|
closeOnClick: true,
|
||||||
|
onClick: () => {
|
||||||
|
props.handleDateFormatUpdate("DD-MM-YY");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "DD/MM/YY",
|
||||||
|
isSelected: props.format === "DD/MM/YY",
|
||||||
|
closeOnClick: true,
|
||||||
|
onClick: () => {
|
||||||
|
props.handleDateFormatUpdate("DD/MM/YY");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: "MM/DD/YY",
|
||||||
|
isSelected: props.format === "MM/DD/YY",
|
||||||
|
closeOnClick: true,
|
||||||
|
onClick: () => {
|
||||||
|
props.handleDateFormatUpdate("MM/DD/YY");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
content: (
|
||||||
|
<MenuColumnWrapper selected={props.columnType === "time"}>
|
||||||
|
<Icon
|
||||||
|
icon="time"
|
||||||
|
iconSize={12}
|
||||||
|
color={props.columnType === "time" ? "#ffffff" : "#2E3D49"}
|
||||||
|
/>
|
||||||
|
<div className="title">Time</div>
|
||||||
|
</MenuColumnWrapper>
|
||||||
|
),
|
||||||
|
closeOnClick: true,
|
||||||
|
isSelected: props.columnType === "time",
|
||||||
|
onClick: (isSelected: boolean) => {
|
||||||
|
if (isSelected) {
|
||||||
|
props.updateColumnType("");
|
||||||
|
} else {
|
||||||
|
props.updateColumnType("time");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return columnMenuOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const renderCell = (
|
||||||
|
value: any,
|
||||||
|
columnType: string,
|
||||||
|
isHidden: boolean,
|
||||||
|
format?: string,
|
||||||
|
) => {
|
||||||
|
if (!value) {
|
||||||
|
return <div></div>;
|
||||||
|
}
|
||||||
|
switch (columnType) {
|
||||||
|
case "image":
|
||||||
|
return (
|
||||||
|
<CellWrapper isHidden={isHidden}>
|
||||||
|
{value
|
||||||
|
.toString()
|
||||||
|
.split(",")
|
||||||
|
.map((item: string, index: number) => {
|
||||||
|
if (item.match(/\.(jpeg|jpg|gif|png)$/)) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className="image-cell"
|
||||||
|
style={{ backgroundImage: `url("${item}")` }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <div>Invalid Image</div>;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</CellWrapper>
|
||||||
|
);
|
||||||
|
case "video":
|
||||||
|
return (
|
||||||
|
<CellWrapper isHidden={isHidden}>
|
||||||
|
<video width="56" height="32" autoPlay={false}>
|
||||||
|
<source src={`${value}`} type="video/mp4" />
|
||||||
|
</video>
|
||||||
|
</CellWrapper>
|
||||||
|
);
|
||||||
|
case "currency":
|
||||||
|
if (!isNaN(value)) {
|
||||||
|
return (
|
||||||
|
<CellWrapper isHidden={isHidden}>{`${format}${value}`}</CellWrapper>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <CellWrapper isHidden={isHidden}>Invalid Value</CellWrapper>;
|
||||||
|
}
|
||||||
|
case "date":
|
||||||
|
let isValidDate = true;
|
||||||
|
if (isNaN(value)) {
|
||||||
|
const dateTime = Date.parse(value);
|
||||||
|
if (isNaN(dateTime)) {
|
||||||
|
isValidDate = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isValidDate) {
|
||||||
|
return (
|
||||||
|
<CellWrapper isHidden={isHidden}>
|
||||||
|
{moment(value).format(format)}
|
||||||
|
</CellWrapper>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <CellWrapper isHidden={isHidden}>Invalid Date</CellWrapper>;
|
||||||
|
}
|
||||||
|
case "time":
|
||||||
|
let isValidTime = true;
|
||||||
|
if (isNaN(value)) {
|
||||||
|
const time = Date.parse(value);
|
||||||
|
if (isNaN(time)) {
|
||||||
|
isValidTime = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isValidTime) {
|
||||||
|
return (
|
||||||
|
<CellWrapper isHidden={isHidden}>
|
||||||
|
{moment(value).format("HH:mm")}
|
||||||
|
</CellWrapper>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <CellWrapper isHidden={isHidden}>Invalid Time</CellWrapper>;
|
||||||
|
}
|
||||||
|
case "text":
|
||||||
|
return <CellWrapper isHidden={isHidden}>{value}</CellWrapper>;
|
||||||
|
default:
|
||||||
|
return <CellWrapper isHidden={isHidden}>{value}</CellWrapper>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
interface RenderActionProps {
|
||||||
|
columnActions?: ColumnAction[];
|
||||||
|
onCommandClick: (dynamicTrigger: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const renderActions = (props: RenderActionProps) => {
|
||||||
|
if (!props.columnActions) return <CellWrapper isHidden={false}></CellWrapper>;
|
||||||
|
return (
|
||||||
|
<CellWrapper isHidden={false}>
|
||||||
|
{props.columnActions.map((action: ColumnAction, index: number) => {
|
||||||
|
return (
|
||||||
|
<ActionWrapper
|
||||||
|
key={index}
|
||||||
|
onClick={() => {
|
||||||
|
props.onCommandClick(action.dynamicTrigger);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{action.label}
|
||||||
|
</ActionWrapper>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</CellWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { useContext } from "react";
|
import React, { useContext } from "react";
|
||||||
import DocumentationSearch from "components/designSystems/appsmith/help/DocumentationSearch";
|
import DocumentationSearch from "components/designSystems/appsmith/help/DocumentationSearch";
|
||||||
import Button from "components/editorComponents/Button";
|
|
||||||
|
|
||||||
import { useSelector } from "store";
|
import { useSelector } from "store";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
|
|
@ -13,8 +12,7 @@ import {
|
||||||
setHelpModalVisibility,
|
setHelpModalVisibility,
|
||||||
} from "actions/helpActions";
|
} from "actions/helpActions";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { IntentColors, theme } from "constants/DefaultTheme";
|
import { theme } from "constants/DefaultTheme";
|
||||||
import { Position } from "@blueprintjs/core";
|
|
||||||
import ModalComponent from "components/designSystems/blueprint/ModalComponent";
|
import ModalComponent from "components/designSystems/blueprint/ModalComponent";
|
||||||
import { LayersContext } from "constants/Layers";
|
import { LayersContext } from "constants/Layers";
|
||||||
import { HelpIcons } from "icons/HelpIcons";
|
import { HelpIcons } from "icons/HelpIcons";
|
||||||
|
|
@ -87,7 +85,7 @@ export function HelpModal() {
|
||||||
dispatch(setHelpModalVisibility(!helpModalOpen));
|
dispatch(setHelpModalVisibility(!helpModalOpen));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<HelpIcon></HelpIcon>
|
<HelpIcon />
|
||||||
</HelpButton>
|
</HelpButton>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,11 @@ import { AppState } from "reducers";
|
||||||
import { ActionResponse } from "api/ActionAPI";
|
import { ActionResponse } from "api/ActionAPI";
|
||||||
import { formatBytes } from "utils/helpers";
|
import { formatBytes } from "utils/helpers";
|
||||||
import { APIEditorRouteParams } from "constants/routes";
|
import { APIEditorRouteParams } from "constants/routes";
|
||||||
import { ApiPaneReduxState } from "reducers/uiReducers/apiPaneReducer";
|
|
||||||
import LoadingOverlayScreen from "components/editorComponents/LoadingOverlayScreen";
|
import LoadingOverlayScreen from "components/editorComponents/LoadingOverlayScreen";
|
||||||
import CodeEditor from "components/editorComponents/CodeEditor";
|
import CodeEditor from "components/editorComponents/CodeEditor";
|
||||||
import { getActionResponses } from "selectors/entitiesSelector";
|
import { getActionResponses } from "selectors/entitiesSelector";
|
||||||
import { Colors } from "constants/Colors";
|
import { Colors } from "constants/Colors";
|
||||||
|
import _ from "lodash";
|
||||||
import FormActionButton from "./form/FormActionButton";
|
import FormActionButton from "./form/FormActionButton";
|
||||||
|
|
||||||
const ResponseWrapper = styled.div`
|
const ResponseWrapper = styled.div`
|
||||||
|
|
@ -53,7 +53,7 @@ const TableWrapper = styled.div`
|
||||||
|
|
||||||
interface ReduxStateProps {
|
interface ReduxStateProps {
|
||||||
responses: Record<string, ActionResponse | undefined>;
|
responses: Record<string, ActionResponse | undefined>;
|
||||||
apiPane: ApiPaneReduxState;
|
isRunning: Record<string, boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ResponseHeadersView = (props: { data: Record<string, string[]> }) => {
|
const ResponseHeadersView = (props: { data: Record<string, string[]> }) => {
|
||||||
|
|
@ -109,14 +109,13 @@ const ApiResponseView = (props: Props) => {
|
||||||
params: { apiId },
|
params: { apiId },
|
||||||
},
|
},
|
||||||
responses,
|
responses,
|
||||||
apiPane,
|
|
||||||
} = props;
|
} = props;
|
||||||
let response: ActionResponse = EMPTY_RESPONSE;
|
let response: ActionResponse = EMPTY_RESPONSE;
|
||||||
let isRunning = false;
|
let isRunning = false;
|
||||||
let hasFailed = false;
|
let hasFailed = false;
|
||||||
if (apiId && apiId in responses) {
|
if (apiId && apiId in responses) {
|
||||||
response = responses[apiId] || EMPTY_RESPONSE;
|
response = responses[apiId] || EMPTY_RESPONSE;
|
||||||
isRunning = apiPane.isRunning[apiId];
|
isRunning = props.isRunning[apiId];
|
||||||
hasFailed = response.statusCode ? response.statusCode[0] !== "2" : false;
|
hasFailed = response.statusCode ? response.statusCode[0] !== "2" : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,7 +134,7 @@ const ApiResponseView = (props: Props) => {
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedIndex(3);
|
setSelectedIndex(3);
|
||||||
}}
|
}}
|
||||||
></FormActionButton>
|
/>
|
||||||
)}
|
)}
|
||||||
</FailedMessageContainer>
|
</FailedMessageContainer>
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
|
|
@ -165,9 +164,9 @@ const ApiResponseView = (props: Props) => {
|
||||||
panelComponent: (
|
panelComponent: (
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
input={{
|
input={{
|
||||||
value: response.requestBody
|
value: _.isObject(response.requestBody)
|
||||||
? JSON.stringify(response.requestBody, null, 2)
|
? JSON.stringify(response.requestBody, null, 2)
|
||||||
: "",
|
: response.requestBody || "",
|
||||||
}}
|
}}
|
||||||
height={700}
|
height={700}
|
||||||
/>
|
/>
|
||||||
|
|
@ -217,7 +216,7 @@ const ApiResponseView = (props: Props) => {
|
||||||
const mapStateToProps = (state: AppState): ReduxStateProps => {
|
const mapStateToProps = (state: AppState): ReduxStateProps => {
|
||||||
return {
|
return {
|
||||||
responses: getActionResponses(state),
|
responses: getActionResponses(state),
|
||||||
apiPane: state.ui.apiPane,
|
isRunning: state.ui.apiPane.isRunning,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,11 @@ import "codemirror/addon/mode/multiplex";
|
||||||
import "codemirror/addon/tern/tern.css";
|
import "codemirror/addon/tern/tern.css";
|
||||||
import { getDataTreeForAutocomplete } from "selectors/dataTreeSelectors";
|
import { getDataTreeForAutocomplete } from "selectors/dataTreeSelectors";
|
||||||
import { AUTOCOMPLETE_MATCH_REGEX } from "constants/BindingsConstants";
|
import { AUTOCOMPLETE_MATCH_REGEX } from "constants/BindingsConstants";
|
||||||
import ErrorTooltip from "components/editorComponents/ErrorTooltip";
|
|
||||||
import HelperTooltip from "components/editorComponents/HelperTooltip";
|
import HelperTooltip from "components/editorComponents/HelperTooltip";
|
||||||
|
import EvaluatedValuePopup from "components/editorComponents/EvaluatedValuePopup";
|
||||||
import { WrappedFieldInputProps, WrappedFieldMetaProps } from "redux-form";
|
import { WrappedFieldInputProps, WrappedFieldMetaProps } from "redux-form";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { parseDynamicString } from "utils/DynamicBindingUtils";
|
import { getDynamicStringSegments } from "utils/DynamicBindingUtils";
|
||||||
import { DataTree } from "entities/DataTree/dataTreeFactory";
|
import { DataTree } from "entities/DataTree/dataTreeFactory";
|
||||||
import { Theme } from "constants/DefaultTheme";
|
import { Theme } from "constants/DefaultTheme";
|
||||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
|
|
@ -63,7 +63,7 @@ CodeMirror.defineMode("js-js", function(config) {
|
||||||
|
|
||||||
const getBorderStyle = (
|
const getBorderStyle = (
|
||||||
props: { theme: Theme } & {
|
props: { theme: Theme } & {
|
||||||
editorTheme?: THEME;
|
editorTheme?: EditorTheme;
|
||||||
hasError: boolean;
|
hasError: boolean;
|
||||||
singleLine: boolean;
|
singleLine: boolean;
|
||||||
isFocused: boolean;
|
isFocused: boolean;
|
||||||
|
|
@ -78,7 +78,7 @@ const getBorderStyle = (
|
||||||
return "transparent";
|
return "transparent";
|
||||||
};
|
};
|
||||||
|
|
||||||
const HintStyles = createGlobalStyle`
|
const HintStyles = createGlobalStyle<{ editorTheme: EditorTheme }>`
|
||||||
.CodeMirror-hints {
|
.CodeMirror-hints {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
|
|
@ -91,8 +91,11 @@ const HintStyles = createGlobalStyle`
|
||||||
max-height: 20em;
|
max-height: 20em;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background: #FFFFFF;
|
background: ${props =>
|
||||||
border: 1px solid #EBEFF2;
|
props.editorTheme === "DARK" ? "#090A0F" : "#ffffff"};
|
||||||
|
border: 1px solid;
|
||||||
|
border-color: ${props =>
|
||||||
|
props.editorTheme === "DARK" ? "#535B62" : "#EBEFF2"}
|
||||||
box-shadow: 0px 2px 4px rgba(67, 70, 74, 0.14);
|
box-shadow: 0px 2px 4px rgba(67, 70, 74, 0.14);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
@ -102,7 +105,7 @@ const HintStyles = createGlobalStyle`
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
color: #2E3D49;
|
color: ${props => (props.editorTheme === "DARK" ? "#F4F4F4" : "#1E242B")};
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -110,7 +113,10 @@ const HintStyles = createGlobalStyle`
|
||||||
}
|
}
|
||||||
|
|
||||||
li.CodeMirror-hint-active {
|
li.CodeMirror-hint-active {
|
||||||
background: #E9FAF3;
|
background: ${props =>
|
||||||
|
props.editorTheme === "DARK"
|
||||||
|
? "rgba(244,244,244,0.1)"
|
||||||
|
: "rgba(128,136,141,0.1)"};
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
.CodeMirror-Tern-completion {
|
.CodeMirror-Tern-completion {
|
||||||
|
|
@ -121,10 +127,30 @@ const HintStyles = createGlobalStyle`
|
||||||
bottom: 7px !important;
|
bottom: 7px !important;
|
||||||
line-height: 15px !important;
|
line-height: 15px !important;
|
||||||
}
|
}
|
||||||
|
.CodeMirror-Tern-tooltip {
|
||||||
|
z-index: 20 !important;
|
||||||
|
}
|
||||||
|
.CodeMirror-Tern-hint-doc {
|
||||||
|
background-color: ${props =>
|
||||||
|
props.editorTheme === "DARK" ? "#23292e" : "#fff"} !important;
|
||||||
|
color: ${props =>
|
||||||
|
props.editorTheme === "DARK" ? "#F4F4F4" : "#1E242B"} !important;
|
||||||
|
max-height: 150px;
|
||||||
|
width: 250px;
|
||||||
|
padding: 12px !important;
|
||||||
|
border: 1px solid #DEDEDE !important;
|
||||||
|
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.12) !important;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Wrapper = styled.div<{
|
const Wrapper = styled.div`
|
||||||
editorTheme?: THEME;
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const EditorWrapper = styled.div<{
|
||||||
|
editorTheme?: EditorTheme;
|
||||||
hasError: boolean;
|
hasError: boolean;
|
||||||
singleLine: boolean;
|
singleLine: boolean;
|
||||||
isFocused: boolean;
|
isFocused: boolean;
|
||||||
|
|
@ -245,12 +271,12 @@ const IconContainer = styled.div`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const THEMES = {
|
const THEMES: Record<string, EditorTheme> = {
|
||||||
LIGHT: "LIGHT",
|
LIGHT: "LIGHT",
|
||||||
DARK: "DARK",
|
DARK: "DARK",
|
||||||
};
|
};
|
||||||
|
|
||||||
type THEME = "LIGHT" | "DARK";
|
export type EditorTheme = "LIGHT" | "DARK";
|
||||||
|
|
||||||
const AUTOCOMPLETE_CLOSE_KEY_CODES = ["Enter", "Tab", "Escape"];
|
const AUTOCOMPLETE_CLOSE_KEY_CODES = ["Enter", "Tab", "Escape"];
|
||||||
|
|
||||||
|
|
@ -264,7 +290,7 @@ export type DynamicAutocompleteInputProps = {
|
||||||
rightIcon?: Function;
|
rightIcon?: Function;
|
||||||
description?: string;
|
description?: string;
|
||||||
height?: number;
|
height?: number;
|
||||||
theme?: THEME;
|
theme?: EditorTheme;
|
||||||
meta?: Partial<WrappedFieldMetaProps>;
|
meta?: Partial<WrappedFieldMetaProps>;
|
||||||
showLineNumbers?: boolean;
|
showLineNumbers?: boolean;
|
||||||
allowTabIndent?: boolean;
|
allowTabIndent?: boolean;
|
||||||
|
|
@ -276,6 +302,8 @@ export type DynamicAutocompleteInputProps = {
|
||||||
link?: string;
|
link?: string;
|
||||||
baseMode?: string | object;
|
baseMode?: string | object;
|
||||||
setMaxHeight?: boolean;
|
setMaxHeight?: boolean;
|
||||||
|
dataTreePath?: string;
|
||||||
|
expected?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = ReduxStateProps &
|
type Props = ReduxStateProps &
|
||||||
|
|
@ -316,7 +344,7 @@ class DynamicAutocompleteInput extends Component<Props, State> {
|
||||||
mode: this.props.mode || { name: "javascript", globalVars: true },
|
mode: this.props.mode || { name: "javascript", globalVars: true },
|
||||||
viewportMargin: 10,
|
viewportMargin: 10,
|
||||||
tabSize: 2,
|
tabSize: 2,
|
||||||
indentWithTabs: true,
|
indentWithTabs: !!this.props.allowTabIndent,
|
||||||
lineWrapping: !this.props.singleLine,
|
lineWrapping: !this.props.singleLine,
|
||||||
extraKeys,
|
extraKeys,
|
||||||
autoCloseBrackets: true,
|
autoCloseBrackets: true,
|
||||||
|
|
@ -324,6 +352,7 @@ class DynamicAutocompleteInput extends Component<Props, State> {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.editor.on("change", _.debounce(this.handleChange, 300));
|
this.editor.on("change", _.debounce(this.handleChange, 300));
|
||||||
|
this.editor.on("change", this.handleAutocompleteVisibility);
|
||||||
this.editor.on("keyup", this.handleAutocompleteHide);
|
this.editor.on("keyup", this.handleAutocompleteHide);
|
||||||
this.editor.on("focus", this.handleEditorFocus);
|
this.editor.on("focus", this.handleEditorFocus);
|
||||||
this.editor.on("blur", this.handleEditorBlur);
|
this.editor.on("blur", this.handleEditorBlur);
|
||||||
|
|
@ -409,7 +438,6 @@ class DynamicAutocompleteInput extends Component<Props, State> {
|
||||||
globalScope: this.props.dynamicData,
|
globalScope: this.props.dynamicData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.editor.on("cursorActivity", this.handleAutocompleteVisibility);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEditorFocus = () => {
|
handleEditorFocus = () => {
|
||||||
|
|
@ -447,20 +475,30 @@ class DynamicAutocompleteInput extends Component<Props, State> {
|
||||||
let cursorBetweenBinding = false;
|
let cursorBetweenBinding = false;
|
||||||
const cursor = this.editor.getCursor();
|
const cursor = this.editor.getCursor();
|
||||||
const value = this.editor.getValue();
|
const value = this.editor.getValue();
|
||||||
|
let cursorIndex = cursor.ch;
|
||||||
|
if (cursor.line > 0) {
|
||||||
|
for (let lineIndex = 0; lineIndex < cursor.line; lineIndex++) {
|
||||||
|
const line = this.editor.getLine(lineIndex);
|
||||||
|
// Add line length + 1 for new line character
|
||||||
|
cursorIndex = cursorIndex + line.length + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const stringSegments = getDynamicStringSegments(value);
|
||||||
|
// count of chars processed
|
||||||
let cumulativeCharCount = 0;
|
let cumulativeCharCount = 0;
|
||||||
parseDynamicString(value).forEach(segment => {
|
stringSegments.forEach((segment: string) => {
|
||||||
const start = cumulativeCharCount;
|
const start = cumulativeCharCount;
|
||||||
const dynamicStart = segment.indexOf("{{");
|
const dynamicStart = segment.indexOf("{{");
|
||||||
const dynamicDoesStart = dynamicStart > -1;
|
const dynamicDoesStart = dynamicStart > -1;
|
||||||
const dynamicEnd = segment.indexOf("}}");
|
const dynamicEnd = segment.indexOf("}}");
|
||||||
const dynamicDoesEnd = dynamicEnd > -1;
|
const dynamicDoesEnd = dynamicEnd > -1;
|
||||||
const dynamicStartIndex = dynamicStart + start + 1;
|
const dynamicStartIndex = dynamicStart + start + 2;
|
||||||
const dynamicEndIndex = dynamicEnd + start + 1;
|
const dynamicEndIndex = dynamicEnd + start;
|
||||||
if (
|
if (
|
||||||
dynamicDoesStart &&
|
dynamicDoesStart &&
|
||||||
cursor.ch > dynamicStartIndex &&
|
cursorIndex >= dynamicStartIndex &&
|
||||||
((dynamicDoesEnd && cursor.ch < dynamicEndIndex) ||
|
((dynamicDoesEnd && cursorIndex <= dynamicEndIndex) ||
|
||||||
(!dynamicDoesEnd && cursor.ch > dynamicStartIndex))
|
(!dynamicDoesEnd && cursorIndex >= dynamicStartIndex))
|
||||||
) {
|
) {
|
||||||
cursorBetweenBinding = true;
|
cursorBetweenBinding = true;
|
||||||
}
|
}
|
||||||
|
|
@ -523,63 +561,75 @@ class DynamicAutocompleteInput extends Component<Props, State> {
|
||||||
disabled,
|
disabled,
|
||||||
className,
|
className,
|
||||||
setMaxHeight,
|
setMaxHeight,
|
||||||
|
dataTreePath,
|
||||||
|
dynamicData,
|
||||||
|
expected,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const hasError = !!(meta && meta.error);
|
const hasError = !!(meta && meta.error);
|
||||||
let showError = false;
|
let evaluatedValue: any = "undefined";
|
||||||
if (this.editor) {
|
if (dataTreePath) {
|
||||||
showError =
|
evaluatedValue = _.get(dynamicData, dataTreePath);
|
||||||
hasError && this.state.isFocused && !this.state.autoCompleteVisible;
|
|
||||||
}
|
}
|
||||||
|
const showEvaluatedValue =
|
||||||
|
this.state.isFocused && "dataTreePath" in this.props;
|
||||||
return (
|
return (
|
||||||
<ErrorTooltip message={meta ? meta.error : ""} isOpen={showError}>
|
<Wrapper>
|
||||||
<Wrapper
|
<EvaluatedValuePopup
|
||||||
editorTheme={theme}
|
theme={theme || THEMES.LIGHT}
|
||||||
|
isOpen={showEvaluatedValue}
|
||||||
|
evaluatedValue={evaluatedValue}
|
||||||
|
expected={expected}
|
||||||
hasError={hasError}
|
hasError={hasError}
|
||||||
singleLine={singleLine}
|
|
||||||
isFocused={this.state.isFocused}
|
|
||||||
disabled={disabled}
|
|
||||||
className={className}
|
|
||||||
setMaxHeight={setMaxHeight}
|
|
||||||
>
|
>
|
||||||
<HintStyles />
|
<EditorWrapper
|
||||||
<IconContainer>
|
editorTheme={theme}
|
||||||
{this.props.leftIcon && <this.props.leftIcon />}
|
hasError={hasError}
|
||||||
</IconContainer>
|
singleLine={singleLine}
|
||||||
|
isFocused={this.state.isFocused}
|
||||||
|
disabled={disabled}
|
||||||
|
className={className}
|
||||||
|
setMaxHeight={setMaxHeight}
|
||||||
|
>
|
||||||
|
<HintStyles editorTheme={theme || THEMES.LIGHT} />
|
||||||
|
<IconContainer>
|
||||||
|
{this.props.leftIcon && <this.props.leftIcon />}
|
||||||
|
</IconContainer>
|
||||||
|
|
||||||
{this.props.leftImage && (
|
{this.props.leftImage && (
|
||||||
<img
|
<img
|
||||||
src={this.props.leftImage}
|
src={this.props.leftImage}
|
||||||
alt="img"
|
alt="img"
|
||||||
className="leftImageStyles"
|
className="leftImageStyles"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<textarea
|
<textarea
|
||||||
ref={this.textArea}
|
ref={this.textArea}
|
||||||
{..._.omit(this.props.input, ["onChange", "value"])}
|
{..._.omit(this.props.input, ["onChange", "value"])}
|
||||||
defaultValue={input.value}
|
defaultValue={input.value}
|
||||||
placeholder={this.props.placeholder}
|
placeholder={this.props.placeholder}
|
||||||
/>
|
|
||||||
{this.props.link && (
|
|
||||||
<React.Fragment>
|
|
||||||
<a
|
|
||||||
href={this.props.link}
|
|
||||||
target="_blank"
|
|
||||||
className="linkStyles"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
API documentation
|
|
||||||
</a>
|
|
||||||
</React.Fragment>
|
|
||||||
)}
|
|
||||||
{this.props.rightIcon && (
|
|
||||||
<HelperTooltip
|
|
||||||
description={this.props.description}
|
|
||||||
rightIcon={this.props.rightIcon}
|
|
||||||
/>
|
/>
|
||||||
)}
|
{this.props.link && (
|
||||||
</Wrapper>
|
<React.Fragment>
|
||||||
</ErrorTooltip>
|
<a
|
||||||
|
href={this.props.link}
|
||||||
|
target="_blank"
|
||||||
|
className="linkStyles"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
API documentation
|
||||||
|
</a>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
{this.props.rightIcon && (
|
||||||
|
<HelperTooltip
|
||||||
|
description={this.props.description}
|
||||||
|
rightIcon={this.props.rightIcon}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</EditorWrapper>
|
||||||
|
</EvaluatedValuePopup>
|
||||||
|
</Wrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,200 @@
|
||||||
|
import React, { useRef } from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import _ from "lodash";
|
||||||
|
import Popper from "pages/Editor/Popper";
|
||||||
|
import ReactJson from "react-json-view";
|
||||||
|
import { EditorTheme } from "components/editorComponents/DynamicAutocompleteInput";
|
||||||
|
import { theme } from "constants/DefaultTheme";
|
||||||
|
import { Placement } from "popper.js";
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
position: relative;
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type ThemeConfig = {
|
||||||
|
backgroundColor: string;
|
||||||
|
textColor: string;
|
||||||
|
editorColor: string;
|
||||||
|
editorBackground: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PopupTheme = Record<EditorTheme, ThemeConfig>;
|
||||||
|
|
||||||
|
const THEMES: PopupTheme = {
|
||||||
|
LIGHT: {
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
textColor: "#1E242B",
|
||||||
|
editorBackground: "#F4F4F4",
|
||||||
|
editorColor: "#1E242B",
|
||||||
|
},
|
||||||
|
DARK: {
|
||||||
|
backgroundColor: "#23292e",
|
||||||
|
textColor: "#F4F4F4",
|
||||||
|
editorBackground: "#090a0f",
|
||||||
|
editorColor: "#F4F4F4",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const ContentWrapper = styled.div<{ colorTheme: EditorTheme }>`
|
||||||
|
width: ${props => props.theme.evaluatedValuePopup.width}px;
|
||||||
|
max-height: ${props => props.theme.evaluatedValuePopup.height}px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: ${props => THEMES[props.colorTheme].backgroundColor};
|
||||||
|
color: ${props => THEMES[props.colorTheme].textColor};
|
||||||
|
padding: 15px;
|
||||||
|
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
|
||||||
|
border-radius: 4px;
|
||||||
|
.react-json-view {
|
||||||
|
.icon-container {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CurrentValueWrapper = styled.div`
|
||||||
|
max-height: 150px;
|
||||||
|
overflow-y: auto;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CodeWrapper = styled.pre<{ colorTheme: EditorTheme }>`
|
||||||
|
padding: 10px;
|
||||||
|
background-color: ${props => THEMES[props.colorTheme].editorBackground};
|
||||||
|
color: ${props => THEMES[props.colorTheme].editorColor};
|
||||||
|
overflow: scroll;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TypeText = styled.pre<{ colorTheme: EditorTheme }>`
|
||||||
|
padding: 5px;
|
||||||
|
background-color: ${props => THEMES[props.colorTheme].editorBackground};
|
||||||
|
color: ${props => THEMES[props.colorTheme].editorColor};
|
||||||
|
overflow: scroll;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ErrorText = styled.p`
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: rgba(235, 87, 87, 0.2);
|
||||||
|
color: ${props => props.theme.colors.error};
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
theme: EditorTheme;
|
||||||
|
isOpen: boolean;
|
||||||
|
hasError: boolean;
|
||||||
|
expected?: string;
|
||||||
|
evaluatedValue?: any;
|
||||||
|
children: JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PopoverContentProps {
|
||||||
|
hasError: boolean;
|
||||||
|
expected?: string;
|
||||||
|
evaluatedValue: any;
|
||||||
|
theme: EditorTheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CurrentValueViewer = (props: {
|
||||||
|
theme: EditorTheme;
|
||||||
|
evaluatedValue: any;
|
||||||
|
}) => {
|
||||||
|
let content = (
|
||||||
|
<CodeWrapper colorTheme={props.theme}>{"undefined"}</CodeWrapper>
|
||||||
|
);
|
||||||
|
if (props.evaluatedValue !== undefined) {
|
||||||
|
if (
|
||||||
|
_.isObject(props.evaluatedValue) ||
|
||||||
|
Array.isArray(props.evaluatedValue)
|
||||||
|
) {
|
||||||
|
const reactJsonProps = {
|
||||||
|
theme: props.theme === "DARK" ? "monokai" : "rjv-default",
|
||||||
|
name: null,
|
||||||
|
enableClipboard: false,
|
||||||
|
displayObjectSize: false,
|
||||||
|
displayDataTypes: false,
|
||||||
|
style: {
|
||||||
|
fontSize: "14px",
|
||||||
|
},
|
||||||
|
collapsed: 2,
|
||||||
|
collapseStringsAfterLength: 20,
|
||||||
|
};
|
||||||
|
content = <ReactJson src={props.evaluatedValue} {...reactJsonProps} />;
|
||||||
|
} else {
|
||||||
|
content = (
|
||||||
|
<CodeWrapper colorTheme={props.theme}>
|
||||||
|
{props.evaluatedValue.toString()}
|
||||||
|
</CodeWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<p>Current Value:</p>
|
||||||
|
<CurrentValueWrapper>{content}</CurrentValueWrapper>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const PopoverContent = (props: PopoverContentProps) => {
|
||||||
|
return (
|
||||||
|
<ContentWrapper
|
||||||
|
colorTheme={props.theme}
|
||||||
|
className="t--CodeEditor-evaluatedValue"
|
||||||
|
>
|
||||||
|
{props.hasError && (
|
||||||
|
<ErrorText>{`This value does not evaluate to type "${props.expected}"`}</ErrorText>
|
||||||
|
)}
|
||||||
|
{props.expected && (
|
||||||
|
<React.Fragment>
|
||||||
|
<p>Expected type:</p>
|
||||||
|
<TypeText colorTheme={props.theme}>{props.expected}</TypeText>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
<CurrentValueViewer
|
||||||
|
theme={props.theme}
|
||||||
|
evaluatedValue={props.evaluatedValue}
|
||||||
|
/>
|
||||||
|
</ContentWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const EvaluatedValuePopup = (props: Props) => {
|
||||||
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||||
|
let placement: Placement = "left-start";
|
||||||
|
if (wrapperRef.current) {
|
||||||
|
const boundingRect = wrapperRef.current.getBoundingClientRect();
|
||||||
|
if (boundingRect.left < theme.evaluatedValuePopup.width) {
|
||||||
|
placement = "right-start";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Wrapper ref={wrapperRef}>
|
||||||
|
{props.isOpen && (
|
||||||
|
<Popper
|
||||||
|
targetNode={wrapperRef.current || undefined}
|
||||||
|
isOpen
|
||||||
|
zIndex={15}
|
||||||
|
placement={placement}
|
||||||
|
modifiers={{
|
||||||
|
offset: {
|
||||||
|
enabled: true,
|
||||||
|
offset: "0, 15",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PopoverContent
|
||||||
|
expected={props.expected}
|
||||||
|
evaluatedValue={props.evaluatedValue}
|
||||||
|
hasError={props.hasError}
|
||||||
|
theme={props.theme}
|
||||||
|
/>
|
||||||
|
</Popper>
|
||||||
|
)}
|
||||||
|
{props.children}
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EvaluatedValuePopup;
|
||||||
|
|
@ -196,7 +196,7 @@ const views = {
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
isValid={props.isValid}
|
isValid={props.isValid}
|
||||||
validationMessage={props.validationMessage}
|
errorMessage={props.validationMessage}
|
||||||
/>
|
/>
|
||||||
</ControlWrapper>
|
</ControlWrapper>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,65 +1,266 @@
|
||||||
import React from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import CreatableDropdown from "components/designSystems/appsmith/CreatableDropdown";
|
import CreatableDropdown from "components/designSystems/appsmith/CreatableDropdown";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Field } from "redux-form";
|
import { Field, formValueSelector, change } from "redux-form";
|
||||||
import { AppState } from "reducers";
|
import { AppState } from "reducers";
|
||||||
|
import { ReactComponent as StorageIcon } from "assets/icons/menu/storage.svg";
|
||||||
import { DatasourceDataState } from "reducers/entityReducers/datasourceReducer";
|
import { DatasourceDataState } from "reducers/entityReducers/datasourceReducer";
|
||||||
import { Plugin } from "api/PluginApi";
|
import { Plugin } from "api/PluginApi";
|
||||||
import { getDatasourcePlugins } from "selectors/entitiesSelector";
|
import { getDatasourcePlugins } from "selectors/entitiesSelector";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { createDatasource } from "actions/datasourceActions";
|
import { createDatasource, storeAsDatasource } from "actions/datasourceActions";
|
||||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
|
import { Datasource, CreateDatasourceConfig } from "api/DatasourcesApi";
|
||||||
|
import styled, { createGlobalStyle } from "styled-components";
|
||||||
|
import { MenuItem, Menu, Popover, Position } from "@blueprintjs/core";
|
||||||
|
import { IconWrapper } from "constants/IconConstants";
|
||||||
|
import { theme } from "constants/DefaultTheme";
|
||||||
|
import { ControlIcons } from "icons/ControlIcons";
|
||||||
|
import { API_EDITOR_FORM_NAME } from "constants/forms";
|
||||||
|
import { InputActionMeta } from "react-select";
|
||||||
|
import { setDatasourceFieldText } from "actions/apiPaneActions";
|
||||||
|
|
||||||
interface ReduxStateProps {
|
interface ReduxStateProps {
|
||||||
datasources: DatasourceDataState;
|
datasources: DatasourceDataState;
|
||||||
validDatasourcePlugins: Plugin[];
|
validDatasourcePlugins: Plugin[];
|
||||||
|
apiId: string;
|
||||||
|
value: Datasource;
|
||||||
}
|
}
|
||||||
interface ReduxActionProps {
|
interface ReduxActionProps {
|
||||||
createDatasource: (value: string) => void;
|
createDatasource: (value: string) => void;
|
||||||
|
storeAsDatasource: () => void;
|
||||||
|
changeDatasource: (value: Datasource | CreateDatasourceConfig) => void;
|
||||||
|
changePath: (value: string) => void;
|
||||||
|
setDatasourceFieldText: (apiId: string, value: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ComponentProps {
|
interface ComponentProps {
|
||||||
name: string;
|
name: string;
|
||||||
pluginId: string;
|
pluginId: string;
|
||||||
appName: string;
|
appName: string;
|
||||||
|
datasourceFieldText: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const StyledMenuItem = styled(MenuItem)`
|
||||||
|
&&&&.bp3-menu-item {
|
||||||
|
align-items: center;
|
||||||
|
width: 202px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledMenu = styled(Menu)`
|
||||||
|
&&&&.bp3-menu {
|
||||||
|
padding: 8px;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #ebeff2;
|
||||||
|
box-sizing: border-box;
|
||||||
|
box-shadow: 0px 2px 4px rgba(67, 70, 74, 0.14);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const TooltipStyles = createGlobalStyle`
|
||||||
|
.helper-tooltip{
|
||||||
|
.bp3-popover {
|
||||||
|
margin-right: 10px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
const DatasourcesField = (
|
const DatasourcesField = (
|
||||||
props: ReduxActionProps & ReduxStateProps & ComponentProps,
|
props: ReduxActionProps & ReduxStateProps & ComponentProps,
|
||||||
) => {
|
) => {
|
||||||
const options = props.datasources.list
|
const [inputValue, setValue] = useState(props.datasourceFieldText);
|
||||||
.filter(r => r.pluginId === props.pluginId)
|
|
||||||
.filter(r =>
|
useEffect(() => {
|
||||||
props.validDatasourcePlugins.some(plugin => plugin.id === r.pluginId),
|
setValue(props.datasourceFieldText);
|
||||||
)
|
}, [props.datasourceFieldText]);
|
||||||
.filter(r => r.datasourceConfiguration)
|
|
||||||
.filter(r => r.datasourceConfiguration.url)
|
const options = React.useMemo(() => {
|
||||||
.map(r => ({
|
return props.datasources.list
|
||||||
label: r.datasourceConfiguration?.url.endsWith("/")
|
.filter(r => r.pluginId === props.pluginId)
|
||||||
? r.datasourceConfiguration?.url.slice(0, -1)
|
.filter(r => {
|
||||||
: r.datasourceConfiguration.url,
|
return props.validDatasourcePlugins.some(
|
||||||
value: r.id,
|
plugin => plugin.id === r.pluginId,
|
||||||
}));
|
);
|
||||||
|
})
|
||||||
|
.filter(r => r.datasourceConfiguration)
|
||||||
|
.filter(r => r.datasourceConfiguration.url)
|
||||||
|
.map(r => ({
|
||||||
|
label: r.datasourceConfiguration?.url,
|
||||||
|
value: r.id,
|
||||||
|
}));
|
||||||
|
}, [props.datasources.list, props.validDatasourcePlugins, props.pluginId]);
|
||||||
|
|
||||||
|
const { storeAsDatasource } = props;
|
||||||
|
let isEmbeddedDatasource = true;
|
||||||
|
|
||||||
|
if (props.value && props.value.id) {
|
||||||
|
isEmbeddedDatasource = false;
|
||||||
|
} else if (props.value && props.value.datasourceConfiguration) {
|
||||||
|
isEmbeddedDatasource = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DropdownIndicator = (props: any) => {
|
||||||
|
if (props.hasValue) return null;
|
||||||
|
|
||||||
|
const MenuContainer = (
|
||||||
|
<StyledMenu>
|
||||||
|
<StyledMenuItem
|
||||||
|
icon={
|
||||||
|
<IconWrapper
|
||||||
|
width={theme.fontSizes[4]}
|
||||||
|
height={theme.fontSizes[4]}
|
||||||
|
color={"#535B62"}
|
||||||
|
>
|
||||||
|
<StorageIcon />
|
||||||
|
</IconWrapper>
|
||||||
|
}
|
||||||
|
text="Store as datasource"
|
||||||
|
onClick={storeAsDatasource}
|
||||||
|
/>
|
||||||
|
</StyledMenu>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TooltipStyles />
|
||||||
|
<Popover
|
||||||
|
content={MenuContainer}
|
||||||
|
position={Position.BOTTOM_LEFT}
|
||||||
|
usePortal
|
||||||
|
portalClassName="helper-tooltip"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: "8px 13px 3px 13px",
|
||||||
|
}}
|
||||||
|
onMouseDown={e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ControlIcons.MORE_HORIZONTAL_CONTROL
|
||||||
|
width={theme.fontSizes[4]}
|
||||||
|
height={theme.fontSizes[4]}
|
||||||
|
color="#C4C4C4"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<Field
|
<Field
|
||||||
name={props.name}
|
name={props.name}
|
||||||
component={CreatableDropdown}
|
component={CreatableDropdown}
|
||||||
isLoading={props.datasources.loading}
|
isLoading={props.datasources.loading}
|
||||||
options={options}
|
options={options}
|
||||||
|
components={{
|
||||||
|
ClearIndicator: () => null,
|
||||||
|
IndicatorSeparator: () => null,
|
||||||
|
DropdownIndicator,
|
||||||
|
}}
|
||||||
placeholder="https://<base-url>.com"
|
placeholder="https://<base-url>.com"
|
||||||
onCreateOption={props.createDatasource}
|
onInputChange={(value: string, actionMeta: InputActionMeta) => {
|
||||||
format={(value: string) => _.find(options, { value }) || ""}
|
const { action } = actionMeta;
|
||||||
parse={(option: { value: string }) => (option ? option.value : null)}
|
if (action === "input-blur") {
|
||||||
formatCreateLabel={(value: string) => `Create data source "${value}"`}
|
props.setDatasourceFieldText(props.apiId, inputValue);
|
||||||
noOptionsMessage={() => "No data sources created"}
|
|
||||||
|
return value;
|
||||||
|
} else if (action === "set-value") {
|
||||||
|
setValue("");
|
||||||
|
|
||||||
|
return "";
|
||||||
|
} else if (action === "menu-close") {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
setValue(value);
|
||||||
|
if (isEmbeddedDatasource) {
|
||||||
|
let datasourcePayload: Datasource | CreateDatasourceConfig;
|
||||||
|
let pathPayload: string;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = new URL(value);
|
||||||
|
const path = url.pathname === "/" ? "" : url.pathname;
|
||||||
|
const params = url.search;
|
||||||
|
const baseUrl = url.origin;
|
||||||
|
|
||||||
|
datasourcePayload = {
|
||||||
|
name: baseUrl,
|
||||||
|
datasourceConfiguration: {
|
||||||
|
url: baseUrl,
|
||||||
|
},
|
||||||
|
pluginId: props.pluginId,
|
||||||
|
appName: props.appName,
|
||||||
|
};
|
||||||
|
pathPayload = path + params;
|
||||||
|
} catch (e) {
|
||||||
|
datasourcePayload = {
|
||||||
|
name: value,
|
||||||
|
datasourceConfiguration: {
|
||||||
|
url: value,
|
||||||
|
},
|
||||||
|
pluginId: props.pluginId,
|
||||||
|
appName: props.appName,
|
||||||
|
};
|
||||||
|
pathPayload = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateValues = _.debounce(() => {
|
||||||
|
props.changeDatasource(datasourcePayload);
|
||||||
|
props.changePath(pathPayload);
|
||||||
|
}, 50);
|
||||||
|
|
||||||
|
updateValues();
|
||||||
|
} else {
|
||||||
|
const updatePath = _.debounce(() => {
|
||||||
|
props.changePath(value);
|
||||||
|
}, 50);
|
||||||
|
|
||||||
|
updatePath();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
format={(value: Datasource) => {
|
||||||
|
if (!value || !value.datasourceConfiguration) return "";
|
||||||
|
|
||||||
|
if (!value.id) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const option = _.find(options, { value: value.id });
|
||||||
|
|
||||||
|
return option ? [option] : "";
|
||||||
|
}}
|
||||||
|
parse={(option: { value: string }[]) => {
|
||||||
|
if (!option) return null;
|
||||||
|
|
||||||
|
if (option.length) {
|
||||||
|
const datasources = props.datasources.list;
|
||||||
|
|
||||||
|
return datasources.find(
|
||||||
|
datasource => datasource.id === option[0].value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
inputValue={inputValue}
|
||||||
|
noOptionsMessage={() => null}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (state: AppState): ReduxStateProps => ({
|
const mapStateToProps = (state: AppState): ReduxStateProps => {
|
||||||
datasources: state.entities.datasources,
|
const selector = formValueSelector(API_EDITOR_FORM_NAME);
|
||||||
validDatasourcePlugins: getDatasourcePlugins(state),
|
const apiId = selector(state, "id");
|
||||||
});
|
const datasource = selector(state, "datasource");
|
||||||
|
return {
|
||||||
|
datasources: state.entities.datasources,
|
||||||
|
validDatasourcePlugins: getDatasourcePlugins(state),
|
||||||
|
apiId,
|
||||||
|
value: datasource,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const mapDispatchToProps = (
|
const mapDispatchToProps = (
|
||||||
dispatch: any,
|
dispatch: any,
|
||||||
|
|
@ -88,6 +289,16 @@ const mapDispatchToProps = (
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
storeAsDatasource: () => dispatch(storeAsDatasource()),
|
||||||
|
changeDatasource: value => {
|
||||||
|
dispatch(change(API_EDITOR_FORM_NAME, "datasource", value));
|
||||||
|
},
|
||||||
|
changePath: (value: string) => {
|
||||||
|
dispatch(change(API_EDITOR_FORM_NAME, "actionConfiguration.path", value));
|
||||||
|
},
|
||||||
|
setDatasourceFieldText: (apiId, value) => {
|
||||||
|
dispatch(setDatasourceFieldText(apiId, value));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(DatasourcesField);
|
export default connect(mapStateToProps, mapDispatchToProps)(DatasourcesField);
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { Field } from "redux-form";
|
|
||||||
import DynamicAutocompleteInput, {
|
|
||||||
DynamicAutocompleteInputProps,
|
|
||||||
} from "components/editorComponents/DynamicAutocompleteInput";
|
|
||||||
|
|
||||||
type Props = { name: string } & DynamicAutocompleteInputProps;
|
|
||||||
|
|
||||||
const JSONEditorField = (props: Props) => {
|
|
||||||
return (
|
|
||||||
<Field
|
|
||||||
name={props.name}
|
|
||||||
component={DynamicAutocompleteInput}
|
|
||||||
singleLine={false}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default JSONEditorField;
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { FormIcons } from "icons/FormIcons";
|
||||||
import DynamicTextField from "./DynamicTextField";
|
import DynamicTextField from "./DynamicTextField";
|
||||||
import FormRow from "components/editorComponents/FormRow";
|
import FormRow from "components/editorComponents/FormRow";
|
||||||
import FormLabel from "components/editorComponents/FormLabel";
|
import FormLabel from "components/editorComponents/FormLabel";
|
||||||
|
import FIELD_VALUES from "constants/FieldExpectedValue";
|
||||||
|
|
||||||
const FormRowWithLabel = styled(FormRow)`
|
const FormRowWithLabel = styled(FormRow)`
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
@ -26,7 +27,6 @@ const KeyValueRow = (props: Props & WrappedFieldArrayProps) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [props.fields, props.pushFields]);
|
}, [props.fields, props.pushFields]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{typeof props.fields.getAll() === "object" && (
|
{typeof props.fields.getAll() === "object" && (
|
||||||
|
|
@ -42,6 +42,8 @@ const KeyValueRow = (props: Props & WrappedFieldArrayProps) => {
|
||||||
placeholder="Key"
|
placeholder="Key"
|
||||||
singleLine
|
singleLine
|
||||||
setMaxHeight
|
setMaxHeight
|
||||||
|
dataTreePath={`${props.dataTreePath}[${index}].key`}
|
||||||
|
expected={FIELD_VALUES.API_ACTION.params}
|
||||||
/>
|
/>
|
||||||
{!props.actionConfig && (
|
{!props.actionConfig && (
|
||||||
<DynamicTextField
|
<DynamicTextField
|
||||||
|
|
@ -50,6 +52,8 @@ const KeyValueRow = (props: Props & WrappedFieldArrayProps) => {
|
||||||
placeholder="Value"
|
placeholder="Value"
|
||||||
singleLine
|
singleLine
|
||||||
setMaxHeight
|
setMaxHeight
|
||||||
|
dataTreePath={`${props.dataTreePath}[${index}].value`}
|
||||||
|
expected={FIELD_VALUES.API_ACTION.params}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -58,7 +62,9 @@ const KeyValueRow = (props: Props & WrappedFieldArrayProps) => {
|
||||||
<DynamicTextField
|
<DynamicTextField
|
||||||
className={`t--${field}.value.${index}`}
|
className={`t--${field}.value.${index}`}
|
||||||
name={`${field}.value`}
|
name={`${field}.value`}
|
||||||
|
dataTreePath={`${props.dataTreePath}[${index}].value`}
|
||||||
setMaxHeight
|
setMaxHeight
|
||||||
|
expected={FIELD_VALUES.API_ACTION.params}
|
||||||
placeholder={
|
placeholder={
|
||||||
props.placeholder
|
props.placeholder
|
||||||
? props.placeholder
|
? props.placeholder
|
||||||
|
|
@ -123,6 +129,7 @@ type Props = {
|
||||||
type?: string;
|
type?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
pushFields?: boolean;
|
pushFields?: boolean;
|
||||||
|
dataTreePath?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const KeyValueFieldArray = (props: Props) => {
|
const KeyValueFieldArray = (props: Props) => {
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ const KeyValueRow = (props: Props & WrappedFieldArrayProps) => {
|
||||||
props.fields.push({ [keyName[1]]: "", [valueName[1]]: "" });
|
props.fields.push({ [keyName[1]]: "", [valueName[1]]: "" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [props.fields]);
|
}, [props.fields, keyName, valueName]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof props.fields.getAll() === "string") {
|
if (typeof props.fields.getAll() === "string") {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ class ActionSelectorControl extends BaseControl<ControlProps> {
|
||||||
<ActionCreator
|
<ActionCreator
|
||||||
value={propertyValue}
|
value={propertyValue}
|
||||||
isValid={this.props.isValid}
|
isValid={this.props.isValid}
|
||||||
validationMessage={this.props.validationMessage}
|
validationMessage={this.props.errorMessage}
|
||||||
onValueChange={this.handleValueUpdate}
|
onValueChange={this.handleValueUpdate}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,10 @@ export interface ControlData {
|
||||||
controlType: ControlType;
|
controlType: ControlType;
|
||||||
propertyValue?: any;
|
propertyValue?: any;
|
||||||
isValid: boolean;
|
isValid: boolean;
|
||||||
|
errorMessage?: string;
|
||||||
|
expected: string;
|
||||||
validationMessage?: string;
|
validationMessage?: string;
|
||||||
|
dataTreePath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ControlFunctions {
|
export interface ControlFunctions {
|
||||||
|
|
|
||||||
|
|
@ -70,18 +70,11 @@ type RenderComponentProps = {
|
||||||
validationMessage: string;
|
validationMessage: string;
|
||||||
deleteOption: Function;
|
deleteOption: Function;
|
||||||
updateOption: Function;
|
updateOption: Function;
|
||||||
|
dataTreePath: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function DataControlComponent(props: RenderComponentProps) {
|
function DataControlComponent(props: RenderComponentProps) {
|
||||||
const {
|
const { deleteOption, updateOption, item, index, length, isValid } = props;
|
||||||
deleteOption,
|
|
||||||
updateOption,
|
|
||||||
item,
|
|
||||||
index,
|
|
||||||
length,
|
|
||||||
isValid,
|
|
||||||
validationMessage,
|
|
||||||
} = props;
|
|
||||||
return (
|
return (
|
||||||
<StyledOptionControlWrapper orientation={"VERTICAL"}>
|
<StyledOptionControlWrapper orientation={"VERTICAL"}>
|
||||||
<StyledOptionControlWrapper orientation={"HORIZONTAL"}>
|
<StyledOptionControlWrapper orientation={"HORIZONTAL"}>
|
||||||
|
|
@ -117,8 +110,9 @@ function DataControlComponent(props: RenderComponentProps) {
|
||||||
updateOption(index, "data", value);
|
updateOption(index, "data", value);
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
dataTreePath={`${props.dataTreePath}`}
|
||||||
meta={{
|
meta={{
|
||||||
error: isValid ? "" : validationMessage,
|
error: isValid ? "" : "There is an error",
|
||||||
touched: true,
|
touched: true,
|
||||||
}}
|
}}
|
||||||
theme={"DARK"}
|
theme={"DARK"}
|
||||||
|
|
@ -190,6 +184,7 @@ class ChartDataControl extends BaseControl<ControlProps> {
|
||||||
updateOption={this.updateOption}
|
updateOption={this.updateOption}
|
||||||
isValid={validations[index].isValid}
|
isValid={validations[index].isValid}
|
||||||
validationMessage={validations[index].validationMessage}
|
validationMessage={validations[index].validationMessage}
|
||||||
|
dataTreePath={`${this.props.dataTreePath}`}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,21 @@ import DynamicAutocompleteInput from "components/editorComponents/DynamicAutocom
|
||||||
import { EventOrValueHandler } from "redux-form";
|
import { EventOrValueHandler } from "redux-form";
|
||||||
class CodeEditorControl extends BaseControl<ControlProps> {
|
class CodeEditorControl extends BaseControl<ControlProps> {
|
||||||
render() {
|
render() {
|
||||||
const { validationMessage, propertyValue, isValid } = this.props;
|
const {
|
||||||
|
errorMessage,
|
||||||
|
expected,
|
||||||
|
propertyValue,
|
||||||
|
isValid,
|
||||||
|
dataTreePath,
|
||||||
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<DynamicAutocompleteInput
|
<DynamicAutocompleteInput
|
||||||
theme={"DARK"}
|
theme={"DARK"}
|
||||||
input={{ value: propertyValue, onChange: this.onChange }}
|
input={{ value: propertyValue, onChange: this.onChange }}
|
||||||
|
dataTreePath={dataTreePath}
|
||||||
|
expected={expected}
|
||||||
meta={{
|
meta={{
|
||||||
error: isValid ? "" : validationMessage,
|
error: isValid ? "" : errorMessage,
|
||||||
touched: true,
|
touched: true,
|
||||||
}}
|
}}
|
||||||
singleLine={false}
|
singleLine={false}
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,20 @@ export function InputText(props: {
|
||||||
value: string;
|
value: string;
|
||||||
onChange: (event: React.ChangeEvent<HTMLTextAreaElement> | string) => void;
|
onChange: (event: React.ChangeEvent<HTMLTextAreaElement> | string) => void;
|
||||||
isValid: boolean;
|
isValid: boolean;
|
||||||
validationMessage?: string;
|
errorMessage?: string;
|
||||||
|
expected?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
dataTreePath?: string;
|
||||||
}) {
|
}) {
|
||||||
const { validationMessage, value, isValid, onChange, placeholder } = props;
|
const {
|
||||||
|
errorMessage,
|
||||||
|
expected,
|
||||||
|
value,
|
||||||
|
isValid,
|
||||||
|
onChange,
|
||||||
|
placeholder,
|
||||||
|
dataTreePath,
|
||||||
|
} = props;
|
||||||
return (
|
return (
|
||||||
<StyledDynamicInput>
|
<StyledDynamicInput>
|
||||||
<DynamicAutocompleteInput
|
<DynamicAutocompleteInput
|
||||||
|
|
@ -20,8 +30,10 @@ export function InputText(props: {
|
||||||
value: value,
|
value: value,
|
||||||
onChange: onChange,
|
onChange: onChange,
|
||||||
}}
|
}}
|
||||||
|
expected={expected}
|
||||||
|
dataTreePath={dataTreePath}
|
||||||
meta={{
|
meta={{
|
||||||
error: isValid ? "" : validationMessage,
|
error: isValid ? "" : errorMessage,
|
||||||
touched: true,
|
touched: true,
|
||||||
}}
|
}}
|
||||||
theme={"DARK"}
|
theme={"DARK"}
|
||||||
|
|
@ -35,11 +47,13 @@ export function InputText(props: {
|
||||||
class InputTextControl extends BaseControl<InputControlProps> {
|
class InputTextControl extends BaseControl<InputControlProps> {
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
validationMessage,
|
errorMessage,
|
||||||
|
expected,
|
||||||
propertyValue,
|
propertyValue,
|
||||||
isValid,
|
isValid,
|
||||||
label,
|
label,
|
||||||
placeholderText,
|
placeholderText,
|
||||||
|
dataTreePath,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<InputText
|
<InputText
|
||||||
|
|
@ -47,8 +61,10 @@ class InputTextControl extends BaseControl<InputControlProps> {
|
||||||
value={propertyValue}
|
value={propertyValue}
|
||||||
onChange={this.onTextChange}
|
onChange={this.onTextChange}
|
||||||
isValid={isValid}
|
isValid={isValid}
|
||||||
validationMessage={validationMessage}
|
errorMessage={errorMessage}
|
||||||
|
expected={expected}
|
||||||
placeholder={placeholderText}
|
placeholder={placeholderText}
|
||||||
|
dataTreePath={dataTreePath}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ export type ContentType =
|
||||||
| "application/json"
|
| "application/json"
|
||||||
| "application/x-www-form-urlencoded";
|
| "application/x-www-form-urlencoded";
|
||||||
|
|
||||||
export const REQUEST_TIMEOUT_MS = 10000;
|
export const REQUEST_TIMEOUT_MS = 20000;
|
||||||
|
export const DEFAULT_ACTION_TIMEOUT = 10000;
|
||||||
export const DEFAULT_EXECUTE_ACTION_TIMEOUT_MS = 15000;
|
export const DEFAULT_EXECUTE_ACTION_TIMEOUT_MS = 15000;
|
||||||
export const DEFAULT_TEST_DATA_SOURCE_TIMEOUT_MS = 30000;
|
export const DEFAULT_TEST_DATA_SOURCE_TIMEOUT_MS = 30000;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { RestAction } from "api/ActionAPI";
|
import { RestAction } from "entities/Action";
|
||||||
|
import { DEFAULT_ACTION_TIMEOUT } from "constants/ApiConstants";
|
||||||
|
|
||||||
export const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"];
|
export const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"];
|
||||||
|
|
||||||
|
|
@ -11,6 +12,7 @@ export const REST_PLUGIN_PACKAGE_NAME = "restapi-plugin";
|
||||||
|
|
||||||
export const DEFAULT_API_ACTION: Partial<RestAction> = {
|
export const DEFAULT_API_ACTION: Partial<RestAction> = {
|
||||||
actionConfiguration: {
|
actionConfiguration: {
|
||||||
|
timeoutInMillisecond: DEFAULT_ACTION_TIMEOUT,
|
||||||
httpMethod: HTTP_METHODS[0],
|
httpMethod: HTTP_METHODS[0],
|
||||||
headers: [
|
headers: [
|
||||||
{ key: "", value: "" },
|
{ key: "", value: "" },
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,8 @@ export const Colors: Record<string, string> = {
|
||||||
BLUE_CHARCOAL: "#23292E",
|
BLUE_CHARCOAL: "#23292E",
|
||||||
TROUT: "#4C565E",
|
TROUT: "#4C565E",
|
||||||
JAFFA_DARK: "#EF7541",
|
JAFFA_DARK: "#EF7541",
|
||||||
|
GRAY: "#828282",
|
||||||
|
ATHENS_GRAY_DARKER: "#F8F9FA",
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Color = typeof Colors[keyof typeof Colors];
|
export type Color = typeof Colors[keyof typeof Colors];
|
||||||
|
|
|
||||||
|
|
@ -243,6 +243,10 @@ export type Theme = {
|
||||||
lineHeights: Array<number>;
|
lineHeights: Array<number>;
|
||||||
fonts: Array<FontFamily>;
|
fonts: Array<FontFamily>;
|
||||||
borders: ThemeBorder[];
|
borders: ThemeBorder[];
|
||||||
|
evaluatedValuePopup: {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
propertyPane: PropertyPaneTheme;
|
propertyPane: PropertyPaneTheme;
|
||||||
headerHeight: string;
|
headerHeight: string;
|
||||||
sidebarWidth: string;
|
sidebarWidth: string;
|
||||||
|
|
@ -312,6 +316,10 @@ export const theme: Theme = {
|
||||||
height: 600,
|
height: 600,
|
||||||
dividerColor: Colors.MAKO,
|
dividerColor: Colors.MAKO,
|
||||||
},
|
},
|
||||||
|
evaluatedValuePopup: {
|
||||||
|
width: 300,
|
||||||
|
height: 400,
|
||||||
|
},
|
||||||
drawerWidth: "80%",
|
drawerWidth: "80%",
|
||||||
colors: {
|
colors: {
|
||||||
primary: Colors.GREEN,
|
primary: Colors.GREEN,
|
||||||
|
|
|
||||||
146
app/client/src/constants/FieldExpectedValue.ts
Normal file
146
app/client/src/constants/FieldExpectedValue.ts
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
import { WidgetType } from "constants/WidgetConstants";
|
||||||
|
|
||||||
|
const FIELD_VALUES: Record<
|
||||||
|
WidgetType | "API_ACTION",
|
||||||
|
Record<string, string>
|
||||||
|
> = {
|
||||||
|
API_ACTION: {
|
||||||
|
body: "JSON",
|
||||||
|
params: "string",
|
||||||
|
headers: "string",
|
||||||
|
path: "string",
|
||||||
|
},
|
||||||
|
CANVAS_WIDGET: {},
|
||||||
|
ICON_WIDGET: {},
|
||||||
|
CONTAINER_WIDGET: {
|
||||||
|
backgroundColor: "string",
|
||||||
|
isVisible: "boolean",
|
||||||
|
},
|
||||||
|
DATE_PICKER_WIDGET: {
|
||||||
|
defaultDate: "Date",
|
||||||
|
isRequired: "boolean",
|
||||||
|
isVisible: "boolean",
|
||||||
|
isDisabled: "boolean",
|
||||||
|
onDateSelected: "undefined",
|
||||||
|
},
|
||||||
|
TABLE_WIDGET: {
|
||||||
|
tableData: "Array<Object>",
|
||||||
|
serverSidePaginationEnabled: "boolean",
|
||||||
|
isVisible: "boolean",
|
||||||
|
exportPDF: "boolean",
|
||||||
|
exportExcel: "boolean",
|
||||||
|
exportCsv: "boolean",
|
||||||
|
onRowSelected: "undefined",
|
||||||
|
onPageChange: "undefined",
|
||||||
|
},
|
||||||
|
IMAGE_WIDGET: {
|
||||||
|
image: "string",
|
||||||
|
defaultImage: "string",
|
||||||
|
isVisible: "boolean",
|
||||||
|
},
|
||||||
|
RADIO_GROUP_WIDGET: {
|
||||||
|
options: "Array<{ label: string, value: string }>",
|
||||||
|
defaultOptionValue: "string",
|
||||||
|
isRequired: "boolean",
|
||||||
|
isVisible: "boolean",
|
||||||
|
onSelectionChange: "undefined",
|
||||||
|
},
|
||||||
|
TABS_WIDGET: {
|
||||||
|
tabs: "Array<{ label: string, id: string }>",
|
||||||
|
selectedTab: "string",
|
||||||
|
isVisible: "boolean",
|
||||||
|
},
|
||||||
|
CHART_WIDGET: {
|
||||||
|
chartName: "string",
|
||||||
|
chartType: "LINE_CHART | BAR_CHART | PIE_CHART | COLUMN_CHART | AREA_CHART",
|
||||||
|
singleChartData: "Array<{ x: string, y: number }>",
|
||||||
|
xAxisName: "string",
|
||||||
|
yAxisName: "string",
|
||||||
|
isVisible: "boolean",
|
||||||
|
},
|
||||||
|
MODAL_WIDGET: {
|
||||||
|
canOutsideClickClose: "boolean",
|
||||||
|
size: "MODAL_LARGE | MODAL_SMALL",
|
||||||
|
},
|
||||||
|
INPUT_WIDGET: {
|
||||||
|
inputType: "string",
|
||||||
|
placeholderText: "string",
|
||||||
|
defaultText: "string",
|
||||||
|
regex: "string",
|
||||||
|
errorMessage: "string",
|
||||||
|
isRequired: "boolean",
|
||||||
|
isVisible: "boolean",
|
||||||
|
isDisabled: "boolean",
|
||||||
|
onTextChanged: "undefined",
|
||||||
|
},
|
||||||
|
DROP_DOWN_WIDGET: {
|
||||||
|
label: "string",
|
||||||
|
selectionType: "SINGLE_SELECT | MULTI_SELECT",
|
||||||
|
options: "Array<{ label: string, value: string }>",
|
||||||
|
defaultOptionValue: "string",
|
||||||
|
isRequired: "boolean",
|
||||||
|
isVisible: "boolean",
|
||||||
|
onOptionChange: "boolean",
|
||||||
|
},
|
||||||
|
FORM_BUTTON_WIDGET: {
|
||||||
|
text: "string",
|
||||||
|
buttonStyle: "PRIMARY_BUTTON | SECONDARY_BUTTON | DANGER_BUTTON",
|
||||||
|
disabledWhenInvalid: "boolean",
|
||||||
|
resetFormOnClick: "boolean",
|
||||||
|
isVisible: "boolean",
|
||||||
|
onClick: "boolean",
|
||||||
|
},
|
||||||
|
MAP_WIDGET: {
|
||||||
|
defaultMarkers: "Array<{ lat: number, long: number }>",
|
||||||
|
enableSearch: "boolean",
|
||||||
|
enablePickLocation: "boolean",
|
||||||
|
enableCreateMarker: "boolean",
|
||||||
|
isVisible: "boolean",
|
||||||
|
onMarkerClick: "undefined",
|
||||||
|
onCreateMarker: "undefined",
|
||||||
|
},
|
||||||
|
BUTTON_WIDGET: {
|
||||||
|
text: "string",
|
||||||
|
buttonStyle: "PRIMARY_BUTTON | SECONDARY_BUTTON | DANGER_BUTTON",
|
||||||
|
isVisible: "boolean",
|
||||||
|
onClick: "boolean",
|
||||||
|
},
|
||||||
|
RICH_TEXT_EDITOR_WIDGET: {
|
||||||
|
defaultText: "string",
|
||||||
|
isVisible: "boolean",
|
||||||
|
isDisabled: "boolean",
|
||||||
|
onTextChange: "undefined",
|
||||||
|
},
|
||||||
|
FILE_PICKER_WIDGET: {
|
||||||
|
label: "string",
|
||||||
|
maxNumFiles: "number",
|
||||||
|
maxFileSize: "number",
|
||||||
|
|
||||||
|
allowedFileTypes: "Array<string>",
|
||||||
|
isRequired: "boolean",
|
||||||
|
isVisible: "boolean",
|
||||||
|
uploadedFileUrls: "string",
|
||||||
|
onFilesSelected: "undefined",
|
||||||
|
},
|
||||||
|
CHECKBOX_WIDGET: {
|
||||||
|
label: "string",
|
||||||
|
defaultCheckedState: "boolean",
|
||||||
|
isRequired: "boolean",
|
||||||
|
isDisabled: "boolean",
|
||||||
|
isVisible: "boolean",
|
||||||
|
onCheckChange: "undefined",
|
||||||
|
},
|
||||||
|
FORM_WIDGET: {
|
||||||
|
backgroundColor: "string",
|
||||||
|
isVisible: "boolean",
|
||||||
|
},
|
||||||
|
TEXT_WIDGET: {
|
||||||
|
text: "string",
|
||||||
|
textAlign: "LEFT | CENTER | RIGHT",
|
||||||
|
textStyle: "HEADING | LABEL | BODY",
|
||||||
|
shouldScroll: "boolean",
|
||||||
|
isVisible: "boolean",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FIELD_VALUES;
|
||||||
|
|
@ -77,6 +77,10 @@ export const ReduxActionTypes: { [key: string]: string } = {
|
||||||
FETCH_PUBLISHED_PAGE_SUCCESS: "FETCH_PUBLISHED_PAGE_SUCCESS",
|
FETCH_PUBLISHED_PAGE_SUCCESS: "FETCH_PUBLISHED_PAGE_SUCCESS",
|
||||||
DELETE_DATASOURCE_INIT: "DELETE_DATASOURCE_INIT",
|
DELETE_DATASOURCE_INIT: "DELETE_DATASOURCE_INIT",
|
||||||
DELETE_DATASOURCE_SUCCESS: "DELETE_DATASOURCE_SUCCESS",
|
DELETE_DATASOURCE_SUCCESS: "DELETE_DATASOURCE_SUCCESS",
|
||||||
|
STORE_AS_DATASOURCE_INIT: "STORE_AS_DATASOURCE_INIT",
|
||||||
|
STORE_AS_DATASOURCE_UPDATE: "STORE_AS_DATASOURCE_UPDATE",
|
||||||
|
STORE_AS_DATASOURCE_COMPLETE: "STORE_AS_DATASOURCE_COMPLETE",
|
||||||
|
SET_DATASOURCE_FIELD_TEXT: "SET_DATASOURCE_FIELD_TEXT",
|
||||||
PUBLISH_APPLICATION_INIT: "PUBLISH_APPLICATION_INIT",
|
PUBLISH_APPLICATION_INIT: "PUBLISH_APPLICATION_INIT",
|
||||||
PUBLISH_APPLICATION_SUCCESS: "PUBLISH_APPLICATION_SUCCESS",
|
PUBLISH_APPLICATION_SUCCESS: "PUBLISH_APPLICATION_SUCCESS",
|
||||||
CREATE_PAGE_INIT: "CREATE_PAGE_INIT",
|
CREATE_PAGE_INIT: "CREATE_PAGE_INIT",
|
||||||
|
|
@ -127,6 +131,7 @@ export const ReduxActionTypes: { [key: string]: string } = {
|
||||||
FETCH_ORGS_SUCCESS: "FETCH_ORGS_SUCCES",
|
FETCH_ORGS_SUCCESS: "FETCH_ORGS_SUCCES",
|
||||||
FETCH_ORGS_INIT: "FETCH_ORGS_INIT",
|
FETCH_ORGS_INIT: "FETCH_ORGS_INIT",
|
||||||
SAVE_ORG_INIT: "SAVE_ORG_INIT",
|
SAVE_ORG_INIT: "SAVE_ORG_INIT",
|
||||||
|
SET_CURRENT_ORG_ID: "SET_CURRENT_ORG_ID",
|
||||||
STORE_DATASOURCE_REFS: "STORE_DATASOURCE_REFS",
|
STORE_DATASOURCE_REFS: "STORE_DATASOURCE_REFS",
|
||||||
UPDATE_DATASOURCE_REFS: "UPDATE_DATASOURCE_REFS",
|
UPDATE_DATASOURCE_REFS: "UPDATE_DATASOURCE_REFS",
|
||||||
FETCH_USER_INIT: "FETCH_USER_INIT",
|
FETCH_USER_INIT: "FETCH_USER_INIT",
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ export const VALIDATION_TYPES = {
|
||||||
ARRAY: "ARRAY",
|
ARRAY: "ARRAY",
|
||||||
TABLE_DATA: "TABLE_DATA",
|
TABLE_DATA: "TABLE_DATA",
|
||||||
OPTIONS_DATA: "OPTIONS_DATA",
|
OPTIONS_DATA: "OPTIONS_DATA",
|
||||||
|
SINGLE_CHART_DATA: "SINGLE_CHART_DATA",
|
||||||
DATE: "DATE",
|
DATE: "DATE",
|
||||||
TABS_DATA: "TABS_DATA",
|
TABS_DATA: "TABS_DATA",
|
||||||
CHART_DATA: "CHART_DATA",
|
CHART_DATA: "CHART_DATA",
|
||||||
|
|
|
||||||
91
app/client/src/entities/Action/index.ts
Normal file
91
app/client/src/entities/Action/index.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
import { Datasource } from "api/DatasourcesApi";
|
||||||
|
|
||||||
|
export enum PluginType {
|
||||||
|
API = "API",
|
||||||
|
DB = "DB",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PaginationType {
|
||||||
|
NONE = "NONE",
|
||||||
|
PAGE_NO = "PAGE_NO",
|
||||||
|
URL = "URL",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ActionConfig {
|
||||||
|
timeoutInMillisecond: number;
|
||||||
|
paginationType?: PaginationType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ActionProvider {
|
||||||
|
name: string;
|
||||||
|
imageUrl: string;
|
||||||
|
url: string;
|
||||||
|
description: string;
|
||||||
|
credentialSteps: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Property {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BodyFormData {
|
||||||
|
editable: boolean;
|
||||||
|
mandatory: boolean;
|
||||||
|
description: string;
|
||||||
|
key: string;
|
||||||
|
value?: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiActionConfig extends ActionConfig {
|
||||||
|
headers: Property[];
|
||||||
|
httpMethod: string;
|
||||||
|
path?: string;
|
||||||
|
body?: JSON | string | Record<string, any> | null;
|
||||||
|
queryParameters?: Property[];
|
||||||
|
bodyFormData?: BodyFormData[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QueryActionConfig extends ActionConfig {
|
||||||
|
queryString: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Action {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
datasource: Partial<Datasource>;
|
||||||
|
organizationId: string;
|
||||||
|
pageId: string;
|
||||||
|
collectionId?: string;
|
||||||
|
actionConfiguration: Partial<ActionConfig>;
|
||||||
|
pluginId: string;
|
||||||
|
pluginType: PluginType;
|
||||||
|
executeOnLoad: boolean;
|
||||||
|
dynamicBindingPathList: Property[];
|
||||||
|
isValid: boolean;
|
||||||
|
invalids: string[];
|
||||||
|
jsonPathKeys: string[];
|
||||||
|
cacheResponse: string;
|
||||||
|
templateId?: string;
|
||||||
|
providerId?: string;
|
||||||
|
provider?: ActionProvider;
|
||||||
|
documentation?: { text: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RestAction extends Action {
|
||||||
|
actionConfiguration: Partial<ApiActionConfig>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RapidApiAction extends Action {
|
||||||
|
actionConfiguration: Partial<ApiActionConfig>;
|
||||||
|
templateId: string;
|
||||||
|
proverId: string;
|
||||||
|
provider: ActionProvider;
|
||||||
|
pluginId: string;
|
||||||
|
documentation: { text: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface QueryAction extends Action {
|
||||||
|
actionConfiguration: Partial<QueryActionConfig>;
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,8 @@ import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsRe
|
||||||
import { MetaState } from "reducers/entityReducers/metaReducer";
|
import { MetaState } from "reducers/entityReducers/metaReducer";
|
||||||
import { PageListPayload } from "constants/ReduxActionConstants";
|
import { PageListPayload } from "constants/ReduxActionConstants";
|
||||||
import WidgetFactory from "utils/WidgetFactory";
|
import WidgetFactory from "utils/WidgetFactory";
|
||||||
|
import { ActionDraftsState } from "reducers/entityReducers/actionDraftsReducer";
|
||||||
|
import { Property } from "entities/Action";
|
||||||
|
|
||||||
export type ActionDescription<T> = {
|
export type ActionDescription<T> = {
|
||||||
type: string;
|
type: string;
|
||||||
|
|
@ -32,6 +34,7 @@ export type RunActionPayload = {
|
||||||
export interface DataTreeAction extends Omit<ActionData, "data"> {
|
export interface DataTreeAction extends Omit<ActionData, "data"> {
|
||||||
data: ActionResponse["body"];
|
data: ActionResponse["body"];
|
||||||
run: ActionDispatcher<RunActionPayload, [string, string]>;
|
run: ActionDispatcher<RunActionPayload, [string, string]>;
|
||||||
|
dynamicBindingPathList: Property[];
|
||||||
ENTITY_TYPE: ENTITY_TYPE.ACTION;
|
ENTITY_TYPE: ENTITY_TYPE.ACTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -63,6 +66,7 @@ export type DataTree = {
|
||||||
|
|
||||||
type DataTreeSeed = {
|
type DataTreeSeed = {
|
||||||
actions: ActionDataState;
|
actions: ActionDataState;
|
||||||
|
actionDrafts: ActionDraftsState;
|
||||||
widgets: CanvasWidgetsReduxState;
|
widgets: CanvasWidgetsReduxState;
|
||||||
widgetsMeta: MetaState;
|
widgetsMeta: MetaState;
|
||||||
pageList: PageListPayload;
|
pageList: PageListPayload;
|
||||||
|
|
@ -72,6 +76,7 @@ type DataTreeSeed = {
|
||||||
export class DataTreeFactory {
|
export class DataTreeFactory {
|
||||||
static create({
|
static create({
|
||||||
actions,
|
actions,
|
||||||
|
actionDrafts,
|
||||||
widgets,
|
widgets,
|
||||||
widgetsMeta,
|
widgetsMeta,
|
||||||
pageList,
|
pageList,
|
||||||
|
|
@ -84,8 +89,23 @@ export class DataTreeFactory {
|
||||||
"closeModal",
|
"closeModal",
|
||||||
];
|
];
|
||||||
actions.forEach(a => {
|
actions.forEach(a => {
|
||||||
dataTree[a.config.name] = {
|
const config =
|
||||||
|
a.config.id in actionDrafts ? actionDrafts[a.config.id] : a.config;
|
||||||
|
let dynamicBindingPathList: Property[] = [];
|
||||||
|
// update paths
|
||||||
|
if (
|
||||||
|
config.dynamicBindingPathList &&
|
||||||
|
config.dynamicBindingPathList.length
|
||||||
|
) {
|
||||||
|
dynamicBindingPathList = config.dynamicBindingPathList.map(d => ({
|
||||||
|
...d,
|
||||||
|
key: `config.${d.key}`,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
dataTree[config.name] = {
|
||||||
...a,
|
...a,
|
||||||
|
config: config,
|
||||||
|
dynamicBindingPathList,
|
||||||
data: a.data ? a.data.body : {},
|
data: a.data ? a.data.body : {},
|
||||||
run: function(onSuccess: string, onError: string) {
|
run: function(onSuccess: string, onError: string) {
|
||||||
return {
|
return {
|
||||||
|
|
@ -99,7 +119,7 @@ export class DataTreeFactory {
|
||||||
},
|
},
|
||||||
ENTITY_TYPE: ENTITY_TYPE.ACTION,
|
ENTITY_TYPE: ENTITY_TYPE.ACTION,
|
||||||
};
|
};
|
||||||
dataTree.actionPaths && dataTree.actionPaths.push(`${a.config.name}.run`);
|
dataTree.actionPaths && dataTree.actionPaths.push(`${config.name}.run`);
|
||||||
});
|
});
|
||||||
Object.keys(widgets).forEach(w => {
|
Object.keys(widgets).forEach(w => {
|
||||||
const widget = widgets[w];
|
const widget = widgets[w];
|
||||||
|
|
|
||||||
|
|
@ -312,39 +312,34 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
|
||||||
chartType: "LINE_CHART",
|
chartType: "LINE_CHART",
|
||||||
chartName: "Sales on working days",
|
chartName: "Sales on working days",
|
||||||
allowHorizontalScroll: false,
|
allowHorizontalScroll: false,
|
||||||
chartData: [
|
singleChartData: [
|
||||||
{
|
{
|
||||||
seriesName: "",
|
x: "Mon",
|
||||||
data: [
|
y: 10000,
|
||||||
{
|
},
|
||||||
x: "Mon",
|
{
|
||||||
y: 10000,
|
x: "Tue",
|
||||||
},
|
y: 12000,
|
||||||
{
|
},
|
||||||
x: "Tue",
|
{
|
||||||
y: 12000,
|
x: "Wed",
|
||||||
},
|
y: 32000,
|
||||||
{
|
},
|
||||||
x: "Wed",
|
{
|
||||||
y: 32000,
|
x: "Thu",
|
||||||
},
|
y: 28000,
|
||||||
{
|
},
|
||||||
x: "Thu",
|
{
|
||||||
y: 28000,
|
x: "Fri",
|
||||||
},
|
y: 14000,
|
||||||
{
|
},
|
||||||
x: "Fri",
|
{
|
||||||
y: 14000,
|
x: "Sat",
|
||||||
},
|
y: 19000,
|
||||||
{
|
},
|
||||||
x: "Sat",
|
{
|
||||||
y: 19000,
|
x: "Sun",
|
||||||
},
|
y: 36000,
|
||||||
{
|
|
||||||
x: "Sun",
|
|
||||||
y: 36000,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
xAxisName: "Last Week",
|
xAxisName: "Last Week",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ export const AppViewerHeader = (props: AppViewerHeaderProps) => {
|
||||||
<HeaderWrapper>
|
<HeaderWrapper>
|
||||||
{props.url && (
|
{props.url && (
|
||||||
<Button
|
<Button
|
||||||
|
className="t--back-to-editor"
|
||||||
href={props.url}
|
href={props.url}
|
||||||
intent="primary"
|
intent="primary"
|
||||||
icon="chevron-left"
|
icon="chevron-left"
|
||||||
|
|
|
||||||
|
|
@ -9,29 +9,27 @@ import {
|
||||||
import {
|
import {
|
||||||
HTTP_METHOD_OPTIONS,
|
HTTP_METHOD_OPTIONS,
|
||||||
HTTP_METHODS,
|
HTTP_METHODS,
|
||||||
CONTENT_TYPE,
|
|
||||||
} from "constants/ApiEditorConstants";
|
} from "constants/ApiEditorConstants";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import PostBodyData from "./PostBodyData";
|
|
||||||
import FormLabel from "components/editorComponents/FormLabel";
|
import FormLabel from "components/editorComponents/FormLabel";
|
||||||
import FormRow from "components/editorComponents/FormRow";
|
import FormRow from "components/editorComponents/FormRow";
|
||||||
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
|
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
|
||||||
import { RestAction, PaginationField } from "api/ActionAPI";
|
import { PaginationField } from "api/ActionAPI";
|
||||||
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||||
import TextField from "components/editorComponents/form/fields/TextField";
|
import TextField from "components/editorComponents/form/fields/TextField";
|
||||||
import DynamicTextField from "components/editorComponents/form/fields/DynamicTextField";
|
|
||||||
import DropdownField from "components/editorComponents/form/fields/DropdownField";
|
import DropdownField from "components/editorComponents/form/fields/DropdownField";
|
||||||
import DatasourcesField from "components/editorComponents/form/fields/DatasourcesField";
|
import DatasourcesField from "components/editorComponents/form/fields/DatasourcesField";
|
||||||
import KeyValueFieldArray from "components/editorComponents/form/fields/KeyValueFieldArray";
|
|
||||||
import ApiResponseView from "components/editorComponents/ApiResponseView";
|
|
||||||
import { API_EDITOR_FORM_NAME } from "constants/forms";
|
import { API_EDITOR_FORM_NAME } from "constants/forms";
|
||||||
import LoadingOverlayScreen from "components/editorComponents/LoadingOverlayScreen";
|
import LoadingOverlayScreen from "components/editorComponents/LoadingOverlayScreen";
|
||||||
import { FormIcons } from "icons/FormIcons";
|
|
||||||
import { BaseTabbedView } from "components/designSystems/appsmith/TabbedView";
|
import { BaseTabbedView } from "components/designSystems/appsmith/TabbedView";
|
||||||
import Pagination, { PaginationType } from "./Pagination";
|
import Pagination from "./Pagination";
|
||||||
|
import { PaginationType, RestAction } from "entities/Action";
|
||||||
import { Icon } from "@blueprintjs/core";
|
import { Icon } from "@blueprintjs/core";
|
||||||
import { HelpMap, HelpBaseURL } from "constants/HelpConstants";
|
import { HelpMap, HelpBaseURL } from "constants/HelpConstants";
|
||||||
import CollapsibleHelp from "components/designSystems/appsmith/help/CollapsibleHelp";
|
import CollapsibleHelp from "components/designSystems/appsmith/help/CollapsibleHelp";
|
||||||
|
import KeyValueFieldArray from "components/editorComponents/form/fields/KeyValueFieldArray";
|
||||||
|
import PostBodyData from "./PostBodyData";
|
||||||
|
import ApiResponseView from "components/editorComponents/ApiResponseView";
|
||||||
|
|
||||||
const Form = styled.form`
|
const Form = styled.form`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -59,23 +57,6 @@ const MainConfiguration = styled.div`
|
||||||
padding-left: 17px;
|
padding-left: 17px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const SecondaryWrapper = styled.div`
|
|
||||||
display: flex;
|
|
||||||
height: 100%;
|
|
||||||
border-top: 1px solid #d0d7dd;
|
|
||||||
margin-top: 15px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const RequestParamsWrapper = styled.div`
|
|
||||||
flex: 4;
|
|
||||||
border-right: 1px solid #d0d7dd;
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding-top: 6px;
|
|
||||||
padding-left: 17px;
|
|
||||||
padding-right: 10px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const ActionButtons = styled.div`
|
const ActionButtons = styled.div`
|
||||||
flex: 1;
|
flex: 1;
|
||||||
`;
|
`;
|
||||||
|
|
@ -88,13 +69,15 @@ const ActionButton = styled(BaseButton)`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const HeadersSection = styled.div`
|
|
||||||
margin-bottom: 32px;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const DatasourceWrapper = styled.div`
|
const DatasourceWrapper = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 320px;
|
`;
|
||||||
|
|
||||||
|
const SecondaryWrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
border-top: 1px solid #d0d7dd;
|
||||||
|
margin-top: 15px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const TabbedViewContainer = styled.div`
|
const TabbedViewContainer = styled.div`
|
||||||
|
|
@ -113,6 +96,19 @@ const StyledOpenDocsIcon = styled(Icon)`
|
||||||
height: 18px;
|
height: 18px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
const RequestParamsWrapper = styled.div`
|
||||||
|
flex: 4;
|
||||||
|
border-right: 1px solid #d0d7dd;
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-top: 6px;
|
||||||
|
padding-left: 17px;
|
||||||
|
padding-right: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const HeadersSection = styled.div`
|
||||||
|
margin-bottom: 32px;
|
||||||
|
`;
|
||||||
|
|
||||||
interface APIFormProps {
|
interface APIFormProps {
|
||||||
pluginId: string;
|
pluginId: string;
|
||||||
|
|
@ -126,18 +122,16 @@ interface APIFormProps {
|
||||||
isDeleting: boolean;
|
isDeleting: boolean;
|
||||||
paginationType: PaginationType;
|
paginationType: PaginationType;
|
||||||
appName: string;
|
appName: string;
|
||||||
actionConfiguration?: any;
|
|
||||||
httpMethodFromForm: string;
|
httpMethodFromForm: string;
|
||||||
actionConfigurationBody: object | string;
|
actionConfigurationBody: object | string;
|
||||||
actionConfigurationHeaders?: any;
|
actionConfigurationHeaders?: any;
|
||||||
contentType: {
|
actionName: string;
|
||||||
key: string;
|
apiId: string;
|
||||||
value: string;
|
|
||||||
};
|
|
||||||
location: {
|
location: {
|
||||||
pathname: string;
|
pathname: string;
|
||||||
};
|
};
|
||||||
dispatch: any;
|
dispatch: any;
|
||||||
|
datasourceFieldText: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = APIFormProps & InjectedFormProps<RestAction, APIFormProps>;
|
type Props = APIFormProps & InjectedFormProps<RestAction, APIFormProps>;
|
||||||
|
|
@ -153,15 +147,14 @@ const ApiEditorForm: React.FC<Props> = (props: Props) => {
|
||||||
isDeleting,
|
isDeleting,
|
||||||
isRunning,
|
isRunning,
|
||||||
isSaving,
|
isSaving,
|
||||||
actionConfiguration,
|
|
||||||
actionConfigurationHeaders,
|
actionConfigurationHeaders,
|
||||||
actionConfigurationBody,
|
actionConfigurationBody,
|
||||||
httpMethodFromForm,
|
httpMethodFromForm,
|
||||||
|
actionName,
|
||||||
location,
|
location,
|
||||||
dispatch,
|
dispatch,
|
||||||
|
apiId,
|
||||||
} = props;
|
} = props;
|
||||||
const allowPostBody =
|
|
||||||
httpMethodFromForm && httpMethodFromForm !== HTTP_METHODS[0];
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ReduxActionTypes.SET_LAST_USED_EDITOR_PAGE,
|
type: ReduxActionTypes.SET_LAST_USED_EDITOR_PAGE,
|
||||||
|
|
@ -170,6 +163,8 @@ const ApiEditorForm: React.FC<Props> = (props: Props) => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
const allowPostBody =
|
||||||
|
httpMethodFromForm && httpMethodFromForm !== HTTP_METHODS[0];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form onSubmit={handleSubmit}>
|
<Form onSubmit={handleSubmit}>
|
||||||
|
|
@ -219,20 +214,13 @@ const ApiEditorForm: React.FC<Props> = (props: Props) => {
|
||||||
/>
|
/>
|
||||||
<DatasourceWrapper className="t--dataSourceField">
|
<DatasourceWrapper className="t--dataSourceField">
|
||||||
<DatasourcesField
|
<DatasourcesField
|
||||||
name="datasource.id"
|
key={apiId}
|
||||||
|
name="datasource"
|
||||||
pluginId={pluginId}
|
pluginId={pluginId}
|
||||||
|
datasourceFieldText={props.datasourceFieldText}
|
||||||
appName={props.appName}
|
appName={props.appName}
|
||||||
/>
|
/>
|
||||||
</DatasourceWrapper>
|
</DatasourceWrapper>
|
||||||
<DynamicTextField
|
|
||||||
className="t--path"
|
|
||||||
placeholder="v1/method"
|
|
||||||
name="actionConfiguration.path"
|
|
||||||
leftIcon={FormIcons.SLASH_ICON}
|
|
||||||
normalize={value => value.trim()}
|
|
||||||
singleLine
|
|
||||||
setMaxHeight
|
|
||||||
/>
|
|
||||||
</FormRow>
|
</FormRow>
|
||||||
</MainConfiguration>
|
</MainConfiguration>
|
||||||
<SecondaryWrapper>
|
<SecondaryWrapper>
|
||||||
|
|
@ -252,23 +240,23 @@ const ApiEditorForm: React.FC<Props> = (props: Props) => {
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
{" Learn How "}
|
{" Learn How "}
|
||||||
<StyledOpenDocsIcon icon="document-open"></StyledOpenDocsIcon>
|
<StyledOpenDocsIcon icon="document-open" />
|
||||||
</a>
|
</a>
|
||||||
</CollapsibleHelp>
|
</CollapsibleHelp>
|
||||||
<HeadersSection>
|
<HeadersSection>
|
||||||
<KeyValueFieldArray
|
<KeyValueFieldArray
|
||||||
name="actionConfiguration.headers"
|
name="actionConfiguration.headers"
|
||||||
label="Headers"
|
label="Headers"
|
||||||
actionConfig={
|
actionConfig={actionConfigurationHeaders}
|
||||||
actionConfiguration && actionConfigurationHeaders
|
|
||||||
}
|
|
||||||
placeholder="Value"
|
placeholder="Value"
|
||||||
|
dataTreePath={`${actionName}.config.actionConfiguration.headers`}
|
||||||
pushFields
|
pushFields
|
||||||
/>
|
/>
|
||||||
</HeadersSection>
|
</HeadersSection>
|
||||||
<KeyValueFieldArray
|
<KeyValueFieldArray
|
||||||
name="actionConfiguration.queryParameters"
|
name="actionConfiguration.queryParameters"
|
||||||
label="Params"
|
label="Params"
|
||||||
|
dataTreePath={`${actionName}.config.actionConfiguration.queryParameters`}
|
||||||
pushFields
|
pushFields
|
||||||
/>
|
/>
|
||||||
{allowPostBody && (
|
{allowPostBody && (
|
||||||
|
|
@ -276,6 +264,7 @@ const ApiEditorForm: React.FC<Props> = (props: Props) => {
|
||||||
actionConfigurationHeaders={actionConfigurationHeaders}
|
actionConfigurationHeaders={actionConfigurationHeaders}
|
||||||
actionConfiguration={actionConfigurationBody}
|
actionConfiguration={actionConfigurationBody}
|
||||||
change={props.change}
|
change={props.change}
|
||||||
|
dataTreePath={`${actionName}.config.actionConfiguration.body`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</RequestParamsWrapper>
|
</RequestParamsWrapper>
|
||||||
|
|
@ -305,24 +294,19 @@ const selector = formValueSelector(API_EDITOR_FORM_NAME);
|
||||||
|
|
||||||
export default connect(state => {
|
export default connect(state => {
|
||||||
const httpMethodFromForm = selector(state, "actionConfiguration.httpMethod");
|
const httpMethodFromForm = selector(state, "actionConfiguration.httpMethod");
|
||||||
const actionConfiguration = selector(state, "actionConfiguration");
|
|
||||||
const actionConfigurationBody = selector(state, "actionConfiguration.body");
|
const actionConfigurationBody = selector(state, "actionConfiguration.body");
|
||||||
|
const actionName = selector(state, "name");
|
||||||
const actionConfigurationHeaders = selector(
|
const actionConfigurationHeaders = selector(
|
||||||
state,
|
state,
|
||||||
"actionConfiguration.headers",
|
"actionConfiguration.headers",
|
||||||
);
|
);
|
||||||
let contentType;
|
const apiId = selector(state, "id");
|
||||||
if (actionConfigurationHeaders) {
|
|
||||||
contentType = actionConfigurationHeaders.find(
|
|
||||||
(header: any) => header.key.toLowerCase() === CONTENT_TYPE,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
actionName,
|
||||||
|
apiId,
|
||||||
httpMethodFromForm,
|
httpMethodFromForm,
|
||||||
actionConfiguration,
|
|
||||||
actionConfigurationBody,
|
actionConfigurationBody,
|
||||||
contentType,
|
|
||||||
actionConfigurationHeaders,
|
actionConfigurationHeaders,
|
||||||
};
|
};
|
||||||
})(
|
})(
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ import DropdownField from "components/editorComponents/form/fields/DropdownField
|
||||||
import FormRow from "components/editorComponents/FormRow";
|
import FormRow from "components/editorComponents/FormRow";
|
||||||
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
|
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
|
||||||
import CalloutComponent from "components/designSystems/blueprint/CalloutComponent";
|
import CalloutComponent from "components/designSystems/blueprint/CalloutComponent";
|
||||||
|
import { PaginationType } from "entities/Action";
|
||||||
|
|
||||||
interface PaginationProps {
|
interface PaginationProps {
|
||||||
onTestClick: Function;
|
onTestClick: Function;
|
||||||
paginationType: PaginationType;
|
paginationType: PaginationType;
|
||||||
|
|
@ -15,12 +17,6 @@ const PaginationFieldWrapper = styled.div`
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export enum PaginationType {
|
|
||||||
"NONE" = "NONE",
|
|
||||||
"PAGE_NO" = "PAGE_NO",
|
|
||||||
"URL" = "URL",
|
|
||||||
}
|
|
||||||
|
|
||||||
const ExampleApi = styled.p`
|
const ExampleApi = styled.p`
|
||||||
color: #ef7b63;
|
color: #ef7b63;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import KeyValueFieldArray from "components/editorComponents/form/fields/KeyValue
|
||||||
import DynamicTextField from "components/editorComponents/form/fields/DynamicTextField";
|
import DynamicTextField from "components/editorComponents/form/fields/DynamicTextField";
|
||||||
import { AppState } from "reducers";
|
import { AppState } from "reducers";
|
||||||
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||||
|
import FIELD_VALUES from "constants/FieldExpectedValue";
|
||||||
|
|
||||||
const DropDownContainer = styled.div`
|
const DropDownContainer = styled.div`
|
||||||
width: 300px;
|
width: 300px;
|
||||||
|
|
@ -45,6 +46,7 @@ interface PostDataProps {
|
||||||
onDisplayFormatChange: Function;
|
onDisplayFormatChange: Function;
|
||||||
apiId: string;
|
apiId: string;
|
||||||
setDisplayFormat: Function;
|
setDisplayFormat: Function;
|
||||||
|
dataTreePath: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = PostDataProps;
|
type Props = PostDataProps;
|
||||||
|
|
@ -56,6 +58,7 @@ const PostBodyData = (props: Props) => {
|
||||||
displayFormat,
|
displayFormat,
|
||||||
setDisplayFormat,
|
setDisplayFormat,
|
||||||
apiId,
|
apiId,
|
||||||
|
dataTreePath,
|
||||||
} = props;
|
} = props;
|
||||||
return (
|
return (
|
||||||
<PostbodyContainer>
|
<PostbodyContainer>
|
||||||
|
|
@ -110,14 +113,13 @@ const PostBodyData = (props: Props) => {
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<JSONEditorFieldWrapper className={"t--apiFormPostBody"}>
|
<JSONEditorFieldWrapper className={"t--apiFormPostBody"}>
|
||||||
<DynamicTextField
|
<DynamicTextField
|
||||||
|
expected={FIELD_VALUES.API_ACTION.body}
|
||||||
name="actionConfiguration.body[0]"
|
name="actionConfiguration.body[0]"
|
||||||
height={300}
|
height={300}
|
||||||
showLineNumbers
|
showLineNumbers
|
||||||
allowTabIndent
|
allowTabIndent
|
||||||
singleLine={false}
|
singleLine={false}
|
||||||
placeholder={
|
dataTreePath={`${dataTreePath}[0]`}
|
||||||
'Please enter this POST request\'s JSON body.\n\n\nDid you know?\n\tIn Appsmith, we can use a widget\'s or API\'s property dynamically, using {{ }} templates.\n\n\tFor example: If we have an input widget named Input1 in which the user would provide their name \n\tand this post body structure should be { "name": "<text from Input1>" } \n\tWe can access it in this post body using { "name": "{{Input1.text}}" }'
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</JSONEditorFieldWrapper>
|
</JSONEditorFieldWrapper>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|
@ -125,7 +127,11 @@ const PostBodyData = (props: Props) => {
|
||||||
|
|
||||||
{displayFormat?.value === POST_BODY_FORMAT_OPTIONS[1].value && (
|
{displayFormat?.value === POST_BODY_FORMAT_OPTIONS[1].value && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<KeyValueFieldArray name="actionConfiguration.body[1]" label="" />
|
<KeyValueFieldArray
|
||||||
|
name="actionConfiguration.body[1]"
|
||||||
|
dataTreePath={`${dataTreePath}[1]`}
|
||||||
|
label=""
|
||||||
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
@ -137,6 +143,7 @@ const PostBodyData = (props: Props) => {
|
||||||
height={300}
|
height={300}
|
||||||
allowTabIndent
|
allowTabIndent
|
||||||
singleLine={false}
|
singleLine={false}
|
||||||
|
dataTreePath={`${dataTreePath}[2]`}
|
||||||
/>
|
/>
|
||||||
</JSONEditorFieldWrapper>
|
</JSONEditorFieldWrapper>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,7 @@ import styled from "styled-components";
|
||||||
import FormLabel from "components/editorComponents/FormLabel";
|
import FormLabel from "components/editorComponents/FormLabel";
|
||||||
import FormRow from "components/editorComponents/FormRow";
|
import FormRow from "components/editorComponents/FormRow";
|
||||||
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
|
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
|
||||||
import {
|
import { PaginationField, BodyFormData, Property } from "api/ActionAPI";
|
||||||
RestAction,
|
|
||||||
PaginationField,
|
|
||||||
BodyFormData,
|
|
||||||
Property,
|
|
||||||
} from "api/ActionAPI";
|
|
||||||
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||||
import DynamicTextField from "components/editorComponents/form/fields/DynamicTextField";
|
import DynamicTextField from "components/editorComponents/form/fields/DynamicTextField";
|
||||||
import KeyValueFieldArray from "components/editorComponents/form/fields/KeyValueFieldArray";
|
import KeyValueFieldArray from "components/editorComponents/form/fields/KeyValueFieldArray";
|
||||||
|
|
@ -26,7 +21,8 @@ import LoadingOverlayScreen from "components/editorComponents/LoadingOverlayScre
|
||||||
import CredentialsTooltip from "components/editorComponents/form/CredentialsTooltip";
|
import CredentialsTooltip from "components/editorComponents/form/CredentialsTooltip";
|
||||||
import { FormIcons } from "icons/FormIcons";
|
import { FormIcons } from "icons/FormIcons";
|
||||||
import { BaseTabbedView } from "components/designSystems/appsmith/TabbedView";
|
import { BaseTabbedView } from "components/designSystems/appsmith/TabbedView";
|
||||||
import Pagination, { PaginationType } from "./Pagination";
|
import Pagination from "./Pagination";
|
||||||
|
import { PaginationType, RestAction } from "entities/Action";
|
||||||
|
|
||||||
const Form = styled.form`
|
const Form = styled.form`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import {
|
||||||
deleteAction,
|
deleteAction,
|
||||||
updateAction,
|
updateAction,
|
||||||
} from "actions/actionActions";
|
} from "actions/actionActions";
|
||||||
import { PaginationField, RapidApiAction, RestAction } from "api/ActionAPI";
|
import { PaginationField } from "api/ActionAPI";
|
||||||
import { AppState } from "reducers";
|
import { AppState } from "reducers";
|
||||||
import { RouteComponentProps } from "react-router";
|
import { RouteComponentProps } from "react-router";
|
||||||
import { API_EDITOR_FORM_NAME } from "constants/forms";
|
import { API_EDITOR_FORM_NAME } from "constants/forms";
|
||||||
|
|
@ -17,7 +17,6 @@ import {
|
||||||
ActionData,
|
ActionData,
|
||||||
ActionDataState,
|
ActionDataState,
|
||||||
} from "reducers/entityReducers/actionsReducer";
|
} from "reducers/entityReducers/actionsReducer";
|
||||||
import { ApiPaneReduxState } from "reducers/uiReducers/apiPaneReducer";
|
|
||||||
import { REST_PLUGIN_PACKAGE_NAME } from "constants/ApiEditorConstants";
|
import { REST_PLUGIN_PACKAGE_NAME } from "constants/ApiEditorConstants";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { getCurrentApplication } from "selectors/applicationSelectors";
|
import { getCurrentApplication } from "selectors/applicationSelectors";
|
||||||
|
|
@ -26,6 +25,7 @@ import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
import { getActionById, getCurrentPageName } from "selectors/editorSelectors";
|
import { getActionById, getCurrentPageName } from "selectors/editorSelectors";
|
||||||
import { Plugin } from "api/PluginApi";
|
import { Plugin } from "api/PluginApi";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
import { RapidApiAction, RestAction, PaginationType } from "entities/Action";
|
||||||
import FeatureFlag from "utils/featureFlags";
|
import FeatureFlag from "utils/featureFlags";
|
||||||
import { FeatureFlagsEnum } from "configs/types";
|
import { FeatureFlagsEnum } from "configs/types";
|
||||||
|
|
||||||
|
|
@ -39,15 +39,19 @@ const EmptyStateContainer = styled.div`
|
||||||
|
|
||||||
interface ReduxStateProps {
|
interface ReduxStateProps {
|
||||||
actions: ActionDataState;
|
actions: ActionDataState;
|
||||||
apiPane: ApiPaneReduxState;
|
isRunning: Record<string, boolean>;
|
||||||
formData: RestAction;
|
isSaving: Record<string, boolean>;
|
||||||
|
isDeleting: Record<string, boolean>;
|
||||||
|
allowSave: boolean;
|
||||||
|
apiName: string;
|
||||||
currentApplication: UserApplication;
|
currentApplication: UserApplication;
|
||||||
currentPageName: string | undefined;
|
currentPageName: string | undefined;
|
||||||
pages: any;
|
pages: any;
|
||||||
plugins: Plugin[];
|
plugins: Plugin[];
|
||||||
pluginId: any;
|
pluginId: any;
|
||||||
apiAction: RestAction | ActionData | RapidApiAction | undefined;
|
apiAction: RestAction | ActionData | RapidApiAction | undefined;
|
||||||
data: RestAction | ActionData | RapidApiAction | undefined;
|
paginationType: PaginationType;
|
||||||
|
datasourceFieldText: string;
|
||||||
}
|
}
|
||||||
interface ReduxActionProps {
|
interface ReduxActionProps {
|
||||||
submitForm: (name: string) => void;
|
submitForm: (name: string) => void;
|
||||||
|
|
@ -67,35 +71,42 @@ type Props = ReduxActionProps &
|
||||||
|
|
||||||
class ApiEditor extends React.Component<Props> {
|
class ApiEditor extends React.Component<Props> {
|
||||||
handleSubmit = (values: RestAction) => {
|
handleSubmit = (values: RestAction) => {
|
||||||
const { formData } = this.props;
|
this.props.updateAction(values);
|
||||||
this.props.updateAction(formData);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handleSaveClick = () => {
|
handleSaveClick = () => {
|
||||||
const pageName = getPageName(this.props.pages, this.props.formData.pageId);
|
const pageName = getPageName(
|
||||||
|
this.props.pages,
|
||||||
|
this.props.match.params.pageId,
|
||||||
|
);
|
||||||
AnalyticsUtil.logEvent("SAVE_API_CLICK", {
|
AnalyticsUtil.logEvent("SAVE_API_CLICK", {
|
||||||
apiName: this.props.formData.name,
|
apiName: this.props.apiName,
|
||||||
apiID: this.props.match.params.apiId,
|
apiID: this.props.match.params.apiId,
|
||||||
pageName: pageName,
|
pageName: pageName,
|
||||||
});
|
});
|
||||||
this.props.submitForm(API_EDITOR_FORM_NAME);
|
this.props.submitForm(API_EDITOR_FORM_NAME);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleDeleteClick = () => {
|
handleDeleteClick = () => {
|
||||||
const pageName = getPageName(this.props.pages, this.props.formData.pageId);
|
const pageName = getPageName(
|
||||||
|
this.props.pages,
|
||||||
|
this.props.match.params.pageId,
|
||||||
|
);
|
||||||
AnalyticsUtil.logEvent("DELETE_API_CLICK", {
|
AnalyticsUtil.logEvent("DELETE_API_CLICK", {
|
||||||
apiName: this.props.formData.name,
|
apiName: this.props.apiName,
|
||||||
apiID: this.props.match.params.apiId,
|
apiID: this.props.match.params.apiId,
|
||||||
pageName: pageName,
|
pageName: pageName,
|
||||||
});
|
});
|
||||||
this.props.deleteAction(
|
this.props.deleteAction(this.props.match.params.apiId, this.props.apiName);
|
||||||
this.props.match.params.apiId,
|
|
||||||
this.props.formData.name,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
handleRunClick = (paginationField?: PaginationField) => {
|
handleRunClick = (paginationField?: PaginationField) => {
|
||||||
const pageName = getPageName(this.props.pages, this.props.formData.pageId);
|
const pageName = getPageName(
|
||||||
|
this.props.pages,
|
||||||
|
this.props.match.params.pageId,
|
||||||
|
);
|
||||||
AnalyticsUtil.logEvent("RUN_API_CLICK", {
|
AnalyticsUtil.logEvent("RUN_API_CLICK", {
|
||||||
apiName: this.props.formData.name,
|
apiName: this.props.apiName,
|
||||||
apiID: this.props.match.params.apiId,
|
apiID: this.props.match.params.apiId,
|
||||||
pageName: pageName,
|
pageName: pageName,
|
||||||
});
|
});
|
||||||
|
|
@ -130,13 +141,16 @@ class ApiEditor extends React.Component<Props> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
apiPane,
|
|
||||||
match: {
|
match: {
|
||||||
params: { apiId },
|
params: { apiId },
|
||||||
},
|
},
|
||||||
plugins,
|
plugins,
|
||||||
pluginId,
|
pluginId,
|
||||||
data,
|
isSaving,
|
||||||
|
isRunning,
|
||||||
|
isDeleting,
|
||||||
|
allowSave,
|
||||||
|
paginationType,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let formUiComponent: string | undefined;
|
let formUiComponent: string | undefined;
|
||||||
|
|
@ -148,8 +162,6 @@ class ApiEditor extends React.Component<Props> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { isSaving, isRunning, isDeleting, drafts } = apiPane;
|
|
||||||
const paginationType = _.get(data, "actionConfiguration.paginationType");
|
|
||||||
const apiHomeScreen = (
|
const apiHomeScreen = (
|
||||||
<ApiHomeScreen
|
<ApiHomeScreen
|
||||||
applicationId={this.props.match.params.applicationId}
|
applicationId={this.props.match.params.applicationId}
|
||||||
|
|
@ -177,7 +189,7 @@ class ApiEditor extends React.Component<Props> {
|
||||||
{formUiComponent === "ApiEditorForm" && (
|
{formUiComponent === "ApiEditorForm" && (
|
||||||
<ApiEditorForm
|
<ApiEditorForm
|
||||||
pluginId={pluginId}
|
pluginId={pluginId}
|
||||||
allowSave={apiId in drafts}
|
allowSave={allowSave}
|
||||||
paginationType={paginationType}
|
paginationType={paginationType}
|
||||||
isSaving={isSaving[apiId]}
|
isSaving={isSaving[apiId]}
|
||||||
isRunning={isRunning[apiId]}
|
isRunning={isRunning[apiId]}
|
||||||
|
|
@ -186,6 +198,7 @@ class ApiEditor extends React.Component<Props> {
|
||||||
onSaveClick={this.handleSaveClick}
|
onSaveClick={this.handleSaveClick}
|
||||||
onDeleteClick={this.handleDeleteClick}
|
onDeleteClick={this.handleDeleteClick}
|
||||||
onRunClick={this.handleRunClick}
|
onRunClick={this.handleRunClick}
|
||||||
|
datasourceFieldText={this.props.datasourceFieldText}
|
||||||
appName={
|
appName={
|
||||||
this.props.currentApplication
|
this.props.currentApplication
|
||||||
? this.props.currentApplication.name
|
? this.props.currentApplication.name
|
||||||
|
|
@ -197,7 +210,7 @@ class ApiEditor extends React.Component<Props> {
|
||||||
|
|
||||||
{formUiComponent === "RapidApiEditorForm" && (
|
{formUiComponent === "RapidApiEditorForm" && (
|
||||||
<RapidApiEditorForm
|
<RapidApiEditorForm
|
||||||
allowSave={apiId in drafts}
|
allowSave={allowSave}
|
||||||
paginationType={paginationType}
|
paginationType={paginationType}
|
||||||
isSaving={isSaving[apiId]}
|
isSaving={isSaving[apiId]}
|
||||||
isRunning={isRunning[apiId]}
|
isRunning={isRunning[apiId]}
|
||||||
|
|
@ -227,25 +240,34 @@ const mapStateToProps = (state: AppState, props: any): ReduxStateProps => {
|
||||||
const formData = getFormValues(API_EDITOR_FORM_NAME)(state) as RestAction;
|
const formData = getFormValues(API_EDITOR_FORM_NAME)(state) as RestAction;
|
||||||
const apiAction = getActionById(state, props);
|
const apiAction = getActionById(state, props);
|
||||||
|
|
||||||
const { drafts } = state.ui.apiPane;
|
const { drafts, isSaving, isDeleting, isRunning } = state.ui.apiPane;
|
||||||
let data: RestAction | ActionData | RapidApiAction | undefined;
|
let data: RestAction | ActionData | RapidApiAction | undefined;
|
||||||
|
let allowSave;
|
||||||
if (apiAction && apiAction.id in drafts) {
|
if (apiAction && apiAction.id in drafts) {
|
||||||
data = drafts[apiAction.id];
|
data = drafts[apiAction.id];
|
||||||
|
allowSave = true;
|
||||||
} else {
|
} else {
|
||||||
data = apiAction;
|
data = apiAction;
|
||||||
|
allowSave = false;
|
||||||
}
|
}
|
||||||
|
const datasourceFieldText =
|
||||||
|
state.ui.apiPane.datasourceFieldText[formData?.id ?? ""] || "";
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
datasourceFieldText,
|
||||||
actions: state.entities.actions,
|
actions: state.entities.actions,
|
||||||
apiPane: state.ui.apiPane,
|
|
||||||
currentApplication: getCurrentApplication(state),
|
currentApplication: getCurrentApplication(state),
|
||||||
currentPageName: getCurrentPageName(state),
|
currentPageName: getCurrentPageName(state),
|
||||||
pages: state.entities.pageList.pages,
|
pages: state.entities.pageList.pages,
|
||||||
formData,
|
apiName: formData?.name || "",
|
||||||
data,
|
|
||||||
plugins: state.entities.plugins.list,
|
plugins: state.entities.plugins.list,
|
||||||
pluginId: _.get(data, "pluginId"),
|
pluginId: _.get(data, "pluginId"),
|
||||||
|
paginationType: _.get(data, "actionConfiguration.paginationType"),
|
||||||
apiAction,
|
apiAction,
|
||||||
|
isSaving,
|
||||||
|
isRunning,
|
||||||
|
isDeleting,
|
||||||
|
allowSave,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,11 @@ import {
|
||||||
createNewApiAction,
|
createNewApiAction,
|
||||||
initApiPane,
|
initApiPane,
|
||||||
} from "actions/apiPaneActions";
|
} from "actions/apiPaneActions";
|
||||||
import { RestAction } from "api/ActionAPI";
|
|
||||||
import EditorSidebar from "pages/Editor/EditorSidebar";
|
import EditorSidebar from "pages/Editor/EditorSidebar";
|
||||||
import { getNextEntityName } from "utils/AppsmithUtils";
|
import { getNextEntityName } from "utils/AppsmithUtils";
|
||||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
import { Page } from "constants/ReduxActionConstants";
|
import { Page } from "constants/ReduxActionConstants";
|
||||||
|
import { RestAction } from "entities/Action";
|
||||||
import { FeatureFlagsEnum } from "configs/types";
|
import { FeatureFlagsEnum } from "configs/types";
|
||||||
import FeatureFlag from "utils/featureFlags";
|
import FeatureFlag from "utils/featureFlags";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ interface DatasourceDBEditorProps {
|
||||||
isTesting: boolean;
|
isTesting: boolean;
|
||||||
loadingFormConfigs: boolean;
|
loadingFormConfigs: boolean;
|
||||||
formConfig: [];
|
formConfig: [];
|
||||||
|
isNewDatasource: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DatasourceDBEditorState {
|
interface DatasourceDBEditorState {
|
||||||
|
|
@ -327,7 +328,7 @@ class DatasourceDBEditor extends React.Component<
|
||||||
<Field
|
<Field
|
||||||
name="name"
|
name="name"
|
||||||
component={FormTitle}
|
component={FormTitle}
|
||||||
focusOnMount={this.isNewDatasource()}
|
focusOnMount={this.props.isNewDatasource}
|
||||||
/>
|
/>
|
||||||
</FormTitleContainer>
|
</FormTitleContainer>
|
||||||
{!_.isNil(sections)
|
{!_.isNil(sections)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ interface ReduxStateProps {
|
||||||
formConfig: [];
|
formConfig: [];
|
||||||
loadingFormConfigs: boolean;
|
loadingFormConfigs: boolean;
|
||||||
isDeleting: boolean;
|
isDeleting: boolean;
|
||||||
|
newDatasource: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Props = ReduxStateProps &
|
type Props = ReduxStateProps &
|
||||||
|
|
@ -58,6 +59,7 @@ class DataSourceEditor extends React.Component<Props> {
|
||||||
formConfig,
|
formConfig,
|
||||||
isDeleting,
|
isDeleting,
|
||||||
deleteDatasource,
|
deleteDatasource,
|
||||||
|
newDatasource,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -69,6 +71,7 @@ class DataSourceEditor extends React.Component<Props> {
|
||||||
isSaving={isSaving}
|
isSaving={isSaving}
|
||||||
isTesting={isTesting}
|
isTesting={isTesting}
|
||||||
isDeleting={isDeleting}
|
isDeleting={isDeleting}
|
||||||
|
isNewDatasource={newDatasource === datasourceId}
|
||||||
onSubmit={this.handleSubmit}
|
onSubmit={this.handleSubmit}
|
||||||
onSave={this.handleSave}
|
onSave={this.handleSave}
|
||||||
onTest={this.props.testDatasource}
|
onTest={this.props.testDatasource}
|
||||||
|
|
@ -112,6 +115,7 @@ const mapStateToProps = (state: AppState): ReduxStateProps => {
|
||||||
isTesting: datasources.isTesting,
|
isTesting: datasources.isTesting,
|
||||||
formConfig: formConfigs[datasourcePane.selectedPlugin] || [],
|
formConfig: formConfigs[datasourcePane.selectedPlugin] || [],
|
||||||
loadingFormConfigs,
|
loadingFormConfigs,
|
||||||
|
newDatasource: datasourcePane.newDatasource,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,47 +1,20 @@
|
||||||
import React, { useRef, useEffect } from "react";
|
import React, { useRef, useEffect } from "react";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { createPortal } from "react-dom";
|
import { createPortal } from "react-dom";
|
||||||
import PopperJS from "popper.js";
|
import PopperJS, { Placement, PopperOptions } from "popper.js";
|
||||||
import PaneWrapper from "pages/common/PaneWrapper";
|
|
||||||
import { getColorWithOpacity } from "constants/DefaultTheme";
|
|
||||||
type PopperProps = {
|
type PopperProps = {
|
||||||
|
zIndex: number;
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
targetNode?: Element;
|
targetNode?: Element;
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
|
placement: Placement;
|
||||||
|
modifiers?: Partial<PopperOptions["modifiers"]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const PopperWrapper = styled(PaneWrapper)`
|
const PopperWrapper = styled.div<{ zIndex: number }>`
|
||||||
|
z-index: ${props => props.zIndex};
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 3;
|
|
||||||
max-height: ${props => props.theme.propertyPane.height}px;
|
|
||||||
width: ${props => props.theme.propertyPane.width}px;
|
|
||||||
margin: ${props => props.theme.spaces[2]}px;
|
|
||||||
box-shadow: 0px 0px 10px ${props => props.theme.colors.paneCard};
|
|
||||||
border: ${props => props.theme.spaces[5]}px solid
|
|
||||||
${props => props.theme.colors.paneBG};
|
|
||||||
border-right: 0;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden;
|
|
||||||
padding: 0 ${props => props.theme.spaces[5]}px 0 0;
|
|
||||||
text-transform: none;
|
|
||||||
|
|
||||||
scrollbar-color: ${props => props.theme.colors.paneCard}
|
|
||||||
${props => props.theme.colors.paneBG};
|
|
||||||
scrollbar-width: thin;
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
width: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
box-shadow: inset 0 0 6px
|
|
||||||
${props => getColorWithOpacity(props.theme.colors.paneBG, 0.3)};
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
background-color: ${props => props.theme.colors.paneCard};
|
|
||||||
outline: 1px solid ${props => props.theme.paneText};
|
|
||||||
border-radius: ${props => props.theme.radii[1]}px;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
/* eslint-disable react/display-name */
|
/* eslint-disable react/display-name */
|
||||||
|
|
@ -64,7 +37,7 @@ export default (props: PopperProps) => {
|
||||||
props.targetNode,
|
props.targetNode,
|
||||||
(contentRef.current as unknown) as Element,
|
(contentRef.current as unknown) as Element,
|
||||||
{
|
{
|
||||||
placement: "right-start",
|
placement: props.placement,
|
||||||
modifiers: {
|
modifiers: {
|
||||||
flip: {
|
flip: {
|
||||||
behavior: ["right", "left", "bottom", "top"],
|
behavior: ["right", "left", "bottom", "top"],
|
||||||
|
|
@ -79,6 +52,7 @@ export default (props: PopperProps) => {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
boundariesElement: "viewport",
|
boundariesElement: "viewport",
|
||||||
},
|
},
|
||||||
|
...props.modifiers,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -87,9 +61,11 @@ export default (props: PopperProps) => {
|
||||||
_popper.destroy();
|
_popper.destroy();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [props.targetNode, props.isOpen]);
|
}, [props.targetNode, props.isOpen, props.modifiers, props.placement]);
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<PopperWrapper ref={contentRef}>{props.children}</PopperWrapper>,
|
<PopperWrapper ref={contentRef} zIndex={props.zIndex}>
|
||||||
|
{props.children}
|
||||||
|
</PopperWrapper>,
|
||||||
document.body,
|
document.body,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import PropertyControlFactory from "utils/PropertyControlFactory";
|
||||||
import { WidgetProps } from "widgets/BaseWidget";
|
import { WidgetProps } from "widgets/BaseWidget";
|
||||||
import { PropertyControlPropsType } from "components/propertyControls";
|
import { PropertyControlPropsType } from "components/propertyControls";
|
||||||
import { Tooltip, Position } from "@blueprintjs/core";
|
import { Tooltip, Position } from "@blueprintjs/core";
|
||||||
|
import FIELD_EXPECTED_VALUE from "constants/FieldExpectedValue";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
widgetProperties: WidgetProps;
|
widgetProperties: WidgetProps;
|
||||||
|
|
@ -63,7 +64,7 @@ function UnderlinedLabel({
|
||||||
}
|
}
|
||||||
: {}
|
: {}
|
||||||
}
|
}
|
||||||
></span>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
|
@ -98,8 +99,16 @@ const PropertyControl = (props: Props) => {
|
||||||
const { propertyName, label } = propertyConfig;
|
const { propertyName, label } = propertyConfig;
|
||||||
if (widgetProperties) {
|
if (widgetProperties) {
|
||||||
const propertyValue = widgetProperties[propertyName];
|
const propertyValue = widgetProperties[propertyName];
|
||||||
const validation = getPropertyValidation(propertyName);
|
const dataTreePath = `${widgetProperties.widgetName}.evaluatedValues.${propertyName}`;
|
||||||
const config = { ...propertyConfig, ...validation, propertyValue };
|
const { isValid, validationMessage } = getPropertyValidation(propertyName);
|
||||||
|
const config = {
|
||||||
|
...propertyConfig,
|
||||||
|
isValid,
|
||||||
|
propertyValue,
|
||||||
|
validationMessage,
|
||||||
|
dataTreePath,
|
||||||
|
expected: FIELD_EXPECTED_VALUE[widgetProperties.type][propertyName],
|
||||||
|
};
|
||||||
const isDynamic: boolean = _.get(
|
const isDynamic: boolean = _.get(
|
||||||
widgetProperties,
|
widgetProperties,
|
||||||
["dynamicProperties", propertyName],
|
["dynamicProperties", propertyName],
|
||||||
|
|
@ -122,10 +131,7 @@ const PropertyControl = (props: Props) => {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ControlPropertyLabelContainer>
|
<ControlPropertyLabelContainer>
|
||||||
<UnderlinedLabel
|
<UnderlinedLabel tooltip={propertyConfig.helpText} label={label} />
|
||||||
tooltip={propertyConfig.helpText}
|
|
||||||
label={label}
|
|
||||||
></UnderlinedLabel>
|
|
||||||
|
|
||||||
{isConvertible && (
|
{isConvertible && (
|
||||||
<JSToggleButton
|
<JSToggleButton
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,13 @@ import { generateClassName } from "utils/generators";
|
||||||
import { RenderModes } from "constants/WidgetConstants";
|
import { RenderModes } from "constants/WidgetConstants";
|
||||||
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||||
import { CloseButton } from "components/designSystems/blueprint/CloseButton";
|
import { CloseButton } from "components/designSystems/blueprint/CloseButton";
|
||||||
import { theme } from "constants/DefaultTheme";
|
import { getColorWithOpacity, theme } from "constants/DefaultTheme";
|
||||||
import { WidgetProps } from "widgets/BaseWidget";
|
import { WidgetProps } from "widgets/BaseWidget";
|
||||||
import PropertyPaneTitle from "pages/Editor/PropertyPaneTitle";
|
import PropertyPaneTitle from "pages/Editor/PropertyPaneTitle";
|
||||||
import PropertyControl from "pages/Editor/PropertyPane/PropertyControl";
|
import PropertyControl from "pages/Editor/PropertyPane/PropertyControl";
|
||||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
import * as log from "loglevel";
|
import * as log from "loglevel";
|
||||||
|
import PaneWrapper from "pages/common/PaneWrapper";
|
||||||
import { BindingText } from "pages/Editor/APIEditor/Form";
|
import { BindingText } from "pages/Editor/APIEditor/Form";
|
||||||
|
|
||||||
const PropertySectionLabel = styled.div`
|
const PropertySectionLabel = styled.div`
|
||||||
|
|
@ -40,9 +41,38 @@ const PropertySectionLabel = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const PropertyPaneWrapper = styled.div`
|
const PropertyPaneWrapper = styled(PaneWrapper)`
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-height: ${props => props.theme.propertyPane.height}px;
|
||||||
|
width: ${props => props.theme.propertyPane.width}px;
|
||||||
|
margin: ${props => props.theme.spaces[2]}px;
|
||||||
|
box-shadow: 0px 0px 10px ${props => props.theme.colors.paneCard};
|
||||||
|
border: ${props => props.theme.spaces[5]}px solid
|
||||||
|
${props => props.theme.colors.paneBG};
|
||||||
|
border-right: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding: 0 ${props => props.theme.spaces[5]}px 0 0;
|
||||||
|
text-transform: none;
|
||||||
|
|
||||||
|
scrollbar-color: ${props => props.theme.colors.paneCard}
|
||||||
|
${props => props.theme.colors.paneBG};
|
||||||
|
scrollbar-width: thin;
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
box-shadow: inset 0 0 6px
|
||||||
|
${props => getColorWithOpacity(props.theme.colors.paneBG, 0.3)};
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background-color: ${props => props.theme.colors.paneCard};
|
||||||
|
outline: 1px solid ${props => props.theme.paneText};
|
||||||
|
border-radius: ${props => props.theme.radii[1]}px;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledToolTip = styled(Tooltip)`
|
const StyledToolTip = styled(Tooltip)`
|
||||||
|
|
@ -66,7 +96,12 @@ class PropertyPane extends Component<
|
||||||
generateClassName(this.props.widgetId),
|
generateClassName(this.props.widgetId),
|
||||||
)[0];
|
)[0];
|
||||||
return (
|
return (
|
||||||
<Popper isOpen={true} targetNode={el}>
|
<Popper
|
||||||
|
isOpen={true}
|
||||||
|
targetNode={el}
|
||||||
|
zIndex={3}
|
||||||
|
placement="right-start"
|
||||||
|
>
|
||||||
{content}
|
{content}
|
||||||
</Popper>
|
</Popper>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,12 @@ import TextField from "components/editorComponents/form/fields/TextField";
|
||||||
import DropdownField from "components/editorComponents/form/fields/DropdownField";
|
import DropdownField from "components/editorComponents/form/fields/DropdownField";
|
||||||
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
|
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
|
||||||
import { Datasource } from "api/DatasourcesApi";
|
import { Datasource } from "api/DatasourcesApi";
|
||||||
import { RestAction } from "api/ActionAPI";
|
|
||||||
import { QUERY_EDITOR_FORM_NAME } from "constants/forms";
|
import { QUERY_EDITOR_FORM_NAME } from "constants/forms";
|
||||||
import { PLUGIN_PACKAGE_POSTGRES } from "constants/QueryEditorConstants";
|
import { PLUGIN_PACKAGE_POSTGRES } from "constants/QueryEditorConstants";
|
||||||
import "@syncfusion/ej2-react-grids/styles/material.css";
|
import "@syncfusion/ej2-react-grids/styles/material.css";
|
||||||
import { Colors } from "constants/Colors";
|
import { Colors } from "constants/Colors";
|
||||||
import JSONViewer from "./JSONViewer";
|
import JSONViewer from "./JSONViewer";
|
||||||
|
import { RestAction } from "entities/Action";
|
||||||
|
|
||||||
const QueryFormContainer = styled.div`
|
const QueryFormContainer = styled.div`
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import {
|
||||||
} from "selectors/entitiesSelector";
|
} from "selectors/entitiesSelector";
|
||||||
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
|
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
|
||||||
import { Datasource } from "api/DatasourcesApi";
|
import { Datasource } from "api/DatasourcesApi";
|
||||||
import { RestAction } from "api/ActionAPI";
|
|
||||||
import history from "utils/history";
|
import history from "utils/history";
|
||||||
import { createActionRequest } from "actions/actionActions";
|
import { createActionRequest } from "actions/actionActions";
|
||||||
import {
|
import {
|
||||||
|
|
@ -29,6 +28,7 @@ import {
|
||||||
DATA_SOURCES_EDITOR_URL,
|
DATA_SOURCES_EDITOR_URL,
|
||||||
} from "constants/routes";
|
} from "constants/routes";
|
||||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
|
import { QueryAction } from "entities/Action";
|
||||||
|
|
||||||
const QueryHomePage = styled.div`
|
const QueryHomePage = styled.div`
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
|
@ -116,7 +116,7 @@ type QueryHomeScreenProps = {
|
||||||
dataSources: Datasource[];
|
dataSources: Datasource[];
|
||||||
applicationId: string;
|
applicationId: string;
|
||||||
pageId: string;
|
pageId: string;
|
||||||
createAction: (data: Partial<RestAction>) => void;
|
createAction: (data: Partial<QueryAction>) => void;
|
||||||
actions: ActionDataState;
|
actions: ActionDataState;
|
||||||
pluginIds: Array<string> | undefined;
|
pluginIds: Array<string> | undefined;
|
||||||
isCreating: boolean;
|
isCreating: boolean;
|
||||||
|
|
@ -240,7 +240,7 @@ class QueryHomeScreen extends React.Component<QueryHomeScreenProps> {
|
||||||
src={this.getImageSrc(dataSource)}
|
src={this.getImageSrc(dataSource)}
|
||||||
className="dataSourceImage"
|
className="dataSourceImage"
|
||||||
alt="Datasource"
|
alt="Datasource"
|
||||||
></img>
|
/>
|
||||||
|
|
||||||
<p
|
<p
|
||||||
className="textBtn t--datasource-name"
|
className="textBtn t--datasource-name"
|
||||||
|
|
@ -268,7 +268,7 @@ const mapStateToProps = (state: AppState) => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch: any) => ({
|
const mapDispatchToProps = (dispatch: any) => ({
|
||||||
createAction: (data: Partial<RestAction>) => {
|
createAction: (data: Partial<QueryAction>) => {
|
||||||
dispatch(createActionRequest(data));
|
dispatch(createActionRequest(data));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ import { AppState } from "reducers";
|
||||||
import { getDataSources } from "selectors/editorSelectors";
|
import { getDataSources } from "selectors/editorSelectors";
|
||||||
import { QUERY_EDITOR_FORM_NAME } from "constants/forms";
|
import { QUERY_EDITOR_FORM_NAME } from "constants/forms";
|
||||||
import { Datasource } from "api/DatasourcesApi";
|
import { Datasource } from "api/DatasourcesApi";
|
||||||
import { RestAction } from "api/ActionAPI";
|
|
||||||
import { QueryPaneReduxState } from "reducers/uiReducers/queryPaneReducer";
|
import { QueryPaneReduxState } from "reducers/uiReducers/queryPaneReducer";
|
||||||
import {
|
import {
|
||||||
getPluginIdsOfPackageNames,
|
getPluginIdsOfPackageNames,
|
||||||
|
|
@ -27,6 +26,7 @@ import {
|
||||||
import { PLUGIN_PACKAGE_DBS } from "constants/QueryEditorConstants";
|
import { PLUGIN_PACKAGE_DBS } from "constants/QueryEditorConstants";
|
||||||
import { getCurrentApplication } from "selectors/applicationSelectors";
|
import { getCurrentApplication } from "selectors/applicationSelectors";
|
||||||
import { ApiPaneReduxState } from "reducers/uiReducers/apiPaneReducer";
|
import { ApiPaneReduxState } from "reducers/uiReducers/apiPaneReducer";
|
||||||
|
import { RestAction } from "entities/Action";
|
||||||
|
|
||||||
const EmptyStateContainer = styled.div`
|
const EmptyStateContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,12 @@ import {
|
||||||
copyActionRequest,
|
copyActionRequest,
|
||||||
} from "actions/actionActions";
|
} from "actions/actionActions";
|
||||||
import { deleteQuery } from "actions/queryPaneActions";
|
import { deleteQuery } from "actions/queryPaneActions";
|
||||||
import { RestAction } from "api/ActionAPI";
|
|
||||||
import { changeQuery, initQueryPane } from "actions/queryPaneActions";
|
import { changeQuery, initQueryPane } from "actions/queryPaneActions";
|
||||||
import { getQueryActions } from "selectors/entitiesSelector";
|
import { getQueryActions } from "selectors/entitiesSelector";
|
||||||
import { getNextEntityName } from "utils/AppsmithUtils";
|
import { getNextEntityName } from "utils/AppsmithUtils";
|
||||||
import { getDataSources } from "selectors/editorSelectors";
|
import { getDataSources } from "selectors/editorSelectors";
|
||||||
import { QUERY_EDITOR_URL_WITH_SELECTED_PAGE_ID } from "constants/routes";
|
import { QUERY_EDITOR_URL_WITH_SELECTED_PAGE_ID } from "constants/routes";
|
||||||
|
import { RestAction } from "entities/Action";
|
||||||
|
|
||||||
const ActionItem = styled.div`
|
const ActionItem = styled.div`
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ export type CustomizedDropdownProps = {
|
||||||
openOnHover?: boolean;
|
openOnHover?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getIcon = (icon?: string, intent?: Intent) => {
|
export const getIcon = (icon?: string, intent?: Intent) => {
|
||||||
if (icon) {
|
if (icon) {
|
||||||
if (MenuIcons[icon]) {
|
if (MenuIcons[icon]) {
|
||||||
return MenuIcons[icon]({
|
return MenuIcons[icon]({
|
||||||
|
|
@ -68,7 +68,7 @@ const getIcon = (icon?: string, intent?: Intent) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getContentSection = (section: CustomizedDropdownOptionSection) => {
|
export const getContentSection = (section: CustomizedDropdownOptionSection) => {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{section.options &&
|
{section.options &&
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { createReducer } from "utils/AppsmithUtils";
|
||||||
|
import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||||
|
import { Action, RestAction } from "entities/Action";
|
||||||
|
import _ from "lodash";
|
||||||
|
import { ApiPaneReduxState } from "reducers/uiReducers/apiPaneReducer";
|
||||||
|
|
||||||
|
export type ActionDraftsState = Record<string, Action>;
|
||||||
|
|
||||||
|
const initialState: ActionDraftsState = {};
|
||||||
|
|
||||||
|
const actionDraftsReducer = createReducer(initialState, {
|
||||||
|
[ReduxActionTypes.UPDATE_API_DRAFT]: (
|
||||||
|
state: ApiPaneReduxState,
|
||||||
|
action: ReduxAction<{ id: string; draft: Partial<RestAction> }>,
|
||||||
|
) => ({
|
||||||
|
...state,
|
||||||
|
[action.payload.id]: action.payload.draft,
|
||||||
|
}),
|
||||||
|
[ReduxActionTypes.DELETE_API_DRAFT]: (
|
||||||
|
state: ApiPaneReduxState,
|
||||||
|
action: ReduxAction<{ id: string }>,
|
||||||
|
) => _.omit(state, action.payload.id),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default actionDraftsReducer;
|
||||||
|
|
@ -4,9 +4,10 @@ import {
|
||||||
ReduxAction,
|
ReduxAction,
|
||||||
ReduxActionErrorTypes,
|
ReduxActionErrorTypes,
|
||||||
} from "constants/ReduxActionConstants";
|
} from "constants/ReduxActionConstants";
|
||||||
import { ActionResponse, RapidApiAction, RestAction } from "api/ActionAPI";
|
import { ActionResponse } from "api/ActionAPI";
|
||||||
import { ExecuteErrorPayload } from "constants/ActionConstants";
|
import { ExecuteErrorPayload } from "constants/ActionConstants";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { RapidApiAction, RestAction } from "entities/Action";
|
||||||
export interface ActionData {
|
export interface ActionData {
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
config: RestAction | RapidApiAction;
|
config: RestAction | RapidApiAction;
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,14 @@ import pageListReducer from "./pageListReducer";
|
||||||
import jsExecutionsReducer from "./jsExecutionsReducer";
|
import jsExecutionsReducer from "./jsExecutionsReducer";
|
||||||
import pluginsReducer from "reducers/entityReducers/pluginsReducer";
|
import pluginsReducer from "reducers/entityReducers/pluginsReducer";
|
||||||
import metaReducer from "./metaReducer";
|
import metaReducer from "./metaReducer";
|
||||||
|
import actionDraftsReducer from "reducers/entityReducers/actionDraftsReducer";
|
||||||
|
|
||||||
const entityReducer = combineReducers({
|
const entityReducer = combineReducers({
|
||||||
canvasWidgets: canvasWidgetsReducer,
|
canvasWidgets: canvasWidgetsReducer,
|
||||||
queryData: queryDataReducer,
|
queryData: queryDataReducer,
|
||||||
widgetConfig: widgetConfigReducer,
|
widgetConfig: widgetConfigReducer,
|
||||||
actions: actionsReducer,
|
actions: actionsReducer,
|
||||||
|
actionDrafts: actionDraftsReducer,
|
||||||
propertyConfig: propertyPaneConfigReducer,
|
propertyConfig: propertyPaneConfigReducer,
|
||||||
datasources: datasourceReducer,
|
datasources: datasourceReducer,
|
||||||
pageList: pageListReducer,
|
pageList: pageListReducer,
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import { ImportedCollectionsReduxState } from "reducers/uiReducers/importedColle
|
||||||
import { ProvidersReduxState } from "reducers/uiReducers/providerReducer";
|
import { ProvidersReduxState } from "reducers/uiReducers/providerReducer";
|
||||||
import { MetaState } from "./entityReducers/metaReducer";
|
import { MetaState } from "./entityReducers/metaReducer";
|
||||||
import { ImportReduxState } from "reducers/uiReducers/importReducer";
|
import { ImportReduxState } from "reducers/uiReducers/importReducer";
|
||||||
|
import { ActionDraftsState } from "reducers/entityReducers/actionDraftsReducer";
|
||||||
import { HelpReduxState } from "./uiReducers/helpReducer";
|
import { HelpReduxState } from "./uiReducers/helpReducer";
|
||||||
|
|
||||||
const appReducer = combineReducers({
|
const appReducer = combineReducers({
|
||||||
|
|
@ -61,6 +62,7 @@ export interface AppState {
|
||||||
canvasWidgets: CanvasWidgetsReduxState;
|
canvasWidgets: CanvasWidgetsReduxState;
|
||||||
queryData: QueryDataState;
|
queryData: QueryDataState;
|
||||||
actions: ActionDataState;
|
actions: ActionDataState;
|
||||||
|
actionDrafts: ActionDraftsState;
|
||||||
propertyConfig: PropertyPaneConfigState;
|
propertyConfig: PropertyPaneConfigState;
|
||||||
widgetConfig: WidgetConfigReducerState;
|
widgetConfig: WidgetConfigReducerState;
|
||||||
datasources: DatasourceDataState;
|
datasources: DatasourceDataState;
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import {
|
||||||
ReduxActionErrorTypes,
|
ReduxActionErrorTypes,
|
||||||
ReduxAction,
|
ReduxAction,
|
||||||
} from "constants/ReduxActionConstants";
|
} from "constants/ReduxActionConstants";
|
||||||
import { RestAction } from "api/ActionAPI";
|
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { RestAction } from "entities/Action";
|
||||||
|
|
||||||
const initialState: ApiPaneReduxState = {
|
const initialState: ApiPaneReduxState = {
|
||||||
lastUsed: "",
|
lastUsed: "",
|
||||||
|
|
@ -18,6 +18,7 @@ const initialState: ApiPaneReduxState = {
|
||||||
lastUsedEditorPage: "",
|
lastUsedEditorPage: "",
|
||||||
lastSelectedPage: "",
|
lastSelectedPage: "",
|
||||||
extraformData: {},
|
extraformData: {},
|
||||||
|
datasourceFieldText: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ApiPaneReduxState {
|
export interface ApiPaneReduxState {
|
||||||
|
|
@ -29,6 +30,7 @@ export interface ApiPaneReduxState {
|
||||||
isDeleting: Record<string, boolean>;
|
isDeleting: Record<string, boolean>;
|
||||||
currentCategory: string;
|
currentCategory: string;
|
||||||
lastUsedEditorPage: string;
|
lastUsedEditorPage: string;
|
||||||
|
datasourceFieldText: Record<string, string>;
|
||||||
lastSelectedPage: string;
|
lastSelectedPage: string;
|
||||||
extraformData: Record<string, any>;
|
extraformData: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
@ -201,6 +203,19 @@ const apiPaneReducer = createReducer(initialState, {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
[ReduxActionTypes.SET_DATASOURCE_FIELD_TEXT]: (
|
||||||
|
state: ApiPaneReduxState,
|
||||||
|
action: ReduxAction<{ apiId: string; value: string }>,
|
||||||
|
) => {
|
||||||
|
const { apiId } = action.payload;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
datasourceFieldText: {
|
||||||
|
...state.datasourceFieldText,
|
||||||
|
[apiId]: action.payload.value,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default apiPaneReducer;
|
export default apiPaneReducer;
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,21 @@ const initialState: DatasourcePaneReduxState = {
|
||||||
selectedPlugin: "",
|
selectedPlugin: "",
|
||||||
datasourceRefs: {},
|
datasourceRefs: {},
|
||||||
drafts: {},
|
drafts: {},
|
||||||
|
actionRouteInfo: {},
|
||||||
|
newDatasource: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface DatasourcePaneReduxState {
|
export interface DatasourcePaneReduxState {
|
||||||
selectedPlugin: string;
|
selectedPlugin: string;
|
||||||
datasourceRefs: {};
|
datasourceRefs: {};
|
||||||
drafts: Record<string, Datasource>;
|
drafts: Record<string, Datasource>;
|
||||||
|
actionRouteInfo: Partial<{
|
||||||
|
apiId: string;
|
||||||
|
datasourceId: string;
|
||||||
|
pageId: string;
|
||||||
|
applicationId: string;
|
||||||
|
}>;
|
||||||
|
newDatasource: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const datasourcePaneReducer = createReducer(initialState, {
|
const datasourcePaneReducer = createReducer(initialState, {
|
||||||
|
|
@ -64,6 +73,43 @@ const datasourcePaneReducer = createReducer(initialState, {
|
||||||
...state,
|
...state,
|
||||||
drafts: _.omit(state.drafts, action.payload.id),
|
drafts: _.omit(state.drafts, action.payload.id),
|
||||||
}),
|
}),
|
||||||
|
[ReduxActionTypes.STORE_AS_DATASOURCE_UPDATE]: (
|
||||||
|
state: DatasourcePaneReduxState,
|
||||||
|
action: ReduxAction<{
|
||||||
|
apiId: string;
|
||||||
|
datasourceId: string;
|
||||||
|
pageId: string;
|
||||||
|
applicationId: string;
|
||||||
|
}>,
|
||||||
|
) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
actionRouteInfo: action.payload,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[ReduxActionTypes.STORE_AS_DATASOURCE_COMPLETE]: (
|
||||||
|
state: DatasourcePaneReduxState,
|
||||||
|
) => ({
|
||||||
|
...state,
|
||||||
|
actionRouteInfo: {},
|
||||||
|
}),
|
||||||
|
[ReduxActionTypes.CREATE_DATASOURCE_SUCCESS]: (
|
||||||
|
state: DatasourcePaneReduxState,
|
||||||
|
action: ReduxAction<{ id: string }>,
|
||||||
|
) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
newDatasource: action.payload.id,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[ReduxActionTypes.UPDATE_DATASOURCE_SUCCESS]: (
|
||||||
|
state: DatasourcePaneReduxState,
|
||||||
|
) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
newDatasource: "",
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default datasourcePaneReducer;
|
export default datasourcePaneReducer;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ const initialState: OrgReduxState = {
|
||||||
isFetchAllUsers: false,
|
isFetchAllUsers: false,
|
||||||
isDeletingOrgUser: false,
|
isDeletingOrgUser: false,
|
||||||
},
|
},
|
||||||
|
currentOrgId: "",
|
||||||
orgUsers: [],
|
orgUsers: [],
|
||||||
orgRoles: [],
|
orgRoles: [],
|
||||||
};
|
};
|
||||||
|
|
@ -39,6 +40,7 @@ const orgReducer = createReducer(initialState, {
|
||||||
isFetchAllUsers: true,
|
isFetchAllUsers: true,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[ReduxActionTypes.FETCH_ORG_ROLES_SUCCESS]: (
|
[ReduxActionTypes.FETCH_ORG_ROLES_SUCCESS]: (
|
||||||
state: OrgReduxState,
|
state: OrgReduxState,
|
||||||
action: ReduxAction<OrgRole[]>,
|
action: ReduxAction<OrgRole[]>,
|
||||||
|
|
@ -113,6 +115,13 @@ const orgReducer = createReducer(initialState, {
|
||||||
[ReduxActionTypes.DELETE_ORG_USER_ERROR]: (state: OrgReduxState) => {
|
[ReduxActionTypes.DELETE_ORG_USER_ERROR]: (state: OrgReduxState) => {
|
||||||
return { ...state, isDeletingOrgUser: false };
|
return { ...state, isDeletingOrgUser: false };
|
||||||
},
|
},
|
||||||
|
[ReduxActionTypes.SET_CURRENT_ORG_ID]: (
|
||||||
|
state: OrgReduxState,
|
||||||
|
action: ReduxAction<{ orgId: string }>,
|
||||||
|
) => ({
|
||||||
|
...state,
|
||||||
|
currentOrgId: action.payload.orgId,
|
||||||
|
}),
|
||||||
|
|
||||||
[ReduxActionTypes.FETCH_ORGS_SUCCESS]: (
|
[ReduxActionTypes.FETCH_ORGS_SUCCESS]: (
|
||||||
state: OrgReduxState,
|
state: OrgReduxState,
|
||||||
|
|
@ -134,6 +143,7 @@ export interface OrgReduxState {
|
||||||
};
|
};
|
||||||
orgUsers: OrgUser[];
|
orgUsers: OrgUser[];
|
||||||
orgRoles: any;
|
orgRoles: any;
|
||||||
|
currentOrgId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default orgReducer;
|
export default orgReducer;
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ import {
|
||||||
ReduxActionErrorTypes,
|
ReduxActionErrorTypes,
|
||||||
ReduxAction,
|
ReduxAction,
|
||||||
} from "constants/ReduxActionConstants";
|
} from "constants/ReduxActionConstants";
|
||||||
import { RestAction } from "api/ActionAPI";
|
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { RestAction } from "entities/Action";
|
||||||
|
|
||||||
const initialState: QueryPaneReduxState = {
|
const initialState: QueryPaneReduxState = {
|
||||||
isFetching: false,
|
isFetching: false,
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import ActionAPI, {
|
||||||
ExecuteActionRequest,
|
ExecuteActionRequest,
|
||||||
PaginationField,
|
PaginationField,
|
||||||
Property,
|
Property,
|
||||||
RestAction,
|
|
||||||
} from "api/ActionAPI";
|
} from "api/ActionAPI";
|
||||||
import { AppState } from "reducers";
|
import { AppState } from "reducers";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
|
@ -74,6 +73,7 @@ import { ToastType } from "react-toastify";
|
||||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
import * as log from "loglevel";
|
import * as log from "loglevel";
|
||||||
import { QUERY_CONSTANT } from "constants/QueryEditorConstants";
|
import { QUERY_CONSTANT } from "constants/QueryEditorConstants";
|
||||||
|
import { RestAction } from "entities/Action";
|
||||||
|
|
||||||
export const getAction = (
|
export const getAction = (
|
||||||
state: AppState,
|
state: AppState,
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,8 @@ import {
|
||||||
import { initialize, autofill, change } from "redux-form";
|
import { initialize, autofill, change } from "redux-form";
|
||||||
import { getAction } from "./ActionSagas";
|
import { getAction } from "./ActionSagas";
|
||||||
import { AppState } from "reducers";
|
import { AppState } from "reducers";
|
||||||
import { Property, RestAction } from "api/ActionAPI";
|
import { Property } from "api/ActionAPI";
|
||||||
import { changeApi } from "actions/apiPaneActions";
|
import { changeApi, setDatasourceFieldText } from "actions/apiPaneActions";
|
||||||
import {
|
import {
|
||||||
API_PATH_START_WITH_SLASH_ERROR,
|
API_PATH_START_WITH_SLASH_ERROR,
|
||||||
FIELD_REQUIRED_ERROR,
|
FIELD_REQUIRED_ERROR,
|
||||||
|
|
@ -46,6 +46,8 @@ import { getPluginIdOfPackageName } from "sagas/selectors";
|
||||||
import { getActions } from "selectors/entitiesSelector";
|
import { getActions } from "selectors/entitiesSelector";
|
||||||
import { ActionData } from "reducers/entityReducers/actionsReducer";
|
import { ActionData } from "reducers/entityReducers/actionsReducer";
|
||||||
import { createActionRequest } from "actions/actionActions";
|
import { createActionRequest } from "actions/actionActions";
|
||||||
|
import { RestAction } from "entities/Action";
|
||||||
|
import { isDynamicValue } from "utils/DynamicBindingUtils";
|
||||||
|
|
||||||
const getApiDraft = (state: AppState, id: string) => {
|
const getApiDraft = (state: AppState, id: string) => {
|
||||||
const drafts = state.ui.apiPane.drafts;
|
const drafts = state.ui.apiPane.drafts;
|
||||||
|
|
@ -147,6 +149,28 @@ function* syncApiParamsSaga(
|
||||||
`${currentPath}${paramsString}`,
|
`${currentPath}${paramsString}`,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
actionPayload.type === ReduxFormActionTypes.VALUE_CHANGE ||
|
||||||
|
actionPayload.type === ReduxFormActionTypes.ARRAY_REMOVE
|
||||||
|
) {
|
||||||
|
if (values.datasource && values.datasource.id) {
|
||||||
|
yield put(
|
||||||
|
setDatasourceFieldText(values.id, `${currentPath}${paramsString}`),
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
values.datasource &&
|
||||||
|
values.datasource.datasourceConfiguration
|
||||||
|
) {
|
||||||
|
yield put(
|
||||||
|
setDatasourceFieldText(
|
||||||
|
values.id,
|
||||||
|
values.datasource.datasourceConfiguration.url +
|
||||||
|
`${currentPath}${paramsString}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -191,13 +215,6 @@ function* changeApiSaga(actionPayload: ReduxAction<{ id: string }>) {
|
||||||
data = draft;
|
data = draft;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.actionConfiguration.path) {
|
|
||||||
if (data.actionConfiguration.path.charAt(0) === "/")
|
|
||||||
data.actionConfiguration.path = data.actionConfiguration.path.substring(
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
data.actionConfiguration.httpMethod !== "GET" &&
|
data.actionConfiguration.httpMethod !== "GET" &&
|
||||||
!data.providerId &&
|
!data.providerId &&
|
||||||
|
|
@ -286,29 +303,23 @@ function* validateInputSaga(
|
||||||
payload,
|
payload,
|
||||||
meta: { field },
|
meta: { field },
|
||||||
} = actionPayload;
|
} = actionPayload;
|
||||||
|
if (field === "dynamicBindingPathList") return;
|
||||||
const actions: RestAction[] = yield select(getActionConfigs);
|
const actions: RestAction[] = yield select(getActionConfigs);
|
||||||
const sameNames = actions.filter(
|
|
||||||
(action: RestAction) => action.name === payload && action.id,
|
|
||||||
);
|
|
||||||
if (field === "name") {
|
if (field === "name") {
|
||||||
|
const sameNames = actions.filter(
|
||||||
|
(action: RestAction) => action.name === payload && action.id,
|
||||||
|
);
|
||||||
if (!_.trim(payload)) {
|
if (!_.trim(payload)) {
|
||||||
_.set(errors, field, FIELD_REQUIRED_ERROR);
|
_.set(errors, field, FIELD_REQUIRED_ERROR);
|
||||||
} else if (payload.indexOf(" ") !== -1) {
|
} else if (payload.indexOf(" ") !== -1) {
|
||||||
_.set(errors, field, VALID_FUNCTION_NAME_ERROR);
|
_.set(errors, field, VALID_FUNCTION_NAME_ERROR);
|
||||||
} else if (sameNames.length > 0) {
|
} else if (sameNames.length > 0) {
|
||||||
// TODO Check this
|
|
||||||
_.set(errors, field, UNIQUE_NAME_ERROR);
|
_.set(errors, field, UNIQUE_NAME_ERROR);
|
||||||
} else {
|
} else {
|
||||||
_.unset(errors, field);
|
_.unset(errors, field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (field === "actionConfiguration.path") {
|
|
||||||
if (payload && payload.startsWith("/")) {
|
|
||||||
_.set(errors, field, API_PATH_START_WITH_SLASH_ERROR);
|
|
||||||
} else {
|
|
||||||
_.unset(errors, field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
yield put({
|
yield put({
|
||||||
type: ReduxFormActionTypes.UPDATE_FIELD_ERROR,
|
type: ReduxFormActionTypes.UPDATE_FIELD_ERROR,
|
||||||
meta: {
|
meta: {
|
||||||
|
|
@ -391,12 +402,37 @@ function* updateFormFields(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function* updateDynamicBindingsSaga(
|
||||||
|
actionPayload: ReduxActionWithMeta<string, { field: string }>,
|
||||||
|
) {
|
||||||
|
const field = actionPayload.meta.field;
|
||||||
|
if (field === "dynamicBindingPathList") return;
|
||||||
|
const value = actionPayload.payload;
|
||||||
|
const { values } = yield select(getFormData, API_EDITOR_FORM_NAME);
|
||||||
|
if (!values.id) return;
|
||||||
|
|
||||||
|
const isDynamic = isDynamicValue(value);
|
||||||
|
let dynamicBindings: Property[] = values.dynamicBindingPathList || [];
|
||||||
|
const fieldExists = _.some(dynamicBindings, { key: field });
|
||||||
|
|
||||||
|
if (!isDynamic && fieldExists) {
|
||||||
|
dynamicBindings = dynamicBindings.filter(d => d.key !== field);
|
||||||
|
}
|
||||||
|
if (isDynamic && !fieldExists) {
|
||||||
|
dynamicBindings.push({ key: field });
|
||||||
|
}
|
||||||
|
yield put(
|
||||||
|
change(API_EDITOR_FORM_NAME, "dynamicBindingPathList", dynamicBindings),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function* formValueChangeSaga(
|
function* formValueChangeSaga(
|
||||||
actionPayload: ReduxActionWithMeta<string, { field: string; form: string }>,
|
actionPayload: ReduxActionWithMeta<string, { field: string; form: string }>,
|
||||||
) {
|
) {
|
||||||
const { form } = actionPayload.meta;
|
const { form } = actionPayload.meta;
|
||||||
if (form !== API_EDITOR_FORM_NAME) return;
|
if (form !== API_EDITOR_FORM_NAME) return;
|
||||||
yield all([
|
yield all([
|
||||||
|
call(updateDynamicBindingsSaga, actionPayload),
|
||||||
call(validateInputSaga, actionPayload),
|
call(validateInputSaga, actionPayload),
|
||||||
call(updateDraftsSaga),
|
call(updateDraftsSaga),
|
||||||
call(syncApiParamsSaga, actionPayload),
|
call(syncApiParamsSaga, actionPayload),
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
import { takeLatest, put, all, select } from "redux-saga/effects";
|
import { takeLatest, put, all, select, take } from "redux-saga/effects";
|
||||||
import { initialize } from "redux-form";
|
|
||||||
import {
|
import {
|
||||||
ReduxActionTypes,
|
ReduxActionTypes,
|
||||||
ReduxActionErrorTypes,
|
ReduxActionErrorTypes,
|
||||||
ReduxAction,
|
ReduxAction,
|
||||||
} from "constants/ReduxActionConstants";
|
} from "constants/ReduxActionConstants";
|
||||||
import { API_EDITOR_FORM_NAME } from "constants/forms";
|
|
||||||
import { validateResponse } from "sagas/ErrorSagas";
|
import { validateResponse } from "sagas/ErrorSagas";
|
||||||
import CurlImportApi, { CurlImportRequest } from "api/ImportApi";
|
import CurlImportApi, { CurlImportRequest } from "api/ImportApi";
|
||||||
import { ApiResponse } from "api/ApiResponses";
|
import { ApiResponse } from "api/ApiResponses";
|
||||||
|
|
@ -13,14 +11,10 @@ import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
import { AppToaster } from "components/editorComponents/ToastComponent";
|
import { AppToaster } from "components/editorComponents/ToastComponent";
|
||||||
import { ToastType } from "react-toastify";
|
import { ToastType } from "react-toastify";
|
||||||
import { CURL_IMPORT_SUCCESS } from "constants/messages";
|
import { CURL_IMPORT_SUCCESS } from "constants/messages";
|
||||||
import { API_EDITOR_ID_URL } from "constants/routes";
|
import { getCurrentApplicationId } from "selectors/editorSelectors";
|
||||||
import history from "utils/history";
|
|
||||||
import {
|
|
||||||
getCurrentApplicationId,
|
|
||||||
getCurrentPageId,
|
|
||||||
} from "selectors/editorSelectors";
|
|
||||||
import { fetchActions } from "actions/actionActions";
|
import { fetchActions } from "actions/actionActions";
|
||||||
import { CURL } from "constants/ApiConstants";
|
import { CURL } from "constants/ApiConstants";
|
||||||
|
import { changeApi } from "actions/apiPaneActions";
|
||||||
|
|
||||||
export function* curlImportSaga(action: ReduxAction<CurlImportRequest>) {
|
export function* curlImportSaga(action: ReduxAction<CurlImportRequest>) {
|
||||||
const { type, pageId, name } = action.payload;
|
const { type, pageId, name } = action.payload;
|
||||||
|
|
@ -35,12 +29,16 @@ export function* curlImportSaga(action: ReduxAction<CurlImportRequest>) {
|
||||||
const response: ApiResponse = yield CurlImportApi.curlImport(request);
|
const response: ApiResponse = yield CurlImportApi.curlImport(request);
|
||||||
const isValidResponse = yield validateResponse(response);
|
const isValidResponse = yield validateResponse(response);
|
||||||
const applicationId = yield select(getCurrentApplicationId);
|
const applicationId = yield select(getCurrentApplicationId);
|
||||||
const currentPageId = yield select(getCurrentPageId);
|
|
||||||
|
|
||||||
if (isValidResponse) {
|
if (isValidResponse) {
|
||||||
AnalyticsUtil.logEvent("IMPORT_API", {
|
AnalyticsUtil.logEvent("IMPORT_API", {
|
||||||
importSource: CURL,
|
importSource: CURL,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
yield put(fetchActions(applicationId));
|
||||||
|
const data = { ...response.data };
|
||||||
|
yield take(ReduxActionTypes.FETCH_ACTIONS_SUCCESS);
|
||||||
|
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: CURL_IMPORT_SUCCESS,
|
message: CURL_IMPORT_SUCCESS,
|
||||||
type: ToastType.SUCCESS,
|
type: ToastType.SUCCESS,
|
||||||
|
|
@ -49,12 +47,8 @@ export function* curlImportSaga(action: ReduxAction<CurlImportRequest>) {
|
||||||
type: ReduxActionTypes.SUBMIT_CURL_FORM_SUCCESS,
|
type: ReduxActionTypes.SUBMIT_CURL_FORM_SUCCESS,
|
||||||
payload: response.data,
|
payload: response.data,
|
||||||
});
|
});
|
||||||
yield put(fetchActions(applicationId));
|
|
||||||
const data = { ...response.data };
|
yield put(changeApi(data.id));
|
||||||
yield put(initialize(API_EDITOR_FORM_NAME, data));
|
|
||||||
history.push(
|
|
||||||
API_EDITOR_ID_URL(applicationId, currentPageId, response.data.id),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
yield put({
|
yield put({
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { all, put, takeEvery, select, call } from "redux-saga/effects";
|
import { all, put, takeEvery, select, call, take } from "redux-saga/effects";
|
||||||
import { change, initialize, getFormValues } from "redux-form";
|
import { change, initialize, getFormValues } from "redux-form";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import {
|
import {
|
||||||
|
|
@ -18,7 +18,11 @@ import {
|
||||||
getDatasource,
|
getDatasource,
|
||||||
getDatasourceDraft,
|
getDatasourceDraft,
|
||||||
} from "selectors/entitiesSelector";
|
} from "selectors/entitiesSelector";
|
||||||
import { selectPlugin } from "actions/datasourceActions";
|
import {
|
||||||
|
selectPlugin,
|
||||||
|
createDatasource,
|
||||||
|
changeDatasource,
|
||||||
|
} from "actions/datasourceActions";
|
||||||
import { fetchPluginForm } from "actions/pluginActions";
|
import { fetchPluginForm } from "actions/pluginActions";
|
||||||
import { GenericApiResponse } from "api/ApiResponses";
|
import { GenericApiResponse } from "api/ApiResponses";
|
||||||
import DatasourcesApi, {
|
import DatasourcesApi, {
|
||||||
|
|
@ -26,6 +30,7 @@ import DatasourcesApi, {
|
||||||
Datasource,
|
Datasource,
|
||||||
} from "api/DatasourcesApi";
|
} from "api/DatasourcesApi";
|
||||||
import PluginApi, { DatasourceForm } from "api/PluginApi";
|
import PluginApi, { DatasourceForm } from "api/PluginApi";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DATA_SOURCES_EDITOR_ID_URL,
|
DATA_SOURCES_EDITOR_ID_URL,
|
||||||
DATA_SOURCES_EDITOR_URL,
|
DATA_SOURCES_EDITOR_URL,
|
||||||
|
|
@ -37,6 +42,8 @@ import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
import { AppToaster } from "components/editorComponents/ToastComponent";
|
import { AppToaster } from "components/editorComponents/ToastComponent";
|
||||||
import { ToastType } from "react-toastify";
|
import { ToastType } from "react-toastify";
|
||||||
import { getFormData } from "selectors/formSelectors";
|
import { getFormData } from "selectors/formSelectors";
|
||||||
|
import { changeApi, setDatasourceFieldText } from "actions/apiPaneActions";
|
||||||
|
import { getCurrentOrgId } from "selectors/organizationSelectors";
|
||||||
|
|
||||||
function* fetchDatasourcesSaga() {
|
function* fetchDatasourcesSaga() {
|
||||||
try {
|
try {
|
||||||
|
|
@ -60,8 +67,12 @@ function* createDatasourceSaga(
|
||||||
actionPayload: ReduxAction<CreateDatasourceConfig>,
|
actionPayload: ReduxAction<CreateDatasourceConfig>,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
|
const organizationId = yield select(getCurrentOrgId);
|
||||||
const response: GenericApiResponse<Datasource> = yield DatasourcesApi.createDatasource(
|
const response: GenericApiResponse<Datasource> = yield DatasourcesApi.createDatasource(
|
||||||
actionPayload.payload,
|
{
|
||||||
|
...actionPayload.payload,
|
||||||
|
organizationId,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
const isValidResponse = yield validateResponse(response);
|
const isValidResponse = yield validateResponse(response);
|
||||||
if (isValidResponse) {
|
if (isValidResponse) {
|
||||||
|
|
@ -73,9 +84,7 @@ function* createDatasourceSaga(
|
||||||
type: ReduxActionTypes.CREATE_DATASOURCE_SUCCESS,
|
type: ReduxActionTypes.CREATE_DATASOURCE_SUCCESS,
|
||||||
payload: response.data,
|
payload: response.data,
|
||||||
});
|
});
|
||||||
yield put(
|
yield put(change(API_EDITOR_FORM_NAME, "datasource", response.data));
|
||||||
change(API_EDITOR_FORM_NAME, "datasource.id", response.data.id),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
yield put({
|
yield put({
|
||||||
|
|
@ -156,6 +165,7 @@ function* updateDatasourceSaga(actionPayload: ReduxAction<Datasource>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function* testDatasourceSaga(actionPayload: ReduxAction<Datasource>) {
|
function* testDatasourceSaga(actionPayload: ReduxAction<Datasource>) {
|
||||||
|
const organizationId = yield select(getCurrentOrgId);
|
||||||
const { initialValues, values } = yield select(
|
const { initialValues, values } = yield select(
|
||||||
getFormData,
|
getFormData,
|
||||||
DATASOURCE_DB_FORM,
|
DATASOURCE_DB_FORM,
|
||||||
|
|
@ -168,7 +178,10 @@ function* testDatasourceSaga(actionPayload: ReduxAction<Datasource>) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response: GenericApiResponse<Datasource> = yield DatasourcesApi.testDatasource(
|
const response: GenericApiResponse<Datasource> = yield DatasourcesApi.testDatasource(
|
||||||
payload,
|
{
|
||||||
|
...payload,
|
||||||
|
organizationId,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
const isValidResponse = yield validateResponse(response);
|
const isValidResponse = yield validateResponse(response);
|
||||||
if (isValidResponse) {
|
if (isValidResponse) {
|
||||||
|
|
@ -203,6 +216,7 @@ function* createDatasourceFromFormSaga(
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
let formConfig;
|
let formConfig;
|
||||||
|
const organizationId = yield select(getCurrentOrgId);
|
||||||
const initialValues = {};
|
const initialValues = {};
|
||||||
const parseConfig = (section: any): any => {
|
const parseConfig = (section: any): any => {
|
||||||
return _.map(section.children, (subSection: any) => {
|
return _.map(section.children, (subSection: any) => {
|
||||||
|
|
@ -247,7 +261,10 @@ function* createDatasourceFromFormSaga(
|
||||||
};
|
};
|
||||||
|
|
||||||
const response: GenericApiResponse<Datasource> = yield DatasourcesApi.createDatasource(
|
const response: GenericApiResponse<Datasource> = yield DatasourcesApi.createDatasource(
|
||||||
payload,
|
{
|
||||||
|
...payload,
|
||||||
|
organizationId,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
const isValidResponse = yield validateResponse(response);
|
const isValidResponse = yield validateResponse(response);
|
||||||
if (isValidResponse) {
|
if (isValidResponse) {
|
||||||
|
|
@ -344,6 +361,53 @@ function* formValueChangeSaga(
|
||||||
yield all([call(updateDraftsSaga)]);
|
yield all([call(updateDraftsSaga)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function* storeAsDatasourceSaga() {
|
||||||
|
const { values } = yield select(getFormData, API_EDITOR_FORM_NAME);
|
||||||
|
const applicationId = yield select(getCurrentApplicationId);
|
||||||
|
const pageId = yield select(getCurrentPageId);
|
||||||
|
const datasource = _.get(values, "datasource");
|
||||||
|
|
||||||
|
history.push(DATA_SOURCES_EDITOR_URL(applicationId, pageId));
|
||||||
|
|
||||||
|
yield put(createDatasource(datasource));
|
||||||
|
const createDatasourceSuccessAction = yield take(
|
||||||
|
ReduxActionTypes.CREATE_DATASOURCE_SUCCESS,
|
||||||
|
);
|
||||||
|
const createdDatasource = createDatasourceSuccessAction.payload;
|
||||||
|
|
||||||
|
yield put({
|
||||||
|
type: ReduxActionTypes.STORE_AS_DATASOURCE_UPDATE,
|
||||||
|
payload: {
|
||||||
|
pageId,
|
||||||
|
applicationId,
|
||||||
|
apiId: values.id,
|
||||||
|
datasourceId: createdDatasource.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
yield put(changeDatasource(createdDatasource));
|
||||||
|
}
|
||||||
|
|
||||||
|
function* updateDatasourceSuccessSaga(action: ReduxAction<Datasource>) {
|
||||||
|
const state = yield select();
|
||||||
|
const actionRouteInfo = _.get(state, "ui.datasourcePane.actionRouteInfo");
|
||||||
|
const updatedDatasource = action.payload;
|
||||||
|
|
||||||
|
if (
|
||||||
|
actionRouteInfo &&
|
||||||
|
updatedDatasource.id === actionRouteInfo.datasourceId
|
||||||
|
) {
|
||||||
|
const { apiId } = actionRouteInfo;
|
||||||
|
|
||||||
|
yield put(setDatasourceFieldText(apiId, ""));
|
||||||
|
yield put(changeApi(apiId));
|
||||||
|
}
|
||||||
|
|
||||||
|
yield put({
|
||||||
|
type: ReduxActionTypes.STORE_AS_DATASOURCE_COMPLETE,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function* watchDatasourcesSagas() {
|
export function* watchDatasourcesSagas() {
|
||||||
yield all([
|
yield all([
|
||||||
takeEvery(ReduxActionTypes.FETCH_DATASOURCES_INIT, fetchDatasourcesSaga),
|
takeEvery(ReduxActionTypes.FETCH_DATASOURCES_INIT, fetchDatasourcesSaga),
|
||||||
|
|
@ -356,6 +420,11 @@ export function* watchDatasourcesSagas() {
|
||||||
takeEvery(ReduxActionTypes.TEST_DATASOURCE_INIT, testDatasourceSaga),
|
takeEvery(ReduxActionTypes.TEST_DATASOURCE_INIT, testDatasourceSaga),
|
||||||
takeEvery(ReduxActionTypes.DELETE_DATASOURCE_INIT, deleteDatasourceSaga),
|
takeEvery(ReduxActionTypes.DELETE_DATASOURCE_INIT, deleteDatasourceSaga),
|
||||||
takeEvery(ReduxActionTypes.CHANGE_DATASOURCE, changeDatasourceSaga),
|
takeEvery(ReduxActionTypes.CHANGE_DATASOURCE, changeDatasourceSaga),
|
||||||
|
takeEvery(ReduxActionTypes.STORE_AS_DATASOURCE_INIT, storeAsDatasourceSaga),
|
||||||
|
takeEvery(
|
||||||
|
ReduxActionTypes.UPDATE_DATASOURCE_SUCCESS,
|
||||||
|
updateDatasourceSuccessSaga,
|
||||||
|
),
|
||||||
// Intercepting the redux-form change actionType
|
// Intercepting the redux-form change actionType
|
||||||
takeEvery(ReduxFormActionTypes.VALUE_CHANGE, formValueChangeSaga),
|
takeEvery(ReduxFormActionTypes.VALUE_CHANGE, formValueChangeSaga),
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,8 @@ export function* fetchPageListSaga(
|
||||||
);
|
);
|
||||||
const isValidResponse = yield validateResponse(response);
|
const isValidResponse = yield validateResponse(response);
|
||||||
if (isValidResponse) {
|
if (isValidResponse) {
|
||||||
const pages: PageListPayload = response.data.map(page => ({
|
const orgId = response.data.organizationId;
|
||||||
|
const pages: PageListPayload = response.data.pages.map(page => ({
|
||||||
pageName: page.name,
|
pageName: page.name,
|
||||||
pageId: page.id,
|
pageId: page.id,
|
||||||
isDefault: page.isDefault,
|
isDefault: page.isDefault,
|
||||||
|
|
@ -87,6 +88,13 @@ export function* fetchPageListSaga(
|
||||||
applicationId,
|
applicationId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
yield put({
|
||||||
|
type: ReduxActionTypes.SET_CURRENT_ORG_ID,
|
||||||
|
payload: {
|
||||||
|
orgId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ function* fetchPluginsSaga() {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
yield put({
|
yield put({
|
||||||
type: ReduxActionTypes.FETCH_PLUGINS_ERROR,
|
type: ReduxActionErrorTypes.FETCH_PLUGINS_ERROR,
|
||||||
payload: { error },
|
payload: { error },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ import { initialize } from "redux-form";
|
||||||
import { getAction, getActionParams, getActionTimeout } from "./ActionSagas";
|
import { getAction, getActionParams, getActionTimeout } from "./ActionSagas";
|
||||||
import { AppState } from "reducers";
|
import { AppState } from "reducers";
|
||||||
import ActionAPI, {
|
import ActionAPI, {
|
||||||
RestAction,
|
|
||||||
PaginationField,
|
PaginationField,
|
||||||
ExecuteActionRequest,
|
ExecuteActionRequest,
|
||||||
ActionApiResponse,
|
ActionApiResponse,
|
||||||
|
|
@ -45,6 +44,7 @@ import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
import { GenericApiResponse } from "api/ApiResponses";
|
import { GenericApiResponse } from "api/ApiResponses";
|
||||||
import { validateResponse } from "./ErrorSagas";
|
import { validateResponse } from "./ErrorSagas";
|
||||||
import { getQueryName } from "selectors/entitiesSelector";
|
import { getQueryName } from "selectors/entitiesSelector";
|
||||||
|
import { RestAction } from "entities/Action";
|
||||||
|
|
||||||
const getQueryDraft = (state: AppState, id: string) => {
|
const getQueryDraft = (state: AppState, id: string) => {
|
||||||
const drafts = state.ui.apiPane.drafts;
|
const drafts = state.ui.apiPane.drafts;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import { createSelector } from "reselect";
|
import { createSelector } from "reselect";
|
||||||
import { getActionsForCurrentPage } from "./entitiesSelector";
|
import { getActionDrafts, getActionsForCurrentPage } from "./entitiesSelector";
|
||||||
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
|
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
|
||||||
import { getEvaluatedDataTree } from "utils/DynamicBindingUtils";
|
import { getEvaluatedDataTree } from "utils/DynamicBindingUtils";
|
||||||
import { extraLibraries } from "jsExecution/JSExecutionManagerSingleton";
|
|
||||||
import { DataTree, DataTreeFactory } from "entities/DataTree/dataTreeFactory";
|
import { DataTree, DataTreeFactory } from "entities/DataTree/dataTreeFactory";
|
||||||
import { getWidgets, getWidgetsMeta } from "sagas/selectors";
|
import { getWidgets, getWidgetsMeta } from "sagas/selectors";
|
||||||
import * as log from "loglevel";
|
import * as log from "loglevel";
|
||||||
|
|
@ -40,13 +39,15 @@ import { getPageList } from "./appViewSelectors";
|
||||||
//
|
//
|
||||||
export const getUnevaluatedDataTree = createSelector(
|
export const getUnevaluatedDataTree = createSelector(
|
||||||
getActionsForCurrentPage,
|
getActionsForCurrentPage,
|
||||||
|
getActionDrafts,
|
||||||
getWidgets,
|
getWidgets,
|
||||||
getWidgetsMeta,
|
getWidgetsMeta,
|
||||||
getPageList,
|
getPageList,
|
||||||
(actions, widgets, widgetsMeta, pageListPayload) => {
|
(actions, actionDrafts, widgets, widgetsMeta, pageListPayload) => {
|
||||||
const pageList = pageListPayload || [];
|
const pageList = pageListPayload || [];
|
||||||
return DataTreeFactory.create({
|
return DataTreeFactory.create({
|
||||||
actions,
|
actions,
|
||||||
|
actionDrafts,
|
||||||
widgets,
|
widgets,
|
||||||
widgetsMeta,
|
widgetsMeta,
|
||||||
pageList,
|
pageList,
|
||||||
|
|
@ -82,8 +83,6 @@ export const getDataTreeForAutocomplete = createSelector(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const libs: Record<string, any> = {};
|
return tree;
|
||||||
extraLibraries.forEach(config => (libs[config.accessor] = config.lib));
|
|
||||||
return { ...tree, ...cachedResponses, ...libs };
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -154,11 +154,9 @@ export const getQueryActions = (state: AppState): ActionDataState => {
|
||||||
const getCurrentPageId = (state: AppState) =>
|
const getCurrentPageId = (state: AppState) =>
|
||||||
state.entities.pageList.currentPageId;
|
state.entities.pageList.currentPageId;
|
||||||
|
|
||||||
export const getDatasourcePlugins = (state: AppState) => {
|
export const getDatasourcePlugins = createSelector(getPlugins, plugins => {
|
||||||
return state.entities.plugins.list.filter(
|
return plugins.filter(plugin => plugin?.allowUserDatasources ?? true);
|
||||||
plugin => plugin?.allowUserDatasources ?? true,
|
});
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getActionsForCurrentPage = createSelector(
|
export const getActionsForCurrentPage = createSelector(
|
||||||
getCurrentPageId,
|
getCurrentPageId,
|
||||||
|
|
@ -169,13 +167,14 @@ export const getActionsForCurrentPage = createSelector(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getActionResponses = (
|
export const getActionDrafts = (state: AppState) => state.entities.actionDrafts;
|
||||||
state: AppState,
|
|
||||||
): Record<string, ActionResponse | undefined> => {
|
export const getActionResponses = createSelector(getActions, actions => {
|
||||||
const responses: Record<string, ActionResponse | undefined> = {};
|
const responses: Record<string, ActionResponse | undefined> = {};
|
||||||
state.entities.actions.forEach(a => {
|
|
||||||
|
actions.forEach(a => {
|
||||||
responses[a.config.id] = a.data;
|
responses[a.config.id] = a.data;
|
||||||
});
|
});
|
||||||
|
|
||||||
return responses;
|
return responses;
|
||||||
};
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { getFormValues, isValid, getFormInitialValues } from "redux-form";
|
import { getFormValues, isValid, getFormInitialValues } from "redux-form";
|
||||||
import { AppState } from "reducers";
|
import { AppState } from "reducers";
|
||||||
import { RestAction } from "api/ActionAPI";
|
import { RestAction } from "entities/Action";
|
||||||
|
|
||||||
type GetFormData = (
|
type GetFormData = (
|
||||||
state: AppState,
|
state: AppState,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import { OrgRole, Org } from "constants/orgConstants";
|
||||||
export const getRolesFromState = (state: AppState) => {
|
export const getRolesFromState = (state: AppState) => {
|
||||||
return state.ui.orgs.roles;
|
return state.ui.orgs.roles;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getCurrentOrgId = (state: AppState) => state.ui.orgs.currentOrgId;
|
||||||
export const getOrgs = (state: AppState) => state.ui.orgs.list;
|
export const getOrgs = (state: AppState) => state.ui.orgs.list;
|
||||||
export const getAllUsers = (state: AppState) => state.ui.orgs.orgUsers;
|
export const getAllUsers = (state: AppState) => state.ui.orgs.orgUsers;
|
||||||
export const getAllRoles = (state: AppState) => state.ui.orgs.orgRoles;
|
export const getAllRoles = (state: AppState) => state.ui.orgs.orgRoles;
|
||||||
|
|
|
||||||
|
|
@ -47,15 +47,15 @@ export const getWidgetPropsForPropertyPane = createSelector(
|
||||||
const evaluatedWidget = _.find(evaluatedTree, {
|
const evaluatedWidget = _.find(evaluatedTree, {
|
||||||
widgetId: widget.widgetId,
|
widgetId: widget.widgetId,
|
||||||
}) as DataTreeWidget;
|
}) as DataTreeWidget;
|
||||||
|
const widgetProperties = {
|
||||||
|
...widget,
|
||||||
|
};
|
||||||
if (evaluatedWidget.invalidProps) {
|
if (evaluatedWidget.invalidProps) {
|
||||||
const { invalidProps, validationMessages } = evaluatedWidget;
|
const { invalidProps, validationMessages } = evaluatedWidget;
|
||||||
return {
|
widgetProperties.invalidProps = invalidProps;
|
||||||
...widget,
|
widgetProperties.validationMessages = validationMessages;
|
||||||
invalidProps,
|
|
||||||
validationMessages,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return widget;
|
return widgetProperties;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user