Merge branch 'release' of https://github.com/appsmithorg/appsmith into release

This commit is contained in:
Automated Github Action 2020-08-10 09:05:06 +00:00
commit 0c1d791cb8
176 changed files with 4351 additions and 2227 deletions

View File

@ -37,6 +37,9 @@ Do all this **without HTML/CSS**, and writing any custom integrations.
* [Customer Support Dashboard](https://app.appsmith.com/applications/5f2aeb2580ca1f6faaed4e4a/pages/5f2d61b580ca1f6faaed4e79#utm_source=github&utm_medium=homepage)
## Build & Deploy
* [Docker](https://docs.appsmith.com/quick-start#docker)
## Why Appsmith?
@ -55,10 +58,6 @@ Appsmith provides a better way of building internal tools by visualising them as
* **Fine-grained access control**: Control who can edit / view your applications from a single control panel
* **App management**: Build and organise multiple applications on a single platform
## Build & Deploy
* [Docker](https://docs.appsmith.com/quick-start#docker)
## Documentation & Support
If you have encountered a bug or need to get in touch with us, you can contact us using one of the following channels:

View File

@ -171,13 +171,11 @@ describe("API Panel Test Functionality", function() {
it("API check with Invalid Header", function() {
cy.CreateAPI("FourthAPI");
cy.log("Creation of API Action successful");
cy.EnterSourceDetailsWithQueryParam(
cy.EnterSourceDetailsWithHeader(
testdata.baseUrl,
testdata.methods,
testdata.headerKey,
testdata.invalidValue,
testdata.queryKey,
testdata.queryValue,
);
cy.WaitAutoSave();
cy.RunAPI();

View File

@ -12,13 +12,10 @@ describe("API Panel Test Functionality", function() {
cy.SaveAndRunAPI();
cy.validateRequest(testdata.baseUrl, testdata.methods, testdata.Get);
cy.ResponseStatusCheck(testdata.successStatusCode);
cy.get(apiwidget.createApiOnSideBar)
.first()
.click({ force: true });
cy.SearchAPIandClick("FirstAPI");
cy.SearchEntityandOpen("FirstAPI");
cy.EditApiName("SecondAPI");
cy.ClearSearch();
cy.SearchAPIandClick("SecondAPI");
cy.SearchEntityandOpen("SecondAPI");
cy.DeleteAPI();
});
});

View File

@ -2,15 +2,19 @@ const commonlocators = require("../../../locators/commonlocators.json");
const dsl = require("../../../fixtures/commondsl.json");
const widgetsPage = require("../../../locators/Widgets.json");
const testdata = require("../../../fixtures/testdata.json");
const pages = require("../../../locators/Pages.json");
describe("Moustache test Functionality", function() {
beforeEach(() => {
cy.addDsl(dsl);
});
it("Moustache test Functionality", function() {
//cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("textwidget");
cy.widgetText("Api", widgetsPage.textWidget, widgetsPage.textInputval);
cy.testCodeMirror("/api/users/2");
cy.NavigateToEntityExplorer();
cy.wait(10000);
cy.NavigateToAPI_Panel();
cy.log("Navigation to API Panel screen successful");
cy.CreateAPI("TestAPINew");

View File

@ -8,9 +8,10 @@ describe("API Panel Test Functionality ", function() {
cy.CreateAPI("FirstAPI");
cy.RunAPI();
cy.log("Creation of FirstAPI Action successful");
cy.NavigateToAPI_Panel();
cy.CreateAPI("SecondAPI");
cy.RunAPI();
cy.log("Creation of SecondAPI Action successful");
cy.SearchAPI("SecondAPI", "FirstAPI");
cy.SearchEntity("SecondAPI", "FirstAPI");
});
});

View File

@ -1,17 +1,20 @@
const commonlocators = require("../../../locators/commonlocators.json");
describe("API Panel Test Functionality ", function() {
it("Test API copy/Move/delete feature", function() {
cy.log("Login Successful");
cy.NavigateToAPI_Panel();
cy.log("Navigation to API Panel screen successful");
cy.CreateAPI("FirstAPI");
cy.log("Creation of FirstAPI Action successful");
cy.CopyAPIToHome("FirstAPI");
cy.DeleteAPI("FirstAPI");
//cy.MoveAPIToPage();
cy.CreateAPI("SecondApi");
cy.log("Creation of FirstAPI Action successful");
cy.CreationOfUniqueAPIcheck("SecondApi");
cy.GlobalSearchEntity("FirstAPI");
cy.xpath('//*[local-name()="g" and @id="Icon/Outline/more-vertical"]')
.last()
.should("be.hidden")
.invoke("show")
.click({ force: true });
cy.CopyAPIToHome();
cy.GlobalSearchEntity("FirstAPICopy");
cy.DeleteAPIFromSideBar();
});
});

View File

@ -2,6 +2,7 @@ const commonlocators = require("../../../locators/commonlocators.json");
const formWidgetsPage = require("../../../locators/FormWidgets.json");
const dsl = require("../../../fixtures/uiBindDsl.json");
const publishPage = require("../../../locators/publishWidgetspage.json");
const pages = require("../../../locators/Pages.json");
describe("Binding the Datepicker and Text Widget", function() {
let nextDay;
@ -15,6 +16,7 @@ describe("Binding the Datepicker and Text Widget", function() {
/**
* Bind DatePicker1 to Text for "selectedDate"
*/
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("textwidget");
cy.testJsontext("text", "{{DatePicker1.selectedDate}}");
cy.get(commonlocators.editPropCrossButton).click();
@ -48,6 +50,7 @@ describe("Binding the Datepicker and Text Widget", function() {
});
it("DatePicker1-text: Change the date in DatePicker1 and Validate the same in text widget", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("textwidget");
/**
@ -105,6 +108,7 @@ describe("Binding the Datepicker and Text Widget", function() {
/**
* Bind the DatePicker1 and DatePicker2 along with hard coded text to Text widget
*/
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("textwidget");
cy.testJsontext(
"text",

View File

@ -16,12 +16,13 @@ describe("Test Create Api and Bind to Table widget", function() {
});
it("Table-Text, Validate Server Side Pagination of Paginate with Table Page No", function() {
cy.get(pages.pagesIcon).click({ force: true });
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("tablewidget");
/**Bind Api1 with Table widget */
cy.testJsontext("tabledata", "{{Api1.data.results}}");
cy.CheckWidgetProperties(commonlocators.serverSidePaginationCheckbox);
/**Bind Table with Textwidget with selected row */
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("textwidget");
cy.testJsontext("text", "{{Table1.selectedRow.url}}");
cy.readTabledata("0", "1").then(tabData => {
@ -65,12 +66,12 @@ describe("Test Create Api and Bind to Table widget", function() {
parseSpecialCharSequences: false,
});
cy.WaitAutoSave();
cy.get(pages.pagesIcon).click({ force: true });
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("textwidget");
/** Bind the Table widget with Text widget*/
cy.testJsontext("text", "{{Table1.selectedRow.url}}");
cy.get(commonlocators.editPropCrossButton).click();
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("tablewidget");
cy.testJsontext("tabledata", "{{Api2.data.results}}");
cy.callApi("Api2");

View File

@ -27,7 +27,8 @@ describe("Test Create Api and Bind to Table widget", function() {
});
it("Test_Validate the Api data is updated on Table widget", function() {
cy.get(pages.pagesIcon).click({ force: true });
//cy.get(pages.pagesIcon).click({ force: true });
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("tablewidget");
cy.testJsontext("tabledata", "{{Api1.data}}");
cy.get(commonlocators.editPropCrossButton).click();

View File

@ -2,12 +2,14 @@ const commonlocators = require("../../../locators/commonlocators.json");
const viewWidgetsPage = require("../../../locators/ViewWidgets.json");
const publish = require("../../../locators/publishWidgetspage.json");
const dsl = require("../../../fixtures/ChartTextDsl.json");
const pages = require("../../../locators/Pages.json");
describe("Text-Chart Binding Functionality", function() {
before(() => {
cy.addDsl(dsl);
});
it("Text-Chart Binding Functionality View", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("textwidget");
cy.testJsontext("text", JSON.stringify(this.data.chartInputValidate));
cy.get(commonlocators.TextInside).should(
@ -15,6 +17,7 @@ describe("Text-Chart Binding Functionality", function() {
JSON.stringify(this.data.chartInputValidate),
);
cy.closePropertyPane();
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("chartwidget");
cy.get(viewWidgetsPage.chartType)
.find(commonlocators.dropdownbuttonclick)

View File

@ -1,12 +1,20 @@
const commonlocators = require("../../../locators/commonlocators.json");
const publish = require("../../../locators/publishWidgetspage.json");
const dsl = require("../../../fixtures/TextTabledsl.json");
const pages = require("../../../locators/Pages.json");
describe("Text-Table Binding Functionality", function() {
Cypress.on("uncaught:exception", (err, runnable) => {
// returning false here prevents Cypress from
// failing the test
return false;
});
before(() => {
cy.addDsl(dsl);
});
it("Text-Table Binding Functionality For Id", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("tablewidget");
/**
* @param(Index) Provide index value to select the row.
@ -35,6 +43,7 @@ describe("Text-Table Binding Functionality", function() {
cy.get(publish.backToEditor)
.first()
.click();
cy.get(pages.widgetsEditor).click();
cy.isSelectRow(2);
cy.openPropertyPane("textwidget");
cy.testJsontext("text", "{{Table1.selectedRow.email}}");
@ -59,6 +68,7 @@ describe("Text-Table Binding Functionality", function() {
cy.get(publish.backToEditor)
.first()
.click();
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("textwidget");
cy.testJsontext("text", "{{Table1.pageSize}}");
cy.get(commonlocators.TableRow)
@ -85,6 +95,7 @@ describe("Text-Table Binding Functionality", function() {
* @param(Index) Provide index value to select the row.
*/
cy.isSelectRow(1);
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("textwidget");
cy.testJsontext("text", JSON.stringify(this.data.textfun));
/**

View File

@ -2,14 +2,19 @@ const commonlocators = require("../../../locators/commonlocators.json");
const viewWidgetsPage = require("../../../locators/ViewWidgets.json");
const publish = require("../../../locators/publishWidgetspage.json");
const dsl = require("../../../fixtures/displayWidgetDsl.json");
const pages = require("../../../locators/Pages.json");
describe("Chart Widget Functionality", function() {
before(() => {
cy.addDsl(dsl);
});
it("Chart Widget Functionality", function() {
cy.openPropertyPane("chartwidget");
beforeEach(() => {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("chartwidget");
});
it("Chart Widget Functionality", function() {
/**
* @param{Text} Random Text
* @param{ChartWidget}Mouseover
@ -66,28 +71,24 @@ describe("Chart Widget Functionality", function() {
cy.get(commonlocators.editPropCrossButton).click();
});
it("Chart Widget Functionality To Unchecked Visible Widget", function() {
cy.openPropertyPane("chartwidget");
cy.togglebarDisable(commonlocators.visibleCheckbox);
cy.PublishtheApp();
cy.get(publish.chartWidget).should("not.be.visible");
cy.get(publish.backToEditor).click();
});
it("Chart Widget Functionality To Check Visible Widget", function() {
cy.openPropertyPane("chartwidget");
cy.togglebar(commonlocators.visibleCheckbox);
cy.PublishtheApp();
cy.get(publish.chartWidget).should("be.visible");
cy.get(publish.backToEditor).click();
});
it("Chart Widget Functionality To Uncheck Horizontal Scroll Visible", function() {
cy.openPropertyPane("chartwidget");
cy.togglebarDisable(commonlocators.horizontalScroll);
cy.PublishtheApp();
cy.get(publish.horizontalTab).should("not.visible");
cy.get(publish.backToEditor).click();
});
it("Chart Widget Functionality To Check Horizontal Scroll Visible", function() {
cy.openPropertyPane("chartwidget");
cy.togglebar(commonlocators.horizontalScroll);
cy.PublishtheApp();
cy.get(publish.horizontalTab)

View File

@ -2,12 +2,15 @@ const commonlocators = require("../../../locators/commonlocators.json");
const viewWidgetsPage = require("../../../locators/ViewWidgets.json");
const publish = require("../../../locators/publishWidgetspage.json");
const dsl = require("../../../fixtures/displayWidgetDsl.json");
const pages = require("../../../locators/Pages.json");
describe("Image Widget Functionality", function() {
before(() => {
cy.addDsl(dsl);
});
it("Image Widget Functionality", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("imagewidget");
/**
* @param{Text} Random Text
@ -40,6 +43,7 @@ describe("Image Widget Functionality", function() {
});
it("Image Widget Functionality To Unchecked Visible Widget", function() {
cy.get(publish.backToEditor).click();
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("imagewidget");
cy.togglebarDisable(commonlocators.visibleCheckbox);
cy.PublishtheApp();
@ -47,6 +51,7 @@ describe("Image Widget Functionality", function() {
cy.get(publish.backToEditor).click();
});
it("Image Widget Functionality To Check Visible Widget", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("imagewidget");
cy.togglebar(commonlocators.visibleCheckbox);
cy.PublishtheApp();

View File

@ -2,6 +2,7 @@ const widgetsPage = require("../../../locators/Widgets.json");
const commonlocators = require("../../../locators/commonlocators.json");
const publish = require("../../../locators/publishWidgetspage.json");
const dsl = require("../../../fixtures/tableWidgetDsl.json");
const pages = require("../../../locators/Pages.json");
describe("Table Widget Functionality", function() {
before(() => {
@ -9,6 +10,7 @@ describe("Table Widget Functionality", function() {
});
it("Table Widget Functionality", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("tablewidget");
/**

View File

@ -2,6 +2,7 @@ const commonlocators = require("../../../locators/commonlocators.json");
const widgetsPage = require("../../../locators/Widgets.json");
const publishPage = require("../../../locators/publishWidgetspage.json");
const dsl = require("../../../fixtures/displayWidgetDsl.json");
const pages = require("../../../locators/Pages.json");
describe("Text Widget Functionality", function() {
before(() => {
@ -9,6 +10,7 @@ describe("Text Widget Functionality", function() {
});
beforeEach(() => {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("textwidget");
});

View File

@ -66,6 +66,7 @@ describe("Dynamic input autocomplete", () => {
.focus();
cy.assertEvaluatedValuePopup("string");
cy.NavigateToEntityExplorer();
// Test on api pane
cy.NavigateToAPI_Panel();
cy.get(apiwidget.createapi).click({ force: true });

View File

@ -3,12 +3,14 @@ const formWidgetsPage = require("../../../locators/FormWidgets.json");
const widgetsPage = require("../../../locators/Widgets.json");
const publish = require("../../../locators/publishWidgetspage.json");
const dsl = require("../../../fixtures/newFormDsl.json");
const pages = require("../../../locators/Pages.json");
describe("Checkbox Widget Functionality", function() {
before(() => {
cy.addDsl(dsl);
});
it("Checkbox Widget Functionality", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("checkboxwidget");
/**
* @param{Text} Random Text
@ -43,6 +45,7 @@ describe("Checkbox Widget Functionality", function() {
cy.get(publish.backToEditor).click();
});
it("Checkbox Functionality To Check Disabled Widget", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("checkboxwidget");
cy.togglebar(commonlocators.Disablejs + " " + "input");
cy.PublishtheApp();
@ -50,6 +53,7 @@ describe("Checkbox Widget Functionality", function() {
cy.get(publish.backToEditor).click();
});
it("Checkbox Functionality To Check Enabled Widget", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("checkboxwidget");
cy.togglebarDisable(commonlocators.Disablejs + " " + "input");
cy.PublishtheApp();
@ -57,6 +61,7 @@ describe("Checkbox Widget Functionality", function() {
cy.get(publish.backToEditor).click();
});
it("Checkbox Functionality To Unchecked Visible Widget", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("checkboxwidget");
cy.togglebarDisable(commonlocators.visibleCheckbox);
cy.PublishtheApp();
@ -64,6 +69,7 @@ describe("Checkbox Widget Functionality", function() {
cy.get(publish.backToEditor).click();
});
it("Checkbox Functionality To Check Visible Widget", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("checkboxwidget");
cy.togglebar(commonlocators.visibleCheckbox);
cy.PublishtheApp();

View File

@ -2,6 +2,7 @@ const commonlocators = require("../../../locators/commonlocators.json");
const formWidgetsPage = require("../../../locators/FormWidgets.json");
const dsl = require("../../../fixtures/newFormDsl.json");
const publishPage = require("../../../locators/publishWidgetspage.json");
const pages = require("../../../locators/Pages.json");
describe("DatePicker Widget Functionality", function() {
before(() => {
@ -9,6 +10,7 @@ describe("DatePicker Widget Functionality", function() {
});
beforeEach(() => {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("datepickerwidget");
});

View File

@ -2,12 +2,14 @@ const commonlocators = require("../../../locators/commonlocators.json");
const formWidgetsPage = require("../../../locators/FormWidgets.json");
const publish = require("../../../locators/publishWidgetspage.json");
const dsl = require("../../../fixtures/newFormDsl.json");
const pages = require("../../../locators/Pages.json");
describe("Dropdown Widget Functionality", function() {
before(() => {
cy.addDsl(dsl);
});
it("Dropdown Widget Functionality", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("dropdownwidget");
/**
* @param{Text} Random Text
@ -46,6 +48,7 @@ describe("Dropdown Widget Functionality", function() {
cy.get(publish.backToEditor).click();
});
it("Dropdown Functionality To Unchecked Visible Widget", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("dropdownwidget");
cy.togglebarDisable(commonlocators.visibleCheckbox);
cy.PublishtheApp();
@ -53,6 +56,7 @@ describe("Dropdown Widget Functionality", function() {
cy.get(publish.backToEditor).click();
});
it("Dropdown Functionality To Check Visible Widget", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("dropdownwidget");
cy.togglebar(commonlocators.visibleCheckbox);
cy.PublishtheApp();

View File

@ -1,11 +1,13 @@
const commonlocators = require("../../../locators/commonlocators.json");
const dsl = require("../../../fixtures/newFormDsl.json");
const pages = require("../../../locators/Pages.json");
describe("FilePicker Widget Functionality", function() {
beforeEach(() => {
cy.addDsl(dsl);
});
it("FilePicker Widget Functionality", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("filepickerwidget");
//Checking the edit props for FilePicker and also the properties of FilePicker widget

View File

@ -2,12 +2,14 @@ const commonlocators = require("../../../locators/commonlocators.json");
const formWidgetsPage = require("../../../locators/FormWidgets.json");
const publish = require("../../../locators/publishWidgetspage.json");
const dsl = require("../../../fixtures/formdsl.json");
const pages = require("../../../locators/Pages.json");
describe("Form Widget Functionality", function() {
before(() => {
cy.addDsl(dsl);
});
it("Form Widget Functionality", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("formwidget");
/**
* @param{Text} Random Text
@ -43,6 +45,7 @@ describe("Form Widget Functionality", function() {
});
it("Form Widget Functionality To Unchecked Visible Widget", function() {
cy.get(publish.backToEditor).click();
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("formwidget");
cy.togglebarDisable(commonlocators.visibleCheckbox);
cy.PublishtheApp();

View File

@ -2,12 +2,14 @@ const commonlocators = require("../../../locators/commonlocators.json");
const dsl = require("../../../fixtures/newFormDsl.json");
const widgetsPage = require("../../../locators/Widgets.json");
const publish = require("../../../locators/publishWidgetspage.json");
const pages = require("../../../locators/Pages.json");
describe("Input Widget Functionality", function() {
before(() => {
cy.addDsl(dsl);
});
it("Input Widget Functionality", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("inputwidget");
/**
* @param{Text} Random Text
@ -63,6 +65,7 @@ describe("Input Widget Functionality", function() {
cy.get(publish.backToEditor).click({ force: true });
});
it("Input Widget Functionality To Check Disabled Widget", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("inputwidget");
cy.togglebar(commonlocators.Disablejs + " " + "input");
cy.PublishtheApp();
@ -70,6 +73,7 @@ describe("Input Widget Functionality", function() {
cy.get(publish.backToEditor).click({ force: true });
});
it("Input Widget Functionality To Check Enabled Widget", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("inputwidget");
cy.togglebarDisable(commonlocators.Disablejs + " " + "input");
cy.PublishtheApp();
@ -77,6 +81,7 @@ describe("Input Widget Functionality", function() {
cy.get(publish.backToEditor).click({ force: true });
});
it("Input Functionality To Unchecked Visible Widget", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("inputwidget");
cy.togglebarDisable(commonlocators.visibleCheckbox);
cy.PublishtheApp();
@ -84,6 +89,7 @@ describe("Input Widget Functionality", function() {
cy.get(publish.backToEditor).click({ force: true });
});
it("Input Functionality To Check Visible Widget", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("inputwidget");
cy.togglebar(commonlocators.visibleCheckbox);
cy.PublishtheApp();

View File

@ -2,11 +2,14 @@ const commonlocators = require("../../../locators/commonlocators.json");
const formWidgetsPage = require("../../../locators/FormWidgets.json");
const publish = require("../../../locators/publishWidgetspage.json");
const dsl = require("../../../fixtures/newFormDsl.json");
const pages = require("../../../locators/Pages.json");
describe("Radio Widget Functionality", function() {
before(() => {
cy.addDsl(dsl);
});
it("Radio Widget Functionality", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("radiogroupwidget");
/**
* @param{Text} Random Text
@ -56,6 +59,7 @@ describe("Radio Widget Functionality", function() {
});
it("Radio Functionality To Unchecked Visible Widget", function() {
cy.get(publish.backToEditor).click();
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("radiogroupwidget");
cy.togglebarDisable(commonlocators.visibleCheckbox);
cy.PublishtheApp();
@ -63,6 +67,7 @@ describe("Radio Widget Functionality", function() {
cy.get(publish.backToEditor).click();
});
it("Radio Functionality To Check Visible Widget", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("radiogroupwidget");
cy.togglebar(commonlocators.visibleCheckbox);
cy.PublishtheApp();

View File

@ -2,6 +2,7 @@ const commonlocators = require("../../../locators/commonlocators.json");
const formWidgetsPage = require("../../../locators/FormWidgets.json");
const dsl = require("../../../fixtures/formdsl1.json");
const publishPage = require("../../../locators/publishWidgetspage.json");
const pages = require("../../../locators/Pages.json");
describe("RichTextEditor Widget Functionality", function() {
before(() => {
@ -9,6 +10,7 @@ describe("RichTextEditor Widget Functionality", function() {
});
beforeEach(() => {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("richtexteditorwidget");
});

View File

@ -3,12 +3,14 @@ const Layoutpage = require("../../../locators/Layout.json");
const widgetsPage = require("../../../locators/Widgets.json");
const publish = require("../../../locators/publishWidgetspage.json");
const dsl = require("../../../fixtures/layoutdsl.json");
const pages = require("../../../locators/Pages.json");
describe("Tab widget test", function() {
before(() => {
cy.addDsl(dsl);
});
it("Tab Widget Functionality Test", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("tabswidget");
/**
* @param{Text} Random Text
@ -58,6 +60,7 @@ describe("Tab widget test", function() {
});
it("Tab Widget Functionality To Unchecked Visible Widget", function() {
cy.get(publish.backToEditor).click();
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("tabswidget");
cy.togglebarDisable(commonlocators.visibleCheckbox);
cy.PublishtheApp();
@ -65,6 +68,7 @@ describe("Tab widget test", function() {
cy.get(publish.backToEditor).click();
});
it("Tab Widget Functionality To Check Visible Widget", function() {
cy.get(pages.widgetsEditor).click();
cy.openPropertyPane("tabswidget");
cy.togglebar(commonlocators.visibleCheckbox);
cy.PublishtheApp();

View File

@ -33,10 +33,11 @@ describe("Create a query with a mongo datasource, run, save and then delete the
cy.runAndDeleteQuery();
cy.NavigateToDatasourceEditor();
cy.get(".t--entity-name:contains(MongoDB)").click();
cy.get("@createDatasource").then(httpResponse => {
const datasourceId = httpResponse.response.body.data.id;
const datasourceName = httpResponse.response.body.data.name;
cy.get(`[data-cy=${datasourceId}]`).click();
cy.get(`.t--entity-name:contains(${datasourceName})`).click();
});
cy.get(".t--delete-datasource").click();
cy.wait("@deleteDatasource").should(

View File

@ -32,11 +32,13 @@ describe("Create a query with a postgres datasource, run, save and then delete t
cy.runAndDeleteQuery();
cy.NavigateToDatasourceEditor();
cy.get(".t--entity-name:contains(PostgreSQL)").click();
cy.get("@createDatasource").then(httpResponse => {
const datasourceId = httpResponse.response.body.data.id;
const datasourceName = httpResponse.response.body.data.name;
cy.get(`[data-cy=${datasourceId}]`).click();
cy.get(`.t--entity-name:contains(${datasourceName})`).click();
});
cy.get(".t--delete-datasource").click();
cy.wait("@deleteDatasource").should(
"have.nested.property",

View File

@ -12,9 +12,7 @@ describe("Login from UI and check the functionality", function() {
cy.generateUUID().then(uid => {
pageid = uid;
cy.Createpage(pageid);
cy.NavigateToWidgets(pageid);
localStorage.setItem("PageName", pageid);
cy.Deletepage(pageid);
cy.DeletepageFromSideBar();
});
cy.wait("@deletePage");
cy.get("@deletePage").should("have.property", "status", 200);

View File

@ -1,5 +1,5 @@
{
"datasourceEditorIcon": ".t--nav-link-datasource-editor",
"datasourceEditorIcon": ".t--entity-name:contains('DataSources)",
"host": "input[name='datasourceConfiguration.endpoints[0].host']",
"port": "input[name='datasourceConfiguration.endpoints[0].port']",
"databaseName": "input[name='datasourceConfiguration.authentication.databaseName']",
@ -12,5 +12,6 @@
"RESTAPI": ".t--plugin-name:contains('REST API')",
"PostgreSQL": ".t--plugin-name:contains('PostgreSQL')",
"sectionAuthentication": "[data-cy=section-Authentication]",
"sectionSSL": "[data-cy=section-SSL\\ \\(optional\\)]"
"sectionSSL": "[data-cy=section-SSL\\ \\(optional\\)]",
"addDatasourceEntity": "//div[contains(@class,'t--entity group plugins')]//div[contains(@class,'t--entity-add-btn')]"
}

View File

@ -1,12 +1,20 @@
{
"pagesIcon": ".t--nav-link-manage-pages",
"pagesIcon": ".t--entity-name:contains('Pages')",
"commonWidgets": ".t--page-sidebar-CommonWidgets",
"formWidgets": ".t--page-sidebar-FormWidgets",
"viewWidgets": ".t--page-sidebar-ViewWidgets",
"widgetsEditor": ".t--nav-link-widgets-editor",
"AddPage": " .t--add-page-btn",
"AddPage": "//div[contains(@class,'t--entity group pages')]//div[contains(@class,'t--entity-add-btn')]",
"editInput": "input.bp3-editable-text-input",
"Menuaction": ".bp3-overlay-open>.bp3-transition-container",
"Delete": ":nth-child(2) > .bp3-menu-item",
"apiEditorIcon": ".t--nav-link-api-editor"
"apiEditorIcon": ".t--nav-link-api-editor",
"addEntityAPI": "//div[contains(@class,'t--entity group apis')]//div[contains(@class,'t--entity-add-btn')]",
"entityWidget": ".t--entity-name:contains('Widgets')",
"entityTable": ".t--entity-name:contains('Table1')",
"entityText": ".t--entity-name:contains('Text1')",
"entityExplorer": ".t--nav-link-entity-explorer",
"popover": "//div[contains(@class,'t--entity page')]//*[local-name()='g' and @id='Icon/Outline/more-vertical']",
"editName": ".single-select >div:contains('Edit Name')",
"deletePage": ".single-select >div:contains('Delete')"
}

View File

@ -3,5 +3,6 @@
"templateMenu": ".t--template-menu",
"runQuery": ".t--run-query",
"saveQuery": ".t--save-query",
"deleteQuery": ".t--delete-query"
"deleteQuery": ".t--delete-query",
"addQueryEntity": ".//div[contains(@class,'t--entity group queries')]//div[contains(@class,'t--entity-add-btn')]"
}

View File

@ -3,9 +3,9 @@
"searchApi": ".t--sidebar input[type=text]",
"createapi": ".t--createBlankApiCard",
"apiTxt": ".t--action-name-edit-field input",
"popover": ".bp3-popover-target >div>svg",
"popover": "//*[local-name()='g' and @id='Icon/Outline/more-vertical']",
"moveTo": ".single-select >div:contains('Move to')",
"copyTo": ".single-select >div:contains('Copy to')",
"copyTo": ".single-select >div:contains('Copy to page')",
"home": ".single-select >div:contains('Page1')",
"delete": ".single-select >div:contains('Delete')",
"path": ".t--path >div textarea",
@ -41,5 +41,6 @@
"content-Type": "(//span[@class='bp3-tree-node-label']/span)[3]",
"requestBody": "(//div[contains(@class,'bp3-collapse-body')]//textarea)[1]",
"showrequest": "span:contains('Show Request')",
"Responsetab": "//li[text()='Response Body']"
"Responsetab": "//li[text()='Response Body']",
"deleteAPI": ".t--apiFormDeleteBtn"
}

View File

@ -58,6 +58,8 @@
"selectMenuItem": ".bp3-menu li>a>div",
"evaluatedType": ".t--CodeEditor-evaluatedValue>pre",
"evaluatedCurrentValue": ".t--CodeEditor-evaluatedValue div pre",
"entityExplorersearch": "#entity-explorer-search",
"entitySearchResult": ".t--entity-name:contains('",
"saveStatusContainer": ".t--save-status-container",
"saveStatusIsSaving": "t--save-status-is-saving",
"saveStatusSuccess": ".t--save-status-success",

View File

@ -234,16 +234,13 @@ Cypress.Commands.add("DeleteApp", appName => {
cy.get(homePage.deleteButton).click({ force: true });
});
Cypress.Commands.add("Deletepage", Pagename => {
cy.get(pages.pagesIcon).click({ force: true });
cy.get(".t--page-sidebar-" + Pagename + "");
cy.get(
".t--page-sidebar-" +
Pagename +
">.t--page-sidebar-menu-actions>.bp3-popover-target",
).click({ force: true });
cy.get(pages.Menuaction).click({ force: true });
cy.get(pages.Delete).click({ force: true });
Cypress.Commands.add("DeletepageFromSideBar", () => {
cy.xpath(pages.popover)
.last()
.click({ force: true });
cy.get(pages.deletePage)
.first()
.click({ force: true });
cy.wait(2000);
});
@ -281,15 +278,27 @@ Cypress.Commands.add("SearchApp", appname => {
// Wait added because after opening the application editor, sometimes it takes a little time.
});
Cypress.Commands.add("SearchAPI", (apiname1, apiname2) => {
cy.get("span:contains(".concat(apiname2).concat(")")).should("be.visible");
cy.get(apiwidget.searchApi)
.click({ force: true })
.type(apiname1, { force: true });
cy.get("span:contains(".concat(apiname1).concat(")")).should("be.visible");
cy.get("span:contains(".concat(apiname2).concat(")")).should(
"not.be.visible",
);
Cypress.Commands.add("SearchEntity", (apiname1, apiname2) => {
cy.get(commonlocators.entityExplorersearch).should("be.visible");
cy.get("#entity-explorer-search")
.clear()
.type(apiname1);
cy.get(
commonlocators.entitySearchResult.concat(apiname1).concat("')"),
).should("be.visible");
cy.get(
commonlocators.entitySearchResult.concat(apiname2).concat("')"),
).should("not.be.visible");
});
Cypress.Commands.add("GlobalSearchEntity", apiname1 => {
cy.get(commonlocators.entityExplorersearch).should("be.visible");
cy.get("#entity-explorer-search")
.clear()
.type(apiname1);
cy.get(
commonlocators.entitySearchResult.concat(apiname1).concat("')"),
).should("be.visible");
});
Cypress.Commands.add("ResponseStatusCheck", statusCode => {
@ -303,23 +312,27 @@ Cypress.Commands.add("ResponseCheck", textTocheck => {
});
Cypress.Commands.add("NavigateToAPI_Panel", () => {
cy.get(pages.apiEditorIcon)
cy.xpath(pages.addEntityAPI)
.should("be.visible")
.click({ force: true });
cy.get("#loading").should("not.exist");
});
Cypress.Commands.add("NavigateToEntityExplorer", () => {
cy.get(pages.entityExplorer)
.should("be.visible")
.click({ force: true });
cy.get("#loading").should("not.exist");
});
Cypress.Commands.add("CreateAPI", apiname => {
cy.get(apiwidget.createApiOnSideBar)
.first()
.click({ force: true });
cy.get(apiwidget.createapi).click({ force: true });
cy.wait("@createNewApi");
cy.get(apiwidget.resourceUrl).should("be.visible");
cy.get(apiwidget.apiTxt).click();
cy.get(apiwidget.ApiName).click({ force: true });
cy.get(apiwidget.apiTxt)
.clear()
.type(apiname)
.type(apiname, { force: true })
.should("have.value", apiname)
.blur();
cy.WaitAutoSave();
@ -343,10 +356,10 @@ Cypress.Commands.add("CreateSubsequentAPI", apiname => {
Cypress.Commands.add("EditApiName", apiname => {
//cy.wait("@getUser");
cy.get(apiwidget.EditApiName).click();
cy.get(apiwidget.ApiName).click({ force: true });
cy.get(apiwidget.apiTxt)
.clear()
.type(apiname)
.type(apiname, { force: true })
.should("have.value", apiname);
cy.WaitAutoSave();
});
@ -389,16 +402,21 @@ Cypress.Commands.add("SelectAction", action => {
});
Cypress.Commands.add("ClearSearch", () => {
cy.get(apiwidget.searchApi).clear();
cy.get(commonlocators.entityExplorersearch).should("be.visible");
cy.get(commonlocators.entityExplorersearch).clear();
});
Cypress.Commands.add("SearchAPIandClick", apiname1 => {
cy.get(apiwidget.searchApi)
.click({ force: true })
.type(apiname1, { force: true });
cy.get(".t--sidebar span:contains(".concat(apiname1).concat(")"))
.should("be.visible")
.click({ force: true });
Cypress.Commands.add("SearchEntityandOpen", apiname1 => {
cy.get(commonlocators.entityExplorersearch).should("be.visible");
cy.get(commonlocators.entityExplorersearch)
.clear()
.type(apiname1);
cy.get(
commonlocators.entitySearchResult.concat(apiname1).concat("')"),
).should("be.visible");
cy.get(
commonlocators.entitySearchResult.concat(apiname1).concat("')"),
).click({ force: true });
});
Cypress.Commands.add("enterDatasourceAndPath", (datasource, path) => {
@ -529,16 +547,15 @@ Cypress.Commands.add(
);
Cypress.Commands.add("CreationOfUniqueAPIcheck", apiname => {
cy.get(apiwidget.createApiOnSideBar)
.first()
.click({ force: true });
cy.xpath(pages.addEntityAPI).click();
cy.get(apiwidget.createapi).click({ force: true });
cy.wait("@createNewApi");
// cy.wait("@getUser");
cy.get(apiwidget.resourceUrl).should("be.visible");
cy.get(apiwidget.ApiName).click({ force: true });
cy.get(apiwidget.apiTxt)
.clear()
.type(apiname)
.type(apiname, { force: true })
.should("have.value", apiname)
.focus();
cy.get(".bp3-popover-content").should($x => {
@ -561,8 +578,8 @@ Cypress.Commands.add("MoveAPIToHome", apiname => {
});
Cypress.Commands.add("MoveAPIToPage", () => {
cy.get(apiwidget.popover)
.first()
cy.xpath(apiwidget.popover)
.last()
.click({ force: true });
cy.get(apiwidget.moveTo).click({ force: true });
cy.get(apiwidget.home).click({ force: true });
@ -573,9 +590,9 @@ Cypress.Commands.add("MoveAPIToPage", () => {
);
});
Cypress.Commands.add("CopyAPIToHome", apiname => {
cy.get(apiwidget.popover)
.first()
Cypress.Commands.add("CopyAPIToHome", () => {
cy.xpath(apiwidget.popover)
.last()
.click({ force: true });
cy.get(apiwidget.copyTo).click({ force: true });
cy.get(apiwidget.home).click({ force: true });
@ -586,11 +603,27 @@ Cypress.Commands.add("CopyAPIToHome", apiname => {
);
});
Cypress.Commands.add("DeleteAPI", apiname => {
cy.get(apiwidget.popover)
.first()
Cypress.Commands.add("DeleteAPIFromSideBar", () => {
cy.xpath(apiwidget.popover)
.last()
.click({ force: true });
cy.get(apiwidget.delete).click({ force: true });
cy.wait("@deleteAction").should(
"have.nested.property",
"response.body.responseMeta.status",
200,
);
});
Cypress.Commands.add("DeleteAPI", apiname => {
cy.get(apiwidget.deleteAPI)
.first()
.click({ force: true });
cy.wait("@deleteAction").should(
"have.nested.property",
"response.body.responseMeta.status",
200,
);
});
Cypress.Commands.add("CreateModal", () => {
@ -800,7 +833,19 @@ Cypress.Commands.add("DeleteModal", () => {
Cypress.Commands.add("Createpage", Pagename => {
cy.get(pages.pagesIcon).click({ force: true });
cy.get(pages.AddPage).click();
cy.xpath(pages.AddPage)
.first()
.click();
cy.wait("@createPage").should(
"have.nested.property",
"response.body.responseMeta.status",
201,
);
cy.wait(2000);
cy.xpath(pages.popover)
.last()
.click({ force: true });
cy.get(pages.editName).click({ force: true });
cy.get(pages.editInput)
.type(Pagename)
.type("{Enter}");
@ -1022,11 +1067,8 @@ Cypress.Commands.add("tabVerify", (index, text) => {
.should("be.visible");
});
Cypress.Commands.add("NavigateToDatasourceEditor", () => {
cy.get(datasourceEditor.datasourceEditorIcon).click({ force: true });
});
Cypress.Commands.add("getPluginFormsAndCreateDatasource", () => {
/*
cy.wait("@getPluginForm").should(
"have.nested.property",
"response.body.responseMeta.status",
@ -1037,10 +1079,11 @@ Cypress.Commands.add("getPluginFormsAndCreateDatasource", () => {
"response.body.responseMeta.status",
201,
);
*/
});
Cypress.Commands.add("NavigateToApiEditor", () => {
cy.get(pages.apiEditorIcon).click({ force: true });
cy.xpath(pages.addEntityAPI).click({ force: true });
});
Cypress.Commands.add("testCreateApiButton", () => {
@ -1085,11 +1128,11 @@ Cypress.Commands.add("importCurl", () => {
});
Cypress.Commands.add("NavigateToDatasourceEditor", () => {
cy.get(datasourceEditor.datasourceEditorIcon).click({ force: true });
cy.xpath(datasourceEditor.addDatasourceEntity).click({ force: true });
});
Cypress.Commands.add("NavigateToQueryEditor", () => {
cy.get(queryEditor.queryEditorIcon).click({ force: true });
cy.xpath(queryEditor.addQueryEntity).click({ force: true });
});
Cypress.Commands.add("testSaveDatasource", () => {
@ -1335,6 +1378,7 @@ Cypress.Commands.add("startServerAndRoutes", () => {
cy.route("POST", "/api/v1/users/invite").as("postInvite");
cy.route("GET", "/api/v1/organizations/roles").as("getRoles");
cy.route("GET", "/api/v1/users/me").as("getUser");
cy.route("POST", "/api/v1/pages").as("createPage");
});
Cypress.Commands.add("alertValidate", text => {
@ -1400,11 +1444,9 @@ Cypress.Commands.add("ValidatePublishTableData", () => {
});
Cypress.Commands.add("ValidatePaginateResponseUrlData", runTestCss => {
cy.NavigateToEntityExplorer();
cy.NavigateToApiEditor();
cy.get("div[tabindex='0'] >div>span")
.contains("Api2")
.first()
.click();
cy.SearchEntityandOpen("Api2");
cy.NavigateToPaginationTab();
cy.RunAPI();
cy.get(ApiEditor.apiPaginationNextTest).click();

View File

@ -42,6 +42,7 @@
"@uppy/react": "^1.4.5",
"@uppy/url": "^1.3.2",
"@uppy/webcam": "^1.3.1",
"@welldone-software/why-did-you-render": "^4.2.5",
"algoliasearch": "^4.2.0",
"axios": "^0.18.0",
"chance": "^1.1.3",

View File

@ -48,15 +48,8 @@ class AppRouter extends React.Component<any, any> {
path={ORG_URL}
component={OrganizationLoader}
name={"Organisation"}
routeProtected
/>
<AppRoute
exact
path={USERS_URL}
component={Users}
name={"Users"}
routeProtected
/>
<AppRoute exact path={USERS_URL} component={Users} name={"Users"} />
<AppRoute
path={USER_AUTH_URL}
component={UserAuth}
@ -67,19 +60,16 @@ class AppRouter extends React.Component<any, any> {
path={APPLICATIONS_URL}
component={ApplicationListLoader}
name={"Home"}
routeProtected
/>
<AppRoute
path={BUILDER_URL}
component={EditorLoader}
name={"Editor"}
routeProtected
/>
<AppRoute
path={APP_VIEW_URL}
component={AppViewerLoader}
name={"AppViewer"}
routeProtected
logDisable
/>
<AppRoute

View File

@ -165,8 +165,8 @@ export const executeApiActionSuccess = (payload: {
payload: payload,
});
export const saveApiName = (payload: { id: string; name: string }) => ({
type: ReduxActionTypes.SAVE_API_NAME,
export const saveActionName = (payload: { id: string; name: string }) => ({
type: ReduxActionTypes.SAVE_ACTION_NAME_INIT,
payload: payload,
});
@ -189,11 +189,12 @@ export type UpdateActionPropertyActionPayload = {
export const updateActionProperty = (
payload: UpdateActionPropertyActionPayload,
) =>
batchAction({
) => {
return batchAction({
type: ReduxActionTypes.UPDATE_ACTION_PROPERTY,
payload,
});
};
export default {
createAction: createActionRequest,

View File

@ -51,7 +51,9 @@ export const createNewApiAction = (
payload: { pageId },
});
export const createNewQueryAction = (pageId: string): ReduxAction<{}> => ({
export const createNewQueryAction = (
pageId: string,
): ReduxAction<{ pageId: string }> => ({
type: ReduxActionTypes.CREATE_NEW_QUERY_ACTION,
payload: { pageId },
});

View File

@ -27,3 +27,12 @@ export const fetchApplication = (applicationId: string) => {
},
};
};
export const publishApplication = (applicationId: string) => {
return {
type: ReduxActionTypes.PUBLISH_APPLICATION_INIT,
payload: {
applicationId,
},
};
};

View File

@ -29,6 +29,13 @@ export const changeDatasource = (payload: Datasource) => {
};
};
export const switchDatasource = (id: string) => {
return {
type: ReduxActionTypes.SWITCH_DATASOURCE,
payload: { datasourceId: id },
};
};
export const testDatasource = (payload: Partial<Datasource>) => {
return {
type: ReduxActionTypes.TEST_DATASOURCE_INIT,

View File

@ -0,0 +1,10 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
export const initExplorerEntityNameEdit = (actionId: string) => {
return {
type: ReduxActionTypes.INIT_EXPLORER_ENTITY_NAME_EDIT,
payload: {
id: actionId,
},
};
};

View File

@ -10,6 +10,7 @@ import {
} from "constants/ReduxActionConstants";
import { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsReducer";
import { ContainerWidgetProps } from "widgets/ContainerWidget";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { UrlDataState } from "@appsmith/reducers/entityReducers/urlReducer";
export const fetchPageList = (
@ -27,7 +28,7 @@ export const fetchPage = (pageId: string): ReduxAction<FetchPageRequest> => {
return {
type: ReduxActionTypes.FETCH_PAGE_INIT,
payload: {
pageId,
id: pageId,
},
};
};
@ -91,6 +92,29 @@ export const updateAndSaveLayout = (widgets: FlattenedWidgetProps) => {
};
};
export const createPage = (applicationId: string, pageName: string) => {
AnalyticsUtil.logEvent("CREATE_PAGE", {
pageName,
});
return {
type: ReduxActionTypes.CREATE_PAGE_INIT,
payload: {
applicationId,
name: pageName,
},
};
};
export const updatePage = (id: string, name: string) => {
return {
type: ReduxActionTypes.UPDATE_PAGE_INIT,
payload: {
id,
name,
},
};
};
export type WidgetAddChild = {
widgetId: string;
widgetName?: string;

View File

@ -3,7 +3,7 @@ export const updateWidgetName = (widgetId: string, newName: string) => {
return {
type: ReduxActionTypes.UPDATE_WIDGET_NAME_INIT,
payload: {
widgetId,
id: widgetId,
newName,
},
};

View File

@ -61,3 +61,29 @@ export const focusWidget = (
type: ReduxActionTypes.FOCUS_WIDGET,
payload: { widgetId },
});
export const showModal = (id: string) => {
return {
type: ReduxActionTypes.SHOW_MODAL,
payload: {
modalId: id,
},
};
};
export const closeAllModals = () => {
return {
type: ReduxActionTypes.CLOSE_MODAL,
payload: {},
};
};
export const forceOpenPropertyPane = (id: string) => {
return {
type: ReduxActionTypes.SHOW_PROPERTY_PANE,
payload: {
widgetId: id,
force: true,
},
};
};

View File

@ -46,7 +46,7 @@ export interface CreateApplicationRequest {
}
export interface SetDefaultPageRequest {
pageId: string;
id: string;
applicationId: string;
}
@ -87,7 +87,7 @@ class ApplicationApi extends Api {
static changeAppViewAccessPath = (applicationId: string) =>
`${applicationId}/changeAccess`;
static setDefaultPagePath = (request: SetDefaultPageRequest) =>
`${ApplicationApi.baseURL}${request.applicationId}/page/${request.pageId}/makeDefault`;
`${ApplicationApi.baseURL}${request.applicationId}/page/${request.id}/makeDefault`;
static publishApplication(
publishApplicationRequest: PublishApplicationRequest,
): AxiosPromise<PublishApplicationResponse> {

View File

@ -6,7 +6,7 @@ import { AxiosPromise } from "axios";
import { PageAction } from "constants/ActionConstants";
export interface FetchPageRequest {
pageId: string;
id: string;
}
export interface FetchPublishedPageRequest {
@ -75,7 +75,7 @@ export interface FetchPageListResponse extends ApiResponse {
}
export interface DeletePageRequest {
pageId: string;
id: string;
}
export interface UpdateWidgetNameRequest {
@ -106,7 +106,7 @@ class PageApi extends Api {
static fetchPage(
pageRequest: FetchPageRequest,
): AxiosPromise<FetchPageResponse> {
return Api.get(PageApi.url + "/" + pageRequest.pageId);
return Api.get(PageApi.url + "/" + pageRequest.id);
}
static savePage(
@ -147,7 +147,7 @@ class PageApi extends Api {
}
static deletePage(request: DeletePageRequest): AxiosPromise<ApiResponse> {
return Api.delete(PageApi.url + "/" + request.pageId);
return Api.delete(PageApi.url + "/" + request.id);
}
static updateWidgetName(

View File

@ -0,0 +1,3 @@
<svg width="12" height="10" viewBox="0 0 12 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 0H2.5L0 5.00366L2.5 10H12V0ZM4.5 5C4.5 5.55228 4.05228 6 3.5 6C2.94772 6 2.5 5.55228 2.5 5C2.5 4.44772 2.94772 4 3.5 4C4.05228 4 4.5 4.44772 4.5 5ZM6.5 6C7.05228 6 7.5 5.55228 7.5 5C7.5 4.44772 7.05228 4 6.5 4C5.94772 4 5.5 4.44772 5.5 5C5.5 5.55228 5.94772 6 6.5 6ZM9.5 6C10.0523 6 10.5 5.55228 10.5 5C10.5 4.44772 10.0523 4 9.5 4C8.94772 4 8.5 4.44772 8.5 5C8.5 5.55228 8.94772 6 9.5 6Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 561 B

View File

@ -4,10 +4,5 @@
<mask id="mask0" mask-type="alpha" maskUnits="userSpaceOnUse" x="10" y="3" width="4" height="18">
<path id="Mask_2" fill-rule="evenodd" clip-rule="evenodd" d="M12 7C13.104 7 14 6.104 14 5C14 3.896 13.104 3 12 3C10.896 3 10 3.896 10 5C10 6.104 10.896 7 12 7ZM12 10C10.896 10 10 10.896 10 12C10 13.104 10.896 14 12 14C13.104 14 14 13.104 14 12C14 10.896 13.104 10 12 10ZM10 19C10 17.896 10.896 17 12 17C13.104 17 14 17.896 14 19C14 20.104 13.104 21 12 21C10.896 21 10 20.104 10 19Z" fill="white"/>
</mask>
<g mask="url(#mask0)">
<g id="&#240;&#159;&#142;&#168; Color">
<rect id="Base" width="24" height="24" fill="#A3B3BF"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 5.00773L10.6039 4.71741L10.1629 3.65083L10.9443 2.45913L9.54088 1.05571L8.34912 1.83716L7.28278 1.39599L6.99246 0H5.00754L4.71722 1.39599L3.65088 1.83716L2.45912 1.05571L1.0557 2.45913L1.83715 3.65083L1.39606 4.71741L0 5.00773V6.99235L1.39606 7.28267L1.83715 8.34925L1.0557 9.54095L2.45912 10.9444L3.65072 10.1629L4.71722 10.6041L5.00754 12H6.99246L7.28278 10.6041L8.34928 10.1629L9.54088 10.9444L10.9443 9.54095L10.1629 8.34925L10.6039 7.28267L12 6.99235V5.00773ZM2.82293 7.63715L3.0443 6.88257H4.16545L4.37969 7.63715H5.33184L4.17736 4.20227H3.10857L1.95647 7.63715H2.82293ZM4.00121 6.25415H3.21093L3.58702 4.97589H3.63225L4.00121 6.25415ZM6.55648 7.63715V6.63025H7.13491C7.89901 6.63025 8.42507 6.15179 8.42507 5.42102C8.42507 4.68311 7.9252 4.20227 7.18728 4.20227H5.68289V7.63715H6.55648ZM6.95162 5.97089H6.55648V4.87354H6.95639C7.32534 4.87354 7.54196 5.06158 7.54196 5.4234C7.54196 5.78046 7.32296 5.97089 6.95162 5.97089ZM9.71399 7.63715V4.20227H8.84039V7.63715H9.71399Z" fill="#D6415F"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.9135 0.561055C7.9135 0.870918 6.23457 1.12211 4.1635 1.12211C2.09243 1.12211 0.413498 0.870918 0.413498 0.561055C0.413498 0.251193 2.09243 0 4.1635 0C6.23457 0 7.9135 0.251193 7.9135 0.561055ZM3.03755 3.94381V3.20132H8.24959V1.04044C8.24959 1.04044 7.17068 1.7044 4.1248 1.7044C1.07891 1.7044 0 1.12211 0 1.12211V3.34213C0 3.34213 0.739498 3.62842 1.21327 3.72683C1.73671 3.83556 2.3448 3.90789 3.03755 3.94381ZM3.03618 6.7922C2.42801 6.76073 1.88837 6.70121 1.41063 6.61366C0.850902 6.51107 0 6.19042 0 6.19042V3.9704C0 3.9704 0.897126 4.31392 1.59814 4.41095C2.01645 4.46886 2.49801 4.50902 3.03618 4.53144V6.7922ZM1.41063 9.43546C1.88837 9.52302 2.42801 9.58253 3.03618 9.614V7.35324C2.49801 7.33082 2.01645 7.29066 1.59814 7.23276C0.897126 7.13572 0 6.7922 0 6.7922V9.01222C0 9.01222 0.850902 9.33287 1.41063 9.43546ZM9.71951 3.77119L12 6.01541H9.71951V3.77119ZM9.19505 3.77119H3.75V12H12V6.56804H9.19505V3.77119ZM10.5 7.5H5.25V8.4H10.5V7.5ZM5.25 9.375H9V10.275H5.25V9.375Z" fill="#DEAB41"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,3 @@
<svg width="20" height="18" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 8V0H13V3H7V0H0V8H7V5H9V15H13V18H20V10H13V13H11V5H13V8H20Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 190 B

View File

@ -1,5 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.5 4C0.5 2.067 2.067 0.5 4 0.5H28C29.933 0.5 31.5 2.067 31.5 4V28C31.5 29.933 29.933 31.5 28 31.5H4C2.067 31.5 0.5 29.933 0.5 28V4Z" fill="#29CCA3" stroke="#29CCA3"/>
<path d="M18 18H14V25H18V18Z" fill="white"/>
<path d="M24.4201 14.18L16.7101 6.30001C16.6172 6.20628 16.5066 6.13189 16.3847 6.08112C16.2628 6.03035 16.1321 6.00421 16.0001 6.00421C15.8681 6.00421 15.7374 6.03035 15.6155 6.08112C15.4937 6.13189 15.3831 6.20628 15.2901 6.30001L7.58012 14.19C7.39355 14.3781 7.24621 14.6013 7.14664 14.8468C7.04708 15.0923 6.99727 15.3551 7.00012 15.62V24C6.99934 24.5119 7.19489 25.0046 7.54649 25.3767C7.89809 25.7488 8.37898 25.9719 8.89012 26H12.0001V17C12.0001 16.7348 12.1055 16.4804 12.293 16.2929C12.4805 16.1054 12.7349 16 13.0001 16H19.0001C19.2653 16 19.5197 16.1054 19.7072 16.2929C19.8948 16.4804 20.0001 16.7348 20.0001 17V26H23.1101C23.6213 25.9719 24.1021 25.7488 24.4537 25.3767C24.8053 25.0046 25.0009 24.5119 25.0001 24V15.62C25.0009 15.0829 24.7929 14.5666 24.4201 14.18Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 943 B

View File

@ -0,0 +1,3 @@
<svg width="10" height="12" viewBox="0 0 10 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H6.78482V3.32332H10V12H0V0ZM1.66667 4.41933H7.33333V5.486H1.66667V4.41933ZM7.33333 8.86378H1.66667V9.93044H7.33333V8.86378ZM1.66667 6.64155H8.33333V7.70822H1.66667V6.64155ZM10 2.65982L7.46613 0V2.65982H10Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 378 B

View File

@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6 6V0H12V6H6ZM0 6V1H5V6H0ZM1 11V7H5V11H1ZM6 7V12H11V7H6Z" fill="#5BB749"/>
</svg>

After

Width:  |  Height:  |  Size: 228 B

View File

@ -1,9 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.5 14.25C8.5 13.8358 8.83579 13.5 9.25 13.5H10.75C11.1642 13.5 11.5 13.8358 11.5 14.25C11.5 14.6642 11.1642 15 10.75 15H9.25C8.83579 15 8.5 14.6642 8.5 14.25Z" fill="#F2FAFF" fill-opacity="0.9"/>
<path d="M13 14.25C13 13.8358 13.3358 13.5 13.75 13.5H22.75C23.1642 13.5 23.5 13.8358 23.5 14.25C23.5 14.6642 23.1642 15 22.75 15H13.75C13.3358 15 13 14.6642 13 14.25Z" fill="#F2FAFF" fill-opacity="0.9"/>
<path d="M8.5 17.25C8.5 16.8358 8.83579 16.5 9.25 16.5H10.75C11.1642 16.5 11.5 16.8358 11.5 17.25C11.5 17.6642 11.1642 18 10.75 18H9.25C8.83579 18 8.5 17.6642 8.5 17.25Z" fill="#F2FAFF" fill-opacity="0.9"/>
<path d="M13 17.25C13 16.8358 13.3358 16.5 13.75 16.5H22.75C23.1642 16.5 23.5 16.8358 23.5 17.25C23.5 17.6642 23.1642 18 22.75 18H13.75C13.3358 18 13 17.6642 13 17.25Z" fill="#F2FAFF" fill-opacity="0.9"/>
<path d="M8.5 20.25C8.5 19.8358 8.83579 19.5 9.25 19.5H10.75C11.1642 19.5 11.5 19.8358 11.5 20.25C11.5 20.6642 11.1642 21 10.75 21H9.25C8.83579 21 8.5 20.6642 8.5 20.25Z" fill="#F2FAFF" fill-opacity="0.9"/>
<path d="M13 20.25C13 19.8358 13.3358 19.5 13.75 19.5H22.75C23.1642 19.5 23.5 19.8358 23.5 20.25C23.5 20.6642 23.1642 21 22.75 21H13.75C13.3358 21 13 20.6642 13 20.25Z" fill="#F2FAFF" fill-opacity="0.9"/>
<path d="M5 6C4.44772 6 4 6.44772 4 7V26C4 26.5523 4.44772 27 5 27H27C27.5523 27 28 26.5523 28 26V7C28 6.44772 27.5523 6 27 6H5ZM26.5 24.5C26.5 25.0523 26.0523 25.5 25.5 25.5H6.5C5.94772 25.5 5.5 25.0523 5.5 24.5V11.5C5.5 10.9477 5.94772 10.5 6.5 10.5H25.5C26.0523 10.5 26.5 10.9477 26.5 11.5V24.5ZM26.5 8.25C26.5 8.66421 26.1642 9 25.75 9C25.3358 9 25 8.66421 25 8.25C25 7.83579 25.3358 7.5 25.75 7.5C26.1642 7.5 26.5 7.83579 26.5 8.25Z" fill="#F2FAFF" fill-opacity="0.9"/>
<svg width="18" height="16" viewBox="0 0 18 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.5 1.5H1.5V11.5H13.5V1.5Z" stroke="white"/>
<path d="M17 4H4V15H17V4Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 201 B

View File

@ -30,6 +30,7 @@ export const PositionedContainer = (props: PositionedContainerProps) => {
width: props.style.componentWidth + (props.style.widthUnit || "px"),
padding: padding + "px",
}}
id={props.widgetId}
//Before you remove: This is used by property pane to reference the element
className={
generateClassName(props.widgetId) +

View File

@ -23,6 +23,7 @@ export const RichtextEditorComponent = (
props: RichtextEditorComponentProps,
) => {
const [editorInstance, setEditorInstance] = useState(null as any);
/* eslint-disable react-hooks/exhaustive-deps */
useEffect(() => {
if (editorInstance !== null) {
editorInstance.mode.set(
@ -40,7 +41,7 @@ export const RichtextEditorComponent = (
const onChange = debounce(props.onValueChange, 200);
(window as any).tinyMCE.init({
height: "100%",
selector: `textarea#${props.widgetId}`,
selector: `textarea#rte-${props.widgetId}`,
menubar: false,
branding: false,
resize: false,
@ -81,7 +82,7 @@ export const RichtextEditorComponent = (
}, []);
return (
<StyledRTEditor>
<textarea id={props.widgetId}></textarea>
<textarea id={`rte-${props.widgetId}`}></textarea>
</StyledRTEditor>
);
};

View File

@ -25,7 +25,6 @@ import { getDefaultRefinement } from "selectors/helpSelectors";
import { getAppsmithConfigs } from "configs";
const { algolia } = getAppsmithConfigs();
const searchClient = algoliasearch(algolia.apiId, algolia.apiKey);
console.log({ algolia });
const OenLinkIcon = HelpIcons.OPEN_LINK;
const DocumentIcon = HelpIcons.DOCUMENT;
@ -220,7 +219,6 @@ const StyledPoweredBy = styled(PoweredBy)`
export default function DocumentationSearch(props: { hitsPerPage: number }) {
const dispatch = useDispatch();
const defaultRefinement = useSelector(getDefaultRefinement);
console.log({ algolia });
if (!algolia.enabled) return null;
return (
<SearchContainer className="ais-InstantSearch t--docSearchModal">

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import React, { useEffect, useState, useCallback } from "react";
import { useSelector, useDispatch } from "react-redux";
import { useParams } from "react-router-dom";
@ -11,7 +11,7 @@ import { AppState } from "reducers";
import { RestAction } from "entities/Action";
import { Page } from "constants/ReduxActionConstants";
import { saveApiName } from "actions/actionActions";
import { saveActionName } from "actions/actionActions";
import { Spinner } from "@blueprintjs/core";
const ApiNameWrapper = styled.div`
@ -67,34 +67,43 @@ export const ActionNameEditor = () => {
};
});
const hasActionNameConflict = (name: string) =>
!(
existingPageNames.indexOf(name) === -1 &&
actions.findIndex(action => action.name === name) === -1 &&
existingWidgetNames.indexOf(name) === -1
);
const hasActionNameConflict = useCallback(
(name: string) =>
!(
existingPageNames.indexOf(name) === -1 &&
actions.findIndex(action => action.name === name) === -1 &&
existingWidgetNames.indexOf(name) === -1
),
[existingPageNames, actions, existingWidgetNames],
);
const isInvalidActionName = (name: string): string | boolean => {
if (!name || name.trim().length === 0) {
return "Please enter a valid name";
} else if (
name !== currentActionConfig?.name &&
hasActionNameConflict(name)
) {
return `${name} is already being used.`;
}
return false;
};
const isInvalidActionName = useCallback(
(name: string): string | boolean => {
if (!name || name.trim().length === 0) {
return "Please enter a valid name";
} else if (
name !== currentActionConfig?.name &&
hasActionNameConflict(name)
) {
return `${name} is already being used.`;
}
return false;
},
[currentActionConfig, hasActionNameConflict],
);
const handleAPINameChange = (name: string) => {
if (
currentActionConfig &&
name !== currentActionConfig?.name &&
!isInvalidActionName(name)
) {
dispatch(saveApiName({ id: currentActionConfig.id, name }));
}
};
const handleAPINameChange = useCallback(
(name: string) => {
if (
currentActionConfig &&
name !== currentActionConfig?.name &&
!isInvalidActionName(name)
) {
dispatch(saveActionName({ id: currentActionConfig.id, name }));
}
},
[dispatch, isInvalidActionName, currentActionConfig],
);
useEffect(() => {
if (saveStatus.isSaving === false && saveStatus.error === true) {

View File

@ -11,8 +11,10 @@ import {
Intent as BlueprintIntent,
IconName,
MaybeElement,
IButtonProps,
} from "@blueprintjs/core";
import { Direction, Directions } from "utils/helpers";
import { omit } from "lodash";
const outline = css`
&&&&&& {
@ -21,14 +23,7 @@ const outline = css`
}
`;
const buttonStyles = css<{
outline?: string;
intent?: Intent;
filled?: string;
fluid?: boolean;
skin?: Skin;
iconAlignment?: Direction;
}>`
const buttonStyles = css<Partial<ButtonProps>>`
${BlueprintButtonIntentsCSS}
&&&& {
padding: ${props =>
@ -61,22 +56,20 @@ const buttonStyles = css<{
}
${props => (props.outline ? outline : "")}
`;
const StyledButton = styled(BlueprintButton)<{
outline?: string;
intent?: Intent;
filled?: string;
skin?: Skin;
iconAlignment?: Direction;
}>`
const StyledButton = styled((props: IButtonProps & Partial<ButtonProps>) => (
<BlueprintButton
{...omit(props, ["iconAlignment", "fluid", "filled", "outline"])}
/>
))`
${buttonStyles}
`;
const StyledAnchorButton = styled(BlueprintAnchorButton)<{
outline?: string;
intent?: Intent;
filled?: string;
skin?: Skin;
iconAlignment?: Direction;
}>`
const StyledAnchorButton = styled(
(props: IButtonProps & Partial<ButtonProps>) => (
<BlueprintAnchorButton
{...omit(props, ["iconAlignment", "fluid", "filled", "outline"])}
/>
),
)`
${buttonStyles}
`;
@ -114,8 +107,8 @@ export const Button = (props: ButtonProps) => {
const baseProps = {
text: props.text,
minimal: !props.filled,
outline: props.outline ? props.outline.toString() : undefined,
filled: props.filled ? props.filled.toString() : undefined,
outline: !!props.outline,
filled: !!props.filled,
intent: props.intent as BlueprintIntent,
large: props.size === "large",
small: props.size === "small",
@ -123,7 +116,7 @@ export const Button = (props: ButtonProps) => {
disabled: props.disabled,
type: props.type,
className: props.className,
fluid: props.fluid ? props.fluid.toString() : undefined,
fluid: !!props.fluid,
skin: props.skin,
iconAlignment: props.iconAlignment ? props.iconAlignment : undefined,
};

View File

@ -119,9 +119,10 @@ interface PopoverContentProps {
onMouseLeave: () => void;
}
const CurrentValueViewer = (props: {
export const CurrentValueViewer = (props: {
theme: EditorTheme;
evaluatedValue: any;
hideLabel?: boolean;
}) => {
let content = (
<CodeWrapper colorTheme={props.theme}>{"undefined"}</CodeWrapper>

View File

@ -12,6 +12,8 @@ import { DropdownOption } from "widgets/DropdownWidget";
import { ControlIconName, ControlIcons } from "icons/ControlIcons";
import { noop } from "utils/AppsmithUtils";
import { Intent } from "constants/DefaultTheme";
import { IconProps } from "constants/IconConstants";
import { Colors } from "constants/Colors";
export type ContextDropdownOption = DropdownOption & {
onSelect: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
@ -39,6 +41,7 @@ type ContextDropdownProps = {
iconSize?: number;
text?: string;
placeholder?: string;
color?: string;
};
};
@ -74,10 +77,10 @@ export const ContextDropdown = (props: ContextDropdownProps) => {
let trigger: ReactNode;
if (props.toggle.type === "icon" && props.toggle.icon) {
const TriggerElement = ControlIcons[props.toggle.icon];
const TriggerElementProps = {
style: { display: "flex" },
const TriggerElementProps: IconProps = {
width: props.toggle.iconSize,
height: props.toggle.iconSize,
color: props.toggle.color || Colors.SLATE_GRAY,
};
trigger = <TriggerElement {...TriggerElementProps} />;
}

View File

@ -1,7 +1,7 @@
import { Divider } from "@blueprintjs/core";
import styled from "styled-components";
export const StyledDivider = styled(Divider)`
export const StyledDivider = styled(Divider)<{ color?: string }>`
&& {
margin: 10px 0;
}

View File

@ -53,13 +53,13 @@ const DraggableComponent = (props: DraggableComponentProps) => {
// This state tells us which widget is selected
// The value is the widgetId of the selected widget
const selectedWidget = useSelector(
(state: AppState) => state.ui.editor.selectedWidget,
(state: AppState) => state.ui.widgetDragResize.selectedWidget,
);
// This state tels us which widget is focused
// The value is the widgetId of the focused widget.
const focusedWidget = useSelector(
(state: AppState) => state.ui.editor.focusedWidget,
(state: AppState) => state.ui.widgetDragResize.focusedWidget,
);
// This state tells us whether a `ResizableComponent` is resizing

View File

@ -5,6 +5,7 @@ import React, {
Context,
createContext,
useEffect,
memo,
} from "react";
import styled from "styled-components";
import { useDrop, XYCoord, DropTargetMonitor } from "react-dnd";
@ -30,6 +31,7 @@ import {
useWidgetSelection,
useCanvasSnapRowsUpdateHook,
} from "utils/hooks/dragResizeHooks";
import { getOccupiedSpaces } from "selectors/editorSelectors";
type DropTargetComponentProps = WidgetProps & {
children?: ReactNode;
@ -70,15 +72,15 @@ export const DropTargetContext: Context<{
persistDropTargetRows?: (widgetId: string, row: number) => void;
}> = createContext({});
export const DropTargetComponent = (props: DropTargetComponentProps) => {
export const DropTargetComponent = memo((props: DropTargetComponentProps) => {
const canDropTargetExtend = props.canExtend;
const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend);
const { updateWidget, occupiedSpaces } = useContext(EditorContext);
const { updateWidget } = useContext(EditorContext);
const occupiedSpaces = useSelector(getOccupiedSpaces);
const selectedWidget = useSelector(
(state: AppState) => state.ui.editor.selectedWidget,
(state: AppState) => state.ui.widgetDragResize.selectedWidget,
);
const isResizing = useSelector(
(state: AppState) => state.ui.widgetDragResize.isResizing,
@ -180,9 +182,14 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => {
// Only show propertypane if this is a new widget.
// If it is not a new widget, then let the DraggableComponent handle it.
showPropertyPane &&
updateWidgetParams.payload.newWidgetId &&
showPropertyPane(updateWidgetParams.payload.newWidgetId);
// Give evaluations a second to complete.
setTimeout(
() =>
showPropertyPane &&
updateWidgetParams.payload.newWidgetId &&
showPropertyPane(updateWidgetParams.payload.newWidgetId),
100,
);
// Select the widget if it is a new widget
selectWidget && selectWidget(widget.widgetId);
@ -285,6 +292,6 @@ export const DropTargetComponent = (props: DropTargetComponentProps) => {
</StyledDropTarget>
</DropTargetContext.Provider>
);
};
});
export default DropTargetComponent;

View File

@ -7,6 +7,7 @@ import styled from "styled-components";
import _ from "lodash";
import Edit from "assets/images/EditPen.svg";
import ErrorTooltip from "./ErrorTooltip";
import { Colors } from "constants/Colors";
export enum EditInteractionKind {
SINGLE,
@ -26,6 +27,8 @@ type EditableTextProps = {
isInvalid?: (value: string) => string | boolean;
editInteractionKind: EditInteractionKind;
hideEditIcon?: boolean;
minimal?: boolean;
onBlur?: (value?: string) => void;
};
const EditPen = styled.img`
@ -35,19 +38,26 @@ const EditPen = styled.img`
}
`;
const EditableTextWrapper = styled.div<{ isEditing: boolean }>`
const EditableTextWrapper = styled.div<{
isEditing: boolean;
minimal: boolean;
}>`
&& {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
& .${Classes.EDITABLE_TEXT} {
border: ${props => (props.isEditing ? "1px solid #ccc" : "none")};
border: ${props =>
props.isEditing && !props.minimal
? `1px solid ${Colors.HIT_GRAY}`
: "none"};
cursor: pointer;
padding: 5px 5px;
padding: ${props => (!props.minimal ? "5px 5px" : "0px")};
text-transform: none;
flex: 1 0 100%;
max-width: 100%;
overflow: hidden;
display: flex;
&:before,
&:after {
@ -61,11 +71,16 @@ const EditableTextWrapper = styled.div<{ isEditing: boolean }>`
}
`;
const TextContainer = styled.div<{ isValid: boolean }>`
const TextContainer = styled.div<{ isValid: boolean; minimal: boolean }>`
display: flex;
&&&& .bp3-editable-text {
border-radius: 3px;
border-color: ${props => (props.isValid ? "hsl(0,0%,80%)" : "red")};
${props => (!props.minimal ? "border-radius: 3px;" : "")}
${props =>
!props.minimal
? `border-color: ${props.isValid ? Colors.HIT_GRAY : "red"}`
: ""};
${props =>
props.minimal ? `border-bottom: 1px solid ${Colors.HIT_GRAY}` : ""}
}
`;
@ -79,7 +94,7 @@ export const EditableText = (props: EditableTextProps) => {
useEffect(() => {
if (props.forceDefault === true) setValue(props.defaultValue);
}, [props.forceDefault]);
}, [props.forceDefault, props.defaultValue]);
const edit = (e: any) => {
setIsEditing(true);
@ -87,6 +102,7 @@ export const EditableText = (props: EditableTextProps) => {
e.stopPropagation();
};
const onChange = (_value: string) => {
props.onBlur && props.onBlur();
const isInvalid = props.isInvalid ? props.isInvalid(_value) : false;
if (!isInvalid) {
props.onTextChanged(_value);
@ -115,9 +131,10 @@ export const EditableText = (props: EditableTextProps) => {
onClick={
props.editInteractionKind === EditInteractionKind.SINGLE ? edit : _.noop
}
minimal={!!props.minimal}
>
<ErrorTooltip isOpen={!!error} message={errorMessage as string}>
<TextContainer isValid={!error}>
<TextContainer isValid={!error} minimal={!!props.minimal}>
<BlueprintEditableText
disabled={!isEditing}
isEditing={isEditing}
@ -127,10 +144,12 @@ export const EditableText = (props: EditableTextProps) => {
value={value}
placeholder={props.placeholder}
className={props.className}
onCancel={props.onBlur}
/>
{!props.hideEditIcon && !props.updating && !isEditing && (
<EditPen src={Edit} alt="Edit pen" />
)}
{!props.minimal &&
!props.hideEditIcon &&
!props.updating &&
!isEditing && <EditPen src={Edit} alt="Edit pen" />}
</TextContainer>
</ErrorTooltip>
</EditableTextWrapper>

View File

@ -1,8 +1,6 @@
import React, { Context, createContext, ReactNode } from "react";
import { connect } from "react-redux";
import { AppState } from "reducers";
import { WidgetOperation } from "widgets/BaseWidget";
import { updateWidget } from "actions/pageActions";
@ -13,7 +11,6 @@ import { ExecuteActionPayload } from "constants/ActionConstants";
import { RenderModes } from "constants/WidgetConstants";
import { OccupiedSpace } from "constants/editorConstants";
import { getOccupiedSpaces } from "selectors/editorSelectors";
import {
resetChildrenMetaProperty,
updateWidgetMetaProperty,
@ -52,7 +49,6 @@ const EditorContextProvider = (props: EditorContextProviderProps) => {
updateWidget,
updateWidgetProperty,
updateWidgetMetaProperty,
occupiedSpaces,
disableDrag,
children,
resetChildrenMetaProperty,
@ -64,7 +60,6 @@ const EditorContextProvider = (props: EditorContextProviderProps) => {
updateWidget,
updateWidgetProperty,
updateWidgetMetaProperty,
occupiedSpaces,
disableDrag,
resetChildrenMetaProperty,
}}
@ -74,18 +69,6 @@ const EditorContextProvider = (props: EditorContextProviderProps) => {
);
};
/**
* TODO<Satbir>: If a property is created here, it is only available
* in editor mode. If you need a property in published app, it
* has to be copied in src/pages/AppViewer/index.tsx file as well.
* Rework to avoid duplicating the property.
*/
const mapStateToProps = (state: AppState) => {
return {
occupiedSpaces: getOccupiedSpaces(state),
};
};
const mapDispatchToProps = (dispatch: any) => {
return {
updateWidgetProperty: (
@ -122,7 +105,4 @@ const mapDispatchToProps = (dispatch: any) => {
};
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(EditorContextProvider);
export default connect(null, mapDispatchToProps)(EditorContextProvider);

View File

@ -1,54 +1,82 @@
import React, { useRef, useEffect, MutableRefObject } from "react";
import React, {
useRef,
useEffect,
MutableRefObject,
forwardRef,
Ref,
} from "react";
import styled from "styled-components";
import Prism from "prismjs";
import themes from "./themes";
import { Skin } from "constants/DefaultTheme";
// TODO(abhinav): See if this can be re-used in other places.
export enum SKINS {
LIGHT = "LIGHT",
DARK = "DARK",
}
// TODO(abhinav): This is rudimentary. Enhance it.
Prism.languages["appsmith-binding"] = {
punctuation: {
pattern: /^{{|}}$/,
},
property: {
pattern: /(\.\w+)/,
},
};
const StyledCode = styled.div<{ skin: SKINS }>`
${props => (props.skin === SKINS.DARK ? themes.DARK : themes.LIGHT)}
const StyledCode = styled.div<{ skin: Skin }>`
position: relative;
${props => (props.skin === Skin.DARK ? themes.DARK : themes.LIGHT)};
padding: 0 0px;
}
`;
/* When adding an entry please make sure to include it in the craco.common.config.js as well */
export enum SYNTAX_HIGHLIGHTING_SUPPORTED_LANGUAGES {
JAVASCRIPT = "language-javascript", // Please note that we're using the CSS class name required by prismjs.
JAVASCRIPT = "language-javascript",
APPSMITH = "language-appsmith-binding", // Please note that we're using the CSS class name required by prismjs.
}
type HighlightedCodeProps = {
codeText: string;
language?: SYNTAX_HIGHLIGHTING_SUPPORTED_LANGUAGES;
skin?: SKINS;
skin?: Skin;
multiline?: boolean;
onClick?: () => void;
className?: string;
};
/* eslint-disable react/display-name */
export const HighlightedCode = forwardRef(
(props: HighlightedCodeProps, ref: Ref<HTMLDivElement>) => {
const codeRef: MutableRefObject<HTMLElement | null> = useRef(null);
export const HighlightedCode = (props: HighlightedCodeProps) => {
const codeBlockRef: MutableRefObject<HTMLElement | null> = useRef(null);
// Highlight when component renders with new props.
// Skin is irrelevant here, as it only uses css.
// Skinning is handled in StyledCode component.
useEffect(() => {
if (codeRef.current) {
// When this is run, the code text is tokenized
// into HTML on which the theme CSS is applied
Prism.highlightElement(codeRef.current);
}
}, [props.codeText, props.language, codeRef]);
// Highlight when component renders with new props.
// Skin is irrelevant here, as it only uses css.
// Skinning is handled in StyledCode component.
useEffect(() => {
if (codeBlockRef.current) {
// When this is run, the code text is tokenized
// into HTML on which the theme CSS is applied
Prism.highlightElement(codeBlockRef.current);
}
}, [props.codeText, props.language]);
// Set the default language to javascript if not provided.
const language =
props.language || SYNTAX_HIGHLIGHTING_SUPPORTED_LANGUAGES.JAVASCRIPT;
// Set the default language to javascript if not provided.
const language =
props.language || SYNTAX_HIGHLIGHTING_SUPPORTED_LANGUAGES.JAVASCRIPT;
return (
<StyledCode skin={props.skin || SKINS.DARK}>
<code ref={codeBlockRef} className={language}>
{props.codeText}
</code>
</StyledCode>
);
};
return (
<StyledCode
skin={props.skin || Skin.DARK}
onClick={props.onClick}
ref={ref}
className={props.className}
>
{!props.multiline && (
<code ref={codeRef} className={language}>
{props.codeText}
</code>
)}
</StyledCode>
);
},
);
export default HighlightedCode;

View File

@ -243,7 +243,7 @@ export const DARK = css`
.token.constant,
.token.symbol,
.token.builtin {
color: hsl(53, 89%, 79%); /* #F9EE98 */
color: #29cca3;
}
.token.attr-name,

View File

@ -2,30 +2,22 @@ import React from "react";
import styled from "styled-components";
import { NavLink } from "react-router-dom";
import AnalyticsUtil from "utils/AnalyticsUtil";
import NotificationIcon from "components/designSystems/appsmith/NotificationIcon";
import { theme } from "constants/DefaultTheme";
import { Colors } from "constants/Colors";
type MenuBarItemProps = {
icon: Function;
path: string;
title: string;
exact: boolean;
exact?: boolean;
width: number;
height: number;
external?: boolean;
className?: string;
highlight?: boolean;
onClick?: Function;
isActive: (currentPath: string, expectedPath: string) => boolean;
};
// const AnmiatedNotificationIcon = <NotificationIcon pla></NotificationIcon>
const StyledNotificationIcon = styled(NotificationIcon)`
position: absolute;
top: -4px;
right: -3px;
`;
type Props = MenuBarItemProps;
const IconContainer = styled.div<{
@ -39,8 +31,8 @@ const IconContainer = styled.div<{
margin-bottom: 5px;
background-color: ${props => props.theme.colors.menuButtonBGInactive};
border-radius: ${props => props.theme.radii[1]}px;
height: ${props => props.height}px;
width: ${props => props.width}px;
width: ${props => props.width + 8}px;
height: ${props => props.width + 8}px;
svg path {
fill: ${props => props.theme.colors.menuIconColorInactive};
}
@ -57,16 +49,14 @@ const ItemContainer = styled.div`
color: ${props => props.theme.colors.textOnDarkBG};
font-size: ${props => props.theme.fontSizes[1]}px;
cursor: pointer;
background-color: ${props => props.theme.colors.navBG};
&:hover {
background-color: ${props => props.theme.colors.paneBG};
background: ${Colors.TUNDORA};
text-decoration: none;
}
color: ${props => props.theme.colors.menuButtonBGInactive};
&.active {
background-color: ${props => props.theme.colors.paneBG};
background: ${Colors.TUNDORA};
color: ${props => props.theme.colors.textOnDarkBG};
${IconContainer} {
& > div {
background-color: ${props => props.theme.colors.primaryOld};
svg path {
fill: ${props => props.theme.colors.textOnDarkBG};
@ -82,30 +72,6 @@ const ItemContainer = styled.div`
}
`;
const Anchor = styled.a`
width: 64px;
display: inline-block;
`;
const ExternalLink = function(props: any) {
return (
<Anchor
onClick={() => {
props.onClick && props.onClick();
}}
href={props.to}
className={props.className}
target="_blank"
>
{props.children}
</Anchor>
);
};
const DetailsContainer = styled.div`
position: relative;
`;
class NavBarItem extends React.Component<Props> {
render(): React.ReactNode {
const {
@ -115,16 +81,18 @@ class NavBarItem extends React.Component<Props> {
exact,
width,
height,
external,
highlight,
onClick,
isActive,
} = this.props;
const Link = external ? ExternalLink : NavLink;
return (
<ItemContainer>
<Link
<NavLink
exact={exact}
to={path}
isActive={(match, location) => {
return isActive(path, location.pathname);
}}
className={this.props.className}
onClick={() => {
onClick && onClick();
@ -133,21 +101,11 @@ class NavBarItem extends React.Component<Props> {
});
}}
>
<DetailsContainer>
<IconContainer width={width} height={height}>
{icon({ width: width - 8, height: height - 8 })}
</IconContainer>
<span>{title}</span>
{highlight && (
<StyledNotificationIcon
animate
width={9}
height={9}
color={theme.colors.primaryOld}
/>
)}
</DetailsContainer>
</Link>
<IconContainer width={width} height={height}>
{icon({ width, height })}
</IconContainer>
<span>{title}</span>
</NavLink>
</ItemContainer>
);
}

View File

@ -1,4 +1,4 @@
import React, { useContext, useRef } from "react";
import React, { useContext, useRef, memo } from "react";
import { XYCoord } from "react-dnd";
import { ContainerWidgetProps } from "widgets/ContainerWidget";
@ -38,16 +38,19 @@ import {
import AnalyticsUtil from "utils/AnalyticsUtil";
import { scrollElementIntoParentCanvasView } from "utils/helpers";
import { getNearestParentCanvas } from "utils/generators";
import { getOccupiedSpaces } from "selectors/editorSelectors";
export type ResizableComponentProps = ContainerWidgetProps<WidgetProps> & {
paddingOffset: number;
};
/* eslint-disable react/display-name */
export const ResizableComponent = (props: ResizableComponentProps) => {
export const ResizableComponent = memo((props: ResizableComponentProps) => {
const resizableRef = useRef<HTMLDivElement>(null);
// Fetch information from the context
const { updateWidget, occupiedSpaces } = useContext(EditorContext);
const { updateWidget } = useContext(EditorContext);
const occupiedSpaces = useSelector(getOccupiedSpaces);
const { updateDropTargetRows, persistDropTargetRows } = useContext(
DropTargetContext,
);
@ -56,10 +59,10 @@ export const ResizableComponent = (props: ResizableComponentProps) => {
const { selectWidget } = useWidgetSelection();
const { setIsResizing } = useWidgetDragResize();
const selectedWidget = useSelector(
(state: AppState) => state.ui.editor.selectedWidget,
(state: AppState) => state.ui.widgetDragResize.selectedWidget,
);
const focusedWidget = useSelector(
(state: AppState) => state.ui.editor.focusedWidget,
(state: AppState) => state.ui.widgetDragResize.focusedWidget,
);
const isDragging = useSelector(
@ -275,6 +278,6 @@ export const ResizableComponent = (props: ResizableComponentProps) => {
</VisibilityContainer>
</Resizable>
);
};
});
export default ResizableComponent;

View File

@ -1,108 +1,32 @@
import React from "react";
import { Switch } from "react-router";
import React, { memo } from "react";
import { Switch, Route } from "react-router";
import styled from "styled-components";
import {
API_EDITOR_URL,
BUILDER_URL,
API_EDITOR_ID_URL,
PAGE_LIST_EDITOR_URL,
DATA_SOURCES_EDITOR_URL,
DATA_SOURCES_EDITOR_ID_URL,
QUERIES_EDITOR_URL,
QUERIES_EDITOR_ID_URL,
getCurlImportPageURL,
API_EDITOR_URL_WITH_SELECTED_PAGE_ID,
getProviderTemplatesURL,
} from "constants/routes";
import { WIDGETS_URL } from "constants/routes";
import WidgetSidebar from "pages/Editor/WidgetSidebar";
import QuerySidebar from "pages/Editor/QuerySidebar";
import DataSourceSidebar from "pages/Editor/DataSourceSidebar";
import ApiSidebar from "pages/Editor/ApiSidebar";
import PageListSidebar from "pages/Editor/PageListSidebar";
import AppRoute from "pages/common/AppRoute";
import ExplorerSidebar from "pages/Editor/Explorer";
const SidebarWrapper = styled.div`
background-color: ${props => props.theme.colors.paneBG};
padding: 5px 0;
padding: 0px 0 0 6px;
color: ${props => props.theme.colors.textOnDarkBG};
overflow-y: auto;
`;
export const Sidebar = () => {
export const Sidebar = memo(() => {
return (
<SidebarWrapper className="t--sidebar">
<Switch>
<AppRoute
<Route
exact
path={BUILDER_URL}
path={WIDGETS_URL()}
component={WidgetSidebar}
name={"WidgetSidebar"}
/>
<AppRoute
exact
path={API_EDITOR_URL()}
component={ApiSidebar}
name={"ApiSidebar"}
/>
<AppRoute
exact
path={API_EDITOR_ID_URL()}
component={ApiSidebar}
name={"ApiSidebar"}
/>
<AppRoute
exact
path={PAGE_LIST_EDITOR_URL()}
component={PageListSidebar}
name={"PageListSidebar"}
/>
<AppRoute
exact
path={getCurlImportPageURL()}
component={ApiSidebar}
name={"ApiSidebar"}
/>
<AppRoute
exact
path={getProviderTemplatesURL()}
component={ApiSidebar}
name={"ApiSidebar"}
/>
<AppRoute
exact
path={API_EDITOR_URL_WITH_SELECTED_PAGE_ID()}
component={ApiSidebar}
name={"ApiSidebar"}
/>
<AppRoute
exact
path={QUERIES_EDITOR_URL()}
component={QuerySidebar}
name={"QuerySidebar"}
/>
<AppRoute
exact
path={QUERIES_EDITOR_ID_URL()}
component={QuerySidebar}
name={"QuerySidebar"}
/>
<AppRoute
exact
path={DATA_SOURCES_EDITOR_URL()}
component={DataSourceSidebar}
name="DataSourceSidebar"
/>
<AppRoute
exact
path={DATA_SOURCES_EDITOR_ID_URL()}
component={DataSourceSidebar}
name="DataSourceSidebar"
/>
<Route component={ExplorerSidebar} name={"ExplorerSidebar"} />
</Switch>
</SidebarWrapper>
);
};
});
Sidebar.displayName = "Sidebar";
export default Sidebar;

View File

@ -0,0 +1,2 @@
import { Tooltip } from "@blueprintjs/core";
export default Tooltip;

View File

@ -53,10 +53,10 @@ export const WidgetNameComponent = (props: WidgetNameComponentProps) => {
(state: AppState) => state.ui.propertyPane,
);
const selectedWidget = useSelector(
(state: AppState) => state.ui.editor.selectedWidget,
(state: AppState) => state.ui.widgetDragResize.selectedWidget,
);
const focusedWidget = useSelector(
(state: AppState) => state.ui.editor.focusedWidget,
(state: AppState) => state.ui.widgetDragResize.focusedWidget,
);
const isResizing = useSelector(

View File

@ -1,5 +1,5 @@
import React from "react";
import _ from "lodash";
import React, { useState } from "react";
import { find, noop } from "lodash";
import { DropdownOption } from "widgets/DropdownWidget";
import {
StyledPopover,
@ -11,6 +11,7 @@ import {
Button as BlueprintButton,
PopoverInteractionKind,
PopoverPosition,
IPopoverSharedProps,
} from "@blueprintjs/core";
import { IconNames } from "@blueprintjs/icons";
@ -33,6 +34,8 @@ type TreeDropdownProps = {
) => React.ReactNode;
displayValue?: string;
toggle?: React.ReactNode;
className?: string;
modifiers?: IPopoverSharedProps["modifiers"];
};
function getSelectedOption(
@ -50,7 +53,7 @@ function getSelectedOption(
if (option.value === selectedValue) {
selectedOption = option;
} else {
const childOption = _.find(option.children, {
const childOption = find(option.children, {
value: selectedValue,
});
if (childOption) {
@ -78,6 +81,8 @@ export default function TreeDropdown(props: TreeDropdownProps) {
optionTree,
);
const [isOpen, setIsOpen] = useState<boolean>(false);
const handleSelect = (option: TreeDropdownOption) => {
if (option.onSelect) {
option.onSelect(option, props.onSelect);
@ -97,13 +102,22 @@ export default function TreeDropdown(props: TreeDropdownProps) {
active={isSelected}
key={option.value}
icon={option.id === "create" ? "plus" : undefined}
onClick={option.children ? _.noop : () => handleSelect(option)}
onClick={
option.children
? noop
: (e: any) => {
handleSelect(option);
setIsOpen(false);
e.stopPropagation();
}
}
text={option.label}
intent={option.intent}
popoverProps={{
minimal: true,
interactionKind: PopoverInteractionKind.CLICK,
position: PopoverPosition.RIGHT,
targetProps: { onClick: (e: any) => e.stopPropagation() },
}}
>
{option.children && option.children.map(renderTreeOption)}
@ -130,10 +144,21 @@ export default function TreeDropdown(props: TreeDropdownProps) {
);
return (
<StyledPopover
usePortal={true}
minimal={true}
isOpen={isOpen}
minimal
content={menuItems}
position={PopoverPosition.AUTO_END}
className={props.className}
modifiers={props.modifiers}
onClose={() => {
setIsOpen(false);
}}
targetProps={{
onClick: (e: any) => {
setIsOpen(true);
e.stopPropagation();
},
}}
>
{toggle ? toggle : defaultToggle}
</StyledPopover>

View File

@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import React from "react";
import { FieldArray, WrappedFieldArrayProps } from "redux-form";
import styled from "styled-components";
import { Icon } from "@blueprintjs/core";
@ -20,14 +20,6 @@ const FormRowWithLabel = styled(FormRow)`
`;
const KeyValueRow = (props: Props & WrappedFieldArrayProps) => {
useEffect(() => {
// Always maintain 2 rows
if (props.fields.length < 2 && props.pushFields !== false) {
for (let i = props.fields.length; i < 2; i += 1) {
props.fields.push({ key: "", value: "" });
}
}
}, [props.fields, props.pushFields]);
return (
<React.Fragment>
{props.fields.length && (
@ -152,7 +144,6 @@ type Props = {
const KeyValueFieldArray = (props: Props) => {
return (
<FieldArray
name={props.name}
component={KeyValueRow}
rerenderOnEveryChange={false}
{...props}

View File

@ -1,7 +1,20 @@
import { RestAction } from "entities/Action";
import { DEFAULT_ACTION_TIMEOUT } from "constants/ApiConstants";
import { zipObject } from "lodash";
export const HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"];
const HTTP_METHOD_COLORS = [
"#457AE6",
"#EABB0C",
"#5BB749",
"#E22C2C",
"#6D6D6D",
];
export const HTTP_METHOD_COLOR_MAP = zipObject(
HTTP_METHODS,
HTTP_METHOD_COLORS,
);
export const HTTP_METHOD_OPTIONS = HTTP_METHODS.map(method => ({
label: method,

View File

@ -19,9 +19,14 @@ export const Colors: Record<string, string> = {
BLACK: "#000000",
BLACK_PEARL: "#040627",
CODE_GRAY: "#090707",
SHARK: "#21282C",
SHARK2: "#232324",
MINE_SHAFT: "#262626",
DEEP_SPACE: "#272E32",
OUTER_SPACE: "#363E44",
TUNDORA: "#404040",
DOVE_GRAY: "#6D6D6D",
SLATE_GRAY: "#768896",
PORCELAIN: "#EBEEF0",
HIT_GRAY: "#A1ACB3",
@ -33,6 +38,7 @@ export const Colors: Record<string, string> = {
GREEN: "#29CCA3",
JUNGLE_GREEN: "#24BA91",
JUNGLE_GREEN_DARKER: "#30A481",
EUCALYPTUS: "#218358",
RED: "#CE4257",
ERROR_RED: "#E22C2C",
PURPLE: "#6871EF",
@ -48,6 +54,7 @@ export const Colors: Record<string, string> = {
BLUE_CHARCOAL: "#23292E",
TROUT: "#4C565E",
JAFFA_DARK: "#EF7541",
TIA_MARIA: "#CB4810",
SOLID_MERCURY: "#E5E5E5",
TROUT_DARK: "#535B62",
ALABASTER: "#F9F8F8",

View File

@ -2,7 +2,6 @@ import * as styledComponents from "styled-components";
import { Colors, Color } from "./Colors";
import * as FontFamilies from "./Fonts";
import tinycolor from "tinycolor2";
import _ from "lodash";
import { Classes } from "@blueprintjs/core";
import { AlertIcons } from "icons/AlertIcons";
import { IconProps } from "constants/IconConstants";
@ -42,6 +41,25 @@ export enum Skin {
DARK,
}
export const scrollbarDark = css`
scrollbar-color: ${props => props.theme.colors.paneCard}
${props => props.theme.colors.paneBG};
scrollbar-width: thin;
&::-webkit-scrollbar {
width: 6px;
}
&::-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};
border-radius: ${props => props.theme.radii[1]}px;
}
`;
export const BlueprintControlTransform = css`
&& {
.${Classes.CONTROL} {
@ -366,9 +384,11 @@ export const getColorWithOpacity = (color: Color, opacity: number) => {
export const getBorderCSSShorthand = (border?: ThemeBorder): string => {
const values: string[] = [];
_.forIn(border, (value, key) => {
values.push(key === "thickness" ? value + "px" : value);
});
if (border) {
for (const [key, value] of Object.entries(border)) {
values.push(key === "thickness" ? value + "px" : value.toString());
}
}
return values.join(" ");
};
@ -570,12 +590,8 @@ export const theme: Theme = {
cmBacground: Colors.BLUE_CHARCOAL,
lightningborder: Colors.ALABASTER,
},
lineHeights: [0, 14, 18, 22, 24, 28, 36, 48, 64, 80],
fonts: [
FontFamilies.DMSans,
FontFamilies.AppsmithWidget,
FontFamilies.FiraCode,
],
lineHeights: [0, 14, 16, 18, 22, 24, 28, 36, 48, 64, 80],
fonts: [FontFamilies.DMSans, FontFamilies.FiraCode],
borders: [
{
thickness: 1,

View File

@ -0,0 +1,2 @@
export const ENTITY_EXPLORER_SEARCH_ID = "entity-explorer-search";
export const ENTITY_EXPLORER_SEARCH_LOCATION_HASH = "#search";

View File

@ -1,5 +1,4 @@
export const DMSans = "DM Sans";
export const AppsmithWidget = "widget-icons";
export const FiraCode = '"Fira code", "Fira Mono", monospace';
export const HomePageRedesign =
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"';

View File

@ -8,6 +8,7 @@ export type IconProps = {
background?: Color;
onClick?: (e?: any) => void;
className?: string;
keepColors?: boolean;
};
export const IconWrapper = styled.div<IconProps>`
@ -20,11 +21,13 @@ export const IconWrapper = styled.div<IconProps>`
svg {
width: ${props => props.width || props.theme.fontSizes[7]}px;
height: ${props => props.height || props.theme.fontSizes[7]}px;
path {
fill: ${props => props.color || props.theme.colors.textOnDarkBG};
${props =>
!props.keepColors
? `path {
fill: ${props.color || props.theme.colors.textOnDarkBG};
}
circle {
fill: ${props => props.background || props.theme.colors.paneBG};
}
}
fill: ${props.background || props.theme.colors.paneBG};
}`
: ""}
`;

View File

@ -232,13 +232,20 @@ export const ReduxActionTypes: { [key: string]: string } = {
CHANGE_ORG_USER_ROLE_ERROR: "CHANGE_ORG_USER_ROLE_ERROR",
SET_DEFAULT_REFINEMENT: "SET_DEFAULT_REFINEMENT",
SET_HELP_MODAL_OPEN: "SET_HELP_MODAL_OPEN",
SAVE_API_NAME: "SAVE_API_NAME",
SAVE_API_NAME_SUCCESS: "SAVE_API_NAME_SUCCESS",
SAVE_ACTION_NAME_INIT: "SAVE_ACTION_NAME_INIT",
SAVE_ACTION_NAME_SUCCESS: "SAVE_ACTION_NAME_SUCCESS",
UPDATE_API_NAME_DRAFT: "UPDATE_API_NAME_DRAFT",
SET_ACTION_PROPERTY: "SET_ACTION_PROPERTY",
UPDATE_ACTION_PROPERTY: "UPDATE_ACTION_PROPERTY",
SWITCH_DATASOURCE: "SWITCH_DATASOURCE",
INIT_EXPLORER_ENTITY_NAME_EDIT: "INIT_EXPLORER_ENTITY_NAME_EDIT",
FETCH_ACTIONS_VIEW_MODE_INIT: "FETCH_ACTIONS_VIEW_MODE_INIT",
FETCH_ACTIONS_VIEW_MODE_SUCCESS: "FETCH_ACTIONS_VIEW_MODE_SUCCESS",
END_EXPLORER_ENTITY_NAME_EDIT: "END_EXPLORER_ENTITY_NAME_EDIT",
POPULATE_PAGEDSLS_INIT: "POPULATE_PAGEDSLS_INIT",
POPULATE_PAGEDSLS_SUCCESS: "POPULATE_PAGEDSLS_SUCCESS",
FETCH_PAGE_DSL_INIT: "FETCH_PAGE_DSL_INIT",
FETCH_PAGE_DSL_SUCCESS: "FETCH_PAGE_DSL_SUCCESS",
SET_URL_DATA: "SET_URL_DATA",
};
@ -319,11 +326,14 @@ export const ReduxActionErrorTypes: { [key: string]: string } = {
CREATE_MODAL_ERROR: "CREATE_MODAL_ERROR",
FETCH_PROVIDER_DETAILS_BY_PROVIDER_ID_ERROR:
"FETCH_PROVIDER_DETAILS_BY_PROVIDER_ID_ERROR",
SAVE_API_NAME_ERROR: "SAVE_API_NAME_ERROR",
SAVE_ACTION_NAME_ERROR: "SAVE_ACTION_NAME_ERROR",
FETCH_USER_APPLICATIONS_ORGS_ERROR: "FETCH_USER_APPLICATIONS_ORGS_ERROR",
FETCH_ALL_USERS_ERROR: "FETCH_ALL_USERS_ERROR",
FETCH_ALL_ROLES_ERROR: "FETCH_ALL_ROLES_ERROR",
FETCH_ACTIONS_VIEW_MODE_ERROR: "FETCH_ACTION_VIEW_MODE_ERROR",
SAVE_API_NAME_ERROR: "SAVE_API_NAME_ERROR",
POPULATE_PAGEDSLS_ERROR: "POPULATE_PAGEDSLS_ERROR",
FETCH_PAGE_DSL_ERROR: "FETCH_PAGE_DSL_ERROR",
};
export const ReduxFormActionTypes: { [key: string]: string } = {

View File

@ -1,5 +1,3 @@
import { MenuIcons } from "icons/MenuIcons";
export const BASE_URL = "/";
export const ORG_URL = "/org";
export const PAGE_NOT_FOUND_URL = "/404";
@ -51,6 +49,19 @@ export const BUILDER_PAGE_URL = (
);
};
export const WIDGETS_URL = (
applicationId = ":applicationId",
pageId = ":pageId",
params?: Record<string, string>,
): string => {
if (!pageId) return APPLICATIONS_URL;
const queryParams = convertToQueryParams(params);
return (
`${BUILDER_BASE_URL(applicationId)}/pages/${pageId}/edit/widgets` +
queryParams
);
};
export const API_EDITOR_URL = (
applicationId = ":applicationId",
pageId = ":pageId",
@ -164,45 +175,6 @@ export const QUERY_EDITOR_URL_WITH_SELECTED_PAGE_ID = (
)}/queries?importTo=${selectedPageId}`;
};
export const EDITOR_ROUTES = [
{
icon: MenuIcons.WIDGETS_ICON,
path: BUILDER_PAGE_URL,
title: "Widgets",
className: "t--nav-link-widgets-editor",
exact: true,
},
{
icon: MenuIcons.APIS_ICON,
path: API_EDITOR_URL,
className: "t--nav-link-api-editor",
title: "APIs",
exact: false,
},
{
icon: MenuIcons.QUERIES_ICON,
className: "t--nav-link-query-editor",
path: QUERIES_EDITOR_URL,
title: "Queries",
exact: false,
},
{
icon: MenuIcons.DATASOURCES_ICON,
className: "t--nav-link-datasource-editor",
path: DATA_SOURCES_EDITOR_URL,
title: "Datasources",
exact: false,
allowed: true,
},
{
icon: MenuIcons.PAGES_ICON,
path: PAGE_LIST_EDITOR_URL,
className: "t--nav-link-manage-pages",
title: "Pages",
exact: true,
},
];
export const FORGOT_PASSWORD_URL = `${USER_AUTH_URL}/forgotPassword`;
export const RESET_PASSWORD_URL = `${USER_AUTH_URL}/resetPassword`;
export const BASE_SIGNUP_URL = `/signup`;

View File

@ -8,7 +8,7 @@ import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsRe
import { MetaState } from "reducers/entityReducers/metaReducer";
import { PageListPayload } from "constants/ReduxActionConstants";
import WidgetFactory from "utils/WidgetFactory";
import { ActionConfig, Property } from "entities/Action";
import { ActionConfig, Property, PluginType } from "entities/Action";
import { AuthUserState } from "@appsmith/reducers/entityReducers/authUserReducer";
import { UrlDataState } from "@appsmith/reducers/entityReducers/urlReducer";
@ -38,6 +38,8 @@ export interface DataTreeAction extends Omit<ActionData, "data" | "config"> {
data: ActionResponse["body"];
actionId: string;
config: Partial<ActionConfig>;
pluginType: PluginType;
name: string;
run: ActionDispatcher<RunActionPayload, [string, string, string]> | {};
dynamicBindingPathList: Property[];
ENTITY_TYPE: ENTITY_TYPE.ACTION;
@ -111,6 +113,8 @@ export class DataTreeFactory {
dataTree[config.name] = {
...a,
actionId: config.id,
name: config.name,
pluginType: config.pluginType,
config: config.actionConfiguration,
dynamicBindingPathList,
data: a.data ? a.data.body : {},

View File

@ -12,7 +12,7 @@ import { ReactComponent as DecreaseIcon } from "assets/icons/control/decrease.sv
import { ReactComponent as DraggableIcon } from "assets/icons/control/draggable.svg";
import { ReactComponent as CloseIcon } from "assets/icons/control/close.svg";
import { ReactComponent as HelpIcon } from "assets/icons/control/help.svg";
import { ReactComponent as CollapseIcon } from "assets/icons/control/collapse.svg";
import { ReactComponent as PickMyLocationSelectedIcon } from "assets/icons/control/pick-location-selected.svg";
import { ReactComponent as SettingsIcon } from "assets/icons/control/settings.svg";
import { ReactComponent as DragIcon } from "assets/icons/control/drag.svg";
@ -108,6 +108,11 @@ export const ControlIcons: {
<DragIcon />
</IconWrapper>
),
COLLAPSE_CONTROL: (props: IconProps) => (
<IconWrapper {...props}>
<CollapseIcon />
</IconWrapper>
),
SORT_CONTROL: (props: IconProps) => (
<IconWrapper {...props}>
<SortIcon />

View File

@ -4,9 +4,14 @@ import { ReactComponent as WidgetsIcon } from "assets/icons/menu/widgets.svg";
import { ReactComponent as ApisIcon } from "assets/icons/menu/api.svg";
import { ReactComponent as OrgIcon } from "assets/icons/menu/org.svg";
import { ReactComponent as PagesIcon } from "assets/icons/menu/pages.svg";
import { ReactComponent as PageIcon } from "assets/icons/menu/page.svg";
import { ReactComponent as DataSourcesIcon } from "assets/icons/menu/data-sources.svg";
import { ReactComponent as QueriesIcon } from "assets/icons/menu/queries.svg";
import { ReactComponent as HomepageIcon } from "assets/icons/menu/homepage.svg";
import { ReactComponent as ExplorerIcon } from "assets/icons/menu/explorer.svg";
import { ReactComponent as ApisColoredIcon } from "assets/icons/menu/api-colored.svg";
import { ReactComponent as DataSourcesColoredIcon } from "assets/icons/menu/datasource-colored.svg";
import { ReactComponent as WidgetsColoredIcon } from "assets/icons/menu/widgets-colored.svg";
import { Icon } from "@blueprintjs/core";
/* eslint-disable react/display-name */
@ -33,6 +38,11 @@ export const MenuIcons: {
<PagesIcon />
</IconWrapper>
),
PAGE_ICON: (props: IconProps) => (
<IconWrapper {...props}>
<PageIcon />
</IconWrapper>
),
DATASOURCES_ICON: (props: IconProps) => (
<IconWrapper {...props}>
<DataSourcesIcon />
@ -48,9 +58,29 @@ export const MenuIcons: {
<HomepageIcon />
</IconWrapper>
),
EXPLORER_ICON: (props: IconProps) => (
<IconWrapper {...props}>
<ExplorerIcon />
</IconWrapper>
),
DOCS_ICON: (props: IconProps) => (
<IconWrapper {...props}>
<Icon icon="help"></Icon>
</IconWrapper>
),
WIDGETS_COLORED_ICON: (props: IconProps) => (
<IconWrapper {...props}>
<WidgetsColoredIcon />
</IconWrapper>
),
APIS_COLORED_ICON: (props: IconProps) => (
<IconWrapper {...props}>
<ApisColoredIcon />
</IconWrapper>
),
DATASOURCES_COLORED_ICON: (props: IconProps) => (
<IconWrapper {...props}>
<DataSourcesColoredIcon />
</IconWrapper>
),
};

View File

@ -1,4 +1,4 @@
import React from "react";
import React, { JSXElementConstructor } from "react";
import { IconProps, IconWrapper } from "constants/IconConstants";
import { ReactComponent as SpinnerIcon } from "assets/icons/widget/alert.svg";
import { ReactComponent as ButtonIcon } from "assets/icons/widget/button.svg";
@ -19,11 +19,11 @@ import { ReactComponent as RichTextEditorIcon } from "assets/icons/widget/rich-t
import { ReactComponent as ChartIcon } from "assets/icons/widget/chart.svg";
import { ReactComponent as FormIcon } from "assets/icons/widget/form.svg";
import { ReactComponent as MapIcon } from "assets/icons/widget/map.svg";
import { ReactComponent as ModalIcon } from "assets/icons/widget/modal.svg";
/* eslint-disable react/display-name */
export const WidgetIcons: {
[id: string]: Function;
[id: string]: JSXElementConstructor<IconProps>;
} = {
SPINNER_WIDGET: (props: IconProps) => (
<IconWrapper {...props}>
@ -120,6 +120,11 @@ export const WidgetIcons: {
<MapIcon />
</IconWrapper>
),
MODAL_WIDGET: (props: IconProps) => (
<IconWrapper {...props}>
<ModalIcon />
</IconWrapper>
),
};
export type WidgetIcon = typeof WidgetIcons[keyof typeof WidgetIcons];

View File

@ -1,4 +1,5 @@
import React from "react";
import "./wdyr";
import { Helmet } from "react-helmet";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";

View File

@ -219,7 +219,6 @@ const WidgetConfigResponse: WidgetConfigReducerState = {
detachFromLayout: true,
canOutsideClickClose: true,
shouldScrollContents: true,
isVisible: false,
widgetName: "Modal",
children: [],
blueprint: {

View File

@ -1,4 +1,4 @@
import React, { useState } from "react";
import React from "react";
import styled from "styled-components";
import { Menu, Button } from "@blueprintjs/core";
import SideNavItem, { SideNavItemProps } from "./SideNavItem";

View File

@ -41,11 +41,7 @@ import Spinner from "components/editorComponents/Spinner";
import CurlLogo from "assets/images/Curl-logo.svg";
import { FetchProviderWithCategoryRequest } from "api/ProvidersApi";
import { Plugin } from "api/PluginApi";
import {
createNewApiAction,
setCurrentCategory,
setLastUsedEditorPage,
} from "actions/apiPaneActions";
import { createNewApiAction, setCurrentCategory } from "actions/apiPaneActions";
import { getInitialsAndColorCode } from "utils/AppsmithUtils";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { CURL } from "constants/ApiConstants";
@ -285,42 +281,6 @@ const CardList = styled.div`
}
`;
// const NoCollections = styled.div`
// padding-top: 52px;
// padding-bottom: 50px;
// text-align: center;
// width: 438px;
// margin: 0 auto;
// color: #666666;
// font-size: 14px;
// line-height: 24px;
// `;
//
// const ImportedApisCard = styled.div`
// flex: 1;
// display: inline-flex;
// flex-wrap: wrap;
// margin-left: -10px;
// justify-content: flex-start;
// text-align: center;
// border-radius: 4px;
//
// .importedApiIcon {
// display: flex;
// height: 30px;
// width: 30px;
// margin-right: 15px;
// color: ${Colors.OXFORD_BLUE};
// }
// .eachImportedApiCard {
// margin: 10px;
// width: 225px;
// height: 60px;
// display: flex;
// flex-wrap: wrap;
// }
// `;
const DropdownSelect = styled.div`
font-size: 14px;
float: right;
@ -376,7 +336,6 @@ type ApiHomeScreenProps = {
isSwitchingCategory: boolean;
createNewApiAction: (pageId: string) => void;
setCurrentCategory: (category: string) => void;
setLastUsedEditorPage: (path: string) => void;
previouslySetCategory: string;
fetchProvidersError: boolean;
};
@ -419,7 +378,6 @@ class ApiHomeScreen extends React.Component<Props, ApiHomeScreenState> {
page: 1,
});
}
this.props.setLastUsedEditorPage(this.props.match.url);
}
componentDidUpdate(prevProps: Props) {
@ -437,8 +395,10 @@ class ApiHomeScreen extends React.Component<Props, ApiHomeScreenState> {
}
}
handleCreateNew = (params: string) => {
const pageId = new URLSearchParams(params).get("importTo");
handleCreateNew = () => {
const pageId = new URLSearchParams(this.props.location.search).get(
"importTo",
);
if (pageId) {
this.props.createNewApiAction(pageId);
}
@ -480,7 +440,6 @@ class ApiHomeScreen extends React.Component<Props, ApiHomeScreenState> {
} = this.props;
const { showSearchResults } = this.state;
const queryParams: string = location.search;
let destinationPageId = new URLSearchParams(location.search).get(
"importTo",
);
@ -600,7 +559,7 @@ class ApiHomeScreen extends React.Component<Props, ApiHomeScreenState> {
<Card
interactive={false}
className="eachCard t--createBlankApiCard"
onClick={() => this.handleCreateNew(queryParams)}
onClick={this.handleCreateNew}
>
<Icon icon="plus" iconSize={20} className="createIcon" />
<p className="textBtn">Create new</p>
@ -852,8 +811,6 @@ const mapDispatchToProps = (dispatch: any) => ({
createNewApiAction: (pageId: string) => dispatch(createNewApiAction(pageId)),
setCurrentCategory: (category: string) =>
dispatch(setCurrentCategory(category)),
setLastUsedEditorPage: (path: string) =>
dispatch(setLastUsedEditorPage(path)),
});
export default connect(

View File

@ -14,7 +14,7 @@ import { Colors } from "constants/Colors";
import Button from "components/editorComponents/Button";
import CurlLogo from "assets/images/Curl-logo.svg";
const CurlImportFormContainer = styled.form`
const CurlImportFormContainer = styled.div`
display: flex;
flex-direction: column;
width: 100%;
@ -111,7 +111,6 @@ type Props = StateAndRouteProps &
class CurlImportForm extends React.Component<Props> {
render() {
const { handleSubmit, history, isImportingCurl } = this.props;
return (
<React.Fragment>
<Header>

View File

@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import React from "react";
import { connect } from "react-redux";
import { reduxForm, InjectedFormProps, formValueSelector } from "redux-form";
import {
@ -10,7 +10,6 @@ import FormLabel from "components/editorComponents/FormLabel";
import FormRow from "components/editorComponents/FormRow";
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
import { PaginationField } from "api/ActionAPI";
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import DropdownField from "components/editorComponents/form/fields/DropdownField";
import { API_EDITOR_FORM_NAME } from "constants/forms";
import { BaseTabbedView } from "components/designSystems/appsmith/TabbedView";
@ -121,10 +120,6 @@ interface APIFormProps {
actionConfigurationHeaders?: any;
actionName: string;
apiId: string;
location: {
pathname: string;
};
dispatch: any;
apiName: string;
}
@ -137,7 +132,6 @@ export const NameWrapper = styled.div`
input {
margin: 0;
box-sizing: border-box;
// border: 0;
}
`;
@ -153,17 +147,7 @@ const ApiEditorForm: React.FC<Props> = (props: Props) => {
actionConfigurationBody,
httpMethodFromForm,
actionName,
location,
dispatch,
} = props;
useEffect(() => {
dispatch({
type: ReduxActionTypes.SET_LAST_USED_EDITOR_PAGE,
payload: {
path: location.pathname,
},
});
});
const allowPostBody =
httpMethodFromForm && httpMethodFromForm !== HTTP_METHODS[0];
@ -298,6 +282,5 @@ export default connect((state: AppState) => {
})(
reduxForm<RestAction, APIFormProps>({
form: API_EDITOR_FORM_NAME,
destroyOnUnmount: false,
})(ApiEditorForm),
);

View File

@ -1,4 +1,4 @@
import React, { useEffect } from "react";
import React from "react";
import { connect } from "react-redux";
import { reduxForm, InjectedFormProps, formValueSelector } from "redux-form";
import { POST_BODY_FORMAT_OPTIONS } from "constants/ApiEditorConstants";
@ -7,7 +7,6 @@ import FormLabel from "components/editorComponents/FormLabel";
import FormRow from "components/editorComponents/FormRow";
import { BaseButton } from "components/designSystems/blueprint/ButtonComponent";
import { PaginationField, BodyFormData, Property } from "api/ActionAPI";
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import DynamicTextField from "components/editorComponents/form/fields/DynamicTextField";
import KeyValueFieldArray from "components/editorComponents/form/fields/KeyValueFieldArray";
import ApiResponseView from "components/editorComponents/ApiResponseView";
@ -130,8 +129,6 @@ const RapidApiEditorForm: React.FC<Props> = (props: Props) => {
providerImage,
providerURL,
providerCredentialSteps,
location,
dispatch,
} = props;
const postbodyResponsePresent =
@ -140,26 +137,6 @@ const RapidApiEditorForm: React.FC<Props> = (props: Props) => {
actionConfigurationBodyFormData &&
actionConfigurationBodyFormData.length > 0;
// let credentialStepsData;
// if (providerCredentialSteps.length !== 0) {
// credentialStepsData = providerCredentialSteps.split("\\n");
// }
// console.log(credentialStepsData, "credentialStepsData");
useEffect(() => {
dispatch({
type: ReduxActionTypes.SET_LAST_USED_EDITOR_PAGE,
payload: {
path: location.pathname,
},
});
});
// const abc = (text: string) => {
// console.log(text, "text");
// }
return (
<Form
onSubmit={handleSubmit}

View File

@ -28,6 +28,7 @@ import { getApiName } from "selectors/formSelectors";
import Spinner from "components/editorComponents/Spinner";
import styled from "styled-components";
import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper";
import { changeApi } from "actions/apiPaneActions";
const LoadingContainer = styled(CenteredWrapper)`
height: 50%;
@ -38,8 +39,6 @@ interface ReduxStateProps {
isRunning: Record<string, boolean>;
isDeleting: Record<string, boolean>;
isCreating: boolean;
isMoving: boolean;
isCopying: boolean;
apiName: string;
currentApplication: UserApplication;
currentPageName: string | undefined;
@ -54,6 +53,7 @@ interface ReduxActionProps {
submitForm: (name: string) => void;
runAction: (id: string, paginationField?: PaginationField) => void;
deleteAction: (id: string, name: string) => void;
changeAPIPage: (apiId: string) => void;
}
function getPageName(pages: any, pageId: string) {
@ -66,6 +66,9 @@ type Props = ReduxActionProps &
RouteComponentProps<{ apiId: string; applicationId: string; pageId: string }>;
class ApiEditor extends React.Component<Props> {
componentDidMount() {
this.props.changeAPIPage(this.props.match.params.apiId);
}
handleDeleteClick = () => {
const pageName = getPageName(
this.props.pages,
@ -79,6 +82,12 @@ class ApiEditor extends React.Component<Props> {
this.props.deleteAction(this.props.match.params.apiId, this.props.apiName);
};
componentDidUpdate(prevProps: Props) {
if (prevProps.match.params.apiId !== this.props.match.params.apiId) {
this.props.changeAPIPage(this.props.match.params.apiId);
}
}
handleRunClick = (paginationField?: PaginationField) => {
const pageName = getPageName(
this.props.pages,
@ -119,12 +128,10 @@ class ApiEditor extends React.Component<Props> {
isRunning,
isDeleting,
isCreating,
isCopying,
isMoving,
paginationType,
isEditorInitialized,
} = this.props;
if (isCreating || isCopying || isMoving || !isEditorInitialized) {
if (isCreating || !isEditorInitialized) {
return (
<LoadingContainer>
<Spinner size={30} />
@ -174,7 +181,6 @@ class ApiEditor extends React.Component<Props> {
: ""
}
apiName={this.props.apiName}
location={this.props.location}
/>
)}
@ -207,13 +213,7 @@ class ApiEditor extends React.Component<Props> {
const mapStateToProps = (state: AppState, props: any): ReduxStateProps => {
const apiAction = getActionById(state, props);
const apiName = getApiName(state, props.match.params.apiId);
const {
isDeleting,
isRunning,
isCreating,
isMoving,
isCopying,
} = state.ui.apiPane;
const { isDeleting, isRunning, isCreating } = state.ui.apiPane;
return {
actions: state.entities.actions,
currentApplication: getCurrentApplication(state),
@ -227,8 +227,6 @@ const mapStateToProps = (state: AppState, props: any): ReduxStateProps => {
isRunning,
isDeleting,
isCreating,
isMoving,
isCopying,
isEditorInitialized: getIsEditorInitialized(state),
};
};
@ -239,6 +237,7 @@ const mapDispatchToProps = (dispatch: any): ReduxActionProps => ({
dispatch(runAction(id, paginationField)),
deleteAction: (id: string, name: string) =>
dispatch(deleteAction({ id, name })),
changeAPIPage: (actionId: string) => dispatch(changeApi(actionId)),
});
export default connect(mapStateToProps, mapDispatchToProps)(ApiEditor);

View File

@ -1,239 +0,0 @@
import React from "react";
import { connect } from "react-redux";
import { RouteComponentProps } from "react-router";
import styled from "styled-components";
import { AppState } from "reducers";
import { ActionDataState } from "reducers/entityReducers/actionsReducer";
import {
API_EDITOR_URL_WITH_SELECTED_PAGE_ID,
APIEditorRouteParams,
} from "constants/routes";
import { ApiPaneReduxState } from "reducers/uiReducers/apiPaneReducer";
import {
moveActionRequest,
copyActionRequest,
deleteAction,
} from "actions/actionActions";
import {
changeApi,
createNewApiAction,
initApiPane,
} from "actions/apiPaneActions";
import EditorSidebar from "pages/Editor/EditorSidebar";
import { getNextEntityName } from "utils/AppsmithUtils";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { Page } from "constants/ReduxActionConstants";
import { RestAction } from "entities/Action";
const HTTPMethod = styled.span<{ method?: string }>`
flex: 1;
font-size: 12px;
color: ${props => {
switch (props.method) {
case "GET":
return "#29CCA3";
case "POST":
return "#F7C75B";
case "PUT":
return "#30A5E0";
case "PATCH":
return "#8E8E8E";
case "DELETE":
return "#CE4257";
default:
return "#333";
}
}};
`;
const ActionItem = styled.div`
flex: 1;
display: flex;
align-items: center;
max-width: 90%;
`;
const ActionName = styled.span`
flex: 3;
padding: 0 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
`;
interface ReduxStateProps {
actions: ActionDataState;
apiPane: ApiPaneReduxState;
pages: Page[];
}
interface ReduxDispatchProps {
onApiChange: (id: string) => void;
initApiPane: (urlId?: string) => void;
moveAction: (
id: string,
pageId: string,
name: string,
originalPageId: string,
) => void;
copyAction: (id: string, pageId: string, name: string) => void;
deleteAction: (id: string, name: string) => void;
createNewApiAction: (pageId: string) => void;
}
type Props = ReduxStateProps &
ReduxDispatchProps &
RouteComponentProps<APIEditorRouteParams>;
class ApiSidebar extends React.Component<Props> {
componentDidMount(): void {
this.props.initApiPane(this.props.match.params.apiId);
}
handleApiChange = (actionId: string) => {
this.props.onApiChange(actionId);
};
handleMove = (itemId: string, destinationPageId: string) => {
const { pages } = this.props;
const action = this.props.actions.filter(a => a.config.id === itemId)[0];
const pageApiNames = this.props.actions
.filter(a => a.config.pageId === destinationPageId)
.map(a => a.config.name);
let name = action.config.name;
const page = pages.find(page => page.pageId === destinationPageId);
AnalyticsUtil.logEvent("MOVE_API_CLICK", {
apiId: itemId,
apiName: name,
pageName: page?.pageName,
});
if (pageApiNames.indexOf(action.config.name) > -1) {
name = getNextEntityName(name, pageApiNames);
}
this.props.moveAction(
itemId,
destinationPageId,
name,
action.config.pageId,
);
};
handleCopy = (itemId: string, destinationPageId: string) => {
const { pages } = this.props;
const action = this.props.actions.filter(a => a.config.id === itemId)[0];
const pageApiNames = this.props.actions
.filter(a => a.config.pageId === destinationPageId)
.map(a => a.config.name);
let name = `${action.config.name}Copy`;
const page = pages.find(page => page.pageId === destinationPageId);
AnalyticsUtil.logEvent("DUPLICATE_API_CLICK", {
apiId: itemId,
apiName: name,
pageName: page?.pageName,
});
if (pageApiNames.indexOf(name) > -1) {
name = getNextEntityName(name, pageApiNames);
}
this.props.copyAction(itemId, destinationPageId, name);
};
handleDelete = (itemId: string, itemName: string, pageName: string) => {
AnalyticsUtil.logEvent("DELETE_API_CLICK", {
apiId: itemId,
apiName: itemName,
pageName: pageName,
});
this.props.deleteAction(itemId, itemName);
};
renderItem = (action: RestAction) => {
return (
<ActionItem
onClick={() => {
AnalyticsUtil.logEvent("API_SELECT", {
apiId: action.id,
apiName: action.name,
});
}}
>
{action.actionConfiguration ? (
<HTTPMethod method={action.actionConfiguration.httpMethod}>
{action.actionConfiguration.httpMethod}
</HTTPMethod>
) : (
<HTTPMethod />
)}
<ActionName>{action.name}</ActionName>
</ActionItem>
);
};
handleCreateNewApiClick = (selectedPageId: string) => {
const { history } = this.props;
const { pageId, applicationId } = this.props.match.params;
history.push(
API_EDITOR_URL_WITH_SELECTED_PAGE_ID(
applicationId,
pageId,
selectedPageId,
),
);
};
render() {
const {
apiPane: { isFetching },
match: {
params: { apiId },
},
actions,
} = this.props;
const data = actions
.filter(a => a.config?.pluginType === "API")
.map(a => a.config);
return (
<EditorSidebar
isLoading={isFetching}
list={data}
selectedItemId={apiId}
itemRender={this.renderItem}
onItemCreateClick={this.handleCreateNewApiClick}
onItemSelected={this.handleApiChange}
moveItem={this.handleMove}
copyItem={this.handleCopy}
deleteItem={this.handleDelete}
createButtonTitle="Create new API"
/>
);
}
}
const mapStateToProps = (state: AppState): ReduxStateProps => ({
actions: state.entities.actions,
apiPane: state.ui.apiPane,
pages: state.entities.pageList.pages,
});
const mapDispatchToProps = (dispatch: Function): ReduxDispatchProps => ({
onApiChange: (actionId: string) => dispatch(changeApi(actionId)),
initApiPane: (urlId?: string) => dispatch(initApiPane(urlId)),
moveAction: (
id: string,
destinationPageId: string,
name: string,
originalPageId: string,
) =>
dispatch(
moveActionRequest({ id, destinationPageId, originalPageId, name }),
),
copyAction: (id: string, destinationPageId: string, name: string) =>
dispatch(copyActionRequest({ id, destinationPageId, name })),
deleteAction: (id: string, name: string) =>
dispatch(deleteAction({ id, name })),
createNewApiAction: (pageId: string) => dispatch(createNewApiAction(pageId)),
});
export default connect(mapStateToProps, mapDispatchToProps)(ApiSidebar);

View File

@ -1,4 +1,4 @@
import React from "react";
import React, { memo } from "react";
import WidgetFactory from "utils/WidgetFactory";
import { RenderModes } from "constants/WidgetConstants";
import { ContainerWidgetProps } from "widgets/ContainerWidget";
@ -11,7 +11,7 @@ interface CanvasProps {
}
// TODO(abhinav): get the render mode from context
const Canvas = (props: CanvasProps) => {
const Canvas = memo((props: CanvasProps) => {
try {
return (
<React.Fragment>
@ -26,6 +26,8 @@ const Canvas = (props: CanvasProps) => {
console.log("Error rendering DSL", error);
return null;
}
};
});
Canvas.displayName = "Canvas";
export default Canvas;

View File

@ -12,6 +12,7 @@ import {
updateDatasource,
testDatasource,
deleteDatasource,
switchDatasource,
} from "actions/datasourceActions";
import { DATASOURCE_DB_FORM } from "constants/forms";
import DatasourceHome from "./DatasourceHome";
@ -44,6 +45,20 @@ type Props = ReduxStateProps &
}>;
class DataSourceEditor extends React.Component<Props> {
componentDidUpdate(prevProps: Props) {
if (
this.props.match.params.datasourceId &&
this.props.match.params.datasourceId !==
prevProps.match.params.datasourceId
) {
this.props.switchDatasource(this.props.match.params.datasourceId);
}
}
componentDidMount() {
if (this.props.match.params.datasourceId) {
this.props.switchDatasource(this.props.match.params.datasourceId);
}
}
handleSubmit = () => {
this.props.submitForm(DATASOURCE_DB_FORM);
};
@ -137,6 +152,7 @@ const mapDispatchToProps = (dispatch: any): DatasourcePaneFunctions => ({
},
testDatasource: (data: Datasource) => dispatch(testDatasource(data)),
deleteDatasource: (id: string) => dispatch(deleteDatasource({ id })),
switchDatasource: (id: string) => dispatch(switchDatasource(id)),
});
export interface DatasourcePaneFunctions {
@ -144,6 +160,7 @@ export interface DatasourcePaneFunctions {
updateDatasource: (data: Datasource) => void;
testDatasource: (data: Datasource) => void;
deleteDatasource: (id: string) => void;
switchDatasource: (id: string) => void;
}
export default connect(mapStateToProps, mapDispatchToProps)(DataSourceEditor);

View File

@ -1,364 +0,0 @@
import React from "react";
import { connect } from "react-redux";
import { initialize } from "redux-form";
import styled from "styled-components";
import { RouteComponentProps } from "react-router";
import Button from "components/editorComponents/Button";
import { DATASOURCE_DB_FORM } from "constants/forms";
import { IIconProps } from "@blueprintjs/core";
import { Colors } from "constants/Colors";
import TreeDropdown from "components/editorComponents/actioncreator/TreeDropdown";
import { BaseTextInput } from "components/designSystems/appsmith/TextInputComponent";
import { getDataSources } from "selectors/editorSelectors";
import { getPluginImages } from "selectors/entitiesSelector";
import {
initDatasourcePane,
storeDatastoreRefs,
deleteDatasource,
changeDatasource,
} from "actions/datasourceActions";
import { ControlIcons } from "icons/ControlIcons";
import { theme } from "constants/DefaultTheme";
import { selectPlugin } from "actions/datasourceActions";
import { fetchPluginForm } from "actions/pluginActions";
import { DATA_SOURCES_EDITOR_URL } from "constants/routes";
import { AppState } from "reducers";
import { Datasource } from "api/DatasourcesApi";
import Fuse from "fuse.js";
interface ReduxDispatchProps {
initDatasourcePane: (pluginType: string, urlId?: string) => void;
selectPlugin: (pluginType: string) => void;
initializeForm: (data: Record<string, any>) => void;
storeDatastoreRefs: (refsList: []) => void;
fetchFormConfig: (id: string) => void;
deleteDatasource: (id: string) => void;
onDatasourceChange: (datasource: Datasource) => void;
}
interface ReduxStateProps {
dataSources: Datasource[];
pluginImages: Record<string, string>;
datastoreRefs: Record<string, any>;
formConfigs: Record<string, []>;
drafts: Record<string, Datasource>;
}
type DataSourceSidebarProps = {};
type State = {
search: string;
};
const ActionItem = styled.div`
flex: 1;
display: flex;
align-items: center;
`;
const ActionName = styled.span`
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 130px;
margin-left: 10px;
`;
const SearchBar = styled(BaseTextInput)`
margin-bottom: 10px;
input {
background-color: #23292e;
border: none;
color: ${props => props.theme.colors.textOnDarkBG}
:focus {
background-color: #23292e;
}
}
.bp3-icon {
background-color: #23292e;
}
`;
const ItemContainer = styled.div<{
isSelected: boolean;
}>`
padding: 8px 12px;
border-radius: 4px;
align-items: center;
cursor: pointer;
display: flex;
font-size: 14px;
margin-bottom: 2px;
background-color: ${props =>
props.isSelected ? props.theme.colors.paneCard : props.theme.colors.paneBG}
:hover {
background-color: ${props => props.theme.colors.paneCard};
}
`;
const StyledImage = styled.img`
height: 20px;
width: 20px;
svg {
path {
fill: ${Colors.WHITE};
}
}
`;
const StyledAddButton = styled(Button)<IIconProps>`
padding: "9px";
&&& {
outline: none;
padding: 10px !important;
}
span {
font-weight: normal !important;
}
`;
const Controls = styled.div`
padding: 10px;
`;
const Wrapper = styled.div`
padding: 5px;
`;
const Container = styled.div`
.createBtn {
border: none;
color: ${Colors.WHITE} !important;
width: 100%;
display: block !important;
font-weight: normal;
font-size: 14px;
line-height: 20px;
&:hover {
color: ${Colors.WHITE} !important;
background-color: ${Colors.BLUE_CHARCOAL} !important;
svg {
path {
fill: ${Colors.WHITE};
}
}
}
}
.highlightButton {
color: ${Colors.WHITE} !important;
background-color: ${Colors.BLUE_CHARCOAL} !important;
display: block !important;
font-weight: normal;
font-size: 14px;
line-height: 20px;
svg {
path {
fill: ${Colors.WHITE};
}
}
}
`;
const DraftIconIndicator = styled.span<{ isHidden: boolean }>`
width: 8px;
height: 8px;
border-radius: 8px;
background-color: #f2994a;
margin: 0 5px;
opacity: ${({ isHidden }) => (isHidden ? 0 : 1)};
`;
type Props = DataSourceSidebarProps &
RouteComponentProps<{
pageId: string;
applicationId: string;
datasourceId: string;
}> &
ReduxStateProps &
ReduxDispatchProps;
const FUSE_OPTIONS = {
shouldSort: true,
threshold: 0.5,
location: 0,
minMatchCharLength: 3,
findAllMatches: true,
keys: ["name"],
};
class DataSourceSidebar extends React.Component<Props, State> {
refsCollection: any;
constructor(props: Props) {
super(props);
this.state = {
search: "",
};
this.refsCollection = {};
}
componentDidMount() {
const { dataSources, storeDatastoreRefs } = this.props;
this.refsCollection = dataSources.reduce((acc: any, value) => {
acc[value.id] = React.createRef();
return acc;
}, {});
storeDatastoreRefs(this.refsCollection);
}
shouldComponentUpdate(nextProps: Readonly<Props>): boolean {
if (Object.keys(nextProps.drafts) !== Object.keys(this.props.drafts)) {
return true;
}
return nextProps.dataSources !== this.props.dataSources;
}
handleCreateNewDatasource = () => {
const { history } = this.props;
const { pageId, applicationId } = this.props.match.params;
history.push(DATA_SOURCES_EDITOR_URL(applicationId, pageId));
};
handleItemSelected = (datasource: Datasource) => {
this.props.onDatasourceChange(datasource);
};
handleSearchChange = (e: React.ChangeEvent<{ value: string }>) => {
const value = e.target.value;
this.setState({
search: value,
});
};
getSearchFilteredList = () => {
const { search } = this.state;
const { dataSources } = this.props;
const fuse = new Fuse(dataSources, FUSE_OPTIONS);
return search ? fuse.search(search) : dataSources;
};
renderItem = () => {
const {
match: {
params: { datasourceId },
},
datastoreRefs,
deleteDatasource,
drafts,
pluginImages,
} = this.props;
const filteredList = this.getSearchFilteredList();
return filteredList.map(datasource => {
return (
<ItemContainer
data-cy={datasource.id}
ref={datastoreRefs[datasource.id]}
key={datasource.id}
isSelected={datasourceId === datasource.id}
onClick={() => this.handleItemSelected(datasource)}
>
<ActionItem>
<StyledImage
src={pluginImages[datasource.pluginId]}
className="pluginImage"
alt="Plugin Image"
/>
<ActionName>{datasource.name}</ActionName>
</ActionItem>
<DraftIconIndicator isHidden={!(datasource.id in drafts)} />
<TreeDropdown
defaultText=""
onSelect={() => {
return null;
}}
selectedValue=""
optionTree={[
{
value: "delete",
onSelect: () => deleteDatasource(datasource.id),
label: "Delete",
intent: "danger",
},
]}
toggle={
<ControlIcons.MORE_HORIZONTAL_CONTROL
width={theme.fontSizes[4]}
height={theme.fontSizes[4]}
/>
}
/>
</ItemContainer>
);
});
};
render() {
const { search } = this.state;
const {
match: {
params: { datasourceId },
},
} = this.props;
return (
<Wrapper>
<Controls>
<SearchBar
icon="search"
input={{
value: search,
onChange: this.handleSearchChange,
}}
placeholder="Search"
/>
</Controls>
<Container>
<StyledAddButton
text={"Create a new Datasource"}
icon="plus"
fluid
className={datasourceId ? "createBtn" : "highlightButton"}
onClick={this.handleCreateNewDatasource}
/>
</Container>
{this.renderItem()}
</Wrapper>
);
}
}
const mapStateToProps = (state: AppState): ReduxStateProps => {
const { drafts } = state.ui.datasourcePane;
return {
formConfigs: state.entities.plugins.formConfigs,
dataSources: getDataSources(state),
pluginImages: getPluginImages(state),
datastoreRefs: state.ui.datasourcePane.datasourceRefs,
drafts,
};
};
const mapDispatchToProps = (dispatch: Function): ReduxDispatchProps => ({
initDatasourcePane: (pluginType: string, urlId?: string) =>
dispatch(initDatasourcePane(pluginType, urlId)),
onDatasourceChange: (datasource: Datasource) =>
dispatch(changeDatasource(datasource)),
fetchFormConfig: (id: string) => dispatch(fetchPluginForm({ id })),
selectPlugin: (pluginId: string) => dispatch(selectPlugin(pluginId)),
initializeForm: (data: Record<string, any>) =>
dispatch(initialize(DATASOURCE_DB_FORM, data)),
storeDatastoreRefs: (refsList: {}) => {
dispatch(storeDatastoreRefs(refsList));
},
deleteDatasource: (id: string) => dispatch(deleteDatasource({ id })),
});
export default connect(mapStateToProps, mapDispatchToProps)(DataSourceSidebar);

Some files were not shown because too many files have changed in this diff Show More