mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 08:58:26 +00:00
Merge pull request #8711 from ToolJet/appbuilder-1.5
Release v2.29.0 - App builder 1.5
This commit is contained in:
commit
f6ececf1ac
157 changed files with 24665 additions and 8590 deletions
2
.version
2
.version
|
|
@ -1 +1 @@
|
|||
2.28.4
|
||||
2.29.0
|
||||
|
|
|
|||
|
|
@ -82,6 +82,8 @@ module.exports = defineConfig({
|
|||
"cypress/e2e/editor/inspectorHappypath.cy.js",
|
||||
"cypress/e2e/editor/queries/runpyHappyPath.cy.js",
|
||||
"cypress/e2e/editor/queries/runjsHappyPath.cy.js",
|
||||
"cypress/e2e/editor/multipage/multipageHappypath.cy.js",
|
||||
"cypress/e2e/editor/queries/chainingOfQueries.cy.js",
|
||||
],
|
||||
numTestsKeptInMemory: 1,
|
||||
redirectionLimit: 7,
|
||||
|
|
|
|||
|
|
@ -133,23 +133,6 @@ Cypress.Commands.add(
|
|||
}
|
||||
);
|
||||
|
||||
// cy.apiLogin();
|
||||
// cy.apiCreateApp();
|
||||
// cy.apiCreateGDS(
|
||||
// "http://localhost:3000/api/v2/data_sources",
|
||||
// "aaaaaadish",
|
||||
// "postgresql",
|
||||
// [
|
||||
// { key: "host", value: "localhost" },
|
||||
// { key: "port", value: 5432 },
|
||||
// { key: "database", value: "" },
|
||||
// { key: "username", value: "dev@tooljet.io" },
|
||||
// { key: "password", value: "password", encrypted: true },
|
||||
// { key: "ssl_enabled", value: true, encrypted: false },
|
||||
// { key: "ssl_certificate", value: "none", encrypted: false },
|
||||
// ]
|
||||
// );
|
||||
|
||||
Cypress.Commands.add("apiCreateWorkspace", (workspaceName, workspaceSlug) => {
|
||||
cy.getCookie("tj_auth_token").then((cookie) => {
|
||||
cy.request(
|
||||
|
|
@ -212,6 +195,7 @@ Cypress.Commands.add("userInviteApi", (userName, userEmail) => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("addQueryApi", (queryName, query, dataQueryId) => {
|
||||
cy.getCookie("tj_auth_token").then((cookie) => {
|
||||
const headers = {
|
||||
|
|
@ -236,3 +220,53 @@ Cypress.Commands.add("addQueryApi", (queryName, query, dataQueryId) => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add(
|
||||
"apiAddQueryToApp",
|
||||
(queryName, options, dsName, dsKind) => {
|
||||
cy.getCookie("tj_auth_token", { log: false }).then((cookie) => {
|
||||
const authToken = `tj_auth_token=${cookie.value}`;
|
||||
const workspaceId = Cypress.env("workspaceId");
|
||||
const appId = Cypress.env("appId");
|
||||
|
||||
cy.request({
|
||||
method: "GET",
|
||||
url: `http://localhost:3000/api/apps/${appId}`,
|
||||
headers: {
|
||||
"Tj-Workspace-Id": workspaceId,
|
||||
Cookie: `${authToken}; app_id=${appId}`,
|
||||
},
|
||||
body: {},
|
||||
}).then((appResponse) => {
|
||||
const editingVersionId = appResponse.body.editing_version.id;
|
||||
Cypress.env("version-id", editingVersionId);
|
||||
|
||||
cy.request({
|
||||
method: "POST",
|
||||
url: "http://localhost:3000/api/data_queries",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Cookie: authToken,
|
||||
"tj-workspace-id": workspaceId,
|
||||
},
|
||||
body: {
|
||||
app_id: appId,
|
||||
app_version_id: editingVersionId,
|
||||
name: queryName,
|
||||
kind: dsKind,
|
||||
options: options,
|
||||
data_source_id: dsName != null ? Cypress.env(`${dsName}-id`) : null,
|
||||
plugin_id: null,
|
||||
},
|
||||
}).then((queryResponse) => {
|
||||
expect(queryResponse.status).to.equal(201);
|
||||
Cypress.log({
|
||||
name: "Created queery",
|
||||
displayName: "QUERY CREATED",
|
||||
message: `: ${queryName}: ${dsKind}`,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -115,9 +115,9 @@ Cypress.Commands.add(
|
|||
.last()
|
||||
.click()
|
||||
.type(createBackspaceText(text), { delay: 0 }),
|
||||
{
|
||||
delay: 0,
|
||||
};
|
||||
{
|
||||
delay: 0,
|
||||
};
|
||||
});
|
||||
if (!Array.isArray(value)) {
|
||||
cy.wrap(subject).last().type(value, {
|
||||
|
|
@ -193,9 +193,9 @@ Cypress.Commands.add(
|
|||
.invoke("text")
|
||||
.then((text) => {
|
||||
cy.wrap(subject).realType(createBackspaceText(text)),
|
||||
{
|
||||
delay: 0,
|
||||
};
|
||||
{
|
||||
delay: 0,
|
||||
};
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
@ -387,6 +387,23 @@ Cypress.Commands.add(
|
|||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add("releaseApp", () => {
|
||||
if (Cypress.env("environment") !== "Community") {
|
||||
cy.get(commonEeSelectors.promoteButton).click();
|
||||
cy.get(commonEeSelectors.promoteButton).eq(1).click();
|
||||
cy.waitForAppLoad();
|
||||
cy.wait(3000);
|
||||
cy.get(commonEeSelectors.promoteButton).click();
|
||||
cy.get(commonEeSelectors.promoteButton).eq(1).click();
|
||||
cy.waitForAppLoad();
|
||||
cy.wait(3000);
|
||||
}
|
||||
cy.get(commonSelectors.releaseButton).click();
|
||||
cy.get(commonSelectors.yesButton).click();
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, "Version v1 released");
|
||||
cy.wait(1000);
|
||||
});
|
||||
|
||||
Cypress.Commands.add("backToApps", () => {
|
||||
cy.get(commonSelectors.editorPageLogo).click();
|
||||
cy.get(commonSelectors.backToAppOption).click();
|
||||
|
|
@ -399,12 +416,16 @@ Cypress.Commands.add("removeAssignedApps", () => {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
Cypress.Commands.add('saveFromIntercept', (interceptAlias, property, envVariable) => {
|
||||
cy.get(interceptAlias).its('response.body').then((responseBody) => {
|
||||
Cypress.env(envVariable, responseBody[property]);
|
||||
});
|
||||
});
|
||||
Cypress.Commands.add(
|
||||
"saveFromIntercept",
|
||||
(interceptAlias, property, envVariable) => {
|
||||
cy.get(interceptAlias)
|
||||
.its("response.body")
|
||||
.then((responseBody) => {
|
||||
Cypress.env(envVariable, responseBody[property]);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add("verifyLabel", (labelName) => {
|
||||
cy.get(commonSelectors.label(`${labelName}`)).verifyVisibleElement(
|
||||
|
|
@ -413,8 +434,29 @@ Cypress.Commands.add("verifyLabel", (labelName) => {
|
|||
);
|
||||
});
|
||||
|
||||
Cypress.Commands.add(
|
||||
"visitSlug",
|
||||
({ actualUrl, currentUrl = "http://localhost:8082/error/unknown" }) => {
|
||||
cy.visit(actualUrl);
|
||||
cy.wait(3000);
|
||||
|
||||
cy.url().then((url) => {
|
||||
if (url === currentUrl) {
|
||||
cy.visit(actualUrl);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add(
|
||||
"verifyCssProperty",
|
||||
(selector, property, expectedValue) => {
|
||||
cy.get(selector).should("have.css", property).and("eq", expectedValue);
|
||||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add("skipWalkthrough", () => {
|
||||
cy.window({ log: false }).then((win) => {
|
||||
win.localStorage.setItem("walkthroughCompleted", "true");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ export const commonWidgetText = {
|
|||
accordionEvents: "Events",
|
||||
accordionGenaral: "General",
|
||||
accordionValidation: "Validation",
|
||||
accordionLayout: "Layout",
|
||||
accordionLayout: "Devices",
|
||||
accordionDevices: "Devices",
|
||||
|
||||
parameterCustomValidation: "Custom validation",
|
||||
|
|
@ -204,6 +204,7 @@ export const commonWidgetText = {
|
|||
parameterOptionvalues: "Option values",
|
||||
boxShadowColor: "Box shadow Color",
|
||||
boxShadowFxValue: "-5px 6px 5px 8px #ee121240",
|
||||
loadingState: "Loading state",
|
||||
|
||||
codeMirrorLabelTrue: "{{true}}",
|
||||
codeMirrorLabelFalse: "{{false}}",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import {
|
|||
} from "Support/utils/database";
|
||||
import { fake } from "Fixtures/fake";
|
||||
import { randomNumber } from "Support/utils/commonWidget";
|
||||
import { randomString } from "Support/utils/textInput";
|
||||
import { randomString } from "Support/utils/editor/textInput";
|
||||
|
||||
describe("Database Functionality", () => {
|
||||
const data = {};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
verifyMultipleComponentValuesFromInspector,
|
||||
verifyComponentValueFromInspector,
|
||||
} from "Support/utils/commonWidget";
|
||||
import { verifyNodeData, openNode, verifyValue } from "Support/utils/inspector";
|
||||
import { verifyNodeData, openNode, verifyValue, deleteComponentFromInspector } from "Support/utils/inspector";
|
||||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import { addNewPage } from "Support/utils/multipage";
|
||||
import {
|
||||
|
|
@ -12,8 +12,16 @@ import {
|
|||
addSupportCSAData,
|
||||
} from "Support/utils/events";
|
||||
import { multipageSelector } from "Selectors/multipage";
|
||||
import {
|
||||
navigateToCreateNewVersionModal
|
||||
} from "Support/utils/version";
|
||||
import { createNewVersion } from "Support/utils/exportImport";
|
||||
|
||||
|
||||
describe("Editor- Inspector", () => {
|
||||
let currentVersion = "";
|
||||
let newVersion = [];
|
||||
let versionFrom = "";
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.apiCreateApp(`${fake.companyName}-App`);
|
||||
|
|
@ -170,4 +178,17 @@ describe("Editor- Inspector", () => {
|
|||
cy.notVisible(commonWidgetSelector.draggableWidget("button1"));
|
||||
cy.apiDeleteApp();
|
||||
});
|
||||
it("should verify deletion of component from inspector", () => {
|
||||
cy.dragAndDropWidget("button", 500, 500);
|
||||
cy.get(commonWidgetSelector.sidebarinspector).click();
|
||||
deleteComponentFromInspector("button1");
|
||||
cy.verifyToastMessage(
|
||||
`[class=go3958317564]`,
|
||||
"Component deleted! (⌘ + Z to undo)"
|
||||
);
|
||||
navigateToCreateNewVersionModal((currentVersion = "v1"));
|
||||
createNewVersion((newVersion = ["v2"]), (versionFrom = "v1"));
|
||||
cy.notVisible(commonWidgetSelector.draggableWidget("button1"));
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import {
|
|||
modifyPageHandle,
|
||||
clearSearch,
|
||||
searchPage,
|
||||
disableOrEnablePage,
|
||||
} from "Support/utils/multipage";
|
||||
|
||||
describe("Multipage", () => {
|
||||
|
|
@ -285,4 +286,27 @@ describe("Multipage", () => {
|
|||
|
||||
cy.get(multipageSelector.homePageLabel).click();
|
||||
});
|
||||
it("should verify the disable/delete functions of multipage", () => {
|
||||
cy.get(multipageSelector.sidebarPageButton).click();
|
||||
cy.get(multipageSelector.pagesPinIcon).click();
|
||||
addNewPage("pageOne");
|
||||
addNewPage("pageTwo");
|
||||
addNewPage("pageThree");
|
||||
|
||||
detetePage("pageOne");
|
||||
disableOrEnablePage("pageTwo");
|
||||
cy.get(
|
||||
'[data-cy="pages-name-pagetwo"] > .color-slate09'
|
||||
).verifyVisibleElement("have.text", "Disabled");
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
cy.notVisible(`[data-cy="pages-name-pageone}"]`);
|
||||
cy.notVisible(`[data-cy="pages-name-pagetwo}"]`);
|
||||
cy.wait(1000);
|
||||
cy.url().should("contain", "/home?");
|
||||
cy.url().then((url) => {
|
||||
cy.visit(url.replace("home", "pagetwo"));
|
||||
});
|
||||
cy.wait(1000);
|
||||
cy.url().should("contain", "/home?");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
176
cypress-tests/cypress/e2e/editor/queries/chainingOfQueries.cy.js
Normal file
176
cypress-tests/cypress/e2e/editor/queries/chainingOfQueries.cy.js
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
import { fake } from "Fixtures/fake";
|
||||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import { openEditorSidebar } from "Support/utils/commonWidget";
|
||||
import { selectEvent } from "Support/utils/events";
|
||||
import { randomString } from "Support/utils/textInput";
|
||||
import { buttonText } from "Texts/button";
|
||||
|
||||
import { addSuccessNotification, chainQuery } from "Support/utils/queries";
|
||||
|
||||
import { resizeQueryPanel } from "Support/utils/dataSource";
|
||||
|
||||
describe("Chaining of queries", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.apiCreateApp(`${fake.companyName}-App`);
|
||||
cy.openApp();
|
||||
cy.viewport(1800, 1800);
|
||||
cy.dragAndDropWidget("Button");
|
||||
resizeQueryPanel("80");
|
||||
});
|
||||
|
||||
it("should verify the chainig of runjs, restapi, runpy, tooljetdb and postgres", () => {
|
||||
const data = {};
|
||||
let dsName = fake.companyName;
|
||||
data.customText = randomString(12);
|
||||
cy.apiAddQueryToApp(
|
||||
"runjs",
|
||||
{ code: "return true", hasParamSupport: true, parameters: [] },
|
||||
null,
|
||||
"runjs"
|
||||
);
|
||||
cy.apiAddQueryToApp(
|
||||
"runpy",
|
||||
{ code: "True", hasParamSupport: true, parameters: [] },
|
||||
null,
|
||||
"runpy"
|
||||
);
|
||||
cy.apiAddQueryToApp(
|
||||
"restapi",
|
||||
{
|
||||
method: "get",
|
||||
url: "https://gorest.co.in/public/v2/users",
|
||||
url_params: [["", ""]],
|
||||
},
|
||||
null,
|
||||
"restapi"
|
||||
);
|
||||
cy.apiAddQueryToApp(
|
||||
"tjdb",
|
||||
{
|
||||
operation: "",
|
||||
transformationLanguage: "javascript",
|
||||
enableTransformation: false,
|
||||
},
|
||||
null,
|
||||
"tooljetdb"
|
||||
);
|
||||
|
||||
cy.apiCreateGDS(
|
||||
"http://localhost:3000/api/v2/data_sources",
|
||||
`cypress-${dsName}-postgresql`,
|
||||
"postgresql",
|
||||
[
|
||||
{ key: "host", value: Cypress.env("pg_host") },
|
||||
{ key: "port", value: 5432 },
|
||||
{ key: "database", value: Cypress.env("pg_user") },
|
||||
{ key: "username", value: Cypress.env("pg_user") },
|
||||
{ key: "password", value: Cypress.env("pg_password"), encrypted: true },
|
||||
{ key: "ssl_enabled", value: false, encrypted: false },
|
||||
{ key: "ssl_certificate", value: "none", encrypted: false },
|
||||
]
|
||||
);
|
||||
cy.apiAddQueryToApp(
|
||||
"psql",
|
||||
{
|
||||
mode: "sql",
|
||||
transformationLanguage: "javascript",
|
||||
enableTransformation: false,
|
||||
query: `SELECT * FROM server_side_pagination`,
|
||||
},
|
||||
`cypress-${dsName}-postgresql`,
|
||||
"postgresql"
|
||||
);
|
||||
cy.reload();
|
||||
resizeQueryPanel("80");
|
||||
chainQuery("psql", "runjs");
|
||||
addSuccessNotification("psql");
|
||||
chainQuery("runjs", "runpy");
|
||||
addSuccessNotification("runjs");
|
||||
chainQuery("runpy", "restapi");
|
||||
addSuccessNotification("runpy");
|
||||
chainQuery("restapi", "tjdb");
|
||||
addSuccessNotification("restapi");
|
||||
|
||||
openEditorSidebar(buttonText.defaultWidgetName);
|
||||
selectEvent("On Click", "Run Query", 1, `[data-cy="add-event-handler"]`, 1);
|
||||
cy.wait(500);
|
||||
cy.get('[data-cy="query-selection-field"]')
|
||||
.click()
|
||||
.find("input")
|
||||
.type(`{selectAll}{backspace}psql{enter}`);
|
||||
cy.forceClickOnCanvas();
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button1")).click();
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, "psql");
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, "runjs");
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, "runpy");
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, "restapi");
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, "Invalid operation");
|
||||
});
|
||||
|
||||
it("should verify query duplication", () => {
|
||||
const data = {};
|
||||
let dsName = fake.companyName;
|
||||
data.customText = randomString(12);
|
||||
cy.apiAddQueryToApp(
|
||||
"runjs",
|
||||
{ code: "return true", hasParamSupport: true, parameters: [] },
|
||||
null,
|
||||
"runjs"
|
||||
);
|
||||
cy.apiAddQueryToApp(
|
||||
"runpy",
|
||||
{ code: "True", hasParamSupport: true, parameters: [] },
|
||||
null,
|
||||
"runpy"
|
||||
);
|
||||
|
||||
cy.reload();
|
||||
resizeQueryPanel("80");
|
||||
addSuccessNotification("runpy");
|
||||
chainQuery("runjs", "runpy");
|
||||
addSuccessNotification("runjs");
|
||||
|
||||
openEditorSidebar(buttonText.defaultWidgetName);
|
||||
selectEvent("On Click", "Run Query", 1, `[data-cy="add-event-handler"]`, 1);
|
||||
cy.wait(500);
|
||||
cy.get('[data-cy="query-selection-field"]')
|
||||
.click()
|
||||
.find("input")
|
||||
.type(`{selectAll}{backspace}runjs{enter}`);
|
||||
cy.forceClickOnCanvas();
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button1")).click();
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, "runjs");
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, "runpy");
|
||||
cy.get('[data-cy="list-query-runjs"]')
|
||||
.trigger("mouseover")
|
||||
.parent()
|
||||
.parent()
|
||||
.find('[data-cy="copy-icon"]')
|
||||
.eq(0)
|
||||
.invoke("show")
|
||||
.click({ force: true });
|
||||
cy.get('[data-cy="list-query-runjs_copy"]').verifyVisibleElement(
|
||||
"have.text",
|
||||
"runjs_copy "
|
||||
);
|
||||
cy.get('[data-cy="notification-on-success-toggle-switch"]').should(
|
||||
"have.value",
|
||||
"on"
|
||||
);
|
||||
cy.get('[data-cy="success-message-input-field"]').should(
|
||||
"contain.text",
|
||||
"runjs"
|
||||
);
|
||||
cy.get(".query-definition-pane-wrapper").within(() => {
|
||||
cy.get('[data-cy="event-handler-card"]').eq(0).click();
|
||||
cy.wait(500);
|
||||
});
|
||||
cy.get(
|
||||
`[data-cy="action-selection"] > .select-search > .react-select__control > .react-select__value-container > `
|
||||
).should("have.text", "Run Query");
|
||||
cy.get('[data-cy="query-selection-field"]').should("have.text", "runpy");
|
||||
});
|
||||
});
|
||||
|
|
@ -72,7 +72,7 @@ describe("RunJS", () => {
|
|||
deleteDownloadsFolder();
|
||||
});
|
||||
|
||||
it.only("should verify basic runjs", () => {
|
||||
it("should verify basic runjs", () => {
|
||||
const data = {};
|
||||
data.customText = randomString(12);
|
||||
|
||||
|
|
|
|||
65
cypress-tests/cypress/e2e/editor/widget/appTitle.cy.js
Normal file
65
cypress-tests/cypress/e2e/editor/widget/appTitle.cy.js
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import { fake } from "Fixtures/fake";
|
||||
import { textInputText } from "Texts/textInput";
|
||||
import { commonWidgetText, widgetValue, customValidation } from "Texts/common";
|
||||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import { buttonText } from "Texts/button";
|
||||
import {
|
||||
verifyControlComponentAction,
|
||||
randomString,
|
||||
} from "Support/utils/textInput";
|
||||
import {
|
||||
openAccordion,
|
||||
verifyAndModifyParameter,
|
||||
openEditorSidebar,
|
||||
verifyAndModifyToggleFx,
|
||||
addDefaultEventHandler,
|
||||
verifyComponentValueFromInspector,
|
||||
selectColourFromColourPicker,
|
||||
verifyBoxShadowCss,
|
||||
verifyLayout,
|
||||
verifyTooltip,
|
||||
editAndVerifyWidgetName,
|
||||
verifyPropertiesGeneralAccordion,
|
||||
verifyStylesGeneralAccordion,
|
||||
randomNumber,
|
||||
closeAccordions,
|
||||
} from "Support/utils/commonWidget";
|
||||
import {
|
||||
selectCSA,
|
||||
selectEvent,
|
||||
addSupportCSAData,
|
||||
} from "Support/utils/events";
|
||||
|
||||
describe("Editor title", () => {
|
||||
const data = {};
|
||||
beforeEach(() => {
|
||||
data.appName = fake.companyName;
|
||||
cy.apiLogin();
|
||||
cy.apiCreateApp(data.appName);
|
||||
cy.visit("/");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.apiDeleteApp();
|
||||
});
|
||||
it("should verify titles", () => {
|
||||
cy.url().should("include", "/my-workspace");
|
||||
cy.title().should("eq", "ToolJet - Dashboard");
|
||||
cy.log(data.appName);
|
||||
|
||||
cy.openApp();
|
||||
cy.url().should("include", Cypress.env("appId"));
|
||||
cy.title().should("eq", `${data.appName} - Tooljet`);
|
||||
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
|
||||
cy.url().should("include", `/applications/${Cypress.env("appId")}`);
|
||||
cy.title().should("eq", `${data.appName}`);
|
||||
cy.go("back");
|
||||
cy.releaseApp();
|
||||
cy.url().then((url) => cy.visit(`/applications/${url.split("/").pop()}`));
|
||||
|
||||
cy.url().should("include", `/applications/${Cypress.env("appId")}`);
|
||||
cy.title().should("eq", `${data.appName}`);
|
||||
});
|
||||
});
|
||||
|
|
@ -234,7 +234,7 @@ describe("Editor- Test Button widget", () => {
|
|||
).should("have.attr", "disabled");
|
||||
|
||||
cy.get(commonWidgetSelector.parameterTogglebutton("Disable")).click();
|
||||
|
||||
cy.get('[data-cy="border-radius-input-field"]').realHover();
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterFxButton(
|
||||
commonWidgetText.parameterBorderRadius
|
||||
|
|
@ -443,4 +443,27 @@ describe("Editor- Test Button widget", () => {
|
|||
).should("not.be.visible");
|
||||
cy.apiDeleteApp();
|
||||
});
|
||||
it("Should verify deletion of button component from right side panel", () => {
|
||||
cy.get(".col-2").click();
|
||||
cy.get(".list-item-popover-body > :nth-child(3)").click();
|
||||
cy.get('[data-cy="yes-button"]').click();
|
||||
cy.verifyToastMessage(
|
||||
`[class=go3958317564]`,
|
||||
"Component deleted! (⌘ + Z to undo)"
|
||||
);
|
||||
cy.notVisible(commonWidgetSelector.draggableWidget("button1"));
|
||||
cy.reload();
|
||||
cy.notVisible(commonWidgetSelector.draggableWidget("button1"));
|
||||
});
|
||||
it("Should delete button via keyboard action", () => {
|
||||
cy.realPress("Backspace");
|
||||
cy.get('[data-cy="yes-button"]').click();
|
||||
cy.verifyToastMessage(
|
||||
`[class=go3958317564]`,
|
||||
"Component deleted! (⌘ + Z to undo)"
|
||||
);
|
||||
cy.notVisible(commonWidgetSelector.draggableWidget("button1"));
|
||||
cy.reload();
|
||||
cy.notVisible(commonWidgetSelector.draggableWidget("button1"));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
import { fake } from "Fixtures/fake";
|
||||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import { buttonText } from "Texts/button";
|
||||
|
||||
import { addBasicData, verifyBasicData } from "Support/utils/button";
|
||||
|
||||
import { openEditorSidebar } from "Support/utils/commonWidget";
|
||||
|
||||
describe("Editor- component duplication", () => {
|
||||
const data = {};
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.apiCreateApp(`${fake.companyName}-App`);
|
||||
cy.openApp();
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 500, 500);
|
||||
|
||||
data.appName = `${fake.companyName}-App`;
|
||||
data.alertMessage = fake.randomSentence;
|
||||
data.widgetName = fake.widgetName;
|
||||
data.customMessage = fake.randomSentence;
|
||||
data.backgroundColor = fake.randomRgba;
|
||||
data.textColor = fake.randomRgba;
|
||||
data.loaderColor = fake.randomRgba;
|
||||
data.boxShadowColor = fake.randomRgba;
|
||||
data.boxShadowParam = fake.boxShadowParam;
|
||||
data.tooltipText = fake.randomSentence;
|
||||
});
|
||||
|
||||
it("should verify duplication using copy and paste", () => {
|
||||
addBasicData(data);
|
||||
cy.forceClickOnCanvas();
|
||||
openEditorSidebar("button1");
|
||||
cy.realPress(["Control", "c"]);
|
||||
cy.moveComponent("button1", 200, 200);
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
"Component copied successfully"
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
|
||||
cy.get('[data-cy="real-canvas"]').realPress(["Control", "v"]);
|
||||
|
||||
verifyBasicData("button2", data);
|
||||
|
||||
cy.reload();
|
||||
cy.wait(2500);
|
||||
verifyBasicData("button2", data);
|
||||
});
|
||||
it("should verify componen paste to container", () => {
|
||||
addBasicData(data);
|
||||
cy.forceClickOnCanvas();
|
||||
openEditorSidebar("button1");
|
||||
cy.realPress(["Control", "c"]);
|
||||
cy.moveComponent("button1", 200, 90);
|
||||
cy.dragAndDropWidget("Container", 300, 200);
|
||||
cy.resizeWidget("container1", 800, 500);
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
"Component copied successfully",
|
||||
false
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
openEditorSidebar("container1");
|
||||
cy.get(`${commonWidgetSelector.draggableWidget("container1")}>`)
|
||||
.click({ force: true })
|
||||
.within(() => {
|
||||
cy.realPress(["Control", "v"]);
|
||||
cy.wait(1000);
|
||||
cy.get(commonWidgetSelector.draggableWidget("button2")).should(
|
||||
"be.visible"
|
||||
);
|
||||
});
|
||||
verifyBasicData("button2", data);
|
||||
});
|
||||
|
||||
it("should verify duplication using right side panel", () => {
|
||||
addBasicData(data);
|
||||
cy.forceClickOnCanvas();
|
||||
openEditorSidebar("button1");
|
||||
cy.get('[data-cy="component-inspector-options"]>').click();
|
||||
cy.get('[data-cy="component-inspector-duplicate-button"]').click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
"Component cloned succesfully"
|
||||
);
|
||||
cy.moveComponent("button1", 200, 200);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.wait(1000);
|
||||
verifyBasicData("button2", data);
|
||||
});
|
||||
|
||||
it("should verify duplication using keyboard", () => {
|
||||
addBasicData(data);
|
||||
cy.forceClickOnCanvas();
|
||||
openEditorSidebar("button1");
|
||||
cy.realPress(["Control", "d"]);
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
"Component cloned succesfully"
|
||||
);
|
||||
cy.moveComponent("button1", 200, 200);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.wait(1000);
|
||||
verifyBasicData("button2", data);
|
||||
|
||||
cy.reload();
|
||||
cy.wait(2500);
|
||||
verifyBasicData("button2", data);
|
||||
});
|
||||
});
|
||||
|
|
@ -356,14 +356,14 @@ describe("Basic components", () => {
|
|||
verifyComponent("container1");
|
||||
|
||||
openEditorSidebar("container1");
|
||||
editAndVerifyWidgetName("container2", ["Layout"]);
|
||||
editAndVerifyWidgetName("container2", ["Devices"]);
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.resizeWidget("container2", 650, 400, false);
|
||||
cy.waitForAutoSave();
|
||||
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
verifyComponent("container2", ["Layout"]);
|
||||
verifyComponent("container2", ["Devices"]);
|
||||
|
||||
cy.go("back");
|
||||
resizeQueryPanel(0);
|
||||
|
|
|
|||
|
|
@ -1,300 +1,369 @@
|
|||
import { fake } from "Fixtures/fake";
|
||||
import { commonWidgetText } from "Texts/common";
|
||||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import {
|
||||
addDefaultEventHandler,
|
||||
checkPaddingOfContainer,
|
||||
closeAccordions,
|
||||
editAndVerifyWidgetName,
|
||||
openAccordion,
|
||||
openEditorSidebar,
|
||||
randomNumber,
|
||||
selectColourFromColourPicker,
|
||||
verifyAndModifyParameter,
|
||||
verifyBoxShadowCss,
|
||||
verifyComponentValueFromInspector,
|
||||
verifyContainerElements,
|
||||
verifyLayout,
|
||||
verifyStylesGeneralAccordion,
|
||||
verifyTooltip,
|
||||
verifyWidgetColorCss,
|
||||
} from "Support/utils/commonWidget";
|
||||
import { numberInputText } from "Texts/numberInput";
|
||||
|
||||
import {
|
||||
openAccordion,
|
||||
verifyAndModifyParameter,
|
||||
openEditorSidebar,
|
||||
verifyAndModifyToggleFx,
|
||||
verifyComponentValueFromInspector,
|
||||
verifyBoxShadowCss,
|
||||
verifyLayout,
|
||||
verifyTooltip,
|
||||
editAndVerifyWidgetName,
|
||||
addTextWidgetToVerifyValue,
|
||||
verifyPropertiesGeneralAccordion,
|
||||
verifyStylesGeneralAccordion,
|
||||
randomNumber,
|
||||
fillBoxShadowParams,
|
||||
selectColourFromColourPicker,
|
||||
} from "Support/utils/commonWidget";
|
||||
addAllInputFieldColors,
|
||||
addAndVerifyAdditionalActions,
|
||||
addCustomWidthOfLabel,
|
||||
addValidations,
|
||||
verifyAlignment,
|
||||
verifyCustomWidthOfLabel,
|
||||
verifyInputFieldColors,
|
||||
verifyLabelStyleElements,
|
||||
} from "Support/utils/editor/inputFieldUtils";
|
||||
import { addCSA, verifyCSA } from "Support/utils/editor/passwordNumberInput.js";
|
||||
import { commonWidgetText } from "Texts/common";
|
||||
|
||||
describe("Number Input", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.apiCreateApp();
|
||||
cy.apiCreateApp(`${fake.companyName}-numberinput-App`);
|
||||
cy.openApp();
|
||||
cy.dragAndDropWidget("Number Input");
|
||||
cy.dragAndDropWidget("Number Input", 500, 500);
|
||||
});
|
||||
afterEach(() => {
|
||||
cy.apiDeleteApp();
|
||||
});
|
||||
|
||||
it("should verify the properties of the number input widget", () => {
|
||||
itß("should verify the properties of the number input widget", () => {
|
||||
const data = {};
|
||||
data.appName = `${fake.companyName}-App`;
|
||||
data.widgetName = fake.widgetName;
|
||||
data.tooltipText = fake.randomSentence;
|
||||
data.randomNumber = `${randomNumber(10, 99)}`;
|
||||
data.minimumvalue = `${randomNumber(5, 10)}`;
|
||||
data.maximumValue = `${randomNumber(90, 99)}`;
|
||||
data.minimumLength = randomNumber(2, 4);
|
||||
data.maximumLength = randomNumber(8, 10);
|
||||
data.customText = randomNumber(12);
|
||||
data.customNumber = randomNumber(12);
|
||||
|
||||
openEditorSidebar(numberInputText.defaultWidgetName);
|
||||
editAndVerifyWidgetName(data.widgetName);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName)
|
||||
).verifyVisibleElement("have.value", "99");
|
||||
|
||||
openEditorSidebar(data.widgetName);
|
||||
openAccordion(commonWidgetText.accordionProperties, [
|
||||
closeAccordions([
|
||||
"Data",
|
||||
"Validation",
|
||||
"Additional Actions",
|
||||
"Devices",
|
||||
"Events",
|
||||
]);
|
||||
editAndVerifyWidgetName(data.widgetName, [
|
||||
"Data",
|
||||
"Validation",
|
||||
"Additional Actions",
|
||||
"Devices",
|
||||
"Events",
|
||||
]);
|
||||
openAccordion("Data", [
|
||||
"Data",
|
||||
"Validation",
|
||||
"Additional Actions",
|
||||
"Devices",
|
||||
"Events",
|
||||
"Properties",
|
||||
"Layout",
|
||||
]);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelDefaultValue,
|
||||
data.randomNumber
|
||||
data.customNumber
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName)
|
||||
).verifyVisibleElement("have.value", data.randomNumber);
|
||||
).verifyVisibleElement("have.value", data.customNumber);
|
||||
|
||||
verifyComponentValueFromInspector(data.widgetName, data.randomNumber);
|
||||
verifyComponentValueFromInspector(data.widgetName, data.customNumber);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
|
||||
data.customText = fake.randomSentence;
|
||||
openEditorSidebar(data.widgetName);
|
||||
openAccordion(commonWidgetText.accordionProperties, [
|
||||
openAccordion("Data", [
|
||||
"Data",
|
||||
"Validation",
|
||||
"Additional Actions",
|
||||
"Devices",
|
||||
"Events",
|
||||
"Properties",
|
||||
"Layout",
|
||||
]);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelMinimumValue,
|
||||
data.minimumvalue
|
||||
);
|
||||
cy.get(commonWidgetSelector.buttonCloseEditorSideBar).click();
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
randomNumber(1, 4)
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName)
|
||||
).verifyVisibleElement("have.value", `${data.minimumvalue}`);
|
||||
|
||||
openEditorSidebar(data.widgetName);
|
||||
openAccordion(commonWidgetText.accordionProperties, [
|
||||
"Events",
|
||||
"Properties",
|
||||
"Layout",
|
||||
]);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelMaximumValue,
|
||||
`${data.maximumValue}`
|
||||
);
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
randomNumber(100, 110)
|
||||
);
|
||||
cy.forceClickOnCanvas;
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName)
|
||||
).verifyVisibleElement("have.value", `${data.maximumValue}`);
|
||||
|
||||
openEditorSidebar(data.widgetName);
|
||||
openAccordion(commonWidgetText.accordionProperties, [
|
||||
"Events",
|
||||
"Properties",
|
||||
"Layout",
|
||||
]);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelPlaceHolder,
|
||||
data.randomNumber
|
||||
data.customText
|
||||
);
|
||||
cy.get(commonWidgetSelector.buttonCloseEditorSideBar).click();
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName))
|
||||
.invoke("attr", "placeholder")
|
||||
.should("contain", data.randomNumber);
|
||||
.should("contain", data.customText);
|
||||
|
||||
verifyPropertiesGeneralAccordion(data.widgetName, data.tooltipText);
|
||||
openEditorSidebar(data.widgetName);
|
||||
openAccordion(commonWidgetText.accordionEvents, ["Validation", "Devices"]);
|
||||
addDefaultEventHandler(data.customText);
|
||||
cy.get(commonWidgetSelector.eventSelection).type("On Enter Pressed{Enter}");
|
||||
|
||||
// verifyLayout(data.widgetName);
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
`${data.customNumber}{Enter}`
|
||||
);
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, data.customText);
|
||||
cy.forceClickOnCanvas();
|
||||
|
||||
// cy.get(commonWidgetSelector.changeLayoutButton).click();
|
||||
// cy.get(
|
||||
// commonWidgetSelector.parameterTogglebutton(
|
||||
// commonWidgetText.parameterShowOnDesktop
|
||||
// )
|
||||
// ).click();
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
|
||||
addValidations(data.widgetName, data, "Min value", "Max value");
|
||||
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
data.customNumber
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
).verifyVisibleElement("have.text", commonWidgetText.regexValidationError);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterInputField(commonWidgetText.labelRegex)
|
||||
)
|
||||
.click()
|
||||
.clearCodeMirror();
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName))
|
||||
.clear()
|
||||
.type("1");
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
).verifyVisibleElement(
|
||||
"have.text",
|
||||
`Minimum value is ${data.minimumLength}`
|
||||
);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterInputField("Min value")
|
||||
).clearAndTypeOnCodeMirror("0");
|
||||
cy.forceClickOnCanvas();
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
"99"
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
).verifyVisibleElement(
|
||||
"have.text",
|
||||
`Maximum value is ${data.maximumLength}`
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
).verifyVisibleElement("have.text", data.customText);
|
||||
cy.forceClickOnCanvas();
|
||||
openEditorSidebar(data.widgetName);
|
||||
cy.get(
|
||||
commonWidgetSelector.accordion(commonWidgetText.accordionValidation)
|
||||
).click();
|
||||
addAndVerifyAdditionalActions(data.widgetName, data.tooltipText);
|
||||
|
||||
openEditorSidebar(data.widgetName);
|
||||
cy.get(
|
||||
commonWidgetSelector.accordion(commonWidgetText.accordionValidation)
|
||||
).click();
|
||||
verifyLayout(data.widgetName, "Devices");
|
||||
|
||||
cy.get(commonWidgetSelector.changeLayoutToDesktopButton).click();
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterTogglebutton(
|
||||
commonWidgetText.parameterShowOnDesktop
|
||||
)
|
||||
).click();
|
||||
|
||||
openEditorSidebar(data.widgetName);
|
||||
openAccordion("Validation", [
|
||||
"Data",
|
||||
"Validation",
|
||||
"Additional Actions",
|
||||
"Devices",
|
||||
"Events",
|
||||
]);
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterInputField("Min value")
|
||||
).clearAndTypeOnCodeMirror("2");
|
||||
cy.forceClickOnCanvas();
|
||||
cy.waitForAutoSave();
|
||||
openEditorSidebar(data.widgetName);
|
||||
|
||||
cy.get(commonWidgetSelector.widgetDocumentationLink).should(
|
||||
"have.text",
|
||||
numberInputText.numberInputDocumentationLink
|
||||
"Read documentation for NumberInput"
|
||||
);
|
||||
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName))
|
||||
.invoke("attr", "placeholder")
|
||||
.should("contain", data.customText);
|
||||
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
`${data.customText}{Enter}`
|
||||
);
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, data.customText);
|
||||
cy.forceClickOnCanvas();
|
||||
|
||||
// cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
|
||||
// cy.clearAndType(
|
||||
// commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
// data.customText
|
||||
// );
|
||||
// cy.forceClickOnCanvas();
|
||||
// cy.get(
|
||||
// commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
// ).verifyVisibleElement("have.text", commonWidgetText.regexValidationError);
|
||||
|
||||
// cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.clearAndType(commonWidgetSelector.draggableWidget(data.widgetName), "1");
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
).verifyVisibleElement(
|
||||
"have.text",
|
||||
`Minimum value is ${data.minimumLength}`
|
||||
);
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
"13"
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
).verifyVisibleElement(
|
||||
"have.text",
|
||||
`Maximum value is ${data.maximumLength}`
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
verifyTooltip(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
data.tooltipText
|
||||
);
|
||||
});
|
||||
|
||||
it("should verify the styles of the number input widget", () => {
|
||||
const data = {};
|
||||
data.appName = `${fake.companyName}-App`;
|
||||
data.colourHex = fake.randomRgbaHex;
|
||||
data.boxShadowColor = fake.randomRgba;
|
||||
data.boxShadowParam = fake.boxShadowParam;
|
||||
data.bgColor = fake.randomRgba;
|
||||
data.borderColor = fake.randomRgba;
|
||||
data.textColor = fake.randomRgba;
|
||||
data.errorTextColor = fake.randomRgba;
|
||||
data.iconColor = fake.randomRgba;
|
||||
data.labelColor = fake.randomRgba;
|
||||
data.widgetName = numberInputText.defaultWidgetName;
|
||||
|
||||
openEditorSidebar(numberInputText.defaultWidgetName);
|
||||
cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click();
|
||||
addAllInputFieldColors(data);
|
||||
|
||||
verifyAndModifyToggleFx(
|
||||
commonWidgetText.parameterVisibility,
|
||||
commonWidgetText.codeMirrorLabelTrue
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(numberInputText.defaultWidgetName)
|
||||
).should("not.be.visible");
|
||||
cy.clearAndType('[data-cy="border-radius-input"]', "20");
|
||||
cy.get('[data-cy="icon-visibility-button"]').click();
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterTogglebutton(
|
||||
commonWidgetText.parameterVisibility
|
||||
)
|
||||
).click();
|
||||
|
||||
verifyAndModifyToggleFx(
|
||||
commonWidgetText.parameterDisable,
|
||||
commonWidgetText.codeMirrorLabelFalse
|
||||
);
|
||||
cy.waitForAutoSave();
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(numberInputText.defaultWidgetName)
|
||||
).should("have.attr", "disabled");
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterTogglebutton(
|
||||
commonWidgetText.parameterDisable
|
||||
)
|
||||
).click();
|
||||
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.parameterBorderRadius,
|
||||
commonWidgetText.borderRadiusInput
|
||||
);
|
||||
|
||||
cy.get(commonWidgetSelector.buttonCloseEditorSideBar).click();
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(numberInputText.defaultWidgetName)
|
||||
).should("have.css", "border-radius", "20px");
|
||||
|
||||
verifyInputFieldColors("numberinput1", data);
|
||||
|
||||
verifyStylesGeneralAccordion(
|
||||
numberInputText.defaultWidgetName,
|
||||
data.boxShadowParam,
|
||||
data.colourHex,
|
||||
data.boxShadowColor,
|
||||
3
|
||||
);
|
||||
});
|
||||
|
||||
it("should verify the app preview", () => {
|
||||
const data = {};
|
||||
data.appName = `${fake.companyName}-App`;
|
||||
data.tooltipText = fake.randomSentence;
|
||||
data.colourHex = fake.randomRgbaHex;
|
||||
data.boxShadowColor = fake.randomRgba;
|
||||
data.boxShadowParam = fake.boxShadowParam;
|
||||
data.randomNumber = randomNumber(10, 99);
|
||||
data.minimumvalue = randomNumber(5, 10);
|
||||
data.maximumValue = randomNumber(90, 99);
|
||||
|
||||
openEditorSidebar(numberInputText.defaultWidgetName);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelDefaultValue,
|
||||
data.randomNumber
|
||||
);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelMinimumValue,
|
||||
`${data.minimumvalue}`
|
||||
);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelMaximumValue,
|
||||
`${data.maximumValue}`
|
||||
);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelPlaceHolder,
|
||||
data.randomNumber
|
||||
);
|
||||
|
||||
verifyPropertiesGeneralAccordion(
|
||||
numberInputText.defaultWidgetName,
|
||||
data.tooltipText
|
||||
4
|
||||
);
|
||||
|
||||
openEditorSidebar(numberInputText.defaultWidgetName);
|
||||
cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click();
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.parameterBorderRadius,
|
||||
commonWidgetText.borderRadiusInput
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
|
||||
openEditorSidebar(numberInputText.defaultWidgetName);
|
||||
cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click();
|
||||
openAccordion(commonWidgetText.accordionGenaral, [], 1);
|
||||
cy.get(commonWidgetSelector.boxShadowColorPicker).click();
|
||||
verifyContainerElements();
|
||||
checkPaddingOfContainer(numberInputText.defaultWidgetName, 1);
|
||||
cy.get('[data-cy="togglr-button-none"]').click();
|
||||
checkPaddingOfContainer(numberInputText.defaultWidgetName, 0);
|
||||
|
||||
fillBoxShadowParams(
|
||||
commonWidgetSelector.boxShadowDefaultParam,
|
||||
data.boxShadowParam
|
||||
);
|
||||
verifyLabelStyleElements();
|
||||
verifyAlignment(numberInputText.defaultWidgetName, "sideLeft");
|
||||
cy.get('[data-cy="togglr-button-top"]').click();
|
||||
verifyAlignment(numberInputText.defaultWidgetName, "topLeft");
|
||||
cy.get('[data-cy="togglr-button-right"]').click();
|
||||
verifyAlignment(numberInputText.defaultWidgetName, "topRight");
|
||||
cy.get('[data-cy="togglr-button-side"]').click();
|
||||
verifyAlignment(numberInputText.defaultWidgetName, "sideRight");
|
||||
cy.get('[data-cy="togglr-button-left"]').click();
|
||||
verifyAlignment(numberInputText.defaultWidgetName, "sideLeft");
|
||||
addCustomWidthOfLabel("50");
|
||||
verifyCustomWidthOfLabel(numberInputText.defaultWidgetName, "50");
|
||||
selectColourFromColourPicker(
|
||||
commonWidgetText.boxShadowColor,
|
||||
data.boxShadowColor,
|
||||
3
|
||||
"Text",
|
||||
data.labelColor,
|
||||
0,
|
||||
commonWidgetSelector.colourPickerParent,
|
||||
"0"
|
||||
);
|
||||
verifyWidgetColorCss(
|
||||
`[data-cy="label-${numberInputText.defaultWidgetName}"]>label`,
|
||||
"color",
|
||||
data.labelColor,
|
||||
true
|
||||
);
|
||||
addTextWidgetToVerifyValue("components.numberinput1.value");
|
||||
|
||||
cy.waitForAutoSave();
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(numberInputText.defaultWidgetName)
|
||||
).verifyVisibleElement("have.value", data.randomNumber);
|
||||
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(numberInputText.defaultWidgetName),
|
||||
randomNumber(1, 4)
|
||||
verifyWidgetColorCss(
|
||||
`[data-cy="label-${numberInputText.defaultWidgetName}"]>label`,
|
||||
"color",
|
||||
data.labelColor,
|
||||
true
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(numberInputText.defaultWidgetName)
|
||||
).verifyVisibleElement("have.value", `${data.minimumvalue}`);
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(numberInputText.defaultWidgetName),
|
||||
randomNumber(100, 110)
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(numberInputText.defaultWidgetName)
|
||||
).verifyVisibleElement("have.value", `${data.maximumValue}`);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(numberInputText.defaultWidgetName)
|
||||
)
|
||||
.invoke("attr", "placeholder")
|
||||
.should("contain", data.randomNumber);
|
||||
|
||||
verifyTooltip(
|
||||
commonWidgetSelector.draggableWidget(numberInputText.defaultWidgetName),
|
||||
data.tooltipText
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(commonWidgetText.text1)
|
||||
).verifyVisibleElement("have.text", data.maximumValue);
|
||||
verifyAlignment(numberInputText.defaultWidgetName, "sideLeft");
|
||||
verifyCustomWidthOfLabel(numberInputText.defaultWidgetName, "50");
|
||||
verifyInputFieldColors("numberinput1", data);
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(numberInputText.defaultWidgetName)
|
||||
).should("have.css", "border-radius", "20px");
|
||||
verifyBoxShadowCss(
|
||||
numberInputText.defaultWidgetName,
|
||||
data.boxShadowColor,
|
||||
data.boxShadowParam
|
||||
);
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(numberInputText.defaultWidgetName)
|
||||
).should("have.css", "border-radius", "20px");
|
||||
});
|
||||
|
||||
it.skip("should verify the app preview", () => {});
|
||||
|
||||
it("should verify CSA", () => {
|
||||
const data = {};
|
||||
data.widgetName = numberInputText.defaultWidgetName;
|
||||
data.customText = randomNumber(12);
|
||||
|
||||
addCSA(data);
|
||||
verifyCSA(data);
|
||||
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
verifyCSA(data);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,35 +1,47 @@
|
|||
import { fake } from "Fixtures/fake";
|
||||
import { commonWidgetText, customValidation } from "Texts/common";
|
||||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import { buttonText } from "Texts/button";
|
||||
import {
|
||||
openAccordion,
|
||||
verifyAndModifyParameter,
|
||||
openEditorSidebar,
|
||||
verifyAndModifyToggleFx,
|
||||
verifyComponentValueFromInspector,
|
||||
verifyBoxShadowCss,
|
||||
verifyLayout,
|
||||
verifyTooltip,
|
||||
editAndVerifyWidgetName,
|
||||
addTextWidgetToVerifyValue,
|
||||
verifyPropertiesGeneralAccordion,
|
||||
verifyStylesGeneralAccordion,
|
||||
randomNumber,
|
||||
fillBoxShadowParams,
|
||||
selectColourFromColourPicker,
|
||||
addDefaultEventHandler,
|
||||
checkPaddingOfContainer,
|
||||
closeAccordions,
|
||||
editAndVerifyWidgetName,
|
||||
openAccordion,
|
||||
openEditorSidebar,
|
||||
randomNumber,
|
||||
selectColourFromColourPicker,
|
||||
verifyAndModifyParameter,
|
||||
verifyBoxShadowCss,
|
||||
verifyComponentValueFromInspector,
|
||||
verifyContainerElements,
|
||||
verifyLayout,
|
||||
verifyStylesGeneralAccordion,
|
||||
verifyTooltip,
|
||||
verifyWidgetColorCss,
|
||||
} from "Support/utils/commonWidget";
|
||||
import {
|
||||
addAllInputFieldColors,
|
||||
addAndVerifyAdditionalActions,
|
||||
addCustomWidthOfLabel,
|
||||
addValidations,
|
||||
verifyAlignment,
|
||||
verifyCustomWidthOfLabel,
|
||||
verifyInputFieldColors,
|
||||
verifyLabelStyleElements,
|
||||
} from "Support/utils/editor/inputFieldUtils";
|
||||
import {
|
||||
addCSA,
|
||||
randomString,
|
||||
verifyCSA,
|
||||
} from "Support/utils/editor/passwordNumberInput.js";
|
||||
import { commonWidgetText } from "Texts/common";
|
||||
import { passwordInputText } from "Texts/passwordInput";
|
||||
import { randomString } from "Support/utils/textInput";
|
||||
|
||||
describe("Password Input", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.apiCreateApp();
|
||||
cy.apiCreateApp(`${fake.companyName}-Passwordinput-App`);
|
||||
cy.openApp();
|
||||
cy.modifyCanvasSize(1200, 780);
|
||||
cy.dragAndDropWidget("Password Input", 350, 300);
|
||||
cy.dragAndDropWidget("Password Input", 500, 500);
|
||||
});
|
||||
afterEach(() => {
|
||||
cy.apiDeleteApp();
|
||||
|
|
@ -37,52 +49,81 @@ describe("Password Input", () => {
|
|||
|
||||
it("should verify the properties of the password input widget", () => {
|
||||
const data = {};
|
||||
data.appName = `${fake.companyName}-App`;
|
||||
data.widgetName = fake.widgetName;
|
||||
data.customText = fake.firstName;
|
||||
data.tooltipText = fake.randomSentence;
|
||||
data.minimumLength = randomNumber(1, 4);
|
||||
data.maximumLength = randomNumber(8, 10);
|
||||
data.customText = randomString(12);
|
||||
|
||||
openEditorSidebar(passwordInputText.defaultWidgetName);
|
||||
closeAccordions(["Events", "Validation", "Properties", "Layout"]);
|
||||
editAndVerifyWidgetName(data.widgetName);
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName))
|
||||
.invoke("attr", "placeholder")
|
||||
.should("contain", "password");
|
||||
|
||||
openEditorSidebar(data.widgetName);
|
||||
openAccordion(commonWidgetText.accordionProperties, [
|
||||
"Events",
|
||||
closeAccordions([
|
||||
"Data",
|
||||
"Validation",
|
||||
"Properties",
|
||||
"Layout",
|
||||
"Additional Actions",
|
||||
"Devices",
|
||||
"Events",
|
||||
]);
|
||||
editAndVerifyWidgetName(data.widgetName, [
|
||||
"Data",
|
||||
"Validation",
|
||||
"Additional Actions",
|
||||
"Devices",
|
||||
"Events",
|
||||
]);
|
||||
openAccordion("Data", [
|
||||
"Data",
|
||||
"Validation",
|
||||
"Additional Actions",
|
||||
"Devices",
|
||||
"Events",
|
||||
]);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelDefaultValue,
|
||||
data.customText
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName)
|
||||
).verifyVisibleElement("have.value", data.customText);
|
||||
|
||||
verifyAndModifyParameter("Placeholder", data.customText);
|
||||
verifyComponentValueFromInspector(data.widgetName, data.customText);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
|
||||
data.customText = fake.randomSentence;
|
||||
openEditorSidebar(data.widgetName);
|
||||
openAccordion("Data", [
|
||||
"Data",
|
||||
"Validation",
|
||||
"Additional Actions",
|
||||
"Devices",
|
||||
"Events",
|
||||
]);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelPlaceHolder,
|
||||
data.customText
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName))
|
||||
.invoke("attr", "placeholder")
|
||||
.should("contain", data.customText);
|
||||
|
||||
openEditorSidebar(data.widgetName);
|
||||
openAccordion(commonWidgetText.accordionEvents, ["Validation", "Devices"]);
|
||||
addDefaultEventHandler(data.customText);
|
||||
cy.get(commonWidgetSelector.eventSelection).type("On Enter Pressed{Enter}");
|
||||
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
data.customText
|
||||
`${data.customText}{Enter}`
|
||||
);
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).should(
|
||||
"have.value",
|
||||
data.customText
|
||||
);
|
||||
verifyComponentValueFromInspector(data.widgetName, data.customText);
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, data.customText);
|
||||
cy.forceClickOnCanvas();
|
||||
|
||||
openEditorSidebar(data.widgetName);
|
||||
openAccordion(commonWidgetText.accordionValidation);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelRegex,
|
||||
commonWidgetText.regularExpression
|
||||
);
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
|
||||
addValidations(data.widgetName, data);
|
||||
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
data.customText
|
||||
|
|
@ -93,15 +134,10 @@ describe("Password Input", () => {
|
|||
).verifyVisibleElement("have.text", commonWidgetText.regexValidationError);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
openEditorSidebar(data.widgetName);
|
||||
openAccordion(commonWidgetText.accordionValidation);
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterInputField(commonWidgetText.labelRegex)
|
||||
).clearCodeMirror();
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelMinLength,
|
||||
data.minimumLength
|
||||
);
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
|
|
@ -113,269 +149,194 @@ describe("Password Input", () => {
|
|||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterInputField(commonWidgetText.labelMinLength)
|
||||
).clearCodeMirror();
|
||||
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelMaxLength,
|
||||
data.maximumLength
|
||||
);
|
||||
).clearAndTypeOnCodeMirror("0");
|
||||
cy.forceClickOnCanvas();
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
data.customText
|
||||
data.customText.toUpperCase().replaceAll(" ", "").replaceAll(".", "")
|
||||
);
|
||||
cy.get('[data-cy="real-canvas"]').click("topLeft", { force: true });
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
).verifyVisibleElement(
|
||||
"have.text",
|
||||
commonWidgetText.maxLengthValidationError(data.maximumLength)
|
||||
);
|
||||
openEditorSidebar(data.widgetName);
|
||||
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelcustomValidadtion,
|
||||
customValidation(data.widgetName, data.customText)
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
cy.get('[data-cy="real-canvas"]').click("topLeft", { force: true });
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
).verifyVisibleElement("have.text", data.customText);
|
||||
cy.forceClickOnCanvas();
|
||||
openEditorSidebar(data.widgetName);
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.accordion(commonWidgetText.accordionProperties)
|
||||
).click();
|
||||
cy.get(
|
||||
commonWidgetSelector.accordion(commonWidgetText.accordionValidation)
|
||||
).click();
|
||||
verifyPropertiesGeneralAccordion(data.widgetName, data.tooltipText);
|
||||
addAndVerifyAdditionalActions(data.widgetName, data.tooltipText);
|
||||
|
||||
openEditorSidebar(data.widgetName);
|
||||
cy.get(
|
||||
commonWidgetSelector.accordion(commonWidgetText.accordionProperties)
|
||||
).click();
|
||||
cy.get(
|
||||
commonWidgetSelector.accordion(commonWidgetText.accordionValidation)
|
||||
).click();
|
||||
// verifyLayout(data.widgetName);
|
||||
verifyLayout(data.widgetName, "Devices");
|
||||
|
||||
// cy.get(commonWidgetSelector.changeLayoutButton).click();
|
||||
// cy.get(
|
||||
// commonWidgetSelector.parameterTogglebutton(
|
||||
// commonWidgetText.parameterShowOnDesktop
|
||||
// )
|
||||
// ).click();
|
||||
cy.get(commonWidgetSelector.changeLayoutToDesktopButton).click();
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterTogglebutton(
|
||||
commonWidgetText.parameterShowOnDesktop
|
||||
)
|
||||
).click();
|
||||
|
||||
openEditorSidebar(data.widgetName);
|
||||
openAccordion("Validation", [
|
||||
"Data",
|
||||
"Validation",
|
||||
"Additional Actions",
|
||||
"Devices",
|
||||
"Events",
|
||||
]);
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterInputField(commonWidgetText.labelMinLength)
|
||||
).clearAndTypeOnCodeMirror("5");
|
||||
cy.forceClickOnCanvas();
|
||||
cy.waitForAutoSave();
|
||||
openEditorSidebar(data.widgetName);
|
||||
|
||||
cy.get(commonWidgetSelector.widgetDocumentationLink).should(
|
||||
"have.text",
|
||||
"Read documentation for PasswordInput"
|
||||
);
|
||||
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName))
|
||||
.invoke("attr", "placeholder")
|
||||
.should("contain", data.customText);
|
||||
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
`${data.customText}{Enter}`
|
||||
);
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, data.customText);
|
||||
cy.forceClickOnCanvas();
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
data.customText
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
).verifyVisibleElement("have.text", commonWidgetText.regexValidationError);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
).verifyVisibleElement(
|
||||
"have.text",
|
||||
commonWidgetText.minLengthValidationError("5")
|
||||
);
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
data.customText.toUpperCase().replaceAll(" ", "").replaceAll(".", "")
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
).verifyVisibleElement(
|
||||
"have.text",
|
||||
commonWidgetText.maxLengthValidationError(data.maximumLength)
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
verifyTooltip(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
data.tooltipText
|
||||
);
|
||||
});
|
||||
|
||||
it("should verify the styles of the password input widget", () => {
|
||||
const data = {};
|
||||
data.appName = `${fake.companyName}-App`;
|
||||
data.colourHex = fake.randomRgbaHex;
|
||||
data.boxShadowColor = fake.randomRgba;
|
||||
data.boxShadowParam = fake.boxShadowParam;
|
||||
data.bgColor = fake.randomRgba;
|
||||
data.borderColor = fake.randomRgba;
|
||||
data.textColor = fake.randomRgba;
|
||||
data.errorTextColor = fake.randomRgba;
|
||||
data.iconColor = fake.randomRgba;
|
||||
data.labelColor = fake.randomRgba;
|
||||
data.widgetName = passwordInputText.defaultWidgetName;
|
||||
|
||||
openEditorSidebar(passwordInputText.defaultWidgetName);
|
||||
cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click();
|
||||
addAllInputFieldColors(data);
|
||||
|
||||
verifyAndModifyToggleFx(
|
||||
commonWidgetText.parameterVisibility,
|
||||
commonWidgetText.codeMirrorLabelTrue
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(passwordInputText.defaultWidgetName)
|
||||
).should("not.be.visible");
|
||||
cy.clearAndType('[data-cy="border-radius-input"]', "20");
|
||||
// cy.get('[data-cy="icon-visibility-button"]').click();
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterTogglebutton(
|
||||
commonWidgetText.parameterVisibility
|
||||
)
|
||||
).click();
|
||||
|
||||
verifyAndModifyToggleFx(
|
||||
commonWidgetText.parameterDisable,
|
||||
commonWidgetText.codeMirrorLabelFalse
|
||||
);
|
||||
cy.waitForAutoSave();
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(passwordInputText.defaultWidgetName)
|
||||
).should("have.attr", "disabled");
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterTogglebutton(
|
||||
commonWidgetText.parameterDisable
|
||||
)
|
||||
).click();
|
||||
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.parameterBorderRadius,
|
||||
commonWidgetText.borderRadiusInput
|
||||
);
|
||||
|
||||
cy.get(commonWidgetSelector.buttonCloseEditorSideBar).click();
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(passwordInputText.defaultWidgetName)
|
||||
).should("have.css", "border-radius", "20px");
|
||||
|
||||
verifyInputFieldColors("passwordinput1", data);
|
||||
|
||||
verifyStylesGeneralAccordion(
|
||||
passwordInputText.defaultWidgetName,
|
||||
data.boxShadowParam,
|
||||
data.colourHex,
|
||||
data.boxShadowColor,
|
||||
1
|
||||
);
|
||||
});
|
||||
|
||||
it("should verify the app preview", () => {
|
||||
const data = {};
|
||||
data.appName = `${fake.companyName}-App`;
|
||||
data.widgetName = fake.widgetName;
|
||||
data.tooltipText = fake.randomSentence;
|
||||
data.colourHex = fake.randomRgbaHex;
|
||||
data.boxShadowColor = fake.randomRgba;
|
||||
data.boxShadowParam = fake.boxShadowParam;
|
||||
data.minimumLength = randomNumber(1, 4);
|
||||
data.maximumLength = randomNumber(8, 10);
|
||||
data.customText = randomString(12);
|
||||
data.maxLengthText = randomString(data.maximumLength);
|
||||
|
||||
openEditorSidebar(passwordInputText.defaultWidgetName);
|
||||
verifyAndModifyParameter("Placeholder", data.customText);
|
||||
|
||||
openAccordion(commonWidgetText.accordionValidation);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelRegex,
|
||||
commonWidgetText.regularExpression
|
||||
);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelMinLength,
|
||||
data.minimumLength
|
||||
);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelMaxLength,
|
||||
data.maximumLength
|
||||
);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelcustomValidadtion,
|
||||
customValidation(passwordInputText.defaultWidgetName, data.customText)
|
||||
);
|
||||
verifyPropertiesGeneralAccordion(
|
||||
passwordInputText.defaultWidgetName,
|
||||
data.tooltipText
|
||||
4
|
||||
);
|
||||
|
||||
openEditorSidebar(passwordInputText.defaultWidgetName);
|
||||
cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click();
|
||||
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.parameterBorderRadius,
|
||||
commonWidgetText.borderRadiusInput
|
||||
);
|
||||
verifyContainerElements();
|
||||
checkPaddingOfContainer(passwordInputText.defaultWidgetName, 1);
|
||||
cy.get('[data-cy="togglr-button-none"]').click();
|
||||
checkPaddingOfContainer(passwordInputText.defaultWidgetName, 0);
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterTogglebutton(
|
||||
commonWidgetText.parameterVisibility
|
||||
)
|
||||
)
|
||||
.click()
|
||||
.click();
|
||||
|
||||
cy.get(commonWidgetSelector.buttonCloseEditorSideBar).click();
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(passwordInputText.defaultWidgetName)
|
||||
).should("have.css", "border-radius", "20px");
|
||||
|
||||
cy.waitForAutoSave();
|
||||
cy.reload();
|
||||
|
||||
openEditorSidebar(passwordInputText.defaultWidgetName);
|
||||
cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click();
|
||||
openAccordion(commonWidgetText.accordionGenaral, [], 1);
|
||||
cy.get(commonWidgetSelector.boxShadowColorPicker).click();
|
||||
|
||||
fillBoxShadowParams(
|
||||
commonWidgetSelector.boxShadowDefaultParam,
|
||||
data.boxShadowParam
|
||||
);
|
||||
verifyLabelStyleElements();
|
||||
verifyAlignment(passwordInputText.defaultWidgetName, "sideLeft");
|
||||
cy.get('[data-cy="togglr-button-top"]').click();
|
||||
verifyAlignment(passwordInputText.defaultWidgetName, "topLeft");
|
||||
cy.get('[data-cy="togglr-button-right"]').click();
|
||||
verifyAlignment(passwordInputText.defaultWidgetName, "topRight");
|
||||
cy.get('[data-cy="togglr-button-side"]').click();
|
||||
verifyAlignment(passwordInputText.defaultWidgetName, "sideRight");
|
||||
cy.get('[data-cy="togglr-button-left"]').click();
|
||||
verifyAlignment(passwordInputText.defaultWidgetName, "sideLeft");
|
||||
addCustomWidthOfLabel("50");
|
||||
verifyCustomWidthOfLabel(passwordInputText.defaultWidgetName, "50");
|
||||
selectColourFromColourPicker(
|
||||
commonWidgetText.boxShadowColor,
|
||||
data.boxShadowColor,
|
||||
1
|
||||
"Text",
|
||||
data.labelColor,
|
||||
0,
|
||||
commonWidgetSelector.colourPickerParent,
|
||||
"0"
|
||||
);
|
||||
addTextWidgetToVerifyValue("components.passwordinput1.value");
|
||||
cy.waitForAutoSave();
|
||||
verifyWidgetColorCss(
|
||||
`[data-cy="label-${passwordInputText.defaultWidgetName}"]>label`,
|
||||
"color",
|
||||
data.labelColor,
|
||||
true
|
||||
);
|
||||
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(passwordInputText.defaultWidgetName)
|
||||
)
|
||||
.invoke("attr", "placeholder")
|
||||
.should("contain", data.customText);
|
||||
|
||||
cy.get('[data-cy="real-canvas"]').click("topLeft", { force: true });
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(passwordInputText.defaultWidgetName)
|
||||
)
|
||||
.type(" ")
|
||||
.type("{selectAll}{backspace}");
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(
|
||||
passwordInputText.defaultWidgetName
|
||||
)
|
||||
).verifyVisibleElement(
|
||||
"have.text",
|
||||
commonWidgetText.minLengthValidationError(data.minimumLength)
|
||||
);
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(passwordInputText.defaultWidgetName),
|
||||
"t"
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(commonWidgetText.text1)
|
||||
).verifyVisibleElement("have.text", "t");
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get('[data-cy="real-canvas"]').click("topLeft", { force: true });
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(
|
||||
passwordInputText.defaultWidgetName
|
||||
)
|
||||
).verifyVisibleElement("have.text", commonWidgetText.regexValidationError);
|
||||
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(passwordInputText.defaultWidgetName),
|
||||
data.customText.toUpperCase()
|
||||
);
|
||||
cy.get('[data-cy="real-canvas"]').click("topLeft", { force: true });
|
||||
|
||||
// cy.get(
|
||||
// commonWidgetSelector.draggableWidget(passwordInputText.defaultWidgetName)
|
||||
// )
|
||||
// .type("1")
|
||||
// .type("{selectAll}{backspace}");
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(
|
||||
passwordInputText.defaultWidgetName
|
||||
)
|
||||
).verifyVisibleElement(
|
||||
"have.text",
|
||||
commonWidgetText.maxLengthValidationError(data.maximumLength)
|
||||
verifyWidgetColorCss(
|
||||
`[data-cy="label-${passwordInputText.defaultWidgetName}"]>label`,
|
||||
"color",
|
||||
data.labelColor,
|
||||
true
|
||||
);
|
||||
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(passwordInputText.defaultWidgetName),
|
||||
data.maxLengthText.toUpperCase()
|
||||
);
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(passwordInputText.defaultWidgetName)
|
||||
).should("have.css", "border-radius", "20px");
|
||||
verifyAlignment(passwordInputText.defaultWidgetName, "sideLeft");
|
||||
verifyCustomWidthOfLabel(passwordInputText.defaultWidgetName, "50");
|
||||
verifyInputFieldColors("passwordinput1", data);
|
||||
|
||||
verifyBoxShadowCss(
|
||||
passwordInputText.defaultWidgetName,
|
||||
|
|
@ -383,9 +344,22 @@ describe("Password Input", () => {
|
|||
data.boxShadowParam
|
||||
);
|
||||
|
||||
verifyTooltip(
|
||||
commonWidgetSelector.draggableWidget(passwordInputText.defaultWidgetName),
|
||||
data.tooltipText
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(passwordInputText.defaultWidgetName)
|
||||
).should("have.css", "border-radius", "20px");
|
||||
});
|
||||
|
||||
it.skip("should verify the app preview", () => {});
|
||||
|
||||
it("should verify CSA", () => {
|
||||
const data = {};
|
||||
data.widgetName = passwordInputText.defaultWidgetName;
|
||||
data.customText = randomString(12);
|
||||
|
||||
addCSA(data);
|
||||
verifyCSA(data);
|
||||
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
verifyCSA(data);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ import { verifyNodeData, openNode, verifyValue } from "Support/utils/inspector";
|
|||
import { textInputText } from "Texts/textInput";
|
||||
import { deleteDownloadsFolder } from "Support/utils/common";
|
||||
import { resizeQueryPanel } from "Support/utils/dataSource";
|
||||
import { waitForQueryAction } from "Support/utils/queries";
|
||||
|
||||
describe("Table", () => {
|
||||
beforeEach(() => {
|
||||
|
|
@ -339,6 +340,32 @@ describe("Table", () => {
|
|||
openEditorSidebar(tableText.defaultWidgetName);
|
||||
editAndVerifyWidgetName(data.widgetName, []);
|
||||
|
||||
openEditorSidebar(data.widgetName);
|
||||
cy.wait(500);
|
||||
addAndOpenColumnOption("Fake-Link", `link`);
|
||||
verifyAndEnterColumnOptionInput("key", "name");
|
||||
cy.get('[data-cy="dropdown-link-target"] >>:eq(0)')
|
||||
.click()
|
||||
.find("input")
|
||||
.type(`{selectAll}{backspace}new window{enter}`);
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get('[data-cy="linksarah-cell-3"]')
|
||||
.contains("a", "Sarah")
|
||||
.should("have.attr", "target", "_blank");
|
||||
|
||||
openEditorSidebar(data.widgetName);
|
||||
cy.get('[data-cy*="column-Fake-Link"]').click();
|
||||
|
||||
cy.get('[data-cy="dropdown-link-target"] >>:eq(0)')
|
||||
.click()
|
||||
.find("input")
|
||||
.type(`{selectAll}{backspace}same window{enter}`);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get('[data-cy="linksarah-cell-3"]')
|
||||
.contains("a", "Sarah")
|
||||
.should("have.attr", "target", "_self");
|
||||
|
||||
//String/default
|
||||
openEditorSidebar(data.widgetName);
|
||||
cy.wait(500);
|
||||
|
|
@ -1259,4 +1286,70 @@ describe("Table", () => {
|
|||
.eq(0)
|
||||
.verifyVisibleElement("have.value", "Jack");
|
||||
});
|
||||
|
||||
it("should verify server-side paginaion", () => {
|
||||
let dsName = fake.companyName;
|
||||
cy.apiCreateGDS(
|
||||
"http://localhost:3000/api/v2/data_sources",
|
||||
`cypress-${dsName}-postgresql`,
|
||||
"postgresql",
|
||||
[
|
||||
{ key: "host", value: Cypress.env("pg_host") },
|
||||
{ key: "port", value: 5432 },
|
||||
{ key: "database", value: Cypress.env("pg_user") },
|
||||
{ key: "username", value: Cypress.env("pg_user") },
|
||||
{ key: "password", value: Cypress.env("pg_password"), encrypted: true },
|
||||
{ key: "ssl_enabled", value: false, encrypted: false },
|
||||
{ key: "ssl_certificate", value: "none", encrypted: false },
|
||||
]
|
||||
);
|
||||
cy.apiAddQueryToApp(
|
||||
"q112",
|
||||
{
|
||||
mode: "sql",
|
||||
transformationLanguage: "javascript",
|
||||
enableTransformation: false,
|
||||
query: `SELECT *
|
||||
FROM server_side_pagination
|
||||
ORDER BY id
|
||||
LIMIT 10 OFFSET {{(components.table1.pageIndex-1)*10}};`,
|
||||
},
|
||||
`cypress-${dsName}-postgresql`,
|
||||
"postgresql"
|
||||
);
|
||||
cy.reload();
|
||||
openEditorSidebar(tableText.defaultWidgetName);
|
||||
cy.get("[data-state=off]:eq(3)").click();
|
||||
cy.get(
|
||||
'[data-cy="total-records-server-side-input-field"]'
|
||||
).clearAndTypeOnCodeMirror("50");
|
||||
|
||||
selectEvent("Page changed", "Run Query", 1);
|
||||
cy.get('[data-cy="query-selection-field"]')
|
||||
.click()
|
||||
.find("input")
|
||||
.type(`{selectAll}{backspace}q112{enter}`);
|
||||
cy.get('[data-cy="table-data-input-field"]').clearAndTypeOnCodeMirror(
|
||||
`{{queries.q112.data`
|
||||
);
|
||||
cy.get('[data-cy="pagination-button-to-next"]').click();
|
||||
waitForQueryAction("run");
|
||||
cy.get('[data-cy="11-cell-0"]').verifyVisibleElement("have.text", "11");
|
||||
cy.get('[data-cy="pagination-button-to-previous"]').click();
|
||||
waitForQueryAction("run");
|
||||
cy.get('[data-cy="1-cell-0"]').verifyVisibleElement("have.text", "1");
|
||||
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
cy.wait(4000);
|
||||
cy.get('[data-cy="pagination-button-to-next"]').click();
|
||||
cy.get('[data-cy="11-cell-0"]', { timeout: 10000 }).verifyVisibleElement(
|
||||
"have.text",
|
||||
"11"
|
||||
);
|
||||
cy.get('[data-cy="pagination-button-to-previous"]').click();
|
||||
cy.get('[data-cy="1-cell-0"]', { timeout: 10000 }).verifyVisibleElement(
|
||||
"have.text",
|
||||
"1"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,39 +1,34 @@
|
|||
import { fake } from "Fixtures/fake";
|
||||
import { textInputText } from "Texts/textInput";
|
||||
import { commonWidgetText, widgetValue, customValidation } from "Texts/common";
|
||||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import { buttonText } from "Texts/button";
|
||||
import {
|
||||
verifyControlComponentAction,
|
||||
randomString,
|
||||
} from "Support/utils/textInput";
|
||||
verifyNodeData,
|
||||
openNode,
|
||||
verifyValue,
|
||||
deleteComponentFromInspector,
|
||||
verifyfunctions,
|
||||
} from "Support/utils/inspector";
|
||||
import { commonWidgetSelector } from "Selectors/common";
|
||||
import {
|
||||
openAccordion,
|
||||
verifyAndModifyParameter,
|
||||
openEditorSidebar,
|
||||
verifyAndModifyToggleFx,
|
||||
addDefaultEventHandler,
|
||||
verifyComponentValueFromInspector,
|
||||
selectColourFromColourPicker,
|
||||
verifyBoxShadowCss,
|
||||
verifyLayout,
|
||||
verifyTooltip,
|
||||
editAndVerifyWidgetName,
|
||||
verifyPropertiesGeneralAccordion,
|
||||
selectFromSidebarDropdown,
|
||||
addValueOnInput,
|
||||
selectColourFromColourPicker,
|
||||
verifyStylesGeneralAccordion,
|
||||
randomNumber,
|
||||
closeAccordions,
|
||||
} from "Support/utils/commonWidget";
|
||||
import {
|
||||
addSupportCSAData,
|
||||
selectCSA,
|
||||
selectEvent,
|
||||
addSupportCSAData,
|
||||
} from "Support/utils/events";
|
||||
import { randomString } from "Support/utils/textInput";
|
||||
import { buttonText } from "Texts/button";
|
||||
|
||||
describe("Text Input", () => {
|
||||
const data = {};
|
||||
beforeEach(() => {
|
||||
cy.viewport(1200, 1200);
|
||||
data.appName = `${fake.companyName}-text-App1`;
|
||||
cy.apiLogin();
|
||||
cy.apiCreateApp();
|
||||
cy.apiCreateApp(data.appName);
|
||||
cy.openApp();
|
||||
cy.dragAndDropWidget("Text");
|
||||
});
|
||||
|
|
@ -41,6 +36,60 @@ describe("Text Input", () => {
|
|||
afterEach(() => {
|
||||
cy.apiDeleteApp();
|
||||
});
|
||||
|
||||
it("should verify properties of text component", () => {
|
||||
data.componentName = fake.widgetName;
|
||||
cy.get(commonWidgetSelector.draggableWidget("text1")).should("be.visible");
|
||||
|
||||
editAndVerifyWidgetName(data.componentName, [
|
||||
"Data",
|
||||
"Events",
|
||||
"Additional Actions",
|
||||
"Devices",
|
||||
]);
|
||||
cy.get(
|
||||
'[data-cy="textcomponenttextinput-input-field"]'
|
||||
).clearAndTypeOnCodeMirror("Cypress testing text component");
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(data.componentName)
|
||||
).verifyVisibleElement("have.text", "Cypress testing text component");
|
||||
});
|
||||
|
||||
it.only("should verify styles of text component", () => {
|
||||
cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click();
|
||||
selectFromSidebarDropdown("Weight", "bolder");
|
||||
selectFromSidebarDropdown("Font variant", "initial");
|
||||
addValueOnInput("Size", 25);
|
||||
addValueOnInput("Line Height", 3);
|
||||
addValueOnInput("Text Indent", 2);
|
||||
addValueOnInput("Letter Spacing", 2);
|
||||
addValueOnInput("Word Spacing", 2);
|
||||
addValueOnInput("Border radius", 2);
|
||||
|
||||
data.textColor = fake.randomRgba;
|
||||
data.backgroundColor = fake.randomRgba;
|
||||
data.borderColor = fake.randomRgba;
|
||||
data.boxShadowColor = fake.randomRgba;
|
||||
data.boxShadowParam = fake.boxShadowParam;
|
||||
|
||||
selectColourFromColourPicker("color", data.textColor);
|
||||
selectColourFromColourPicker("background", data.backgroundColor);
|
||||
selectColourFromColourPicker("border", data.borderColor);
|
||||
|
||||
data.colourHex = fake.randomRgbaHex;
|
||||
verifyStylesGeneralAccordion(
|
||||
"text1",
|
||||
data.boxShadowParam,
|
||||
data.colourHex,
|
||||
data.boxShadowColor,
|
||||
4,
|
||||
"#00000090"
|
||||
);
|
||||
});
|
||||
|
||||
it("should verify preview of text component", () => {});
|
||||
|
||||
it("should verify CSA", () => {
|
||||
const data = {};
|
||||
data.customText = randomString(12);
|
||||
|
|
@ -71,4 +120,22 @@ describe("Text Input", () => {
|
|||
"not.be.visible"
|
||||
);
|
||||
});
|
||||
it("should verify expossed values", () => {
|
||||
cy.get(commonWidgetSelector.sidebarinspector).click();
|
||||
verifyNodeData("components", "Object", "1 entry ");
|
||||
openNode("components");
|
||||
openNode("text1");
|
||||
|
||||
verifyValue("text", "String", `"Hello World👋"`);
|
||||
verifyValue("isVisible", "Boolean", "true");
|
||||
verifyValue("isLoading", "Boolean", "false");
|
||||
verifyValue("isDisabled", "Boolean", "false");
|
||||
|
||||
verifyfunctions("clear", "Function");
|
||||
verifyfunctions("setText", "Function");
|
||||
verifyfunctions("visibility", "Function");
|
||||
verifyfunctions("setDisabled", "Function");
|
||||
verifyfunctions("setVisibility", "Function");
|
||||
verifyfunctions("setLoadingState", "Function");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,39 +1,45 @@
|
|||
import { fake } from "Fixtures/fake";
|
||||
import { textInputText } from "Texts/textInput";
|
||||
import { commonWidgetText, widgetValue, customValidation } from "Texts/common";
|
||||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import { buttonText } from "Texts/button";
|
||||
import {
|
||||
verifyControlComponentAction,
|
||||
randomString,
|
||||
} from "Support/utils/textInput";
|
||||
import {
|
||||
openAccordion,
|
||||
verifyAndModifyParameter,
|
||||
openEditorSidebar,
|
||||
verifyAndModifyToggleFx,
|
||||
addDefaultEventHandler,
|
||||
verifyComponentValueFromInspector,
|
||||
selectColourFromColourPicker,
|
||||
verifyBoxShadowCss,
|
||||
verifyLayout,
|
||||
verifyTooltip,
|
||||
editAndVerifyWidgetName,
|
||||
verifyPropertiesGeneralAccordion,
|
||||
verifyStylesGeneralAccordion,
|
||||
randomNumber,
|
||||
checkPaddingOfContainer,
|
||||
closeAccordions,
|
||||
editAndVerifyWidgetName,
|
||||
openAccordion,
|
||||
openEditorSidebar,
|
||||
randomNumber,
|
||||
selectColourFromColourPicker,
|
||||
verifyAndModifyParameter,
|
||||
verifyBoxShadowCss,
|
||||
verifyComponentValueFromInspector,
|
||||
verifyContainerElements,
|
||||
verifyLayout,
|
||||
verifyStylesGeneralAccordion,
|
||||
verifyTooltip,
|
||||
verifyWidgetColorCss,
|
||||
} from "Support/utils/commonWidget";
|
||||
import {
|
||||
selectCSA,
|
||||
selectEvent,
|
||||
addSupportCSAData,
|
||||
} from "Support/utils/events";
|
||||
addAllInputFieldColors,
|
||||
addAndVerifyAdditionalActions,
|
||||
addCustomWidthOfLabel,
|
||||
addValidations,
|
||||
verifyAlignment,
|
||||
verifyCustomWidthOfLabel,
|
||||
verifyInputFieldColors,
|
||||
verifyLabelStyleElements,
|
||||
} from "Support/utils/editor/inputFieldUtils";
|
||||
import {
|
||||
addCSA,
|
||||
randomString,
|
||||
verifyCSA,
|
||||
} from "Support/utils/editor/textInput";
|
||||
import { commonWidgetText } from "Texts/common";
|
||||
import { textInputText } from "Texts/textInput";
|
||||
|
||||
describe("Text Input", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.apiCreateApp("11111");
|
||||
cy.apiCreateApp(`${fake.companyName}-Textinput-App`);
|
||||
cy.openApp();
|
||||
cy.dragAndDropWidget("Text Input", 500, 500);
|
||||
});
|
||||
|
|
@ -43,7 +49,6 @@ describe("Text Input", () => {
|
|||
|
||||
it("should verify the properties of the text input widget", () => {
|
||||
const data = {};
|
||||
data.appName = `${fake.companyName}-App`;
|
||||
data.widgetName = fake.widgetName;
|
||||
data.tooltipText = fake.randomSentence;
|
||||
data.minimumLength = randomNumber(1, 4);
|
||||
|
|
@ -51,12 +56,25 @@ describe("Text Input", () => {
|
|||
data.customText = randomString(12);
|
||||
|
||||
openEditorSidebar(textInputText.defaultWidgetName);
|
||||
closeAccordions(["Validation", "General", "Properties", "Layout"]);
|
||||
editAndVerifyWidgetName(data.widgetName);
|
||||
openAccordion(commonWidgetText.accordionProperties, [
|
||||
closeAccordions([
|
||||
"Data",
|
||||
"Validation",
|
||||
"General",
|
||||
"Properties",
|
||||
"Additional Actions",
|
||||
"Devices",
|
||||
"Events",
|
||||
]);
|
||||
editAndVerifyWidgetName(data.widgetName, [
|
||||
"Data",
|
||||
"Validation",
|
||||
"Additional Actions",
|
||||
"Devices",
|
||||
"Events",
|
||||
]);
|
||||
openAccordion("Data", [
|
||||
"Data",
|
||||
"Validation",
|
||||
"Additional Actions",
|
||||
"Devices",
|
||||
"Events",
|
||||
]);
|
||||
verifyAndModifyParameter(
|
||||
|
|
@ -74,11 +92,12 @@ describe("Text Input", () => {
|
|||
|
||||
data.customText = fake.randomSentence;
|
||||
openEditorSidebar(data.widgetName);
|
||||
openAccordion(commonWidgetText.accordionProperties, [
|
||||
openAccordion("Data", [
|
||||
"Data",
|
||||
"Validation",
|
||||
"General",
|
||||
"Additional Actions",
|
||||
"Devices",
|
||||
"Events",
|
||||
"Properties",
|
||||
]);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelPlaceHolder,
|
||||
|
|
@ -90,8 +109,8 @@ describe("Text Input", () => {
|
|||
.should("contain", data.customText);
|
||||
|
||||
openEditorSidebar(data.widgetName);
|
||||
openAccordion(commonWidgetText.accordionEvents, ["Validation", "Layout"]);
|
||||
addDefaultEventHandler(widgetValue(data.widgetName));
|
||||
openAccordion(commonWidgetText.accordionEvents, ["Validation", "Devices"]);
|
||||
addDefaultEventHandler(data.customText);
|
||||
cy.get(commonWidgetSelector.eventSelection).type("On Enter Pressed{Enter}");
|
||||
|
||||
cy.clearAndType(
|
||||
|
|
@ -102,12 +121,9 @@ describe("Text Input", () => {
|
|||
cy.forceClickOnCanvas();
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
openEditorSidebar(data.widgetName);
|
||||
openAccordion(commonWidgetText.accordionValidation);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelRegex,
|
||||
commonWidgetText.regularExpression
|
||||
);
|
||||
|
||||
addValidations(data.widgetName, data);
|
||||
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
data.customText
|
||||
|
|
@ -121,10 +137,7 @@ describe("Text Input", () => {
|
|||
cy.get(
|
||||
commonWidgetSelector.parameterInputField(commonWidgetText.labelRegex)
|
||||
).clearCodeMirror();
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelMinLength,
|
||||
data.minimumLength
|
||||
);
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
|
|
@ -136,15 +149,11 @@ describe("Text Input", () => {
|
|||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterInputField(commonWidgetText.labelMinLength)
|
||||
).clearCodeMirror();
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelMaxLength,
|
||||
data.maximumLength
|
||||
);
|
||||
).clearAndTypeOnCodeMirror("0");
|
||||
cy.forceClickOnCanvas();
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
data.customText
|
||||
data.customText.toUpperCase().replaceAll(" ", "").replaceAll(".", "")
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
|
|
@ -152,33 +161,23 @@ describe("Text Input", () => {
|
|||
"have.text",
|
||||
commonWidgetText.maxLengthValidationError(data.maximumLength)
|
||||
);
|
||||
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelcustomValidadtion,
|
||||
customValidation(data.widgetName, data.customText)
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
).verifyVisibleElement("have.text", data.customText);
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.accordion(commonWidgetText.accordionProperties)
|
||||
).click();
|
||||
cy.forceClickOnCanvas();
|
||||
openEditorSidebar(data.widgetName);
|
||||
cy.get(
|
||||
commonWidgetSelector.accordion(commonWidgetText.accordionValidation)
|
||||
).click();
|
||||
verifyPropertiesGeneralAccordion(data.widgetName, data.tooltipText);
|
||||
addAndVerifyAdditionalActions(data.widgetName, data.tooltipText);
|
||||
|
||||
openEditorSidebar(data.widgetName);
|
||||
cy.get(
|
||||
commonWidgetSelector.accordion(commonWidgetText.accordionProperties)
|
||||
).click();
|
||||
cy.get(
|
||||
commonWidgetSelector.accordion(commonWidgetText.accordionValidation)
|
||||
).click();
|
||||
verifyLayout(data.widgetName);
|
||||
verifyLayout(data.widgetName, "Devices");
|
||||
|
||||
cy.get(commonWidgetSelector.changeLayoutToDesktopButton).click();
|
||||
cy.get(
|
||||
|
|
@ -187,290 +186,180 @@ describe("Text Input", () => {
|
|||
)
|
||||
).click();
|
||||
|
||||
openEditorSidebar(data.widgetName);
|
||||
openAccordion("Validation", [
|
||||
"Data",
|
||||
"Validation",
|
||||
"Additional Actions",
|
||||
"Devices",
|
||||
"Events",
|
||||
]);
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterInputField(commonWidgetText.labelMinLength)
|
||||
).clearAndTypeOnCodeMirror("5");
|
||||
cy.forceClickOnCanvas();
|
||||
cy.waitForAutoSave();
|
||||
openEditorSidebar(data.widgetName);
|
||||
|
||||
cy.get(commonWidgetSelector.widgetDocumentationLink).should(
|
||||
"have.text",
|
||||
textInputText.textInputDocumentationLink
|
||||
);
|
||||
data.customText = fake.firstName;
|
||||
verifyControlComponentAction(data.widgetName, data.customText);
|
||||
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName))
|
||||
.invoke("attr", "placeholder")
|
||||
.should("contain", data.customText);
|
||||
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
`${data.customText}{Enter}`
|
||||
);
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, data.customText);
|
||||
cy.forceClickOnCanvas();
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
data.customText
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
// cy.get(
|
||||
// commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
// ).verifyVisibleElement("have.text", commonWidgetText.regexValidationError);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
).verifyVisibleElement(
|
||||
"have.text",
|
||||
commonWidgetText.minLengthValidationError("5")
|
||||
);
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
data.customText.toUpperCase().replaceAll(" ", "").replaceAll(".", "")
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(data.widgetName)
|
||||
).verifyVisibleElement(
|
||||
"have.text",
|
||||
commonWidgetText.maxLengthValidationError(data.maximumLength)
|
||||
);
|
||||
cy.forceClickOnCanvas();
|
||||
verifyTooltip(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName),
|
||||
data.tooltipText
|
||||
);
|
||||
});
|
||||
|
||||
it("should verify the styles of the text input widget", () => {
|
||||
const data = {};
|
||||
data.appName = `${fake.companyName}-App`;
|
||||
data.colourHex = fake.randomRgbaHex;
|
||||
data.boxShadowColor = fake.randomRgba;
|
||||
data.boxShadowParam = fake.boxShadowParam;
|
||||
data.bgColor = fake.randomRgba;
|
||||
data.borderColor = fake.randomRgba;
|
||||
data.textColor = fake.randomRgba;
|
||||
data.errorTextColor = fake.randomRgba;
|
||||
data.iconColor = fake.randomRgba;
|
||||
data.labelColor = fake.randomRgba;
|
||||
ata.widgetName = textInputText.defaultWidgetName;
|
||||
|
||||
openEditorSidebar(textInputText.defaultWidgetName);
|
||||
cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click();
|
||||
addAllInputFieldColors(data);
|
||||
|
||||
verifyAndModifyToggleFx(
|
||||
commonWidgetText.parameterVisibility,
|
||||
commonWidgetText.codeMirrorLabelTrue
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(textInputText.defaultWidgetName)
|
||||
).should("not.be.visible");
|
||||
cy.clearAndType('[data-cy="border-radius-input"]', "20");
|
||||
cy.get('[data-cy="icon-visibility-button"]').click();
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterTogglebutton(
|
||||
commonWidgetText.parameterVisibility
|
||||
)
|
||||
).click();
|
||||
|
||||
verifyAndModifyToggleFx(
|
||||
commonWidgetText.parameterDisable,
|
||||
commonWidgetText.codeMirrorLabelFalse
|
||||
);
|
||||
cy.waitForAutoSave();
|
||||
cy.get("[data-cy='draggable-widget-textinput1']")
|
||||
.parent('[class="text-input true"]')
|
||||
.invoke("attr", "data-disabled")
|
||||
.and("contain", "true");
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterTogglebutton(
|
||||
commonWidgetText.parameterDisable
|
||||
)
|
||||
).click();
|
||||
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.parameterBorderRadius,
|
||||
commonWidgetText.borderRadiusInput
|
||||
);
|
||||
|
||||
cy.get(commonWidgetSelector.buttonCloseEditorSideBar).click();
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(textInputText.defaultWidgetName)
|
||||
).should("have.css", "border-radius", "20px");
|
||||
|
||||
verifyStylesGeneralAccordion(
|
||||
textInputText.defaultWidgetName,
|
||||
data.boxShadowParam,
|
||||
data.colourHex,
|
||||
data.boxShadowColor,
|
||||
4
|
||||
);
|
||||
});
|
||||
|
||||
it("should verify the app preview", () => {
|
||||
const data = {};
|
||||
data.appName = `${fake.companyName}-App`;
|
||||
data.widgetName = fake.widgetName;
|
||||
data.tooltipText = fake.randomSentence;
|
||||
data.maxLengthErrText = fake.randomSentence;
|
||||
data.colourHex = fake.randomRgbaHex;
|
||||
data.boxShadowColor = fake.randomRgba;
|
||||
data.boxShadowParam = fake.boxShadowParam;
|
||||
data.minimumLength = randomNumber(1, 4);
|
||||
data.maximumLength = randomNumber(8, 10);
|
||||
data.customText = randomString(12);
|
||||
data.maxLengthText = randomString(data.maximumLength);
|
||||
|
||||
openEditorSidebar(textInputText.defaultWidgetName);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelDefaultValue,
|
||||
data.customText
|
||||
);
|
||||
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelPlaceHolder,
|
||||
data.customText
|
||||
);
|
||||
|
||||
openAccordion(commonWidgetText.accordionEvents, ["Validation", "Layout"]);
|
||||
addDefaultEventHandler(widgetValue(textInputText.defaultWidgetName));
|
||||
cy.get(commonWidgetSelector.eventSelection).type("On Enter Pressed{Enter}");
|
||||
|
||||
openAccordion(commonWidgetText.accordionValidation);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelRegex,
|
||||
commonWidgetText.regularExpression
|
||||
);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelMinLength,
|
||||
data.minimumLength
|
||||
);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelMaxLength,
|
||||
data.maximumLength
|
||||
);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelcustomValidadtion,
|
||||
customValidation(textInputText.defaultWidgetName, data.customText)
|
||||
);
|
||||
verifyPropertiesGeneralAccordion(
|
||||
textInputText.defaultWidgetName,
|
||||
data.tooltipText
|
||||
);
|
||||
|
||||
verifyControlComponentAction(
|
||||
textInputText.defaultWidgetName,
|
||||
data.customText
|
||||
);
|
||||
|
||||
openEditorSidebar(textInputText.defaultWidgetName);
|
||||
cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click();
|
||||
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.parameterBorderRadius,
|
||||
commonWidgetText.borderRadiusInput
|
||||
);
|
||||
verifyStylesGeneralAccordion(
|
||||
textInputText.defaultWidgetName,
|
||||
data.boxShadowParam,
|
||||
data.colourHex,
|
||||
data.boxShadowColor,
|
||||
4
|
||||
);
|
||||
|
||||
cy.waitForAutoSave();
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(textInputText.defaultWidgetName)
|
||||
).verifyVisibleElement("have.value", data.customText);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(textInputText.defaultWidgetName)
|
||||
)
|
||||
.invoke("attr", "placeholder")
|
||||
.should("contain", data.customText);
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(textInputText.defaultWidgetName)
|
||||
)
|
||||
.type(`{selectAll}{backspace}{enter}`)
|
||||
.type(data.customText);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(
|
||||
textInputText.defaultWidgetName
|
||||
)
|
||||
).verifyVisibleElement("have.text", commonWidgetText.regexValidationError);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(textInputText.defaultWidgetName)
|
||||
).clear();
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(
|
||||
textInputText.defaultWidgetName
|
||||
)
|
||||
).verifyVisibleElement(
|
||||
"have.text",
|
||||
commonWidgetText.minLengthValidationError(data.minimumLength)
|
||||
);
|
||||
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(textInputText.defaultWidgetName),
|
||||
data.customText.toUpperCase()
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.validationFeedbackMessage(
|
||||
textInputText.defaultWidgetName
|
||||
)
|
||||
).verifyVisibleElement(
|
||||
"have.text",
|
||||
commonWidgetText.maxLengthValidationError(data.maximumLength)
|
||||
);
|
||||
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget(textInputText.defaultWidgetName),
|
||||
`${data.maxLengthText.toUpperCase()}{Enter}`
|
||||
);
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
data.maxLengthText.toUpperCase()
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(buttonText.defaultWidgetName)
|
||||
).should("have.text", data.maxLengthText.toUpperCase());
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(textInputText.defaultWidgetName)
|
||||
).should("have.css", "border-radius", "20px");
|
||||
|
||||
verifyInputFieldColors("textinput1", data);
|
||||
|
||||
verifyStylesGeneralAccordion(
|
||||
textInputText.defaultWidgetName,
|
||||
data.boxShadowParam,
|
||||
data.colourHex,
|
||||
data.boxShadowColor,
|
||||
4
|
||||
);
|
||||
|
||||
openEditorSidebar(textInputText.defaultWidgetName);
|
||||
cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click();
|
||||
|
||||
verifyContainerElements();
|
||||
checkPaddingOfContainer(textInputText.defaultWidgetName, 1);
|
||||
cy.get('[data-cy="togglr-button-none"]').click();
|
||||
checkPaddingOfContainer(textInputText.defaultWidgetName, 0);
|
||||
|
||||
verifyLabelStyleElements();
|
||||
verifyAlignment(textInputText.defaultWidgetName, "sideLeft");
|
||||
cy.get('[data-cy="togglr-button-top"]').click();
|
||||
verifyAlignment(textInputText.defaultWidgetName, "topLeft");
|
||||
cy.get('[data-cy="togglr-button-right"]').click();
|
||||
verifyAlignment(textInputText.defaultWidgetName, "topRight");
|
||||
cy.get('[data-cy="togglr-button-side"]').click();
|
||||
verifyAlignment(textInputText.defaultWidgetName, "sideRight");
|
||||
cy.get('[data-cy="togglr-button-left"]').click();
|
||||
verifyAlignment(textInputText.defaultWidgetName, "sideLeft");
|
||||
addCustomWidthOfLabel("50");
|
||||
verifyCustomWidthOfLabel(textInputText.defaultWidgetName, "50");
|
||||
selectColourFromColourPicker(
|
||||
"Text",
|
||||
data.labelColor,
|
||||
0,
|
||||
commonWidgetSelector.colourPickerParent,
|
||||
"0"
|
||||
);
|
||||
verifyWidgetColorCss(
|
||||
`[data-cy="label-${textInputText.defaultWidgetName}"]>label`,
|
||||
"color",
|
||||
data.labelColor,
|
||||
true
|
||||
);
|
||||
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
verifyWidgetColorCss(
|
||||
`[data-cy="label-${textInputText.defaultWidgetName}"]>label`,
|
||||
"color",
|
||||
data.labelColor,
|
||||
true
|
||||
);
|
||||
|
||||
verifyAlignment(textInputText.defaultWidgetName, "sideLeft");
|
||||
verifyCustomWidthOfLabel(textInputText.defaultWidgetName, "50");
|
||||
verifyInputFieldColors("textinput1", data);
|
||||
|
||||
verifyBoxShadowCss(
|
||||
textInputText.defaultWidgetName,
|
||||
data.boxShadowColor,
|
||||
data.boxShadowParam
|
||||
);
|
||||
|
||||
verifyTooltip(
|
||||
commonWidgetSelector.draggableWidget(textInputText.defaultWidgetName),
|
||||
data.tooltipText
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(textInputText.defaultWidgetName)
|
||||
).should("have.css", "border-radius", "20px");
|
||||
});
|
||||
|
||||
it.skip("should verify the app preview", () => {});
|
||||
|
||||
it("should verify CSA", () => {
|
||||
const data = {};
|
||||
data.customText = randomString(12);
|
||||
data.widgetName = textInputText.defaultWidgetName;
|
||||
|
||||
cy.get('[data-cy="real-canvas"]').click("topRight", { force: true });
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 500, 200);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA("textinput1", "Visibility");
|
||||
addCSA(data);
|
||||
verifyCSA(data);
|
||||
|
||||
cy.get('[data-cy="real-canvas"]').click("topRight", { force: true });
|
||||
cy.dragAndDropWidget("Text input", 50, 50);
|
||||
selectEvent("On change", "Control Component");
|
||||
selectCSA("textinput1", "Set text", "500");
|
||||
addSupportCSAData("text", "{{components.textinput2.value");
|
||||
|
||||
cy.get('[data-cy="real-canvas"]').click("topRight", { force: true });
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 50, 200);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA("textinput1", "Clear", "500");
|
||||
|
||||
cy.get('[data-cy="real-canvas"]').click("topRight", { force: true });
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 50, 400);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA("textinput1", "Disable", "500");
|
||||
cy.wait(500);
|
||||
cy.get('[data-cy="Value-fx-button"]').click();
|
||||
cy.get('[data-cy="Value-input-field"]').clearAndTypeOnCodeMirror("{{true");
|
||||
// cy.wait(1000);
|
||||
|
||||
cy.get('[data-cy="real-canvas"]').click("topRight", { force: true });
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 300, 50);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA("textinput1", "Set blur", "500");
|
||||
|
||||
cy.get('[data-cy="real-canvas"]').click("topRight", { force: true });
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 300, 200);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA("textinput1", "Set focus");
|
||||
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget("textinput2"),
|
||||
data.customText
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget("textinput1")
|
||||
).verifyVisibleElement("have.value", data.customText);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button2")).click();
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget("textinput1")
|
||||
).verifyVisibleElement("have.value", "");
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button5")).click();
|
||||
cy.realType(data.customText);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget("textinput1")
|
||||
).verifyVisibleElement("have.value", data.customText);
|
||||
cy.get(commonWidgetSelector.draggableWidget("button4")).click();
|
||||
cy.realType("not working");
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget("textinput1")
|
||||
).verifyVisibleElement("have.value", data.customText);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button3")).click();
|
||||
cy.get(commonWidgetSelector.draggableWidget("textinput1"))
|
||||
.parent()
|
||||
.should("have.attr", "data-disabled", "true");
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button1")).click();
|
||||
cy.get(commonWidgetSelector.draggableWidget("textinput1")).should(
|
||||
"not.be.visible"
|
||||
);
|
||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||
verifyCSA(data);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -29,7 +29,10 @@ function randomSentence() {
|
|||
|
||||
function randomRgba() {
|
||||
let rgba = faker.color.rgb({ format: "decimal", includeAlpha: true });
|
||||
rgba[rgba.length - 1] = rgba[rgba.length - 1].toPrecision(2) * 100;
|
||||
let alpha = rgba[rgba.length - 1].toPrecision(2) * 100;
|
||||
|
||||
alpha = Math.min(Math.max(alpha, 20), 80);
|
||||
rgba[rgba.length - 1] = alpha;
|
||||
return rgba;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,18 @@
|
|||
import { commonWidgetSelector } from "Selectors/common";
|
||||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import { openAccordion, openEditorSidebar } from "Support/utils/commonWidget";
|
||||
import { buttonText } from "Texts/button";
|
||||
import { commonWidgetText } from "Texts/common";
|
||||
import {
|
||||
addDefaultEventHandler,
|
||||
selectColourFromColourPicker,
|
||||
verifyAndModifyParameter,
|
||||
verifyBoxShadowCss,
|
||||
verifyLoaderColor,
|
||||
verifyPropertiesGeneralAccordion,
|
||||
verifyStylesGeneralAccordion,
|
||||
verifyTooltip,
|
||||
verifyWidgetColorCss,
|
||||
} from "Support/utils/commonWidget";
|
||||
|
||||
export const verifyControlComponentAction = (widgetName, value) => {
|
||||
cy.forceClickOnCanvas();
|
||||
|
|
@ -24,10 +36,97 @@ export const verifyControlComponentAction = (widgetName, value) => {
|
|||
.clearAndTypeOnCodeMirror(value);
|
||||
cy.forceClickOnCanvas();
|
||||
cy.waitForAutoSave();
|
||||
cy.get(commonWidgetSelector.draggableWidget(widgetName)).click();
|
||||
};
|
||||
|
||||
export const addBasicData = (data) => {
|
||||
openEditorSidebar(buttonText.defaultWidgetName);
|
||||
verifyAndModifyParameter(buttonText.buttonTextLabel, data.widgetName);
|
||||
|
||||
openAccordion(commonWidgetText.accordionEvents);
|
||||
addDefaultEventHandler(data.alertMessage);
|
||||
|
||||
verifyPropertiesGeneralAccordion(
|
||||
buttonText.defaultWidgetName,
|
||||
data.tooltipText
|
||||
);
|
||||
|
||||
openEditorSidebar(buttonText.defaultWidgetName);
|
||||
cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click();
|
||||
selectColourFromColourPicker(
|
||||
buttonText.backgroundColor,
|
||||
data.backgroundColor
|
||||
);
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
openEditorSidebar(buttonText.defaultWidgetName);
|
||||
cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click();
|
||||
selectColourFromColourPicker(buttonText.textColor, data.textColor, 1);
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
openEditorSidebar(buttonText.defaultWidgetName);
|
||||
cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click();
|
||||
selectColourFromColourPicker(buttonText.loaderColor, data.loaderColor, 2);
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
openEditorSidebar(buttonText.defaultWidgetName);
|
||||
cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click();
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterInputField(
|
||||
commonWidgetText.parameterBorderRadius
|
||||
)
|
||||
)
|
||||
.first()
|
||||
.clear()
|
||||
.type(buttonText.borderRadiusInput);
|
||||
|
||||
verifyStylesGeneralAccordion(
|
||||
buttonText.defaultWidgetName,
|
||||
data.boxShadowParam,
|
||||
data.colourHex,
|
||||
data.boxShadowColor,
|
||||
4
|
||||
);
|
||||
|
||||
verifyControlComponentAction(
|
||||
buttonText.defaultWidgetName,
|
||||
data.customMessage
|
||||
);
|
||||
|
||||
cy.waitForAutoSave();
|
||||
};
|
||||
|
||||
export const verifyBasicData = (widgetName, data) => {
|
||||
cy.get(commonWidgetSelector.draggableWidget(widgetName)).verifyVisibleElement(
|
||||
"have.text",
|
||||
data.widgetName
|
||||
);
|
||||
cy.wait(1500);
|
||||
cy.get(commonWidgetSelector.draggableWidget(widgetName)).click({
|
||||
force: true,
|
||||
});
|
||||
cy.wait(500);
|
||||
|
||||
cy.verifyToastMessage(commonSelectors.toastMessage, data.alertMessage);
|
||||
cy.get(commonWidgetSelector.draggableWidget("textinput1")).should(
|
||||
"have.value",
|
||||
value
|
||||
data.customMessage
|
||||
);
|
||||
|
||||
verifyTooltip(
|
||||
commonWidgetSelector.draggableWidget(widgetName),
|
||||
data.tooltipText
|
||||
);
|
||||
|
||||
verifyWidgetColorCss(widgetName, "background-color", data.backgroundColor);
|
||||
verifyWidgetColorCss(widgetName, "color", data.textColor);
|
||||
verifyLoaderColor(widgetName, data.loaderColor);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget(widgetName)).should(
|
||||
"have.css",
|
||||
"border-radius",
|
||||
"20px"
|
||||
);
|
||||
|
||||
verifyBoxShadowCss(widgetName, data.boxShadowColor, data.boxShadowParam);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
import { faker } from "@faker-js/faker";
|
||||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import {
|
||||
commonWidgetText,
|
||||
commonText,
|
||||
codeMirrorInputLabel,
|
||||
} from "Texts/common";
|
||||
import { commonWidgetSelector } from "Selectors/common";
|
||||
import { codeMirrorInputLabel, commonWidgetText } from "Texts/common";
|
||||
|
||||
export const openAccordion = (
|
||||
accordionName,
|
||||
|
|
@ -50,6 +46,7 @@ export const verifyAndModifyToggleFx = (
|
|||
"have.text",
|
||||
paramName
|
||||
);
|
||||
cy.get(commonWidgetSelector.parameterTogglebutton(paramName)).realHover();
|
||||
cy.get(commonWidgetSelector.parameterFxButton(paramName, " > svg")).click();
|
||||
if (defaultValue)
|
||||
cy.get(commonWidgetSelector.parameterInputField(paramName))
|
||||
|
|
@ -80,11 +77,11 @@ export const addAndVerifyTooltip = (widgetSelector, message) => {
|
|||
|
||||
export const editAndVerifyWidgetName = (
|
||||
name,
|
||||
accordion = ["General", "Properties", "Layout"]
|
||||
accordion = ["General", "Properties", "Devices"]
|
||||
) => {
|
||||
closeAccordions(accordion);
|
||||
cy.clearAndType(commonWidgetSelector.WidgetNameInputField, name);
|
||||
cy.get(commonWidgetSelector.buttonCloseEditorSideBar).click();
|
||||
cy.get(commonWidgetSelector.buttonCloseEditorSideBar).click({ force: true });
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget(name)).trigger("mouseover");
|
||||
cy.get(commonWidgetSelector.widgetConfigHandle(name))
|
||||
|
|
@ -129,9 +126,14 @@ export const selectColourFromColourPicker = (
|
|||
paramName,
|
||||
colour,
|
||||
index = 0,
|
||||
parent = commonWidgetSelector.colourPickerParent
|
||||
parent = commonWidgetSelector.colourPickerParent,
|
||||
hasIndex = false
|
||||
) => {
|
||||
cy.get(commonWidgetSelector.stylePicker(paramName)).click();
|
||||
if (hasIndex === false) {
|
||||
cy.get(commonWidgetSelector.stylePicker(paramName)).last().click();
|
||||
} else {
|
||||
cy.get(commonWidgetSelector.stylePicker(paramName)).eq(hasIndex).click();
|
||||
}
|
||||
cy.get(parent)
|
||||
.eq(index)
|
||||
.then(() => {
|
||||
|
|
@ -210,7 +212,8 @@ export const verifyAndModifyStylePickerFx = (
|
|||
defaultValue,
|
||||
value,
|
||||
index = 0,
|
||||
boxShadow = ""
|
||||
boxShadow = "",
|
||||
hasIndex = false
|
||||
) => {
|
||||
cy.get(commonWidgetSelector.parameterLabel(paramName)).should(
|
||||
"have.text",
|
||||
|
|
@ -224,8 +227,16 @@ export const verifyAndModifyStylePickerFx = (
|
|||
cy.get(commonWidgetSelector.stylePickerValue(paramName))
|
||||
.should("be.visible")
|
||||
.verifyVisibleElement("have.text", defaultValue);
|
||||
cy.get(commonWidgetSelector.parameterFxButton(paramName, " > svg")).click();
|
||||
|
||||
if (hasIndex === false) {
|
||||
cy.get(commonWidgetSelector.stylePicker(paramName)).last().realHover();
|
||||
} else {
|
||||
cy.get(commonWidgetSelector.stylePicker(paramName))
|
||||
.eq(hasIndex)
|
||||
.realHover();
|
||||
}
|
||||
|
||||
cy.get(commonWidgetSelector.parameterFxButton(paramName)).click();
|
||||
cy.get(commonWidgetSelector.stylePickerFxInput(paramName)).within(() => {
|
||||
cy.get(".CodeMirror-line")
|
||||
.should("be.visible")
|
||||
|
|
@ -274,9 +285,12 @@ export const verifyLoaderColor = (widgetName, color) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const verifyLayout = (widgetName) => {
|
||||
export const verifyLayout = (
|
||||
widgetName,
|
||||
layout = commonWidgetText.accordionLayout
|
||||
) => {
|
||||
openEditorSidebar(widgetName);
|
||||
openAccordion(commonWidgetText.accordionLayout);
|
||||
openAccordion(layout);
|
||||
verifyAndModifyToggleFx(
|
||||
commonWidgetText.parameterShowOnDesktop,
|
||||
commonWidgetText.codeMirrorLabelTrue
|
||||
|
|
@ -306,21 +320,24 @@ export const verifyStylesGeneralAccordion = (
|
|||
boxShadowParameter,
|
||||
hexColor,
|
||||
boxShadowColor,
|
||||
index = 0
|
||||
index = 0,
|
||||
boxShadowDefaultValue = commonWidgetText.boxShadowDefaultValue
|
||||
) => {
|
||||
openEditorSidebar(widgetName);
|
||||
cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click();
|
||||
// openAccordion(commonWidgetText.accordionGenaral, []);
|
||||
verifyAndModifyStylePickerFx(
|
||||
commonWidgetText.parameterBoxShadow,
|
||||
commonWidgetText.boxShadowDefaultValue,
|
||||
boxShadowDefaultValue,
|
||||
`${boxShadowParameter[0]}px ${boxShadowParameter[1]}px ${boxShadowParameter[2]}px ${boxShadowParameter[3]}px ${hexColor}`,
|
||||
0,
|
||||
"0px 0px 0px 0px "
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterFxButton(commonWidgetText.parameterBoxShadow)
|
||||
).click();
|
||||
)
|
||||
.realHover()
|
||||
.click();
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.stylePicker(commonWidgetText.parameterBoxShadow)
|
||||
|
|
@ -354,9 +371,7 @@ export const verifyTooltip = (widgetSelector, message) => {
|
|||
.trigger("mouseover", { timeout: 2000 })
|
||||
.trigger("mouseover")
|
||||
.then(() => {
|
||||
cy.get(commonWidgetSelector.tooltipLabel)
|
||||
.last()
|
||||
.should("have.text", message);
|
||||
cy.get(".tooltip-inner").last().should("have.text", message);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -397,3 +412,41 @@ export const closeAccordions = (accordionNames = [], index = "0") => {
|
|||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const selectFromSidebarDropdown = (property, option) => {
|
||||
cy.get(`[data-cy="dropdown-${property.toLowerCase().replace(/\s+/g, "-")}"]`)
|
||||
.click()
|
||||
.type(`${option}{enter}`);
|
||||
};
|
||||
|
||||
export const addValueOnInput = (property, value) => {
|
||||
cy.get(`[data-cy="${property.toLowerCase().replace(/\s+/g, "-")}-input"]`)
|
||||
.clear()
|
||||
.click()
|
||||
.type(`${value}`);
|
||||
};
|
||||
|
||||
export const verifyContainerElements = () => {
|
||||
cy.get('[data-cy="widget-accordion-container"]').verifyVisibleElement(
|
||||
"have.text",
|
||||
"container"
|
||||
);
|
||||
cy.get('[data-cy="label-padding"]').verifyVisibleElement(
|
||||
"have.text",
|
||||
"Padding"
|
||||
);
|
||||
cy.get('[data-cy="togglr-button-default"]').verifyVisibleElement(
|
||||
"have.text",
|
||||
"Default"
|
||||
);
|
||||
cy.get('[data-cy="togglr-button-none"]').verifyVisibleElement(
|
||||
"have.text",
|
||||
"None"
|
||||
);
|
||||
};
|
||||
|
||||
export const checkPaddingOfContainer = (widgetName, value, mode = "Box") => {
|
||||
cy.get(commonWidgetSelector.draggableWidget(widgetName))
|
||||
.parents(`[role=${mode}]`)
|
||||
.should("have.css", "padding", `${value}px`);
|
||||
};
|
||||
|
|
|
|||
98
cypress-tests/cypress/support/utils/dropdown.js
Normal file
98
cypress-tests/cypress/support/utils/dropdown.js
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||
import { openAccordion, openEditorSidebar } from "Support/utils/commonWidget";
|
||||
import { buttonText } from "Texts/button";
|
||||
import { commonWidgetText } from "Texts/common";
|
||||
import {
|
||||
addDefaultEventHandler,
|
||||
selectColourFromColourPicker,
|
||||
verifyAndModifyParameter,
|
||||
verifyBoxShadowCss,
|
||||
verifyLoaderColor,
|
||||
verifyPropertiesGeneralAccordion,
|
||||
verifyStylesGeneralAccordion,
|
||||
verifyTooltip,
|
||||
verifyWidgetColorCss,
|
||||
} from "Support/utils/commonWidget";
|
||||
export const selectFromDropDown = (dropdownName, option, index = 3) => {
|
||||
cy.get(`[data-cy="dropdown-input-${dropdownName.toLowerCase()}"]`).click(
|
||||
"center"
|
||||
);
|
||||
cy.wait(100);
|
||||
cy.contains(`[id*='react-select-${index}-option-']`, option).click();
|
||||
};
|
||||
|
||||
export const clearSelection = (dropdownName) => {
|
||||
cy.get(`[data-cy=dropdown-input-${dropdownName.toLowerCase()}]>>>>`)
|
||||
.eq(1)
|
||||
.click();
|
||||
};
|
||||
|
||||
export const verifySelectedOptionOnDropdown = (dropdownName, option) => {
|
||||
cy.get(`[data-cy=dropdown-input-${dropdownName.toLowerCase()}]>>>>`)
|
||||
.eq(0)
|
||||
.verifyVisibleElement("have.text", option);
|
||||
};
|
||||
|
||||
export const verifyOptionOnSidePanel = (option) => {
|
||||
cy.get(
|
||||
`[data-cy="options-label-${option.toLowerCase()}"]`
|
||||
).verifyVisibleElement("have.text", option);
|
||||
};
|
||||
|
||||
export const deleteOption = (option) => {
|
||||
cy.get(`[data-cy="options-label-${option.toLowerCase()}"]`).realHover();
|
||||
cy.get(
|
||||
`[data-cy="options-${option.toLowerCase()}-delete-icon"]>span`
|
||||
).click();
|
||||
cy.notVisible(`[data-cy="options-label-${option.toLowerCase()}"]`);
|
||||
};
|
||||
|
||||
export const addNewOption = () => {
|
||||
cy.get('[data-cy="add-new-dropdown-option"]').click();
|
||||
};
|
||||
|
||||
export const updateOptionLabelAndValue = (option, label, value) => {
|
||||
cy.get(`[data-cy="options-label-${option.toLowerCase()}"]`).click();
|
||||
cy.get(`[data-cy="option-label-input-field"]`).clearAndTypeOnCodeMirror(
|
||||
label
|
||||
);
|
||||
cy.get(`[data-cy="option-value-input-field"]`).clearAndTypeOnCodeMirror(
|
||||
value
|
||||
);
|
||||
};
|
||||
|
||||
export const verifyOptionOnDropdown = (dropdownName, options) => {
|
||||
cy.get(`[data-cy="dropdown-input-${dropdownName.toLowerCase()}"]`).click(
|
||||
"center"
|
||||
);
|
||||
options.forEach((option, i) => {
|
||||
cy.get(`#react-select-3-option-${i} > .d-flex`).verifyVisibleElement(
|
||||
"have.text",
|
||||
option
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const verifyOptionMenuElements = (option, options) => {
|
||||
cy.get(`[data-cy="options-label-${option.toLowerCase()}"]`).click();
|
||||
|
||||
cy.get(`[data-cy="label-option-label"]`).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Option label"
|
||||
);
|
||||
cy.get(`[data-cy="label-option-value"]`).verifyVisibleElement(
|
||||
"have.text",
|
||||
"Option value"
|
||||
);
|
||||
|
||||
cy.get('[data-cy="label-mark-this-as-default-option"]').verifyVisibleElement(
|
||||
"have.text",
|
||||
"Mark this as default option"
|
||||
);
|
||||
cy.get('[data-cy="label-visibility"]')
|
||||
.eq(1)
|
||||
.verifyVisibleElement("have.text", "Visibility");
|
||||
cy.get('[data-cy="label-disable"]')
|
||||
.eq(1)
|
||||
.verifyVisibleElement("have.text", "Disable");
|
||||
};
|
||||
176
cypress-tests/cypress/support/utils/editor/inputFieldUtils.js
Normal file
176
cypress-tests/cypress/support/utils/editor/inputFieldUtils.js
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
import { commonWidgetSelector } from "Selectors/common";
|
||||
import {
|
||||
addAndVerifyTooltip,
|
||||
openAccordion,
|
||||
openEditorSidebar,
|
||||
selectColourFromColourPicker,
|
||||
verifyAndModifyParameter,
|
||||
verifyAndModifyToggleFx,
|
||||
verifyWidgetColorCss,
|
||||
} from "Support/utils/commonWidget";
|
||||
import { commonWidgetText, customValidation } from "Texts/common";
|
||||
import { textInputText } from "Texts/textInput";
|
||||
|
||||
export const addValidations = (
|
||||
widgetName,
|
||||
data,
|
||||
min = commonWidgetText.labelMinLength,
|
||||
max = commonWidgetText.labelMaxLength
|
||||
) => {
|
||||
openEditorSidebar(widgetName);
|
||||
openAccordion(commonWidgetText.accordionValidation);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelRegex,
|
||||
commonWidgetText.regularExpression
|
||||
);
|
||||
verifyAndModifyParameter(min, data.minimumLength);
|
||||
verifyAndModifyParameter(max, data.maximumLength);
|
||||
verifyAndModifyParameter(
|
||||
commonWidgetText.labelcustomValidadtion,
|
||||
customValidation(data.widgetName, data.customText)
|
||||
);
|
||||
verifyAndModifyToggleFx("Make this field mandatory", "");
|
||||
};
|
||||
|
||||
export const addAndVerifyAdditionalActions = (widgetName, tooltipText) => {
|
||||
openEditorSidebar(widgetName);
|
||||
openAccordion("Additional Actions");
|
||||
verifyAndModifyToggleFx(
|
||||
commonWidgetText.parameterVisibility,
|
||||
commonWidgetText.codeMirrorLabelTrue
|
||||
);
|
||||
cy.get(commonWidgetSelector.draggableWidget(widgetName)).should(
|
||||
"not.be.visible"
|
||||
);
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterTogglebutton(
|
||||
commonWidgetText.parameterVisibility
|
||||
)
|
||||
).click();
|
||||
|
||||
verifyAndModifyToggleFx(
|
||||
commonWidgetText.parameterDisable,
|
||||
commonWidgetText.codeMirrorLabelFalse
|
||||
);
|
||||
cy.waitForAutoSave();
|
||||
cy.get(commonWidgetSelector.draggableWidget(widgetName)).should(
|
||||
"have.attr",
|
||||
"disabled"
|
||||
);
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterTogglebutton(
|
||||
commonWidgetText.parameterDisable
|
||||
)
|
||||
).click();
|
||||
|
||||
verifyAndModifyToggleFx(
|
||||
commonWidgetText.loadingState,
|
||||
commonWidgetText.codeMirrorLabelFalse
|
||||
);
|
||||
cy.get(commonWidgetSelector.draggableWidget(widgetName))
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get(".tj-widget-loader").should("be.visible");
|
||||
});
|
||||
|
||||
cy.get(
|
||||
commonWidgetSelector.parameterTogglebutton(commonWidgetText.loadingState)
|
||||
).click();
|
||||
|
||||
addAndVerifyTooltip(
|
||||
commonWidgetSelector.draggableWidget(widgetName),
|
||||
tooltipText
|
||||
);
|
||||
};
|
||||
|
||||
export const addAllInputFieldColors = (data) => {
|
||||
selectColourFromColourPicker("Background", data.bgColor);
|
||||
selectColourFromColourPicker("Border", data.borderColor);
|
||||
selectColourFromColourPicker("Text", data.textColor);
|
||||
selectColourFromColourPicker("Error text", data.errorTextColor);
|
||||
selectColourFromColourPicker("", data.iconColor);
|
||||
cy.forceClickOnCanvas();
|
||||
openEditorSidebar(data.widgetName);
|
||||
cy.get('[data-cy="make-this-field-mandatory-toggle-button"]').click();
|
||||
cy.get(commonWidgetSelector.buttonStylesEditorSideBar).click();
|
||||
};
|
||||
|
||||
export const verifyInputFieldColors = (selectorInput, data) => {
|
||||
verifyWidgetColorCss(selectorInput, "color", data.textColor);
|
||||
verifyWidgetColorCss(selectorInput, "border-color", data.borderColor);
|
||||
verifyWidgetColorCss(selectorInput, "background-color", data.bgColor);
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).clear();
|
||||
cy.forceClickOnCanvas();
|
||||
cy.verifyCssProperty(
|
||||
`[data-cy="${data.widgetName}-invalid-feedback"]`,
|
||||
"color",
|
||||
`rgba(${data.errorTextColor[0]}, ${data.errorTextColor[1]}, ${
|
||||
data.errorTextColor[2]
|
||||
}, ${data.errorTextColor[3] / 100})`
|
||||
);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName))
|
||||
.siblings("svg")
|
||||
.should(
|
||||
"have.css",
|
||||
"stroke",
|
||||
`rgba(${data.iconColor[0]}, ${data.iconColor[1]}, ${data.iconColor[2]}, ${
|
||||
data.iconColor[3] / 100
|
||||
})`
|
||||
);
|
||||
};
|
||||
|
||||
export const verifyLabelStyleElements = () => {
|
||||
cy.get('[data-cy="widget-accordion-label"]').verifyVisibleElement(
|
||||
"have.text",
|
||||
"label"
|
||||
);
|
||||
cy.get('[data-cy="label-alignment"]').verifyVisibleElement(
|
||||
"have.text",
|
||||
"Alignment"
|
||||
);
|
||||
cy.get('[data-cy="label-width"]').verifyVisibleElement("have.text", "Width");
|
||||
cy.get('[data-cy="width-input-field"]')
|
||||
.eq(0)
|
||||
.should("have.value", "33")
|
||||
.siblings("label")
|
||||
.should("have.text", "% of the field");
|
||||
cy.get('[data-cy="auto-width-label"]').verifyVisibleElement(
|
||||
"have.text",
|
||||
"Auto width"
|
||||
);
|
||||
};
|
||||
|
||||
export const verifyAlignment = (componentName, position, side) => {
|
||||
const alignments = {
|
||||
topLeft: { y: "flex-column", x: "flex-start" },
|
||||
topRight: { y: "flex-column", x: "flex-end" },
|
||||
sideLeft: { y: "align-items-center", x: "flex-start" },
|
||||
sideRight: { y: "align-items-center", x: "flex-end" },
|
||||
};
|
||||
|
||||
const { y, x } = alignments[position];
|
||||
|
||||
cy.get(`[data-cy="label-${componentName.toLowerCase()}"]`)
|
||||
.should("have.class", y)
|
||||
.children("label")
|
||||
.should("have.css", "justify-content", x);
|
||||
};
|
||||
|
||||
export const verifyCustomWidthOfLabel = (componentName, width) => {
|
||||
cy.get(`[data-cy="label-${componentName.toLowerCase()}"]`)
|
||||
.children("label")
|
||||
.should("have.attr", "style")
|
||||
.and("include", `width: ${width}%`);
|
||||
//
|
||||
// .should("have.css", "width", `${width}%`);
|
||||
};
|
||||
|
||||
export const addCustomWidthOfLabel = (width) => {
|
||||
cy.get('[data-cy="auto-width-checkbox"]').click();
|
||||
cy.get('[data-cy="width-input-field"]')
|
||||
.eq(0)
|
||||
.type(`{selectAll}{backspace}${width}`, { force: true });
|
||||
};
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
import { faker } from "@faker-js/faker";
|
||||
import { commonWidgetSelector } from "Selectors/common";
|
||||
import { openAccordion, openEditorSidebar } from "Support/utils/commonWidget";
|
||||
import { buttonText } from "Texts/button";
|
||||
import { commonWidgetText } from "Texts/common";
|
||||
|
||||
import {
|
||||
addSupportCSAData,
|
||||
selectCSA,
|
||||
selectEvent,
|
||||
} from "Support/utils/events";
|
||||
|
||||
export const verifyControlComponentAction = (widgetName, value) => {
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget("button", 340, 90);
|
||||
|
||||
openEditorSidebar(widgetName);
|
||||
openAccordion(commonWidgetText.accordionEvents, ["Validation", "Devices"]);
|
||||
|
||||
cy.get(commonWidgetSelector.addMoreEventHandlerLink).click();
|
||||
cy.get(commonWidgetSelector.eventHandlerCard).eq(1).click();
|
||||
|
||||
cy.get(commonWidgetSelector.actionSelection).type("Control component{Enter}");
|
||||
cy.get(commonWidgetSelector.eventComponentSelection).type("button1{Enter}");
|
||||
cy.get(commonWidgetSelector.eventComponentActionSelection).type(
|
||||
"Set text{Enter}"
|
||||
);
|
||||
cy.get(commonWidgetSelector.componentTextInput)
|
||||
.find('[data-cy*="-input-field"]')
|
||||
.clearAndTypeOnCodeMirror(["{{", `components.${widgetName}.value}}`]);
|
||||
|
||||
cy.clearAndType(commonWidgetSelector.draggableWidget(widgetName), value);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(buttonText.defaultWidgetName)
|
||||
).should("have.text", value);
|
||||
};
|
||||
|
||||
export const randomString = (length) => {
|
||||
let str = faker.lorem.words();
|
||||
return str.replace(/\s/g, "").substr(0, length);
|
||||
};
|
||||
|
||||
export const verifyCSA = (data) => {
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget("textinput1"),
|
||||
data.customText
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName)
|
||||
).verifyVisibleElement("have.value", data.customText);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button2")).click();
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName)
|
||||
).verifyVisibleElement("have.value", "");
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button5")).click();
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName))
|
||||
.should("have.focus")
|
||||
.realType(String(data.customText));
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName)
|
||||
).verifyVisibleElement("have.value", data.customText);
|
||||
cy.get(commonWidgetSelector.draggableWidget("button4")).click();
|
||||
cy.realType("not working123");
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(data.widgetName)
|
||||
).verifyVisibleElement("have.value", data.customText);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button6")).click();
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName))
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get(".tj-widget-loader").should("be.visible");
|
||||
});
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button3")).click();
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName))
|
||||
.parent()
|
||||
.should("have.attr", "data-disabled", "true");
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button1")).click();
|
||||
cy.get(commonWidgetSelector.draggableWidget(data.widgetName)).should(
|
||||
"not.be.visible"
|
||||
);
|
||||
};
|
||||
|
||||
export const addCSA = (data) => {
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 50, 500);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA(data.widgetName, "Set visibility");
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget("Text input", 50, 50);
|
||||
selectEvent("On change", "Control Component");
|
||||
cy.wait(500);
|
||||
selectCSA(data.widgetName, "Set text", "500");
|
||||
cy.wait(500);
|
||||
addSupportCSAData("text", `{{components.textinput1.value`);
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 150, 400);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA(data.widgetName, "Clear", "500");
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 250, 400);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA(data.widgetName, "Set disable", "500");
|
||||
cy.wait(500);
|
||||
cy.get('[data-cy="event-Value-fx-button"]').click();
|
||||
cy.get('[data-cy="event-Value-input-field"]').clearAndTypeOnCodeMirror(
|
||||
"{{true"
|
||||
);
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 350, 400);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA(data.widgetName, "Set blur", "500");
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 450, 400);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA(data.widgetName, "Set focus");
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 300, 300);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA(data.widgetName, "Set loading", "500");
|
||||
cy.wait(500);
|
||||
cy.get('[data-cy="event-Value-fx-button"]').click();
|
||||
cy.get('[data-cy="event-Value-input-field"]').clearAndTypeOnCodeMirror(
|
||||
"{{true"
|
||||
);
|
||||
};
|
||||
5
cypress-tests/cypress/support/utils/editor/text.js
Normal file
5
cypress-tests/cypress/support/utils/editor/text.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export const verifyBasicStyles = (data) => {
|
||||
verifyWidgetColorCss(selectorInput, "border-color", data.borderColor);
|
||||
verifyWidgetColorCss(selectorInput, "background-color", data.bgColor);
|
||||
verifyWidgetColorCss(selectorInput, "color", data.textColor);
|
||||
};
|
||||
159
cypress-tests/cypress/support/utils/editor/textInput.js
Normal file
159
cypress-tests/cypress/support/utils/editor/textInput.js
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
import { faker } from "@faker-js/faker";
|
||||
import { commonWidgetSelector } from "Selectors/common";
|
||||
import { openAccordion, openEditorSidebar } from "Support/utils/commonWidget";
|
||||
import { buttonText } from "Texts/button";
|
||||
import { commonWidgetText } from "Texts/common";
|
||||
|
||||
import {
|
||||
addSupportCSAData,
|
||||
selectCSA,
|
||||
selectEvent,
|
||||
} from "Support/utils/events";
|
||||
|
||||
export const verifyControlComponentAction = (widgetName, value) => {
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget("button", 340, 90);
|
||||
|
||||
openEditorSidebar(widgetName);
|
||||
openAccordion(commonWidgetText.accordionEvents, ["Validation", "Devices"]);
|
||||
|
||||
cy.get(commonWidgetSelector.addMoreEventHandlerLink).click();
|
||||
cy.get(commonWidgetSelector.eventHandlerCard).eq(1).click();
|
||||
|
||||
cy.get(commonWidgetSelector.actionSelection).type("Control component{Enter}");
|
||||
cy.get(commonWidgetSelector.eventComponentSelection).type("button1{Enter}");
|
||||
cy.get(commonWidgetSelector.eventComponentActionSelection).type(
|
||||
"Set text{Enter}"
|
||||
);
|
||||
cy.get(commonWidgetSelector.componentTextInput)
|
||||
.find('[data-cy*="-input-field"]')
|
||||
.clearAndTypeOnCodeMirror(["{{", `components.${widgetName}.value}}`]);
|
||||
|
||||
cy.clearAndType(commonWidgetSelector.draggableWidget(widgetName), value);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(buttonText.defaultWidgetName)
|
||||
).should("have.text", value);
|
||||
};
|
||||
|
||||
export const randomString = (length) => {
|
||||
let str = faker.lorem.words();
|
||||
return str.replace(/\s/g, "").substr(0, length);
|
||||
};
|
||||
|
||||
export const verifyCSA = (data) => {
|
||||
cy.clearAndType(
|
||||
commonWidgetSelector.draggableWidget("textinput2"),
|
||||
data.customText
|
||||
);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget("textinput1")
|
||||
).verifyVisibleElement("have.value", data.customText);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button2")).click();
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget("textinput1")
|
||||
).verifyVisibleElement("have.value", "");
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button5")).click();
|
||||
cy.realType(data.customText);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget("textinput1")
|
||||
).verifyVisibleElement("have.value", data.customText);
|
||||
cy.get(commonWidgetSelector.draggableWidget("button4")).click();
|
||||
cy.realType("not working");
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget("textinput1")
|
||||
).verifyVisibleElement("have.value", data.customText);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button3")).click();
|
||||
cy.get(commonWidgetSelector.draggableWidget("textinput1"))
|
||||
.parent()
|
||||
.should("have.attr", "data-disabled", "true");
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button1")).click();
|
||||
cy.get(commonWidgetSelector.draggableWidget("textinput1")).should(
|
||||
"not.be.visible"
|
||||
);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button7")).click();
|
||||
cy.get(commonWidgetSelector.draggableWidget("textinput1")).should(
|
||||
"be.visible"
|
||||
);
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button6")).click();
|
||||
cy.get(commonWidgetSelector.draggableWidget("textinput1"))
|
||||
.parent()
|
||||
.should("not.have.attr", "data-disabled", "true");
|
||||
|
||||
cy.get(commonWidgetSelector.draggableWidget("button8")).click();
|
||||
cy.get(commonWidgetSelector.draggableWidget("textinput1"))
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get(".tj-widget-loader").should("be.visible");
|
||||
});
|
||||
};
|
||||
|
||||
export const addCSA = (data) => {
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 50, 500);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA("textinput1", "Visibility");
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget("Text input", 50, 50);
|
||||
selectEvent("On change", "Control Component");
|
||||
cy.wait(500);
|
||||
selectCSA("textinput1", "Set text", "500");
|
||||
cy.wait(500);
|
||||
addSupportCSAData("text", "{{components.textinput2.value");
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 150, 400);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA("textinput1", "Clear", "500");
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 250, 400);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA("textinput1", "Disable", "500");
|
||||
cy.wait(500);
|
||||
cy.get('[data-cy="event-Value-fx-button"]').click();
|
||||
cy.get('[data-cy="event-Value-input-field"]').clearAndTypeOnCodeMirror(
|
||||
"{{true"
|
||||
);
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 350, 400);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA("textinput1", "Set blur", "500");
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 450, 400);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA("textinput1", "Set focus");
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 550, 400);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA("textinput1", "Set disable", "500");
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 650, 400);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA("textinput1", "Set visibility", "500");
|
||||
cy.wait(500);
|
||||
cy.get('[data-cy="event-Value-fx-button"]').click();
|
||||
cy.get('[data-cy="event-Value-input-field"]').clearAndTypeOnCodeMirror(
|
||||
"{{true"
|
||||
);
|
||||
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget(buttonText.defaultWidgetText, 300, 300);
|
||||
selectEvent("On click", "Control Component");
|
||||
selectCSA("textinput1", "Set loading", "500");
|
||||
cy.wait(500);
|
||||
cy.get('[data-cy="event-Value-fx-button"]').click();
|
||||
cy.get('[data-cy="event-Value-input-field"]').clearAndTypeOnCodeMirror(
|
||||
"{{true"
|
||||
);
|
||||
};
|
||||
|
|
@ -5,6 +5,7 @@ export const selectEvent = (
|
|||
addEventhandlerSelector = '[data-cy="add-event-handler"]',
|
||||
eventIndex = 0
|
||||
) => {
|
||||
cy.intercept("PUT", "events").as("events");
|
||||
cy.get(addEventhandlerSelector).eq(index).click();
|
||||
cy.get('[data-cy="event-handler"]').eq(eventIndex).click();
|
||||
cy.get('[data-cy="event-selection"]')
|
||||
|
|
@ -15,6 +16,7 @@ export const selectEvent = (
|
|||
.click()
|
||||
.find("input")
|
||||
.type(`{selectAll}{backspace}${action}{enter}`);
|
||||
cy.wait("@events");
|
||||
};
|
||||
|
||||
export const selectCSA = (
|
||||
|
|
@ -22,6 +24,7 @@ export const selectCSA = (
|
|||
componentAction,
|
||||
debounce = `{selectAll}{backspace}`
|
||||
) => {
|
||||
cy.intercept("PUT", "events").as("events");
|
||||
cy.get('[data-cy="action-options-component-selection-field"]')
|
||||
.click()
|
||||
.find("input")
|
||||
|
|
@ -30,19 +33,37 @@ export const selectCSA = (
|
|||
.click()
|
||||
.find("input")
|
||||
.type(`{selectAll}{backspace}${componentAction}{enter}`);
|
||||
cy.get('[data-cy="-input-field"]')
|
||||
cy.wait("@events");
|
||||
cy.get('[data-cy="debounce-input-field"]')
|
||||
.eq(1)
|
||||
.click()
|
||||
.type(`{selectAll}{backspace}${debounce}{enter}`);
|
||||
cy.wait("@events");
|
||||
};
|
||||
|
||||
export const addSupportCSAData = (field, data) => {
|
||||
cy.get(`[data-cy="${field}-input-field"]`).clearAndTypeOnCodeMirror(data);
|
||||
cy.intercept("PUT", "events").as("events");
|
||||
cy.get(`[data-cy="event-${field}-input-field"]`)
|
||||
.click({ force: true })
|
||||
.clearAndTypeOnCodeMirror(data);
|
||||
};
|
||||
|
||||
export const selectSupportCSAData = (option) => {
|
||||
cy.intercept("PUT", "events").as("events");
|
||||
cy.get('[data-cy="action-options-action-selection-field"]')
|
||||
.eq(1)
|
||||
.click()
|
||||
.find("input")
|
||||
.type(`{selectAll}{backspace}${option}{enter}`);
|
||||
cy.wait("@events");
|
||||
};
|
||||
|
||||
export const changeEventType = (event, eventIndex = 0) => {
|
||||
cy.intercept("PUT", "events").as("events");
|
||||
cy.get('[data-cy="event-handler"]').eq(eventIndex).click();
|
||||
cy.get('[data-cy="event-selection"]')
|
||||
.click()
|
||||
.find("input")
|
||||
.type(`{selectAll}{backspace}${event}{enter}`);
|
||||
cy.wait("@events");
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,11 +24,29 @@ export const verifyValue = (node, type, children, index = 0) => {
|
|||
cy.get(`[data-cy="inspector-node-${node.toLowerCase()}"] > .mx-2`)
|
||||
.eq(index)
|
||||
.realHover()
|
||||
.verifyVisibleElement("have.text", `${children}`);
|
||||
.verifyVisibleElement("contain.text", `${children}`);
|
||||
cy.get(`[data-cy="inspector-node-${node.toLowerCase()}"] > .node-key`)
|
||||
.eq(index)
|
||||
.verifyVisibleElement("have.text", node);
|
||||
.verifyVisibleElement("contain.text", node);
|
||||
cy.get(`[data-cy="inspector-node-${node.toLowerCase()}"] > .mx-1`)
|
||||
.eq(index)
|
||||
.verifyVisibleElement("have.text", type);
|
||||
.verifyVisibleElement("contain.text", type);
|
||||
};
|
||||
export const deleteComponentFromInspector = (node) => {
|
||||
cy.get('[data-cy="inspector-node-components"] > .node-key').click();
|
||||
cy.get(`[data-cy="inspector-node-${node}"] > .node-key`).click();
|
||||
cy.get('[style="height: 13px; width: 13px;"] > img').click();
|
||||
};
|
||||
|
||||
export const verifyfunctions = (node, type, index = 0) => {
|
||||
cy.get(`[data-cy="inspector-node-${node.toLowerCase()}"] > .mx-1`)
|
||||
.eq(index)
|
||||
.realHover()
|
||||
.verifyVisibleElement("contain.text", `${type}`);
|
||||
cy.get(`[data-cy="inspector-node-${node.toLowerCase()}"] > .node-key`)
|
||||
.eq(index)
|
||||
.verifyVisibleElement("contain.text", node);
|
||||
// cy.get(`[data-cy="inspector-node-${node.toLowerCase()}"] > .mx-1`)
|
||||
// .eq(index)
|
||||
// .verifyVisibleElement("contain.text", type);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { multipageSelector } from "Selectors/multipage";
|
||||
import { commonSelectors } from "../../constants/selectors/common";
|
||||
|
||||
export const searchPage = (pageName) => {
|
||||
cy.get('[title="Search"]').click();
|
||||
|
|
@ -21,10 +22,17 @@ export const modifyPageHandle = (pageName, handle) => {
|
|||
};
|
||||
|
||||
export const detetePage = (pageName) => {
|
||||
cy.get(`[data-cy="pages-name-${pageName.toLowerCase()}"]`).click();
|
||||
cy.get(multipageSelector.pageMenuIcon).click();
|
||||
cy.get(`[data-cy="pages-name-${pageName.toLowerCase()}"]`)
|
||||
.click()
|
||||
.parent()
|
||||
.find(multipageSelector.pageMenuIcon)
|
||||
.click();
|
||||
cy.get(multipageSelector.deletePageOptionButton).click();
|
||||
cy.get(multipageSelector.modalConfirmButton).click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
`${pageName} page deleted.`
|
||||
);
|
||||
cy.notVisible(`[data-cy="pages-name-${pageName.toLowerCase()}"]`);
|
||||
};
|
||||
|
||||
|
|
@ -73,3 +81,12 @@ export const hideOrUnhidePageMenu = () => {
|
|||
cy.get(multipageSelector.pagesMenuIcon).click();
|
||||
cy.get(multipageSelector.disableMenuToggle).click();
|
||||
};
|
||||
|
||||
export const disableOrEnablePage = (pageName, option = "disable") => {
|
||||
cy.get(`[data-cy="pages-name-${pageName.toLowerCase()}"]`)
|
||||
.click()
|
||||
.parent()
|
||||
.find(multipageSelector.pageMenuIcon)
|
||||
.click();
|
||||
cy.get(`[data-cy="${option}-option-button"]`).click();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { postgreSqlSelector } from "Selectors/postgreSql";
|
||||
import { selectEvent } from "Support/utils/events";
|
||||
|
||||
export const selectQueryFromLandingPage = (dbName, label) => {
|
||||
cy.get(
|
||||
|
|
@ -11,7 +12,7 @@ export const selectQueryFromLandingPage = (dbName, label) => {
|
|||
|
||||
export const deleteQuery = (queryName) => {
|
||||
cy.get(`[data-cy="list-query-${queryName}"]`).realHover();
|
||||
cy.get(`[data-cy="elete-query-${queryName}"]`).click();
|
||||
cy.get(`[data-cy="delete-query-${queryName}"]`).click();
|
||||
};
|
||||
|
||||
export const query = (action) => {
|
||||
|
|
@ -43,3 +44,19 @@ export const waitForQueryAction = (action) => {
|
|||
"button-loading"
|
||||
);
|
||||
};
|
||||
|
||||
export const chainQuery = (currentQuery, trigger) => {
|
||||
cy.get(`[data-cy="list-query-${currentQuery}"]`).click();
|
||||
selectEvent("Query Success", "Run Query");
|
||||
cy.get('[data-cy="query-selection-field"]')
|
||||
.click()
|
||||
.find("input")
|
||||
.type(`{selectAll}{backspace}${trigger}{enter}`);
|
||||
};
|
||||
|
||||
export const addSuccessNotification = (notification) => {
|
||||
changeQueryToggles("notification-on-success");
|
||||
cy.get('[data-cy="success-message-input-field"]').clearAndTypeOnCodeMirror(
|
||||
notification
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
import { commonWidgetSelector } from "Selectors/common";
|
||||
import { openAccordion, openEditorSidebar } from "Support/utils/commonWidget";
|
||||
import { buttonText } from "Texts/button";
|
||||
import { commonWidgetText } from "Texts/common";
|
||||
import { faker } from "@faker-js/faker";
|
||||
|
||||
export const verifyControlComponentAction = (widgetName, value) => {
|
||||
cy.forceClickOnCanvas();
|
||||
cy.dragAndDropWidget("button", 340, 90);
|
||||
|
||||
openEditorSidebar(widgetName);
|
||||
openAccordion(commonWidgetText.accordionEvents, ["Validation", "Layout"]);
|
||||
|
||||
cy.get(commonWidgetSelector.addMoreEventHandlerLink).click();
|
||||
cy.get(commonWidgetSelector.eventHandlerCard).eq(1).click();
|
||||
|
||||
cy.get(commonWidgetSelector.actionSelection).type("Control component{Enter}");
|
||||
cy.get(commonWidgetSelector.eventComponentSelection).type("button1{Enter}");
|
||||
cy.get(commonWidgetSelector.eventComponentActionSelection).type(
|
||||
"Set text{Enter}"
|
||||
);
|
||||
cy.get(commonWidgetSelector.componentTextInput)
|
||||
.find('[data-cy*="-input-field"]')
|
||||
.clearAndTypeOnCodeMirror(["{{", `components.${widgetName}.value}}`]);
|
||||
|
||||
cy.clearAndType(commonWidgetSelector.draggableWidget(widgetName), value);
|
||||
cy.get(
|
||||
commonWidgetSelector.draggableWidget(buttonText.defaultWidgetName)
|
||||
).should("have.text", value);
|
||||
};
|
||||
|
||||
export const randomString = (length) => {
|
||||
let str = faker.lorem.words();
|
||||
return str.replace(/\s/g, "").substr(0, length);
|
||||
};
|
||||
|
|
@ -3,7 +3,6 @@ import { DocsCard } from './';
|
|||
import styles from './DocsCard.css'
|
||||
|
||||
export const DocsCardList = ({ list }) => {
|
||||
console.log('list', list);
|
||||
return (
|
||||
<div className='card-container-setup'>
|
||||
{list.map(item => <DocsCard key={item.docId} label={item.label} imgSrc={item.docId.split('/')[1]} link={item.href} />)}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
2.28.4
|
||||
2.29.0
|
||||
|
|
|
|||
|
|
@ -495,6 +495,7 @@
|
|||
"properties": "Properties",
|
||||
"events": "Events",
|
||||
"layout": "Layout",
|
||||
"devices":"Devices",
|
||||
"styles": "Styles",
|
||||
"general": "General",
|
||||
"validation": "Validation",
|
||||
|
|
@ -507,6 +508,7 @@
|
|||
"visibility": "Visibility",
|
||||
"disable": "Disable",
|
||||
"borderRadius": "Border radius",
|
||||
"transformation": "Transformation",
|
||||
"boxShadow": "Box shadow",
|
||||
"tooltip": "Tooltip",
|
||||
"showOnDesktop": "Show on desktop",
|
||||
|
|
|
|||
22360
frontend/package-lock.json
generated
22360
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -10,11 +10,12 @@
|
|||
"@emoji-mart/react": "^1.1.1",
|
||||
"@radix-ui/colors": "^0.1.8",
|
||||
"@radix-ui/react-popover": "^1.0.3",
|
||||
"@radix-ui/react-slider": "^1.1.2",
|
||||
"@radix-ui/react-toggle-group": "^1.0.4",
|
||||
"@react-google-maps/api": "^2.18.1",
|
||||
"@sentry/react": "^7.60.0",
|
||||
"@sentry/tracing": "^7.60.0",
|
||||
"@sentry/webpack-plugin": "^2.5.0",
|
||||
"@sentry/react": "^7.100.1",
|
||||
"@sentry/tracing": "^7.100.1",
|
||||
"@sentry/webpack-plugin": "^2.14.0",
|
||||
"@tabler/icons-react": "^2.4.0",
|
||||
"@tooljet/plugins": "../plugins",
|
||||
"@uiw/react-codemirror": "^3.0.6",
|
||||
|
|
@ -79,6 +80,7 @@
|
|||
"react-lazy-load-image-component": "^1.5.6",
|
||||
"react-lazyload": "^3.2.0",
|
||||
"react-loading-skeleton": "^3.1.1",
|
||||
"react-markdown": "^9.0.0",
|
||||
"react-mentions": "^4.4.7",
|
||||
"react-multi-select-component": "^4.3.4",
|
||||
"react-pdf": "^6.2.2",
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import 'react-tooltip/dist/react-tooltip.css';
|
|||
import { getWorkspaceIdOrSlugFromURL } from '@/_helpers/routes';
|
||||
import ErrorPage from '@/_components/ErrorComponents/ErrorPage';
|
||||
import WorkspaceConstants from '@/WorkspaceConstants';
|
||||
import { useAppDataStore } from '@/_stores/appDataStore';
|
||||
|
||||
const AppWrapper = (props) => {
|
||||
return (
|
||||
|
|
@ -57,6 +58,7 @@ class AppComponent extends React.Component {
|
|||
};
|
||||
fetchMetadata = () => {
|
||||
tooljetService.fetchMetaData().then((data) => {
|
||||
useAppDataStore.getState().actions.setMetadata(data);
|
||||
localStorage.setItem('currentVersion', data.installed_version);
|
||||
if (data.latest_version && lt(data.installed_version, data.latest_version) && data.version_ignored === false) {
|
||||
this.setState({ updateAvailable: true });
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { CustomSelect } from './CustomSelect';
|
|||
import { toast } from 'react-hot-toast';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { useAppVersionStore } from '@/_stores/appVersionStore';
|
||||
import { useEditorStore } from '@/_stores/editorStore';
|
||||
|
||||
const appVersionLoadingStatus = Object.freeze({
|
||||
loading: 'loading',
|
||||
|
|
@ -12,7 +13,13 @@ const appVersionLoadingStatus = Object.freeze({
|
|||
error: 'error',
|
||||
});
|
||||
|
||||
export const AppVersionsManager = function ({ appId, setAppDefinitionFromVersion, onVersionDelete }) {
|
||||
export const AppVersionsManager = function ({
|
||||
appId,
|
||||
setAppDefinitionFromVersion,
|
||||
onVersionDelete,
|
||||
isEditable = true,
|
||||
isViewer,
|
||||
}) {
|
||||
const [appVersionStatus, setGetAppVersionStatus] = useState(appVersionLoadingStatus.loading);
|
||||
const [deleteVersion, setDeleteVersion] = useState({
|
||||
versionId: '',
|
||||
|
|
@ -29,6 +36,12 @@ export const AppVersionsManager = function ({ appId, setAppDefinitionFromVersion
|
|||
}),
|
||||
shallow
|
||||
);
|
||||
const { currentLayout } = useEditorStore(
|
||||
(state) => ({
|
||||
currentLayout: state?.currentLayout,
|
||||
}),
|
||||
shallow
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (appVersions && appVersions.length > 0) {
|
||||
|
|
@ -102,7 +115,7 @@ export const AppVersionsManager = function ({ appId, setAppDefinitionFromVersion
|
|||
{appVersion.name}
|
||||
</div>
|
||||
</div>
|
||||
{appVersion.id !== releasedVersionId && (
|
||||
{isEditable && appVersion.id !== releasedVersionId && (
|
||||
<div
|
||||
className="col cursor-pointer m-auto app-version-delete"
|
||||
onClick={(e) => {
|
||||
|
|
@ -139,15 +152,32 @@ export const AppVersionsManager = function ({ appId, setAppDefinitionFromVersion
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="app-versions-selector" data-cy="app-version-selector">
|
||||
<CustomSelect
|
||||
isLoading={appVersionStatus === 'loading'}
|
||||
options={options}
|
||||
value={editingVersion.id}
|
||||
onChange={(id) => selectVersion(id)}
|
||||
{...customSelectProps}
|
||||
className={` ${darkMode && 'dark-theme'}`}
|
||||
/>
|
||||
<div
|
||||
className="d-flex align-items-center p-0"
|
||||
style={{ margin: isViewer && currentLayout === 'mobile' ? '0px' : '0 24px' }}
|
||||
>
|
||||
<div
|
||||
className={cx('d-flex version-manager-container p-0', {
|
||||
'w-100': isViewer && currentLayout === 'mobile',
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={cx('app-versions-selector', {
|
||||
'w-100': isViewer && currentLayout === 'mobile',
|
||||
})}
|
||||
data-cy="app-version-selector"
|
||||
>
|
||||
<CustomSelect
|
||||
isLoading={appVersionStatus === 'loading'}
|
||||
options={options}
|
||||
value={editingVersion?.id}
|
||||
onChange={(id) => selectVersion(id)}
|
||||
{...customSelectProps}
|
||||
className={` ${darkMode && 'dark-theme'}`}
|
||||
isEditable={isEditable}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -7,7 +7,8 @@ import { CreateVersion } from './CreateVersionModal';
|
|||
import { ConfirmDialog } from '@/_components';
|
||||
import { defaultAppEnvironments } from '@/_helpers/utils';
|
||||
|
||||
const Menu = (props) => {
|
||||
export const Menu = (props) => {
|
||||
const isEditable = props.selectProps.isEditable;
|
||||
return (
|
||||
<components.Menu {...props}>
|
||||
<div>
|
||||
|
|
@ -20,7 +21,7 @@ const Menu = (props) => {
|
|||
<div className="col-10 text-truncate tj-text-xsm color-slate12">
|
||||
{props?.selectProps?.value?.appVersionName}
|
||||
</div>
|
||||
{!props?.selectProps?.value?.isReleasedVersion && (
|
||||
{isEditable && !props?.selectProps?.value?.isReleasedVersion && (
|
||||
<div className="col-1">
|
||||
<svg
|
||||
className="icon"
|
||||
|
|
@ -44,35 +45,37 @@ const Menu = (props) => {
|
|||
</div>
|
||||
<hr className="m-0" />
|
||||
<div>{props.children}</div>
|
||||
<div
|
||||
className="cursor-pointer tj-text-xsm"
|
||||
style={{ padding: '8px 12px', color: '#3E63DD' }}
|
||||
onClick={() => props.selectProps.setShowCreateAppVersion(true)}
|
||||
>
|
||||
<svg
|
||||
className="icon me-1"
|
||||
width="34"
|
||||
height="34"
|
||||
viewBox="0 0 34 34"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{isEditable && (
|
||||
<div
|
||||
className="cursor-pointer tj-text-xsm"
|
||||
style={{ padding: '8px 12px', color: '#3E63DD' }}
|
||||
onClick={() => props.selectProps.setShowCreateAppVersion(true)}
|
||||
>
|
||||
<rect width="34" height="34" rx="6" fill="#F0F4FF" />
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M17 11C17.4142 11 17.75 11.3358 17.75 11.75V16.25H22.25C22.6642 16.25 23 16.5858 23 17C23 17.4142 22.6642 17.75 22.25 17.75H17.75V22.25C17.75 22.6642 17.4142 23 17 23C16.5858 23 16.25 22.6642 16.25 22.25V17.75H11.75C11.3358 17.75 11 17.4142 11 17C11 16.5858 11.3358 16.25 11.75 16.25H16.25V11.75C16.25 11.3358 16.5858 11 17 11Z"
|
||||
fill="#3E63DD"
|
||||
/>
|
||||
</svg>
|
||||
Create new version
|
||||
</div>
|
||||
<svg
|
||||
className="icon me-1"
|
||||
width="34"
|
||||
height="34"
|
||||
viewBox="0 0 34 34"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect width="34" height="34" rx="6" fill="#F0F4FF" />
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M17 11C17.4142 11 17.75 11.3358 17.75 11.75V16.25H22.25C22.6642 16.25 23 16.5858 23 17C23 17.4142 22.6642 17.75 22.25 17.75H17.75V22.25C17.75 22.6642 17.4142 23 17 23C16.5858 23 16.25 22.6642 16.25 22.25V17.75H11.75C11.3358 17.75 11 17.4142 11 17C11 16.5858 11.3358 16.25 11.75 16.25H16.25V11.75C16.25 11.3358 16.5858 11 17 11Z"
|
||||
fill="#3E63DD"
|
||||
/>
|
||||
</svg>
|
||||
Create new version
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</components.Menu>
|
||||
);
|
||||
};
|
||||
|
||||
const SingleValue = ({ selectProps }) => {
|
||||
export const SingleValue = ({ selectProps }) => {
|
||||
return (
|
||||
<div className="d-inline-flex align-items-center" data-cy="app-version-label" style={{ gap: '8px' }}>
|
||||
<div className="d-inline-flex align-items-center" style={{ gap: '2px' }}>
|
||||
|
|
@ -105,16 +108,15 @@ const SingleValue = ({ selectProps }) => {
|
|||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const CustomSelect = ({ ...props }) => {
|
||||
const [showEditAppVersion, setShowEditAppVersion] = useState(false);
|
||||
const [showCreateAppVersion, setShowCreateAppVersion] = useState(false);
|
||||
|
||||
const { deleteVersion, deleteAppVersion, resetDeleteModal } = props;
|
||||
const { deleteVersion, deleteAppVersion, resetDeleteModal, isEditable } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
{showCreateAppVersion && (
|
||||
{isEditable && showCreateAppVersion && (
|
||||
<CreateVersion
|
||||
{...props}
|
||||
showCreateAppVersion={showCreateAppVersion}
|
||||
|
|
@ -122,7 +124,9 @@ export const CustomSelect = ({ ...props }) => {
|
|||
/>
|
||||
)}
|
||||
|
||||
<EditVersion {...props} showEditAppVersion={showEditAppVersion} setShowEditAppVersion={setShowEditAppVersion} />
|
||||
{isEditable && (
|
||||
<EditVersion {...props} showEditAppVersion={showEditAppVersion} setShowEditAppVersion={setShowEditAppVersion} />
|
||||
)}
|
||||
{/* When we merge this code to EE update the defaultAppEnvironments object with rest of default environments (then delete this comment)*/}
|
||||
<ConfirmDialog
|
||||
show={deleteVersion.showModal}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useEffect, useState, useMemo, useContext, useRef, memo } from 'react';
|
||||
import React, { useEffect, useState, useMemo, useContext, useRef, memo, useCallback } from 'react';
|
||||
import { Button } from './Components/Button';
|
||||
import { Image } from './Components/Image';
|
||||
import { Text } from './Components/Text';
|
||||
|
|
@ -68,9 +68,8 @@ import { EditorContext } from '@/Editor/Context/EditorContextWrapper';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { useCurrentState } from '@/_stores/currentStateStore';
|
||||
import { useAppInfo } from '@/_stores/appDataStore';
|
||||
import WidgetIcon from '@/../assets/images/icons/widgets';
|
||||
|
||||
const AllComponents = {
|
||||
export const AllComponents = {
|
||||
Button,
|
||||
Image,
|
||||
Text,
|
||||
|
|
@ -149,22 +148,13 @@ export const Box = memo(
|
|||
sideBarDebugger,
|
||||
readOnly,
|
||||
childComponents,
|
||||
isResizing,
|
||||
adjustHeightBasedOnAlignment,
|
||||
currentLayout,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const backgroundColor = yellow ? 'yellow' : '';
|
||||
const currentState = useCurrentState();
|
||||
|
||||
let styles = {
|
||||
height: '100%',
|
||||
padding: '1px',
|
||||
};
|
||||
|
||||
if (inCanvas) {
|
||||
styles = {
|
||||
...styles,
|
||||
};
|
||||
}
|
||||
|
||||
const { events } = useAppInfo();
|
||||
|
||||
const componentMeta = useMemo(() => {
|
||||
|
|
@ -183,11 +173,11 @@ export const Box = memo(
|
|||
: [resolvedProperties, []];
|
||||
|
||||
const resolvedStyles = resolveStyles(component, currentState, null, customResolvables);
|
||||
|
||||
const [validatedStyles, styleErrors] =
|
||||
mode === 'edit' && component.validate
|
||||
? validateProperties(resolvedStyles, componentMeta.styles)
|
||||
: [resolvedStyles, []];
|
||||
validatedStyles.visibility = validatedStyles.visibility !== false ? true : false;
|
||||
|
||||
const resolvedGeneralProperties = resolveGeneralProperties(component, currentState, null, customResolvables);
|
||||
const [validatedGeneralProperties, generalPropertiesErrors] =
|
||||
|
|
@ -205,6 +195,15 @@ export const Box = memo(
|
|||
const darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
const { variablesExposedForPreview, exposeToCodeHinter } = useContext(EditorContext) || {};
|
||||
|
||||
let styles = {
|
||||
height: '100%',
|
||||
};
|
||||
|
||||
if (inCanvas) {
|
||||
styles = {
|
||||
...styles,
|
||||
};
|
||||
}
|
||||
useEffect(() => {
|
||||
if (!component?.parent) {
|
||||
onComponentOptionChanged && onComponentOptionChanged(component, 'id', id);
|
||||
|
|
@ -279,17 +278,29 @@ export const Box = memo(
|
|||
...{ validationObject: component.definition.validation, currentState },
|
||||
customResolveObjects: customResolvables,
|
||||
});
|
||||
|
||||
const shouldAddBoxShadow = ['TextInput', 'PasswordInput', 'NumberInput', 'Text'];
|
||||
return (
|
||||
<OverlayTrigger
|
||||
placement={inCanvas ? 'auto' : 'top'}
|
||||
delay={{ show: 500, hide: 0 }}
|
||||
trigger={inCanvas && !validatedGeneralProperties.tooltip?.toString().trim() ? null : ['hover', 'focus']}
|
||||
trigger={
|
||||
inCanvas && shouldAddBoxShadow.includes(component.component)
|
||||
? !validatedProperties.tooltip?.toString().trim()
|
||||
? null
|
||||
: ['hover', 'focus']
|
||||
: !validatedGeneralProperties.tooltip?.toString().trim()
|
||||
? null
|
||||
: ['hover', 'focus']
|
||||
}
|
||||
overlay={(props) =>
|
||||
renderTooltip({
|
||||
props,
|
||||
text: inCanvas
|
||||
? `${validatedGeneralProperties.tooltip}`
|
||||
? `${
|
||||
shouldAddBoxShadow.includes(component.component)
|
||||
? validatedProperties.tooltip
|
||||
: validatedGeneralProperties.tooltip
|
||||
}`
|
||||
: `${t(`widget.${component.name}.description`, component.description)}`,
|
||||
})
|
||||
}
|
||||
|
|
@ -298,6 +309,7 @@ export const Box = memo(
|
|||
style={{
|
||||
...styles,
|
||||
backgroundColor,
|
||||
padding: validatedStyles?.padding ? (validatedStyles?.padding == 'default' ? '1px' : '0px') : '1px',
|
||||
}}
|
||||
role={preview ? 'BoxPreview' : 'Box'}
|
||||
>
|
||||
|
|
@ -320,7 +332,12 @@ export const Box = memo(
|
|||
canvasWidth={canvasWidth}
|
||||
properties={validatedProperties}
|
||||
exposedVariables={exposedVariables}
|
||||
styles={{ ...validatedStyles, boxShadow: validatedGeneralStyles?.boxShadow }}
|
||||
styles={{
|
||||
...validatedStyles,
|
||||
...(!shouldAddBoxShadow.includes(component.component)
|
||||
? { boxShadow: validatedGeneralStyles?.boxShadow }
|
||||
: {}),
|
||||
}}
|
||||
setExposedVariable={(variable, value) => onComponentOptionChanged(component, variable, value, id)}
|
||||
setExposedVariables={(variableSet) =>
|
||||
onComponentOptionsChanged(component, Object.entries(variableSet), id)
|
||||
|
|
@ -338,6 +355,9 @@ export const Box = memo(
|
|||
resetComponent={() => setResetStatus(true)}
|
||||
childComponents={childComponents}
|
||||
dataCy={`draggable-widget-${String(component.name).toLowerCase()}`}
|
||||
isResizing={isResizing}
|
||||
adjustHeightBasedOnAlignment={adjustHeightBasedOnAlignment}
|
||||
currentLayout={currentLayout}
|
||||
></ComponentToRender>
|
||||
) : (
|
||||
<></>
|
||||
|
|
|
|||
|
|
@ -35,8 +35,17 @@ import cx from 'classnames';
|
|||
import { Alert } from '@/_ui/Alert/Alert';
|
||||
import { useCurrentState } from '@/_stores/currentStateStore';
|
||||
import ClientServerSwitch from './Elements/ClientServerSwitch';
|
||||
import { CodeHinterContext } from './CodeHinterContext';
|
||||
import Switch from './Elements/Switch';
|
||||
import Checkbox from './Elements/Checkbox';
|
||||
import Slider from './Elements/Slider';
|
||||
import { Input } from './Elements/Input';
|
||||
import { Icon } from './Elements/Icon';
|
||||
import { Visibility } from './Elements/Visibility';
|
||||
import { NumberInput } from './Elements/NumberInput';
|
||||
import { validateProperty } from '../component-properties-validation';
|
||||
const HIDDEN_CODE_HINTER_LABELS = ['Table data', 'Column data'];
|
||||
|
||||
const HIDDEN_CODE_HINTER_LABELS = ['Table data', 'Column data', 'Text Format', 'TextComponentTextInput'];
|
||||
|
||||
const AllElements = {
|
||||
Color,
|
||||
|
|
@ -47,11 +56,19 @@ const AllElements = {
|
|||
Number,
|
||||
BoxShadow,
|
||||
ClientServerSwitch,
|
||||
Slider,
|
||||
Switch,
|
||||
Input,
|
||||
Checkbox,
|
||||
Icon,
|
||||
Visibility,
|
||||
NumberInput,
|
||||
};
|
||||
|
||||
export function CodeHinter({
|
||||
initialValue,
|
||||
onChange,
|
||||
onVisibilityChange,
|
||||
mode,
|
||||
theme,
|
||||
lineNumbers,
|
||||
|
|
@ -77,8 +94,12 @@ export function CodeHinter({
|
|||
callgpt = () => null,
|
||||
isCopilotEnabled = false,
|
||||
currentState: _currentState,
|
||||
verticalLine = true,
|
||||
isIcon = false,
|
||||
inspectorTab,
|
||||
staticText,
|
||||
}) {
|
||||
const context = useContext(CodeHinterContext);
|
||||
|
||||
const darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
const options = {
|
||||
lineNumbers: lineNumbers ?? false,
|
||||
|
|
@ -92,7 +113,8 @@ export function CodeHinter({
|
|||
placeholder,
|
||||
};
|
||||
const currentState = useCurrentState();
|
||||
const [realState, setRealState] = useState(currentState);
|
||||
const [realState, setRealState] = useState({ ...currentState, ..._currentState, ...context });
|
||||
|
||||
const [currentValue, setCurrentValue] = useState('');
|
||||
|
||||
const [prevCurrentValue, setPrevCurrentValue] = useState(null);
|
||||
|
|
@ -102,6 +124,7 @@ export function CodeHinter({
|
|||
const [isFocused, setFocused] = useState(false);
|
||||
const [heightRef, currentHeight] = useHeight();
|
||||
const isPreviewFocused = useRef(false);
|
||||
const [isPropertyHovered, setPropertyHovered] = useState(false);
|
||||
const wrapperRef = useRef(null);
|
||||
|
||||
// Todo: Remove this when workspace variables are deprecated
|
||||
|
|
@ -172,13 +195,10 @@ export function CodeHinter({
|
|||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (_currentState) {
|
||||
setRealState(_currentState);
|
||||
} else {
|
||||
setRealState(currentState);
|
||||
}
|
||||
const newState = { ...currentState, ..._currentState, ...context };
|
||||
setRealState(newState);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [JSON.stringify({ currentState, _currentState })]);
|
||||
}, [JSON.stringify([currentState.components, _currentState, context])]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event) => {
|
||||
|
|
@ -199,38 +219,43 @@ export function CodeHinter({
|
|||
};
|
||||
}, [wrapperRef, isFocused, isPreviewFocused, currentValue, prevCountRef, isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
const updatePreview = () => {
|
||||
let globalPreviewCopy = null;
|
||||
let globalErrorCopy = null;
|
||||
if (enablePreview && isFocused && JSON.stringify(currentValue) !== JSON.stringify(prevCurrentValue)) {
|
||||
const [preview, error] = getPreviewAndErrorFromValue(currentValue);
|
||||
// checking type error if any in run time
|
||||
const [_valid, errorMessages] = checkTypeErrorInRunTime(preview);
|
||||
const [preview, error] = getPreviewAndErrorFromValue(currentValue);
|
||||
setPrevCurrentValue(currentValue);
|
||||
|
||||
setPrevCurrentValue(currentValue);
|
||||
if (error || !_valid || typeof preview === 'function') {
|
||||
globalPreviewCopy = null;
|
||||
globalErrorCopy = error || errorMessages?.[errorMessages?.length - 1];
|
||||
setResolvingError(error || errorMessages?.[errorMessages?.length - 1]);
|
||||
setResolvedValue(null);
|
||||
} else {
|
||||
globalPreviewCopy = preview;
|
||||
globalErrorCopy = null;
|
||||
setResolvingError(null);
|
||||
setResolvedValue(preview);
|
||||
}
|
||||
const [_valid, errorMessages] = checkTypeErrorInRunTime(preview);
|
||||
|
||||
setPrevCurrentValue(currentValue);
|
||||
if (error || !_valid || typeof preview === 'function') {
|
||||
globalPreviewCopy = null;
|
||||
globalErrorCopy = error || errorMessages?.[errorMessages?.length - 1];
|
||||
setResolvingError(error || errorMessages?.[errorMessages?.length - 1]);
|
||||
setResolvedValue(null);
|
||||
} else {
|
||||
globalPreviewCopy = preview;
|
||||
globalErrorCopy = null;
|
||||
setResolvingError(null);
|
||||
setResolvedValue(preview);
|
||||
}
|
||||
|
||||
return [globalPreviewCopy, globalErrorCopy];
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let [globalPreviewCopy, globalErrorCopy] = enablePreview ? updatePreview() : [null, null];
|
||||
|
||||
return () => {
|
||||
if (enablePreview && isFocused && JSON.stringify(currentValue) !== JSON.stringify(prevCurrentValue)) {
|
||||
if (enablePreview) {
|
||||
setPrevCurrentValue(null);
|
||||
setResolvedValue(globalPreviewCopy);
|
||||
setResolvingError(globalErrorCopy);
|
||||
}
|
||||
};
|
||||
}, [JSON.stringify({ currentValue, realState, isFocused })]);
|
||||
}, [JSON.stringify({ currentValue, realState, isFocused, context })]);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
// }, [JSON.stringify({ currentValue, realState, isFocused })]);
|
||||
// }, [JSON.stringify({ currentValue, realState, isFocused, context })]);
|
||||
|
||||
function valueChanged(editor, onChange, ignoreBraces) {
|
||||
if (editor.getValue()?.trim() !== currentValue) {
|
||||
|
|
@ -376,48 +401,74 @@ export function CodeHinter({
|
|||
className === 'query-hinter' || className === 'custom-component' || undefined ? '' : 'code-hinter';
|
||||
|
||||
const ElementToRender = AllElements[TypeMapping[type]];
|
||||
|
||||
const [forceCodeBox, setForceCodeBox] = useState(fxActive);
|
||||
const codeShow = (type ?? 'code') === 'code' || forceCodeBox;
|
||||
cyLabel = paramLabel ? paramLabel.toLowerCase().trim().replace(/\s+/g, '-') : cyLabel;
|
||||
|
||||
const fxBtn = () => (
|
||||
<div className="col-auto pt-0 fx-common">
|
||||
{!['Type', 'selectRowOnCellEdit', 'Select row on cell edit', ' ', 'Padding', 'Width'].includes(paramLabel) && ( //add some key if these extends
|
||||
<FxButton
|
||||
active={codeShow}
|
||||
onPress={() => {
|
||||
if (codeShow) {
|
||||
setForceCodeBox(false);
|
||||
onFxPress(false);
|
||||
} else {
|
||||
setForceCodeBox(true);
|
||||
onFxPress(true);
|
||||
}
|
||||
}}
|
||||
dataCy={cyLabel}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const _renderFxBtn = () => {
|
||||
if (inspectorTab === 'styles') {
|
||||
return isPropertyHovered || codeShow ? fxBtn() : null;
|
||||
} else {
|
||||
return fxBtn();
|
||||
}
|
||||
};
|
||||
|
||||
const onFocusHandler = () => {
|
||||
setFocused(true);
|
||||
updatePreview();
|
||||
};
|
||||
|
||||
return (
|
||||
<div ref={wrapperRef} className={cx({ 'codeShow-active': codeShow })}>
|
||||
<div className={cx('d-flex align-items-center justify-content-between')}>
|
||||
{paramLabel === 'Type' && <div className="field-type-vertical-line"></div>}
|
||||
<div
|
||||
ref={wrapperRef}
|
||||
className={cx({ 'codeShow-active': codeShow, 'd-flex': paramLabel == 'Tooltip' })}
|
||||
onMouseEnter={() => setPropertyHovered(true)}
|
||||
onMouseLeave={() => setPropertyHovered(false)}
|
||||
>
|
||||
<div
|
||||
className={cx('d-flex justify-content-between', { 'w-full': fieldMeta?.fullWidth })}
|
||||
style={{
|
||||
marginRight: paramLabel == 'Tooltip' && '40px',
|
||||
alignItems: paramLabel == 'Tooltip' ? 'flex-start' : 'center',
|
||||
}}
|
||||
>
|
||||
{paramLabel && !HIDDEN_CODE_HINTER_LABELS.includes(paramLabel) && (
|
||||
<div className={`field ${options.className}`} data-cy={`${cyLabel}-widget-parameter-label`}>
|
||||
<ToolTip
|
||||
label={t(`widget.commonProperties.${camelCase(paramLabel)}`, paramLabel)}
|
||||
meta={fieldMeta}
|
||||
labelClass={`tj-text-xsm color-slate12 ${codeShow ? 'mb-2' : 'mb-0'} ${
|
||||
labelClass={`tj-text-xsm color-slate12 ${codeShow ? 'label-hinter-margin' : 'mb-0'} ${
|
||||
darkMode && 'color-whitish-darkmode'
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className={`${(type ?? 'code') === 'code' ? 'd-none' : ''} `}>
|
||||
<div className={cx(`${(type ?? 'code') === 'code' ? 'd-none' : ''}`, { 'w-full': fieldMeta?.fullWidth })}>
|
||||
<div
|
||||
style={{ width: width, marginBottom: codeShow ? '0.5rem' : '0px' }}
|
||||
className="d-flex align-items-center"
|
||||
className={cx('d-flex align-items-center', { 'w-full': fieldMeta?.fullWidth })}
|
||||
>
|
||||
<div className="col-auto pt-0 fx-common">
|
||||
{paramLabel !== 'Type' && (
|
||||
<FxButton
|
||||
active={codeShow}
|
||||
onPress={() => {
|
||||
if (codeShow) {
|
||||
setForceCodeBox(false);
|
||||
onFxPress(false);
|
||||
} else {
|
||||
setForceCodeBox(true);
|
||||
onFxPress(true);
|
||||
}
|
||||
}}
|
||||
dataCy={cyLabel}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{!fieldMeta?.isFxNotRequired && _renderFxBtn()}
|
||||
{!codeShow && (
|
||||
<ElementToRender
|
||||
value={resolveReferences(initialValue, realState)}
|
||||
|
|
@ -427,6 +478,12 @@ export function CodeHinter({
|
|||
setCurrentValue(value);
|
||||
}
|
||||
}}
|
||||
onVisibilityChange={(value) => {
|
||||
if (value !== currentValue) {
|
||||
onVisibilityChange(value);
|
||||
setCurrentValue(value);
|
||||
}
|
||||
}}
|
||||
paramName={paramName}
|
||||
paramLabel={paramLabel}
|
||||
forceCodeBox={() => {
|
||||
|
|
@ -435,6 +492,9 @@ export function CodeHinter({
|
|||
}}
|
||||
meta={fieldMeta}
|
||||
cyLabel={cyLabel}
|
||||
isIcon={isIcon}
|
||||
staticText={staticText}
|
||||
component={component}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -442,15 +502,14 @@ export function CodeHinter({
|
|||
</div>
|
||||
<div
|
||||
className={`row${height === '150px' || height === '300px' ? ' tablr-gutter-x-0' : ''} custom-row`}
|
||||
style={{ width: width, display: codeShow ? 'flex' : 'none' }}
|
||||
style={{ width: paramLabel == 'Tooltip' ? '100%' : width, display: codeShow ? 'flex' : 'none' }}
|
||||
>
|
||||
<div className={`col code-hinter-col`}>
|
||||
<div className="d-flex">
|
||||
<div className={`${verticalLine && 'code-hinter-vertical-line'}`}></div>
|
||||
<div className="code-hinter-wrapper position-relative" style={{ width: '100%' }}>
|
||||
<div
|
||||
className={`${defaultClassName} ${className || 'codehinter-default-input'} ${
|
||||
resolvingError && 'border-danger'
|
||||
paramName && resolvingError && 'border-danger'
|
||||
}`}
|
||||
key={componentName}
|
||||
style={{
|
||||
|
|
@ -459,6 +518,7 @@ export function CodeHinter({
|
|||
maxHeight: '320px',
|
||||
overflow: 'auto',
|
||||
fontSize: ' .875rem',
|
||||
maxWidth: paramLabel == 'Tooltip' && '190px',
|
||||
}}
|
||||
data-cy={`${cyLabel}-input-field`}
|
||||
>
|
||||
|
|
@ -489,7 +549,7 @@ export function CodeHinter({
|
|||
realState={realState}
|
||||
scrollbarStyle={null}
|
||||
height={'100%'}
|
||||
onFocus={() => setFocused(true)}
|
||||
onFocus={onFocusHandler}
|
||||
onBlur={(editor, e) => {
|
||||
e?.stopPropagation();
|
||||
const value = editor?.getValue()?.trimEnd();
|
||||
|
|
@ -514,11 +574,6 @@ export function CodeHinter({
|
|||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function CodeHinterInputField() {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const PopupIcon = ({ callback, icon, tip, transformation = false }) => {
|
||||
const size = transformation ? 20 : 12;
|
||||
|
||||
|
|
|
|||
3
frontend/src/Editor/CodeBuilder/CodeHinterContext.js
Normal file
3
frontend/src/Editor/CodeBuilder/CodeHinterContext.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import { createContext } from 'react';
|
||||
|
||||
export const CodeHinterContext = createContext({});
|
||||
|
|
@ -187,7 +187,13 @@ export const BoxShadow = ({ value, onChange, cyLabel }) => {
|
|||
}}
|
||||
data-cy={`${cyLabel}-picker-icon`}
|
||||
></div>
|
||||
<small className="col p-0" data-cy={`${cyLabel}-value`}>
|
||||
<small
|
||||
className="col p-0"
|
||||
data-cy={`${cyLabel}-value`}
|
||||
style={{
|
||||
color: 'var(--slate12)',
|
||||
}}
|
||||
>
|
||||
{_value}
|
||||
</small>
|
||||
</div>
|
||||
|
|
|
|||
30
frontend/src/Editor/CodeBuilder/Elements/Checkbox.jsx
Normal file
30
frontend/src/Editor/CodeBuilder/Elements/Checkbox.jsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
function Checkbox({ value, onChange }) {
|
||||
const [isChecked, setIsChecked] = useState(value); // Initial state of the checkbox
|
||||
|
||||
useEffect(() => {
|
||||
setIsChecked(value);
|
||||
}, [value]);
|
||||
|
||||
return (
|
||||
<div className="d-flex align-items-center color-slate12" style={{ width: '142px', marginTop: '16px' }}>
|
||||
<input
|
||||
data-cy={`auto-width-checkbox`}
|
||||
type="checkbox"
|
||||
checked={isChecked}
|
||||
onChange={() => {
|
||||
setIsChecked(!isChecked); // Toggle the checkbox state
|
||||
onChange(!isChecked);
|
||||
}}
|
||||
value={isChecked}
|
||||
style={{ height: '16px', width: '16px' }}
|
||||
/>
|
||||
<span className="tj-text-xsm" style={{ marginLeft: '8px' }} data-cy={`auto-width-label`}>
|
||||
Auto width
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Checkbox;
|
||||
|
|
@ -2,11 +2,12 @@ import React, { useState } from 'react';
|
|||
import { SketchPicker } from 'react-color';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Popover from 'react-bootstrap/Popover';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export const Color = ({ value, onChange, pickerStyle = {}, cyLabel, asBoxShadowPopover = true }) => {
|
||||
export const Color = ({ value, onChange, pickerStyle = {}, cyLabel, asBoxShadowPopover = true, meta }) => {
|
||||
const [showPicker, setShowPicker] = useState(false);
|
||||
const darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
|
||||
const colorPickerPosition = meta?.colorPickerPosition ?? '';
|
||||
const coverStyles = {
|
||||
position: 'fixed',
|
||||
top: '0px',
|
||||
|
|
@ -38,7 +39,13 @@ export const Color = ({ value, onChange, pickerStyle = {}, cyLabel, asBoxShadowP
|
|||
};
|
||||
const eventPopover = () => {
|
||||
return (
|
||||
<Popover className={`${darkMode && ' dark-theme'}`}>
|
||||
<Popover
|
||||
className={classNames(
|
||||
{ 'dark-theme': darkMode },
|
||||
// This is fix when color picker don't have much space to open in bottom side
|
||||
{ 'inspector-color-input-popover': colorPickerPosition === 'top' }
|
||||
)}
|
||||
>
|
||||
<Popover.Body className={!asBoxShadowPopover && 'boxshadow-picker'}>
|
||||
<>{ColorPicker()}</>
|
||||
</Popover.Body>
|
||||
|
|
@ -110,7 +117,7 @@ export const Color = ({ value, onChange, pickerStyle = {}, cyLabel, asBoxShadowP
|
|||
}}
|
||||
show={showPicker}
|
||||
trigger="click"
|
||||
placement={'left'}
|
||||
placement={!colorPickerPosition ? 'left' : colorPickerPosition}
|
||||
rootClose={true}
|
||||
overlay={eventPopover()}
|
||||
>
|
||||
|
|
|
|||
129
frontend/src/Editor/CodeBuilder/Elements/Icon.jsx
Normal file
129
frontend/src/Editor/CodeBuilder/Elements/Icon.jsx
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
import React, { useRef, useState } from 'react';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Popover from 'react-bootstrap/Popover';
|
||||
import { SearchBox } from '@/_components/SearchBox';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import * as Icons from '@tabler/icons-react';
|
||||
import { VirtuosoGrid } from 'react-virtuoso';
|
||||
import { Visibility } from './Visibility';
|
||||
|
||||
export const Icon = ({ value, onChange, onVisibilityChange, component }) => {
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [showPopOver, setPopOverVisibility] = useState(false);
|
||||
const iconList = useRef(Object.keys(Icons));
|
||||
const darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
|
||||
const searchIcon = (text) => {
|
||||
if (searchText === text) return;
|
||||
setSearchText(text);
|
||||
};
|
||||
|
||||
const filteredIcons =
|
||||
searchText === ''
|
||||
? iconList.current
|
||||
: iconList.current.filter((icon) => icon?.toLowerCase().includes(searchText ? searchText.toLowerCase() : ''));
|
||||
|
||||
const onIconSelect = (icon) => {
|
||||
onChange(icon);
|
||||
};
|
||||
|
||||
const eventPopover = () => {
|
||||
return (
|
||||
<Popover
|
||||
id="popover-basic"
|
||||
style={{ width: '460px', maxWidth: '460px' }}
|
||||
className={`icon-widget-popover ${darkMode && 'dark-theme theme-dark'}`}
|
||||
>
|
||||
<Popover.Header>
|
||||
<SearchBox onSubmit={searchIcon} width="100%" />
|
||||
</Popover.Header>
|
||||
<Popover.Body>
|
||||
<div className="row">
|
||||
{
|
||||
<VirtuosoGrid
|
||||
style={{ height: 300 }}
|
||||
totalCount={filteredIcons.length}
|
||||
listClassName="icon-list-wrapper"
|
||||
itemClassName="icon-list"
|
||||
itemContent={(index) => {
|
||||
if (filteredIcons[index] === undefined || filteredIcons[index] === 'createReactComponent')
|
||||
return null;
|
||||
// eslint-disable-next-line import/namespace
|
||||
const IconElement = Icons[filteredIcons[index]];
|
||||
return (
|
||||
<div
|
||||
className="icon-element p-2"
|
||||
onClick={() => {
|
||||
onIconSelect(filteredIcons[index]);
|
||||
setPopOverVisibility(false);
|
||||
}}
|
||||
>
|
||||
<IconElement
|
||||
color={`${darkMode ? '#fff' : '#000'}`}
|
||||
stroke={1.5}
|
||||
strokeLinejoin="miter"
|
||||
style={{ width: '24px', height: '24px' }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
function RenderIconPicker() {
|
||||
// eslint-disable-next-line import/namespace
|
||||
const IconElement = Icons?.[value] ?? Icons?.['IconHome2'];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="color-picker-input icon-style-container">
|
||||
<div className="p-0">
|
||||
<div className="field">
|
||||
<OverlayTrigger
|
||||
trigger="click"
|
||||
placement={'left'}
|
||||
show={showPopOver}
|
||||
onToggle={(showing) => setPopOverVisibility(showing)}
|
||||
rootClose={true}
|
||||
overlay={eventPopover()}
|
||||
>
|
||||
<div className="d-flex align-items-center" role="button">
|
||||
<div className="" style={{ marginRight: '2px' }}>
|
||||
<IconElement
|
||||
data-cy={`icon-on-side-panel`}
|
||||
color={`${darkMode ? '#fff' : '#000'}`}
|
||||
stroke={1.5}
|
||||
strokeLinejoin="miter"
|
||||
style={{ width: '24px', height: '24px' }}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="text-truncate tj-text-xsm"
|
||||
style={{
|
||||
width: '80px',
|
||||
color: 'var(--slate12)',
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
</div>
|
||||
<Visibility
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onVisibilityChange={onVisibilityChange}
|
||||
component={component}
|
||||
/>
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return <>{RenderIconPicker()}</>;
|
||||
};
|
||||
23
frontend/src/Editor/CodeBuilder/Elements/Input.jsx
Normal file
23
frontend/src/Editor/CodeBuilder/Elements/Input.jsx
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import React from 'react';
|
||||
|
||||
export const Input = ({ value, onChange, cyLabel, staticText }) => {
|
||||
return (
|
||||
<div className="form-text">
|
||||
<input
|
||||
data-cy={`${String(cyLabel)}-input`}
|
||||
style={{ width: '142px', height: '32px' }}
|
||||
type="text"
|
||||
className="tj-input-element tj-text-xsm"
|
||||
value={value}
|
||||
placeholder=""
|
||||
id="labelId"
|
||||
onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<label for="labelId" className="static-value tj-text-xsm">
|
||||
{staticText?.length > 0 ? staticText : staticText?.length == 0 ? '' : 'px'}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
24
frontend/src/Editor/CodeBuilder/Elements/NumberInput.jsx
Normal file
24
frontend/src/Editor/CodeBuilder/Elements/NumberInput.jsx
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import React from 'react';
|
||||
|
||||
export const NumberInput = ({ value, onChange, cyLabel, staticText }) => {
|
||||
return (
|
||||
<div className="form-text tj-number-input-element">
|
||||
<input
|
||||
style={{ width: '142px', height: '32px' }}
|
||||
data-cy={`${String(cyLabel)}-input`}
|
||||
type="number"
|
||||
className="tj-input-element tj-text-xsm"
|
||||
value={value}
|
||||
placeholder=""
|
||||
id="labelId"
|
||||
onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<label for="labelId" className="static-value tj-text-xsm">
|
||||
{staticText?.length > 0 ? staticText : staticText?.length == 0 ? '' : 'px'}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
56
frontend/src/Editor/CodeBuilder/Elements/Slider.jsx
Normal file
56
frontend/src/Editor/CodeBuilder/Elements/Slider.jsx
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import CustomInput from '@/_ui/CustomInput';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import * as Slider from '@radix-ui/react-slider';
|
||||
import './Slider.scss';
|
||||
|
||||
function Slider1({ value, onChange, component }) {
|
||||
const [sliderValue, setSliderValue] = useState(value ? value : 33); // Initial value of the slider
|
||||
|
||||
useEffect(() => {
|
||||
setSliderValue(value);
|
||||
}, [value]);
|
||||
|
||||
const handleSliderChange = (value) => {
|
||||
setSliderValue(value);
|
||||
onChange(value);
|
||||
};
|
||||
|
||||
// Throttle function to handle input changes
|
||||
const onInputChange = (e) => {
|
||||
let inputValue = parseInt(e.target.value, 10) || 0;
|
||||
inputValue = Math.min(inputValue, 100);
|
||||
setSliderValue(inputValue);
|
||||
onChange(inputValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="d-flex flex-column " style={{ width: '142px', position: 'relative' }}>
|
||||
<CustomInput
|
||||
disabled={component.component.definition.styles.auto.value}
|
||||
value={sliderValue}
|
||||
staticText="% of the field"
|
||||
onInputChange={onInputChange}
|
||||
/>
|
||||
<div style={{ position: 'absolute', top: '34px' }}>
|
||||
<Slider.Root
|
||||
className="SliderRoot"
|
||||
defaultValue={[33]}
|
||||
min={0}
|
||||
max={100}
|
||||
step={1}
|
||||
value={[sliderValue]}
|
||||
onValueChange={handleSliderChange}
|
||||
disabled={component.component.definition.styles.auto.value}
|
||||
>
|
||||
<Slider.Track className="SliderTrack">
|
||||
<Slider.Range className="SliderRange" />
|
||||
</Slider.Track>
|
||||
<Slider.Thumb className="SliderThumb" aria-label="Volume" />
|
||||
</Slider.Root>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Slider1;
|
||||
45
frontend/src/Editor/CodeBuilder/Elements/Slider.scss
Normal file
45
frontend/src/Editor/CodeBuilder/Elements/Slider.scss
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
.SliderRoot {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
touch-action: none;
|
||||
width: 142px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.SliderTrack {
|
||||
background-color: #E8ECF0;
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
border-radius: 9999px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.SliderRange {
|
||||
position: absolute;
|
||||
background-color: #4368E3;
|
||||
border-radius: 9999px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.SliderThumb {
|
||||
display: block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #C1C8CD;
|
||||
}
|
||||
|
||||
.SliderThumb:hover {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.SliderThumb:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.SliderRange[data-disabled] {
|
||||
background-color: #C1C8CD;
|
||||
}
|
||||
27
frontend/src/Editor/CodeBuilder/Elements/Switch.jsx
Normal file
27
frontend/src/Editor/CodeBuilder/Elements/Switch.jsx
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import ToggleGroup from '@/ToolJetUI/SwitchGroup/ToggleGroup';
|
||||
import ToggleGroupItem from '@/ToolJetUI/SwitchGroup/ToggleGroupItem';
|
||||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
|
||||
const Switch = ({ value, onChange, cyLabel, meta, paramName, isIcon }) => {
|
||||
const options = meta?.options;
|
||||
const defaultValue = value;
|
||||
return (
|
||||
<div className={cx({ 'w-full': meta?.fullWidth })}>
|
||||
<ToggleGroup onValueChange={onChange} defaultValue={defaultValue} className={cx({ 'w-full': meta?.fullWidth })}>
|
||||
{options.map((option) => (
|
||||
<ToggleGroupItem
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
isIcon={isIcon}
|
||||
style={{ width: meta?.fullWidth ? '100%' : '67px' }}
|
||||
>
|
||||
{isIcon ? option?.iconName ?? '' : option?.displayName}
|
||||
</ToggleGroupItem>
|
||||
))}
|
||||
</ToggleGroup>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Switch;
|
||||
22
frontend/src/Editor/CodeBuilder/Elements/Visibility.jsx
Normal file
22
frontend/src/Editor/CodeBuilder/Elements/Visibility.jsx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import React from 'react';
|
||||
import SolidIcon from '@/_ui/Icon/SolidIcons';
|
||||
|
||||
export const Visibility = ({ value, onVisibilityChange, component }) => {
|
||||
return (
|
||||
<div
|
||||
data-cy={`icon-visibility-button`}
|
||||
className="cursor-pointer visibility-eye"
|
||||
style={{ top: component.component.definition.styles.iconVisibility?.value && '42%' }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onVisibilityChange(!component.component.definition.styles?.iconVisibility?.value);
|
||||
}}
|
||||
>
|
||||
<SolidIcon
|
||||
name={component.component.definition.styles?.iconVisibility?.value ? 'eye1' : 'eyedisable'}
|
||||
width="20"
|
||||
fill={'var(--slate8)'}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -10,4 +10,11 @@ export const TypeMapping = {
|
|||
number: 'Number',
|
||||
boxShadow: 'BoxShadow',
|
||||
clientServerSwitch: 'ClientServerSwitch',
|
||||
checkbox: 'Checkbox',
|
||||
slider: 'Slider',
|
||||
switch: 'Switch',
|
||||
input: 'Input',
|
||||
icon: 'Icon',
|
||||
visibility: 'Visibility',
|
||||
numberInput: 'NumberInput',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ export function getSuggestionKeys(refState, refSource) {
|
|||
const actions = [
|
||||
'runQuery',
|
||||
'setVariable',
|
||||
'getVariable',
|
||||
'unSetVariable',
|
||||
'showAlert',
|
||||
'logout',
|
||||
|
|
@ -81,6 +82,7 @@ export function getSuggestionKeys(refState, refSource) {
|
|||
'goToApp',
|
||||
'generateFile',
|
||||
'setPageVariable',
|
||||
'getPageVariable',
|
||||
'unsetPageVariable',
|
||||
'switchPage',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -164,7 +164,6 @@ export const DropDown = function DropDown({
|
|||
boxShadow: state.isFocused ? boxShadow : boxShadow,
|
||||
borderRadius: Number.parseFloat(borderRadius),
|
||||
}),
|
||||
|
||||
valueContainer: (provided, _state) => ({
|
||||
...provided,
|
||||
height: height,
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ export const Form = function Form(props) {
|
|||
onEvent,
|
||||
dataCy,
|
||||
paramUpdated,
|
||||
adjustHeightBasedOnAlignment,
|
||||
} = props;
|
||||
|
||||
const { events: allAppEvents } = useAppInfo();
|
||||
|
|
@ -293,6 +294,8 @@ export const Form = function Form(props) {
|
|||
allComponents={containerProps.allComponents}
|
||||
sideBarDebugger={containerProps.sideBarDebugger}
|
||||
childComponents={childComponents}
|
||||
adjustHeightBasedOnAlignment={adjustHeightBasedOnAlignment}
|
||||
height={item.defaultSize.height}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ export function generateUIComponents(JSONSchema, advanced) {
|
|||
uiComponentsDraft[index * 2 + 1]['definition']['properties']['value']['value'] = value?.value;
|
||||
if (value?.placeholder)
|
||||
uiComponentsDraft[index * 2 + 1]['definition']['properties']['placeholder']['value'] = value?.placeholder;
|
||||
uiComponentsDraft[index * 2 + 1]['definition']['properties']['label']['value'] = ''; //removing default label in all components with default label to match previous versions
|
||||
break;
|
||||
case 'DropDown':
|
||||
if (value?.styles?.disabled)
|
||||
|
|
@ -155,7 +156,10 @@ export function generateUIComponents(JSONSchema, advanced) {
|
|||
uiComponentsDraft[index * 2 + 1]['definition']['properties']['minValue']['value'] = value?.minValue;
|
||||
if (value?.placeholder)
|
||||
uiComponentsDraft[index * 2 + 1]['definition']['properties']['placeholder']['value'] = value?.placeholder;
|
||||
uiComponentsDraft[index * 2 + 1]['definition']['properties']['label']['value'] = '';
|
||||
|
||||
break;
|
||||
|
||||
case 'PasswordInput':
|
||||
if (value?.styles?.backgroundColor)
|
||||
uiComponentsDraft[index * 2 + 1]['definition']['styles']['backgroundColor']['value'] =
|
||||
|
|
@ -184,6 +188,7 @@ export function generateUIComponents(JSONSchema, advanced) {
|
|||
uiComponentsDraft[index * 2 + 1]['definition']['validation']['regex']['value'] = value?.validation?.regex;
|
||||
if (value?.placeholder)
|
||||
uiComponentsDraft[index * 2 + 1]['definition']['properties']['placeholder']['value'] = value?.placeholder;
|
||||
uiComponentsDraft[index * 2 + 1]['definition']['properties']['label']['value'] = '';
|
||||
break;
|
||||
case 'Datepicker':
|
||||
if (value?.styles?.borderRadius)
|
||||
|
|
|
|||
|
|
@ -65,6 +65,8 @@ export const Modal = function Modal({
|
|||
const canShowModal = exposedVariables.show ?? false;
|
||||
setShowModal(exposedVariables.show ?? false);
|
||||
fireEvent(canShowModal ? 'onOpen' : 'onClose');
|
||||
const inputRef = document?.getElementsByClassName('tj-text-input-widget')?.[0];
|
||||
inputRef?.blur();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [exposedVariables.show]);
|
||||
|
||||
|
|
@ -158,7 +160,7 @@ export const Modal = function Modal({
|
|||
useEffect(() => {
|
||||
if (closeOnClickingOutside) {
|
||||
const handleClickOutside = (event) => {
|
||||
const modalRef = parentRef.current.parentElement.parentElement.parentElement;
|
||||
const modalRef = parentRef?.current?.parentElement?.parentElement?.parentElement;
|
||||
|
||||
if (modalRef && modalRef === event.target) {
|
||||
hideModal();
|
||||
|
|
@ -174,7 +176,12 @@ export const Modal = function Modal({
|
|||
}, [closeOnClickingOutside, parentRef]);
|
||||
|
||||
return (
|
||||
<div className="container" data-disabled={disabledState} data-cy={dataCy}>
|
||||
<div
|
||||
className="container d-flex align-items-center"
|
||||
data-disabled={disabledState}
|
||||
data-cy={dataCy}
|
||||
style={{ height }}
|
||||
>
|
||||
{useDefaultButton && (
|
||||
<button
|
||||
disabled={disabledState}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,74 @@
|
|||
import React, { useRef, useEffect } from 'react';
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import './numberinput.scss';
|
||||
import SolidIcon from '@/_ui/Icon/SolidIcons';
|
||||
import { ToolTip } from '@/_components/ToolTip';
|
||||
import * as Icons from '@tabler/icons-react';
|
||||
import Loader from '@/ToolJetUI/Loader/Loader';
|
||||
import { resolveReferences } from '@/_helpers/utils';
|
||||
import { useCurrentState } from '@/_stores/currentStateStore';
|
||||
const tinycolor = require('tinycolor2');
|
||||
import Label from '@/_ui/Label';
|
||||
|
||||
export const NumberInput = function NumberInput({
|
||||
height,
|
||||
properties,
|
||||
validate,
|
||||
styles,
|
||||
setExposedVariable,
|
||||
darkMode,
|
||||
fireEvent,
|
||||
component,
|
||||
darkMode,
|
||||
dataCy,
|
||||
isResizing,
|
||||
adjustHeightBasedOnAlignment,
|
||||
currentLayout,
|
||||
}) {
|
||||
const { visibility, borderRadius, borderColor, backgroundColor, boxShadow } = styles;
|
||||
const { loadingState, tooltip, disabledState, label, placeholder } = properties;
|
||||
const {
|
||||
padding,
|
||||
borderRadius,
|
||||
borderColor,
|
||||
backgroundColor,
|
||||
boxShadow,
|
||||
width,
|
||||
alignment,
|
||||
direction,
|
||||
color,
|
||||
auto,
|
||||
errTextColor,
|
||||
iconColor,
|
||||
accentColor,
|
||||
} = styles;
|
||||
|
||||
const textColor = darkMode && ['#232e3c', '#000000ff'].includes(styles.textColor) ? '#fff' : styles.textColor;
|
||||
const isMandatory = resolveReferences(component?.definition?.validation?.mandatory?.value, currentState) ?? false;
|
||||
const minValue = resolveReferences(component?.definition?.validation?.minValue?.value, currentState) ?? null;
|
||||
const maxValue = resolveReferences(component?.definition?.validation?.maxValue?.value, currentState) ?? null;
|
||||
|
||||
const [visibility, setVisibility] = useState(properties.visibility);
|
||||
const [loading, setLoading] = useState(loadingState);
|
||||
const [showValidationError, setShowValidationError] = useState(false);
|
||||
const [value, setValue] = React.useState(Number(parseFloat(properties.value).toFixed(properties.decimalPlaces)));
|
||||
const { isValid, validationError } = validate(value);
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
|
||||
const inputRef = useRef(null);
|
||||
const currentState = useCurrentState();
|
||||
const [disable, setDisable] = useState(disabledState || loadingState);
|
||||
const labelRef = useRef();
|
||||
const _width = (width / 100) * 70; // Max width which label can go is 70% for better UX calculate width based on this value
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('label', label);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [label]);
|
||||
|
||||
useEffect(() => {
|
||||
if (alignment == 'top' && ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)))
|
||||
adjustHeightBasedOnAlignment(true);
|
||||
else adjustHeightBasedOnAlignment(false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [alignment, label?.length, currentLayout, width, auto]);
|
||||
|
||||
useEffect(() => {
|
||||
setValue(Number(parseFloat(value).toFixed(properties.decimalPlaces)));
|
||||
|
|
@ -26,27 +80,12 @@ export const NumberInput = function NumberInput({
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [properties.value]);
|
||||
|
||||
const handleChange = (e) => {
|
||||
if (
|
||||
!isNaN(parseFloat(properties.minValue)) &&
|
||||
!isNaN(parseFloat(properties.maxValue)) &&
|
||||
parseFloat(properties.minValue) > parseFloat(properties.maxValue)
|
||||
) {
|
||||
setValue(Number(parseFloat(properties.maxValue)));
|
||||
} else if (
|
||||
!isNaN(parseFloat(properties.maxValue)) &&
|
||||
parseFloat(e.target.value) > parseFloat(properties.maxValue)
|
||||
) {
|
||||
setValue(Number(parseFloat(properties.maxValue)));
|
||||
} else {
|
||||
setValue(Number(parseFloat(e.target.value)));
|
||||
}
|
||||
fireEvent('onChange');
|
||||
};
|
||||
const handleBlur = (e) => {
|
||||
if (!isNaN(parseFloat(properties.minValue)) && parseFloat(e.target.value) < parseFloat(properties.minValue)) {
|
||||
setValue(Number(parseFloat(properties.minValue)));
|
||||
} else setValue(Number(parseFloat(e.target.value ? e.target.value : 0).toFixed(properties.decimalPlaces)));
|
||||
setValue(Number(parseFloat(e.target.value).toFixed(properties.decimalPlaces)));
|
||||
setShowValidationError(true);
|
||||
e.stopPropagation();
|
||||
fireEvent('onBlur');
|
||||
setIsFocused(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -56,41 +95,344 @@ export const NumberInput = function NumberInput({
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [value]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('isMandatory', isMandatory);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isMandatory]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('isLoading', loading);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [loading]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('setLoading', async function (loading) {
|
||||
setLoading(loading);
|
||||
setExposedVariable('isLoading', loading);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [properties.loadingState]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('isVisible', visibility);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [visibility]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('setVisibility', async function (state) {
|
||||
setVisibility(state);
|
||||
setExposedVariable('isVisible', state);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [properties.visibility]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('setDisable', async function (disable) {
|
||||
setDisable(disable);
|
||||
setExposedVariable('isDisabled', disable);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [disabledState]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('isDisabled', disable);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [disable]);
|
||||
useEffect(() => {
|
||||
if (labelRef?.current) {
|
||||
const absolutewidth = labelRef?.current?.getBoundingClientRect()?.width;
|
||||
setLabelWidth(absolutewidth);
|
||||
} else setLabelWidth(0);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
isResizing,
|
||||
width,
|
||||
auto,
|
||||
defaultAlignment,
|
||||
component?.definition?.styles?.iconVisibility?.value,
|
||||
labelRef?.current?.getBoundingClientRect()?.width,
|
||||
isMandatory,
|
||||
padding,
|
||||
direction,
|
||||
alignment,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('isValid', isValid);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isValid]);
|
||||
|
||||
const computedStyles = {
|
||||
height,
|
||||
display: visibility ? '' : 'none',
|
||||
height: height == 36 ? (padding == 'default' ? '36px' : '38px') : padding == 'default' ? height : height + 2,
|
||||
borderRadius: `${borderRadius}px`,
|
||||
borderColor,
|
||||
color: textColor,
|
||||
backgroundColor: darkMode && ['#ffffff', '#ffffffff'].includes(backgroundColor) ? '#000000' : backgroundColor,
|
||||
boxShadow,
|
||||
color: darkMode && textColor === '#11181C' ? '#ECEDEE' : textColor,
|
||||
borderColor: isFocused
|
||||
? accentColor
|
||||
: ['#D7DBDF'].includes(borderColor)
|
||||
? darkMode
|
||||
? '#6D757D7A'
|
||||
: '#6A727C47'
|
||||
: borderColor,
|
||||
'--tblr-input-border-color-darker': tinycolor(borderColor).darken(24).toString(),
|
||||
backgroundColor:
|
||||
darkMode && ['#ffffff', '#ffffffff', '#fff'].includes(backgroundColor) ? '#313538' : backgroundColor,
|
||||
|
||||
boxShadow: boxShadow,
|
||||
padding: styles.iconVisibility ? '8px 10px 8px 29px' : '8px 10px 8px 10px',
|
||||
// flex: padding !== 'none' && 1,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{!properties.loadingState && (
|
||||
<input
|
||||
ref={inputRef}
|
||||
disabled={styles.disabledState}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
type="number"
|
||||
className="form-control"
|
||||
placeholder={properties.placeholder}
|
||||
style={computedStyles}
|
||||
value={value}
|
||||
data-cy={dataCy}
|
||||
min={properties.minValue}
|
||||
max={properties.maxValue}
|
||||
/>
|
||||
)}
|
||||
{properties.loadingState === true && (
|
||||
<div style={{ width: '100%' }}>
|
||||
<center>
|
||||
<div className="spinner-border" role="status"></div>
|
||||
</center>
|
||||
const defaultAlignment = alignment === 'side' || alignment === 'top' ? alignment : 'side';
|
||||
const [labelWidth, setLabelWidth] = useState(0);
|
||||
|
||||
const iconName = styles.icon; // Replace with the name of the icon you want
|
||||
// eslint-disable-next-line import/namespace
|
||||
const IconElement = Icons[iconName] == undefined ? Icons['IconHome2'] : Icons[iconName];
|
||||
// eslint-disable-next-line import/namespace
|
||||
|
||||
const handleChange = (e) => {
|
||||
setValue(Number(parseFloat(e.target.value)));
|
||||
if (e.target.value == '') {
|
||||
setValue(null);
|
||||
setExposedVariable('value', null).then(fireEvent('onChange'));
|
||||
}
|
||||
if (!isNaN(Number(parseFloat(e.target.value)))) {
|
||||
setExposedVariable('value', Number(parseFloat(e.target.value))).then(fireEvent('onChange'));
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
disable !== disabledState && setDisable(disabledState);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [disabledState]);
|
||||
useEffect(() => {
|
||||
visibility !== properties.visibility && setVisibility(properties.visibility);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [properties.visibility]);
|
||||
useEffect(() => {
|
||||
loading !== loadingState && setLoading(loadingState);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [loadingState]);
|
||||
|
||||
const handleIncrement = (e) => {
|
||||
e.preventDefault(); // Prevent the default button behavior (form submission, page reload)
|
||||
|
||||
const newValue = (value || 0) + 1;
|
||||
setValue(newValue);
|
||||
if (!isNaN(newValue)) {
|
||||
setExposedVariable('value', newValue).then(fireEvent('onChange'));
|
||||
}
|
||||
};
|
||||
const handleDecrement = (e) => {
|
||||
e.preventDefault();
|
||||
const newValue = (value || 0) - 1;
|
||||
setValue(newValue);
|
||||
if (!isNaN(newValue)) {
|
||||
setExposedVariable('value', newValue).then(fireEvent('onChange'));
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
setExposedVariable('setFocus', async function () {
|
||||
inputRef.current.focus();
|
||||
});
|
||||
setExposedVariable('setBlur', async function () {
|
||||
inputRef.current.blur();
|
||||
});
|
||||
setExposedVariable('setText', async function (text) {
|
||||
if (text) {
|
||||
const newValue = Number(parseFloat(text));
|
||||
setValue(newValue);
|
||||
setExposedVariable('value', text).then(fireEvent('onChange'));
|
||||
}
|
||||
});
|
||||
|
||||
setExposedVariable('clear', async function () {
|
||||
setValue('');
|
||||
setExposedVariable('value', '').then(fireEvent('onChange'));
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const loaderStyle = {
|
||||
right:
|
||||
direction === 'right' &&
|
||||
defaultAlignment === 'side' &&
|
||||
((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0))
|
||||
? `${labelWidth + 11 + 20}px` // 23 px usual + 20 for number input arrows
|
||||
: '31px',
|
||||
top: `${
|
||||
defaultAlignment === 'top'
|
||||
? ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
|
||||
'calc(50% + 10px)'
|
||||
: ''
|
||||
}`,
|
||||
transform:
|
||||
defaultAlignment === 'top' &&
|
||||
((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
|
||||
' translateY(-50%)',
|
||||
zIndex: 3,
|
||||
};
|
||||
|
||||
const renderInput = () => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
data-cy={`label-${String(component.name).toLowerCase()}`}
|
||||
data-disabled={disable || loading}
|
||||
className={`text-input tj-number-input-widget d-flex ${
|
||||
defaultAlignment === 'top' &&
|
||||
((width != 0 && label && label?.length != 0) || (auto && width == 0 && label && label?.length != 0))
|
||||
? 'flex-column'
|
||||
: 'align-items-center '
|
||||
} ${direction === 'right' && defaultAlignment === 'side' ? 'flex-row-reverse' : ''}
|
||||
${direction === 'right' && defaultAlignment === 'top' ? 'text-right' : ''}
|
||||
${visibility || 'invisible'}`}
|
||||
style={{
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
display: !visibility ? 'none' : 'flex',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
<Label
|
||||
label={label}
|
||||
width={width}
|
||||
labelRef={labelRef}
|
||||
darkMode={darkMode}
|
||||
color={color}
|
||||
defaultAlignment={defaultAlignment}
|
||||
direction={direction}
|
||||
auto={auto}
|
||||
isMandatory={isMandatory}
|
||||
_width={_width}
|
||||
labelWidth={labelWidth}
|
||||
/>
|
||||
{component?.definition?.styles?.iconVisibility?.value && !isResizing && (
|
||||
<IconElement
|
||||
data-cy={'text-input-icon'}
|
||||
style={{
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
left:
|
||||
direction === 'right'
|
||||
? '11px'
|
||||
: defaultAlignment === 'top'
|
||||
? '11px'
|
||||
: (label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)
|
||||
? `${labelWidth + 11}px`
|
||||
: '11px', //23 :: is 10 px inside the input + 1 px border + 12px margin right
|
||||
position: 'absolute',
|
||||
top: `${
|
||||
defaultAlignment === 'side'
|
||||
? '50%'
|
||||
: (label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)
|
||||
? 'calc(50% + 10px)'
|
||||
: '50%'
|
||||
}`,
|
||||
transform: ' translateY(-50%)',
|
||||
color: iconColor,
|
||||
zIndex: 3,
|
||||
}}
|
||||
stroke={1.5}
|
||||
/>
|
||||
)}
|
||||
<input
|
||||
ref={inputRef}
|
||||
disabled={disable || loading}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
type="number"
|
||||
className={`${!isValid && showValidationError ? 'is-invalid' : ''} input-number tj-text-input-widget`}
|
||||
placeholder={placeholder}
|
||||
style={computedStyles}
|
||||
value={value}
|
||||
data-cy={dataCy}
|
||||
min={minValue}
|
||||
max={maxValue}
|
||||
onKeyUp={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
setValue(e.target.value);
|
||||
setExposedVariable('value', e.target.value);
|
||||
fireEvent('onEnterPressed');
|
||||
}
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
setIsFocused(true);
|
||||
e.stopPropagation();
|
||||
setTimeout(() => {
|
||||
fireEvent('onFocus');
|
||||
}, 0);
|
||||
}}
|
||||
/>
|
||||
{!isResizing && (
|
||||
<>
|
||||
<div onClick={(e) => handleIncrement(e)}>
|
||||
<SolidIcon
|
||||
width={padding == 'default' ? `${height / 2 - 1}px` : `${height / 2 + 1}px`}
|
||||
height={`${height / 2}px`}
|
||||
style={{
|
||||
top: defaultAlignment === 'top' && label?.length > 0 && width > 0 ? '21px' : '1px',
|
||||
right:
|
||||
labelWidth == 0
|
||||
? '1px'
|
||||
: alignment == 'side' && direction === 'right'
|
||||
? `${labelWidth + 1}px`
|
||||
: '1px',
|
||||
borderLeft: darkMode ? '1px solid #313538' : '1px solid #D7D7D7',
|
||||
borderBottom: darkMode ? '.5px solid #313538' : '0.5px solid #D7D7D7',
|
||||
borderTopRightRadius: borderRadius - 1,
|
||||
backgroundColor: !darkMode ? 'white' : 'black',
|
||||
zIndex: 3,
|
||||
}}
|
||||
className="numberinput-up-arrow arrow"
|
||||
name="cheveronup"
|
||||
></SolidIcon>
|
||||
</div>
|
||||
|
||||
<div onClick={(e) => handleDecrement(e)}>
|
||||
<SolidIcon
|
||||
style={{
|
||||
right:
|
||||
labelWidth == 0
|
||||
? '1px'
|
||||
: alignment == 'side' && direction === 'right'
|
||||
? `${labelWidth + 1}px`
|
||||
: '1px',
|
||||
bottom: '1px',
|
||||
borderLeft: darkMode ? '1px solid #313538' : '1px solid #D7D7D7',
|
||||
borderTop: darkMode ? '0.5px solid #313538' : '0.5px solid #D7D7D7',
|
||||
borderBottomRightRadius: borderRadius - 1,
|
||||
backgroundColor: !darkMode ? 'white' : 'black',
|
||||
zIndex: 3,
|
||||
}}
|
||||
width={padding == 'default' ? `${height / 2 - 1}px` : `${height / 2 + 1}px`}
|
||||
height={`${height / 2}px`}
|
||||
className="numberinput-down-arrow arrow"
|
||||
name="cheverondown"
|
||||
></SolidIcon>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{loading && <Loader style={{ ...loaderStyle }} width="16" />}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
{showValidationError && visibility && (
|
||||
<div
|
||||
className="tj-text-sm"
|
||||
data-cy={`${String(component.name).toLowerCase()}-invalid-feedback`}
|
||||
style={{
|
||||
color: errTextColor,
|
||||
textAlign: direction == 'left' && 'end',
|
||||
}}
|
||||
>
|
||||
{showValidationError && validationError}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return <div>{renderInput()}</div>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,61 +1,380 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { resolveReferences } from '@/_helpers/utils';
|
||||
import { useCurrentState } from '@/_stores/currentStateStore';
|
||||
import { ToolTip } from '@/_components/ToolTip';
|
||||
import * as Icons from '@tabler/icons-react';
|
||||
import Loader from '@/ToolJetUI/Loader/Loader';
|
||||
import SolidIcon from '@/_ui/Icon/SolidIcons';
|
||||
import Label from '@/_ui/Label';
|
||||
|
||||
export const PasswordInput = ({
|
||||
export const PasswordInput = function PasswordInput({
|
||||
height,
|
||||
validate,
|
||||
properties,
|
||||
styles,
|
||||
setExposedVariable,
|
||||
darkMode,
|
||||
component,
|
||||
fireEvent,
|
||||
component,
|
||||
darkMode,
|
||||
dataCy,
|
||||
}) => {
|
||||
const { visibility, disabledState, borderRadius, backgroundColor, boxShadow } = styles;
|
||||
isResizing,
|
||||
adjustHeightBasedOnAlignment,
|
||||
currentLayout,
|
||||
}) {
|
||||
const textInputRef = useRef();
|
||||
const labelRef = useRef();
|
||||
|
||||
const placeholder = properties.placeholder;
|
||||
const { loadingState, tooltip, disabledState, label, placeholder } = properties;
|
||||
const {
|
||||
padding,
|
||||
borderRadius,
|
||||
borderColor,
|
||||
backgroundColor,
|
||||
textColor,
|
||||
boxShadow,
|
||||
width,
|
||||
alignment,
|
||||
direction,
|
||||
color,
|
||||
auto,
|
||||
errTextColor,
|
||||
iconColor,
|
||||
accentColor,
|
||||
} = styles;
|
||||
|
||||
const [passwordValue, setPasswordValue] = useState('');
|
||||
const [disable, setDisable] = useState(disabledState || loadingState);
|
||||
const [passwordValue, setPasswordValue] = useState(properties.value);
|
||||
const [visibility, setVisibility] = useState(properties.visibility);
|
||||
const { isValid, validationError } = validate(passwordValue);
|
||||
const [showValidationError, setShowValidationError] = useState(false);
|
||||
const currentState = useCurrentState();
|
||||
const isMandatory = resolveReferences(component?.definition?.validation?.mandatory?.value, currentState);
|
||||
const [labelWidth, setLabelWidth] = useState(0);
|
||||
const defaultAlignment = alignment === 'side' || alignment === 'top' ? alignment : 'side';
|
||||
const [iconVisibility, setIconVisibility] = useState(false);
|
||||
const [loading, setLoading] = useState(loadingState);
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
const tinycolor = require('tinycolor2');
|
||||
|
||||
const _width = (width / 100) * 70; // Max width which label can go is 70% for better UX calculate width based on this value
|
||||
|
||||
const computedStyles = {
|
||||
height,
|
||||
display: visibility ? '' : 'none',
|
||||
height: height == 36 ? (padding == 'default' ? '36px' : '38px') : padding == 'default' ? height : height + 2,
|
||||
borderRadius: `${borderRadius}px`,
|
||||
color: darkMode && '#fff',
|
||||
borderColor: darkMode && '#DADCDE',
|
||||
backgroundColor: darkMode && ['#ffffff'].includes(backgroundColor) ? '#232e3c' : backgroundColor,
|
||||
color: darkMode && textColor === '#11181C' ? '#ECEDEE' : textColor,
|
||||
borderColor: isFocused
|
||||
? accentColor
|
||||
: ['#D7DBDF'].includes(borderColor)
|
||||
? darkMode
|
||||
? '#6D757D7A'
|
||||
: '#6A727C47'
|
||||
: borderColor,
|
||||
'--tblr-input-border-color-darker': tinycolor(borderColor).darken(24).toString(),
|
||||
backgroundColor: darkMode && ['#fff', '#ffffff'].includes(backgroundColor) ? '#313538' : backgroundColor,
|
||||
boxShadow: boxShadow,
|
||||
padding: styles.iconVisibility ? '8px 10px 8px 29px' : '8px 10px 8px 10px',
|
||||
// flex: padding !== 'none' && 1,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
const loaderStyle = {
|
||||
right:
|
||||
direction === 'right' &&
|
||||
defaultAlignment === 'side' &&
|
||||
((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0))
|
||||
? `${labelWidth + 11}px`
|
||||
: '11px',
|
||||
top: `${
|
||||
defaultAlignment === 'top'
|
||||
? ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
|
||||
'calc(50% + 10px)'
|
||||
: ''
|
||||
}`,
|
||||
transform:
|
||||
defaultAlignment === 'top' &&
|
||||
((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
|
||||
' translateY(-50%)',
|
||||
zIndex: 3,
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('label', label);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [label]);
|
||||
|
||||
useEffect(() => {
|
||||
if (labelRef?.current) {
|
||||
const absolutewidth = labelRef?.current?.getBoundingClientRect()?.width;
|
||||
setLabelWidth(absolutewidth);
|
||||
} else setLabelWidth(0);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
isResizing,
|
||||
width,
|
||||
auto,
|
||||
defaultAlignment,
|
||||
component?.definition?.styles?.iconVisibility?.value,
|
||||
label?.length,
|
||||
isMandatory,
|
||||
padding,
|
||||
direction,
|
||||
alignment,
|
||||
isMandatory,
|
||||
labelRef?.current?.getBoundingClientRect()?.width,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
disable !== disabledState && setDisable(disabledState);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [disabledState]);
|
||||
|
||||
useEffect(() => {
|
||||
visibility !== properties.visibility && setVisibility(properties.visibility);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [properties.visibility]);
|
||||
|
||||
useEffect(() => {
|
||||
loading !== loadingState && setLoading(loadingState);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [loadingState]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('isValid', isValid);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [passwordValue, isValid]);
|
||||
}, [isValid]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
disabled={disabledState}
|
||||
onChange={(e) => {
|
||||
setPasswordValue(e.target.value);
|
||||
setExposedVariable('value', e.target.value);
|
||||
fireEvent('onChange');
|
||||
setShowValidationError(true);
|
||||
useEffect(() => {
|
||||
setPasswordValue(properties.value);
|
||||
setExposedVariable('value', properties?.value ?? '');
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [properties.value]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('setFocus', async function () {
|
||||
textInputRef.current.focus();
|
||||
});
|
||||
setExposedVariable('setBlur', async function () {
|
||||
textInputRef.current.blur();
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('setText', async function (text) {
|
||||
setPasswordValue(text);
|
||||
setExposedVariable('value', text).then(fireEvent('onChange'));
|
||||
});
|
||||
setExposedVariable('clear', async function () {
|
||||
setPasswordValue('');
|
||||
setExposedVariable('value', '').then(fireEvent('onChange'));
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [setPasswordValue]);
|
||||
|
||||
const iconName = styles.icon; // Replace with the name of the icon you want
|
||||
// eslint-disable-next-line import/namespace
|
||||
const IconElement = Icons[iconName] == undefined ? Icons['IconHome2'] : Icons[iconName];
|
||||
// eslint-disable-next-line import/namespace
|
||||
|
||||
useEffect(() => {
|
||||
if (alignment == 'top' && ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)))
|
||||
adjustHeightBasedOnAlignment(true);
|
||||
else adjustHeightBasedOnAlignment(false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [alignment, label?.length, currentLayout, width, auto]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('isMandatory', isMandatory);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isMandatory]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('isLoading', loading);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [loading]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('setLoading', async function (loading) {
|
||||
setLoading(loading);
|
||||
setExposedVariable('isLoading', loading);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [properties.loadingState]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('isVisible', visibility);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [visibility]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('setVisibility', async function (state) {
|
||||
setVisibility(state);
|
||||
setExposedVariable('isVisible', state);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [properties.visibility]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('setDisable', async function (disable) {
|
||||
setDisable(disable);
|
||||
setExposedVariable('isDisabled', disable);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [disabledState]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('isDisabled', disable);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [disable]);
|
||||
|
||||
const renderInput = () => (
|
||||
<>
|
||||
<div
|
||||
data-disabled={disable || loading}
|
||||
className={`text-input d-flex ${
|
||||
defaultAlignment === 'top' &&
|
||||
((width != 0 && label && label?.length != 0) || (auto && width == 0 && label && label?.length != 0))
|
||||
? 'flex-column'
|
||||
: 'align-items-center '
|
||||
} ${direction === 'right' && defaultAlignment === 'side' ? 'flex-row-reverse' : ''}
|
||||
${direction === 'right' && defaultAlignment === 'top' ? 'text-right' : ''}
|
||||
${visibility || 'invisible'}`}
|
||||
style={{
|
||||
position: 'relative',
|
||||
whiteSpace: 'nowrap',
|
||||
width: '100%',
|
||||
}}
|
||||
type={'password'}
|
||||
className={`form-control ${!isValid && showValidationError ? 'is-invalid' : ''} validation-without-icon ${
|
||||
darkMode && 'dark-theme-placeholder'
|
||||
}`}
|
||||
placeholder={placeholder}
|
||||
value={passwordValue}
|
||||
style={computedStyles}
|
||||
data-cy={dataCy}
|
||||
/>
|
||||
<div className="invalid-feedback" data-cy={`${String(component.name).toLowerCase()}-invalid-feedback`}>
|
||||
{showValidationError && validationError}
|
||||
>
|
||||
<Label
|
||||
label={label}
|
||||
width={width}
|
||||
labelRef={labelRef}
|
||||
darkMode={darkMode}
|
||||
color={color}
|
||||
defaultAlignment={defaultAlignment}
|
||||
direction={direction}
|
||||
auto={auto}
|
||||
isMandatory={isMandatory}
|
||||
_width={_width}
|
||||
labelWidth={labelWidth}
|
||||
/>
|
||||
{component?.definition?.styles?.iconVisibility?.value && !isResizing && (
|
||||
<IconElement
|
||||
data-cy={'text-input-icon'}
|
||||
style={{
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
left:
|
||||
direction === 'right'
|
||||
? '11px'
|
||||
: defaultAlignment === 'top'
|
||||
? '11px'
|
||||
: (label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)
|
||||
? `${labelWidth + 11}px`
|
||||
: '11px', //11 :: is 10 px inside the input + 1 px border + 12px margin right
|
||||
position: 'absolute',
|
||||
top: `${
|
||||
defaultAlignment === 'side'
|
||||
? '50%'
|
||||
: (label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)
|
||||
? 'calc(50% + 10px)'
|
||||
: '50%'
|
||||
}`,
|
||||
transform: ' translateY(-50%)',
|
||||
color: iconColor,
|
||||
zIndex: 3,
|
||||
}}
|
||||
stroke={1.5}
|
||||
/>
|
||||
)}
|
||||
{!loading && !isResizing && (
|
||||
<div
|
||||
onClick={() => {
|
||||
setIconVisibility(!iconVisibility);
|
||||
}}
|
||||
style={{
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
position: 'absolute',
|
||||
right:
|
||||
direction === 'right' &&
|
||||
defaultAlignment === 'side' &&
|
||||
((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0))
|
||||
? `${labelWidth + 11}px`
|
||||
: '11px',
|
||||
top: `${
|
||||
defaultAlignment === 'top'
|
||||
? ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
|
||||
'calc(50% + 10px)'
|
||||
: ''
|
||||
}`,
|
||||
transform:
|
||||
defaultAlignment === 'top' &&
|
||||
((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
|
||||
' translateY(-50%)',
|
||||
display: 'flex',
|
||||
zIndex: 3,
|
||||
}}
|
||||
stroke={1.5}
|
||||
>
|
||||
<SolidIcon width={16} className="password-component-eye" name={!iconVisibility ? 'eye1' : 'eyedisable'} />
|
||||
</div>
|
||||
)}
|
||||
<input
|
||||
data-cy={`label-${String(component.name).toLowerCase()}`}
|
||||
className={`tj-text-input-widget ${
|
||||
!isValid && showValidationError ? 'is-invalid' : ''
|
||||
} validation-without-icon ${darkMode && 'dark-theme-placeholder'}`}
|
||||
ref={textInputRef}
|
||||
onKeyUp={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
setPasswordValue(e.target.value);
|
||||
setExposedVariable('value', e.target.value);
|
||||
fireEvent('onEnterPressed');
|
||||
}
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setPasswordValue(e.target.value);
|
||||
setExposedVariable('value', e.target.value);
|
||||
fireEvent('onChange');
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
setIsFocused(false);
|
||||
setShowValidationError(true);
|
||||
e.stopPropagation();
|
||||
fireEvent('onBlur');
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
setIsFocused(true);
|
||||
e.stopPropagation();
|
||||
setTimeout(() => {
|
||||
fireEvent('onFocus');
|
||||
}, 0);
|
||||
}}
|
||||
type={!iconVisibility ? 'password' : 'text'}
|
||||
placeholder={placeholder}
|
||||
style={computedStyles}
|
||||
value={passwordValue}
|
||||
disabled={disable || loading}
|
||||
/>
|
||||
{loading && <Loader style={{ ...loaderStyle }} width="16" />}
|
||||
</div>
|
||||
</div>
|
||||
{showValidationError && visibility && (
|
||||
<div
|
||||
className="tj-text-sm"
|
||||
data-cy={`${String(component.name).toLowerCase()}-invalid-feedback`}
|
||||
style={{
|
||||
color: errTextColor,
|
||||
textAlign: direction === 'left' && 'end',
|
||||
}}
|
||||
>
|
||||
{showValidationError && validationError}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
return <div>{renderInput()}</div>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,16 +1,31 @@
|
|||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import Select from '@/_ui/Select';
|
||||
import defaultStyles from '@/_ui/Select/styles';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ButtonSolid } from '@/_ui/AppButton/AppButton';
|
||||
import _ from 'lodash';
|
||||
import _, { isArray } from 'lodash';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { diff as deepDiff } from 'deep-object-diff';
|
||||
|
||||
const FILTER_OPTIONS = [
|
||||
{ name: 'contains', value: 'contains' },
|
||||
{ name: 'does not contains', value: 'doesNotContains' },
|
||||
{ name: 'matches', value: 'matches' },
|
||||
{ name: 'does not match', value: 'nl' },
|
||||
{ name: 'equals', value: 'equals' },
|
||||
{ name: 'does not equal', value: 'ne' },
|
||||
{ name: 'is empty', value: 'isEmpty' },
|
||||
{ name: 'is not empty', value: 'isNotEmpty' },
|
||||
{ name: 'greater than', value: 'gt' },
|
||||
{ name: 'less than', value: 'lt' },
|
||||
{ name: 'greater than or equals', value: 'gte' },
|
||||
{ name: 'less than or equals', value: 'lte' },
|
||||
];
|
||||
|
||||
export function Filter(props) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { mergeToFilterDetails, filterDetails, setAllFilters, fireEvent, darkMode } = props;
|
||||
const { mergeToFilterDetails, filterDetails, setAllFilters, fireEvent, darkMode, setExposedVariable } = props;
|
||||
const { filters } = filterDetails;
|
||||
|
||||
const [activeFilters, set] = React.useState(filters);
|
||||
|
|
@ -37,13 +52,16 @@ export function Filter(props) {
|
|||
if (value === 'isEmpty' || value === 'isNotEmpty') {
|
||||
newFilters[index].value.value = '';
|
||||
}
|
||||
|
||||
mergeToFilterDetails({
|
||||
filters: newFilters,
|
||||
});
|
||||
setAllFilters(newFilters.filter((filter) => filter.id !== ''));
|
||||
}
|
||||
|
||||
const debouncedFilterChanged = _.debounce((newFilters) => {
|
||||
setAllFilters(newFilters.filter((filter) => filter.id !== ''));
|
||||
}, 500);
|
||||
|
||||
function filterValueChanged(index, value) {
|
||||
const newFilters = filters;
|
||||
newFilters[index].value = {
|
||||
|
|
@ -53,7 +71,7 @@ export function Filter(props) {
|
|||
mergeToFilterDetails({
|
||||
filters: newFilters,
|
||||
});
|
||||
setAllFilters(newFilters.filter((filter) => filter.id !== ''));
|
||||
debouncedFilterChanged(newFilters);
|
||||
}
|
||||
|
||||
function addFilter() {
|
||||
|
|
@ -82,7 +100,7 @@ export function Filter(props) {
|
|||
setTimeout(() => fireEvent('onFilterChanged'), 0);
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (filters.length > 0) {
|
||||
const tableFilters = JSON.parse(JSON.stringify(filters));
|
||||
const shouldFire = findFilterDiff(activeFilters, tableFilters);
|
||||
|
|
@ -93,6 +111,45 @@ export function Filter(props) {
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [JSON.stringify(filters)]);
|
||||
|
||||
useEffect(() => {
|
||||
// Add CSA to set filters
|
||||
setExposedVariable('setFilters', async function (_filters) {
|
||||
if (!isArray(_filters)) return;
|
||||
const filterArr = [];
|
||||
_filters.forEach((_filter) => {
|
||||
const { column = '', value = '', condition = '' } = _filter;
|
||||
const columnId = props.columns.find((col) => col.name === column)?.value;
|
||||
const isCorrectCondition = FILTER_OPTIONS.some((option) => option.value === condition);
|
||||
if (columnId && isCorrectCondition) {
|
||||
const filterObj = {
|
||||
id: columnId,
|
||||
value: {
|
||||
column,
|
||||
condition,
|
||||
value,
|
||||
},
|
||||
};
|
||||
filterArr.push(filterObj);
|
||||
}
|
||||
});
|
||||
if (filterArr.length) {
|
||||
setAllFilters(filterArr.filter((filter) => filter.id !== ''));
|
||||
mergeToFilterDetails({
|
||||
filters: filterArr,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Add CSA to clear filters
|
||||
setExposedVariable('clearFilters', async function (_filters) {
|
||||
setAllFilters([]);
|
||||
mergeToFilterDetails({
|
||||
filters: [],
|
||||
});
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [JSON.stringify(props.columns)]);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const debounceFn = React.useCallback(
|
||||
_.debounce(() => {
|
||||
|
|
@ -110,6 +167,10 @@ export function Filter(props) {
|
|||
};
|
||||
};
|
||||
|
||||
if (!filterDetails.filtersVisible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`table-filters card ${darkMode && 'dark-theme'}`}>
|
||||
<div className="card-header row">
|
||||
|
|
@ -150,20 +211,7 @@ export function Filter(props) {
|
|||
</div>
|
||||
<div data-cy={`select-operation-dropdown-${index ?? ''}`} className="col" style={{ maxWidth: '180px' }}>
|
||||
<Select
|
||||
options={[
|
||||
{ name: 'contains', value: 'contains' },
|
||||
{ name: 'does not contains', value: 'doesNotContains' },
|
||||
{ name: 'matches', value: 'matches' },
|
||||
{ name: 'does not match', value: 'nl' },
|
||||
{ name: 'equals', value: 'equals' },
|
||||
{ name: 'does not equal', value: 'ne' },
|
||||
{ name: 'is empty', value: 'isEmpty' },
|
||||
{ name: 'is not empty', value: 'isNotEmpty' },
|
||||
{ name: 'greater than', value: 'gt' },
|
||||
{ name: 'less than', value: 'lt' },
|
||||
{ name: 'greater than or equals', value: 'gte' },
|
||||
{ name: 'less than or equals', value: 'lte' },
|
||||
]}
|
||||
options={FILTER_OPTIONS}
|
||||
value={filter.value.condition}
|
||||
search={true}
|
||||
onChange={(value) => {
|
||||
|
|
@ -184,7 +232,7 @@ export function Filter(props) {
|
|||
value={filter.value.value}
|
||||
placeholder="value"
|
||||
className="form-control"
|
||||
onChange={(e) => _.debounce(filterValueChanged(index, e.target.value), 500)}
|
||||
onChange={(e) => filterValueChanged(index, e.target.value)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -252,8 +300,8 @@ const findFilterDiff = (oldFilters, newFilters) => {
|
|||
};
|
||||
|
||||
const diff = Object.entries(filterDiff).reduce((acc, [key, value]) => {
|
||||
const type = getType(value.value);
|
||||
return (acc = { ...acc, keyIndex: key, type: type, diff: value.value[type] });
|
||||
const type = getType(value?.value);
|
||||
return (acc = { ...acc, keyIndex: key, type: type, diff: value?.value?.[type] });
|
||||
}, {});
|
||||
|
||||
return shouldFireEvent(diff, newFilters);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
|
||||
const IndeterminateCheckbox = React.forwardRef(({ indeterminate, ...rest }, ref) => {
|
||||
const IndeterminateCheckbox = React.forwardRef(({ indeterminate, fireEvent, ...rest }, ref) => {
|
||||
const defaultRef = React.useRef();
|
||||
const resolvedRef = ref || defaultRef;
|
||||
|
||||
|
|
@ -14,7 +14,12 @@ const IndeterminateCheckbox = React.forwardRef(({ indeterminate, ...rest }, ref)
|
|||
data-cy={`checkbox-input`}
|
||||
type="checkbox"
|
||||
ref={resolvedRef}
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
onClick={(event) => {
|
||||
if (fireEvent) {
|
||||
fireEvent('onRowClicked');
|
||||
}
|
||||
event.stopPropagation();
|
||||
}}
|
||||
{...rest}
|
||||
style={{
|
||||
width: 16,
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ export function Table({
|
|||
showAddNewRowButton,
|
||||
allowSelection,
|
||||
enablePagination,
|
||||
selectRowOnCellEdit,
|
||||
} = loadPropertiesAndStyles(properties, styles, darkMode, component);
|
||||
|
||||
const updatedDataReference = useRef([]);
|
||||
|
|
@ -404,8 +405,10 @@ export function Table({
|
|||
|
||||
const tableRef = useRef();
|
||||
|
||||
const columnProperties = useDynamicColumn ? generatedColumn : component.definition.properties.columns.value;
|
||||
|
||||
let columnData = generateColumnsData({
|
||||
columnProperties: useDynamicColumn ? generatedColumn : component.definition.properties.columns.value,
|
||||
columnProperties,
|
||||
columnSizes,
|
||||
currentState,
|
||||
handleCellValueChange: handleExistingRowCellValueChange,
|
||||
|
|
@ -433,6 +436,32 @@ export function Table({
|
|||
[columnData, currentState]
|
||||
);
|
||||
|
||||
const transformations = columnProperties
|
||||
.filter((column) => column.transformation && column.transformation != '{{cellValue}}')
|
||||
.map((column) => ({
|
||||
key: column.key ? column.key : column.name,
|
||||
transformation: column.transformation,
|
||||
}));
|
||||
|
||||
tableData = useMemo(() => {
|
||||
return tableData.map((row) => ({
|
||||
...row,
|
||||
...Object.fromEntries(
|
||||
transformations.map((t) => [
|
||||
t.key,
|
||||
resolveReferences(t.transformation, currentState, row[t.key], { cellValue: row[t.key], rowData: row }),
|
||||
])
|
||||
),
|
||||
}));
|
||||
}, [JSON.stringify([transformations, currentState])]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariables({
|
||||
currentData: tableData,
|
||||
updatedData: tableData,
|
||||
});
|
||||
}, [JSON.stringify(tableData)]);
|
||||
|
||||
const columnDataForAddNewRows = generateColumnsData({
|
||||
columnProperties: useDynamicColumn ? generatedColumn : component.definition.properties.columns.value,
|
||||
columnSizes,
|
||||
|
|
@ -512,7 +541,7 @@ export function Table({
|
|||
}
|
||||
}
|
||||
return _.isEmpty(updatedDataReference.current) ? tableData : updatedDataReference.current;
|
||||
}, [tableData.length, component.definition.properties.data.value, JSON.stringify(properties.data)]);
|
||||
}, [tableData.length, component.definition.properties.data.value, JSON.stringify([properties.data, tableData])]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
|
|
@ -626,7 +655,7 @@ export function Table({
|
|||
Cell: ({ row }) => {
|
||||
return (
|
||||
<div className="d-flex flex-column align-items-center">
|
||||
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
|
||||
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} fireEvent={fireEvent} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
|
@ -716,6 +745,7 @@ export function Table({
|
|||
}
|
||||
});
|
||||
}, [JSON.stringify(tableData), JSON.stringify(tableDetails.changeSet)]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('discardNewlyAddedRows', async function () {
|
||||
if (
|
||||
|
|
@ -883,6 +913,26 @@ export function Table({
|
|||
//hack : in the initial render, data is undefined since, upon feeding data to the table from some query, query inside current state is {}. Hence we added data in the dependency array, now question is should we add data or rows?
|
||||
}, [JSON.stringify(defaultSelectedRow), JSON.stringify(data)]);
|
||||
|
||||
useEffect(() => {
|
||||
// csa for select all rows in table
|
||||
setExposedVariable('selectAllRows', async function () {
|
||||
if (showBulkSelector) {
|
||||
await toggleAllRowsSelected(true);
|
||||
}
|
||||
});
|
||||
// csa for deselect all rows in table
|
||||
setExposedVariable('deselectAllRows', async function () {
|
||||
if (showBulkSelector) {
|
||||
await toggleAllRowsSelected(false);
|
||||
}
|
||||
});
|
||||
}, [JSON.stringify(tableDetails.selectedRowsDetails)]);
|
||||
|
||||
const pageData = page.map((row) => row.original);
|
||||
useEffect(() => {
|
||||
setExposedVariable('currentPageData', pageData);
|
||||
}, [JSON.stringify(pageData)]);
|
||||
|
||||
function downlaodPopover() {
|
||||
const options = [
|
||||
{ dataCy: 'option-download-CSV', text: 'Download as CSV', value: 'csv' },
|
||||
|
|
@ -1013,7 +1063,6 @@ export function Table({
|
|||
)}
|
||||
{showFilterButton && !loadingState && (
|
||||
<div className="position-relative">
|
||||
{''}
|
||||
<Tooltip id="tooltip-for-filter-data" className="tooltip" />
|
||||
<ButtonSolid
|
||||
variant="tertiary"
|
||||
|
|
@ -1427,6 +1476,14 @@ export function Table({
|
|||
{...cellProps}
|
||||
style={{ ...cellProps.style, backgroundColor: cellBackgroundColor ?? 'inherit' }}
|
||||
onClick={(e) => {
|
||||
if (
|
||||
(isEditable || ['rightActions', 'leftActions'].includes(cell.column.id)) &&
|
||||
allowSelection &&
|
||||
!selectRowOnCellEdit
|
||||
) {
|
||||
// to avoid on click event getting propagating to row when td is editable or has action button and allowSelection is true and selectRowOnCellEdit is false
|
||||
e.stopPropagation();
|
||||
}
|
||||
setExposedVariable('selectedCell', {
|
||||
columnName: cell.column.exportValue,
|
||||
columnKey: cell.column.key,
|
||||
|
|
@ -1683,20 +1740,19 @@ export function Table({
|
|||
</div>
|
||||
</div>
|
||||
)}
|
||||
{tableDetails.filterDetails.filtersVisible && (
|
||||
<Filter
|
||||
hideFilters={hideFilters}
|
||||
filters={tableDetails.filterDetails.filters}
|
||||
columns={columnData.map((column) => {
|
||||
return { name: column.Header, value: column.id };
|
||||
})}
|
||||
mergeToFilterDetails={mergeToFilterDetails}
|
||||
filterDetails={tableDetails.filterDetails}
|
||||
darkMode={darkMode}
|
||||
setAllFilters={setAllFilters}
|
||||
fireEvent={fireEvent}
|
||||
/>
|
||||
)}
|
||||
<Filter
|
||||
hideFilters={hideFilters}
|
||||
filters={tableDetails.filterDetails.filters}
|
||||
columns={columnData.map((column) => {
|
||||
return { name: column.Header, value: column.id };
|
||||
})}
|
||||
mergeToFilterDetails={mergeToFilterDetails}
|
||||
filterDetails={tableDetails.filterDetails}
|
||||
darkMode={darkMode}
|
||||
setAllFilters={setAllFilters}
|
||||
fireEvent={fireEvent}
|
||||
setExposedVariable={setExposedVariable}
|
||||
/>
|
||||
{tableDetails.addNewRowsDetails.addingNewRows && (
|
||||
<AddNewRowComponent
|
||||
hideAddNewRowPopup={hideAddNewRowPopup}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ export default function autogenerateColumns(
|
|||
if (dynamicColumn.length > 0 && dynamicColumn[0].name) {
|
||||
const generatedColumns = dynamicColumn.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
id: uuidv4(),
|
||||
...item,
|
||||
name: item?.name,
|
||||
key: item?.key || item?.name,
|
||||
autogenerated: true,
|
||||
|
|
|
|||
|
|
@ -219,7 +219,6 @@ export default function generateColumnsData({
|
|||
});
|
||||
|
||||
const { isValid, validationError } = validationData;
|
||||
console.log('validationData', column.minValue, column.maxValue, validationData);
|
||||
const cellStyles = {
|
||||
color: textColor ?? '',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ export default function loadPropertiesAndStyles(properties, styles, darkMode, co
|
|||
const showAddNewRowButton = properties?.showAddNewRowButton ?? true;
|
||||
const allowSelection = properties?.allowSelection ?? (showBulkSelector || highlightSelectedRow) ? true : false;
|
||||
const defaultSelectedRow = properties?.defaultSelectedRow ?? { id: 1 };
|
||||
const selectRowOnCellEdit = properties?.selectRowOnCellEdit ?? true;
|
||||
|
||||
return {
|
||||
color,
|
||||
serverSidePagination,
|
||||
|
|
@ -107,5 +109,6 @@ export default function loadPropertiesAndStyles(properties, styles, darkMode, co
|
|||
defaultSelectedRow,
|
||||
showAddNewRowButton,
|
||||
allowSelection,
|
||||
selectRowOnCellEdit,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import DOMPurify from 'dompurify';
|
||||
import Markdown from 'react-markdown';
|
||||
import './text.scss';
|
||||
import Loader from '@/ToolJetUI/Loader/Loader';
|
||||
|
||||
export const Text = function Text({
|
||||
height,
|
||||
properties,
|
||||
styles,
|
||||
darkMode,
|
||||
setExposedVariable,
|
||||
setExposedVariables,
|
||||
dataCy,
|
||||
}) {
|
||||
const VERTICAL_ALIGNMENT_VS_CSS_VALUE = {
|
||||
top: 'flex-start',
|
||||
center: 'center',
|
||||
bottom: 'flex-end',
|
||||
};
|
||||
|
||||
export const Text = function Text({ height, properties, fireEvent, styles, darkMode, setExposedVariable, dataCy }) {
|
||||
let {
|
||||
textSize,
|
||||
textColor,
|
||||
|
|
@ -24,50 +25,85 @@ export const Text = function Text({
|
|||
letterSpacing,
|
||||
wordSpacing,
|
||||
fontVariant,
|
||||
disabledState,
|
||||
boxShadow,
|
||||
verticalAlignment,
|
||||
borderColor,
|
||||
borderRadius,
|
||||
isScrollRequired,
|
||||
} = styles;
|
||||
const { loadingState } = properties;
|
||||
const { loadingState, textFormat, disabledState } = properties;
|
||||
const [text, setText] = useState(() => computeText());
|
||||
const [visibility, setVisibility] = useState(styles.visibility);
|
||||
const [visibility, setVisibility] = useState(properties.visibility);
|
||||
const [isLoading, setLoading] = useState(loadingState);
|
||||
const [isDisabled, setIsDisabled] = useState(disabledState);
|
||||
const color = ['#000', '#000000'].includes(textColor) ? (darkMode ? '#fff' : '#000') : textColor;
|
||||
|
||||
useEffect(() => {
|
||||
if (visibility !== styles.visibility) setVisibility(styles.visibility);
|
||||
if (visibility !== properties.visibility) setVisibility(properties.visibility);
|
||||
if (isLoading !== loadingState) setLoading(loadingState);
|
||||
if (isDisabled !== disabledState) setIsDisabled(disabledState);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [styles.visibility]);
|
||||
}, [properties.visibility, loadingState, disabledState]);
|
||||
|
||||
useEffect(() => {
|
||||
const text = computeText();
|
||||
setText(text);
|
||||
setExposedVariable('text', text);
|
||||
|
||||
const exposedVariables = {
|
||||
text: text,
|
||||
setText: async function (text) {
|
||||
setText(text);
|
||||
setExposedVariable('text', text);
|
||||
},
|
||||
visibility: async function (value) {
|
||||
setVisibility(value);
|
||||
},
|
||||
};
|
||||
setExposedVariables(exposedVariables);
|
||||
setExposedVariable('setText', async function (text) {
|
||||
setText(text);
|
||||
setExposedVariable('text', text);
|
||||
});
|
||||
setExposedVariable('clear', async function (text) {
|
||||
setText('');
|
||||
setExposedVariable('text', '');
|
||||
});
|
||||
setExposedVariable('isVisible', properties.visibility);
|
||||
setExposedVariable('isLoading', loadingState);
|
||||
setExposedVariable('isDisabled', disabledState);
|
||||
|
||||
setExposedVariable('visibility', async function (value) {
|
||||
setVisibility(value);
|
||||
});
|
||||
|
||||
setExposedVariable('setVisibility', async function (value) {
|
||||
setVisibility(value);
|
||||
});
|
||||
|
||||
setExposedVariable('setLoading', async function (value) {
|
||||
setLoading(value);
|
||||
});
|
||||
|
||||
setExposedVariable('setDisable', async function (value) {
|
||||
setIsDisabled(value);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [properties.text, setText, setVisibility]);
|
||||
}, [
|
||||
properties.text,
|
||||
setText,
|
||||
setVisibility,
|
||||
properties.visibility,
|
||||
loadingState,
|
||||
disabledState,
|
||||
setIsDisabled,
|
||||
setLoading,
|
||||
]);
|
||||
|
||||
function computeText() {
|
||||
return properties.text === 0 || properties.text === false ? properties.text?.toString() : properties.text;
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
fireEvent('onClick');
|
||||
};
|
||||
|
||||
const computedStyles = {
|
||||
backgroundColor,
|
||||
height: `${height}px`,
|
||||
backgroundColor: darkMode && ['#edeff5'].includes(backgroundColor) ? '#2f3c4c' : backgroundColor,
|
||||
color,
|
||||
height,
|
||||
display: visibility ? 'flex' : 'none',
|
||||
alignItems: 'center',
|
||||
textAlign,
|
||||
fontWeight: fontWeight ? fontWeight : fontWeight === '0' ? 0 : 'normal',
|
||||
lineHeight: lineHeight ?? 1.5,
|
||||
textDecoration: decoration ?? 'none',
|
||||
|
|
@ -78,20 +114,51 @@ export const Text = function Text({
|
|||
letterSpacing: `${letterSpacing}px` ?? '0px',
|
||||
wordSpacing: `${wordSpacing}px` ?? '0px',
|
||||
boxShadow,
|
||||
border: '1px solid',
|
||||
borderColor: darkMode && ['#f2f2f5'].includes(borderColor) ? '#2f3c4c' : borderColor ? borderColor : 'transparent',
|
||||
borderRadius: borderRadius ? `${borderRadius}px` : '0px',
|
||||
fontSize: `${textSize}px`,
|
||||
};
|
||||
|
||||
const commonStyles = {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflowY: isScrollRequired == 'enabled' ? 'auto' : 'hidden',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: VERTICAL_ALIGNMENT_VS_CSS_VALUE[verticalAlignment],
|
||||
textAlign,
|
||||
overflowX: isScrollRequired === 'disabled' && 'hidden',
|
||||
};
|
||||
|
||||
return (
|
||||
<div data-disabled={disabledState} className="text-widget" style={computedStyles} data-cy={dataCy}>
|
||||
{!loadingState && (
|
||||
<div
|
||||
style={{ width: '100%', fontSize: textSize }}
|
||||
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(text || '0') }}
|
||||
/>
|
||||
<div
|
||||
data-disabled={isDisabled}
|
||||
className="text-widget"
|
||||
style={computedStyles}
|
||||
data-cy={dataCy}
|
||||
onMouseOver={() => {
|
||||
fireEvent('onHover');
|
||||
}}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{!isLoading && (
|
||||
<div style={commonStyles} className="text-widget-section">
|
||||
{textFormat === 'plainText' && <div>{text}</div>}
|
||||
{textFormat === 'markdown' && <Markdown className={'reactMarkdown'}>{text}</Markdown>}
|
||||
{(textFormat === 'html' || !textFormat) && (
|
||||
<div
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: DOMPurify.sanitize(text || ''),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{loadingState === true && (
|
||||
<div style={{ width: '100%' }}>
|
||||
{isLoading === true && (
|
||||
<div style={{ width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<center>
|
||||
<div className="spinner-border" role="status"></div>
|
||||
<Loader width="16" absolute={false} />
|
||||
</center>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { resolveReferences } from '@/_helpers/utils';
|
||||
import { useCurrentState } from '@/_stores/currentStateStore';
|
||||
import { ToolTip } from '@/_components/ToolTip';
|
||||
import * as Icons from '@tabler/icons-react';
|
||||
import Loader from '@/ToolJetUI/Loader/Loader';
|
||||
const tinycolor = require('tinycolor2');
|
||||
import Label from '@/_ui/Label';
|
||||
|
||||
export const TextInput = function TextInput({
|
||||
height,
|
||||
|
|
@ -11,33 +18,122 @@ export const TextInput = function TextInput({
|
|||
component,
|
||||
darkMode,
|
||||
dataCy,
|
||||
isResizing,
|
||||
adjustHeightBasedOnAlignment,
|
||||
currentLayout,
|
||||
}) {
|
||||
const textInputRef = useRef();
|
||||
const labelRef = useRef();
|
||||
|
||||
const [disable, setDisable] = useState(styles.disabledState);
|
||||
const { loadingState, tooltip, disabledState, label, placeholder } = properties;
|
||||
|
||||
const {
|
||||
padding,
|
||||
borderRadius,
|
||||
borderColor,
|
||||
backgroundColor,
|
||||
textColor,
|
||||
boxShadow,
|
||||
width,
|
||||
alignment,
|
||||
direction,
|
||||
color,
|
||||
auto,
|
||||
errTextColor,
|
||||
iconColor,
|
||||
accentColor,
|
||||
} = styles;
|
||||
const [disable, setDisable] = useState(disabledState || loadingState);
|
||||
const [value, setValue] = useState(properties.value);
|
||||
const [visibility, setVisibility] = useState(styles.visibility);
|
||||
const [visibility, setVisibility] = useState(properties.visibility);
|
||||
const { isValid, validationError } = validate(value);
|
||||
const [showValidationError, setShowValidationError] = useState(false);
|
||||
const currentState = useCurrentState();
|
||||
const isMandatory = resolveReferences(component?.definition?.validation?.mandatory?.value, currentState);
|
||||
const [labelWidth, setLabelWidth] = useState(0);
|
||||
const defaultAlignment = alignment === 'side' || alignment === 'top' ? alignment : 'side';
|
||||
const [loading, setLoading] = useState(loadingState);
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
const _width = (width / 100) * 70; // Max width which label can go is 70% for better UX calculate width based on this value
|
||||
|
||||
const computedStyles = {
|
||||
height,
|
||||
borderRadius: `${styles.borderRadius}px`,
|
||||
color: darkMode && styles.textColor === '#000' ? '#fff' : styles.textColor,
|
||||
borderColor: styles.borderColor,
|
||||
backgroundColor: darkMode && ['#fff'].includes(styles.backgroundColor) ? '#232e3c' : styles.backgroundColor,
|
||||
boxShadow: styles.boxShadow,
|
||||
height: height == 36 ? (padding == 'default' ? '36px' : '38px') : padding == 'default' ? height : height + 2,
|
||||
borderRadius: `${borderRadius}px`,
|
||||
color: darkMode && textColor === '#11181C' ? '#ECEDEE' : textColor,
|
||||
borderColor: isFocused
|
||||
? accentColor
|
||||
: ['#D7DBDF'].includes(borderColor)
|
||||
? darkMode
|
||||
? '#6D757D7A'
|
||||
: '#6A727C47'
|
||||
: borderColor,
|
||||
'--tblr-input-border-color-darker': tinycolor(borderColor).darken(24).toString(),
|
||||
backgroundColor: darkMode && ['#fff'].includes(backgroundColor) ? '#313538' : backgroundColor,
|
||||
boxShadow: boxShadow,
|
||||
padding: styles.iconVisibility ? '8px 10px 8px 29px' : '8px 10px 8px 10px',
|
||||
// flex: padding !== 'none' && 1,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
};
|
||||
const loaderStyle = {
|
||||
right:
|
||||
direction === 'right' &&
|
||||
defaultAlignment === 'side' &&
|
||||
((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0))
|
||||
? `${labelWidth + 11}px`
|
||||
: '11px',
|
||||
top: `${
|
||||
defaultAlignment === 'top'
|
||||
? ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
|
||||
'calc(50% + 10px)'
|
||||
: ''
|
||||
}`,
|
||||
transform:
|
||||
defaultAlignment === 'top' &&
|
||||
((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)) &&
|
||||
' translateY(-50%)',
|
||||
zIndex: 3,
|
||||
};
|
||||
useEffect(() => {
|
||||
if (labelRef?.current) {
|
||||
const absolutewidth = labelRef?.current?.getBoundingClientRect()?.width;
|
||||
setLabelWidth(absolutewidth);
|
||||
} else setLabelWidth(0);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
isResizing,
|
||||
width,
|
||||
auto,
|
||||
defaultAlignment,
|
||||
component?.definition?.styles?.iconVisibility?.value,
|
||||
label?.length,
|
||||
isMandatory,
|
||||
padding,
|
||||
direction,
|
||||
alignment,
|
||||
labelRef?.current?.getBoundingClientRect()?.width,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
disable !== styles.disabledState && setDisable(styles.disabledState);
|
||||
setExposedVariable('label', label);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [styles.disabledState]);
|
||||
}, [label]);
|
||||
|
||||
useEffect(() => {
|
||||
visibility !== styles.visibility && setVisibility(styles.visibility);
|
||||
disable !== disabledState && setDisable(disabledState);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [styles.visibility]);
|
||||
}, [disabledState]);
|
||||
|
||||
useEffect(() => {
|
||||
visibility !== properties.visibility && setVisibility(properties.visibility);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [properties.visibility]);
|
||||
|
||||
useEffect(() => {
|
||||
loading !== loadingState && setLoading(loadingState);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [loadingState]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('isValid', isValid);
|
||||
|
|
@ -66,7 +162,6 @@ export const TextInput = function TextInput({
|
|||
},
|
||||
};
|
||||
setExposedVariables(exposedVariables);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
|
|
@ -84,48 +179,176 @@ export const TextInput = function TextInput({
|
|||
setExposedVariables(exposedVariables);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [setValue]);
|
||||
const iconName = styles.icon; // Replace with the name of the icon you want
|
||||
// eslint-disable-next-line import/namespace
|
||||
const IconElement = Icons[iconName] == undefined ? Icons['IconHome2'] : Icons[iconName];
|
||||
// eslint-disable-next-line import/namespace
|
||||
|
||||
return (
|
||||
<div data-disabled={disable} className={`text-input ${visibility || 'invisible'}`}>
|
||||
<input
|
||||
ref={textInputRef}
|
||||
onKeyUp={(e) => {
|
||||
if (e.key == 'Enter') {
|
||||
useEffect(() => {
|
||||
if (alignment == 'top' && ((label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)))
|
||||
adjustHeightBasedOnAlignment(true);
|
||||
else adjustHeightBasedOnAlignment(false);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [alignment, label?.length, currentLayout]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('isMandatory', isMandatory);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isMandatory]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('isLoading', loading);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [loading]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('setLoading', async function (loading) {
|
||||
setLoading(loading);
|
||||
setExposedVariable('isLoading', loading);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [properties.loadingState]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('isVisible', visibility);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [visibility]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('setVisibility', async function (state) {
|
||||
setVisibility(state);
|
||||
setExposedVariable('isVisible', state);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [properties.visibility]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('setDisable', async function (disable) {
|
||||
setDisable(disable);
|
||||
setExposedVariable('isDisabled', disable);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [disabledState]);
|
||||
|
||||
useEffect(() => {
|
||||
setExposedVariable('isDisabled', disable);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [disable]);
|
||||
|
||||
const renderInput = () => (
|
||||
<>
|
||||
<div
|
||||
data-disabled={disable || loading}
|
||||
className={`text-input d-flex ${
|
||||
defaultAlignment === 'top' &&
|
||||
((width != 0 && label?.length != 0) || (auto && width == 0 && label && label?.length != 0))
|
||||
? 'flex-column'
|
||||
: 'align-items-center '
|
||||
} ${direction === 'right' && defaultAlignment === 'side' ? 'flex-row-reverse' : ''}
|
||||
${direction === 'right' && defaultAlignment === 'top' ? 'text-right' : ''}
|
||||
${visibility || 'invisible'}`}
|
||||
style={{
|
||||
position: 'relative',
|
||||
whiteSpace: 'nowrap',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Label
|
||||
label={label}
|
||||
width={width}
|
||||
labelRef={labelRef}
|
||||
darkMode={darkMode}
|
||||
color={color}
|
||||
defaultAlignment={defaultAlignment}
|
||||
direction={direction}
|
||||
auto={auto}
|
||||
isMandatory={isMandatory}
|
||||
_width={_width}
|
||||
/>
|
||||
{component?.definition?.styles?.iconVisibility?.value && !isResizing && (
|
||||
<IconElement
|
||||
data-cy={'text-input-icon'}
|
||||
style={{
|
||||
width: '16px',
|
||||
height: '16px',
|
||||
left:
|
||||
direction === 'right'
|
||||
? '11px'
|
||||
: defaultAlignment === 'top'
|
||||
? '11px'
|
||||
: (label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)
|
||||
? `${labelWidth + 11}px`
|
||||
: '11px', //23 :: is 10 px inside the input + 1 px border + 12px margin right
|
||||
position: 'absolute',
|
||||
top: `${
|
||||
defaultAlignment === 'side'
|
||||
? '50%'
|
||||
: (label?.length > 0 && width > 0) || (auto && width == 0 && label && label?.length != 0)
|
||||
? 'calc(50% + 10px)'
|
||||
: '50%'
|
||||
}`,
|
||||
transform: ' translateY(-50%)',
|
||||
color: iconColor,
|
||||
zIndex: 3,
|
||||
}}
|
||||
stroke={1.5}
|
||||
/>
|
||||
)}
|
||||
<input
|
||||
data-cy={`label-${String(component.name).toLowerCase()}`}
|
||||
ref={textInputRef}
|
||||
className={`tj-text-input-widget ${
|
||||
!isValid && showValidationError ? 'is-invalid' : ''
|
||||
} validation-without-icon ${darkMode && 'dark-theme-placeholder'}`}
|
||||
onKeyUp={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
setValue(e.target.value);
|
||||
setExposedVariable('value', e.target.value);
|
||||
fireEvent('onEnterPressed');
|
||||
}
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setValue(e.target.value);
|
||||
setExposedVariable('value', e.target.value);
|
||||
fireEvent('onEnterPressed');
|
||||
}
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setValue(e.target.value);
|
||||
setExposedVariable('value', e.target.value);
|
||||
fireEvent('onChange');
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
setShowValidationError(true);
|
||||
e.stopPropagation();
|
||||
fireEvent('onBlur');
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
e.stopPropagation();
|
||||
fireEvent('onFocus');
|
||||
}}
|
||||
type="text"
|
||||
className={`form-control ${!isValid ? 'is-invalid' : ''} validation-without-icon ${
|
||||
darkMode && 'dark-theme-placeholder'
|
||||
}`}
|
||||
placeholder={properties.placeholder}
|
||||
style={computedStyles}
|
||||
value={value}
|
||||
data-cy={dataCy}
|
||||
/>
|
||||
<div
|
||||
className="invalid-feedback"
|
||||
data-cy={`${String(component.name).toLowerCase()}-invalid-feedback`}
|
||||
style={{ color: styles.errTextColor }}
|
||||
>
|
||||
{showValidationError && validationError}
|
||||
fireEvent('onChange');
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
setShowValidationError(true);
|
||||
setIsFocused(false);
|
||||
e.stopPropagation();
|
||||
fireEvent('onBlur');
|
||||
setIsFocused(false);
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
setIsFocused(true);
|
||||
e.stopPropagation();
|
||||
|
||||
setTimeout(() => {
|
||||
fireEvent('onFocus');
|
||||
}, 0);
|
||||
}}
|
||||
type="text"
|
||||
placeholder={placeholder}
|
||||
style={computedStyles}
|
||||
value={value}
|
||||
disabled={disable || loading}
|
||||
/>
|
||||
{loading && <Loader style={{ ...loaderStyle }} width="16" />}
|
||||
</div>
|
||||
</div>
|
||||
{showValidationError && visibility && (
|
||||
<div
|
||||
className="tj-text-sm"
|
||||
data-cy={`${String(component.name).toLowerCase()}-invalid-feedback`}
|
||||
style={{
|
||||
color: errTextColor,
|
||||
textAlign: direction == 'left' && 'end',
|
||||
}}
|
||||
>
|
||||
{showValidationError && validationError}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
return <>{renderInput()}</>;
|
||||
};
|
||||
|
|
|
|||
39
frontend/src/Editor/Components/numberinput.scss
Normal file
39
frontend/src/Editor/Components/numberinput.scss
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
.arrow {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
position: absolute;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.numberinput-label {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.input-number::-webkit-inner-spin-button,
|
||||
.input-number::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.input-number {
|
||||
align-items: center;
|
||||
-webkit-appearance: none !important;
|
||||
/* Remove clear button on Safari */
|
||||
-moz-appearance: textfield !important;
|
||||
/* Remove clear button on Firefox */
|
||||
appearance: none !important;
|
||||
/* Remove clear button on modern browsers */
|
||||
}
|
||||
|
||||
.numberinput-down-arrow {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.numberinput-up-arrow {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
}
|
||||
25
frontend/src/Editor/Components/text.scss
Normal file
25
frontend/src/Editor/Components/text.scss
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
|
||||
.reactMarkdown {
|
||||
p,h1,h2,h3,h4,h5,h6 {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.text-widget-section {
|
||||
scrollbar-color: transparent transparent;
|
||||
scrollbar-width: thin;
|
||||
&::-webkit-scrollbar {
|
||||
background-color: transparent;
|
||||
width: 6px;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: transparent;
|
||||
}
|
||||
&:hover{
|
||||
scrollbar-color: #a0a6ae transparent;
|
||||
}
|
||||
}
|
||||
|
|
@ -13,13 +13,14 @@ export const ConfigHandle = function ConfigHandle({
|
|||
customClassName = '',
|
||||
configWidgetHandlerForModalComponent = false,
|
||||
isVersionReleased,
|
||||
isVerticalResizingAllowed,
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={`config-handle ${customClassName}`}
|
||||
ref={dragRef}
|
||||
style={{
|
||||
top: position === 'top' ? '-22px' : widgetTop + widgetHeight - 10,
|
||||
top: position === 'top' ? (!isVerticalResizingAllowed() ? '-20px' : '-22px') : widgetTop + widgetHeight - 10,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ export const Container = ({
|
|||
// Dont update first time to skip
|
||||
// redundant save on app definition load
|
||||
const firstUpdate = useRef(true);
|
||||
|
||||
const { showComments, currentLayout } = useEditorStore(
|
||||
(state) => ({
|
||||
showComments: state?.showComments,
|
||||
|
|
@ -74,7 +73,7 @@ export const Container = ({
|
|||
const gridWidth = canvasWidth / NO_OF_GRIDS;
|
||||
const styles = {
|
||||
width: currentLayout === 'mobile' ? deviceWindowWidth : '100%',
|
||||
maxWidth: `${canvasWidth}px`,
|
||||
maxWidth: currentLayout === 'mobile' ? deviceWindowWidth : `${canvasWidth}px`,
|
||||
backgroundSize: `${gridWidth}px 10px`,
|
||||
};
|
||||
|
||||
|
|
@ -240,7 +239,6 @@ export const Container = ({
|
|||
|
||||
const bottomPadding = mode === 'view' ? 100 : 300;
|
||||
const frameHeight = mode === 'view' ? 45 : 85;
|
||||
|
||||
setCanvasHeight(`max(100vh - ${frameHeight}px, ${maxHeight + bottomPadding}px)`);
|
||||
},
|
||||
[setCanvasHeight, currentLayout, mode]
|
||||
|
|
@ -562,7 +560,6 @@ export const Container = ({
|
|||
},
|
||||
[setIsDragging]
|
||||
);
|
||||
|
||||
const containerProps = useMemo(() => {
|
||||
return {
|
||||
mode,
|
||||
|
|
|
|||
|
|
@ -24,33 +24,6 @@ const resizerClasses = {
|
|||
topLeft: 'top-left',
|
||||
};
|
||||
|
||||
const resizerStyles = {
|
||||
topRight: {
|
||||
width: '8px',
|
||||
height: '8px',
|
||||
right: '-4px',
|
||||
top: '-4px',
|
||||
},
|
||||
bottomRight: {
|
||||
width: '8px',
|
||||
height: '8px',
|
||||
right: '-4px',
|
||||
bottom: '-4px',
|
||||
},
|
||||
bottomLeft: {
|
||||
width: '8px',
|
||||
height: '8px',
|
||||
left: '-4px',
|
||||
bottom: '-4px',
|
||||
},
|
||||
topLeft: {
|
||||
width: '8px',
|
||||
height: '8px',
|
||||
left: '-4px',
|
||||
top: '-4px',
|
||||
},
|
||||
};
|
||||
|
||||
function computeWidth(currentLayoutOptions) {
|
||||
return `${currentLayoutOptions?.width}%`;
|
||||
}
|
||||
|
|
@ -121,6 +94,70 @@ export const DraggableBox = React.memo(
|
|||
shallow
|
||||
);
|
||||
const currentState = useCurrentState();
|
||||
const [boxHeight, setboxHeight] = useState(layoutData?.height); // height for layouting with top and side values
|
||||
|
||||
const resizerStyles = {
|
||||
topRight: {
|
||||
width: '8px',
|
||||
height: '8px',
|
||||
right: '-4px',
|
||||
top: '-4px',
|
||||
},
|
||||
bottomRight: {
|
||||
width: '8px',
|
||||
height: '8px',
|
||||
right: '-4px',
|
||||
bottom: '-4px',
|
||||
},
|
||||
bottomLeft: {
|
||||
width: '8px',
|
||||
height: '8px',
|
||||
left: '-4px',
|
||||
bottom: '-4px',
|
||||
},
|
||||
topLeft: {
|
||||
width: '8px',
|
||||
height: '8px',
|
||||
left: '-4px',
|
||||
top: '-4px',
|
||||
},
|
||||
};
|
||||
if (!isVerticalResizingAllowed()) {
|
||||
resizerStyles.right = {
|
||||
position: 'absolute',
|
||||
height: '20px',
|
||||
width: '5px',
|
||||
right: '-3px',
|
||||
background: '#4368E3',
|
||||
borderRadius: '8px',
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
display:
|
||||
(mode === 'edit' && !readOnly && mouseOver) || isResizing || isDragging2 || isSelectedComponent
|
||||
? isVerticalResizingAllowed()
|
||||
? 'none'
|
||||
: 'block'
|
||||
: 'none',
|
||||
zIndex: 5,
|
||||
};
|
||||
resizerStyles.left = {
|
||||
position: 'absolute',
|
||||
height: '20px',
|
||||
width: '5px',
|
||||
left: '-3px',
|
||||
background: '#4368E3',
|
||||
borderRadius: '8px',
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
display:
|
||||
(mode === 'edit' && !readOnly && mouseOver) || isResizing || isDragging2 || isSelectedComponent
|
||||
? isVerticalResizingAllowed()
|
||||
? 'none'
|
||||
: 'block'
|
||||
: 'none',
|
||||
zIndex: 5,
|
||||
};
|
||||
}
|
||||
const [{ isDragging }, drag, preview] = useDrag(
|
||||
() => ({
|
||||
type: ItemTypes.BOX,
|
||||
|
|
@ -165,7 +202,6 @@ export const DraggableBox = React.memo(
|
|||
display: 'inline-block',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: '0px',
|
||||
};
|
||||
|
||||
let _refProps = {};
|
||||
|
|
@ -189,11 +225,9 @@ export const DraggableBox = React.memo(
|
|||
width: 445,
|
||||
height: 500,
|
||||
};
|
||||
|
||||
const layoutData = inCanvas ? layouts[currentLayout] || defaultData : defaultData;
|
||||
const gridWidth = canvasWidth / NO_OF_GRIDS;
|
||||
const width = (canvasWidth * layoutData.width) / NO_OF_GRIDS;
|
||||
|
||||
const configWidgetHandlerForModalComponent =
|
||||
!isSelectedComponent &&
|
||||
component.component === 'Modal' &&
|
||||
|
|
@ -203,7 +237,21 @@ export const DraggableBox = React.memo(
|
|||
if (selectionInProgress) return;
|
||||
setHoveredComponent(id);
|
||||
};
|
||||
function isVerticalResizingAllowed() {
|
||||
// Return true if vertical resizing is allowed, false otherwise
|
||||
return (
|
||||
mode === 'edit' &&
|
||||
component.component !== 'TextInput' &&
|
||||
component.component !== 'PasswordInput' &&
|
||||
component.component !== 'NumberInput' &&
|
||||
!readOnly
|
||||
);
|
||||
}
|
||||
|
||||
const adjustHeightBasedOnAlignment = (increase) => {
|
||||
if (increase) return setboxHeight(layoutData?.height + 20);
|
||||
else return setboxHeight(layoutData?.height);
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
|
|
@ -240,7 +288,7 @@ export const DraggableBox = React.memo(
|
|||
dragGrid={[gridWidth, 10]}
|
||||
size={{
|
||||
width: width,
|
||||
height: layoutData.height,
|
||||
height: isVerticalResizingAllowed() ? layoutData.height : boxHeight,
|
||||
}}
|
||||
position={{
|
||||
x: layoutData ? (layoutData.left * canvasWidth) / 100 : 0,
|
||||
|
|
@ -260,7 +308,16 @@ export const DraggableBox = React.memo(
|
|||
}}
|
||||
resizeHandleClasses={isSelectedComponent || mouseOver ? resizerClasses : {}}
|
||||
resizeHandleStyles={resizerStyles}
|
||||
enableResizing={mode === 'edit' && !readOnly}
|
||||
enableResizing={{
|
||||
top: mode == 'edit' && !readOnly && isVerticalResizingAllowed(),
|
||||
right: mode == 'edit' && !readOnly && true,
|
||||
bottom: mode == 'edit' && !readOnly && isVerticalResizingAllowed(),
|
||||
left: mode == 'edit' && !readOnly && true,
|
||||
topRight: mode == 'edit' && !readOnly && isVerticalResizingAllowed(),
|
||||
bottomRight: mode == 'edit' && !readOnly && isVerticalResizingAllowed(),
|
||||
bottomLeft: mode == 'edit' && !readOnly && isVerticalResizingAllowed(),
|
||||
topLeft: mode == 'edit' && !readOnly && isVerticalResizingAllowed(),
|
||||
}}
|
||||
disableDragging={mode !== 'edit' || readOnly}
|
||||
onDragStop={(e, direction) => {
|
||||
setDragging(false);
|
||||
|
|
@ -288,6 +345,7 @@ export const DraggableBox = React.memo(
|
|||
widgetHeight={layoutData.height}
|
||||
isMultipleComponentsSelected={isMultipleComponentsSelected}
|
||||
configWidgetHandlerForModalComponent={configWidgetHandlerForModalComponent}
|
||||
isVerticalResizingAllowed={isVerticalResizingAllowed}
|
||||
/>
|
||||
)}
|
||||
{/* Adding a sentry's error boundary to differentiate between our generic error boundary and one from editor's component */}
|
||||
|
|
@ -320,6 +378,9 @@ export const DraggableBox = React.memo(
|
|||
allComponents={allComponents}
|
||||
sideBarDebugger={sideBarDebugger}
|
||||
childComponents={childComponents}
|
||||
isResizing={isResizing}
|
||||
adjustHeightBasedOnAlignment={adjustHeightBasedOnAlignment}
|
||||
currentLayout={currentLayout}
|
||||
/>
|
||||
</Sentry.ErrorBoundary>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -538,6 +538,8 @@ const EditorComponent = (props) => {
|
|||
};
|
||||
|
||||
const onNameChanged = (newName) => {
|
||||
app.name = newName;
|
||||
updateState({ appName: newName, app: app });
|
||||
updateState({ appName: newName });
|
||||
setWindowTitle({ page: pageTitles.EDITOR, appName: newName });
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,15 @@ import { useEditorStore } from '@/_stores/editorStore';
|
|||
import { shallow } from 'zustand/shallow';
|
||||
import SolidIcon from '@/_ui/Icon/SolidIcons';
|
||||
|
||||
function HeaderActions({ handleUndo, canUndo, handleRedo, canRedo }) {
|
||||
function HeaderActions({
|
||||
handleUndo,
|
||||
canUndo,
|
||||
handleRedo,
|
||||
canRedo,
|
||||
showToggleLayoutBtn,
|
||||
showUndoRedoBtn,
|
||||
showFullWidth,
|
||||
}) {
|
||||
const darkMode = localStorage.getItem('darkMode') === 'true';
|
||||
const { currentLayout, toggleCurrentLayout } = useEditorStore(
|
||||
(state) => ({
|
||||
|
|
@ -15,94 +23,100 @@ function HeaderActions({ handleUndo, canUndo, handleRedo, canRedo }) {
|
|||
shallow
|
||||
);
|
||||
return (
|
||||
<div className="editor-header-actions">
|
||||
<div style={{ borderRadius: 6 }}>
|
||||
<div
|
||||
className="d-flex align-items-center p-1 current-layout"
|
||||
style={{ height: 28, background: darkMode ? '#202425' : '#F1F3F5', borderRadius: 6 }}
|
||||
role="tablist"
|
||||
aria-orientation="horizontal"
|
||||
>
|
||||
<button
|
||||
className={cx('btn border-0 p-1', {
|
||||
'bg-transparent': currentLayout !== 'desktop',
|
||||
'bg-white': currentLayout === 'desktop',
|
||||
'opacity-100': currentLayout === 'desktop',
|
||||
})}
|
||||
style={{ height: 20 }}
|
||||
role="tab"
|
||||
type="button"
|
||||
aria-selected="true"
|
||||
tabIndex="0"
|
||||
onClick={() => toggleCurrentLayout('desktop')}
|
||||
data-cy={`button-change-layout-to-desktop`}
|
||||
<div className={cx('editor-header-actions', { 'w-100': showFullWidth })}>
|
||||
{showToggleLayoutBtn && (
|
||||
<div style={{ borderRadius: 6 }} className={cx({ 'w-100': showFullWidth })}>
|
||||
<div
|
||||
className="d-flex align-items-center p-1 current-layout"
|
||||
style={{ height: 28, background: darkMode ? '#202425' : '#F1F3F5', borderRadius: 6 }}
|
||||
role="tablist"
|
||||
aria-orientation="horizontal"
|
||||
>
|
||||
<button
|
||||
className={cx('btn border-0 p-1', {
|
||||
'bg-transparent': currentLayout !== 'desktop',
|
||||
'bg-white opacity-100': currentLayout === 'desktop',
|
||||
'w-100': showFullWidth,
|
||||
})}
|
||||
style={{ height: 20 }}
|
||||
role="tab"
|
||||
type="button"
|
||||
aria-selected="true"
|
||||
tabIndex="0"
|
||||
onClick={() => toggleCurrentLayout('desktop')}
|
||||
data-cy={`button-change-layout-to-desktop`}
|
||||
>
|
||||
<SolidIcon
|
||||
name="computer"
|
||||
width="14"
|
||||
fill={currentLayout === 'desktop' ? 'var(--slate12)' : 'var(--slate8)'}
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
className={cx('btn border-0 p-1', {
|
||||
'bg-transparent': currentLayout !== 'mobile',
|
||||
'bg-white opacity-100': currentLayout === 'mobile',
|
||||
'w-100': showFullWidth,
|
||||
})}
|
||||
role="tab"
|
||||
type="button"
|
||||
style={{ height: 20 }}
|
||||
aria-selected="false"
|
||||
tabIndex="-1"
|
||||
onClick={() => {
|
||||
toggleCurrentLayout('mobile');
|
||||
}}
|
||||
data-cy={`button-change-layout-to-mobile`}
|
||||
>
|
||||
<SolidIcon
|
||||
name="mobile"
|
||||
width="14"
|
||||
fill={currentLayout !== 'desktop' ? 'var(--slate12)' : 'var(--slate8)'}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{showUndoRedoBtn && (
|
||||
<div className="undo-redo-container">
|
||||
<div
|
||||
onClick={handleUndo}
|
||||
className="tj-ghost-black-btn"
|
||||
data-tooltip-id="tooltip-for-undo"
|
||||
data-tooltip-content="Undo"
|
||||
data-cy={`editor-undo-button`}
|
||||
>
|
||||
<SolidIcon
|
||||
name="computer"
|
||||
width="14"
|
||||
fill={currentLayout === 'desktop' ? 'var(--slate12)' : 'var(--slate8)'}
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill={darkMode ? '#fff' : '#2c3e50'}
|
||||
name="arrowforwardup"
|
||||
className={cx('cursor-pointer', {
|
||||
disabled: !canUndo,
|
||||
})}
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
className={cx('btn border-0 p-1', {
|
||||
'bg-transparent': currentLayout !== 'mobile',
|
||||
'bg-white': currentLayout === 'mobile',
|
||||
'opacity-100': currentLayout === 'mobile',
|
||||
})}
|
||||
role="tab"
|
||||
type="button"
|
||||
style={{ height: 20 }}
|
||||
aria-selected="false"
|
||||
tabIndex="-1"
|
||||
onClick={() => toggleCurrentLayout('mobile')}
|
||||
data-cy={`button-change-layout-to-mobile`}
|
||||
</div>
|
||||
<div
|
||||
onClick={handleRedo}
|
||||
className="tj-ghost-black-btn"
|
||||
data-tooltip-id="tooltip-for-redo"
|
||||
data-tooltip-content="Redo"
|
||||
data-cy={`editor-redo-button`}
|
||||
>
|
||||
<SolidIcon
|
||||
name="mobile"
|
||||
width="14"
|
||||
fill={currentLayout !== 'desktop' ? 'var(--slate12)' : 'var(--slate8)'}
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill={darkMode ? '#fff' : '#2c3e50'}
|
||||
name="arrowbackup"
|
||||
className={cx('cursor-pointer', {
|
||||
disabled: !canRedo,
|
||||
})}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="undo-redo-container">
|
||||
<div
|
||||
onClick={handleUndo}
|
||||
className="tj-ghost-black-btn"
|
||||
data-tooltip-id="tooltip-for-undo"
|
||||
data-tooltip-content="Undo"
|
||||
data-cy={`editor-undo-button`}
|
||||
>
|
||||
<SolidIcon
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill={darkMode ? '#fff' : '#2c3e50'}
|
||||
name="arrowforwardup"
|
||||
className={cx('cursor-pointer', {
|
||||
disabled: !canUndo,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
onClick={handleRedo}
|
||||
className="tj-ghost-black-btn"
|
||||
data-tooltip-id="tooltip-for-redo"
|
||||
data-tooltip-content="Redo"
|
||||
data-cy={`editor-redo-button`}
|
||||
>
|
||||
<SolidIcon
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill={darkMode ? '#fff' : '#2c3e50'}
|
||||
name="arrowbackup"
|
||||
className={cx('cursor-pointer', {
|
||||
disabled: !canRedo,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Tooltip id="tooltip-for-undo" className="tooltip" />
|
||||
<Tooltip id="tooltip-for-redo" className="tooltip" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import AppLogo from '@/_components/AppLogo';
|
|||
import EditAppName from './EditAppName';
|
||||
import HeaderActions from './HeaderActions';
|
||||
import RealtimeAvatars from '../RealtimeAvatars';
|
||||
import { AppVersionsManager } from '../AppVersionsManager/List';
|
||||
import { AppVersionsManager } from '@/Editor/AppVersionsManager/AppVersionsManager';
|
||||
import { ManageAppUsers } from '../ManageAppUsers';
|
||||
import { ReleaseVersionButton } from '../ReleaseVersionButton';
|
||||
import cx from 'classnames';
|
||||
|
|
@ -110,7 +110,14 @@ export default function EditorHeader({
|
|||
<div className="global-settings-app-wrapper p-0 m-0 ">
|
||||
<EditAppName appId={appId} appName={appName} onNameChanged={onNameChanged} />
|
||||
</div>
|
||||
<HeaderActions canUndo={canUndo} canRedo={canRedo} handleUndo={handleUndo} handleRedo={handleRedo} />
|
||||
<HeaderActions
|
||||
canUndo={canUndo}
|
||||
canRedo={canRedo}
|
||||
handleUndo={handleUndo}
|
||||
handleRedo={handleRedo}
|
||||
showToggleLayoutBtn
|
||||
showUndoRedoBtn
|
||||
/>
|
||||
<div className="d-flex align-items-center">
|
||||
<div style={{ width: '100px', marginRight: '20px' }}>
|
||||
<span
|
||||
|
|
@ -140,18 +147,15 @@ export default function EditorHeader({
|
|||
</div>
|
||||
</div>
|
||||
<div className="navbar-seperator"></div>
|
||||
<div className="d-flex align-items-center p-0" style={{ marginRight: '12px' }}>
|
||||
<div className="d-flex version-manager-container p-0">
|
||||
{editingVersion && (
|
||||
<AppVersionsManager
|
||||
appId={appId}
|
||||
setAppDefinitionFromVersion={setAppDefinitionFromVersion}
|
||||
onVersionDelete={onVersionDelete}
|
||||
isPublic={isPublic ?? false}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{editingVersion && (
|
||||
<AppVersionsManager
|
||||
appId={appId}
|
||||
setAppDefinitionFromVersion={setAppDefinitionFromVersion}
|
||||
onVersionDelete={onVersionDelete}
|
||||
isPublic={isPublic ?? false}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="d-flex justify-content-end navbar-right-section"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,15 @@ import { renderElement } from '../Utils';
|
|||
// eslint-disable-next-line import/no-unresolved
|
||||
import i18next from 'i18next';
|
||||
import { resolveReferences } from '@/_helpers/utils';
|
||||
import { AllComponents } from '@/Editor/Box';
|
||||
|
||||
const SHOW_ADDITIONAL_ACTIONS = ['Text', 'TextInput', 'NumberInput', 'PasswordInput'];
|
||||
const PROPERTIES_VS_ACCORDION_TITLE = {
|
||||
Text: 'Data',
|
||||
TextInput: 'Data',
|
||||
PasswordInput: 'Data',
|
||||
NumberInput: 'Data',
|
||||
};
|
||||
|
||||
export const DefaultComponent = ({ componentMeta, darkMode, ...restProps }) => {
|
||||
const {
|
||||
|
|
@ -19,9 +28,17 @@ export const DefaultComponent = ({ componentMeta, darkMode, ...restProps }) => {
|
|||
pages,
|
||||
} = restProps;
|
||||
|
||||
const properties = Object.keys(componentMeta.properties);
|
||||
const events = Object.keys(componentMeta.events);
|
||||
const validations = Object.keys(componentMeta.validation || {});
|
||||
let properties = [];
|
||||
let additionalActions = [];
|
||||
for (const [key] of Object.entries(componentMeta?.properties)) {
|
||||
if (componentMeta?.properties[key]?.section === 'additionalActions') {
|
||||
additionalActions.push(key);
|
||||
} else {
|
||||
properties.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
const accordionItems = baseComponentProperties(
|
||||
properties,
|
||||
|
|
@ -37,7 +54,8 @@ export const DefaultComponent = ({ componentMeta, darkMode, ...restProps }) => {
|
|||
components,
|
||||
validations,
|
||||
darkMode,
|
||||
pages
|
||||
pages,
|
||||
additionalActions
|
||||
);
|
||||
|
||||
return <Accordion items={accordionItems} />;
|
||||
|
|
@ -57,14 +75,18 @@ export const baseComponentProperties = (
|
|||
allComponents,
|
||||
validations,
|
||||
darkMode,
|
||||
pages
|
||||
pages,
|
||||
additionalActions
|
||||
) => {
|
||||
// Add widget title to section key to filter that property section from specified widgets' settings
|
||||
const accordionFilters = {
|
||||
Properties: [],
|
||||
Events: [],
|
||||
Validation: [],
|
||||
General: ['Modal'],
|
||||
'Additional Actions': Object.keys(AllComponents).filter(
|
||||
(component) => !SHOW_ADDITIONAL_ACTIONS.includes(component)
|
||||
),
|
||||
General: ['Modal', 'TextInput', 'PasswordInput', 'NumberInput', 'Text'],
|
||||
Layout: [],
|
||||
};
|
||||
if (component.component.component === 'Listview') {
|
||||
|
|
@ -75,7 +97,9 @@ export const baseComponentProperties = (
|
|||
let items = [];
|
||||
if (properties.length > 0) {
|
||||
items.push({
|
||||
title: `${i18next.t('widget.common.properties', 'Properties')}`,
|
||||
title:
|
||||
PROPERTIES_VS_ACCORDION_TITLE[component?.component?.component] ??
|
||||
`${i18next.t('widget.common.properties', 'Properties')}`,
|
||||
children: properties.map((property) =>
|
||||
renderElement(
|
||||
component,
|
||||
|
|
@ -113,7 +137,6 @@ export const baseComponentProperties = (
|
|||
),
|
||||
});
|
||||
}
|
||||
|
||||
if (validations.length > 0) {
|
||||
items.push({
|
||||
title: `${i18next.t('widget.common.validation', 'Validation')}`,
|
||||
|
|
@ -127,7 +150,8 @@ export const baseComponentProperties = (
|
|||
'validation',
|
||||
currentState,
|
||||
allComponents,
|
||||
darkMode
|
||||
darkMode,
|
||||
componentMeta.validation?.[property]?.placeholder
|
||||
)
|
||||
),
|
||||
});
|
||||
|
|
@ -153,7 +177,27 @@ export const baseComponentProperties = (
|
|||
});
|
||||
|
||||
items.push({
|
||||
title: `${i18next.t('widget.common.layout', 'Layout')}`,
|
||||
title: `${i18next.t('widget.common.additionalActions', 'Additional Actions')}`,
|
||||
isOpen: true,
|
||||
children: additionalActions?.map((property) => {
|
||||
const paramType = property === 'Tooltip' ? 'general' : 'properties';
|
||||
return renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
property,
|
||||
paramType,
|
||||
currentState,
|
||||
allComponents,
|
||||
darkMode,
|
||||
componentMeta.properties?.[property]?.placeholder
|
||||
);
|
||||
}),
|
||||
});
|
||||
|
||||
items.push({
|
||||
title: `${i18next.t('widget.common.devices', 'Devices')}`,
|
||||
isOpen: true,
|
||||
children: (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { resolveReferences } from '@/_helpers/utils';
|
||||
import React from 'react';
|
||||
import { CodeHinter } from '../../../CodeBuilder/CodeHinter';
|
||||
|
||||
|
|
@ -13,6 +14,7 @@ export const ProgramaticallyHandleProperties = ({
|
|||
paramMeta,
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
paramType,
|
||||
currentState,
|
||||
}) => {
|
||||
const getValueBasedOnProperty = (property, props) => {
|
||||
switch (property) {
|
||||
|
|
@ -45,6 +47,52 @@ export const ProgramaticallyHandleProperties = ({
|
|||
const initialValue = getInitialValue(property, definition);
|
||||
|
||||
const options = {};
|
||||
|
||||
const calcFxActiveState = (props, property) => {
|
||||
const fxActiveFieldsPresent = props?.hasOwnProperty('fxActiveFields');
|
||||
if (!fxActiveFieldsPresent) {
|
||||
return props?.fxActive ?? false;
|
||||
}
|
||||
const fxActiveFields = props.fxActiveFields;
|
||||
const propertyPresentInFxActiveFields = fxActiveFields.length >= 1 && fxActiveFields.includes(property);
|
||||
return propertyPresentInFxActiveFields;
|
||||
};
|
||||
|
||||
const calculateFxActiveFields = (active, props, property) => {
|
||||
const fxActiveFieldsPropExists = props?.hasOwnProperty('fxActiveFields') ?? false;
|
||||
//to support backward compatibility, when fxActive is true for a particular column, we are passing all possible combinations which should render codehinter
|
||||
const fxActive =
|
||||
props?.fxActive && resolveReferences(props.fxActive, currentState)
|
||||
? ['isEditable', 'columnVisibility', 'linkTarget']
|
||||
: [];
|
||||
|
||||
const checkFxActiveFieldIsArrray = (fxActiveFieldsProperty) => {
|
||||
// adding error handling mechanism for fxActiveFieldsProperty , if props.fxActiveFields is array , then return props.fxActiveFields or else return [], this will make sure, fxActiveFields wil always be array
|
||||
return Array.isArray(fxActiveFieldsProperty) ? fxActiveFieldsProperty : [];
|
||||
};
|
||||
|
||||
const fxActiveFields = fxActiveFieldsPropExists ? checkFxActiveFieldIsArrray(props.fxActiveFields) : fxActive;
|
||||
|
||||
const findIndexOfPropertyInFxActiveFields = (property, fxActiveFields) => {
|
||||
// checking if particular property is already present in the fxActiveFields or not
|
||||
const index = fxActiveFields.findIndex((prop) => prop === property);
|
||||
return index;
|
||||
};
|
||||
|
||||
const indexOfProp = findIndexOfPropertyInFxActiveFields(property, fxActiveFields);
|
||||
|
||||
// removing or addding property for which we want to render codehinter
|
||||
if (active && indexOfProp === -1) {
|
||||
//if active is true and property does not present in the fxActiveField before hand, if both conditions are satisfied, we add it to the array of fxActiveField.
|
||||
fxActiveFields.push(property);
|
||||
} else if (!active && indexOfProp !== -1) {
|
||||
// if active is falsy and particular property is present in the fxActiveField , we remove that particular property from the array
|
||||
fxActiveFields.splice(indexOfProp, 1);
|
||||
}
|
||||
|
||||
return fxActiveFields;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`mb-2 field ${options.className}`} onClick={(e) => e.stopPropagation()}>
|
||||
<CodeHinter
|
||||
|
|
@ -60,9 +108,10 @@ export const ProgramaticallyHandleProperties = ({
|
|||
paramLabel={paramMeta.displayName}
|
||||
fieldMeta={paramMeta}
|
||||
onFxPress={(active) => {
|
||||
callbackFunction(index, 'fxActive', active);
|
||||
const resultFxActiveFields = calculateFxActiveFields(active, props, property);
|
||||
callbackFunction(index, 'fxActiveFields', resultFxActiveFields);
|
||||
}}
|
||||
fxActive={props?.fxActive ?? false}
|
||||
fxActive={calcFxActiveState(props, property)}
|
||||
component={component.component}
|
||||
className={options.className}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -268,6 +268,25 @@ class TableComponent extends React.Component {
|
|||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div data-cy={`transformation-field`} className="field mb-2 mt-1">
|
||||
<label className="form-label">{this.props.t('widget.Table.transformationField', 'Transformation')}</label>
|
||||
<CodeHinter
|
||||
currentState={this.props.currentState}
|
||||
initialValue={column?.transformation ?? '{{cellValue}}'}
|
||||
theme={this.props.darkMode ? 'monokai' : 'default'}
|
||||
mode="javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={column.name}
|
||||
onChange={(value) => this.onColumnItemChange(index, 'transformation', value)}
|
||||
componentName={this.getPopoverFieldSource(column.columnType, 'transformation')}
|
||||
popOverCallback={(showing) => {
|
||||
this.setColumnPopoverRootCloseBlocker('transformation', showing);
|
||||
}}
|
||||
enablePreview={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="field mb-2">
|
||||
<label className="form-label">
|
||||
{this.props.t('widget.Table.horizontalAlignment', 'Horizontal Alignment')}
|
||||
|
|
@ -908,7 +927,7 @@ class TableComponent extends React.Component {
|
|||
addNewColumn = () => {
|
||||
const columns = this.props.component.component.definition.properties.columns;
|
||||
const newValue = columns.value;
|
||||
newValue.push({ name: this.generateNewColumnName(columns.value), id: uuidv4() });
|
||||
newValue.push({ name: this.generateNewColumnName(columns.value), id: uuidv4(), fxActiveFields: [] });
|
||||
this.props.paramUpdated({ name: 'columns' }, 'value', newValue, 'properties', true);
|
||||
};
|
||||
|
||||
|
|
@ -1133,7 +1152,9 @@ class TableComponent extends React.Component {
|
|||
|
||||
const rowSelectionsOptions = [
|
||||
'allowSelection',
|
||||
...(allowSelection ? ['highlightSelectedRow', 'showBulkSelector', 'defaultSelectedRow'] : []),
|
||||
...(allowSelection
|
||||
? ['highlightSelectedRow', 'showBulkSelector', 'defaultSelectedRow', 'selectRowOnCellEdit']
|
||||
: []),
|
||||
];
|
||||
const searchSortFilterOptions = [
|
||||
...(displaySearchBox ? ['displaySearchBox'] : []),
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ export const Code = ({
|
|||
fxActive,
|
||||
component,
|
||||
verticalLine,
|
||||
accordian,
|
||||
placeholder,
|
||||
}) => {
|
||||
const currentState = useCurrentState();
|
||||
|
||||
|
|
@ -31,6 +33,13 @@ export const Code = ({
|
|||
if (isPaginationEnabled) return '{{true}}';
|
||||
return '{{false}}';
|
||||
}
|
||||
// Following condition is needed to support older Text component not having textFormat switch
|
||||
if (component?.component?.component === 'Text' && param === 'textFormat') {
|
||||
const doTextFormatAlreadyExist = component?.component?.definition?.properties?.textFormat;
|
||||
if (!doTextFormatAlreadyExist) {
|
||||
return 'html';
|
||||
}
|
||||
}
|
||||
if (['showAddNewRowButton', 'allowSelection', 'defaultSelectedRow'].includes(param)) {
|
||||
if (param === 'allowSelection') {
|
||||
const highlightSelectedRow = component?.component?.definition?.properties?.highlightSelectedRow?.value ?? false;
|
||||
|
|
@ -44,14 +53,18 @@ export const Code = ({
|
|||
} else {
|
||||
return '{{true}}';
|
||||
}
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
if (param === 'selectRowOnCellEdit') {
|
||||
const selectRowOnCellEdit =
|
||||
component?.component?.definition?.properties?.selectRowOnCellEdit?.value ?? '{{true}}';
|
||||
return selectRowOnCellEdit;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
let initialValue = !_.isEmpty(definition) ? definition.value : getDefinitionForNewProps(param.name);
|
||||
const paramMeta = componentMeta[paramType][param.name];
|
||||
const displayName = paramMeta.displayName || param.name;
|
||||
const paramMeta = accordian ? componentMeta[paramType]?.[param.name] : componentMeta[paramType][param.name];
|
||||
const displayName = paramMeta?.displayName || param?.name;
|
||||
|
||||
/*
|
||||
following block is written for cellSize Prop to support backward compatibility,
|
||||
|
|
@ -75,13 +88,18 @@ export const Code = ({
|
|||
onChange(param, 'value', value, paramType);
|
||||
}
|
||||
|
||||
const options = paramMeta.options || {};
|
||||
function onVisibilityChange(value) {
|
||||
onChange({ name: 'iconVisibility' }, 'value', value, 'styles');
|
||||
}
|
||||
|
||||
const options = paramMeta?.options || {};
|
||||
|
||||
const getfieldName = React.useMemo(() => {
|
||||
return param.name;
|
||||
}, [param]);
|
||||
|
||||
return (
|
||||
<div className={`field ${options.className}`} style={{ marginBottom: '20px' }}>
|
||||
<div className={`field ${options.className}`} style={{ marginBottom: '8px' }}>
|
||||
<CodeHinter
|
||||
enablePreview={true}
|
||||
initialValue={initialValue}
|
||||
|
|
@ -90,15 +108,21 @@ export const Code = ({
|
|||
lineWrapping={true}
|
||||
className={options.className}
|
||||
onChange={(value) => handleCodeChanged(value)}
|
||||
onVisibilityChange={(value) => onVisibilityChange(value)}
|
||||
componentName={`component/${componentName}::${getfieldName}`}
|
||||
type={paramMeta.type}
|
||||
type={paramMeta?.type}
|
||||
paramName={param.name}
|
||||
paramLabel={displayName}
|
||||
paramLabel={paramMeta?.showLabel !== false ? displayName : ' '}
|
||||
fieldMeta={paramMeta}
|
||||
onFxPress={onFxPress}
|
||||
fxActive={CLIENT_SERVER_TOGGLE_FIELDS.includes(param.name) ? false : fxActive} // Client Server Toggle don't support Fx
|
||||
component={component}
|
||||
verticalLine={verticalLine}
|
||||
isIcon={paramMeta?.isIcon}
|
||||
staticText={paramMeta?.staticText}
|
||||
placeholder={placeholder}
|
||||
inspectorTab={paramType}
|
||||
bold={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ const tooltipStyle = {
|
|||
textDecorationStyle: 'dashed',
|
||||
};
|
||||
|
||||
export const ToolTip = ({ label, meta, labelClass }) => {
|
||||
export const ToolTip = ({ label, meta, labelClass, bold = false }) => {
|
||||
function renderTooltip(props) {
|
||||
return (
|
||||
<Tooltip id="button-tooltip" {...props}>
|
||||
|
|
@ -31,6 +31,7 @@ export const ToolTip = ({ label, meta, labelClass }) => {
|
|||
.toLowerCase()
|
||||
.replace(/\s+/g, '-')}`}
|
||||
className={labelClass || 'form-label'}
|
||||
style={{ fontWeight: bold ? 500 : 400 }}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
|
|
|
|||
|
|
@ -48,10 +48,20 @@ export const EventManager = ({
|
|||
appId,
|
||||
apps,
|
||||
events: allAppEvents,
|
||||
eventsUpdatedLoader,
|
||||
eventsCreatedLoader,
|
||||
actionsUpdatedLoader,
|
||||
eventToDeleteLoaderIndex,
|
||||
setEventToDeleteLoaderIndex,
|
||||
} = useAppDataStore((state) => ({
|
||||
appId: state.appId,
|
||||
apps: state.apps,
|
||||
events: state.events,
|
||||
eventsUpdatedLoader: state.eventsUpdatedLoader,
|
||||
eventsCreatedLoader: state.eventsCreatedLoader,
|
||||
actionsUpdatedLoader: state.actionsUpdatedLoader,
|
||||
eventToDeleteLoaderIndex: state.eventToDeleteLoaderIndex,
|
||||
setEventToDeleteLoaderIndex: state.actions.setEventToDeleteLoaderIndex,
|
||||
}));
|
||||
|
||||
const { handleYmapEventUpdates } = useContext(EditorContext) || {};
|
||||
|
|
@ -71,6 +81,7 @@ export const EventManager = ({
|
|||
|
||||
const [events, setEvents] = useState([]);
|
||||
const [focusedEventIndex, setFocusedEventIndex] = useState(null);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -276,6 +287,11 @@ export const EventManager = ({
|
|||
let updatedEvent = newEvents[index];
|
||||
updatedEvent.event[param] = value;
|
||||
|
||||
// Remove debounce key if it's empty
|
||||
if (param === 'debounce' && value === '') {
|
||||
delete updatedEvent.event.debounce;
|
||||
}
|
||||
|
||||
if (param === 'componentSpecificActionHandle') {
|
||||
const getDefault = getComponentActionDefaultParams(updatedEvent.event?.componentId, value);
|
||||
updatedEvent.event['componentSpecificActionParams'] = getDefault;
|
||||
|
|
@ -290,7 +306,8 @@ export const EventManager = ({
|
|||
diff: updatedEvent,
|
||||
},
|
||||
],
|
||||
'update'
|
||||
'update',
|
||||
param
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -298,14 +315,13 @@ export const EventManager = ({
|
|||
const eventsHandler = _.cloneDeep(events);
|
||||
|
||||
const eventId = eventsHandler[index].id;
|
||||
|
||||
setEventToDeleteLoaderIndex(index);
|
||||
deleteAppVersionEventHandler(eventId);
|
||||
}
|
||||
|
||||
function addHandler() {
|
||||
let newEvents = events;
|
||||
const eventIndex = newEvents.length;
|
||||
|
||||
createAppVersionEventHandlers({
|
||||
event: {
|
||||
eventId: Object.keys(eventMetaDefinition?.events)[0],
|
||||
|
|
@ -876,7 +892,7 @@ export const EventManager = ({
|
|||
enablePreview={true}
|
||||
type={param?.type}
|
||||
fieldMeta={{ options: param?.options }}
|
||||
cyLabel={param.displayName}
|
||||
cyLabel={`event-${param.displayName}`}
|
||||
component={component}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -932,7 +948,6 @@ export const EventManager = ({
|
|||
};
|
||||
|
||||
const renderDraggable = useDraggableInPortal();
|
||||
|
||||
const renderHandlers = (events) => {
|
||||
return (
|
||||
<DragDropContext
|
||||
|
|
@ -982,6 +997,11 @@ export const EventManager = ({
|
|||
removeHandler={removeHandler}
|
||||
index={index}
|
||||
darkMode={darkMode}
|
||||
actionsUpdatedLoader={index === focusedEventIndex ? actionsUpdatedLoader : false}
|
||||
eventsUpdatedLoader={index === focusedEventIndex ? eventsUpdatedLoader : false}
|
||||
eventsDeletedLoader={
|
||||
index === eventToDeleteLoaderIndex ? !!eventToDeleteLoaderIndex : false
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
|
|
@ -1000,7 +1020,7 @@ export const EventManager = ({
|
|||
|
||||
const renderAddHandlerBtn = () => {
|
||||
return (
|
||||
<AddNewButton onClick={addHandler} dataCy="add-event-handler" className="mt-0">
|
||||
<AddNewButton onClick={addHandler} dataCy="add-event-handler" className="mt-0" isLoading={eventsCreatedLoader}>
|
||||
{t('editor.inspector.eventManager.addHandler', 'New event handler')}
|
||||
</AddNewButton>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { componentTypes } from '../WidgetManager/components';
|
|||
import { Table } from './Components/Table/Table.jsx';
|
||||
import { Chart } from './Components/Chart';
|
||||
import { Form } from './Components/Form';
|
||||
import { renderElement } from './Utils';
|
||||
import { renderElement, renderCustomStyles } from './Utils';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { validateQueryName, convertToKebabCase, resolveReferences } from '@/_helpers/utils';
|
||||
import { ConfirmDialog } from '@/_components';
|
||||
|
|
@ -83,6 +83,8 @@ export const Inspector = ({
|
|||
const [inputRef, setInputFocus] = useFocus();
|
||||
|
||||
const [showHeaderActionsMenu, setShowHeaderActionsMenu] = useState(false);
|
||||
const shouldAddBoxShadow = ['TextInput', 'PasswordInput', 'NumberInput', 'Text'];
|
||||
|
||||
const { isVersionReleased } = useAppVersionStore(
|
||||
(state) => ({
|
||||
isVersionReleased: state.isVersionReleased,
|
||||
|
|
@ -310,7 +312,15 @@ export const Inspector = ({
|
|||
);
|
||||
const stylesTab = (
|
||||
<div style={{ marginBottom: '6rem' }} className={`${isVersionReleased && 'disabled'}`}>
|
||||
<div className="p-3">
|
||||
<div
|
||||
className={
|
||||
component.component.component !== 'TextInput' &&
|
||||
component.component.component !== 'PasswordInput' &&
|
||||
component.component.component !== 'NumberInput' &&
|
||||
component.component.component !== 'Text' &&
|
||||
'p-3'
|
||||
}
|
||||
>
|
||||
<Inspector.RenderStyleOptions
|
||||
componentMeta={componentMeta}
|
||||
component={component}
|
||||
|
|
@ -320,7 +330,7 @@ export const Inspector = ({
|
|||
allComponents={allComponents}
|
||||
/>
|
||||
</div>
|
||||
{buildGeneralStyle()}
|
||||
{!shouldAddBoxShadow.includes(component.component.component) && buildGeneralStyle()}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
@ -353,7 +363,11 @@ export const Inspector = ({
|
|||
<div>
|
||||
<div className="row inspector-component-title-input-holder">
|
||||
<div className="col-1" onClick={() => setSelectedComponents(EMPTY_ARRAY)}>
|
||||
<span data-cy={`inspector-close-icon`} className="cursor-pointer">
|
||||
<span
|
||||
data-cy={`inspector-close-icon`}
|
||||
className="cursor-pointer d-flex align-items-center "
|
||||
style={{ height: '28px', width: '28px' }}
|
||||
>
|
||||
<ArrowLeft fill={'var(--slate12)'} width={'14'} />
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -370,7 +384,7 @@ export const Inspector = ({
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-2">
|
||||
<div className="col-2" data-cy={'component-inspector-options'}>
|
||||
<OverlayTrigger
|
||||
trigger={'click'}
|
||||
placement={'bottom-end'}
|
||||
|
|
@ -381,6 +395,7 @@ export const Inspector = ({
|
|||
<Popover.Body bsPrefix="list-item-popover-body">
|
||||
{INSPECTOR_HEADER_OPTIONS.map((option) => (
|
||||
<div
|
||||
data-cy={`component-inspector-${String(option?.value).toLowerCase()}-button`}
|
||||
className="list-item-popover-option"
|
||||
key={option?.value}
|
||||
onClick={(e) => {
|
||||
|
|
@ -454,10 +469,41 @@ const widgetsWithStyleConditions = {
|
|||
],
|
||||
},
|
||||
};
|
||||
const styleGroupedComponentTypes = ['TextInput', 'NumberInput', 'PasswordInput'];
|
||||
|
||||
const RenderStyleOptions = ({ componentMeta, component, paramUpdated, dataQueries, currentState, allComponents }) => {
|
||||
return Object.keys(componentMeta.styles).map((style) => {
|
||||
const conditionWidget = widgetsWithStyleConditions[component?.component?.component] ?? null;
|
||||
// Initialize an object to group properties by "accordian"
|
||||
const groupedProperties = {};
|
||||
if (
|
||||
component.component.component === 'TextInput' ||
|
||||
component.component.component === 'PasswordInput' ||
|
||||
component.component.component === 'NumberInput' ||
|
||||
component.component.component === 'Text'
|
||||
) {
|
||||
// Iterate over the properties in componentMeta.styles
|
||||
for (const key in componentMeta.styles) {
|
||||
const property = componentMeta.styles[key];
|
||||
const accordian = property.accordian;
|
||||
|
||||
// Check if the "accordian" key exists in groupedProperties
|
||||
if (!groupedProperties[accordian]) {
|
||||
groupedProperties[accordian] = {}; // Create an empty object for the "accordian" key if it doesn't exist
|
||||
}
|
||||
|
||||
// Add the property to the corresponding "accordian" object
|
||||
groupedProperties[accordian][key] = property;
|
||||
}
|
||||
}
|
||||
|
||||
return Object.keys(
|
||||
component.component.component === 'TextInput' ||
|
||||
component.component.component === 'PasswordInput' ||
|
||||
component.component.component === 'NumberInput' ||
|
||||
component.component.component === 'Text'
|
||||
? groupedProperties
|
||||
: componentMeta.styles
|
||||
).map((style) => {
|
||||
const conditionWidget = widgetsWithStyleConditions[component.component.component] ?? null;
|
||||
const condition = conditionWidget?.conditions.find((condition) => condition.property) ?? {};
|
||||
|
||||
if (conditionWidget && conditionWidget.conditions.find((condition) => condition.conditionStyles.includes(style))) {
|
||||
|
|
@ -477,16 +523,43 @@ const RenderStyleOptions = ({ componentMeta, component, paramUpdated, dataQuerie
|
|||
);
|
||||
}
|
||||
|
||||
return renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
style,
|
||||
'styles',
|
||||
currentState,
|
||||
allComponents
|
||||
);
|
||||
const items = [];
|
||||
|
||||
if (
|
||||
component.component.component === 'TextInput' ||
|
||||
component.component.component === 'PasswordInput' ||
|
||||
component.component.component === 'NumberInput' ||
|
||||
component.component.component === 'Text'
|
||||
) {
|
||||
items.push({
|
||||
title: `${style}`,
|
||||
children: Object.entries(groupedProperties[style]).map(([key, value]) => ({
|
||||
...renderCustomStyles(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
key,
|
||||
'styles',
|
||||
currentState,
|
||||
allComponents,
|
||||
value.accordian
|
||||
),
|
||||
})),
|
||||
});
|
||||
return <Accordion key={style} items={items} />;
|
||||
} else {
|
||||
return renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
style,
|
||||
'styles',
|
||||
currentState,
|
||||
allComponents
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,10 +4,18 @@ import AddRectangle from '@/_ui/Icon/solidIcons/AddRectangle';
|
|||
import Trash from '@/_ui/Icon/solidIcons/Trash';
|
||||
import { ButtonSolid } from '@/_ui/AppButton/AppButton';
|
||||
import SortableList from '@/_components/SortableList';
|
||||
import { Spinner } from 'react-bootstrap';
|
||||
|
||||
const ManageEventButton = ({ eventDisplayName = 'Upon events', actionName, index, removeHandler }) => {
|
||||
const ManageEventButton = ({
|
||||
eventDisplayName = 'Upon events',
|
||||
actionName,
|
||||
index,
|
||||
removeHandler,
|
||||
actionsUpdatedLoader,
|
||||
eventsUpdatedLoader,
|
||||
eventsDeletedLoader,
|
||||
}) => {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
return (
|
||||
<div style={{ marginBottom: '8px' }}>
|
||||
<div
|
||||
|
|
@ -15,52 +23,67 @@ const ManageEventButton = ({ eventDisplayName = 'Upon events', actionName, index
|
|||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<div data-cy="event-handler-card" className="d-flex">
|
||||
<span className="d-flex align-items-center px-2">
|
||||
<SortableList.DragHandle show />
|
||||
</span>
|
||||
<div
|
||||
className="d-flex justify-content-between"
|
||||
role="button"
|
||||
style={{ padding: '6px 12px 6px 8px', width: '100%' }}
|
||||
>
|
||||
<div className="text-truncate event-handler-text" data-cy="event-handler">
|
||||
{eventDisplayName}
|
||||
</div>
|
||||
<div className="text-truncate event-name-text" data-cy="event-name">
|
||||
<small className="event-action font-weight-light text-truncate">
|
||||
{actionName ? actionName : 'Select action'}
|
||||
</small>
|
||||
{!actionName && <AddRectangle width={13.33} />}
|
||||
<span>
|
||||
<span>
|
||||
{isHovered && (
|
||||
<ButtonSolid
|
||||
variant="tertiary"
|
||||
size="xs"
|
||||
className={'list-menu-option-btn'}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="list-item-popover-option"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
removeHandler(index);
|
||||
}}
|
||||
>
|
||||
<div className="d-flex align-center">
|
||||
<Trash fill={'#E54D2E'} width={12} />
|
||||
</div>
|
||||
</div>
|
||||
</ButtonSolid>
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
{eventsDeletedLoader ? (
|
||||
<div className="d-flex justify-content-center p-2">
|
||||
{' '}
|
||||
<Spinner style={{ width: '16px', height: '16px', color: 'var(--indigo9)' }} />
|
||||
</div>
|
||||
) : (
|
||||
<div data-cy="event-handler-card" className="d-flex">
|
||||
<span className="d-flex align-items-center px-2">
|
||||
<SortableList.DragHandle show />
|
||||
</span>
|
||||
<div
|
||||
className="d-flex justify-content-between"
|
||||
role="button"
|
||||
style={{ padding: '6px 12px 6px 8px', width: '100%' }}
|
||||
>
|
||||
<div className="text-truncate event-handler-text" data-cy="event-handler">
|
||||
{!eventsUpdatedLoader ? (
|
||||
eventDisplayName
|
||||
) : (
|
||||
<Spinner style={{ width: '16px', height: '16px', color: 'var(--indigo9)' }} />
|
||||
)}
|
||||
</div>
|
||||
{!actionsUpdatedLoader ? (
|
||||
<div className="text-truncate event-name-text" data-cy="event-name">
|
||||
<small className="event-action font-weight-light text-truncate">
|
||||
{actionName ? actionName : 'Select action'}
|
||||
</small>
|
||||
{!actionName && <AddRectangle width={13.33} />}
|
||||
<span>
|
||||
<span>
|
||||
{isHovered && (
|
||||
<ButtonSolid
|
||||
variant="tertiary"
|
||||
size="xs"
|
||||
className={'list-menu-option-btn'}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="list-item-popover-option"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
removeHandler(index);
|
||||
}}
|
||||
>
|
||||
<div className="d-flex align-center">
|
||||
<Trash fill={'#E54D2E'} width={12} />
|
||||
</div>
|
||||
</div>
|
||||
</ButtonSolid>
|
||||
)}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<Spinner style={{ width: '16px', height: '16px', color: 'var(--indigo9)' }} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,75 @@ export function renderQuerySelector(component, dataQueries, eventOptionUpdated,
|
|||
/>
|
||||
);
|
||||
}
|
||||
export function renderCustomStyles(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
param,
|
||||
paramType,
|
||||
currentState,
|
||||
components = {},
|
||||
accordian,
|
||||
darkMode = false,
|
||||
verticalLine = true,
|
||||
placeholder = ''
|
||||
) {
|
||||
const componentConfig = component.component;
|
||||
const componentDefinition = componentConfig.definition;
|
||||
const paramTypeDefinition = componentDefinition[paramType] || {};
|
||||
const definition = paramTypeDefinition[param] || {};
|
||||
const meta = componentMeta[paramType]?.[accordian]?.[param];
|
||||
|
||||
if (
|
||||
componentConfig.component == 'DropDown' ||
|
||||
componentConfig.component == 'Form' ||
|
||||
componentConfig.component == 'Listview' ||
|
||||
componentConfig.component == 'TextInput' ||
|
||||
componentConfig.component == 'NumberInput' ||
|
||||
componentConfig.component == 'PasswordInput'
|
||||
) {
|
||||
const paramTypeConfig = componentMeta[paramType] || {};
|
||||
const paramConfig = paramTypeConfig[param] || {};
|
||||
const { conditionallyRender = null } = paramConfig;
|
||||
|
||||
if (conditionallyRender) {
|
||||
const { key, value } = conditionallyRender;
|
||||
if (paramTypeDefinition?.[key] ?? value) {
|
||||
const resolvedValue = paramTypeDefinition?.[key] && resolveReferences(paramTypeDefinition?.[key], currentState);
|
||||
|
||||
if (resolvedValue?.value !== value) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Code
|
||||
param={{ name: param, ...component.component.properties[param] }}
|
||||
definition={definition}
|
||||
dataQueries={dataQueries}
|
||||
onChange={paramUpdated}
|
||||
paramType={paramType}
|
||||
components={components}
|
||||
componentMeta={componentMeta}
|
||||
darkMode={darkMode}
|
||||
componentName={component.component.name || null}
|
||||
type={meta?.type}
|
||||
fxActive={definition.fxActive ?? false}
|
||||
onFxPress={(active) => {
|
||||
paramUpdated({ name: param, ...component.component.properties[param] }, 'fxActive', active, paramType);
|
||||
}}
|
||||
component={component}
|
||||
verticalLine={verticalLine}
|
||||
accordian={accordian}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function renderElement(
|
||||
component,
|
||||
|
|
@ -28,6 +97,7 @@ export function renderElement(
|
|||
currentState,
|
||||
components = {},
|
||||
darkMode = false,
|
||||
placeholder = '',
|
||||
verticalLine = true
|
||||
) {
|
||||
const componentConfig = component.component;
|
||||
|
|
@ -72,6 +142,7 @@ export function renderElement(
|
|||
}}
|
||||
component={component}
|
||||
verticalLine={verticalLine}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import JSONTreeViewer from '@/_ui/JSONTreeViewer';
|
|||
import _, { isEmpty } from 'lodash';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { getSvgIcon } from '@/_helpers/appUtils';
|
||||
|
||||
import Icon from '@/_ui/Icon/solidIcons/index';
|
||||
import { useGlobalDataSources } from '@/_stores/dataSourcesStore';
|
||||
import { useDataQueries } from '@/_stores/dataQueriesStore';
|
||||
import { useCurrentState } from '@/_stores/currentStateStore';
|
||||
|
|
@ -126,9 +126,58 @@ export const LeftSidebarInspector = ({
|
|||
};
|
||||
}
|
||||
});
|
||||
const exposedVariablesIcon = Object.entries(currentState['components'])
|
||||
.map(([key, value]) => {
|
||||
const component = componentDefinitions[value.id]?.component ?? {};
|
||||
const componentExposedVariables = value;
|
||||
|
||||
const iconsList = useMemo(() => [...queryIcons, ...componentIcons], [queryIcons, componentIcons]);
|
||||
if (!_.isEmpty(component) && component.component === 'TextInput') {
|
||||
const icons = [];
|
||||
|
||||
if (componentExposedVariables.disable) {
|
||||
icons.push({
|
||||
iconName: 'disable',
|
||||
jsx: () => <Icon name={'warning'} height={16} width={16} fill="#DB4324" />,
|
||||
className: 'component-icon',
|
||||
tooltipMessage: 'This function will be deprecated soon, You can use setVisibility as an alternative',
|
||||
isInfoIcon: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (componentExposedVariables.visibility) {
|
||||
icons.push({
|
||||
iconName: 'visibility',
|
||||
jsx: () => <Icon name={'warning'} height={16} width={16} fill="#DB4324" />,
|
||||
className: 'component-icon',
|
||||
tooltipMessage: 'This function will be deprecated soon, You can use setVisibility as an alternative',
|
||||
isInfoIcon: true,
|
||||
});
|
||||
}
|
||||
|
||||
return icons;
|
||||
}
|
||||
|
||||
if (!_.isEmpty(component) && component.component === 'Text' && componentExposedVariables?.visibility) {
|
||||
return [
|
||||
{
|
||||
iconName: 'visibility',
|
||||
jsx: () => <Icon name={'warning'} height={16} width={16} fill="#DB4324" />,
|
||||
className: 'component-icon',
|
||||
tooltipMessage: 'This function will be deprecated soon, You can use setVisibility as an alternative',
|
||||
isInfoIcon: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
})
|
||||
.flat()
|
||||
.filter((value) => value !== undefined); // Remove undefined values
|
||||
|
||||
const iconsList = useMemo(
|
||||
() => [...queryIcons, ...componentIcons, ...exposedVariablesIcon],
|
||||
[queryIcons, componentIcons, exposedVariablesIcon]
|
||||
);
|
||||
const handleRemoveComponent = (component) => {
|
||||
removeComponent(component.id);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -71,23 +71,26 @@ const ParameterDetails = ({ darkMode, onSubmit, isEdit, name, defaultValue, onRe
|
|||
</Popover>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<span className="parameterItem">
|
||||
{isEdit ? (
|
||||
<PillButton name={name} onClick={() => setShowModal(true)} onRemove={onRemove} />
|
||||
<PillButton
|
||||
className="parameterItemPillButton"
|
||||
name={name}
|
||||
onClick={() => setShowModal(true)}
|
||||
onRemove={onRemove}
|
||||
/>
|
||||
) : (
|
||||
<ButtonSolid
|
||||
variant="ghostBlue"
|
||||
size="sm"
|
||||
<button
|
||||
onClick={() => setShowModal((show) => !show)}
|
||||
className="ms-2"
|
||||
id="runjs-param-add-btn"
|
||||
data-cy={`runjs-add-param-button`}
|
||||
style={{ background: 'none' }}
|
||||
>
|
||||
<span className="m-0">
|
||||
<PlusRectangle fill={'#3E63DD'} width={15} />
|
||||
<PlusRectangle fill={darkMode ? '#9BA1A6' : '#687076'} width={15} />
|
||||
</span>
|
||||
Add
|
||||
</ButtonSolid>
|
||||
</button>
|
||||
)}
|
||||
</span>
|
||||
</OverlayTrigger>
|
||||
|
|
@ -97,23 +100,22 @@ const ParameterDetails = ({ darkMode, onSubmit, isEdit, name, defaultValue, onRe
|
|||
export const PillButton = ({ name, onClick, onRemove, marginBottom, className, size }) => (
|
||||
<ButtonGroup
|
||||
aria-label="Parameter"
|
||||
className={cx('ms-2 bg-slate3', { 'mb-2': marginBottom, ...(className && { [className]: true }) })}
|
||||
style={{ borderRadius: '15px' }}
|
||||
className={cx({ 'mb-2': marginBottom, ...(className && { [className]: true }) })}
|
||||
style={{ borderRadius: '6px', marginLeft: '6px', height: '24px', background: '#A1A7AE1F' }}
|
||||
>
|
||||
<Button
|
||||
size="sm"
|
||||
className={cx('bg-transparent color-slate12 runjs-parameter-badge', { 'py-0 px-2': size === 'sm' })}
|
||||
onClick={onClick}
|
||||
style={{
|
||||
borderTopLeftRadius: '15px',
|
||||
borderBottomLeftRadius: '15px',
|
||||
borderTopLeftRadius: '6px',
|
||||
borderBottomLeftRadius: '6px',
|
||||
textTransform: 'none',
|
||||
padding: '0.8rem',
|
||||
fontWeight: 500,
|
||||
...(!onRemove && { borderRadius: '15px' }),
|
||||
...(!onRemove && { borderRadius: '6px' }),
|
||||
}}
|
||||
>
|
||||
<span data-cy={`query-param-${String(name).toLowerCase()}`} className="text-truncate">
|
||||
<span data-cy={`query-param-${String(name).toLowerCase()}`} className="text-truncate query-param-text">
|
||||
{name}
|
||||
</span>
|
||||
</Button>
|
||||
|
|
@ -122,15 +124,16 @@ export const PillButton = ({ name, onClick, onRemove, marginBottom, className, s
|
|||
data-cy={`query-param-${String(name).toLowerCase()}-remove-button`}
|
||||
onClick={onRemove}
|
||||
size="sm"
|
||||
className={cx('bg-transparent color-slate12', { 'p-0 pe-1': size === 'sm' })}
|
||||
className={cx('bg-transparent color-slate12', { 'p-0': size === 'sm' })}
|
||||
style={{
|
||||
borderTopRightRadius: '15px',
|
||||
borderBottomRightRadius: '15px',
|
||||
paddingLeft: 0,
|
||||
paddingRight: '0.75rem',
|
||||
borderTopRightRadius: '6px',
|
||||
borderBottomRightRadius: '6px',
|
||||
paddingRight: '6px',
|
||||
paddingLeft: '0px',
|
||||
height: '24px',
|
||||
}}
|
||||
>
|
||||
<Remove fill="var(--slate12)" />
|
||||
<Remove fill="#6A727CCC" height={20} width={20} />
|
||||
</Button>
|
||||
)}
|
||||
</ButtonGroup>
|
||||
|
|
@ -56,21 +56,29 @@ const ParameterForm = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<Popover.Header className={darkMode && 'dark-theme'} style={{ fontSize: '12px' }}>
|
||||
{isEdit ? 'UPDATE PARAMETER' : 'ADD PARAMETER'}
|
||||
<Popover.Header className={`${darkMode && 'dark-theme'}`}>
|
||||
<p className="tj-text-xsm " style={{ fontWeight: '600' }}>
|
||||
{isEdit ? 'UPDATE PARAMETER' : 'ADD PARAMETER'}
|
||||
</p>
|
||||
</Popover.Header>
|
||||
<Popover.Body className={darkMode && 'dark-theme dark-theme'} key={'1'} bsPrefix="popover-body">
|
||||
<Popover.Body
|
||||
className={darkMode && 'dark-theme dark-theme'}
|
||||
key={'1'}
|
||||
bsPrefix="popover-body"
|
||||
style={{ padding: '16px 16px 32px 16px', overflowY: 'auto' }}
|
||||
>
|
||||
<Form
|
||||
className="container p-0 tj-app-input"
|
||||
onSubmit={handleSubmit}
|
||||
style={{ paddingRight: '25px !important' }}
|
||||
>
|
||||
<Form.Group as={Row} className="mb-2 pr-1">
|
||||
<Form.Group as={Col} style={{ display: 'flex' }} className="mb-2 pr-1">
|
||||
<Form.Label column htmlFor="paramName">
|
||||
Name
|
||||
</Form.Label>
|
||||
<Col sm="12">
|
||||
<Col>
|
||||
<Form.Control
|
||||
style={{ height: '32px', width: '177px' }}
|
||||
type="text"
|
||||
aria-describedby="paramName"
|
||||
onChange={(event) => setName(event.target.value)}
|
||||
|
|
@ -79,9 +87,9 @@ const ParameterForm = ({
|
|||
{name && error && <div className="invalid-feedback d-block">{error}</div>}
|
||||
</Col>
|
||||
</Form.Group>
|
||||
<Form.Group as={Row}>
|
||||
<Form.Label column htmlFor="defaultValue">
|
||||
Default Value
|
||||
<Form.Group as={Col} style={{ display: 'flex' }}>
|
||||
<Form.Label column htmlFor="defaultValue" style={{ marginRight: '0px' }}>
|
||||
Value
|
||||
<span className="ms-1">
|
||||
<OverlayTrigger
|
||||
placement="bottom"
|
||||
|
|
@ -98,15 +106,15 @@ const ParameterForm = ({
|
|||
</OverlayTrigger>
|
||||
</span>
|
||||
</Form.Label>
|
||||
<Col sm="12">
|
||||
<Col style={{ padding: '0px', width: '177px' }}>
|
||||
<div className="d-flex">
|
||||
<div className="w-100">
|
||||
<div className="" style={{ height: '32px', width: '177px' }}>
|
||||
<CodeHinter
|
||||
onChange={(value) => setDefaultValue(value)}
|
||||
theme={darkMode ? 'monokai' : 'default'}
|
||||
currentState={emptyObj.current}
|
||||
usePortalEditor={false}
|
||||
height={36}
|
||||
style={{ height: '32px', width: '177px', marginBotto: '16px' }}
|
||||
initialValue={defaultValue}
|
||||
enablePreview={false}
|
||||
/>
|
||||
|
|
@ -10,11 +10,11 @@ const ParameterList = ({
|
|||
handleParameterRemove,
|
||||
currentState,
|
||||
darkMode,
|
||||
containerRef,
|
||||
}) => {
|
||||
const [showMore, setShowMore] = useState(false);
|
||||
const [selectedParameter, setSelectedParameter] = useState();
|
||||
const [formattedParameters, setFormattedParameters] = useState([]);
|
||||
const containerRef = useRef(null);
|
||||
const containerWidth = containerRef.current?.offsetWidth;
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -22,10 +22,10 @@ const ParameterList = ({
|
|||
const formattedParams = containerWidth
|
||||
? parameters.map((param, index) => {
|
||||
const boxWidth = Math.min((param?.name || '').length * 6 + 63 + 8, 178);
|
||||
totalWidth += boxWidth;
|
||||
totalWidth = Math.min(totalWidth + boxWidth, containerWidth);
|
||||
return {
|
||||
...param,
|
||||
isVisible: totalWidth <= containerWidth - 57 - 125 - 85,
|
||||
isVisible: totalWidth < containerWidth - 178,
|
||||
index,
|
||||
};
|
||||
})
|
||||
|
|
@ -54,8 +54,8 @@ const ParameterList = ({
|
|||
}, [showMore]);
|
||||
|
||||
return (
|
||||
<div className="card-header" ref={containerRef}>
|
||||
Parameters
|
||||
<div className="card-header">
|
||||
<p style={{ marginRight: '4px', margin: '0px' }}>Parameters</p>
|
||||
{formattedParameters
|
||||
.filter((param) => param.isVisible)
|
||||
.map((parameter) => {
|
||||
|
|
@ -104,7 +104,7 @@ const ParameterList = ({
|
|||
<Popover.Body
|
||||
key={'1'}
|
||||
bsPrefix="popover-body"
|
||||
className={`ps-1 pe-1 me-2 py-2 query-manager`}
|
||||
className={`ps-1 pe-1 py-2 query-manager`}
|
||||
style={{ maxWidth: '500px' }}
|
||||
>
|
||||
{formattedParameters
|
||||
|
|
@ -129,8 +129,9 @@ const ParameterList = ({
|
|||
<span>
|
||||
{formattedParameters.some((param) => !param.isVisible) && (
|
||||
<PillButton
|
||||
name={`${formattedParameters.reduce((count, param) => count + (!param.isVisible ? 1 : 0), 0)} More`}
|
||||
name={`+${formattedParameters.reduce((count, param) => count + (!param.isVisible ? 1 : 0), 0)}`}
|
||||
onClick={() => setShowMore(true)}
|
||||
className="more-parameters-button"
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
|
|
@ -269,7 +269,6 @@ export const QueryManagerBody = ({
|
|||
}`}
|
||||
>
|
||||
{selectedQuery?.data_source_id && selectedDataSource !== null ? renderChangeDataSource() : null}
|
||||
|
||||
{selectedDataSource === null || !selectedQuery ? renderDataSourcesList() : renderQueryElement()}
|
||||
{selectedDataSource !== null ? renderQueryOptions() : null}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -20,14 +20,18 @@ import { useAppVersionStore } from '@/_stores/appVersionStore';
|
|||
import { shallow } from 'zustand/shallow';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import { cloneDeep } from 'lodash';
|
||||
|
||||
export const QueryManagerHeader = forwardRef(({ darkMode, options, editorRef }, ref) => {
|
||||
import ParameterList from './ParameterList';
|
||||
|
||||
export const QueryManagerHeader = forwardRef(({ darkMode, options, editorRef, setOptions }, ref) => {
|
||||
const { renameQuery } = useDataQueriesActions();
|
||||
const selectedQuery = useSelectedQuery();
|
||||
const selectedDataSource = useSelectedDataSource();
|
||||
const [showCreateQuery, setShowCreateQuery] = useShowCreateQuery();
|
||||
const queryName = selectedQuery?.name ?? '';
|
||||
const { queries } = useCurrentState((state) => ({ queries: state.queries }), shallow);
|
||||
const currentState = useCurrentState((state) => ({ queries: state.queries }), shallow);
|
||||
const { queries } = currentState;
|
||||
const { isVersionReleased } = useAppVersionStore(
|
||||
(state) => ({
|
||||
isVersionReleased: state.isVersionReleased,
|
||||
|
|
@ -36,6 +40,8 @@ export const QueryManagerHeader = forwardRef(({ darkMode, options, editorRef },
|
|||
shallow
|
||||
);
|
||||
|
||||
const { updateDataQuery } = useDataQueriesActions();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedQuery?.name) {
|
||||
setShowCreateQuery(false);
|
||||
|
|
@ -80,8 +86,7 @@ export const QueryManagerHeader = forwardRef(({ darkMode, options, editorRef },
|
|||
kind: selectedDataSource.kind,
|
||||
name: queryName,
|
||||
};
|
||||
const hasParamSupport = selectedQuery?.options?.hasParamSupport;
|
||||
previewQuery(editorRef, query, false, undefined, hasParamSupport)
|
||||
previewQuery(editorRef, query, false, undefined)
|
||||
.then(() => {
|
||||
ref.current.scrollIntoView();
|
||||
})
|
||||
|
|
@ -100,11 +105,7 @@ export const QueryManagerHeader = forwardRef(({ darkMode, options, editorRef },
|
|||
})}
|
||||
>
|
||||
<button
|
||||
onClick={() =>
|
||||
runQuery(editorRef, selectedQuery?.id, selectedQuery?.name, undefined, 'edit', {
|
||||
shouldSetPreviewData: true,
|
||||
})
|
||||
}
|
||||
onClick={() => runQuery(editorRef, selectedQuery?.id, selectedQuery?.name, undefined, 'edit', {}, true)}
|
||||
className={`border-0 default-secondary-button float-right1 ${buttonLoadingState(isLoading)}`}
|
||||
data-cy="query-run-button"
|
||||
disabled={isInDraft}
|
||||
|
|
@ -129,14 +130,54 @@ export const QueryManagerHeader = forwardRef(({ darkMode, options, editorRef },
|
|||
|
||||
const renderButtons = () => {
|
||||
if (selectedQuery === null || showCreateQuery) return;
|
||||
const { isLoading } = queries[selectedQuery?.name] ?? false;
|
||||
return (
|
||||
<>
|
||||
<PreviewButton onClick={previewButtonOnClick} buttonLoadingState={buttonLoadingState} />
|
||||
<PreviewButton
|
||||
onClick={previewButtonOnClick}
|
||||
buttonLoadingState={buttonLoadingState}
|
||||
isRunButtonLoading={isLoading}
|
||||
/>
|
||||
{renderRunButton()}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const optionsChanged = (newOptions) => {
|
||||
setOptions(newOptions);
|
||||
updateDataQuery(cloneDeep(newOptions));
|
||||
};
|
||||
|
||||
const handleAddParameter = (newParameter) => {
|
||||
const prevOptions = { ...options };
|
||||
//check if paramname already used
|
||||
if (!prevOptions?.parameters?.some((param) => param.name === newParameter.name)) {
|
||||
const newOptions = {
|
||||
...prevOptions,
|
||||
parameters: [...(prevOptions?.parameters ?? []), newParameter],
|
||||
};
|
||||
optionsChanged(newOptions);
|
||||
}
|
||||
};
|
||||
|
||||
const handleParameterChange = (index, updatedParameter) => {
|
||||
const prevOptions = { ...options };
|
||||
//check if paramname already used
|
||||
if (!prevOptions?.parameters?.some((param, idx) => param.name === updatedParameter.name && index !== idx)) {
|
||||
const updatedParameters = [...prevOptions.parameters];
|
||||
updatedParameters[index] = updatedParameter;
|
||||
optionsChanged({ ...prevOptions, parameters: updatedParameters });
|
||||
}
|
||||
};
|
||||
|
||||
const handleParameterRemove = (index) => {
|
||||
const prevOptions = { ...options };
|
||||
const updatedParameters = prevOptions.parameters.filter((param, i) => index !== i);
|
||||
optionsChanged({ ...prevOptions, parameters: updatedParameters });
|
||||
};
|
||||
|
||||
const paramListContainerRef = useRef(null);
|
||||
|
||||
return (
|
||||
<div className="row header">
|
||||
<div className="col font-weight-500">
|
||||
|
|
@ -148,20 +189,37 @@ export const QueryManagerHeader = forwardRef(({ darkMode, options, editorRef },
|
|||
isDiabled={isVersionReleased}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div
|
||||
className="query-parameters-list col w-100 d-flex justify-content-center font-weight-500"
|
||||
ref={paramListContainerRef}
|
||||
>
|
||||
{selectedQuery && (
|
||||
<ParameterList
|
||||
parameters={options.parameters}
|
||||
handleAddParameter={handleAddParameter}
|
||||
handleParameterChange={handleParameterChange}
|
||||
handleParameterRemove={handleParameterRemove}
|
||||
currentState={currentState}
|
||||
darkMode={darkMode}
|
||||
containerRef={paramListContainerRef}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="query-header-buttons me-3">{renderButtons()}</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
const PreviewButton = ({ buttonLoadingState, onClick }) => {
|
||||
const PreviewButton = ({ buttonLoadingState, onClick, isRunButtonLoading }) => {
|
||||
const previewLoading = usePreviewLoading();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`default-tertiary-button float-right1 ${buttonLoadingState(previewLoading)}`}
|
||||
className={`default-tertiary-button float-right1 ${buttonLoadingState(previewLoading && !isRunButtonLoading)}`}
|
||||
data-cy={'query-preview-button'}
|
||||
>
|
||||
<span className="query-preview-svg d-flex align-items-center query-icon-wrapper">
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import { CodeHinter } from '@/Editor/CodeBuilder/CodeHinter';
|
|||
import { defaults } from 'lodash';
|
||||
import { Card } from 'react-bootstrap';
|
||||
import { useCurrentState } from '@/_stores/currentStateStore';
|
||||
import ParameterList from './ParameterList';
|
||||
|
||||
const Runjs = (props) => {
|
||||
const currentState = useCurrentState();
|
||||
|
|
@ -18,50 +17,12 @@ const Runjs = (props) => {
|
|||
});
|
||||
}, [currentState?.components, options?.parameters]);
|
||||
|
||||
const handleAddParameter = (newParameter) => {
|
||||
const prevOptions = { ...options };
|
||||
//check if paramname already used
|
||||
if (!prevOptions?.parameters?.some((param) => param.name === newParameter.name)) {
|
||||
props.optionsChanged({
|
||||
...prevOptions,
|
||||
parameters: [...prevOptions.parameters, newParameter],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setOptions(props.options);
|
||||
}, [props.options]);
|
||||
|
||||
const handleParameterChange = (index, updatedParameter) => {
|
||||
const prevOptions = { ...options };
|
||||
//check if paramname already used
|
||||
if (!prevOptions?.parameters?.some((param, idx) => param.name === updatedParameter.name && index !== idx)) {
|
||||
const updatedParameters = [...prevOptions.parameters];
|
||||
updatedParameters[index] = updatedParameter;
|
||||
props.optionsChanged({ ...prevOptions, parameters: updatedParameters });
|
||||
}
|
||||
};
|
||||
|
||||
const handleParameterRemove = (index) => {
|
||||
const prevOptions = { ...options };
|
||||
const updatedParameters = prevOptions.parameters.filter((param, i) => index !== i);
|
||||
props.optionsChanged({ ...prevOptions, parameters: updatedParameters });
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="runjs-editor mb-3">
|
||||
{(options.hasParamSupport || props.mode === 'create') && (
|
||||
<ParameterList
|
||||
parameters={options.parameters}
|
||||
handleAddParameter={handleAddParameter}
|
||||
handleParameterChange={handleParameterChange}
|
||||
handleParameterRemove={handleParameterRemove}
|
||||
currentState={props.currentState}
|
||||
darkMode={props.darkMode}
|
||||
/>
|
||||
)}
|
||||
|
||||
<CodeHinter
|
||||
initialValue={props.options.code}
|
||||
mode="javascript"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ export function changeOption(_ref, option, value) {
|
|||
_ref.setState(
|
||||
{
|
||||
options: {
|
||||
..._ref.state.options,
|
||||
..._ref.props.options,
|
||||
[option]: value,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import { runQuery } from '@/_helpers/appUtils';
|
|||
import { defaultSources } from './constants';
|
||||
import { useDataSources, useGlobalDataSources, useLoadingDataSources } from '@/_stores/dataSourcesStore';
|
||||
import { useQueryToBeRun, useSelectedQuery, useQueryPanelActions } from '@/_stores/queryPanelStore';
|
||||
import { CodeHinterContext } from '@/Editor/CodeBuilder/CodeHinterContext';
|
||||
import { resolveReferences } from '@/_helpers/utils';
|
||||
|
||||
const QueryManager = ({ mode, appId, darkMode, apps, allComponents, appDefinition, editorRef }) => {
|
||||
const loadingDataSources = useLoadingDataSources();
|
||||
|
|
@ -14,7 +16,6 @@ const QueryManager = ({ mode, appId, darkMode, apps, allComponents, appDefinitio
|
|||
const queryToBeRun = useQueryToBeRun();
|
||||
const selectedQuery = useSelectedQuery();
|
||||
const { setSelectedDataSource, setQueryToBeRun } = useQueryPanelActions();
|
||||
|
||||
const [options, setOptions] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -53,16 +54,34 @@ const QueryManager = ({ mode, appId, darkMode, apps, allComponents, appDefinitio
|
|||
'd-none': loadingDataSources,
|
||||
})}
|
||||
>
|
||||
<QueryManagerHeader darkMode={darkMode} options={options} editorRef={editorRef} appId={appId} />
|
||||
<QueryManagerBody
|
||||
<QueryManagerHeader
|
||||
darkMode={darkMode}
|
||||
options={options}
|
||||
allComponents={allComponents}
|
||||
apps={apps}
|
||||
editorRef={editorRef}
|
||||
appId={appId}
|
||||
appDefinition={appDefinition}
|
||||
setOptions={setOptions}
|
||||
/>
|
||||
<CodeHinterContext.Provider
|
||||
value={{
|
||||
parameters: selectedQuery?.options?.parameters?.reduce(
|
||||
(parameters, parameter) => ({
|
||||
...parameters,
|
||||
[parameter.name]: resolveReferences(parameter.defaultValue, {}, undefined),
|
||||
}),
|
||||
{}
|
||||
),
|
||||
}}
|
||||
>
|
||||
<QueryManagerBody
|
||||
darkMode={darkMode}
|
||||
options={options}
|
||||
allComponents={allComponents}
|
||||
apps={apps}
|
||||
appId={appId}
|
||||
appDefinition={appDefinition}
|
||||
setOptions={setOptions}
|
||||
/>
|
||||
</CodeHinterContext.Provider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -79,7 +79,6 @@ export const schemaUnavailableOptions = {
|
|||
},
|
||||
runjs: {
|
||||
code: '',
|
||||
hasParamSupport: true,
|
||||
parameters: [],
|
||||
},
|
||||
runpy: {},
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import useShowPopover from '@/_hooks/useShowPopover';
|
|||
import DataSourceIcon from '../QueryManager/Components/DataSourceIcon';
|
||||
import { staticDataSources } from '../QueryManager/constants';
|
||||
import { Tooltip } from 'react-tooltip';
|
||||
import { PillButton } from '../QueryManager/QueryEditors/Runjs/ParameterDetails';
|
||||
import { PillButton } from '../QueryManager/Components/ParameterDetails';
|
||||
|
||||
const FilterandSortPopup = ({ darkMode, selectedDataSources, onFilterDatasourcesChange, clearSelectedDataSources }) => {
|
||||
const [showMenu, setShowMenu] = useShowPopover(false, '#query-sort-filter-popover', '#query-sort-filter-popover-btn');
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ import {
|
|||
orgEnvironmentConstantService,
|
||||
dataqueryService,
|
||||
appService,
|
||||
appEnvironmentService,
|
||||
} from '@/_services';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { Container } from './Container';
|
||||
import { Confirm } from './Viewer/Confirm';
|
||||
import { ViewerNavigation } from './Viewer/ViewerNavigation';
|
||||
import {
|
||||
onComponentOptionChanged,
|
||||
onComponentOptionsChanged,
|
||||
|
|
@ -38,7 +38,13 @@ import { shallow } from 'zustand/shallow';
|
|||
import { useAppDataActions, useAppDataStore } from '@/_stores/appDataStore';
|
||||
import { getPreviewQueryParams, redirectToErrorPage } from '@/_helpers/routes';
|
||||
import { ERROR_TYPES } from '@/_helpers/constants';
|
||||
import { camelizeKeys } from 'humps';
|
||||
import { useAppVersionStore } from '@/_stores/appVersionStore';
|
||||
import TooljetLogoIcon from '@/_ui/Icon/solidIcons/TooljetLogoIcon';
|
||||
import TooljetLogoText from '@/_ui/Icon/solidIcons/TooljetLogoText';
|
||||
import ViewerSidebarNavigation from './Viewer/ViewerSidebarNavigation';
|
||||
import MobileHeader from './Viewer/MobileHeader';
|
||||
import DesktopHeader from './Viewer/DesktopHeader';
|
||||
import './Viewer/viewer.scss';
|
||||
|
||||
class ViewerComponent extends React.Component {
|
||||
constructor(props) {
|
||||
|
|
@ -75,13 +81,13 @@ class ViewerComponent extends React.Component {
|
|||
|
||||
setStateForApp = (data, byAppSlug = false) => {
|
||||
const appDefData = buildAppDefinition(data);
|
||||
|
||||
if (byAppSlug) {
|
||||
appDefData.globalSettings = data.globalSettings;
|
||||
appDefData.homePageId = data.homePageId;
|
||||
appDefData.showViewerNavigation = data.showViewerNavigation;
|
||||
}
|
||||
|
||||
useAppVersionStore.getState().actions.updateEditingVersion(data.editing_version);
|
||||
useAppVersionStore.getState().actions.updateReleasedVersionId(data.currentVersionId);
|
||||
this.setState({
|
||||
app: data,
|
||||
isLoading: false,
|
||||
|
|
@ -92,7 +98,7 @@ class ViewerComponent extends React.Component {
|
|||
};
|
||||
|
||||
setStateForContainer = async (data, appVersionId) => {
|
||||
const appDefData = buildAppDefinition(data);
|
||||
const appDefData = this.state.appDefinition;
|
||||
|
||||
const currentUser = this.state.currentUser;
|
||||
let userVars = {};
|
||||
|
|
@ -199,7 +205,6 @@ class ViewerComponent extends React.Component {
|
|||
|
||||
computeComponentState(components).then(async () => {
|
||||
this.setState({ initialComputationOfStateDone: true, defaultComponentStateComputed: true });
|
||||
console.log('Default component state computed and set');
|
||||
this.runQueries(dataQueries);
|
||||
|
||||
const currentPageEvents = this.state.events.filter(
|
||||
|
|
@ -233,16 +238,11 @@ class ViewerComponent extends React.Component {
|
|||
variablesResult = constants;
|
||||
}
|
||||
|
||||
console.log('--org constant 2.0', { variablesResult });
|
||||
|
||||
if (variablesResult && Array.isArray(variablesResult)) {
|
||||
variablesResult.map((constant) => {
|
||||
const constantValue = constant.values.find((value) => value.environmentName === 'production')['value'];
|
||||
orgConstants[constant.name] = constantValue;
|
||||
});
|
||||
|
||||
// console.log('--org constant 2.0', { orgConstants });
|
||||
|
||||
return {
|
||||
constants: orgConstants,
|
||||
};
|
||||
|
|
@ -271,6 +271,11 @@ class ViewerComponent extends React.Component {
|
|||
return variables;
|
||||
};
|
||||
|
||||
fetchAppVersions = async (appId) => {
|
||||
const appVersions = await appEnvironmentService.getVersionsByEnvironment(appId);
|
||||
useAppVersionStore.getState().actions.setAppVersions(appVersions.appVersions);
|
||||
};
|
||||
|
||||
loadApplicationBySlug = (slug, authentication_failed = false) => {
|
||||
appService
|
||||
.fetchAppBySlug(slug)
|
||||
|
|
@ -303,8 +308,8 @@ class ViewerComponent extends React.Component {
|
|||
});
|
||||
};
|
||||
|
||||
loadApplicationByVersion = (appId, versionId) => {
|
||||
appService
|
||||
loadApplicationByVersion = async (appId, versionId) => {
|
||||
await appService
|
||||
.fetchAppByVersion(appId, versionId)
|
||||
.then((data) => {
|
||||
this.setStateForApp(data);
|
||||
|
|
@ -317,6 +322,13 @@ class ViewerComponent extends React.Component {
|
|||
});
|
||||
};
|
||||
|
||||
setAppDefinitionFromVersion = (data) => {
|
||||
this.setState({
|
||||
isLoading: true,
|
||||
});
|
||||
this.loadApplicationByVersion(this.props.id, data.editing_version.id);
|
||||
};
|
||||
|
||||
updateQueryConfirmationList = (queryConfirmationList) =>
|
||||
useEditorStore.getState().actions.updateQueryConfirmationList(queryConfirmationList);
|
||||
|
||||
|
|
@ -348,7 +360,7 @@ class ViewerComponent extends React.Component {
|
|||
userVars,
|
||||
versionId,
|
||||
});
|
||||
|
||||
this.fetchAppVersions(appId);
|
||||
versionId ? this.loadApplicationByVersion(appId, versionId) : this.loadApplicationBySlug(slug);
|
||||
} else if (currentSession?.authentication_failed) {
|
||||
this.loadApplicationBySlug(slug, true);
|
||||
|
|
@ -386,6 +398,11 @@ class ViewerComponent extends React.Component {
|
|||
this.setState({ isLoading: true });
|
||||
this.loadApplicationBySlug(this.props.params.slug);
|
||||
}
|
||||
if (prevProps.currentLayout !== this.props.currentLayout) {
|
||||
if (this.props.id && useAppVersionStore.getState()?.editingVersion?.id) {
|
||||
this.loadApplicationByVersion(this.props.id, useAppVersionStore.getState().editingVersion.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.state.initialComputationOfStateDone) this.handlePageSwitchingBasedOnURLparam();
|
||||
if (this.state.homepage !== prevState.homepage && !this.state.isLoading) {
|
||||
|
|
@ -544,7 +561,6 @@ class ViewerComponent extends React.Component {
|
|||
componentWillUnmount() {
|
||||
this.subscription && this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
appDefinition,
|
||||
|
|
@ -555,12 +571,15 @@ class ViewerComponent extends React.Component {
|
|||
dataQueries,
|
||||
canvasWidth,
|
||||
} = this.state;
|
||||
|
||||
const currentCanvasWidth = canvasWidth;
|
||||
const queryConfirmationList = this.props?.queryConfirmationList ?? [];
|
||||
|
||||
const canvasMaxWidth = this.computeCanvasMaxWidth();
|
||||
const pages =
|
||||
Object.entries(_.cloneDeep(appDefinition)?.pages)
|
||||
.map(([id, page]) => ({ id, ...page }))
|
||||
.sort((a, b) => a.index - b.index) || [];
|
||||
|
||||
const isMobilePreviewMode = this.props.versionId && this.props.currentLayout === 'mobile';
|
||||
if (this.state.app?.isLoading) {
|
||||
return (
|
||||
<div className="tooljet-logo-loader">
|
||||
|
|
@ -574,116 +593,173 @@ class ViewerComponent extends React.Component {
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
if (this.state.app?.is_maintenance_on) {
|
||||
return (
|
||||
<div className="maintenance_container">
|
||||
<div className="card">
|
||||
<div className="card-body" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<h3>{this.props.t('viewer', 'Sorry!. This app is under maintenance')}</h3>
|
||||
</div>
|
||||
} else if (this.state.app?.is_maintenance_on) {
|
||||
return (
|
||||
<div className="maintenance_container">
|
||||
<div className="card">
|
||||
<div className="card-body" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<h3>{this.props.t('viewer', 'Sorry!. This app is under maintenance')}</h3>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className="viewer wrapper">
|
||||
<Confirm
|
||||
show={queryConfirmationList.length > 0}
|
||||
message={'Do you want to run this query?'}
|
||||
onConfirm={(queryConfirmationData) =>
|
||||
onQueryConfirmOrCancel(this.getViewerRef(), queryConfirmationData, true, 'view')
|
||||
}
|
||||
onCancel={() => onQueryConfirmOrCancel(this.getViewerRef(), queryConfirmationList[0], false, 'view')}
|
||||
queryConfirmationData={queryConfirmationList[0]}
|
||||
key={queryConfirmationList[0]?.queryName}
|
||||
/>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<ViewerNavigation.Header
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div className={`viewer wrapper ${this.props.currentLayout === 'mobile' ? 'mobile-layout' : ''}`}>
|
||||
<Confirm
|
||||
show={queryConfirmationList.length > 0}
|
||||
message={'Do you want to run this query?'}
|
||||
onConfirm={(queryConfirmationData) =>
|
||||
onQueryConfirmOrCancel(this.getViewerRef(), queryConfirmationData, true, 'view')
|
||||
}
|
||||
onCancel={() => onQueryConfirmOrCancel(this.getViewerRef(), queryConfirmationList[0], false, 'view')}
|
||||
queryConfirmationData={queryConfirmationList[0]}
|
||||
key={queryConfirmationList[0]?.queryName}
|
||||
/>
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
{this.props.currentLayout !== 'mobile' && (
|
||||
<DesktopHeader
|
||||
showHeader={!appDefinition.globalSettings?.hideHeader && isAppLoaded}
|
||||
appName={this.state.app?.name ?? null}
|
||||
changeDarkMode={this.changeDarkMode}
|
||||
darkMode={this.props.darkMode}
|
||||
pages={Object.entries(this.state.appDefinition?.pages) ?? []}
|
||||
pages={pages}
|
||||
currentPageId={this.state?.currentPageId ?? this.state.appDefinition?.homePageId}
|
||||
switchPage={this.switchPage}
|
||||
setAppDefinitionFromVersion={this.setAppDefinitionFromVersion}
|
||||
showViewerNavigation={appDefinition?.showViewerNavigation}
|
||||
/>
|
||||
<div className="sub-section">
|
||||
<div className="main">
|
||||
<div
|
||||
className="canvas-container align-items-center"
|
||||
style={{
|
||||
background: this.computeCanvasBackgroundColor() || (!this.props.darkMode ? '#EBEBEF' : '#2E3035'),
|
||||
}}
|
||||
>
|
||||
<div className="areas d-flex flex-rows">
|
||||
{appDefinition?.showViewerNavigation && (
|
||||
<ViewerNavigation
|
||||
isMobileDevice={this.props.currentLayout === 'mobile'}
|
||||
canvasBackgroundColor={this.computeCanvasBackgroundColor()}
|
||||
pages={Object.entries(this.state.appDefinition?.pages) ?? []}
|
||||
currentPageId={this.state?.currentPageId ?? this.state.appDefinition?.homePageId}
|
||||
switchPage={this.switchPage}
|
||||
darkMode={this.props.darkMode}
|
||||
/>
|
||||
)}
|
||||
<div className="flex-grow-1 d-flex justify-content-center">
|
||||
<div
|
||||
className="canvas-area"
|
||||
style={{
|
||||
width: currentCanvasWidth,
|
||||
maxWidth: canvasMaxWidth,
|
||||
backgroundColor: this.computeCanvasBackgroundColor(),
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
}}
|
||||
>
|
||||
{defaultComponentStateComputed && (
|
||||
<>
|
||||
{isLoading ? (
|
||||
<div className="mx-auto mt-5 w-50 p-5">
|
||||
<center>
|
||||
<div className="spinner-border text-azure" role="status"></div>
|
||||
</center>
|
||||
</div>
|
||||
) : (
|
||||
<Container
|
||||
appDefinition={appDefinition}
|
||||
appDefinitionChanged={() => false} // function not relevant in viewer
|
||||
snapToGrid={true}
|
||||
appLoading={isLoading}
|
||||
darkMode={this.props.darkMode}
|
||||
onEvent={this.handleEvent}
|
||||
mode="view"
|
||||
deviceWindowWidth={deviceWindowWidth}
|
||||
selectedComponent={this.state.selectedComponent}
|
||||
onComponentClick={(id, component) => {
|
||||
this.setState({
|
||||
selectedComponent: { id, component },
|
||||
});
|
||||
onComponentClick(this, id, component, 'view');
|
||||
}}
|
||||
onComponentOptionChanged={(component, optionName, value) => {
|
||||
return onComponentOptionChanged(component, optionName, value);
|
||||
}}
|
||||
onComponentOptionsChanged={onComponentOptionsChanged}
|
||||
canvasWidth={this.getCanvasWidth()}
|
||||
dataQueries={dataQueries}
|
||||
currentPageId={this.state.currentPageId}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{/* Render following mobile header only when its in preview mode and not in launched app */}
|
||||
{this.props.currentLayout === 'mobile' && !isMobilePreviewMode && (
|
||||
<MobileHeader
|
||||
showHeader={!appDefinition.globalSettings?.hideHeader && isAppLoaded}
|
||||
appName={this.state.app?.name ?? null}
|
||||
changeDarkMode={this.changeDarkMode}
|
||||
darkMode={this.props.darkMode}
|
||||
pages={pages}
|
||||
currentPageId={this.state?.currentPageId ?? this.state.appDefinition?.homePageId}
|
||||
switchPage={this.switchPage}
|
||||
setAppDefinitionFromVersion={this.setAppDefinitionFromVersion}
|
||||
showViewerNavigation={appDefinition?.showViewerNavigation}
|
||||
/>
|
||||
)}
|
||||
<div className="sub-section">
|
||||
<div className="main">
|
||||
<div
|
||||
className="canvas-container align-items-center"
|
||||
style={{
|
||||
background: this.computeCanvasBackgroundColor() || (!this.props.darkMode ? '#EBEBEF' : '#2E3035'),
|
||||
}}
|
||||
>
|
||||
<div className="areas d-flex flex-rows">
|
||||
{appDefinition?.showViewerNavigation && (
|
||||
<ViewerSidebarNavigation
|
||||
showHeader={!appDefinition.globalSettings?.hideHeader && isAppLoaded}
|
||||
isMobileDevice={this.props.currentLayout === 'mobile'}
|
||||
canvasBackgroundColor={this.computeCanvasBackgroundColor()}
|
||||
pages={pages}
|
||||
currentPageId={this.state?.currentPageId ?? this.state.appDefinition?.homePageId}
|
||||
switchPage={this.switchPage}
|
||||
darkMode={this.props.darkMode}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
className="flex-grow-1 d-flex justify-content-center"
|
||||
style={{
|
||||
backgroundColor: isMobilePreviewMode ? '#ACB2B9' : 'unset',
|
||||
marginLeft:
|
||||
appDefinition?.showViewerNavigation && this.props.currentLayout !== 'mobile'
|
||||
? '200px'
|
||||
: 'auto',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="canvas-area"
|
||||
style={{
|
||||
width: isMobilePreviewMode ? '450px' : currentCanvasWidth,
|
||||
maxWidth: isMobilePreviewMode ? '450px' : canvasMaxWidth,
|
||||
backgroundColor: this.computeCanvasBackgroundColor(),
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
}}
|
||||
>
|
||||
{this.props.currentLayout === 'mobile' && isMobilePreviewMode && (
|
||||
<MobileHeader
|
||||
showHeader={!appDefinition.globalSettings?.hideHeader && isAppLoaded}
|
||||
appName={this.state.app?.name ?? null}
|
||||
changeDarkMode={this.changeDarkMode}
|
||||
darkMode={this.props.darkMode}
|
||||
pages={pages}
|
||||
currentPageId={this.state?.currentPageId ?? this.state.appDefinition?.homePageId}
|
||||
switchPage={this.switchPage}
|
||||
setAppDefinitionFromVersion={this.setAppDefinitionFromVersion}
|
||||
showViewerNavigation={appDefinition?.showViewerNavigation}
|
||||
/>
|
||||
)}
|
||||
{defaultComponentStateComputed && (
|
||||
<>
|
||||
{isLoading ? (
|
||||
<div className="mx-auto mt-5 w-50 p-5">
|
||||
<center>
|
||||
<div className="spinner-border text-azure" role="status"></div>
|
||||
</center>
|
||||
</div>
|
||||
) : (
|
||||
<Container
|
||||
appDefinition={appDefinition}
|
||||
appDefinitionChanged={() => false} // function not relevant in viewer
|
||||
snapToGrid={true}
|
||||
appLoading={isLoading}
|
||||
darkMode={this.props.darkMode}
|
||||
onEvent={this.handleEvent}
|
||||
mode="view"
|
||||
deviceWindowWidth={isMobilePreviewMode ? '450px' : deviceWindowWidth}
|
||||
selectedComponent={this.state.selectedComponent}
|
||||
onComponentClick={(id, component) => {
|
||||
this.setState({
|
||||
selectedComponent: { id, component },
|
||||
});
|
||||
onComponentClick(this, id, component, 'view');
|
||||
}}
|
||||
onComponentOptionChanged={(component, optionName, value) => {
|
||||
return onComponentOptionChanged(component, optionName, value);
|
||||
}}
|
||||
onComponentOptionsChanged={onComponentOptionsChanged}
|
||||
canvasWidth={this.getCanvasWidth()}
|
||||
dataQueries={dataQueries}
|
||||
currentPageId={this.state.currentPageId}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className="powered-with-tj"
|
||||
onClick={() => {
|
||||
const url = `https://tooljet.com/?utm_source=powered_by_banner&utm_medium=${
|
||||
useAppDataStore.getState()?.metadata?.instance_id
|
||||
}&utm_campaign=self_hosted`;
|
||||
window.open(url, '_blank');
|
||||
}}
|
||||
>
|
||||
Built with
|
||||
<span className={'powered-with-tj-icon'}>
|
||||
<TooljetLogoIcon />
|
||||
</span>
|
||||
<TooljetLogoText fill={this.props.darkMode ? '#ECEDEE' : '#11181C'} />
|
||||
</div>
|
||||
{/* Following div is a hack to prevent showing mobile drawer navigation coming from left*/}
|
||||
{isMobilePreviewMode && <div className="hide-drawer-transition" style={{ right: 0 }}></div>}
|
||||
{isMobilePreviewMode && <div className="hide-drawer-transition" style={{ left: 0 }}></div>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DndProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
</DndProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -696,7 +772,6 @@ const withStore = (Component) => (props) => {
|
|||
}),
|
||||
shallow
|
||||
);
|
||||
|
||||
const { updateState } = useAppDataActions();
|
||||
return (
|
||||
<Component
|
||||
|
|
|
|||
86
frontend/src/Editor/Viewer/DesktopHeader.jsx
Normal file
86
frontend/src/Editor/Viewer/DesktopHeader.jsx
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import React from 'react';
|
||||
import _, { isEmpty } from 'lodash';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import LogoIcon from '@assets/images/rocket.svg';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { DarkModeToggle } from '@/_components/DarkModeToggle';
|
||||
import Header from './Header';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { redirectToDashboard } from '@/_helpers/routes';
|
||||
import classNames from 'classnames';
|
||||
import { useAppVersionStore } from '@/_stores/appVersionStore';
|
||||
import PreviewSettings from './PreviewSettings';
|
||||
|
||||
const DesktopHeader = ({ showHeader, appName, changeDarkMode, darkMode, setAppDefinitionFromVersion }) => {
|
||||
const { isVersionReleased, editingVersion } = useAppVersionStore(
|
||||
(state) => ({
|
||||
isVersionReleased: state.isVersionReleased,
|
||||
editingVersion: state?.editingVersion,
|
||||
}),
|
||||
shallow
|
||||
);
|
||||
const _renderAppNameAndLogo = () => (
|
||||
<div
|
||||
className={classNames('d-flex', 'align-items-center')}
|
||||
style={{ visibility: showHeader || isVersionReleased ? 'visible' : 'hidden' }}
|
||||
>
|
||||
<h1 className="navbar-brand d-none-navbar-horizontal pe-0">
|
||||
<Link
|
||||
data-cy="viewer-page-logo"
|
||||
onClick={() => {
|
||||
redirectToDashboard();
|
||||
}}
|
||||
>
|
||||
<LogoIcon />
|
||||
</Link>
|
||||
</h1>
|
||||
<div className="navbar-seperator" style={{ margin: '0px 1.375rem' }}></div>
|
||||
{appName && (
|
||||
<div className="d-flex align-items-center app-title">
|
||||
<span>{appName}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
if (!showHeader) {
|
||||
return (
|
||||
<>
|
||||
{!isVersionReleased && !isEmpty(editingVersion) && (
|
||||
<PreviewSettings
|
||||
isMobileLayout={false}
|
||||
showHeader={showHeader}
|
||||
setAppDefinitionFromVersion={setAppDefinitionFromVersion}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
)}
|
||||
<span className="released-version-no-header-dark-mode-icon">
|
||||
<DarkModeToggle switchDarkMode={changeDarkMode} darkMode={darkMode} />
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Header
|
||||
styles={{
|
||||
height: '46px',
|
||||
}}
|
||||
>
|
||||
{_renderAppNameAndLogo()}
|
||||
{!isVersionReleased && !isEmpty(editingVersion) && (
|
||||
<PreviewSettings
|
||||
isMobileLayout={false}
|
||||
showHeader={showHeader}
|
||||
setAppDefinitionFromVersion={setAppDefinitionFromVersion}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
)}
|
||||
<div className="d-flex align-items-center">
|
||||
<DarkModeToggle switchDarkMode={changeDarkMode} darkMode={darkMode} />
|
||||
</div>
|
||||
</Header>
|
||||
);
|
||||
};
|
||||
|
||||
export default DesktopHeader;
|
||||
|
|
@ -1,14 +1,16 @@
|
|||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const Header = ({ children, className }) => {
|
||||
const Header = ({ children, className, styles = {}, showNavbarClass = true }) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
minHeight: 45,
|
||||
minHeight: '47px',
|
||||
...styles,
|
||||
}}
|
||||
className={`header ${className}`}
|
||||
>
|
||||
<header className="navbar navbar-expand-md d-print-none">
|
||||
<header className={classNames({ 'navbar navbar-expand-md': showNavbarClass })}>
|
||||
<div className="container-xl header-container position-relative">{children}</div>
|
||||
</header>
|
||||
</div>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue