Merge pull request #31719 from appsmithorg/release

13/03 daily promotion
This commit is contained in:
Trisha Anand 2024-03-13 12:07:56 +05:30 committed by GitHub
commit 498fb004bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
148 changed files with 6561 additions and 1579 deletions

2
.github/config.json vendored

File diff suppressed because one or more lines are too long

View File

@ -11,6 +11,7 @@ const publish = require("../../../../locators/publishWidgetspage.json");
const testdata = require("../../../../fixtures/testdata.json");
const pageid = "MyPage";
import { agHelper, propPane } from "../../../../support/Objects/ObjectsCore";
import PageList from "../../../../support/Pages/PageList";
describe(
"Binding the multiple Widgets and validating NavigateTo Page",
@ -33,7 +34,7 @@ describe(
agHelper.AddDsl("displayWidgetDsl");
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(3000);
cy.CheckAndUnfoldEntityItem("Pages");
PageList.ShowList();
PageLeftPane.assertPresence(pageid);
});

View File

@ -12,6 +12,7 @@ import {
propPane,
deployMode,
} from "../../../../support/Objects/ObjectsCore";
import PageList from "../../../../support/Pages/PageList";
describe(
"Table Widget V2 and Navigate to functionality validation",
@ -34,7 +35,7 @@ describe(
agHelper.AddDsl("displayWidgetDsl");
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);
PageLeftPane.expandCollapseItem("Pages");
PageList.ShowList();
PageLeftPane.assertPresence(pageid);
//Table Widget V2 Functionality with multiple page
EditorNavigation.SelectEntityByName("Page1", EntityType.Page);

View File

@ -4,14 +4,15 @@ import EditorNavigation, {
} from "../../../../support/Pages/EditorNavigation";
import * as _ from "../../../../support/Objects/ObjectsCore";
import PageList from "../../../../support/Pages/PageList";
describe(
"Hide / Show page test functionality",
{ tags: ["@tag.IDE"] },
function () {
it("1. Hide/Show page test ", function () {
cy.CreatePage(); // Page2
cy.CreatePage(); // Page3
PageList.AddNewPage(); // Page2
PageList.AddNewPage(); // Page3
EditorNavigation.SelectEntityByName("Page1", EntityType.Page);
_.entityExplorer.ActionContextMenuByEntityName({
entityNameinLeftSidebar: "Page2",

View File

@ -8,6 +8,7 @@ import {
deployMode,
entityExplorer,
} from "../../../../support/Objects/ObjectsCore";
import PageList from "../../../../support/Pages/PageList";
describe("Page Load tests", { tags: ["@tag.IDE"] }, () => {
afterEach(() => {
@ -20,7 +21,7 @@ describe("Page Load tests", { tags: ["@tag.IDE"] }, () => {
before(() => {
agHelper.AddDsl("PageLoadDsl");
cy.CreatePage();
PageList.AddNewPage();
cy.get("h2").contains("Drag and drop a widget here");
});

View File

@ -95,7 +95,7 @@ describe("Git discard changes:", { tags: ["@tag.Git"] }, function () {
});
it("4. Delete page2 and trigger discard flow, page2 should be available again", () => {
cy.Deletepage(page2);
PageList.DeletePage(page2);
// verify page is deleted
//entityExplorer.ExpandCollapseEntity("Pages");
PageLeftPane.assertAbsence(page2);
@ -161,7 +161,7 @@ describe("Git discard changes:", { tags: ["@tag.Git"] }, function () {
// discard changes
gitSync.DiscardChanges();
// verify page3 is removed
PageLeftPane.expandCollapseItem("Pages");
PageList.ShowList();
PageLeftPane.assertAbsence(page3);
});

View File

@ -15,6 +15,7 @@ import EditorNavigation, {
PageLeftPane,
PagePaneSegment,
} from "../../../../../support/Pages/EditorNavigation";
import PageList from "../../../../../support/Pages/PageList";
const pagename = "ChildPage";
const tempBranch = "feat/tempBranch";
@ -48,11 +49,10 @@ describe("Git sync Bug #10773", { tags: ["@tag.Git"] }, function () {
cy.wait(2000);
gitSync.CreateGitBranch(tempBranch, false);
//cy.createGitBranch(tempBranch);
cy.CheckAndUnfoldEntityItem("Pages");
// verify tempBranch should contain this page
EditorNavigation.SelectEntityByName(pagename, EntityType.Page);
// delete page from tempBranch and merge to master
cy.Deletepage(pagename);
PageList.DeletePage(pagename);
cy.get(homePageLocators.publishButton).click();
cy.get(gitSyncLocators.commitCommentInput).type("Initial Commit");
cy.get(gitSyncLocators.commitButton).click();
@ -62,12 +62,12 @@ describe("Git sync Bug #10773", { tags: ["@tag.Git"] }, function () {
cy.get(gitSyncLocators.closeGitSyncModal).click();
// verify ChildPage is not on master
cy.switchGitBranch(mainBranch);
PageLeftPane.expandCollapseItem("Pages");
PageList.ShowList();
PageLeftPane.assertAbsence(pagename);
// create another branch and verify deleted page doesn't exist on it
//cy.createGitBranch(tempBranch0);
gitSync.CreateGitBranch(tempBranch0, false);
PageLeftPane.expandCollapseItem("Pages");
PageList.ShowList();
PageLeftPane.assertAbsence(pagename);
gitSync.DeleteTestGithubRepo(repoName);
});

View File

@ -277,7 +277,6 @@ describe("Git sync apps", { tags: ["@tag.Git"] }, function () {
);
dataSources.RunQuery();
// create a new page
cy.CheckAndUnfoldEntityItem("Pages");
cy.Createpage("Child_Page");
EditorNavigation.SelectEntityByName(`${newPage} Copy`, EntityType.Page);
EditorNavigation.SelectEntityByName("get_users", EntityType.Query);
@ -459,7 +458,7 @@ describe("Git sync apps", { tags: ["@tag.Git"] }, function () {
// delete page from page settings
EditorNavigation.SelectEntityByName("Child_Page Copy", EntityType.Page);
cy.wait("@getConsolidatedData");
cy.Deletepage("Child_Page Copy");
PageList.DeletePage("Child_Page Copy");
cy.get(homePageLocators.publishButton).click();
cy.get(gitSyncLocators.commitCommentInput).type("Initial Commit");
cy.get(gitSyncLocators.commitButton).click();
@ -482,11 +481,11 @@ describe("Git sync apps", { tags: ["@tag.Git"] }, function () {
expect(cellData).to.be.equal("New Config");
});
agHelper.AssertAutoSave();
PageLeftPane.expandCollapseItem("Pages");
PageList.ShowList();
PageLeftPane.assertAbsence("Child_Page Copy");
// create another branch and verify deleted page doesn't exist on it
gitSync.CreateGitBranch(tempBranch0, true);
PageLeftPane.expandCollapseItem("Pages");
PageList.ShowList();
PageLeftPane.assertAbsence("Child_Page Copy");
});
@ -498,7 +497,7 @@ describe("Git sync apps", { tags: ["@tag.Git"] }, function () {
// import application from git
cy.importAppFromGit(repoName);
// verify page order remains same as in orignal app
cy.CheckAndUnfoldEntityItem("Pages");
PageList.ShowList();
cy.get(".t--entity-item").eq(1).contains("crudpage_1");
cy.get(".t--entity-item").eq(2).contains("crudpage_1 Copy");
cy.get(".t--entity-item").eq(3).contains("ApiCalls_1");

View File

@ -1,12 +1,10 @@
import widgetsPage from "../../../../../locators/Widgets.json";
import commonlocators from "../../../../../locators/commonlocators.json";
import gitSyncLocators from "../../../../../locators/gitSyncLocators";
import homePage from "../../../../../locators/HomePage";
import * as _ from "../../../../../support/Objects/ObjectsCore";
import {
PageLeftPane,
PagePaneSegment,
} from "../../../../../support/Pages/EditorNavigation";
import PageList from "../../../../../support/Pages/PageList";
let tempBranch = "tempBranch",
tempBranch0 = "tempBranch0",
@ -57,7 +55,6 @@ describe(
//cy.switchGitBranch(mainBranch);
_.gitSync.CreateGitBranch(tempBranch2, true);
PageLeftPane.switchSegment(PagePaneSegment.UI);
cy.CheckAndUnfoldEntityItem("Pages");
cy.Createpage("NewPage");
cy.commitAndPush();
cy.merge(mainBranch);
@ -79,7 +76,7 @@ describe(
});
it("3. Checks clean url updates across branches", () => {
cy.Deletepage("NewPage");
PageList.DeletePage("NewPage");
cy.wait(1000);
let legacyPathname = "";
let newPathname = "";

View File

@ -121,7 +121,7 @@ describe("Git sync:", { tags: ["@tag.Git"] }, function () {
cy.switchGitBranch(parentBranchKey);
PageLeftPane.expandCollapseItem("Pages");
PageList.ShowList();
PageLeftPane.assertAbsence("ParentPageRenamed");
PageLeftPane.switchSegment(PagePaneSegment.Queries);
PageLeftPane.assertAbsence("ParentApiRenamed");
@ -212,7 +212,6 @@ describe("Git sync:", { tags: ["@tag.Git"] }, function () {
cy.generateUUID().then((uuid) => {
gitSync.CreateGitBranch(childBranchKey, true);
//cy.createGitBranch(childBranchKey);
cy.CheckAndUnfoldEntityItem("Pages");
PageList.AddNewPage();
cy.get(gitSyncLocators.branchButton).click({ force: true });
cy.get(gitSyncLocators.branchSearchInput).type("{selectall}master");

View File

@ -1,5 +1,5 @@
const commonlocators = require("../../../../locators/commonlocators.json");
const pages = require("../../../../locators/Pages.json");
import PageList from "../../../../support/Pages/PageList";
describe("Dynamic Layout Functionality", function () {
it("1. Dynamic Layout - Change Layout", function () {
@ -7,7 +7,7 @@ describe("Dynamic Layout Functionality", function () {
cy.get(commonlocators.canvas).invoke("width").should("be.eq", 450);
//Dynamic Layout - New Page should have selected Layout
cy.get(pages.AddPage).first().click();
PageList.AddNewPage();
cy.get(commonlocators.canvas).invoke("width").should("be.eq", 450);
});
});

View File

@ -167,247 +167,15 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
});
});
it("3. Checks if the theme can be saved", () => {
//Click on dropDown elipses
cy.contains("Theme properties")
.closest("div")
.siblings()
.first()
.find("button")
.click({ force: true });
agHelper.AssertAutoSave();
//Click on save theme dropdown option
cy.contains("Save theme").click({ force: true });
//Type the name of the theme:
agHelper.TypeText("input[placeholder='My theme']", "testtheme");
//Click on save theme button
agHelper.ClickButton("Save theme");
agHelper.ValidateToastMessage("Theme testtheme saved");
appSettings.ClosePane();
});
it("4. Verify Save Theme after changing all properties & widgets conform to the selected theme", () => {
cy.dragAndDropToCanvas("iconbuttonwidget", { x: 300, y: 300 });
cy.assertPageSave();
cy.get("canvas").first(0).trigger("click", { force: true });
appSettings.OpenAppSettings();
appSettings.GoToThemeSettings();
//#region Change Font & verify widgets:
agHelper.GetNClick(".rc-select-selection-search-input").then(($elem) => {
cy.get($elem).click({ force: true });
cy.wait(250);
cy.get(".rc-virtual-list-holder div")
.children()
.eq(4)
.then(($childElem) => {
cy.get($childElem).click({ force: true });
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"font-family",
`${$childElem.children().last().text()}Inter, sans-serif`,
);
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"font-family",
`${$childElem.children().last().text()}Inter, sans-serif`,
);
});
});
cy.get(widgetsPage.colorPickerV2Popover).click({ force: true }).click();
cy.get(widgetsPage.colorPickerV2Color)
.eq(-15)
.then(($elem) => {
cy.get($elem).click({ force: true });
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"background-color",
$elem.css("background-color"),
);
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"background-color",
$elem.css("background-color"),
);
});
//Change the background color:
cy.get("[data-testid='theme-backgroundColor']").click({ force: true });
cy.wait(500);
cy.get(widgetsPage.colorPickerV2Popover).click({ force: true }).click();
cy.get(widgetsPage.colorPickerV2TailwindColor)
.eq(23)
.then(($elem) => {
cy.get($elem).click({ force: true });
cy.get(commonlocators.canvas).should(
"have.css",
"background-color",
$elem.css("background-color"),
);
});
cy.get(commonlocators.themeAppBorderRadiusBtn).eq(2).click({ force: true });
cy.get(`${commonlocators.themeAppBorderRadiusBtn}`)
.eq(2)
.invoke("css", "border-top-left-radius")
.then((borderRadius) => {
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"border-radius",
borderRadius,
);
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"border-radius",
borderRadius,
);
});
//#region Change the shadow & verify widgets
cy.get("[data-value='L']").eq(1).click({ force: true });
cy.get("[data-value='L']")
.eq(1)
.invoke("css", "box-shadow")
.then((boxShadow) => {
cy.get(containerShadowElement).should(
"have.css",
"box-shadow",
boxShadow,
);
});
//#region Click on dropDown elipses
cy.contains("Theme properties")
.closest("div")
.siblings()
.first()
.find("button")
.click({ force: true });
cy.wait(300);
//Click on save theme dropdown option & close it
cy.contains("Save theme").click({ force: true });
cy.wait(200);
cy.get(".ads-v2-modal__content-header-close-button").click();
//Click on save theme dropdown option & cancel it
cy.contains("Theme properties")
.closest("div")
.siblings()
.first()
.find("button")
.click({ force: true });
cy.wait(300);
cy.contains("Save theme").click({ force: true });
cy.wait(200);
cy.xpath("//span[text()='Cancel']/parent::div").click();
//Click on save theme dropdown option, give duplicte name & save it
cy.contains("Theme properties")
.closest("div")
.siblings()
.first()
.find("button")
.click({ force: true });
cy.wait(300);
cy.contains("Save theme").click({ force: true });
cy.wait(200);
//Type the name of the theme:
agHelper.TypeText("input[placeholder='My theme']", "testtheme");
cy.contains("Name must be unique");
cy.get("input[placeholder='My theme']").clear().type("VioletYellowTheme");
//Click on save theme button
agHelper.ClickButton("Save theme");
agHelper.ValidateToastMessage("Theme VioletYellowTheme saved");
});
it("5. Verify Themes exists under respective section when ChangeTheme button is cicked in properties with Apply Theme & Trash as applicable", () => {
//Click on change theme:
it("4. Verify user able to change between saved theme & already existing Featured themes", () => {
cy.get(commonlocators.changeThemeBtn).click({ force: true });
cy.xpath(applyTheme("Your themes", "testtheme"))
.click({ force: true })
.wait(1000); //Changing to testtheme
cy.contains("Applied theme")
.click()
.parent()
.siblings()
.find(".t--theme-card > main > main")
.invoke("css", "background-color")
.then((backgroudColor) => {
expect(backgroudColor).to.eq("rgb(236, 72, 153)");
});
//Check if the saved theme is present under 'Yours Themes' section with Trash button
cy.xpath(applyTheme("Your themes", "testtheme")).should("exist");
cy.xpath(themesDeletebtn("Your themes", "testtheme")).should("exist");
cy.xpath(applyTheme("Your themes", "VioletYellowTheme")).should("exist");
cy.xpath(themesDeletebtn("Your themes", "VioletYellowTheme")).should(
"exist",
);
cy.xpath(applyTheme("Featured themes", "Earth")).should("exist");
cy.xpath(themesDeletebtn("Featured themes", "Earth")).should("not.exist");
cy.xpath(applyTheme("Featured themes", "Sunrise")).should("exist");
cy.xpath(themesDeletebtn("Featured themes", "Sunrise")).should("not.exist");
cy.xpath(applyTheme("Featured themes", "Pacific")).should("exist");
cy.xpath(themesDeletebtn("Featured themes", "Pacific")).should("not.exist");
cy.xpath(applyTheme("Featured themes", "Pampas")).should("exist");
cy.xpath(themesDeletebtn("Featured themes", "Pampas")).should("not.exist");
});
it("6. Verify the custom theme can be deleted", () => {
//Delete the created theme
cy.xpath(themesDeletebtn("Your themes", "testtheme"))
.click({ force: true })
.wait(200);
cy.contains(
"Do you really want to delete this theme? This process cannot be undone.",
);
//Click on Delete theme trash icon & close it
cy.xpath("//*[text()='Are you sure?']/following-sibling::button").click();
cy.get(commonlocators.toastMsg).should("not.exist");
//Click on Delete theme trash icon & cancel it
cy.xpath(themesDeletebtn("Your themes", "testtheme"))
.click({ force: true })
.wait(200);
cy.xpath("//span[text()='No']/parent::div").click();
cy.get(commonlocators.toastMsg).should("not.exist");
//Click on Delete theme trash icon & delete it
cy.xpath(themesDeletebtn("Your themes", "testtheme"))
.click({ force: true })
.wait(200);
agHelper.ClickButton("Delete");
// cy.contains("Delete").click({ force: true });
//check for delete alert
// cy.wait(500);
agHelper.ValidateToastMessage("Theme testtheme deleted");
//cy.get(commonlocators.toastMsg).contains("Theme testtheme deleted");
cy.xpath(applyTheme("Your themes", "testtheme")).should("not.exist");
});
it("7. Verify user able to change between saved theme & already existing Featured themes", () => {
//#region Pampas
cy.xpath(applyTheme("Featured themes", "Pampas"))
.click({ force: true })
.wait(1000); //Changing to one of Featured themes
cy.contains("Applied theme")
// .click()
.click()
.parent()
.siblings()
.find(".t--theme-card > main > section > div > main")
@ -620,70 +388,38 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
expect(backgroudColor).to.eq("rgb(248, 250, 252)");
});
//#endregion
//#region VioletYellowTheme
cy.xpath(applyTheme("Your themes", "VioletYellowTheme"))
.click({ force: true })
.wait(1000); //Changing to created test theme
cy.contains("Applied theme")
// .click()
.parent()
.siblings()
.find(".t--theme-card > main > section > div > main")
.eq(0)
.invoke("css", "background-color")
.then((backgroudColor) => {
expect(backgroudColor).to.eq("rgb(219, 234, 254)");
});
cy.contains("Applied theme")
// .click()
.parent()
.siblings()
.find(".t--theme-card > main > section > div > main")
.eq(1)
.invoke("css", "background-color")
.then((backgroudColor) => {
expect(backgroudColor).to.eq("rgb(29, 78, 216)");
});
//#endregion
});
it("8. Verify widgets conform to the selected theme in Publish mode", () => {
it("5. Verify widgets conform to the selected theme in Publish mode", () => {
deployMode.DeployApp();
//cy.wait(4000); //for theme to settle
cy.get("body").should("have.css", "font-family", "Inter, sans-serif"); //Font
cy.get("body").should(
"have.css",
"font-family",
`"Nunito Sans", sans-serif`,
); //Font
cy.xpath("//div[@id='root']//section/parent::div").should(
"have.css",
"background-color",
"rgb(29, 78, 216)",
"rgb(248, 250, 252)",
); //Background Color
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"background-color",
"rgb(219, 234, 254)",
); //Widget Color
cy.get(publish.iconWidgetBtn).should(
"have.css",
"background-color",
"rgb(219, 234, 254)",
"rgb(100, 116, 139)",
); //Widget Color
cy.get(widgetsPage.widgetBtn).should("have.css", "border-radius", "24px"); //Border Radius
cy.get(publish.iconWidgetBtn).should("have.css", "border-radius", "24px"); //Border Radius
cy.get(widgetsPage.widgetBtn).should("have.css", "border-radius", "0px"); //Border Radius
cy.get(widgetsPage.widgetBtn).should("have.css", "box-shadow", "none"); //Shadow
cy.get(publish.iconWidgetBtn).should("have.css", "box-shadow", "none"); //Shadow
deployMode.NavigateBacktoEditor();
});
it("9. Verify Adding new Individual widgets & it can change Color, Border radius, Shadow & can revert [Color/Border Radius] to already selected theme", () => {
it("6. Verify Adding new Individual widgets & it can change Color, Border radius, Shadow & can revert [Color/Border Radius] to already selected theme", () => {
cy.dragAndDropToCanvas("buttonwidget", { x: 200, y: 400 }); //another button widget
cy.moveToStyleTab();
//Change Color & verify
@ -701,13 +437,8 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
cy.get(".t--widget-button1 button").should(
"have.css",
"background-color",
"rgb(219, 234, 254)",
"rgb(100, 116, 139)",
); //old widgets still conforming to theme color
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"background-color",
"rgb(219, 234, 254)",
);
});
//Change Border & verify
@ -722,22 +453,16 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
"border-radius",
borderRadius, //0px
);
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"border-radius",
"24px",
);
cy.get(".t--widget-button1 button").should(
"have.css",
"border-radius",
"24px",
"0px",
);
});
//Change Shadow & verify
cy.contains(".ads-v2-segmented-control-value-0", "Large").click();
cy.get(widgetsPage.iconWidgetBtn).should("have.css", "box-shadow", "none");
cy.get(".t--widget-button1 button").should(
"have.css",
"box-shadow",
@ -748,23 +473,17 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
deployMode.DeployApp();
//Verify Background color
cy.get(".t--widget-buttonwidget:nth-child(4) button").should(
cy.get(".t--widget-button2 button").should(
"have.css",
"background-color",
"rgb(190, 24, 93)",
); //new widget with its own color
////old widgets still conforming to theme color
cy.get(".t--widget-buttonwidget button").should(
cy.get(".t--widget-button1 button").should(
"have.css",
"background-color",
"rgb(219, 234, 254)",
);
cy.get(publish.iconWidgetBtn).should(
"have.css",
"background-color",
"rgb(219, 234, 254)",
"rgb(100, 116, 139)",
);
//Verify Border radius
@ -773,11 +492,10 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
"border-radius",
"0px",
);
cy.get(publish.iconWidgetBtn).should("have.css", "border-radius", "24px");
cy.get(".t--widget-button1 button").should(
"have.css",
"border-radius",
"24px",
"0px",
);
//Verify Box shadow
@ -786,7 +504,6 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
"box-shadow",
"rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px",
);
cy.get(publish.iconWidgetBtn).should("have.css", "box-shadow", "none");
cy.get(".t--widget-button1 button").should(
"have.css",
"box-shadow",
@ -805,7 +522,7 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
cy.get(".t--widget-button2 button").should(
"have.css",
"background-color",
"rgb(219, 234, 254)",
"rgb(100, 116, 139)",
); //verify widget reverted to theme color
cy.get(".t--property-control-borderradius .reset-button").then(($elem) => {
$elem[0].removeAttribute("display: none");
@ -814,24 +531,28 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
cy.get(".t--widget-button2 button").should(
"have.css",
"border-radius",
"24px",
"0px",
);
//the new widget with reverted styles also conforming to theme
deployMode.DeployApp();
cy.wait(4000); //for theme to settle
cy.get("body").should("have.css", "font-family", "Inter, sans-serif"); //Font
cy.get("body").should(
"have.css",
"font-family",
`"Nunito Sans", sans-serif`,
); //Font
cy.xpath("//div[@id='root']//section/parent::div").should(
"have.css",
"background-color",
"rgb(29, 78, 216)",
"rgb(248, 250, 252)",
); //Background Color
cy.get(".t--widget-button1 button").should(
"have.css",
"background-color",
"rgb(219, 234, 254)",
"rgb(100, 116, 139)",
); //Widget Color
cy.get("body").then(($ele) => {
if ($ele.find(widgetsPage.widgetBtn).length <= 1) {
@ -842,25 +563,19 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
cy.get(".t--widget-button2 button").should(
"have.css",
"background-color",
"rgb(219, 234, 254)",
); //Widget Color
cy.get(publish.iconWidgetBtn).should(
"have.css",
"background-color",
"rgb(219, 234, 254)",
"rgb(100, 116, 139)",
); //Widget Color
cy.get(".t--widget-button1 button").should(
"have.css",
"border-radius",
"24px",
"0px",
); //Border Radius
cy.get(".t--widget-button2 button").should(
"have.css",
"border-radius",
"24px",
"0px",
); //Border Radius
cy.get(publish.iconWidgetBtn).should("have.css", "border-radius", "24px"); //Border Radius
cy.get(".t--widget-button1 button").should(
"have.css",
@ -872,12 +587,10 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
"box-shadow",
"rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px",
); //Since Shadow revert option does not exixts
cy.get(publish.iconWidgetBtn).should("have.css", "box-shadow", "none"); //Shadow
deployMode.NavigateBacktoEditor();
});
it("10. Verify Chainging theme should not affect Individual widgets with changed Color, Border radius, Shadow & can revert to newly selected theme", () => {
it("7. Verify Chainging theme should not affect Individual widgets with changed Color, Border radius, Shadow & can revert to newly selected theme", () => {
cy.get("canvas").first(0).trigger("click", { force: true });
appSettings.OpenAppSettings();
@ -911,11 +624,6 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
"background-color",
"rgb(239, 68, 68)",
); //old widgets still conforming to theme color
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"background-color",
"rgb(239, 68, 68)",
);
});
//Change Border & verify
@ -930,11 +638,7 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
"border-radius",
borderRadius, //6px
);
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"border-radius",
"24px",
);
cy.get(".t--widget-button2 button").should(
"have.css",
"border-radius",
@ -944,7 +648,6 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
//Change Shadow & verify
cy.contains(".ads-v2-segmented-control-value-0", "Small").click();
cy.get(widgetsPage.iconWidgetBtn).should("have.css", "box-shadow", "none");
cy.get(".t--widget-button2 button").should(
"have.css",
"box-shadow",
@ -971,11 +674,6 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
"background-color",
"rgb(239, 68, 68)",
);
cy.get(publish.iconWidgetBtn).should(
"have.css",
"background-color",
"rgb(239, 68, 68)",
);
//Verify Border radius
cy.get(".t--widget-button1 button").should(
@ -983,7 +681,6 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
"border-radius",
"6px",
);
cy.get(publish.iconWidgetBtn).should("have.css", "border-radius", "24px");
cy.get(".t--widget-button2 button").should(
"have.css",
"border-radius",
@ -996,7 +693,6 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
"box-shadow",
"rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px",
);
cy.get(publish.iconWidgetBtn).should("have.css", "box-shadow", "none");
cy.get(".t--widget-button2 button").should(
"have.css",
"box-shadow",
@ -1056,11 +752,6 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
"background-color",
"rgb(239, 68, 68)",
); //Widget Color
cy.get(publish.iconWidgetBtn).should(
"have.css",
"background-color",
"rgb(239, 68, 68)",
); //Widget Color
cy.get(".t--widget-button1 button").should(
"have.css",
@ -1072,7 +763,6 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
"border-radius",
"24px",
); //Border Radius
cy.get(publish.iconWidgetBtn).should("have.css", "border-radius", "24px"); //Border Radius
cy.get(".t--widget-button1 button").should(
"have.css",
@ -1084,7 +774,6 @@ describe("App Theming funtionality", { tags: ["@tag.Theme"] }, function () {
"box-shadow",
"rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px",
); //Since Shadow revert option does not exixts
cy.get(publish.iconWidgetBtn).should("have.css", "box-shadow", "none"); //Shadow
deployMode.NavigateBacktoEditor();
});

View File

@ -5,7 +5,6 @@ import {
locators,
propPane,
} from "../../../../support/Objects/ObjectsCore";
import Canvas from "../../../../support/Pages/Canvas";
import EditorNavigation, {
EntityType,
PageLeftPane,
@ -94,12 +93,9 @@ describe(
);
deployMode.NavigateBacktoEditor();
// Verify multiple widgets selected groups into single container
Canvas.selectMultipleWidgets(["Input1", "Select1", "Text3"]);
agHelper.GetElement("body").type(`{${agHelper._modifierKey}}{g}`);
agHelper.Sleep(1000);
PageLeftPane.assertPresence("Container3");
entityExplorer.DeleteWidgetFromEntityExplorer("Container3");
entityExplorer.DeleteWidgetFromEntityExplorer("Input1");
entityExplorer.DeleteWidgetFromEntityExplorer("Select1");
entityExplorer.DeleteWidgetFromEntityExplorer("Text3");
});
it("4. Validate visible toggle", () => {

View File

@ -286,7 +286,7 @@ describe(
//Should check that widget input is not showing any errors on input
cy.get(widgetInput).type("123456789");
cy.focused().then(() => {
expect(Cypress.$(themelocators.popover)).not.to.exist;
cy.get(".error-tooltip .bp3-popover-content").should("not.exist");
});
});
},

View File

@ -1,3 +1,4 @@
import PageList from "../../../../../support/Pages/PageList";
const { ObjectsRegistry } = require("../../../../../support/Objects/Registry");
import EditorNavigation, {
EntityType,
@ -31,13 +32,14 @@ describe(
it("2.Check the OnSrcDocChange event call on first render", () => {
agHelper.RefreshPage();
cy.wait(2000);
cy.get(`.t--entity .page`).first().should("have.class", "activePage");
PageList.ShowList();
PageList.VerifyIsCurrentPage("Page1");
cy.openPropertyPane("iframewidget");
cy.testJsontext("srcdoc", "<h1>Hello World!</h1>");
cy.wait(2000);
cy.get(`.t--entity .page`).last().should("have.class", "activePage");
PageList.VerifyIsCurrentPage("Page2");
EditorNavigation.SelectEntityByName(page1, EntityType.Page);
cy.get(`.t--entity .page`).first().should("have.class", "activePage");
PageList.VerifyIsCurrentPage("Page1");
});
},
);

View File

@ -154,6 +154,7 @@ describe(
it("5. Verify Theme change", () => {
agHelper.PressEscape();
appSettings.OpenPaneAndChangeTheme("Pacific");
agHelper.WaitUntilToastDisappear("Theme Pacific applied");
[0, 1, 2].forEach((index) => {
agHelper.AssertAttribute(
locators._listText,

View File

@ -11,6 +11,7 @@ const containerWidgetSelector = `[type="CONTAINER_WIDGET"]`;
function dragAndDropToWidget(widgetType, destinationWidget, { x, y }) {
const selector = `.t--widget-card-draggable-${widgetType}`;
cy.wait(800);
PageLeftPane.switchToAddNew();
cy.get(selector)
.first()
.scrollIntoView()
@ -34,9 +35,9 @@ function deleteAllWidgetsInContainer() {
force: true,
});
cy.get("body").type(`{${modifierKey}}{a}`);
cy.get("body").type("{del}");
cy.wait(200);
cy.get("body").type("{del}");
cy.get(commonlocators.layoutControls).should("be.visible");
}
function checkSelectedRadioValue(selector, value) {
@ -63,12 +64,13 @@ describe(
x: 250,
y: 50,
});
cy.assertPageSave();
// Verify drop
cy.get(publishLocators.inputWidget).should("exist");
// Type value
cy.get(publishLocators.inputWidget).find("input").type("abcd");
cy.get(publishLocators.inputWidget).find("input").first().type("abcd");
// Verify if the value got typed
cy.get(publishLocators.inputWidget)
@ -78,7 +80,6 @@ describe(
deleteAllWidgetsInContainer();
// Drop Select widget
PageLeftPane.switchToAddNew();
dragAndDropToWidget("selectwidget", "containerwidget", {
x: 250,
y: 50,
@ -115,7 +116,6 @@ describe(
deleteAllWidgetsInContainer();
// Drop Checkbox widget
PageLeftPane.switchToAddNew();
dragAndDropToWidget("checkboxgroupwidget", "containerwidget", {
x: 250,
y: 50,
@ -158,7 +158,6 @@ describe(
deleteAllWidgetsInContainer();
// Drop Switch widget
PageLeftPane.switchToAddNew();
dragAndDropToWidget("switchwidget", "containerwidget", {
x: 250,
y: 50,
@ -201,9 +200,8 @@ describe(
_.deployMode.NavigateBacktoEditor();
deleteAllWidgetsInContainer();
cy.wait(800);
// Drop Radio widget
PageLeftPane.switchToAddNew();
dragAndDropToWidget("radiogroupwidget", "containerwidget", {
x: 250,
y: 50,

View File

@ -68,7 +68,8 @@ describe(
entityExplorer.DragDropWidgetNVerify(widget);
//cy.dragAndDropToWidget(widget, "listwidgetv2", { x: 350, y: 50 });
agHelper.GetNClick(propPane._deleteWidget);
cy.wait("@updateLayout");
cy.assertPageSave();
cy.wait(800);
});
},
);
@ -88,7 +89,8 @@ describe(
cy.assertPageSave();
cy.get(`.t--draggable-${widget}`).should("exist");
cy.get(widgetsPage.removeWidget).click({ force: true });
cy.wait("@updateLayout");
cy.assertPageSave();
cy.wait(800);
});
},
);

View File

@ -23,9 +23,9 @@ describe(
//cy.createModal("Modal", this.dataSet.ModalName);
cy.createModal("Modal", "onRowSelected");
cy.isSelectRow(1);
cy.get(".bp3-overlay-backdrop").click({ force: true });
cy.get(".bp3-overlay-backdrop").last().click({ force: true });
cy.isSelectRow(2);
cy.get(".bp3-overlay-backdrop").click({ force: true });
cy.get(".bp3-overlay-backdrop").last().click({ force: true });
});
it("2. Table widget V2 with button colour change validation", function () {

View File

@ -13,7 +13,6 @@ import {
table,
} from "../../../../support/Objects/ObjectsCore";
import PageList from "../../../../support/Pages/PageList";
import { PageLeftPane } from "../../../../support/Pages/EditorNavigation";
describe(
"Validate Mongo CRUD with JSON Form",
@ -78,7 +77,7 @@ describe(
deployMode.NavigateBacktoEditor();
table.WaitUntilTableLoad(1, 0, "v2");
//Delete the test data
PageLeftPane.expandCollapseItem("Pages");
PageList.ShowList();
entityExplorer.ActionContextMenuByEntityName({
entityNameinLeftSidebar: "CoffeeCafe",
action: "Delete",

View File

@ -14,9 +14,7 @@ import EditorNavigation, {
AppSidebar,
AppSidebarButton,
EntityType,
PageLeftPane,
} from "../../../../support/Pages/EditorNavigation";
import { featureFlagIntercept } from "../../../../support/Objects/FeatureFlags";
import PageList from "../../../../support/Pages/PageList";
let dsName: any;
@ -55,7 +53,7 @@ describe(
deployMode.NavigateBacktoEditor();
table.WaitUntilTableLoad(0, 0, "v2");
//Delete the test data
PageLeftPane.expandCollapseItem("Pages");
PageList.ShowList();
entityExplorer.ActionContextMenuByEntityName({
entityNameinLeftSidebar: "Page2",
action: "Delete",
@ -123,7 +121,7 @@ describe(
deployMode.NavigateBacktoEditor();
table.WaitUntilTableLoad(0, 0, "v2");
//Delete the test data
PageLeftPane.expandCollapseItem("Pages");
PageList.ShowList();
entityExplorer.ActionContextMenuByEntityName({
entityNameinLeftSidebar: "Employees",
action: "Delete",

View File

@ -15,7 +15,6 @@ import EditorNavigation, {
AppSidebar,
AppSidebarButton,
EntityType,
PageLeftPane,
} from "../../../../support/Pages/EditorNavigation";
import PageList from "../../../../support/Pages/PageList";
@ -50,7 +49,7 @@ describe(
deployMode.NavigateBacktoEditor();
table.WaitUntilTableLoad(0, 0, "v2");
//Delete the test data
PageLeftPane.expandCollapseItem("Pages");
PageList.ShowList();
entityExplorer.ActionContextMenuByEntityName({
entityNameinLeftSidebar: "Page2",
action: "Delete",
@ -116,7 +115,7 @@ describe(
deployMode.NavigateBacktoEditor();
table.WaitUntilTableLoad(0, 0, "v2");
//Delete the test data
PageLeftPane.expandCollapseItem("Pages");
PageList.ShowList();
entityExplorer.ActionContextMenuByEntityName({
entityNameinLeftSidebar: "Public.orders",
action: "Delete",

View File

@ -142,20 +142,10 @@ describe(
// });
//Create Dummy Page2 :
cy.CreatePage();
cy.wait("@createPage").should(
"have.nested.property",
"response.body.responseMeta.status",
201,
);
PageList.AddNewPage();
//Creating CRUD Page3
cy.CreatePage();
cy.wait("@createPage").should(
"have.nested.property",
"response.body.responseMeta.status",
201,
);
PageList.AddNewPage();
cy.get("@dSName").then((dbName) => {
PageList.AddNewPage("Generate page with data");
@ -227,7 +217,7 @@ describe(
});
it("4. Generate CRUD page from the page menu", function () {
cy.GenerateCRUD();
PageList.AddNewPage("Generate page with data");
cy.NavigateToDSGeneratePage(datasourceName);
// fetch bucket
cy.wait("@getDatasourceStructure").should(

View File

@ -14,7 +14,6 @@ import {
} from "../../../../support/Objects/ObjectsCore";
import EditorNavigation, {
EntityType,
PageLeftPane,
} from "../../../../support/Pages/EditorNavigation";
import PageList from "../../../../support/Pages/PageList";
@ -728,7 +727,7 @@ describe(
deployMode.NavigateBacktoEditor();
table.WaitUntilTableLoad(0, 0, "v2");
//Delete the test data
PageLeftPane.expandCollapseItem("Pages");
PageList.ShowList();
entityExplorer.ActionContextMenuByEntityName({
entityNameinLeftSidebar: "AuthorNAwards",
action: "Delete",

View File

@ -16,7 +16,6 @@ import oneClickBindingLocator from "../../../locators/OneClickBindingLocator";
import { OneClickBinding } from "../../Regression/ClientSide/OneClickBinding/spec_utility";
import EditorNavigation, {
EntityType,
PageLeftPane,
} from "../../../support/Pages/EditorNavigation";
import PageList from "../../../support/Pages/PageList";
@ -308,7 +307,7 @@ describe(
deployMode.NavigateBacktoEditor();
table.WaitUntilTableLoad();
//Delete the test data
PageLeftPane.expandCollapseItem("Pages");
PageList.ShowList();
entityExplorer.ActionContextMenuByEntityName({
entityNameinLeftSidebar: "Page2",
action: "Delete",

View File

@ -3,7 +3,6 @@
"formWidgets": ".t--page-sidebar-FormWidgets",
"viewWidgets": ".t--page-sidebar-ViewWidgets",
"widgetsEditor": ".t--nav-link-widgets-editor",
"AddPage": ".pages .t--entity-add-btn",
"Menuaction": ".bp3-overlay-open>.bp3-transition-container",
"Delete": ":nth-child(2) > .bp3-menu-item",
"apiEditorIcon": ".t--nav-link-api-editor",

View File

@ -81,7 +81,6 @@
"evaluatedCurrentValue": "div:last-of-type .t--CodeEditor-evaluatedValue > div:last-of-type pre",
"entityExplorersearch": "#entity-explorer-search",
"saveStatusContainer": ".t--save-status-container",
"saveStatusIsSaving": "t--save-status-is-saving",
"statusSaving": ".t--save-status-is-saving",
"saveStatusError": ".t--save-status-error",
"selectWidgetVirtualList": ".menu-virtual-list div",

View File

@ -2,7 +2,6 @@
"NoApiMsg": "p:contains('No APIs yet.')",
"NoQueryMsg": "p:contains('No DB Queries yet.')",
"NoWidgetsMsg": "p:contains('icon above to add widgets')",
"AddPage": ".pages .t--entity-add-btn",
"createQueryMenu": ".file-ops",
"entityExplorer": ".t--entity-explorer",
"popover": "//div[contains(@class,'t--entity page')]//*[local-name()='g' and @id='Icon/Outline/more-vertical']",

View File

@ -3,6 +3,7 @@ import { ObjectsRegistry as _ } from "../Objects/Registry";
import ClickOptions = Cypress.ClickOptions;
import { Sidebar } from "./IDE/Sidebar";
import { LeftPane } from "./IDE/LeftPane";
import PageList from "./PageList";
export enum AppSidebarButton {
Data = "Data",
@ -79,7 +80,7 @@ class EditorNavigation {
NavigateToPage(name: string, networkCallAlias = false) {
AppSidebar.navigate(AppSidebarButton.Editor);
PageLeftPane.expandCollapseItem("Pages");
PageList.ShowList();
PageLeftPane.selectItem(name, { multiple: true, force: true });
_.AggregateHelper.Sleep(); //for selection to settle
networkCallAlias && _.AssertHelper.AssertNetworkStatus("pageSnap");

View File

@ -7,6 +7,7 @@ import EditorNavigation, {
PageLeftPane,
PagePaneSegment,
} from "./EditorNavigation";
import PageList from "./PageList";
type templateActions =
| "Find"
@ -255,6 +256,7 @@ export class EntityExplorer {
viaMenu = false,
) {
AppSidebar.navigate(AppSidebarButton.Editor);
PageList.ShowList();
if (viaMenu)
this.ActionContextMenuByEntityName({
entityNameinLeftSidebar: entityName,

View File

@ -11,6 +11,7 @@ class PageList {
`.t--entity.page:contains('${pageName}')`,
newButton: ".pages .t--entity-add-btn",
newPageOption: (option: string) => `//span[text()='${option}']/parent::div`,
switcher: `.t--pages-switcher`,
};
public AddNewPage(
@ -20,12 +21,14 @@ class PageList {
| "Add page from template" = "New blank page",
) {
AppSidebar.navigate(AppSidebarButton.Editor);
this.ShowList();
ObjectsRegistry.AggregateHelper.GetNClick(this.locators.newButton);
ObjectsRegistry.AggregateHelper.GetNClick(
this.locators.newPageOption(option),
);
if (option === "New blank page") {
ObjectsRegistry.AssertHelper.AssertNetworkStatus("@createPage", 201);
return cy
.get("@createPage")
.then(($pageName: any) => $pageName.response?.body.data.name);
@ -33,6 +36,7 @@ class PageList {
}
public VerifyIsCurrentPage(pageName: string) {
this.ShowList();
ObjectsRegistry.AggregateHelper.GetElement(
this.locators.pageListItem(pageName),
).should("have.class", "activePage");
@ -44,6 +48,7 @@ class PageList {
public ClonePage(pageName = "Page1") {
AppSidebar.navigate(AppSidebarButton.Editor);
this.ShowList();
EditorNavigation.SelectEntityByName(pageName, EntityType.Page);
ObjectsRegistry.EntityExplorer.ActionContextMenuByEntityName({
entityNameinLeftSidebar: pageName,
@ -52,17 +57,41 @@ class PageList {
ObjectsRegistry.AssertHelper.AssertNetworkStatus("@clonePage", 201);
}
public ShowList() {
cy.get(this.locators.switcher).then(($switcher) => {
const isActive: string | undefined = $switcher.attr("data-active");
if (isActive === "false") {
cy.get(this.locators.switcher).click();
}
});
}
assertPresence(pageName: string) {
this.ShowList();
ObjectsRegistry.AggregateHelper.AssertElementVisibility(
this.locators.pageListItem(pageName),
);
}
assertAbsence(pageName: string) {
this.ShowList();
ObjectsRegistry.AggregateHelper.AssertElementAbsence(
this.locators.pageListItem(pageName),
);
}
DeletePage(name: string) {
this.ShowList();
cy.get(this.locators.pageListItem(name)).within(() => {
cy.get(".t--context-menu").click({ force: true });
});
cy.wait(2000);
cy.selectAction("Delete");
cy.selectAction("Are you sure?");
cy.wait("@deletePage")
.its("response.body.responseMeta.status")
.should("eq", 200);
}
}
export default new PageList();

View File

@ -5,7 +5,6 @@ import { ANVIL_EDITOR_TEST } from "./Constants.js";
import EditorNavigation, {
EntityType,
AppSidebarButton,
AppSidebar,
PageLeftPane,
PagePaneSegment,
@ -24,7 +23,6 @@ const loginPage = require("../locators/LoginPage.json");
const signupPage = require("../locators/SignupPage.json");
import homePage from "../locators/HomePage";
const pages = require("../locators/Pages.json");
const commonlocators = require("../locators/commonlocators.json");
const widgetsPage = require("../locators/Widgets.json");
import ApiEditor from "../locators/ApiEditor";
@ -390,14 +388,6 @@ Cypress.Commands.add("DeleteApp", (appName) => {
cy.get(homePage.deleteApp).contains("Are you sure?").click({ force: true });
});
Cypress.Commands.add("DeletepageFromSideBar", () => {
cy.xpath(pages.popover).last().click({ force: true });
cy.get(pages.deletePage).first().click({ force: true });
cy.get(pages.deletePageConfirm).first().click({ force: true });
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(2000);
});
Cypress.Commands.add("LogOut", (toCheckgetPluginForm = true) => {
agHelper.WaitUntilAllToastsDisappear();
//Since these are coming in every self-hosted 1st time login, commenting for CI runs
@ -1952,22 +1942,6 @@ Cypress.Commands.add(
},
);
Cypress.Commands.add("CreatePage", () => {
AppSidebar.navigate(AppSidebarButton.Editor);
cy.get(pages.AddPage).first().click();
cy.xpath("//span[text()='New blank page']/parent::div").click();
});
Cypress.Commands.add("GenerateCRUD", () => {
cy.get(pages.AddPage).first().click();
cy.xpath("//span[text()='Generate page with data']/parent::div").click();
});
Cypress.Commands.add("AddPageFromTemplate", () => {
cy.get(pages.AddPage).first().click();
cy.xpath("//span[text()='Add page from template']/parent::div").click();
});
Cypress.Commands.add(`verifyCallCount`, (alias, expectedNumberOfCalls) => {
cy.wait(alias);
cy.get(`${alias}.all`).should("have.length", expectedNumberOfCalls);

179
app/client/cypress/support/index.d.ts vendored Normal file
View File

@ -0,0 +1,179 @@
/// <reference types="cypress" />
declare namespace Cypress {
interface Chainable<Subject> {
SignupFromAPI(uname: string, password: string);
dragTo(subject: any, targetEl: any);
downloadData(filetype: string);
validateDownload(fileName: string);
AddFilterWithOperator(
operator: string,
option: string,
condition: string,
value: string,
);
stubPostHeaderReq();
addOAuth2ClientCredentialsDetails(
accessTokenUrl: string,
clientId: string,
clientSecret: string,
scope: string,
);
addOAuth2AuthorizationCodeDetails(
accessTokenUrl: string,
clientId: string,
clientSecret: string,
authURL: string,
);
testSelfSignedCertificateSettingsInREST(isOAuth2: boolean);
addBasicProfileDetails(username: string, password: string);
DeleteApp(appName: string);
GetUrlQueryParams();
LogOutUser();
LoginUser(uname: string, pword: string, goToLoginPage?: boolean);
LogintoApp(uname: string, pword: string);
LogintoAppTestUser(uname: string, pword: string);
Signup(uname: string, pword: string);
LoginFromAPI(uname: string, pword: string);
DeletepageFromSideBar();
LogOut(toCheckgetPluginForm?: boolean);
SearchApp(appname: string);
WaitAutoSave();
SelectAction(action: string);
ClearSearch();
paste($element: any, text: string);
clickTest(testbutton: string);
EvaluateCurrentValue(
currentValue: string,
isValueToBeEvaluatedDynamic?: boolean,
);
tabPopertyUpdate(tabId: string, newTabName: string);
generateUUID();
addDsl(dsl: any);
DeleteAppByApi();
DeleteWorkspaceByApi();
togglebar(value: string);
radiovalue(value: string, value2: string);
optionValue(value: string, value2: string);
typeIntoDraftEditor(selector: string, text: string);
getPluginFormsAndCreateDatasource();
NavigateToJSEditor();
importCurl();
selectAction(option: string);
deleteActionAndConfirm();
deleteJSObject();
deleteDataSource();
dragAndDropToCanvas(widgetType: string, { x: number, y: number });
dragAndDropToWidget(
widgetType: string,
destinationWidget: string,
{ x: number, y: number },
);
dragAndDropToWidgetBySelector(
widgetType: string,
destinationSelector: string,
{ x: number, y: number },
);
changeButtonColor(buttonColor: string);
closePropertyPane();
onClickActions(
forSuccess: string,
forFailure: string,
actionType: string,
actionValue: string,
idx?: number,
);
isSelectRow(index: number);
getDate(date: number, dateFormate: string);
setDate(date: number, dateFormate: string);
pageNo();
pageNoValidate(index: number);
validateDisableWidget(widgetCss: string, disableCss: string);
validateToolbarVisible(widgetCss: string, toolbarCss: string);
validateToolbarHidden(widgetCss: string, toolbarCss: string);
validateEnableWidget(widgetCss: string, disableCss: string);
validateHTMLText(widgetCss: string, htmlTag: string, value: string);
setTinyMceContent(tinyMceId: string, content: string);
startRoutesForDatasource();
startServerAndRoutes();
startErrorRoutes();
NavigateToPaginationTab();
ValidateTableData(value: string);
ValidateTableV2Data(value: string);
ValidatePublishTableData(value: string);
ValidatePublishTableV2Data(value: string);
ValidatePaginateResponseUrlData(runTestCss: string);
ValidatePaginateResponseUrlDataV2(runTestCss: string);
ValidatePaginationInputData(valueToTest: string);
ValidatePaginationInputDataV2(valueToTest: string);
CheckForPageSaveError();
assertPageSave(validateSavedState?: boolean);
validateCodeEditorContent(selector: string, contentToValidate: string);
updateMapType(mapType: string);
createJSObject(JSCode: string);
createSuperUser();
SignupFromAPI(uname: string, pword: string);
startInterceptRoutesForMySQL();
startInterceptRoutesForMongo();
startInterceptRoutesForS3();
replaceApplicationIdForInterceptPages(fixtureFile: string);
paste(selector: string, pastePayload: string);
typeValueNValidate(
valueToType: string,
fieldName?: string,
isDynamic?: boolean,
);
checkCodeInputValue(selector: string);
clickButton(btnVisibleText: string, toForceClick?: boolean);
actionContextMenuByEntityName(
entityNameinLeftSidebar: string,
action?: string,
subActions: string,
);
selectEntityByName(entityNameinLeftSidebar: string);
EvaluatFieldValue(fieldName?: string, currentValue?: string);
renameWithInPane(renameVal: string);
getEntityName();
VerifyErrorMsgAbsence(errorMsgToVerifyAbsence: string);
VerifyErrorMsgPresence(errorMsgToVerifyAbsence: string);
setQueryTimeout(timeout: string);
VerifyNoDataDisplayAbsence();
isNotInViewport(element: string);
isInViewport(element: string);
CheckAndUnfoldEntityItem(item: string);
DeleteEntityStateLocalStorage();
checkLabelForWidget(options: string);
saveLocalStorageCache();
restoreLocalStorageCache();
StopContainer(path: string, containerName: string);
StopAllContainer(path: string);
StartContainer(path: string, containerName: string);
StartNewContainer(
url: string,
path: string,
version: string,
containerName: string,
);
GetPath(path: string, containerName: string);
GetCWD(path: string);
GetAndVerifyLogs(path: string, containerName: string);
typeTab();
CreatePage();
GenerateCRUD();
AddPageFromTemplate();
verifyCallCount(alias: string, expectedNumberOfCalls: number);
RenameWidgetFromPropertyPane(
widgetType: string,
oldName: string,
newName: string,
);
forceVisit(url: string);
SelectDropDown(dropdownOption: string);
RemoveMultiSelectItems(dropdownOptions: string[]);
RemoveAllSelections();
SelectFromMultiSelect(options: string);
skipSignposting();
stubPricingPage();
validateEvaluatedValue(value: string);
}
}

View File

@ -1,6 +1,8 @@
/* eslint-disable cypress/no-unnecessary-waiting */
/* eslint-disable cypress/no-assigning-return-values */
import PageList from "./Pages/PageList";
require("cy-verify-downloads").addCustomCommand();
require("cypress-file-upload");
const commonlocators = require("../locators/commonlocators.json");
@ -929,31 +931,17 @@ Cypress.Commands.add("DeleteModal", () => {
});
Cypress.Commands.add("Createpage", (pageName, navigateToCanvasPage = true) => {
cy.CreatePage();
cy.wait("@createPage").then((xhr) => {
expect(xhr.response.body.responseMeta.status).to.equal(201);
PageList.AddNewPage().then((oldPageName) => {
if (pageName) {
const pageId = xhr.response.body.data.id;
const oldPageName = xhr.response.body.data.name;
cy.wait(2000);
ee.RenameEntityFromExplorer(oldPageName, pageName, true);
cy.wrap(pageId).as("currentPageId");
}
cy.get("#loading").should("not.exist");
});
});
Cypress.Commands.add("Deletepage", (Pagename) => {
cy.CheckAndUnfoldEntityItem("Pages");
cy.get(`.t--entity-item:contains(${Pagename})`).within(() => {
cy.get(".t--context-menu").click({ force: true });
cy.get("@createPage").then((xhr) => {
const pageId = xhr.response.body.data.id;
cy.wrap(pageId).as("currentPageId");
});
cy.wait(2000);
cy.selectAction("Delete");
cy.selectAction("Are you sure?");
cy.wait("@deletePage")
.its("response.body.responseMeta.status")
.should("eq", 200);
});
Cypress.Commands.add("dropdownDynamic", (text) => {

View File

@ -110,8 +110,8 @@
"d3-geo": "^3.1.0",
"dayjs": "^1.10.6",
"deep-diff": "^1.0.2",
"design-system": "npm:@appsmithorg/design-system@2.1.35",
"design-system-old": "npm:@appsmithorg/design-system-old@1.1.15",
"design-system": "npm:@appsmithorg/design-system@2.1.36",
"design-system-old": "npm:@appsmithorg/design-system-old@1.1.16",
"downloadjs": "^1.4.7",
"echarts": "^5.4.2",
"fast-deep-equal": "^3.1.3",

View File

@ -3,7 +3,6 @@ import type {
EvaluationReduxAction,
AnyReduxAction,
ReduxAction,
ReduxActionWithoutPayload,
} from "@appsmith/constants/ReduxActionConstants";
import type { JSUpdate } from "utils/JSPaneUtils";
import {
@ -11,6 +10,7 @@ import {
ReduxActionTypes,
} from "@appsmith/constants/ReduxActionConstants";
import type { Action, ActionViewMode } from "entities/Action";
import { ActionExecutionContext } from "entities/Action";
import { batchAction } from "actions/batchActions";
import type { ExecuteErrorPayload } from "constants/AppsmithActionConstants/ActionConstants";
import type { ModalInfo } from "reducers/uiReducers/modalActionReducer";
@ -107,6 +107,8 @@ export const runAction = (
id: string,
paginationField?: PaginationField,
skipOpeningDebugger = false,
action = undefined,
actionExecutionContext = ActionExecutionContext.SELF,
) => {
return {
type: ReduxActionTypes.RUN_ACTION_REQUEST,
@ -114,6 +116,8 @@ export const runAction = (
id,
paginationField,
skipOpeningDebugger,
action,
actionExecutionContext,
},
};
};
@ -314,9 +318,16 @@ export const updateActionProperty = (
});
};
export const executePageLoadActions = (): ReduxActionWithoutPayload => ({
type: ReduxActionTypes.EXECUTE_PAGE_LOAD_ACTIONS,
});
export const executePageLoadActions = (
actionExecutionContext?: ActionExecutionContext,
) => {
return {
type: ReduxActionTypes.EXECUTE_PAGE_LOAD_ACTIONS,
payload: {
actionExecutionContext,
},
};
};
export const executeJSUpdates = (
payload: Record<string, JSUpdate>,

View File

@ -47,9 +47,15 @@ export const createModalAction = (
export const focusWidget = (
widgetId?: string,
): ReduxAction<{ widgetId?: string }> => ({
alt?: boolean,
): ReduxAction<{ widgetId?: string; alt?: boolean }> => ({
type: ReduxActionTypes.FOCUS_WIDGET,
payload: { widgetId },
payload: { widgetId, alt },
});
export const altFocusWidget = (alt: boolean) => ({
type: ReduxActionTypes.ALT_FOCUS_WIDGET,
payload: alt,
});
export const showModal = (id: string, shouldSelectModal = true) => {
@ -144,3 +150,10 @@ export const partialExportWidgets = (params: PartialExportParams) => {
payload: params,
};
};
export const setWidgetSelectionBlock = (payload: boolean) => {
return {
type: ReduxActionTypes.SET_WIDGET_SELECTION_BLOCK,
payload,
};
};

View File

@ -68,23 +68,6 @@ class AppThemingApi extends API {
);
}
/**
* fires api for saving current theme
*
* @param applicationId
* @param theme
* @returns
*/
static async saveTheme(
applicationId: string,
payload: { name: string },
): Promise<AxiosPromise<ApiResponse<AppTheme[]>>> {
return API.patch(
`${AppThemingApi.baseUrl}/themes/applications/${applicationId}`,
payload,
);
}
/**
* fires api for deleting theme
*

View File

@ -129,3 +129,7 @@ export const fetchEntitiesOfWorkspace = (payload: { workspaceId?: string }) => {
payload,
};
};
export const resetImportData = () => ({
type: ReduxActionTypes.RESET_IMPORT_DATA,
});

View File

@ -1,3 +1,5 @@
import type { PluginType } from "entities/Action";
type ID = string;
export enum MODULE_TYPE {
@ -15,9 +17,17 @@ export interface ModuleInputSection {
children?: ModuleInput[];
}
export interface Module {
export interface Module extends ModuleMetadata {
id: ID;
name: string;
packageId: ID;
inputsForm: ModuleInputSection[];
type: MODULE_TYPE;
}
export interface ModuleMetadata {
moduleId: string;
datasourceId?: string;
pluginId: string;
pluginType: PluginType;
}

View File

@ -15,6 +15,7 @@ export interface ModuleInstance {
inputs: {
[key: string]: string;
};
sourceModuleId: ModuleId;
}
export interface ModuleInstanceData {

View File

@ -546,6 +546,7 @@ const ActionTypes = {
IMPORT_APPLICATION_FROM_GIT_INIT: "IMPORT_APPLICATION_FROM_GIT_INIT",
IMPORT_APPLICATION_SUCCESS: "IMPORT_APPLICATION_SUCCESS",
SET_WIDGET_LOADING: "SET_WIDGET_LOADING",
RESET_IMPORT_DATA: "RESET_IMPORT_DATA",
SET_GLOBAL_SEARCH_QUERY: "SET_GLOBAL_SEARCH_QUERY",
SET_GLOBAL_SEARCH_CATEGORY: "SET_GLOBAL_SEARCH_CATEGORY",
TOGGLE_SHOW_GLOBAL_SEARCH_MODAL: "TOGGLE_SHOW_GLOBAL_SEARCH_MODAL",
@ -687,7 +688,6 @@ const ActionTypes = {
CHANGE_SELECTED_APP_THEME_SUCCESS: "CHANGE_SELECTED_APP_THEME_SUCCESS",
SET_PREVIEW_APP_THEME: "SET_PREVIEW_APP_THEME",
SAVE_APP_THEME_INIT: "SAVE_APP_THEME_INIT",
SAVE_APP_THEME_SUCCESS: "SAVE_APP_THEME_SUCCESS",
DELETE_APP_THEME_INIT: "DELETE_APP_THEME_INIT",
DELETE_APP_THEME_SUCCESS: "DELETE_APP_THEME_SUCCESS",
RESET_APP_THEME_INIT: "RESET_APP_THEME_INIT",
@ -917,6 +917,8 @@ const ActionTypes = {
SET_API_PANE_DEBUGGER_STATE: "SET_API_PANE_DEBUGGER_STATE",
SET_JS_PANE_DEBUGGER_STATE: "SET_JS_PANE_DEBUGGER_STATE",
SET_CANVAS_DEBUGGER_STATE: "SET_CANVAS_DEBUGGER_STATE",
SET_WIDGET_SELECTION_BLOCK: "SET_WIDGET_SELECTION_BLOCK",
ALT_FOCUS_WIDGET: "ALT_FOCUS_WIDGET",
};
export const ReduxActionTypes = {
@ -1078,7 +1080,6 @@ export const ReduxActionErrorTypes = {
UPDATE_JS_FUNCTION_PROPERTY_ERROR: "UPDATE_JS_FUNCTION_PROPERTY_ERROR",
DELETE_WORKSPACE_ERROR: "DELETE_WORKSPACE_ERROR",
REFLOW_BETA_FLAGS_INIT_ERROR: "REFLOW_BETA_FLAGS_INIT_ERROR",
SAVE_APP_THEME_ERROR: "SAVE_APP_THEME_ERROR",
DELETE_APP_THEME_ERROR: "DELETE_APP_THEME_ERROR",
GET_ALL_TEMPLATES_ERROR: "GET_ALL_TEMPLATES_ERROR",
GET_SIMILAR_TEMPLATES_ERROR: "GET_SIMILAR_TEMPLATES_ERROR",

View File

@ -2459,3 +2459,8 @@ export const CREATE_A_NEW_ITEM = (item: string) => `Create a new ${item}`;
export const MAXIMIZE_BUTTON_TOOLTIP = () =>
`Expand code editor to full-screen`;
export const MINIMIZE_BUTTON_TOOLTIP = () => `Open code editor next to the UI`;
export const SPLITPANE_ANNOUNCEMENT = {
TITLE: () => "Code and UI, side-by-side",
DESCRIPTION: () =>
"Write queries and JS functions while you refer to the UI on the side! This is a beta version that we will continue to improve with your feedback.",
};

View File

@ -14,7 +14,7 @@ interface ResourcesLoaderProps {
const DEFAULT_BACKGROUND_COLOR = "#9747FF1A";
const DEFAULT_ICON = "book";
const DEAFULT_RESOURCES = [{ name: "Default Resource" }];
const DEAFULT_RESOURCES = [{ id: "default", name: "Default Resource" }];
function ResourceListLoader({ isMobile, resources }: ResourcesLoaderProps) {
const resourcesToUse = resources?.length ? resources : DEAFULT_RESOURCES;

View File

@ -12,7 +12,6 @@ import {
createMessage,
INVITE_USERS_PLACEHOLDER,
NO_APPS_FOUND,
NO_WORKSPACE_DESCRIPTION,
NO_WORKSPACE_HEADING,
WORKSPACES_HEADING,
} from "@appsmith/constants/messages";
@ -370,7 +369,6 @@ export function WorkspaceMenuItem({
isFetchingWorkspaces ? 100 : 22
} /* this is to avoid showing tooltip for loaders */
icon="group-2-line"
key={workspace?.id}
onSelect={handleWorkspaceClick}
selected={selected}
text={workspace?.name}
@ -387,8 +385,8 @@ export const submitCreateWorkspaceForm = async (data: any, dispatch: any) => {
export interface LeftPaneProps {
isBannerVisible?: boolean;
isFetchingWorkspaces: boolean;
workspaces: any;
activeWorkspaceId: string | undefined;
workspaces: Workspace[];
activeWorkspaceId?: string;
}
export function LeftPane(props: LeftPaneProps) {
@ -396,7 +394,7 @@ export function LeftPane(props: LeftPaneProps) {
activeWorkspaceId,
isBannerVisible = false,
isFetchingWorkspaces,
workspaces,
workspaces = [],
} = props;
const isMobile = useIsMobileDevice();
@ -409,18 +407,14 @@ export function LeftPane(props: LeftPaneProps) {
isFetchingWorkspaces={isFetchingWorkspaces}
>
<WorkpsacesNavigator data-testid="t--left-panel">
{workspaces &&
workspaces.map(
(workspace: any) =>
workspace && (
<WorkspaceMenuItem
isFetchingWorkspaces={isFetchingWorkspaces}
key={workspace?.id}
selected={workspace?.id === activeWorkspaceId}
workspace={workspace}
/>
),
)}
{workspaces.map((workspace) => (
<WorkspaceMenuItem
isFetchingWorkspaces={isFetchingWorkspaces}
key={workspace.id}
selected={workspace.id === activeWorkspaceId}
workspace={workspace}
/>
))}
</WorkpsacesNavigator>
</LeftPaneSection>
</LeftPaneWrapper>
@ -565,11 +559,14 @@ export function ApplicationsSection(props: any) {
setSelectedWorkspaceIdForImportApplication,
]);
const leaveWS = (workspaceId: string) => {
setWarnLeavingWorkspace(false);
setWorkspaceToOpenMenu(null);
dispatch(leaveWorkspace(workspaceId));
};
const leaveWS = useCallback(
(workspaceId: string) => {
setWarnLeavingWorkspace(false);
setWorkspaceToOpenMenu(null);
dispatch(leaveWorkspace(workspaceId));
},
[dispatch],
);
const handleDeleteWorkspace = useCallback(
(workspaceId: string) => {
@ -657,9 +654,6 @@ export function ApplicationsSection(props: any) {
<NewText className="!mb-3 !font-semibold" kind="heading-s">
{createMessage(NO_WORKSPACE_HEADING)}
</NewText>
<NewText className="w-[328px]" kind="heading-xs">
{createMessage(NO_WORKSPACE_DESCRIPTION)}
</NewText>
</div>
);
}
@ -896,7 +890,9 @@ export const ApplictionsMainPage = (props: any) => {
if (!isFetchingWorkspaces) {
workspaces = fetchedWorkspaces;
} else {
workspaces = loadingUserWorkspaces as any;
workspaces = loadingUserWorkspaces.map(
(loadingWorkspaces) => loadingWorkspaces.workspace,
) as any;
}
const [activeWorkspaceId, setActiveWorkspaceId] = useState<

View File

@ -60,7 +60,6 @@ export const useQueryAdd = () => {
return addButtonClickHandler;
};
//history.push(location.pathname.replace(`${ADD_PATH}`, ""));
export type GroupedAddOperations = Array<{
title?: string;
@ -214,7 +213,7 @@ export const useAddQueryListItems = () => {
fileOperation.entityExplorerTitle ||
fileOperation.dsName ||
fileOperation.title,
description: "",
description: !!fileOperation.isBeta ? "Beta" : "",
descriptionType: "inline",
onClick: onCreateItemClick.bind(null, fileOperation),
} as ListItemProps;

View File

@ -0,0 +1,33 @@
import { builderURL } from "@appsmith/RouteBuilder";
import {
RECONNECT_MISSING_DATASOURCE_CREDENTIALS_DESCRIPTION,
SKIP_TO_APPLICATION,
createMessage,
} from "@appsmith/constants/messages";
import { EditorNames } from "@appsmith/hooks";
interface UseReconnectModalDataProps {
pageId: string | null;
appId: string | null;
}
function useReconnectModalData({ appId, pageId }: UseReconnectModalDataProps) {
const editorURL =
pageId &&
builderURL({
pageId,
});
return {
skipMessage: createMessage(SKIP_TO_APPLICATION),
missingDsCredentialsDescription: createMessage(
RECONNECT_MISSING_DATASOURCE_CREDENTIALS_DESCRIPTION,
),
editorURL,
editorId: appId,
parentEntityId: pageId,
editorType: EditorNames.APPLICATION,
};
}
export default useReconnectModalData;

View File

@ -18,22 +18,13 @@ import {
} from "constants/routes";
import Navigation from "pages/AppViewer/Navigation";
import type { RouteComponentProps } from "react-router";
import AppEditorHeader from "pages/Editor/EditorHeader";
import { Header } from "pages/Editor/IDE/Header";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
import { Header as AppIDEHeader } from "pages/Editor/IDE/Header";
export type Props = RouteComponentProps;
export const headerRoot = document.getElementById("header-root");
export const Routes = () => {
const isSideBySideFlagEnabled = useFeatureFlag(
FEATURE_FLAG.release_side_by_side_ide_enabled,
);
const HeaderComponent = isSideBySideFlagEnabled ? Header : AppEditorHeader;
return (
<Switch>
<Route component={PageHeader} path={ADMIN_SETTINGS_CATEGORY_PATH} />
@ -46,10 +37,10 @@ export const Routes = () => {
exact
path={CUSTOM_WIDGETS_EDITOR_ID_PATH_CUSTOM}
/>
<Route component={HeaderComponent} path={BUILDER_PATH_DEPRECATED} />
<Route component={AppIDEHeader} path={BUILDER_PATH_DEPRECATED} />
<Route component={Navigation} path={VIEWER_PATH_DEPRECATED} />
<Route component={HeaderComponent} path={BUILDER_PATH} />
<Route component={HeaderComponent} path={BUILDER_CUSTOM_PATH} />
<Route component={AppIDEHeader} path={BUILDER_PATH} />
<Route component={AppIDEHeader} path={BUILDER_CUSTOM_PATH} />
<Route component={Navigation} path={VIEWER_PATH} />
<Route component={Navigation} path={VIEWER_CUSTOM_PATH} />
<Route component={PageHeader} path={BASE_URL} />

View File

@ -247,6 +247,12 @@ export const handlers = {
importingApplication: false,
};
},
[ReduxActionTypes.RESET_IMPORT_DATA]: (state: ApplicationsReduxState) => {
return {
...state,
importedApplication: null,
};
},
[ReduxActionTypes.PARTIAL_IMPORT_INIT]: (state: ApplicationsReduxState) => ({
...state,
partialImportExport: {

View File

@ -20,8 +20,10 @@ import { flushErrors } from "actions/errorActions";
import type { NavigationMethod } from "utils/history";
import UsagePulse from "usagePulse";
import { getIDETypeByUrl } from "@appsmith/entities/IDE/utils";
import type { EditorViewMode } from "@appsmith/entities/IDE/constants";
import { IDE_TYPE } from "@appsmith/entities/IDE/constants";
import { updateIDETabsOnRouteChangeSaga } from "sagas/IDESaga";
import { getIDEViewMode } from "selectors/ideSelectors";
let previousPath: string;
@ -110,6 +112,7 @@ function* logNavigationAnalytics(payload: RouteChangeActionPayload) {
const isRecent = recentEntityIds.some(
(entityId) => entityId === currentEntity.id,
);
const ideViewMode: EditorViewMode = yield select(getIDEViewMode);
const { height, width } = window.screen;
AnalyticsUtil.logEvent("ROUTE_CHANGE", {
toPath: pathname,
@ -121,6 +124,7 @@ function* logNavigationAnalytics(payload: RouteChangeActionPayload) {
fromType: previousEntity.entity,
screenHeight: height,
screenWidth: width,
editorMode: ideViewMode,
});
}

View File

@ -145,6 +145,7 @@ import type { DSLWidget } from "WidgetProvider/constants";
import type { FeatureFlags } from "@appsmith/entities/FeatureFlag";
import { getIsServerDSLMigrationsEnabled } from "selectors/pageSelectors";
import { getCurrentWorkspaceId } from "@appsmith/selectors/selectedWorkspaceSelectors";
import { ActionExecutionContext } from "entities/Action";
export const checkIfMigrationIsNeeded = (
fetchPageResponse?: FetchPageResponse,
@ -977,7 +978,11 @@ export function* clonePageSaga(
}
yield put(selectWidgetInitAction(SelectionRequestType.Empty));
yield put(fetchAllPageEntityCompletion([executePageLoadActions()]));
yield put(
fetchAllPageEntityCompletion([
executePageLoadActions(ActionExecutionContext.CLONE_PAGE),
]),
);
// TODO: Update URL params here.
@ -1374,7 +1379,11 @@ export function* generateTemplatePageSaga(
if (!afterActionsFetch) {
throw new Error("Failed generating template");
}
yield put(fetchAllPageEntityCompletion([executePageLoadActions()]));
yield put(
fetchAllPageEntityCompletion([
executePageLoadActions(ActionExecutionContext.GENERATE_CRUD_PAGE),
]),
);
history.replace(
builderURL({

View File

@ -602,6 +602,7 @@ export function* leaveWorkspaceSaga(
toast.show(`You have successfully left the workspace`, {
kind: "success",
});
history.push("/applications");
}
} catch (error) {
// do nothing as it's already handled globally

View File

@ -0,0 +1,7 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { AppState } from "@appsmith/reducers";
import type { Module } from "@appsmith/constants/ModuleConstants";
export const getAllModules = (
state: AppState,
): Record<string, Module> | any => {};

View File

@ -1,4 +1,5 @@
import type { Action } from "entities/Action";
import { ActionExecutionContext } from "entities/Action";
import type { JSAction, JSCollection } from "entities/JSCollection";
import type { ApplicationPayload } from "@appsmith/constants/ReduxActionConstants";
import store from "store";
@ -65,6 +66,7 @@ export function getActionExecutionAnalytics(
isMock: !!datasource?.isMock,
actionId: action?.id,
inputParams: Object.keys(params).length,
source: ActionExecutionContext.EVALUATION_ACTION_TRIGGER, // Used in analytic events to understand who triggered action execution
};
if (!!currentApp) {

View File

@ -354,7 +354,8 @@ export type EventName =
| CUSTOM_WIDGET_EVENTS
| "MULTI_FILE_PICKER_EXCEEDS_LIMIT"
| "TEMPLATE_ADD_PAGE_FROM_TEMPLATE_FLOW"
| HOMEPAGE_CREATE_APP_FROM_TEMPLATE_EVENTS;
| HOMEPAGE_CREATE_APP_FROM_TEMPLATE_EVENTS
| "EDITOR_MODE_CHANGE";
type HOMEPAGE_CREATE_APP_FROM_TEMPLATE_EVENTS =
| "TEMPLATE_DROPDOWN_CLICK"
@ -384,7 +385,8 @@ export type ONBOARDING_FLOW_EVENTS =
| "ONBOARDING_FLOW_CLICK_SKIP_BUTTON_START_FROM_DATA_PAGE"
| "ONBOARDING_FLOW_CLICK_SKIP_BUTTON_DATASOURCE_FORM_PAGE"
| "ONBOARDING_FLOW_CLICK_SKIP_BUTTON_START_FROM_TEMPLATE_PAGE"
| "ONBOARDING_FLOW_CLICK_SKIP_BUTTON_TEMPLATE_DETAILS_PAGE";
| "ONBOARDING_FLOW_CLICK_SKIP_BUTTON_TEMPLATE_DETAILS_PAGE"
| "CODE_MODE_WIDGET_SELECTION";
export type DATASOURCE_SCHEMA_EVENTS =
| "DATASOURCE_SCHEMA_SEARCH"

View File

@ -1,4 +1,3 @@
import type { ReactNode } from "react";
import React from "react";
import {
getFunctionNameFromJsObjectExpression,
@ -20,7 +19,7 @@ import { PluginType } from "entities/Action";
import type { JSAction, Variable } from "entities/JSCollection";
import keyBy from "lodash/keyBy";
import { getActionConfig } from "pages/Editor/Explorer/Actions/helpers";
import { EntityIcon, JsFileIconV2 } from "pages/Editor/Explorer/ExplorerIcons";
import { JsFileIconV2 } from "pages/Editor/Explorer/ExplorerIcons";
import { useMemo } from "react";
import { useDispatch, useSelector } from "react-redux";
import type {
@ -66,11 +65,13 @@ import { selectEvaluationVersion } from "@appsmith/selectors/applicationSelector
import { isJSAction } from "@appsmith/workers/Evaluation/evaluationUtils";
import type { DataTreeEntity } from "entities/DataTree/dataTreeTypes";
import type { ModuleInstanceDataState } from "@appsmith/constants/ModuleInstanceConstants";
import { MODULE_TYPE } from "@appsmith/constants/ModuleConstants";
import { setShowCreateNewModal } from "actions/propertyPaneActions";
import { setIdeEditorViewMode } from "actions/ideActions";
import { EditorViewMode } from "@appsmith/entities/IDE/constants";
import { getIsSideBySideEnabled } from "selectors/ideSelectors";
import { resolveIcon } from "pages/Editor/utils";
import { getAllModules } from "@appsmith/selectors/modulesSelector";
import type { Module } from "@appsmith/constants/ModuleConstants";
const actionList: {
label: string;
@ -393,16 +394,6 @@ export function useModalDropdownList(handleClose: () => void) {
return finalList;
}
export const getModuleInstanceIcon = (type: MODULE_TYPE): ReactNode => {
if (type === MODULE_TYPE.QUERY || type === MODULE_TYPE.JS) {
return (
<EntityIcon>
<Icon name="module" />
</EntityIcon>
);
}
};
export function getApiQueriesAndJSActionOptionsWithChildren(
pageId: string,
plugins: any,
@ -412,6 +403,7 @@ export function getApiQueriesAndJSActionOptionsWithChildren(
handleClose: () => void,
queryModuleInstances: ModuleInstanceDataState,
jsModuleInstances: ReturnType<typeof getJSModuleInstancesData>,
modules: Record<string, Module>,
) {
// this function gets a list of all the queries/apis and attaches it to actionList
getApiAndQueryOptions(
@ -420,6 +412,7 @@ export function getApiQueriesAndJSActionOptionsWithChildren(
dispatch,
handleClose,
queryModuleInstances,
modules,
);
// this function gets a list of all the JS Objects and attaches it to actionList
@ -434,6 +427,7 @@ function getApiAndQueryOptions(
dispatch: any,
handleClose: () => void,
queryModuleInstances: ModuleInstanceDataState,
modules: Record<string, Module>,
) {
const state = store.getState();
const isSideBySideEnabled = getIsSideBySideEnabled(state);
@ -499,12 +493,17 @@ function getApiAndQueryOptions(
} as TreeDropdownOption);
});
queryModuleInstances.forEach((instance) => {
const module = modules[instance.config.sourceModuleId];
(queryOptions.children as TreeDropdownOption[]).push({
label: instance.config.name,
id: instance.config.id,
value: instance.config.name,
type: queryOptions.value,
icon: getModuleInstanceIcon(instance.config.type),
icon: resolveIcon({
iconLocation: plugins[module.pluginId]?.iconLocation || "",
pluginType: module.pluginType,
moduleType: module.type,
}),
} as TreeDropdownOption);
});
}
@ -587,7 +586,7 @@ export function getJSOptions(
id: jsModuleInstance.config.id,
value: jsModuleInstance.name,
type: jsOption.value,
icon: getModuleInstanceIcon(MODULE_TYPE.JS),
icon: JsFileIconV2(),
} as unknown as TreeDropdownOption;
(jsOption.children as unknown as TreeDropdownOption[]).push(jsObject);
@ -637,6 +636,7 @@ export function useApisQueriesAndJsActionOptions(handleClose: () => void) {
getQueryModuleInstances,
) as unknown as ModuleInstanceDataState;
const jsModuleInstancesData = useSelector(getJSModuleInstancesData);
const modules = useSelector(getAllModules);
// this function gets all the Queries/API's/JS Objects and attaches it to actionList
return getApiQueriesAndJSActionOptionsWithChildren(
@ -648,5 +648,6 @@ export function useApisQueriesAndJsActionOptions(handleClose: () => void) {
handleClose,
queryModuleInstances,
jsModuleInstancesData,
modules,
);
}

View File

@ -58,6 +58,17 @@ const SideBar = styled.div`
}
`;
const Wrapper = styled.div`
border-left: 1px solid var(--ads-v2-color-border);
padding: 0 var(--ads-v2-spaces-7) var(--ads-v2-spaces-4);
overflow: hidden;
border-bottom: 0;
display: flex;
width: ${(props) => props.theme.actionSidePane.width}px;
margin-top: 10px;
/* margin-left: var(--ads-v2-spaces-7); */
`;
export function useEntityDependencies(actionName: string) {
const deps = useSelector((state: AppState) => state.evaluations.dependencies);
const entityDependencies = useMemo(
@ -90,16 +101,18 @@ function ActionSidebar({
}
return (
<SideBar>
{actionRightPaneBackLink}
<CollapsibleGroupContainer>
{additionalSections && (
<CollapsibleGroup maxHeight={"50%"}>
{additionalSections}
</CollapsibleGroup>
)}
</CollapsibleGroupContainer>
</SideBar>
<Wrapper>
<SideBar>
{actionRightPaneBackLink}
<CollapsibleGroupContainer>
{additionalSections && (
<CollapsibleGroup maxHeight={"50%"}>
{additionalSections}
</CollapsibleGroup>
)}
</CollapsibleGroupContainer>
</SideBar>
</Wrapper>
);
}

View File

@ -269,6 +269,7 @@ export interface ActionOperation {
focusEntityType?: FocusEntity;
dsName?: string;
entityExplorerTitle?: string;
isBeta?: boolean;
}
export const actionOperations: ActionOperation[] = [

View File

@ -18,8 +18,6 @@ import type {
ActionData,
ActionDataState,
} from "@appsmith/reducers/entityReducers/actionsReducer";
import { EntityIcon } from "pages/Editor/Explorer/ExplorerIcons";
import { Icon } from "design-system";
import type {
ModuleInstanceData,
ModuleInstanceDataState,
@ -27,6 +25,11 @@ import type {
import { selectFeatureFlagCheck } from "@appsmith/selectors/featureFlagsSelectors";
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
import type { AppState } from "@appsmith/reducers";
import type { Module } from "@appsmith/constants/ModuleConstants";
import { getAllModules } from "@appsmith/selectors/modulesSelector";
import { resolveIcon } from "pages/Editor/utils";
import { Icon } from "design-system";
import { EntityIcon } from "pages/Editor/Explorer/ExplorerIcons";
enum SortingWeights {
alphabetical = 1,
@ -97,8 +100,25 @@ interface ConnectToOptionsProps {
export const getQueryIcon = (
query: ActionData | ModuleInstanceData,
pluginImages: Record<string, string>,
modules: Record<string, Module>,
) => {
if (!query.config.hasOwnProperty("sourceModuleId")) {
if (query.config.hasOwnProperty("type")) {
const q = query as ModuleInstanceData;
const module = modules[q.config.sourceModuleId];
const icon = resolveIcon({
iconLocation: pluginImages[module.pluginId] || "",
pluginType: module.pluginType,
moduleType: module.type,
});
return (
icon || (
<EntityIcon>
<Icon name="module" />
</EntityIcon>
)
);
} else {
const action = query as ActionData;
return (
<ImageWrapper>
@ -109,12 +129,6 @@ export const getQueryIcon = (
/>
</ImageWrapper>
);
} else {
return (
<EntityIcon>
<Icon name="module" />
</EntityIcon>
);
}
};
@ -172,6 +186,7 @@ function useConnectToOptions(props: ConnectToOptionsProps) {
const { pluginImages, widget } = props;
const queryModuleInstances = useSelector(getQueryModuleInstances);
const modules = useSelector(getAllModules);
let filteredQueries: ActionData[] | ModuleInstanceData[] = queries;
/* Exclude Gsheets from query options till this gets resolved https://github.com/appsmithorg/appsmith/issues/27102*/
@ -193,7 +208,7 @@ function useConnectToOptions(props: ConnectToOptionsProps) {
id: query.config.id,
label: query.config.name,
value: getBindingValue(widget, query),
icon: getQueryIcon(query, pluginImages),
icon: getQueryIcon(query, pluginImages, modules),
onSelect: function (value?: string, valueOption?: DropdownOptionType) {
addBinding(
valueOption?.value,

View File

@ -32,6 +32,7 @@ import type {
} from "@appsmith/constants/ModuleInstanceConstants";
import { MODULE_TYPE } from "@appsmith/constants/ModuleConstants";
import type { JSAction } from "entities/JSCollection";
import { getAllModules } from "@appsmith/selectors/modulesSelector";
class ActionSelectorControl extends BaseControl<ControlProps> {
componentRef = React.createRef<HTMLDivElement>();
@ -108,6 +109,7 @@ class ActionSelectorControl extends BaseControl<ControlProps> {
const moduleInstances = getModuleInstances(state);
const queryModuleInstances = [] as ModuleInstanceDataState;
const jsModuleInstances = getJSModuleInstancesData(state);
const modules = getAllModules(state);
if (!!moduleInstances) {
for (const moduleInstance of Object.values(moduleInstances)) {
@ -166,6 +168,7 @@ class ActionSelectorControl extends BaseControl<ControlProps> {
},
queryModuleInstances,
jsModuleInstances,
modules,
);
try {

View File

@ -34,7 +34,7 @@ export const NameWrapper = styled.div<{ enableFontStyling?: boolean }>`
}`
: null}
& .t--action-name-edit-field, & .t--js-action-name-edit-field {
& .t--action-name-edit-field, & .t--js-action-name-edit-field, & .t--module-instance-name-edit-field {
width: 100%;
& > span {

View File

@ -31,6 +31,8 @@ export const DOCS_BRANCH_PROTECTION_URL =
"https://docs.appsmith.com/advanced-concepts/version-control-with-git/working-with-branches#branch-protection";
export const DOCS_DEFAULT_BRANCH_URL =
"https://docs.appsmith.com/advanced-concepts/version-control-with-git/working-with-branches#default-branch";
export const PACKAGES_OVERVIEW_DOC =
"https://docs.appsmith.com/packages/overview";
export const PRICING_PAGE_URL = (
URL: string,

View File

@ -0,0 +1,3 @@
export * from "ce/pages/Editor/gitSync/useReconnectModalData";
import { default as useCEReconnectModalData } from "ce/pages/Editor/gitSync/useReconnectModalData";
export default useCEReconnectModalData;

View File

@ -0,0 +1 @@
export * from "ce/selectors/modulesSelector";

View File

@ -75,6 +75,18 @@ export enum ActionCreationSourceTypeEnum {
COPY_ACTION = "COPY_ACTION",
}
// Used for analytic events
export enum ActionExecutionContext {
SELF = "SELF",
ONE_CLICK_BINDING = "ONE_CLICK_BINDING",
GENERATE_CRUD_PAGE = "GENERATE_CRUD_PAGE",
CLONE_PAGE = "CLONE_PAGE",
FORK_TEMPLATE_PAGE = "FORK_TEMPLATE_PAGE",
PAGE_LOAD = "PAGE_LOAD",
EVALUATION_ACTION_TRIGGER = "EVALUATION_ACTION_TRIGGER",
REFRESH_ACTIONS_ON_ENV_CHANGE = "REFRESH_ACTIONS_ON_ENV_CHANGE",
}
export interface KeyValuePair {
key?: string;
value?: unknown;

View File

@ -20,13 +20,13 @@ import { getShouldAllowDrag } from "selectors/widgetDragSelectors";
import { combinedPreviewModeSelector } from "selectors/editorSelectors";
import { getAnvilSpaceDistributionStatus } from "layoutSystems/anvil/integrations/selectors";
const DraggableWrapper = styled.div`
const DraggableWrapper = styled.div<{ draggable: boolean }>`
display: block;
flex-direction: column;
width: 100%;
height: 100%;
user-select: none;
cursor: grab;
cursor: ${(props) => (props.draggable ? "grab" : "unset")};
`;
export interface DraggableComponentProps {
@ -37,7 +37,7 @@ export interface DraggableComponentProps {
type: string;
children: ReactNode;
generateDragState: (
e: React.DragEvent<Element>,
e: React.DragEvent,
draggableRef: HTMLElement,
) => SetDraggingStateActionPayload;
dragDisabled: boolean;
@ -53,7 +53,6 @@ const WidgetBoundaries = styled.div`
${(props) => getColorWithOpacity(props.theme.colors.textAnchor, 0.5)};
pointer-events: none;
top: 0;
position: absolute;
left: 0;
`;
@ -100,14 +99,14 @@ function DraggableComponent(props: DraggableComponentProps) {
!props.isFlexChild && (isCurrentWidgetDragging || isDraggingSibling);
// When mouse is over this draggable
const handleMouseOver = (e: any) => {
const handleMouseOver = (e: React.MouseEvent) => {
focusWidget &&
!isResizingOrDragging &&
!isFocused &&
!isDistributingSpace &&
!props.resizeDisabled &&
!isPreviewMode &&
focusWidget(props.widgetId);
focusWidget(props.widgetId, e.metaKey);
e.stopPropagation();
};

View File

@ -40,6 +40,7 @@ import { useCurrentAppState } from "pages/Editor/IDE/hooks";
import { getIsAppSettingsPaneWithNavigationTabOpen } from "selectors/appSettingsPaneSelectors";
import { getLayoutSystemType } from "selectors/layoutSystemSelectors";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { getWidgetSelectionBlock } from "selectors/ui";
import {
isAutoHeightEnabledForWidget,
isAutoHeightEnabledForWidgetWithLimits,
@ -259,6 +260,7 @@ export function DropTargetComponent(props: DropTargetComponentProps) {
);
// Are we changing the auto height limits by dragging the signifiers?
const { isAutoHeightWithLimitsChanging } = useAutoHeightUIState();
const isWidgetSelectionBlocked = useSelector(getWidgetSelectionBlock);
const dispatch = useDispatch();
@ -327,7 +329,12 @@ export function DropTargetComponent(props: DropTargetComponentProps) {
(e.target as HTMLDivElement).dataset.testid === selectionDiv ||
(e.target as HTMLDivElement).dataset.testid === mainCanvasId;
if (!isResizing && !isDragging && !isAutoHeightWithLimitsChanging) {
if (
!isResizing &&
!isDragging &&
!isAutoHeightWithLimitsChanging &&
!isWidgetSelectionBlocked
) {
// Check if Target is the MainCanvas
if (isTargetMainCanvas) {
goToWidgetAdd();

View File

@ -23,6 +23,7 @@ import {
combinedPreviewModeSelector,
snipingModeSelector,
} from "selectors/editorSelectors";
import { getWidgetSelectionBlock } from "../../../selectors/ui";
const minSize = 100;
/**
@ -101,7 +102,9 @@ export const ModalResizableLayer = ({
};
const isPreviewMode = useSelector(combinedPreviewModeSelector);
const isSnipingMode = useSelector(snipingModeSelector);
const enableResizing = !isSnipingMode && !isPreviewMode;
const isWidgetSelectionBlocked = useSelector(getWidgetSelectionBlock);
const enableResizing =
!isSnipingMode && !isPreviewMode && !isWidgetSelectionBlocked;
return (
<ModalResizable
allowResize

View File

@ -65,6 +65,10 @@ import {
computeFinalRowCols,
computeFinalAutoLayoutRowCols,
} from "layoutSystems/common/resizer/ResizableUtils";
import {
getAltBlockWidgetSelection,
getWidgetSelectionBlock,
} from "selectors/ui";
export type ResizableComponentProps = WidgetProps & {
paddingOffset: number;
@ -80,6 +84,8 @@ export const ResizableComponent = memo(function ResizableComponent(
const Resizable = isAutoLayout ? AutoLayoutResizable : FixedLayoutResizable;
const isSnipingMode = useSelector(snipingModeSelector);
const isPreviewMode = useSelector(combinedPreviewModeSelector);
const isWidgetSelectionBlock = useSelector(getWidgetSelectionBlock);
const isAltWidgetSelectionBlock = useSelector(getAltBlockWidgetSelection);
const isAppSettingsPaneWithNavigationTabOpen = useSelector(
getIsAppSettingsPaneWithNavigationTabOpen,
);
@ -325,6 +331,7 @@ export const ResizableComponent = memo(function ResizableComponent(
!props.resizeDisabled &&
!isSnipingMode &&
!isPreviewMode &&
!isWidgetSelectionBlock &&
!isAppSettingsPaneWithNavigationTabOpen;
const { updateDropTargetRows } = useContext(DropTargetContext);
@ -387,6 +394,7 @@ export const ResizableComponent = memo(function ResizableComponent(
!isPreviewMode &&
!isAppSettingsPaneWithNavigationTabOpen &&
!isDragging &&
!isAltWidgetSelectionBlock &&
(isHovered || isSelected);
return (

View File

@ -1,11 +1,11 @@
import { Classes, Tooltip } from "@blueprintjs/core";
import { Colors } from "constants/Colors";
import type { CSSProperties } from "react";
import React from "react";
import { useSelector } from "react-redux";
import { snipingModeSelector } from "selectors/editorSelectors";
import styled from "styled-components";
import { Icon } from "design-system";
import { Icon, Text, Tooltip } from "design-system";
// I honestly can't think of a better name for this enum
export enum Activities {
HOVERING,
@ -13,13 +13,7 @@ export enum Activities {
ACTIVE,
NONE,
}
const StyledTooltip = styled(Tooltip)<{
children?: React.ReactNode;
}>`
.${Classes.POPOVER_TARGET} {
height: 100%;
}
`;
const WidgetNameBoundary = 1;
const BORDER_RADIUS = 4;
const SettingsWrapper = styled.div<{ widgetWidth: number; inverted: boolean }>`
@ -60,10 +54,6 @@ const WidgetName = styled.span`
white-space: nowrap;
`;
const StyledErrorIcon = styled(Icon)`
margin-right: ${(props) => props.theme.spaces[1]}px;
`;
interface SettingsControlProps {
toggleSettings: (e: any) => void;
activity: Activities;
@ -111,17 +101,17 @@ const getStyles = (
export function SettingsControl(props: SettingsControlProps) {
const isSnipingMode = useSelector(snipingModeSelector);
const errorIcon = <StyledErrorIcon name="warning" size="sm" />;
const errorIcon = <Icon name="warning" size="sm" />;
return (
<StyledTooltip
<Tooltip
content={
isSnipingMode
? `Bind to widget ${props.name}`
: "Edit widget properties"
<Text color="var(--ads-v2-color-white)">
{isSnipingMode ? `Bind to widget ${props.name}` : `Edit widget`}
</Text>
}
hoverOpenDelay={500}
position="top-right"
mouseEnterDelay={0}
placement="topRight"
>
<SettingsWrapper
className="t--widget-propertypane-toggle"
@ -143,7 +133,7 @@ export function SettingsControl(props: SettingsControlProps) {
{isSnipingMode ? `Bind to ${props.name}` : props.name}
</WidgetName>
</SettingsWrapper>
</StyledTooltip>
</Tooltip>
);
}

View File

@ -30,6 +30,7 @@ import { getAbsolutePixels } from "utils/helpers";
import type { XYCord } from "layoutSystems/common/canvasArenas/ArenaTypes";
import { useCanvasDragToScroll } from "layoutSystems/common/canvasArenas/useCanvasDragToScroll";
import { StickyCanvasArena } from "layoutSystems/common/canvasArenas/StickyCanvasArena";
import { getWidgetSelectionBlock } from "../../../../selectors/ui";
export interface SelectedArenaDimensions {
top: number;
@ -71,6 +72,7 @@ export function CanvasSelectionArena({
);
const appMode = useSelector(getAppMode);
const isPreviewMode = useSelector(combinedPreviewModeSelector);
const isWidgetSelectionBlocked = useSelector(getWidgetSelectionBlock);
const isAppSettingsPaneWithNavigationTabOpen = useSelector(
getIsAppSettingsPaneWithNavigationTabOpen,
);
@ -501,6 +503,7 @@ export function CanvasSelectionArena({
!(
isDragging ||
isPreviewMode ||
isWidgetSelectionBlocked ||
isAppSettingsPaneWithNavigationTabOpen ||
dropDisabled
);

View File

@ -644,102 +644,107 @@ function CommonEditorForm(props: CommonFormPropsWithExtraParams) {
</HelpSection>
)}
<Wrapper>
<SecondaryWrapper>
<TabbedViewContainer>
<Tabs
className="h-full"
defaultValue={
isGraphql ? API_EDITOR_TABS.BODY : API_EDITOR_TABS.HEADERS
}
// eslint-disable-next-line
//@ts-ignore
onValueChange={setSelectedIndex}
value={selectedValue}
>
<TabsListWrapper>
<TabsList>
{Object.values(API_EDITOR_TABS).map((tab) => (
<Tab
data-testid={`t--api-editor-${tab}`}
key={tab}
notificationCount={
tab == "HEADERS"
? headersCount
: tab == "PARAMS"
? paramsCount
: undefined
}
value={tab}
>
{createMessage(API_EDITOR_TAB_TITLES[tab])}
</Tab>
))}
</TabsList>
</TabsListWrapper>
<StyledTabPanel value={API_EDITOR_TABS.HEADERS}>
<ImportedDatas
attributeName="header"
autogeneratedHeaders={autoGeneratedActionConfigHeaders}
data={props.datasourceHeaders}
/>
<KeyValueFieldArray
actionConfig={actionConfigurationHeaders}
dataTreePath={`${actionName}.config.headers`}
hideHeader
label="Headers"
name="actionConfiguration.headers"
placeholder="Value"
pushFields={isChangePermitted}
theme={theme}
/>
</StyledTabPanel>
<StyledTabPanel value={API_EDITOR_TABS.PARAMS}>
<ImportedDatas
attributeName={"param"}
data={props.datasourceParams}
/>
<KeyValueFieldArray
actionConfig={actionConfigurationParams}
dataTreePath={`${actionName}.config.queryParameters`}
hideHeader
label="Params"
name="actionConfiguration.queryParameters"
pushFields={isChangePermitted}
theme={theme}
/>
</StyledTabPanel>
<StyledTabPanel className="h-full" value={API_EDITOR_TABS.BODY}>
{props.bodyUIComponent}
</StyledTabPanel>
<StyledTabPanel value={API_EDITOR_TABS.PAGINATION}>
{props.paginationUIComponent}
</StyledTabPanel>
<StyledTabPanel value={API_EDITOR_TABS.AUTHENTICATION}>
<ApiAuthentication formName={formName} />
</StyledTabPanel>
<StyledTabPanel value={API_EDITOR_TABS.SETTINGS}>
<SettingsWrapper>
<ActionSettings
actionSettingsConfig={settingsConfig}
formName={formName}
<div className="flex flex-1">
<SecondaryWrapper>
<TabbedViewContainer>
<Tabs
className="h-full"
defaultValue={
isGraphql ? API_EDITOR_TABS.BODY : API_EDITOR_TABS.HEADERS
}
// eslint-disable-next-line
//@ts-ignore
onValueChange={setSelectedIndex}
value={selectedValue}
>
<TabsListWrapper>
<TabsList>
{Object.values(API_EDITOR_TABS).map((tab) => (
<Tab
data-testid={`t--api-editor-${tab}`}
key={tab}
notificationCount={
tab == "HEADERS"
? headersCount
: tab == "PARAMS"
? paramsCount
: undefined
}
value={tab}
>
{createMessage(API_EDITOR_TAB_TITLES[tab])}
</Tab>
))}
</TabsList>
</TabsListWrapper>
<StyledTabPanel value={API_EDITOR_TABS.HEADERS}>
<ImportedDatas
attributeName="header"
autogeneratedHeaders={autoGeneratedActionConfigHeaders}
data={props.datasourceHeaders}
/>
<KeyValueFieldArray
actionConfig={actionConfigurationHeaders}
dataTreePath={`${actionName}.config.headers`}
hideHeader
label="Headers"
name="actionConfiguration.headers"
placeholder="Value"
pushFields={isChangePermitted}
theme={theme}
/>
</SettingsWrapper>
</StyledTabPanel>
</Tabs>
</TabbedViewContainer>
<ApiResponseView
actionResponse={actionResponse}
apiName={actionName}
currentActionConfig={currentActionConfig}
disabled={!isExecutePermitted}
isRunning={isRunning}
onRunClick={onRunClick}
responseDataTypes={responseDataTypes}
responseDisplayFormat={responseDisplayFormat}
theme={theme}
/>
</SecondaryWrapper>
</StyledTabPanel>
<StyledTabPanel value={API_EDITOR_TABS.PARAMS}>
<ImportedDatas
attributeName={"param"}
data={props.datasourceParams}
/>
<KeyValueFieldArray
actionConfig={actionConfigurationParams}
dataTreePath={`${actionName}.config.queryParameters`}
hideHeader
label="Params"
name="actionConfiguration.queryParameters"
pushFields={isChangePermitted}
theme={theme}
/>
</StyledTabPanel>
<StyledTabPanel
className="h-full"
value={API_EDITOR_TABS.BODY}
>
{props.bodyUIComponent}
</StyledTabPanel>
<StyledTabPanel value={API_EDITOR_TABS.PAGINATION}>
{props.paginationUIComponent}
</StyledTabPanel>
<StyledTabPanel value={API_EDITOR_TABS.AUTHENTICATION}>
<ApiAuthentication formName={formName} />
</StyledTabPanel>
<StyledTabPanel value={API_EDITOR_TABS.SETTINGS}>
<SettingsWrapper>
<ActionSettings
actionSettingsConfig={settingsConfig}
formName={formName}
theme={theme}
/>
</SettingsWrapper>
</StyledTabPanel>
</Tabs>
</TabbedViewContainer>
<ApiResponseView
actionResponse={actionResponse}
apiName={actionName}
currentActionConfig={currentActionConfig}
disabled={!isExecutePermitted}
isRunning={isRunning}
onRunClick={onRunClick}
responseDataTypes={responseDataTypes}
responseDisplayFormat={responseDisplayFormat}
theme={theme}
/>
</SecondaryWrapper>
</div>
<ActionRightPane
actionRightPaneBackLink={actionRightPaneBackLink}
additionalSections={actionRightPaneAdditionSections}

View File

@ -28,7 +28,7 @@ import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { ApiEditorContextProvider } from "./ApiEditorContext";
import type { PaginationField } from "api/ActionAPI";
import { get } from "lodash";
import { get, keyBy } from "lodash";
import PerformanceTracker, {
PerformanceTransactionName,
} from "utils/PerformanceTracker";
@ -38,6 +38,8 @@ import { MODULE_TYPE } from "@appsmith/constants/ModuleConstants";
import Disabler from "pages/common/Disabler";
import ConvertEntityNotification from "@appsmith/pages/common/ConvertEntityNotification";
import { useIsEditorPaneSegmentsEnabled } from "../IDE/hooks";
import { Icon } from "design-system";
import { resolveIcon } from "../utils";
type ApiEditorWrapperProps = RouteComponentProps<APIEditorRouteParams>;
@ -65,6 +67,12 @@ function ApiEditorWrapper(props: ApiEditorWrapperProps) {
const isConverting = useSelector((state) =>
getIsActionConverting(state, action?.id || ""),
);
const pluginGroups = useMemo(() => keyBy(plugins, "id"), [plugins]);
const icon = resolveIcon({
iconLocation: pluginGroups[pluginId]?.iconLocation || "",
pluginType: action?.pluginType || "",
moduleType: action?.actionConfiguration?.body?.moduleType,
}) || <Icon name="module" />;
const isChangePermitted = getHasManageActionPermission(
isFeatureEnabled,
@ -150,7 +158,7 @@ function ApiEditorWrapper(props: ApiEditorWrapperProps) {
const notification = useMemo(() => {
if (!isConverting) return null;
return <ConvertEntityNotification name={action?.name || ""} />;
return <ConvertEntityNotification icon={icon} name={action?.name || ""} />;
}, [action?.name, isConverting]);
return (

View File

@ -18,6 +18,7 @@ import { CANVAS_ART_BOARD } from "constants/componentClassNameConstants";
import { renderAppsmithCanvas } from "layoutSystems/CanvasFactory";
import type { WidgetProps } from "widgets/BaseWidget";
import { getAppThemeSettings } from "@appsmith/selectors/applicationSelectors";
import CodeModeTooltip from "pages/Editor/WidgetsEditor/CodeModeTooltip";
interface CanvasProps {
widgetsStructure: CanvasWidgetStructure;
@ -83,20 +84,22 @@ const Canvas = (props: CanvasProps) => {
const renderChildren = () => {
return (
<Wrapper
$enableMainCanvasResizer={!!props.enableMainCanvasResizer}
background={isWDSEnabled ? "" : backgroundForCanvas}
className={`relative t--canvas-artboard ${paddingBottomClass} transition-all duration-400 ${marginHorizontalClass} ${getViewportClassName(
canvasWidth,
)}`}
data-testid={"t--canvas-artboard"}
id={CANVAS_ART_BOARD}
ref={isWDSEnabled ? undefined : focusRef}
width={canvasWidth}
>
{props.widgetsStructure.widgetId &&
renderAppsmithCanvas(props.widgetsStructure as WidgetProps)}
</Wrapper>
<CodeModeTooltip>
<Wrapper
$enableMainCanvasResizer={!!props.enableMainCanvasResizer}
background={isWDSEnabled ? "" : backgroundForCanvas}
className={`relative t--canvas-artboard ${paddingBottomClass} transition-all duration-400 ${marginHorizontalClass} ${getViewportClassName(
canvasWidth,
)}`}
data-testid={"t--canvas-artboard"}
id={CANVAS_ART_BOARD}
ref={isWDSEnabled ? undefined : focusRef}
width={canvasWidth}
>
{props.widgetsStructure.widgetId &&
renderAppsmithCanvas(props.widgetsStructure as WidgetProps)}
</Wrapper>
</CodeModeTooltip>
);
};

View File

@ -337,21 +337,3 @@ export function AppsmithAIIcon() {
export function ActionUrlIcon(url: string) {
return <img src={url} />;
}
export function ModuleIcon(
height = 18,
width = 18,
noBackground = false,
noBorder = false,
) {
return (
<EntityIcon
height={height + "px"}
noBackground={noBackground}
noBorder={noBorder}
width={width + "px"}
>
<Icon name="module" size="md" />
</EntityIcon>
);
}

View File

@ -102,6 +102,7 @@ const PagesSection = () => {
style={springs}
>
<PaneHeader
className="pages"
rightIcon={
canCreatePages ? (
<AddPageContextMenu

View File

@ -0,0 +1,51 @@
import React, { useState } from "react";
import { AnnouncementModal, Button } from "design-system";
import localStorage, { LOCAL_STORAGE_KEYS } from "utils/localStorage";
import {
SPLITPANE_ANNOUNCEMENT,
createMessage,
} from "@appsmith/constants/messages";
import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
import { ASSETS_CDN_URL } from "constants/ThirdPartyConstants";
const Announcement = () => {
const localStorageFlag =
localStorage.getItem(LOCAL_STORAGE_KEYS.SPLITPANE_ANNOUNCEMENT) || "true";
const [show, setShow] = useState(JSON.parse(localStorageFlag));
const tryClickHandler = () => {
setShow(false);
localStorage.setItem(LOCAL_STORAGE_KEYS.SPLITPANE_ANNOUNCEMENT, "false");
};
const learnClickHandler = () => {
window.open(
"https://community.appsmith.com/content/blog/discover-ide-20-building-more-efficient-ide",
"_blank",
);
};
const modalFooter = () => (
<>
<Button kind="primary" onClick={tryClickHandler} size="md">
Try it out
</Button>
<Button kind="tertiary" onClick={learnClickHandler} size="md">
Learn more
</Button>
</>
);
return (
<AnnouncementModal
banner={getAssetUrl(`${ASSETS_CDN_URL}/splitpane-banner.svg`)}
description={createMessage(SPLITPANE_ANNOUNCEMENT.DESCRIPTION)}
footer={modalFooter()}
isBeta
isOpen={show}
title={createMessage(SPLITPANE_ANNOUNCEMENT.TITLE)}
/>
);
};
export { Announcement };

View File

@ -1,17 +0,0 @@
import React from "react";
import { useSelector } from "react-redux";
import { default as OldPages } from "pages/Editor/Explorer/Pages";
import { PagesSection } from "../PagesSection";
import { getIsSideBySideEnabled } from "selectors/ideSelectors";
const Pages = () => {
const isSideBySideEnabled = useSelector(getIsSideBySideEnabled);
if (!isSideBySideEnabled) {
return <OldPages />;
} else {
return <PagesSection />;
}
};
export { Pages };

View File

@ -1,4 +1,4 @@
import React from "react";
import React, { useCallback } from "react";
import { Button, Flex, SegmentedControl, Tooltip } from "design-system";
import {
createMessage,
@ -19,6 +19,7 @@ import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
import { getIDEViewMode, getIsSideBySideEnabled } from "selectors/ideSelectors";
import { setIdeEditorViewMode } from "actions/ideActions";
import AnalyticsUtil from "../../../../../utils/AnalyticsUtil";
const Container = styled(Flex)`
#editor-pane-segment-control {
@ -44,6 +45,12 @@ const SegmentedHeader = () => {
};
const { segment } = useCurrentEditorState();
const { onSegmentChange } = useSegmentNavigation();
const handleMaximizeButtonClick = useCallback(() => {
AnalyticsUtil.logEvent("EDITOR_MODE_CHANGE", {
to: EditorViewMode.FullScreen,
});
dispatch(setIdeEditorViewMode(EditorViewMode.FullScreen));
}, []);
return (
<Container
@ -91,9 +98,7 @@ const SegmentedHeader = () => {
id="editor-mode-maximize"
isIconButton
kind="tertiary"
onClick={() =>
dispatch(setIdeEditorViewMode(EditorViewMode.FullScreen))
}
onClick={handleMaximizeButtonClick}
startIcon="maximize-v3"
/>
</Tooltip>

View File

@ -9,7 +9,7 @@ import EditorPaneSegments from "./EditorPaneSegments";
import GlobalAdd from "./GlobalAdd";
import { useEditorPaneWidth } from "../hooks";
import EntityProperties from "pages/Editor/Explorer/Entity/EntityProperties";
import { Pages } from "./components/Pages";
import { PagesSection } from "./PagesSection";
const EditorPane = ({ match: { path } }: RouteComponentProps) => {
const width = useEditorPaneWidth();
@ -26,7 +26,7 @@ const EditorPane = ({ match: { path } }: RouteComponentProps) => {
{/** Entity Properties component is needed to render
the Bindings popover in the context menu. Will be removed eventually **/}
<EntityProperties />
<Pages />
<PagesSection />
<Switch>
<SentryRoute component={GlobalAdd} exact path={`${path}${ADD_PATH}`} />

View File

@ -18,6 +18,7 @@ import {
MINIMIZE_BUTTON_TOOLTIP,
createMessage,
} from "@appsmith/constants/messages";
import AnalyticsUtil from "utils/AnalyticsUtil";
const FullScreenTabs = () => {
const dispatch = useDispatch();
@ -26,6 +27,9 @@ const FullScreenTabs = () => {
const { segment } = useCurrentEditorState();
const setSplitScreenMode = useCallback(() => {
dispatch(setIdeEditorViewMode(EditorViewMode.SplitScreen));
AnalyticsUtil.logEvent("EDITOR_MODE_CHANGE", {
to: EditorViewMode.SplitScreen,
});
}, []);
const tabsConfig = TabSelectors[segment];
const pageId = useSelector(getCurrentPageId);

View File

@ -19,6 +19,7 @@ import { getCurrentPageId } from "@appsmith/selectors/entitiesSelector";
import history, { NavigationMethod } from "utils/history";
import { includes } from "lodash";
import ListButton from "./ListButton";
import { Announcement } from "../EditorPane/components/Announcement";
const SplitScreenTabs = () => {
const isSideBySideEnabled = useSelector(getIsSideBySideEnabled);
@ -53,18 +54,23 @@ const SplitScreenTabs = () => {
if (!isSideBySideEnabled) return null;
if (ideViewMode === EditorViewMode.FullScreen) return null;
if (segment === EditorEntityTab.UI) return null;
return files.length > 0 ? (
<Container>
<ToggleButton
icon="add-line"
isSelected={segmentMode === EditorEntityTabState.Add}
onClick={onAddClick}
size="md"
/>
<FileTabs navigateToTab={onClick} tabs={files} />
<ListButton items={overflowList} navigateToTab={onClick} />
</Container>
) : null;
return (
<>
{files.length > 0 ? (
<Container>
<ToggleButton
icon="add-line"
isSelected={segmentMode === EditorEntityTabState.Add}
onClick={onAddClick}
size="md"
/>
<FileTabs navigateToTab={onClick} tabs={files} />
<ListButton items={overflowList} navigateToTab={onClick} />
</Container>
) : null}
<Announcement />
</>
);
};
export default SplitScreenTabs;

View File

@ -32,6 +32,7 @@ const EditorTitle = ({ title }: { title: string }) => {
<Flex
alignItems={"center"}
className={"t--pages-switcher"}
data-active={active}
gap={"spaces-1"}
height={"100%"}
justifyContent={"center"}

View File

@ -27,7 +27,6 @@ import {
DEPLOY_MENU_OPTION,
IN_APP_EMBED_SETTING,
INVITE_TAB,
RENAME_APPLICATION_TOOLTIP,
HEADER_TITLES,
} from "@appsmith/constants/messages";
import EditorName from "pages/Editor/EditorName";
@ -35,8 +34,10 @@ import { GetNavigationMenuData } from "pages/Editor/EditorName/NavigationMenuDat
import {
getCurrentApplicationId,
getCurrentPageId,
getIsPageSaving,
getIsPublishingApplication,
getPageById,
getPageSavingError,
} from "selectors/editorSelectors";
import {
getApplicationList,
@ -74,6 +75,7 @@ import { EditorTitle } from "./EditorTitle";
import { useCurrentAppState } from "pages/Editor/IDE/hooks";
import { DefaultTitle } from "./DeaultTitle";
import { EditorState } from "@appsmith/entities/IDE/constants";
import { EditorSaveIndicator } from "../../EditorSaveIndicator";
const StyledDivider = styled(Divider)`
height: 50%;
@ -99,6 +101,8 @@ const Header = () => {
const pageId = useSelector(getCurrentPageId) as string;
const currentPage = useSelector(getPageById(pageId));
const appState = useCurrentAppState();
const isSaving = useSelector(getIsPageSaving);
const pageSaveError = useSelector(getPageSavingError);
// states
const [isPopoverOpen, setIsPopoverOpen] = useState<boolean>(false);
@ -202,7 +206,6 @@ const Header = () => {
className={"t--editor-header"}
height={"40px"}
overflow={"hidden"}
px={"spaces-4"}
width={"100%"}
>
<Flex
@ -212,10 +215,12 @@ const Header = () => {
gap={"spaces-4"}
height={"100%"}
justifyContent={"left"}
pl={"spaces-4"}
>
<AppsmithLink />
<Divider orientation="vertical" />
<TitleComponent />
<EditorSaveIndicator isSaving={isSaving} saveError={pageSaveError} />
</Flex>
<Flex
alignItems={"center"}
@ -224,49 +229,43 @@ const Header = () => {
height={"100%"}
justifyContent={"center"}
>
<Tooltip
content={createMessage(RENAME_APPLICATION_TOOLTIP)}
isDisabled={isPopoverOpen}
placement="bottom"
>
<Flex alignItems={"center"}>
{currentWorkspace.name && (
<>
<Text
color={"var(--ads-v2-colors-content-label-inactive-fg)"}
kind="body-m"
>
{currentWorkspace.name + " / "}
</Text>
<EditorName
applicationId={applicationId}
className="t--application-name editable-application-name max-w-48"
defaultSavingState={
isSavingName ? SavingState.STARTED : SavingState.NOT_STARTED
}
defaultValue={currentApplication?.name || ""}
editInteractionKind={EditInteractionKind.SINGLE}
editorName="Application"
fill
getNavigationMenu={GetNavigationMenuData}
isError={isErroredSavingName}
isNewEditor={
applicationList.filter((el) => el.id === applicationId)
.length > 0
}
isPopoverOpen={isPopoverOpen}
onBlur={(value: string) =>
updateApplicationDispatch(applicationId || "", {
name: value,
currentApp: true,
})
}
setIsPopoverOpen={setIsPopoverOpen}
/>
</>
)}
</Flex>
</Tooltip>
<Flex alignItems={"center"}>
{currentWorkspace.name && (
<>
<Text
color={"var(--ads-v2-colors-content-label-inactive-fg)"}
kind="body-m"
>
{currentWorkspace.name + " / "}
</Text>
<EditorName
applicationId={applicationId}
className="t--application-name editable-application-name max-w-48"
defaultSavingState={
isSavingName ? SavingState.STARTED : SavingState.NOT_STARTED
}
defaultValue={currentApplication?.name || ""}
editInteractionKind={EditInteractionKind.SINGLE}
editorName="Application"
fill
getNavigationMenu={GetNavigationMenuData}
isError={isErroredSavingName}
isNewEditor={
applicationList.filter((el) => el.id === applicationId)
.length > 0
}
isPopoverOpen={isPopoverOpen}
onBlur={(value: string) =>
updateApplicationDispatch(applicationId || "", {
name: value,
currentApp: true,
})
}
setIsPopoverOpen={setIsPopoverOpen}
/>
</>
)}
</Flex>
</Flex>
<Flex
alignItems={"center"}

View File

@ -5,6 +5,7 @@ import { Text } from "design-system";
interface Props {
title: string;
rightIcon?: React.ReactNode;
className?: string;
}
const Container = styled.div`
@ -22,7 +23,7 @@ const Container = styled.div`
function PaneHeader(props: Props) {
return (
<Container>
<Container className={props.className}>
<Text kind="heading-xs">{props.title}</Text>
{props.rightIcon ? props.rightIcon : null}
</Container>

View File

@ -4,11 +4,13 @@ import { Route, Switch, useRouteMatch } from "react-router";
import * as Sentry from "@sentry/react";
import useRoutes from "@appsmith/pages/Editor/IDE/MainPane/useRoutes";
import EditorTabs from "pages/Editor/IDE/EditorTabs/FullScreenTabs";
import { useWidgetSelectionBlockListener } from "pages/Editor/IDE/hooks";
const SentryRoute = Sentry.withSentryRouting(Route);
export const MainPane = (props: { id: string }) => {
const { path } = useRouteMatch();
const routes = useRoutes(path);
useWidgetSelectionBlockListener();
return (
<div

View File

@ -7,7 +7,7 @@ import {
} from "@appsmith/entities/IDE/constants";
import { useLocation } from "react-router";
import { FocusEntity, identifyEntityFromPath } from "navigation/FocusEntity";
import { useSelector } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { getIDEViewMode, getIsSideBySideEnabled } from "selectors/ideSelectors";
import { getPropertyPaneWidth } from "selectors/propertyPaneSelectors";
import { getCurrentPageId } from "@appsmith/selectors/entitiesSelector";
@ -28,6 +28,8 @@ import {
} from "constants/AppConstants";
import { useFeatureFlag } from "utils/hooks/useFeatureFlag";
import { FEATURE_FLAG } from "@appsmith/entities/FeatureFlag";
import { getIsAltFocusWidget, getWidgetSelectionBlock } from "selectors/ui";
import { altFocusWidget, setWidgetSelectionBlock } from "actions/widgetActions";
export const useCurrentAppState = () => {
const [appState, setAppState] = useState(EditorState.EDITOR);
@ -215,3 +217,41 @@ export const useIsEditorPaneSegmentsEnabled = () => {
return isEditorSegmentsReleaseEnabled || isEditorSegmentsRolloutEnabled;
};
export function useWidgetSelectionBlockListener() {
const { pathname } = useLocation();
const dispatch = useDispatch();
const currentFocus = identifyEntityFromPath(pathname);
const isAltFocused = useSelector(getIsAltFocusWidget);
const widgetSelectionIsBlocked = useSelector(getWidgetSelectionBlock);
useEffect(() => {
const inUIMode = [
FocusEntity.CANVAS,
FocusEntity.PROPERTY_PANE,
FocusEntity.WIDGET_LIST,
].includes(currentFocus.entity);
dispatch(setWidgetSelectionBlock(!inUIMode));
}, [currentFocus]);
useEffect(() => {
window.addEventListener("keydown", handleKeyDown);
window.addEventListener("keyup", handleKeyUp);
return () => {
window.removeEventListener("keydown", handleKeyDown);
window.removeEventListener("keyup", handleKeyUp);
};
}, [isAltFocused, widgetSelectionIsBlocked]);
const handleKeyDown = (e: KeyboardEvent) => {
if (!isAltFocused && widgetSelectionIsBlocked && e.metaKey) {
dispatch(altFocusWidget(e.metaKey));
}
};
const handleKeyUp = (e: KeyboardEvent) => {
if (!e.metaKey && widgetSelectionIsBlocked) {
dispatch(altFocusWidget(e.metaKey));
}
};
}

View File

@ -3,13 +3,7 @@ import React, { useCallback } from "react";
import type { InjectedFormProps } from "redux-form";
import { noop } from "lodash";
import type { Datasource } from "entities/Datasource";
import { getPluginNameFromId } from "@appsmith/selectors/entitiesSelector";
import {
PluginName,
type Action,
type QueryAction,
type SaaSAction,
} from "entities/Action";
import type { Action, QueryAction, SaaSAction } from "entities/Action";
import { useDispatch, useSelector } from "react-redux";
import ActionSettings from "pages/Editor/ActionSettings";
import { Button, Tab, TabPanel, Tabs, TabsList, Tooltip } from "design-system";
@ -23,9 +17,7 @@ import {
import { useParams } from "react-router";
import type { AppState } from "@appsmith/reducers";
import { thinScrollbar } from "constants/DefaultTheme";
import ActionRightPane, {
useEntityDependencies,
} from "components/editorComponents/ActionRightPane";
import ActionRightPane from "components/editorComponents/ActionRightPane";
import type { ActionResponse } from "api/ActionAPI";
import type { Plugin } from "api/PluginApi";
import type { UIComponentTypes } from "api/PluginApi";
@ -134,17 +126,6 @@ const DocumentationButton = styled(Button)`
z-index: 6;
`;
const SidebarWrapper = styled.div<{ show: boolean }>`
border-left: 1px solid var(--ads-v2-color-border);
padding: 0 var(--ads-v2-spaces-7) var(--ads-v2-spaces-4);
overflow: hidden;
border-bottom: 0;
display: ${(props) => (props.show ? "flex" : "none")};
width: ${(props) => props.theme.actionSidePane.width}px;
margin-top: 10px;
/* margin-left: var(--ads-v2-spaces-7); */
`;
export const SegmentedControlContainer = styled.div`
padding: 0 var(--ads-v2-spaces-7);
padding-top: var(--ads-v2-spaces-4);
@ -234,13 +215,6 @@ export function EditorJSONtoForm(props: Props) {
FEATURE_FLAG.release_actions_redesign_enabled,
);
const showRightPane = Boolean(actionRightPaneAdditionSections);
// get the current action's plugin name
const currentActionPluginName = useSelector((state: AppState) =>
getPluginNameFromId(state, currentActionConfig?.pluginId || ""),
);
const dispatch = useDispatch();
const handleDocumentationClick = () => {
@ -254,21 +228,12 @@ export function EditorJSONtoForm(props: Props) {
id: currentActionConfig ? currentActionConfig.id : "",
};
const { hasDependencies } = useEntityDependencies(props.actionName);
const selectedConfigTab = useSelector(getQueryPaneConfigSelectedTabIndex);
const setSelectedConfigTab = useCallback((selectedIndex: string) => {
dispatch(setQueryPaneConfigSelectedTabIndex(selectedIndex));
}, []);
// here we check for normal conditions for opening action pane
// or if any of the flags are true, We should open the actionpane by default.
const shouldOpenActionPaneByDefault =
hasDependencies ||
!!actionResponse ||
currentActionPluginName !== PluginName.SMTP;
// when switching between different redux forms, make sure this redux form has been initialized before rendering anything.
// the initialized prop below comes from redux-form.
if (!props.initialized) {
@ -419,14 +384,10 @@ export function EditorJSONtoForm(props: Props) {
/>
</SecondaryWrapper>
</div>
{showRightPane && (
<SidebarWrapper show={shouldOpenActionPaneByDefault}>
<ActionRightPane
actionRightPaneBackLink={actionRightPaneBackLink}
additionalSections={actionRightPaneAdditionSections}
/>
</SidebarWrapper>
)}
<ActionRightPane
actionRightPaneBackLink={actionRightPaneBackLink}
additionalSections={actionRightPaneAdditionSections}
/>
</Wrapper>
</QueryFormContainer>
</>

View File

@ -19,6 +19,7 @@ import { DatasourceCreateEntryPoints } from "constants/Datasource";
import {
getAction,
getIsActionConverting,
getPluginImages,
getPluginSettingConfigs,
} from "@appsmith/selectors/entitiesSelector";
import { integrationEditorURL } from "@appsmith/RouteBuilder";
@ -38,6 +39,8 @@ import { MODULE_TYPE } from "@appsmith/constants/ModuleConstants";
import ConvertEntityNotification from "@appsmith/pages/common/ConvertEntityNotification";
import { PluginType } from "entities/Action";
import { useIsEditorPaneSegmentsEnabled } from "../IDE/hooks";
import { Icon } from "design-system";
import { resolveIcon } from "../utils";
type QueryEditorProps = RouteComponentProps<QueryEditorRouteParams>;
@ -58,6 +61,12 @@ function QueryEditor(props: QueryEditorProps) {
const isConverting = useSelector((state) =>
getIsActionConverting(state, actionId || ""),
);
const pluginImages = useSelector(getPluginImages);
const icon = resolveIcon({
iconLocation: pluginImages[pluginId] || "",
pluginType: action?.pluginType || "",
moduleType: action?.actionConfiguration?.body?.moduleType,
}) || <Icon name="module" />;
const isDeletePermitted = getHasDeleteActionPermission(
isFeatureEnabled,
@ -156,7 +165,13 @@ function QueryEditor(props: QueryEditorProps) {
const notification = useMemo(() => {
if (!isConverting) return null;
return <ConvertEntityNotification name={action?.name || ""} withPadding />;
return (
<ConvertEntityNotification
icon={icon}
name={action?.name || ""}
withPadding
/>
);
}, [action?.name, isConverting]);
return (

View File

@ -1,178 +0,0 @@
import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { saveSelectedThemeAction } from "actions/appThemingActions";
import { getCurrentApplicationId } from "selectors/editorSelectors";
import { getAppThemes } from "selectors/appThemingSelectors";
import {
createMessage,
ERROR_MESSAGE_NAME_EMPTY,
APLHANUMERIC_HYPHEN_SLASH_SPACE_ERROR,
UNIQUE_NAME_ERROR,
} from "@appsmith/constants/messages";
import {
Button,
Input,
Text,
Modal,
ModalContent,
ModalHeader,
ModalFooter,
ModalBody,
} from "design-system";
interface SaveThemeModalProps {
isOpen: boolean;
onClose(): void;
}
function SaveThemeModal(props: SaveThemeModalProps) {
const { isOpen } = props;
const dispatch = useDispatch();
const [name, setName] = useState("");
const [inputValidator, setInputValidator] = useState({
isValid: false,
message: "",
isDirty: false,
});
const applicationId = useSelector(getCurrentApplicationId);
const themes = useSelector(getAppThemes);
/**
* dispatches action to save selected theme
*
*/
const onSubmit = (event: any) => {
event.preventDefault();
// if input validations fails, don't do anything
if (!inputValidator.isValid || inputValidator.isDirty === false) return;
AnalyticsUtil.logEvent("APP_THEMING_SAVE_THEME_SUCCESS", {
themeName: name,
});
dispatch(saveSelectedThemeAction({ applicationId, name }));
// close the modal after submit
onClose();
};
/**
* theme creation validator
*
* @param value
* @returns
*/
const createThemeValidator = (value: string) => {
let isValid = !!value;
let errorMessage = !isValid ? createMessage(ERROR_MESSAGE_NAME_EMPTY) : "";
if (
isValid &&
themes.find((theme) => value.toLowerCase() === theme.name.toLowerCase())
) {
isValid = false;
errorMessage = createMessage(UNIQUE_NAME_ERROR);
}
if (/[^a-zA-Z0-9\-\/\ ]/.test(value)) {
isValid = false;
errorMessage = createMessage(APLHANUMERIC_HYPHEN_SLASH_SPACE_ERROR);
}
return {
isValid: isValid,
message: errorMessage,
isDirty: true,
};
};
/**
* on input change
*
* @param value
*/
const onChangeName = (value: string) => {
const validator = createThemeValidator(value);
setInputValidator(validator);
setName(value);
};
/**
* on close modal
*/
const onClose = () => {
// reset validations
setInputValidator({
isValid: false,
message: "",
isDirty: false,
});
props.onClose();
};
return (
<Modal
onOpenChange={(isOpen) => {
if (!isOpen) {
onClose();
}
}}
open={isOpen}
>
<ModalContent
id="save-theme-modal"
onInteractOutside={(e) => {
e.preventDefault();
}}
style={{ width: "640px" }}
>
<ModalHeader>Save theme</ModalHeader>
<ModalBody>
<div className="flex flex-col gap-2">
<Text kind="action-l">
You can save your custom themes to use across applications and use
them when you need.
</Text>
<form data-testid="save-theme-form" noValidate onSubmit={onSubmit}>
<Input
autoFocus
errorMessage={
!inputValidator.isValid ? inputValidator.message : undefined
}
isRequired
label="Your theme name"
name="name"
onChange={onChangeName}
placeholder="My theme"
size="md"
/>
</form>
</div>
</ModalBody>
<ModalFooter>
<div className="flex gap-3">
<Button kind="secondary" onClick={onClose} size="md">
Cancel
</Button>
<Button
isDisabled={!name}
onClick={onSubmit}
size="md"
type="submit"
>
Save theme
</Button>
</div>
</ModalFooter>
</ModalContent>
</Modal>
);
}
export default SaveThemeModal;

View File

@ -1,7 +1,7 @@
import styled, { createGlobalStyle } from "styled-components";
import { get, startCase } from "lodash";
import { useDispatch, useSelector } from "react-redux";
import React, { useCallback, useState } from "react";
import React, { useCallback } from "react";
import ThemeCard from "./ThemeCard";
import {
@ -15,7 +15,6 @@ import {
updateSelectedAppThemeAction,
} from "actions/appThemingActions";
import SettingSection from "./SettingSection";
import SaveThemeModal from "./SaveThemeModal";
import type { AppTheme } from "entities/AppTheming";
import AnalyticsUtil from "utils/AnalyticsUtil";
import ThemeFontControl from "./controls/ThemeFontControl";
@ -64,7 +63,6 @@ function ThemeEditor() {
const applicationId = useSelector(getCurrentApplicationId);
const selectedTheme = useSelector(getSelectedAppTheme);
const themingStack = useSelector(getAppThemingStack);
const [isSaveModalOpen, setSaveModalOpen] = useState(false);
/**
* customizes the current theme
@ -95,22 +93,6 @@ function ThemeEditor() {
);
}, [setAppThemingModeStackAction]);
/**
* open the save modal
*/
const onOpenSaveModal = useCallback(() => {
AnalyticsUtil.logEvent("APP_THEMING_SAVE_THEME_START");
setSaveModalOpen(true);
}, [setSaveModalOpen]);
/**
* on close save modal
*/
const onCloseSaveModal = useCallback(() => {
setSaveModalOpen(false);
}, [setSaveModalOpen]);
/**
* resets theme
*/
@ -136,9 +118,6 @@ function ThemeEditor() {
/>
</MenuTrigger>
<MenuContent align="end" className="t--save-theme-menu">
<MenuItem onClick={onOpenSaveModal} startIcon="save">
Save theme
</MenuItem>
<MenuItem onClick={onResetTheme} startIcon="arrow-go-back">
Reset widget styles
</MenuItem>
@ -270,7 +249,6 @@ function ThemeEditor() {
)}
</SettingSection>
</main>
<SaveThemeModal isOpen={isSaveModalOpen} onClose={onCloseSaveModal} />
<PopoverStyles />
</>
);

View File

@ -0,0 +1,36 @@
import { Tooltip } from "design-system";
import React, { useEffect, useState } from "react";
import { modText } from "utils/helpers";
import { useSelector } from "react-redux";
import { getWidgetSelectionBlock } from "selectors/ui";
import { retrieveCodeWidgetNavigationUsed } from "utils/storage";
const CodeModeTooltip = (props: { children: React.ReactElement }) => {
const isWidgetSelectionBlock = useSelector(getWidgetSelectionBlock);
const [shouldShow, setShouldShow] = useState<boolean>(false);
useEffect(() => {
retrieveCodeWidgetNavigationUsed()
.then((timesUsed) => {
if (timesUsed < 2) {
setShouldShow(true);
}
})
.catch(() => {
setShouldShow(true);
});
}, [isWidgetSelectionBlock]);
if (!isWidgetSelectionBlock) return props.children;
return (
<Tooltip
content={`💡 ${modText()} click a widget to navigate to UI mode.`}
isDisabled={!shouldShow}
placement={"bottom"}
showArrow={false}
trigger={"hover"}
>
{props.children}
</Tooltip>
);
};
export default CodeModeTooltip;

View File

@ -27,8 +27,17 @@ const StyledModalContent = styled(ModalContent)`
width: 640px;
`;
function ImportedApplicationSuccessModal() {
const importedAppSuccess = localStorage.getItem("importApplicationSuccess");
interface ImportSuccessModalProps {
title?: string;
description?: string;
}
function ImportSuccessModal(props: ImportSuccessModalProps) {
const {
description = createMessage(APPLICATION_IMPORT_SUCCESS_DESCRIPTION),
title = createMessage(APPLICATION_IMPORT_SUCCESS),
} = props;
const importedAppSuccess = localStorage.getItem("importSuccess");
// const isOpen = importedAppSuccess === "true";
const [isOpen, setIsOpen] = useState(importedAppSuccess === "true");
@ -40,7 +49,7 @@ function ImportedApplicationSuccessModal() {
const close = () => {
setIsOpen(false);
localStorage.setItem("importApplicationSuccess", "false");
localStorage.setItem("importSuccess", "false");
};
return (
@ -54,10 +63,8 @@ function ImportedApplicationSuccessModal() {
name="success"
size={"lg"}
/>
<Text kind="heading-m">
{createMessage(APPLICATION_IMPORT_SUCCESS)}
</Text>
<Text>{createMessage(APPLICATION_IMPORT_SUCCESS_DESCRIPTION)}</Text>
<Text kind="heading-m">{title}</Text>
<Text>{description}</Text>
</BodyContainer>
</ModalBody>
<ModalFooter>
@ -76,4 +83,4 @@ function ImportedApplicationSuccessModal() {
);
}
export default ImportedApplicationSuccessModal;
export default ImportSuccessModal;

View File

@ -19,9 +19,7 @@ import {
RECONNECT_DATASOURCE_SUCCESS_MESSAGE1,
RECONNECT_DATASOURCE_SUCCESS_MESSAGE2,
RECONNECT_MISSING_DATASOURCE_CREDENTIALS,
RECONNECT_MISSING_DATASOURCE_CREDENTIALS_DESCRIPTION,
SKIP_CONFIGURATION,
SKIP_TO_APPLICATION,
SKIP_TO_APPLICATION_TOOLTIP_DESCRIPTION,
} from "@appsmith/constants/messages";
import {
@ -51,7 +49,6 @@ import {
getOAuthAccessToken,
loadFilePickerAction,
} from "actions/datasourceActions";
import { builderURL } from "@appsmith/RouteBuilder";
import localStorage from "utils/localStorage";
import {
Modal,
@ -76,6 +73,9 @@ import {
import type { AppState } from "@appsmith/reducers";
import { getFetchedWorkspaces } from "@appsmith/selectors/workspaceSelectors";
import { getApplicationsOfWorkspace } from "@appsmith/selectors/selectedWorkspaceSelectors";
import useReconnectModalData from "@appsmith/pages/Editor/gitSync/useReconnectModalData";
import { resetImportData } from "@appsmith/actions/workspaceActions";
import history from "utils/history";
const Section = styled.div`
display: flex;
@ -289,7 +289,6 @@ function ReconnectDatasourceModal() {
>(queryDatasourceId);
const [pageId, setPageId] = useState<string | null>(queryPageId);
const [appId, setAppId] = useState<string | null>(queryAppId);
const [appURL, setAppURL] = useState("");
const [datasource, setDatasource] = useState<Datasource | null>(null);
const [isImport, setIsImport] = useState(queryIsImport);
const [isTesting, setIsTesting] = useState(false);
@ -312,6 +311,21 @@ function ReconnectDatasourceModal() {
return output;
};
/**
* The role of useReconnectModalData is to provide editorId (appId or packageId), parentEntityId (pageId or moduleId)
* and any differentiating elements when a app vs package is imported.
* Right now it takes the pageId and appId and returns editorId/parentEntityId to reduces the changes required to
* refactor this for packages. Ideally the hook should calculate everything and return the necessary values.
*/
const {
editorId,
editorType,
editorURL,
missingDsCredentialsDescription, // pageId or moduleId
parentEntityId, // appId or packageId from query params
skipMessage,
} = useReconnectModalData({ pageId, appId });
// when redirecting from oauth, processing the status
if (isImport) {
setIsImport(false);
@ -333,6 +347,7 @@ function ReconnectDatasourceModal() {
workspaceId: orgId,
datasourceName: dsName,
pluginName: plugins[datasource?.pluginId || ""]?.name,
editorType,
});
} else if (queryDatasourceId) {
dispatch(loadFilePickerAction());
@ -348,7 +363,7 @@ function ReconnectDatasourceModal() {
if (app) {
dispatch(
setWorkspaceIdForImport({
editorId: appId || "",
editorId: editorId || "",
workspaceId: app.workspaceId,
}),
);
@ -363,7 +378,7 @@ function ReconnectDatasourceModal() {
dispatch({
type: ReduxActionTypes.FETCH_UNCONFIGURED_DATASOURCE_LIST,
payload: {
applicationId: appId,
applicationId: editorId,
workspaceId: app.workspaceId,
},
});
@ -428,15 +443,20 @@ function ReconnectDatasourceModal() {
}
};
const clearImportData = () => {
dispatch(resetImportData());
};
const onClose = () => {
localStorage.setItem("importedAppPendingInfo", "null");
dispatch(setIsReconnectingDatasourcesModalOpen({ isOpen: false }));
dispatch(
setWorkspaceIdForImport({ editorId: appId || "", workspaceId: "" }),
setWorkspaceIdForImport({ editorId: editorId || "", workspaceId: "" }),
);
dispatch(setPageIdForImport(""));
dispatch(resetDatasourceConfigForImportFetchedFlag());
setSelectedDatasourceId("");
clearImportData();
};
const onSelectDatasource = useCallback((ds: Datasource) => {
@ -478,17 +498,6 @@ function ReconnectDatasourceModal() {
}
}, [importedApplication, queryIsImport]);
useEffect(() => {
if (pageId) {
// TODO: Update route params here
setAppURL(
builderURL({
pageId: pageId,
}),
);
}
}, [pageId]);
// checking of full configured
useEffect(() => {
if (isModalOpen && !isTesting) {
@ -524,14 +533,14 @@ function ReconnectDatasourceModal() {
}
// When datasources are present and pending datasources are 0,
// then only we want to update status as success
else if (appURL && pending.length === 0 && datasources.length > 0) {
else if (editorURL && pending.length === 0 && datasources.length > 0) {
// open application import successfule
localStorage.setItem("importApplicationSuccess", "true");
localStorage.setItem("importSuccess", "true");
localStorage.setItem("importedAppPendingInfo", "null");
window.open(appURL, "_self");
window.open(editorURL, "_self");
}
}
}, [datasources, appURL, isModalOpen, isTesting, queryIsImport]);
}, [datasources, editorURL, isModalOpen, isTesting, queryIsImport]);
const mappedDataSources = datasources.map((ds: Datasource) => {
return (
@ -551,6 +560,13 @@ function ReconnectDatasourceModal() {
const shouldShowDBForm =
isConfigFetched && !isLoading && !checkIfDatasourceIsConfigured(datasource);
const onSkipBtnClick = () => {
AnalyticsUtil.logEvent("RECONNECTING_SKIP_TO_APPLICATION_BUTTON_CLICK");
localStorage.setItem("importedAppPendingInfo", "null");
editorURL && history.push(editorURL);
onClose();
};
return (
<Modal open={isModalOpen}>
<ModalContentWrapper
@ -567,23 +583,19 @@ function ReconnectDatasourceModal() {
{createMessage(RECONNECT_MISSING_DATASOURCE_CREDENTIALS)}
</Title>
<Text>
{createMessage(
RECONNECT_MISSING_DATASOURCE_CREDENTIALS_DESCRIPTION,
)}
</Text>
<Text>{missingDsCredentialsDescription}</Text>
<ContentWrapper>
<ListContainer>{mappedDataSources}</ListContainer>
<DBFormWrapper>
{shouldShowDBForm && (
<DatasourceForm
applicationId={appId}
applicationId={editorId}
datasourceId={selectedDatasourceId}
fromImporting
// isInsideReconnectModal: indicates that the datasource form is rendering inside reconnect modal
isInsideReconnectModal
pageId={pageId}
pageId={parentEntityId}
/>
)}
{checkIfDatasourceIsConfigured(datasource) && SuccessMessages()}
@ -599,18 +611,12 @@ function ReconnectDatasourceModal() {
<Button
UNSAFE_width={"100px"}
className="t--skip-to-application-btn mt-5"
href={appURL}
kind="secondary"
onClick={() => {
AnalyticsUtil.logEvent(
"RECONNECTING_SKIP_TO_APPLICATION_BUTTON_CLICK",
);
localStorage.setItem("importedAppPendingInfo", "null");
}}
onClick={onSkipBtnClick}
renderAs="a"
size="md"
>
{createMessage(SKIP_TO_APPLICATION)}
{skipMessage}
</Button>
</SkipToAppWrapper>
</ContentWrapper>

View File

@ -57,6 +57,8 @@ export enum Kind {
JS_LIB = "JS_LIB",
THEME = "THEME",
SETTINGS = "SETTINGS",
PACKAGES = "PACKAGES",
MODULES = "MODULES",
}
interface GitStatusProps {
@ -125,6 +127,22 @@ const STATUS_MAP: GitStatusMap = {
iconName: "package",
hasValue: (status?.modifiedJSLibs || 0) > 0,
}),
[Kind.PACKAGES]: (status) => ({
message: `${status?.modifiedPackages || 0} ${
(status?.modifiedPackages || 0) <= 1 ? "package" : "packages"
} modified`,
iconName: "package",
hasValue: (status?.modifiedPackages || 0) > 0,
}),
[Kind.MODULES]: (status) => ({
message: `${status?.modifiedModules || 0} ${
(status?.modifiedModules || 0) <= 1
? "module configuration"
: "module configurations"
} modified`,
iconName: "package",
hasValue: (status?.modifiedModules || 0) > 0,
}),
};
function behindCommitMessage(status: Partial<GitStatusData>) {
@ -193,6 +211,8 @@ export function gitChangeListData(
Kind.JS_OBJECT,
Kind.DATA_SOURCE,
Kind.JS_LIB,
Kind.MODULES,
Kind.PACKAGES,
];
return changeKind
.map((type: Kind) => STATUS_MAP[type](status))

View File

@ -30,7 +30,7 @@ import { setupPage, updateCurrentPage } from "actions/pageActions";
import { getCurrentPageId } from "selectors/editorSelectors";
import { getSearchQuery } from "utils/helpers";
import RepoLimitExceededErrorModal from "./gitSync/RepoLimitExceededErrorModal";
import ImportedApplicationSuccessModal from "./gitSync/ImportedAppSuccessModal";
import ImportedApplicationSuccessModal from "./gitSync/ImportSuccessModal";
import { getIsBranchUpdated } from "../utils";
import { APP_MODE } from "entities/App";
import { GIT_BRANCH_QUERY_KEY } from "constants/routes";

View File

@ -1,7 +1,8 @@
import { debounce, random } from "lodash";
import { useEffect, useMemo, useState } from "react";
import React from "react";
import ReactDOM from "react-dom";
import { useEffect, useMemo, useState } from "react";
import { useLocation } from "react-router";
import { debounce, random } from "lodash";
import type {
WidgetCardsGroupedByTags,
WidgetTags,
@ -19,6 +20,15 @@ import { useSelector } from "react-redux";
import { getCurrentPageId } from "selectors/editorSelectors";
import type { WidgetCardProps } from "widgets/BaseWidget";
import type { ActionResponse } from "api/ActionAPI";
import { MODULE_TYPE } from "@appsmith/constants/ModuleConstants";
import {
ENTITY_ICON_SIZE,
EntityIcon,
JsFileIconV2,
dbQueryIcon,
} from "pages/Editor/Explorer/ExplorerIcons";
import { PluginType } from "entities/Action";
import { getAssetUrl } from "@appsmith/utils/airgapHelpers";
export const draggableElement = (
id: string,
@ -336,3 +346,38 @@ export const actionResponseDisplayDataFormats = (
responseDisplayFormat,
};
};
function resolveQueryModuleIcon(
iconLocation: string,
pluginType: string,
isLargeIcon: boolean,
) {
if (iconLocation)
return (
<EntityIcon
height={`${isLargeIcon ? ENTITY_ICON_SIZE * 2 : ENTITY_ICON_SIZE}px`}
width={`${isLargeIcon ? ENTITY_ICON_SIZE * 2 : ENTITY_ICON_SIZE}px`}
>
<img alt="entityIcon" src={getAssetUrl(iconLocation)} />
</EntityIcon>
);
else if (pluginType === PluginType.DB) return dbQueryIcon;
}
export function resolveIcon({
iconLocation,
isLargeIcon = false,
moduleType,
pluginType,
}: {
iconLocation: string;
pluginType: string;
moduleType: string;
isLargeIcon?: boolean;
}) {
if (moduleType === MODULE_TYPE.JS) {
return isLargeIcon ? JsFileIconV2(34, 34) : JsFileIconV2(16, 16);
} else {
return resolveQueryModuleIcon(iconLocation, pluginType, isLargeIcon);
}
}

View File

@ -111,12 +111,6 @@ const themeReducer = createImmerReducer(initialState, {
(theme) => theme.id !== action.payload.themeId,
);
},
[ReduxActionTypes.SAVE_APP_THEME_SUCCESS]: (
state: AppThemingState,
action: ReduxAction<AppTheme>,
) => {
state.themes.push(action.payload);
},
[ReduxActionTypes.UPDATE_BETA_CARD_SHOWN]: (
state: AppThemingState,
action: ReduxAction<boolean>,

View File

@ -22,6 +22,8 @@ const initialState: WidgetDragResizeState = {
isDistributingSpace: false,
},
isDraggingDisabled: false,
blockSelection: false,
altFocus: false,
};
export const widgetDraggingReducer = createImmerReducer(initialState, {
@ -100,11 +102,20 @@ export const widgetDraggingReducer = createImmerReducer(initialState, {
},
[ReduxActionTypes.FOCUS_WIDGET]: (
state: WidgetDragResizeState,
action: ReduxAction<{ widgetId?: string }>,
action: ReduxAction<{ widgetId?: string; alt?: boolean }>,
) => {
if (state.focusedWidget !== action.payload.widgetId) {
state.focusedWidget = action.payload.widgetId;
}
if (state.altFocus !== action.payload.alt) {
state.altFocus = !!action.payload.alt;
}
},
[ReduxActionTypes.ALT_FOCUS_WIDGET]: (
state: WidgetDragResizeState,
action: ReduxAction<boolean>,
) => {
state.altFocus = action.payload;
},
[ReduxActionTypes.SET_SELECTED_WIDGET_ANCESTRY]: (
state: WidgetDragResizeState,
@ -118,6 +129,12 @@ export const widgetDraggingReducer = createImmerReducer(initialState, {
) => {
state.entityExplorerAncestry = action.payload;
},
[ReduxActionTypes.SET_WIDGET_SELECTION_BLOCK]: (
state: WidgetDragResizeState,
action: ReduxAction<boolean>,
) => {
state.blockSelection = action.payload;
},
//space distribution redux
[AnvilReduxActionTypes.ANVIL_SPACE_DISTRIBUTION_START]: (
state: WidgetDragResizeState,
@ -166,6 +183,8 @@ export interface WidgetDragResizeState {
selectedWidgets: string[];
isAutoCanvasResizing: boolean;
isDraggingDisabled: boolean;
blockSelection: boolean;
altFocus: boolean;
}
export default widgetDraggingReducer;

View File

@ -670,6 +670,9 @@ export interface GitStatusData {
modifiedJSLibs: number;
discardDocUrl?: string;
migrationMessage?: string;
modifiedPackages?: number;
modifiedModules?: number;
modifiedModuleInstances?: number;
}
interface GitErrorPayloadType {

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