|
|
@ -71,6 +71,7 @@ jobs:
|
|||
- name: Build and test
|
||||
env:
|
||||
APPSMITH_MONGODB_URI: "mongodb://localhost:27017/mobtools"
|
||||
APPSMITH_CLOUD_SERVICES_BASE_URL: "https://release-cs.appsmith.com"
|
||||
APPSMITH_REDIS_URL: "redis://127.0.0.1:6379"
|
||||
APPSMITH_ENCRYPTION_PASSWORD: "password"
|
||||
APPSMITH_ENCRYPTION_SALT: "salt"
|
||||
|
|
@ -199,7 +200,7 @@ jobs:
|
|||
if(check.length == 0) {
|
||||
const head_sha = pull.head.sha;
|
||||
const { data: completed_at } = await github.checks.create({
|
||||
owner: context.repo.owner,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
head_sha: head_sha,
|
||||
name: process.env.job,
|
||||
|
|
@ -443,7 +444,7 @@ jobs:
|
|||
if(check.length == 0) {
|
||||
const head_sha = pull.head.sha;
|
||||
const { data: completed_at } = await github.checks.create({
|
||||
owner: context.repo.owner,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
head_sha: head_sha,
|
||||
name: process.env.job,
|
||||
|
|
@ -512,7 +513,7 @@ jobs:
|
|||
if(check.length == 0) {
|
||||
const head_sha = pull.head.sha;
|
||||
const { data: completed_at } = await github.checks.create({
|
||||
owner: context.repo.owner,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
head_sha: head_sha,
|
||||
name: process.env.job,
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ jobs:
|
|||
- name: Build and test
|
||||
env:
|
||||
APPSMITH_MONGODB_URI: "mongodb://localhost:27017/mobtools"
|
||||
APPSMITH_CLOUD_SERVICES_BASE_URL: "https://release-cs.appsmith.com"
|
||||
APPSMITH_REDIS_URL: "redis://127.0.0.1:6379"
|
||||
APPSMITH_ENCRYPTION_PASSWORD: "password"
|
||||
APPSMITH_ENCRYPTION_SALT: "salt"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ GIT_SHA=$(eval git rev-parse HEAD)
|
|||
echo $GIT_SHA
|
||||
echo "Sentry Auth Token: $SENTRY_AUTH_TOKEN"
|
||||
|
||||
REACT_APP_SENTRY_RELEASE=$GIT_SHA EXTEND_ESLINT=true craco --max-old-space-size=4096 build --config craco.build.config.js
|
||||
REACT_APP_SENTRY_RELEASE=$GIT_SHA REACT_APP_CLIENT_LOG_LEVEL=ERROR EXTEND_ESLINT=true craco --max-old-space-size=4096 build --config craco.build.config.js
|
||||
|
||||
rm ./build/static/js/*.js.map
|
||||
echo "build finished"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"dsl": {
|
||||
"widgetName": "MainContainer",
|
||||
"backgroundColor": "none",
|
||||
"rightColumn": 1280,
|
||||
"snapColumns": 64,
|
||||
"detachFromLayout": true,
|
||||
"widgetId": "0",
|
||||
"topRow": 0,
|
||||
"bottomRow": 1300,
|
||||
"containerStyle": "none",
|
||||
"snapRows": 125,
|
||||
"parentRowSpace": 1,
|
||||
"type": "CANVAS_WIDGET",
|
||||
"canExtend": true,
|
||||
"version": 23,
|
||||
"minHeight": 1310,
|
||||
"parentColumnSpace": 1,
|
||||
"dynamicTriggerPathList": [],
|
||||
"dynamicBindingPathList": [],
|
||||
"leftColumn": 0,
|
||||
"children": [
|
||||
{
|
||||
"widgetName": "Divider1",
|
||||
"rightColumn": 23,
|
||||
"widgetId": "i5kplngd19",
|
||||
"topRow": 25,
|
||||
"bottomRow": 75,
|
||||
"parentRowSpace": 10,
|
||||
"isVisible": true,
|
||||
"type": "DIVIDER_WIDGET",
|
||||
"version": 1,
|
||||
"parentId": "0",
|
||||
"isLoading": false,
|
||||
"parentColumnSpace": 19.75,
|
||||
"dynamicTriggerPathList": [],
|
||||
"leftColumn": 15,
|
||||
"dynamicBindingPathList": [],
|
||||
"orientation": "horizontal",
|
||||
"capType": "nc",
|
||||
"capSide": -1,
|
||||
"strokeStyle": "solid",
|
||||
"dividerColor": "black",
|
||||
"thickness": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -7,11 +7,9 @@ describe("API Panel Test Functionality", function() {
|
|||
cy.NavigateToAPI_Panel();
|
||||
cy.log("Navigation to API Panel screen successful");
|
||||
cy.CreateAPI("FirstAPI");
|
||||
cy.get("textarea").should(
|
||||
"have.attr",
|
||||
"placeholder",
|
||||
"https://mock-api.appsmith.com/users",
|
||||
);
|
||||
cy.get(".CodeMirror-placeholder")
|
||||
.first()
|
||||
.should("have.text", "https://mock-api.appsmith.com/users");
|
||||
cy.log("Creation of FirstAPI Action successful");
|
||||
cy.enterDatasourceAndPath(testdata.baseUrl, testdata.methods);
|
||||
cy.SaveAndRunAPI();
|
||||
|
|
@ -29,25 +27,20 @@ describe("API Panel Test Functionality", function() {
|
|||
cy.NavigateToAPI_Panel();
|
||||
cy.CreateAPI("CrashTestAPI");
|
||||
cy.SelectAction(testdata.postAction);
|
||||
cy.xpath(apiwidget.headerKey)
|
||||
cy.get(apiwidget.headerKey)
|
||||
.first()
|
||||
.focus({ force: true })
|
||||
.type("{uparrow}", { force: true })
|
||||
.type("{ctrl}{shift}{downarrow}", { force: true })
|
||||
.type("{backspace}", { force: true });
|
||||
.click({ force: true })
|
||||
.type("{uparrow}", { parseSpecialCharSequences: true })
|
||||
.type("{ctrl}{shift}{downarrow}", { parseSpecialCharSequences: true })
|
||||
.type("{backspace}", { parseSpecialCharSequences: true });
|
||||
// assert so that this fails
|
||||
cy.xpath(apiwidget.headerKey).should("be.visible");
|
||||
cy.xpath(apiwidget.headerKey).should("have.value", "");
|
||||
cy.get(apiwidget.headerKey).should("be.visible");
|
||||
cy.get(apiwidget.headerKey).should("have.value", "");
|
||||
});
|
||||
|
||||
it("Should correctly parse query params", function() {
|
||||
cy.NavigateToAPI_Panel();
|
||||
cy.CreateAPI("APIWithQueryParams");
|
||||
cy.get("textarea").should(
|
||||
"have.attr",
|
||||
"placeholder",
|
||||
"https://mock-api.appsmith.com/users",
|
||||
);
|
||||
cy.enterDatasourceAndPath(testdata.baseUrl, testdata.methodWithQueryParam);
|
||||
cy.ValidateQueryParams({
|
||||
key: "q",
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ describe("API Panel request body", function() {
|
|||
cy.contains(apiEditor.bodyTab).click();
|
||||
cy.contains(testdata.apiFormDataBodyType).click();
|
||||
|
||||
cy.xpath(apiwidget.formEncoded).should("be.visible");
|
||||
cy.get(apiwidget.formEncoded).should("be.visible");
|
||||
|
||||
cy.DeleteAPI();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ describe("Debugger logs", function() {
|
|||
});
|
||||
|
||||
it("Reset debugger state", function() {
|
||||
cy.openPropertyPane("buttonwidget");
|
||||
cy.get(".t--property-control-visible")
|
||||
.find(".t--js-toggle")
|
||||
.click();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
const dsl = require("../../../../fixtures/DividerDsl.json");
|
||||
const explorer = require("../../../../locators/explorerlocators.json");
|
||||
|
||||
describe("Divider Widget Functionality", function() {
|
||||
beforeEach(() => {
|
||||
cy.addDsl(dsl);
|
||||
});
|
||||
|
||||
it("Add new Divider", () => {
|
||||
cy.get(explorer.addWidget).click();
|
||||
cy.dragAndDropToCanvas("dividerwidget", { x: 300, y: 300 });
|
||||
cy.get(".t--divider-widget").should("exist");
|
||||
});
|
||||
|
||||
it("Open Existing Divider from created Widgets list", () => {
|
||||
cy.get(".bp3-icon-caret-right ~ .t--entity-name:contains(Widgets)").click({
|
||||
multiple: true,
|
||||
});
|
||||
cy.get(".bp3-icon-caret-right ~ .t--entity-name:contains(Divider1)").click({
|
||||
multiple: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -23,7 +23,6 @@ describe("Image Widget Functionality", function() {
|
|||
);
|
||||
cy.get(viewWidgetsPage.defaultImage)
|
||||
.click({ force: true })
|
||||
.type(this.data.command)
|
||||
.type(this.data.defaultimage);
|
||||
/**
|
||||
* @param{URL} ImageUrl
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
const widgetsPage = require("../../../../locators/Widgets.json");
|
||||
const dsl = require("../../../../fixtures/tableNewDsl.json");
|
||||
|
||||
describe("Table Widget row multi select validation", function() {
|
||||
before(() => {
|
||||
cy.addDsl(dsl);
|
||||
});
|
||||
|
||||
it("Test multi select column shows when enableMultirowselection is true", function() {
|
||||
cy.openPropertyPane("tablewidget");
|
||||
cy.get(widgetsPage.toggleEnableMultirowselection)
|
||||
.first()
|
||||
.click({ force: true });
|
||||
cy.get(".t--table-multiselect-header")
|
||||
.first()
|
||||
.should("be.visible");
|
||||
|
||||
cy.get(".t--table-multiselect")
|
||||
.first()
|
||||
.should("be.visible");
|
||||
});
|
||||
|
||||
it("Test click on header cell selects all row", function() {
|
||||
// click on header check cell
|
||||
cy.get(".t--table-multiselect-header")
|
||||
.first()
|
||||
.click({ force: true });
|
||||
// check if rows selected
|
||||
cy.get(".tr").should("have.class", "selected-row");
|
||||
});
|
||||
|
||||
it("Test click on single row cell changes header select cell state", function() {
|
||||
// un select all rows
|
||||
cy.get(".t--table-multiselect-header")
|
||||
.first()
|
||||
.click({ force: true });
|
||||
// click on first row select box
|
||||
cy.get(".t--table-multiselect")
|
||||
.first()
|
||||
.click({ force: true });
|
||||
// check if header cell is in half check state
|
||||
cy.get(".t--table-multiselect-header-half-check-svg")
|
||||
.first()
|
||||
.should("be.visible");
|
||||
});
|
||||
});
|
||||
|
|
@ -32,6 +32,11 @@ describe("Test Suite to validate copy/paste table Widget", function() {
|
|||
200,
|
||||
);
|
||||
cy.get(commonlocators.toastAction).should("be.visible");
|
||||
|
||||
//Check after copying new table widget should not have any warnings
|
||||
cy.get('.t--widget-propertypane-toggle [name="warning"]').should(
|
||||
"not.exist",
|
||||
);
|
||||
/*
|
||||
cy.get(commonlocators.toastAction)
|
||||
.contains("UNDO")
|
||||
|
|
|
|||
|
|
@ -264,16 +264,16 @@ describe("Table Widget Functionality", function() {
|
|||
const tabValue = tabData;
|
||||
expect(tabValue).to.be.equal("Lindsay Ferguson");
|
||||
cy.log("the value is" + tabValue);
|
||||
cy.get(publish.compactMode).click();
|
||||
cy.get(publish.compactOpt)
|
||||
cy.get(publish.rowHeight).click();
|
||||
cy.get(publish.rowHeightOpt)
|
||||
.contains("Tall")
|
||||
.click();
|
||||
cy.scrollTabledataPublish("3", "3").then((tabData) => {
|
||||
const tabValue = tabData;
|
||||
expect(tabValue).to.be.equal("Byron Fields");
|
||||
});
|
||||
cy.get(publish.compactMode).click();
|
||||
cy.get(publish.compactOpt)
|
||||
cy.get(publish.rowHeight).click();
|
||||
cy.get(publish.rowHeightOpt)
|
||||
.contains("Short")
|
||||
.click();
|
||||
cy.readTabledataPublish("4", "3").then((tabData) => {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ describe("Video Widget Functionality", function() {
|
|||
cy.get(".CodeMirror textarea")
|
||||
.first()
|
||||
.blur();
|
||||
cy.get(widgetsPage.autoPlay).click();
|
||||
cy.get(widgetsPage.autoPlay).click({ force: true });
|
||||
cy.wait("@updateLayout").should(
|
||||
"have.nested.property",
|
||||
"response.body.responseMeta.status",
|
||||
|
|
@ -62,7 +62,7 @@ describe("Video Widget Functionality", function() {
|
|||
cy.get(commonlocators.toastMsg).should("be.visible");
|
||||
cy.get(commonlocators.toastMsg).contains("Play success");
|
||||
*/
|
||||
cy.get(widgetsPage.autoPlay).click();
|
||||
cy.get(widgetsPage.autoPlay).click({ force: true });
|
||||
cy.wait("@updateLayout").should(
|
||||
"have.nested.property",
|
||||
"response.body.responseMeta.status",
|
||||
|
|
|
|||
|
|
@ -6,29 +6,23 @@ const dynamicInputLocators = require("../../../../locators/DynamicInput.json");
|
|||
const apiwidget = require("../../../../locators/apiWidgetslocator.json");
|
||||
|
||||
describe("Dynamic input autocomplete", () => {
|
||||
beforeEach(() => {
|
||||
before(() => {
|
||||
cy.addDsl(dsl);
|
||||
});
|
||||
it("opens autocomplete for bindings", () => {
|
||||
cy.openPropertyPane("buttonwidget");
|
||||
cy.get(dynamicInputLocators.input)
|
||||
.first()
|
||||
.focus()
|
||||
.type("{ctrl}{shift}{downarrow}")
|
||||
.then(($cm) => {
|
||||
if ($cm.val() !== "") {
|
||||
cy.get(dynamicInputLocators.input)
|
||||
.first()
|
||||
.clear({
|
||||
force: true,
|
||||
});
|
||||
}
|
||||
|
||||
.click({ force: true })
|
||||
.type("{uparrow}", { parseSpecialCharSequences: true })
|
||||
.type("{ctrl}{shift}{downarrow}", { parseSpecialCharSequences: true })
|
||||
.type("{backspace}", { parseSpecialCharSequences: true })
|
||||
.then(() => {
|
||||
cy.get(dynamicInputLocators.input)
|
||||
.first()
|
||||
.click({ force: true })
|
||||
.type("{{", {
|
||||
force: true,
|
||||
parseSpecialCharSequences: false,
|
||||
parseSpecialCharSequences: true,
|
||||
});
|
||||
|
||||
// Tests if autocomplete will open
|
||||
|
|
@ -42,9 +36,11 @@ describe("Dynamic input autocomplete", () => {
|
|||
// Tests if "No suggestions" message will pop if you type any garbage
|
||||
cy.get(dynamicInputLocators.input)
|
||||
.first()
|
||||
.type("garbage", {
|
||||
force: true,
|
||||
parseSpecialCharSequences: false,
|
||||
.click({ force: true })
|
||||
.type("{uparrow}", { parseSpecialCharSequences: true })
|
||||
.type("{ctrl}{shift}{downarrow}", { parseSpecialCharSequences: true })
|
||||
.type("{{ garbage", {
|
||||
parseSpecialCharSequences: true,
|
||||
})
|
||||
.then(() => {
|
||||
cy.get(".CodeMirror-Tern-tooltip").should(
|
||||
|
|
@ -53,21 +49,16 @@ describe("Dynamic input autocomplete", () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
cy.evaluateErrorMessage("ReferenceError: garbage is not defined");
|
||||
});
|
||||
it("opens current value popup", () => {
|
||||
// Test on widgets pane
|
||||
cy.openPropertyPane("buttonwidget");
|
||||
cy.get(dynamicInputLocators.input)
|
||||
.first()
|
||||
.focus();
|
||||
cy.assertEvaluatedValuePopup("string");
|
||||
// Test on api pane
|
||||
cy.NavigateToAPI_Panel();
|
||||
cy.get(apiwidget.createapi).click({ force: true });
|
||||
cy.wait("@createNewApi");
|
||||
cy.xpath(apiwidget.headerValue)
|
||||
.first()
|
||||
.focus();
|
||||
cy.get(apiwidget.headerValue).within(() => {
|
||||
cy.get("textarea").click({ force: true });
|
||||
});
|
||||
cy.assertEvaluatedValuePopup("string");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const datasource = require("../../../../locators/DatasourcesEditor.json");
|
|||
const apiwidget = require("../../../../locators/apiWidgetslocator.json");
|
||||
const commonlocators = require("../../../../locators/commonlocators.json");
|
||||
const explorer = require("../../../../locators/explorerlocators.json");
|
||||
const pages = require("../../../../locators/Pages.json");
|
||||
|
||||
const pageid = "MyPage";
|
||||
let datasourceName;
|
||||
|
|
@ -59,7 +60,7 @@ describe("Entity explorer tests related to query and datasource", function() {
|
|||
|
||||
/* eslint-disable */
|
||||
cy.wait(2000);
|
||||
cy.go("back");
|
||||
cy.NavigateToQueryEditor();
|
||||
|
||||
cy.contains(".t--datasource-name", datasourceName)
|
||||
.find(queryLocators.editDatasourceButton)
|
||||
|
|
@ -121,7 +122,12 @@ describe("Entity explorer tests related to query and datasource", function() {
|
|||
cy.runQuery();
|
||||
|
||||
cy.deleteQuery();
|
||||
|
||||
cy.get(commonlocators.entityExplorersearch).clear({ force: true });
|
||||
cy.wait(500);
|
||||
cy.NavigateToQueryEditor();
|
||||
cy.get(pages.integrationActiveTab)
|
||||
.should("be.visible")
|
||||
.click({ force: true });
|
||||
cy.contains(".t--datasource-name", datasourceName)
|
||||
.find(".t--edit-datasource")
|
||||
.click();
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ describe("Checkbox Widget Functionality", function() {
|
|||
cy.get(publish.checkboxWidget).click();
|
||||
cy.get(widgetsPage.formButtonWidget)
|
||||
.contains("Submit")
|
||||
.should("have.attr", "disabled");
|
||||
.should("have.class", "bp3-disabled");
|
||||
|
||||
cy.get(publish.checkboxWidget).click();
|
||||
cy.get(widgetsPage.formButtonWidget)
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ describe("Radio Widget Functionality", function() {
|
|||
cy.get(formWidgetsPage.radioOnSelectionChangeDropdown)
|
||||
.get(commonlocators.dropdownSelectButton)
|
||||
.click({ force: true })
|
||||
.type(this.data.command)
|
||||
.type("2");
|
||||
cy.PublishtheApp();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
// const explorer = require("../../../../locators/explorerlocators.json");
|
||||
const homePage = require("../../../../locators/HomePage.json");
|
||||
const commonlocators = require("../../../../locators/commonlocators.json");
|
||||
const pages = require("../../../../locators/Pages.json");
|
||||
const datasourceEditor = require("../../../../locators/DatasourcesEditor.json");
|
||||
const datasource = require("../../../../locators/DatasourcesEditor.json");
|
||||
|
||||
describe("Onboarding", function() {
|
||||
it("Onboarding flow", function() {
|
||||
|
|
@ -38,9 +41,6 @@ describe("Onboarding", function() {
|
|||
.should("be.visible")
|
||||
.click({ force: true });
|
||||
|
||||
cy.get(".t--create-query").click();
|
||||
cy.wait("@createNewApi");
|
||||
|
||||
// Create and run query
|
||||
// Using the cheat option to create the action with 30 sec timeout
|
||||
cy.get(".t--onboarding-cheat-action")
|
||||
|
|
|
|||
|
|
@ -51,14 +51,7 @@ describe("Leave organization test spec", function() {
|
|||
|
||||
cy.LogintoApp(Cypress.env("TESTUSERNAME1"), Cypress.env("TESTPASSWORD1"));
|
||||
cy.visit("/applications");
|
||||
cy.get(homePage.orgList.concat(newOrganizationName).concat(")"))
|
||||
.scrollIntoView()
|
||||
.should("be.visible")
|
||||
.within(() => {
|
||||
cy.get(homePage.orgNamePopover)
|
||||
.first()
|
||||
.click();
|
||||
});
|
||||
cy.openOrgOptionsPopup(newOrganizationName);
|
||||
cy.get(homePage.orgNamePopoverContent)
|
||||
.find("a")
|
||||
.should("have.length", 1)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@ describe("Pages", function() {
|
|||
cy.get(".bp3-icon-caret-right ~ .t--entity-name:contains(Page1)").click({
|
||||
multiple: true,
|
||||
});
|
||||
cy.get(".bp3-icon-caret-right ~ .t--entity-name:contains(APIs)").click({
|
||||
cy.get(
|
||||
".bp3-icon-caret-right ~ .t--entity-name:contains(Datasources)",
|
||||
).click({
|
||||
multiple: true,
|
||||
});
|
||||
cy.get(`.t--entity-name:contains(${apiName})`).should("have.length", 2);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
const dsl = require("../../../../fixtures/TextTabledsl.json");
|
||||
|
||||
describe("Property pane CTA to add an action", function() {
|
||||
before(() => {
|
||||
cy.addDsl(dsl);
|
||||
});
|
||||
|
||||
it("Check if CTA is shown when there is no action", function() {
|
||||
cy.openPropertyPane("tablewidget");
|
||||
|
||||
cy.get(".t--propertypane-connect-cta").should("be.visible");
|
||||
});
|
||||
|
||||
it("Check if CTA does not exist when there is an action", function() {
|
||||
cy.NavigateToAPI_Panel();
|
||||
|
||||
cy.CreateAPI("FirstAPI");
|
||||
cy.SearchEntityandOpen("Table1");
|
||||
cy.get(".t--propertypane-connect-cta").should("not.exist");
|
||||
});
|
||||
});
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
const datasource = require("../../../../locators/DatasourcesEditor.json");
|
||||
const queryLocators = require("../../../../locators/QueryEditor.json");
|
||||
const commonlocators = require("../../../../locators/commonlocators.json");
|
||||
const queryEditor = require("../../../../locators/QueryEditor.json");
|
||||
|
||||
describe("Check datasource doc links", function() {
|
||||
let postgresDatasourceName;
|
||||
|
|
@ -44,7 +45,8 @@ describe("Check datasource doc links", function() {
|
|||
});
|
||||
|
||||
it("Delete the query and datasources", function() {
|
||||
cy.get(queryLocators.deleteQuery).click();
|
||||
cy.get(queryEditor.queryMoreAction).click();
|
||||
cy.get(queryEditor.deleteUsingContext).click();
|
||||
cy.wait("@deleteAction").should(
|
||||
"have.nested.property",
|
||||
"response.body.responseMeta.status",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const datasource = require("../../../../locators/DatasourcesEditor.json");
|
||||
const queryLocators = require("../../../../locators/QueryEditor.json");
|
||||
const queryEditor = require("../../../../locators/QueryEditor.json");
|
||||
|
||||
describe("Switch datasource", function() {
|
||||
let postgresDatasourceName;
|
||||
|
|
@ -92,7 +93,8 @@ describe("Switch datasource", function() {
|
|||
});
|
||||
|
||||
it("Delete the query and datasources", function() {
|
||||
cy.get(queryLocators.deleteQuery).click();
|
||||
cy.get(queryEditor.queryMoreAction).click();
|
||||
cy.get(queryEditor.deleteUsingContext).click();
|
||||
cy.wait("@deleteAction").should(
|
||||
"have.nested.property",
|
||||
"response.body.responseMeta.status",
|
||||
|
|
|
|||
|
|
@ -23,5 +23,7 @@ describe("Widget Selection", function() {
|
|||
cy.get(`.t--widget-propertypane-toggle`)
|
||||
.not(`[style *= "background: rgb(255, 224, 210);"]`) // Excluding focused widgets.
|
||||
.should("have.length", 2);
|
||||
|
||||
cy.get(`.t--multi-selection-box`).should("have.length", 1);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
const ApiEditor = require("../../../../locators/ApiEditor.json");
|
||||
const apiwidget = require("../../../../locators/apiWidgetslocator.json");
|
||||
const pages = require("../../../../locators/Pages.json");
|
||||
|
||||
describe("Test curl import flow", function() {
|
||||
it("Test curl import flow Run and Delete", function() {
|
||||
localStorage.setItem("ApiPaneV2", "ApiPaneV2");
|
||||
cy.NavigateToApiEditor();
|
||||
cy.get(pages.integrationCreateNew)
|
||||
.should("be.visible")
|
||||
.click({ force: true });
|
||||
cy.get(ApiEditor.curlImage).click({ force: true });
|
||||
cy.get("textarea").type("curl -X GET https://mock-api.appsmith.com/users");
|
||||
cy.importCurl();
|
||||
|
|
@ -27,7 +31,5 @@ describe("Test curl import flow", function() {
|
|||
cy.get("@deleteAction").then((response) => {
|
||||
cy.expect(response.response.body.responseMeta.success).to.eq(true);
|
||||
});
|
||||
cy.get(ApiEditor.ApiHomePage).should("be.visible");
|
||||
cy.get(ApiEditor.formActionButtons).should("not.exist");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,11 +14,8 @@ describe("API Panel Test Functionality", function() {
|
|||
});
|
||||
|
||||
it("PUT Action test API fetaure", function() {
|
||||
cy.log("Login Successful");
|
||||
cy.NavigateToAPI_Panel();
|
||||
cy.log("Navigation to API Panel screen successful");
|
||||
cy.CreateAPI("FirstAPI");
|
||||
cy.log("Creation of FirstAPI Action successful");
|
||||
cy.SelectAction(testdata.putAction);
|
||||
cy.EnterSourceDetailsWithbody(
|
||||
testdata.baseUrl,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
const ApiEditor = require("../../../../locators/ApiEditor.json");
|
||||
const apiwidget = require("../../../../locators/apiWidgetslocator.json");
|
||||
const pages = require("../../../../locators/Pages.json");
|
||||
|
||||
describe("Test curl import flow", function() {
|
||||
it("Test curl import flow for POST action with JSON body", function() {
|
||||
localStorage.setItem("ApiPaneV2", "ApiPaneV2");
|
||||
cy.NavigateToApiEditor();
|
||||
cy.get(pages.integrationCreateNew)
|
||||
.should("be.visible")
|
||||
.click({ force: true });
|
||||
cy.get(ApiEditor.curlImage).click({ force: true });
|
||||
cy.get("textarea").type(
|
||||
'curl -d \'{"name":"morpheus","job":"leader"}\' -H Content-Type:application/json -X POST https://mock-api.appsmith.com/echo/post',
|
||||
|
|
@ -30,6 +34,9 @@ describe("Test curl import flow", function() {
|
|||
it("Test curl import flow for POST action with multipart form data", function() {
|
||||
localStorage.setItem("ApiPaneV2", "ApiPaneV2");
|
||||
cy.NavigateToApiEditor();
|
||||
cy.get(pages.integrationCreateNew)
|
||||
.should("be.visible")
|
||||
.click({ force: true });
|
||||
cy.get(ApiEditor.curlImage).click({ force: true });
|
||||
cy.get("textarea").type(
|
||||
"curl --request POST http://httpbin.org/post -F 'randomKey=randomValue' --form 'randomKey2=\"randomValue2\"'",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const datasource = require("../../../../locators/DatasourcesEditor.json");
|
||||
const queryEditor = require("../../../../locators/QueryEditor.json");
|
||||
const datasourceEditor = require("../../../../locators/DatasourcesEditor.json");
|
||||
|
||||
let datasourceName;
|
||||
|
||||
|
|
@ -21,14 +22,18 @@ describe("Postgres datasource test cases", function() {
|
|||
|
||||
it("Create a new query from the datasource editor", function() {
|
||||
cy.saveDatasource();
|
||||
cy.get(datasource.createQuerty).click();
|
||||
// cy.get(datasource.createQuerty).click();
|
||||
cy.get(`${datasourceEditor.datasourceCard} ${datasource.createQuerty}`)
|
||||
.last()
|
||||
.click();
|
||||
cy.wait("@createNewApi").should(
|
||||
"have.nested.property",
|
||||
"response.body.responseMeta.status",
|
||||
201,
|
||||
);
|
||||
|
||||
cy.get(queryEditor.deleteQuery).click();
|
||||
cy.get(queryEditor.queryMoreAction).click();
|
||||
cy.get(queryEditor.deleteUsingContext).click();
|
||||
cy.wait("@deleteAction").should(
|
||||
"have.nested.property",
|
||||
"response.body.responseMeta.status",
|
||||
|
|
|
|||
|
|
@ -100,7 +100,6 @@ describe("Entity explorer tests related to copy query", function() {
|
|||
cy.log("sliced id :" + updatedName);
|
||||
cy.EditEntityNameByDoubleClick(datasourceName, updatedName);
|
||||
cy.SearchEntityandOpen(updatedName);
|
||||
cy.testSaveDatasource();
|
||||
cy.hoverAndClick();
|
||||
cy.get(apiwidget.delete).click({ force: true });
|
||||
//This is check to make sure if a datasource is active 409
|
||||
|
|
|
|||
|
|
@ -63,7 +63,8 @@ describe("Entity explorer datasource structure", function() {
|
|||
201,
|
||||
);
|
||||
|
||||
cy.get(queryEditor.deleteQuery).click();
|
||||
cy.get(queryEditor.queryMoreAction).click();
|
||||
cy.get(queryEditor.deleteUsingContext).click();
|
||||
cy.wait("@deleteAction").should(
|
||||
"have.nested.property",
|
||||
"response.body.responseMeta.status",
|
||||
|
|
@ -72,7 +73,8 @@ describe("Entity explorer datasource structure", function() {
|
|||
|
||||
cy.GlobalSearchEntity("MyQuery");
|
||||
cy.get(`.t--entity-name:contains(MyQuery)`).click();
|
||||
cy.get(queryEditor.deleteQuery).click();
|
||||
cy.get(queryEditor.queryMoreAction).click();
|
||||
cy.get(queryEditor.deleteUsingContext).click();
|
||||
cy.wait("@deleteAction").should(
|
||||
"have.nested.property",
|
||||
"response.body.responseMeta.status",
|
||||
|
|
@ -151,7 +153,8 @@ describe("Entity explorer datasource structure", function() {
|
|||
200,
|
||||
);
|
||||
|
||||
cy.get(queryEditor.deleteQuery).click();
|
||||
cy.get(queryEditor.queryMoreAction).click();
|
||||
cy.get(queryEditor.deleteUsingContext).click();
|
||||
cy.wait("@deleteAction").should(
|
||||
"have.nested.property",
|
||||
"response.body.responseMeta.status",
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ describe("Confirm run action", function() {
|
|||
200,
|
||||
);
|
||||
|
||||
cy.get(queryEditor.deleteQuery).click();
|
||||
cy.get(queryEditor.queryMoreAction).click();
|
||||
cy.get(queryEditor.deleteUsingContext).click();
|
||||
cy.wait("@deleteAction").should(
|
||||
"have.nested.property",
|
||||
"response.body.responseMeta.status",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
const queryLocators = require("../../../../locators/QueryEditor.json");
|
||||
const datasource = require("../../../../locators/DatasourcesEditor.json");
|
||||
const pages = require("../../../../locators/Pages.json");
|
||||
|
||||
let datasourceName;
|
||||
|
||||
|
|
@ -39,6 +40,9 @@ describe("Create a query with a postgres datasource, run, save and then delete t
|
|||
});
|
||||
it("Create, runs and delete another query", () => {
|
||||
cy.NavigateToQueryEditor();
|
||||
cy.get(pages.integrationActiveTab)
|
||||
.should("be.visible")
|
||||
.click({ force: true });
|
||||
cy.contains(".t--datasource-name", datasourceName)
|
||||
.find(queryLocators.createQuery)
|
||||
.click();
|
||||
|
|
@ -53,6 +57,9 @@ describe("Create a query with a postgres datasource, run, save and then delete t
|
|||
});
|
||||
it("Deletes a datasource", () => {
|
||||
cy.NavigateToQueryEditor();
|
||||
cy.get(pages.integrationActiveTab)
|
||||
.should("be.visible")
|
||||
.click({ force: true });
|
||||
cy.contains(".t--datasource-name", datasourceName)
|
||||
.find(".t--edit-datasource")
|
||||
.click();
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@
|
|||
"sectionAuthentication": "[data-cy=section-Authentication]",
|
||||
"PostgresEntity": ".t--entity-name:contains(PostgreSQL)",
|
||||
"createQuerty": ".t--create-query",
|
||||
"activeDatasourceList": ".t--active-datasource-list",
|
||||
"datasourceCard": ".t--datasource",
|
||||
"editDatasource": ".t--edit-datasource",
|
||||
"defaultDatabaseName": "input[name='datasourceConfiguration.connection.defaultDatabaseName']",
|
||||
"selConnectionType": "[data-cy='datasourceConfiguration.connection.type']",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"input": ".CodeMirror textarea",
|
||||
"input": ".CodeEditorTarget",
|
||||
"hints": "ul.CodeMirror-hints",
|
||||
"evaluatedValue": ".t--CodeEditor-evaluatedValue",
|
||||
"bindingPrompt": ".t--no-binding-prompt"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@
|
|||
"Menuaction": ".bp3-overlay-open>.bp3-transition-container",
|
||||
"Delete": ":nth-child(2) > .bp3-menu-item",
|
||||
"apiEditorIcon": ".t--nav-link-api-editor",
|
||||
"addEntityAPI": ".apis .t--entity-add-btn",
|
||||
"addEntityAPI": ".datasources .t--entity-add-btn",
|
||||
"integrationCreateNew": "[data-cy=t--tab-CREATE_NEW]",
|
||||
"integrationActiveTab": "[data-cy=t--tab-ACTIVE]",
|
||||
"entityWidget": ".t--entity-name:contains('Widgets')",
|
||||
"entityTable": ".t--entity-name:contains('Table1')",
|
||||
"entityText": ".t--entity-name:contains('Text1')",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
"runQuery": ".t--run-query",
|
||||
"saveQuery": ".t--save-query",
|
||||
"deleteQuery": ".t--delete-query",
|
||||
"queryMoreAction": ".t--more-action-menu",
|
||||
"deleteUsingContext": ".t--apiFormDeleteBtn",
|
||||
"createQuery": ".t--create-query",
|
||||
"addQueryEntity": ".//div[contains(@class,'t--entity group queries')]//div[contains(@class,'t--entity-add-btn')]",
|
||||
"addDatasource": ".t--add-datasource",
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
"createMarker": ".t--property-control-createnewmarker [type='checkbox']",
|
||||
"zoomLevel": ".t--property-control-zoomlevel svg",
|
||||
"sourceImage": ".t--property-control-image .CodeMirror-code",
|
||||
"defaultImage": ".t--property-control-defaultimage .CodeMirror-code",
|
||||
"defaultImage": ".t--property-control-defaultimage .CodeMirror textarea",
|
||||
"Chartlabel": ".t--draggable-chartwidget g:nth-child(5) text",
|
||||
"PieChartLabel": ".t--draggable-chartwidget g g:nth-child(1) g text",
|
||||
"pickMyLocation": ".t--draggable-mapwidget div[title='Pick My Location']"
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@
|
|||
"buttonWidget": ".t--draggable-buttonwidget",
|
||||
"buttonStyleDropdown": ".t--property-control-buttonstyle [name='downArrow']",
|
||||
"buttonBackground": ".sc-ecQjpJ > div > .bp3-button",
|
||||
"copyWidget": ":nth-child(2) > .bp3-popover-target > .sc-bdnylx > svg",
|
||||
"removeWidget": ":nth-child(3) > .bp3-popover-target > .sc-bdnylx > svg",
|
||||
"copyWidget": ".t--copy-widget svg",
|
||||
"removeWidget": ".t--delete-widget svg",
|
||||
"propertypaneText": ".t--propertypane .bp3-panel-stack-view",
|
||||
"formButtonWidget": ".t--widget-formbuttonwidget",
|
||||
"textWidget": ".t--draggable-textwidget",
|
||||
|
|
@ -75,7 +75,7 @@
|
|||
"verticalBottom": ".t--icon-tab-BOTTOM",
|
||||
"textColor": ".t--property-control-textcolor input",
|
||||
"backgroundcolorPicker": ".t--property-control-backgroundcolor input",
|
||||
"greenColor": "//div[@color='rgb(3, 179, 101)']",
|
||||
"greenColor": "//div[@color='#03b365']",
|
||||
"toggleJsColor": ".t--property-control-textcolor .t--js-toggle",
|
||||
"backgroundColor": ".t--property-control-cellbackground input",
|
||||
"toggleJsBcgColor": ".t--property-control-cellbackground .t--js-toggle",
|
||||
|
|
@ -105,5 +105,6 @@
|
|||
"switchWidgetActive": ".t--switch-widget-active",
|
||||
"switchWidgetInactive": ".t--switch-widget-inactive",
|
||||
"switchWidgetLoading": ".t--switch-widget-loading",
|
||||
"chartPlotGroup": "g.raphael-group-63-plot-group"
|
||||
"chartPlotGroup": "g.raphael-group-63-plot-group",
|
||||
"toggleEnableMultirowselection": ".t--property-control-enablemultirowselection .bp3-control-indicator"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,11 @@
|
|||
"path": ".t--path >div textarea",
|
||||
"editResourceUrl": ".t--dataSourceField",
|
||||
"autoSuggest": "//div[contains(@id,'react-select')]",
|
||||
"headerKey": "(//div[contains(@class,'t--actionConfiguration.headers[0].key.0')]//textarea)[2]",
|
||||
"headerValue": "(//div[contains(@class,'t--actionConfiguration.headers[0].value.0')]//textarea)[2]",
|
||||
"postDefaultContentTypeHeaderKey": "(//div[contains(@class,'t--actionConfiguration.headers[2].key.2')]//textarea)[2]",
|
||||
"queryKey": "(//div[contains(@class,'t--actionConfiguration.queryParameters[0].key.0')]//textarea)[2]",
|
||||
"queryValue": "(//div[contains(@class,'t--actionConfiguration.queryParameters[0].value.0')]//textarea)[2]",
|
||||
"formEncoded": "(//div[contains(@class,'t--actionConfiguration.bodyFormData[0].key.0')]//textarea)[2]",
|
||||
"headerKey": ".t--actionConfiguration\\.headers\\[0\\]\\.key\\.0",
|
||||
"headerValue": ".t--actionConfiguration\\.headers\\[0\\]\\.value\\.0",
|
||||
"queryKey": ".t--actionConfiguration\\.queryParameters\\[0\\]\\.key\\.0",
|
||||
"queryValue": ".t--actionConfiguration\\.queryParameters\\[0\\]\\.value\\.0",
|
||||
"formEncoded": ".t--actionConfiguration\\.bodyFormData\\[0\\]\\.key\\.0",
|
||||
"responseStatus": "//div[@id='root']",
|
||||
"responseText": ".CodeMirror-line > [role='presentation']",
|
||||
"createApiOnSideBar": "button:contains('Create new API')",
|
||||
|
|
@ -54,6 +53,6 @@
|
|||
"onPageLoad": "[data-cy=executeOnLoad]",
|
||||
"renameEntity": ".single-select >div:contains('Edit Name')",
|
||||
"paramsTab": "//li//span[text()='Params']",
|
||||
"paramKey": "(//div[contains(@class,'t--actionConfiguration.queryParameters[0].key.0')]//textarea)[1]",
|
||||
"paramValue": "(//div[contains(@class,'t--actionConfiguration.queryParameters[0].value.0')]//textarea)[1]"
|
||||
"paramKey": ".t--actionConfiguration\\.queryParameters\\[0\\]\\.key\\.0",
|
||||
"paramValue": ".t--actionConfiguration\\.queryParameters\\[0\\]\\.value\\.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@
|
|||
"optionchangetextDropdown": " .t--property-control-onoptionchange .CodeMirror-code",
|
||||
"optionchangetextDatePicker": " .t--property-control-ondateselected .CodeMirror-code",
|
||||
"optionchangetextCheckbox": ".t--property-control-oncheckchange .CodeMirror-code",
|
||||
"optionchangetextSwitch": ".t--property-control-onchange .CodeMirror-code",
|
||||
"optionchangetextSwitch": ".t--property-control-onchange .CodeMirror textarea",
|
||||
"optionchangetextInput": ".t--property-control-ontextchanged .CodeMirror-code",
|
||||
"optionchangeRadioselect": ".t--property-control-onselectionchange .CodeMirror-code",
|
||||
"optionchangeRadioselect": ".t--property-control-onselectionchange .CodeMirror textarea",
|
||||
"optionalignment": ".t--property-control-alignment .bp3-popover-target",
|
||||
"onMarkerclick": ".t--property-control-onmarkerclick .CodeMirror-code",
|
||||
"success": "div[type='success'] span",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"NoQueryMsg": "p:contains('No DB Queries yet.')",
|
||||
"NoWidgetsMsg": "p:contains('No widgets yet.')",
|
||||
"AddPage": ".pages .t--entity-add-btn",
|
||||
"addEntityAPI": ".apis .t--entity-add-btn",
|
||||
"addEntityAPI": ".datasources .t--entity-add-btn",
|
||||
"entityWidget": ".t--entity-name:contains('Widgets')",
|
||||
"entityTable": ".t--entity-name:contains('Table1')",
|
||||
"entityText": ".t--entity-name:contains('Text1')",
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
"editName": ".single-select >div:contains('Edit Name')",
|
||||
"deletePage": ".single-select >div:contains('Delete')",
|
||||
"refreshStructure": ".single-select >div:contains('Refresh')",
|
||||
"entityQuery": ".t--entity-name:contains('Queries')",
|
||||
"entityQuery": ".t--entity-name:contains('Datasources')",
|
||||
"datasourceStructure": ".t--entity.datasourceStructure",
|
||||
"datasourceColumn": ".t--datasource-column",
|
||||
"templateMenuIcon": ".t--template-menu-trigger",
|
||||
|
|
@ -25,6 +25,6 @@
|
|||
"addWidget":".widgets .t--entity-add-btn",
|
||||
"dropHere":".appsmith_widget_0",
|
||||
"closeWidgets":".t--close-widgets-sidebar",
|
||||
"addDBQueryEntity": ".dbqueries .t--entity-add-btn",
|
||||
"addDBQueryEntity": ".datasources .t--entity-add-btn",
|
||||
"editEntity": ".t--entity-name input"
|
||||
}
|
||||
|
|
@ -31,8 +31,8 @@
|
|||
"inputValue": ".t--table-filter-value-input",
|
||||
"canvas": ".canvas",
|
||||
"removeFilter": ".t--table-filter-remove-btn",
|
||||
"compactMode": ".t--table-compact-mode-toggle-btn",
|
||||
"compactOpt": ".t--table-compact-mode-option",
|
||||
"rowHeight": ".t--table-compact-mode-toggle-btn",
|
||||
"rowHeightOpt": ".t--table-compact-mode-option",
|
||||
"visibilityMode": ".t--table-column-visibility-toggle-btn",
|
||||
"visibilityOpt": ".option-title",
|
||||
"containerWidget": ".t--widget-containerwidget",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@ const viewWidgetsPage = require("../locators/ViewWidgets.json");
|
|||
|
||||
let pageidcopy = " ";
|
||||
|
||||
export const initLocalstorage = () => {
|
||||
cy.window().then((window) => {
|
||||
window.localStorage.setItem("ShowCommentsButtonToolTip", "");
|
||||
});
|
||||
};
|
||||
|
||||
Cypress.Commands.add("createOrg", () => {
|
||||
cy.get(homePage.createOrg)
|
||||
.should("be.visible")
|
||||
|
|
@ -373,6 +379,8 @@ Cypress.Commands.add("LogintoApp", (uname, pword) => {
|
|||
"response.body.responseMeta.status",
|
||||
200,
|
||||
);
|
||||
|
||||
initLocalstorage();
|
||||
});
|
||||
|
||||
Cypress.Commands.add("LoginFromAPI", (uname, pword) => {
|
||||
|
|
@ -509,6 +517,9 @@ Cypress.Commands.add("NavigateToAPI_Panel", () => {
|
|||
cy.get(pages.addEntityAPI)
|
||||
.should("be.visible")
|
||||
.click({ force: true });
|
||||
cy.get(pages.integrationCreateNew)
|
||||
.should("be.visible")
|
||||
.click({ force: true });
|
||||
cy.get("#loading").should("not.exist");
|
||||
});
|
||||
|
||||
|
|
@ -742,16 +753,14 @@ Cypress.Commands.add(
|
|||
"EnterSourceDetailsWithHeader",
|
||||
(baseUrl, v1method, hKey, hValue) => {
|
||||
cy.enterDatasourceAndPath(baseUrl, v1method);
|
||||
cy.xpath(apiwidget.headerKey)
|
||||
cy.get(apiwidget.headerKey)
|
||||
.first()
|
||||
.click({ force: true })
|
||||
.type(hKey, { force: true })
|
||||
.should("have.value", hKey);
|
||||
cy.xpath(apiwidget.headerValue)
|
||||
.type(hKey, { parseSpecialCharSequences: true });
|
||||
cy.get(apiwidget.headerValue)
|
||||
.first()
|
||||
.click({ force: true })
|
||||
.type(hValue, { force: true })
|
||||
.should("have.value", hValue);
|
||||
.type(hValue, { parseSpecialCharSequences: true });
|
||||
cy.WaitAutoSave();
|
||||
},
|
||||
);
|
||||
|
|
@ -844,22 +853,20 @@ Cypress.Commands.add(
|
|||
"EnterSourceDetailsWithQueryParam",
|
||||
(baseUrl, v1method, hKey, hValue, qKey, qValue) => {
|
||||
cy.enterDatasourceAndPath(baseUrl, v1method);
|
||||
cy.xpath(apiwidget.headerKey)
|
||||
cy.get(apiwidget.headerKey)
|
||||
.first()
|
||||
.click({ force: true })
|
||||
.type(hKey, { force: true })
|
||||
.should("have.value", hKey);
|
||||
cy.xpath(apiwidget.headerValue)
|
||||
.type(hKey, { parseSpecialCharSequences: true });
|
||||
cy.get(apiwidget.headerValue)
|
||||
.first()
|
||||
.click({ force: true })
|
||||
.type(hValue, { force: true })
|
||||
.should("have.value", hValue);
|
||||
cy.xpath(apiwidget.queryKey)
|
||||
.type(hValue, { parseSpecialCharSequences: true });
|
||||
cy.get(apiwidget.queryKey)
|
||||
.first()
|
||||
.click({ force: true })
|
||||
.type(qKey, { force: true })
|
||||
.should("have.value", qKey);
|
||||
cy.xpath(apiwidget.queryValue)
|
||||
cy.get(apiwidget.queryValue)
|
||||
.first()
|
||||
.click({ force: true })
|
||||
.type(qValue, { force: true })
|
||||
|
|
@ -880,6 +887,9 @@ Cypress.Commands.add(
|
|||
|
||||
Cypress.Commands.add("CreationOfUniqueAPIcheck", (apiname) => {
|
||||
cy.get(pages.addEntityAPI).click();
|
||||
cy.get(pages.integrationCreateNew)
|
||||
.should("be.visible")
|
||||
.click({ force: true });
|
||||
cy.get(apiwidget.createapi).click({ force: true });
|
||||
cy.wait("@createNewApi");
|
||||
// cy.wait("@getUser");
|
||||
|
|
@ -1722,9 +1732,7 @@ Cypress.Commands.add("getAlert", (alertcss) => {
|
|||
|
||||
cy.get(alertcss)
|
||||
.click({ force: true })
|
||||
.type("{command}{A}{del}")
|
||||
.type("hello")
|
||||
.should("not.to.be.empty");
|
||||
.type("hello");
|
||||
cy.get(".t--open-dropdown-Select-type").click({ force: true });
|
||||
cy.get(".bp3-popover-content .bp3-menu li")
|
||||
.contains("Success")
|
||||
|
|
@ -1754,9 +1762,7 @@ Cypress.Commands.add(
|
|||
|
||||
cy.get(alertcss)
|
||||
.click({ force: true })
|
||||
.type("{command}{A}{del}")
|
||||
.type("hello")
|
||||
.should("not.to.be.empty");
|
||||
.type("hello");
|
||||
cy.get(".t--open-dropdown-Select-type").click({ force: true });
|
||||
cy.get(".bp3-popover-content .bp3-menu li")
|
||||
.contains("Success")
|
||||
|
|
@ -1768,7 +1774,7 @@ Cypress.Commands.add("addQueryFromLightningMenu", (QueryName) => {
|
|||
cy.get(commonlocators.dropdownSelectButton)
|
||||
.first()
|
||||
.click({ force: true })
|
||||
.selectOnClickOption("Execute a DB Query")
|
||||
.selectOnClickOption("Execute a Query")
|
||||
.selectOnClickOption(QueryName);
|
||||
});
|
||||
|
||||
|
|
@ -1776,7 +1782,7 @@ Cypress.Commands.add("addAPIFromLightningMenu", (ApiName) => {
|
|||
cy.get(commonlocators.dropdownSelectButton)
|
||||
.first()
|
||||
.click({ force: true })
|
||||
.selectOnClickOption("Call An API")
|
||||
.selectOnClickOption("Execute a Query")
|
||||
.selectOnClickOption(ApiName);
|
||||
});
|
||||
|
||||
|
|
@ -1849,7 +1855,11 @@ Cypress.Commands.add("testSaveDeleteDatasource", () => {
|
|||
200,
|
||||
);
|
||||
|
||||
cy.get(datasourceEditor.editDatasource).click();
|
||||
cy.get(
|
||||
`${datasourceEditor.datasourceCard} ${datasourceEditor.editDatasource}`,
|
||||
)
|
||||
.last()
|
||||
.click();
|
||||
|
||||
cy.get(".t--delete-datasource").click();
|
||||
cy.wait("@deleteDatasource").should(
|
||||
|
|
@ -1872,7 +1882,9 @@ Cypress.Commands.add("NavigateToDatasourceEditor", () => {
|
|||
cy.get(explorer.addDBQueryEntity)
|
||||
.last()
|
||||
.click({ force: true });
|
||||
cy.get(queryEditor.addDatasource).click();
|
||||
cy.get(pages.integrationCreateNew)
|
||||
.should("be.visible")
|
||||
.click({ force: true });
|
||||
});
|
||||
|
||||
Cypress.Commands.add("NavigateToQueryEditor", () => {
|
||||
|
|
@ -1902,7 +1914,11 @@ Cypress.Commands.add("saveDatasource", () => {
|
|||
|
||||
Cypress.Commands.add("testSaveDatasource", () => {
|
||||
cy.saveDatasource();
|
||||
cy.get(datasourceEditor.editDatasource).click();
|
||||
cy.get(
|
||||
`${datasourceEditor.datasourceCard} ${datasourceEditor.editDatasource}`,
|
||||
)
|
||||
.last()
|
||||
.click();
|
||||
cy.testDatasource();
|
||||
});
|
||||
|
||||
|
|
@ -1960,7 +1976,9 @@ Cypress.Commands.add("createPostgresDatasource", () => {
|
|||
|
||||
Cypress.Commands.add("deleteDatasource", (datasourceName) => {
|
||||
cy.NavigateToQueryEditor();
|
||||
|
||||
cy.get(pages.integrationActiveTab)
|
||||
.should("be.visible")
|
||||
.click({ force: true });
|
||||
cy.contains(".t--datasource-name", datasourceName)
|
||||
.find(".t--edit-datasource")
|
||||
.click();
|
||||
|
|
@ -2020,7 +2038,8 @@ Cypress.Commands.add("runAndDeleteQuery", () => {
|
|||
200,
|
||||
);
|
||||
|
||||
cy.get(queryEditor.deleteQuery).click();
|
||||
cy.get(queryEditor.queryMoreAction).click();
|
||||
cy.get(queryEditor.deleteUsingContext).click();
|
||||
cy.wait("@deleteAction").should(
|
||||
"have.nested.property",
|
||||
"response.body.responseMeta.status",
|
||||
|
|
@ -2044,7 +2063,7 @@ Cypress.Commands.add("executeDbQuery", (queryName) => {
|
|||
.click({ force: true })
|
||||
.get("ul.bp3-menu")
|
||||
.children()
|
||||
.contains("Execute a DB Query")
|
||||
.contains("Execute a Query")
|
||||
.click({ force: true })
|
||||
.get("ul.bp3-menu")
|
||||
.children()
|
||||
|
|
@ -2168,9 +2187,12 @@ Cypress.Commands.add("copyWidget", (widget, widgetLocator) => {
|
|||
.children()
|
||||
.last()
|
||||
.invoke("text")
|
||||
.then((originalWidget) => {
|
||||
.then((x) => {
|
||||
cy.log(x);
|
||||
let originalWidget = x.replaceAll("x", "");
|
||||
originalWidget = originalWidget.replaceAll(/\u200B/g, "");
|
||||
cy.log(originalWidget);
|
||||
cy.get(widgetsPage.copyWidget).click();
|
||||
cy.get(widgetsPage.copyWidget).click({ force: true });
|
||||
cy.reload();
|
||||
// Wait for the widget to be appear in the DOM and press Ctrl/Cmd + V to paste the button.
|
||||
cy.get(widgetLocator).should("be.visible");
|
||||
|
|
@ -2181,7 +2203,10 @@ Cypress.Commands.add("copyWidget", (widget, widgetLocator) => {
|
|||
.children()
|
||||
.last()
|
||||
.invoke("text")
|
||||
.then((copiedWidget) => {
|
||||
.then((y) => {
|
||||
cy.log(y);
|
||||
let copiedWidget = y.replaceAll("x", "");
|
||||
copiedWidget = copiedWidget.replaceAll(/\u200B/g, "");
|
||||
cy.log(copiedWidget);
|
||||
expect(originalWidget).to.be.equal(copiedWidget);
|
||||
});
|
||||
|
|
@ -2190,7 +2215,7 @@ Cypress.Commands.add("copyWidget", (widget, widgetLocator) => {
|
|||
|
||||
Cypress.Commands.add("deleteWidget", (widget) => {
|
||||
// Delete the button widget
|
||||
cy.get(widgetsPage.removeWidget).click();
|
||||
cy.get(widgetsPage.removeWidget).click({ force: true });
|
||||
cy.get(widgetsPage.deleteToast).should("have.text", "UNDO");
|
||||
});
|
||||
|
||||
|
|
@ -2212,6 +2237,9 @@ Cypress.Commands.add("UpdateChartType", (typeOfChart) => {
|
|||
|
||||
Cypress.Commands.add("createAndFillApi", (url, parameters) => {
|
||||
cy.NavigateToApiEditor();
|
||||
cy.get(pages.integrationCreateNew)
|
||||
.should("be.visible")
|
||||
.click({ force: true });
|
||||
cy.testCreateApiButton();
|
||||
cy.get("@createNewApi").then((response) => {
|
||||
cy.get(ApiEditor.ApiNameField).should("be.visible");
|
||||
|
|
@ -2495,13 +2523,13 @@ Cypress.Commands.add("ValidatePaginationInputData", () => {
|
|||
Cypress.Commands.add("callApi", (apiname) => {
|
||||
cy.get(commonlocators.callApi)
|
||||
.first()
|
||||
.click();
|
||||
.click({ force: true });
|
||||
cy.get(commonlocators.singleSelectMenuItem)
|
||||
.contains("Call An API")
|
||||
.click();
|
||||
.contains("Execute a Query")
|
||||
.click({ force: true });
|
||||
cy.get(commonlocators.selectMenuItem)
|
||||
.contains(apiname)
|
||||
.click();
|
||||
.click({ force: true });
|
||||
});
|
||||
|
||||
Cypress.Commands.add("assertPageSave", () => {
|
||||
|
|
@ -2512,10 +2540,16 @@ Cypress.Commands.add("ValidateQueryParams", (param) => {
|
|||
cy.xpath(apiwidget.paramsTab)
|
||||
.should("be.visible")
|
||||
.click({ force: true });
|
||||
cy.xpath(apiwidget.paramKey)
|
||||
.first()
|
||||
.contains(param.key);
|
||||
cy.xpath(apiwidget.paramValue)
|
||||
.first()
|
||||
.contains(param.value);
|
||||
|
||||
cy.validateCodeEditorContent(apiwidget.paramKey, param.key);
|
||||
cy.validateCodeEditorContent(apiwidget.paramValue, param.value);
|
||||
});
|
||||
|
||||
Cypress.Commands.add(
|
||||
"validateCodeEditorContent",
|
||||
(selector, contentToValidate) => {
|
||||
cy.get(selector).within(() => {
|
||||
cy.get(".CodeMirror-code").should("have.text", contentToValidate);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ let appId;
|
|||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import "./commands";
|
||||
import { initLocalstorage } from "./commands";
|
||||
|
||||
Cypress.on("uncaught:exception", (err, runnable) => {
|
||||
// returning false here prevents Cypress from
|
||||
|
|
@ -31,11 +32,13 @@ Cypress.on("fail", (error, runnable) => {
|
|||
});
|
||||
|
||||
before(function() {
|
||||
initLocalstorage();
|
||||
cy.startServerAndRoutes();
|
||||
// Clear indexedDB
|
||||
cy.window().then((window) => {
|
||||
window.indexedDB.deleteDatabase("Appsmith");
|
||||
});
|
||||
|
||||
const username = Cypress.env("USERNAME");
|
||||
const password = Cypress.env("PASSWORD");
|
||||
cy.LoginFromAPI(username, password);
|
||||
|
|
@ -58,6 +61,7 @@ before(function() {
|
|||
});
|
||||
|
||||
beforeEach(function() {
|
||||
initLocalstorage();
|
||||
Cypress.Cookies.preserveOnce("SESSION", "remember_token");
|
||||
cy.startServerAndRoutes();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ if [ "$target" == "ci" ]; then
|
|||
# On the CI server run the tests in parallel
|
||||
# This requires the projectId and the record_key to be configured in your environment variables. By default this is defined on the CI server
|
||||
echo "Got the Build ID: $BUILD_ID"
|
||||
$(npm bin)/cypress run --headless --browser chrome \
|
||||
$(npm bin)/cypress run --headless \
|
||||
--record --key "$CYPRESS_RECORD_KEY" --ci-build-id $BUILD_ID \
|
||||
--parallel --group "Electrons on Gitlab CI" \
|
||||
--spec "cypress/integration/Smoke_TestSuite/**/*.js"
|
||||
else
|
||||
$(npm bin)/cypress run --headless --browser chrome --spec "cypress/integration/Smoke_TestSuite/**/*.js"
|
||||
$(npm bin)/cypress run --headless --browser chromium --spec "cypress/integration/Smoke_TestSuite/**/*.js"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@
|
|||
"react-mentions": "^4.1.1",
|
||||
"react-paginating": "^1.4.0",
|
||||
"react-player": "^2.3.1",
|
||||
"react-rating": "^2.0.5",
|
||||
"react-redux": "^7.1.3",
|
||||
"react-router": "^5.1.2",
|
||||
"react-router-dom": "^5.1.2",
|
||||
|
|
@ -155,7 +156,7 @@
|
|||
},
|
||||
"scripts": {
|
||||
"analyze": "source-map-explorer 'build/static/js/*.js'",
|
||||
"start": "BROWSER=none EXTEND_ESLINT=true REACT_APP_ENVIRONMENT=DEVELOPMENT HOST=dev.appsmith.com craco start",
|
||||
"start": "BROWSER=none EXTEND_ESLINT=true REACT_APP_ENVIRONMENT=DEVELOPMENT REACT_APP_CLIENT_LOG_LEVEL=debug HOST=dev.appsmith.com craco start",
|
||||
"build": "./build.sh",
|
||||
"build-local": "craco --max-old-space-size=4096 build --config craco.build.config.js",
|
||||
"build-staging": "REACT_APP_ENVIRONMENT=STAGING craco --max-old-space-size=4096 build --config craco.build.config.js",
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@
|
|||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="loader" style="width: 30vw;"></div>
|
||||
<!--
|
||||
To keep zIndex for tooltips higher than app comments, todo remove when migrating to Tooltip2
|
||||
To keep zIndex for tooltips higher than app comments, todo remove when migrating to Tooltip2
|
||||
Currently the className does not apply to the portal root, so we're unable to work with z-indexes based on that
|
||||
-->
|
||||
<div id="tooltip-root"></div>
|
||||
<div id="tooltip-root"></div>
|
||||
<div id="header-root"></div>
|
||||
<div id="root"></div>
|
||||
<script type="text/javascript">
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
return window.localStorage.setItem(key, value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Note: The following handler is necessary for when we have a new deployment.
|
||||
|
||||
// What happens?
|
||||
|
|
@ -103,7 +103,7 @@
|
|||
const regex = /\/static\/js\/(\w.+).js$/;
|
||||
const chunkLoadErrorMessage = "Unexpected token '<'"
|
||||
if(regex.test(url) && url.indexOf(window.location.host) > -1 && message.includes(chunkLoadErrorMessage)) {
|
||||
console.log("chunk load failed!", url);
|
||||
console.error("chunk load failed!", url);
|
||||
// First check if we've set the flag to not reload on chunk load fail
|
||||
const donotReload = localStorageUtil.getItem("donotReloadOnChunkLoadFail");
|
||||
if(!donotReload){
|
||||
|
|
@ -131,7 +131,7 @@
|
|||
if ("serviceWorker" in navigator) {
|
||||
window.addEventListener("load", function () {
|
||||
navigator.serviceWorker.register("/pageService.js").catch(error => {
|
||||
console.log("Service Worker Registration failed: " + error);
|
||||
console.error("Service Worker Registration failed: " + error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,11 @@ export const updateActionSuccess = (payload: { data: Action }) => {
|
|||
};
|
||||
};
|
||||
|
||||
export const deleteAction = (payload: { id: string; name: string }) => {
|
||||
export const deleteAction = (payload: {
|
||||
id: string;
|
||||
name: string;
|
||||
onSuccess?: () => void;
|
||||
}) => {
|
||||
return {
|
||||
type: ReduxActionTypes.DELETE_ACTION_INIT,
|
||||
payload,
|
||||
|
|
|
|||
|
|
@ -70,3 +70,11 @@ export const updateBodyContentType = (
|
|||
type: ReduxActionTypes.UPDATE_API_ACTION_BODY_CONTENT_TYPE,
|
||||
payload: { title, apiId },
|
||||
});
|
||||
|
||||
export const redirectToNewIntegrations = (
|
||||
applicationId: string,
|
||||
pageId: string,
|
||||
): ReduxAction<{ applicationId: string; pageId: string }> => ({
|
||||
type: ReduxActionTypes.REDIRECT_TO_NEW_INTEGRATIONS,
|
||||
payload: { applicationId, pageId },
|
||||
});
|
||||
|
|
|
|||
|
|
@ -248,11 +248,15 @@ export const setAreCommentsEnabled = (flag: boolean) => ({
|
|||
payload: flag,
|
||||
});
|
||||
|
||||
export const fetchUnreadCommentThreadsCountRequest = () => ({
|
||||
type: ReduxActionTypes.FETCH_UNREAD_COMMENT_THREADS_COUNT_REQUEST,
|
||||
export const fetchUnreadCommentThreadsCountSuccess = (count?: number) => ({
|
||||
type: ReduxActionTypes.FETCH_UNREAD_COMMENT_THREADS_COUNT_SUCCESS,
|
||||
payload: count,
|
||||
});
|
||||
|
||||
export const fetchUnreadCommentThreadsCountSuccess = (flag: boolean) => ({
|
||||
type: ReduxActionTypes.FETCH_UNREAD_COMMENT_THREADS_COUNT_SUCCESS,
|
||||
payload: flag,
|
||||
export const incrementThreadUnreadCount = () => ({
|
||||
type: ReduxActionTypes.INCREMENT_COMMENT_THREAD_UNREAD_COUNT,
|
||||
});
|
||||
|
||||
export const decrementThreadUnreadCount = () => ({
|
||||
type: ReduxActionTypes.DECREMENT_COMMENT_THREAD_UNREAD_COUNT,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -137,6 +137,29 @@ export const fetchDatasources = () => {
|
|||
};
|
||||
};
|
||||
|
||||
export const fetchMockDatasources = () => {
|
||||
return {
|
||||
type: ReduxActionTypes.FETCH_MOCK_DATASOURCES_INIT,
|
||||
};
|
||||
};
|
||||
|
||||
export const addMockDatasourceToOrg = (
|
||||
name: string,
|
||||
organizationId: string,
|
||||
pluginId: string,
|
||||
packageName: string,
|
||||
): ReduxAction<{
|
||||
name: string;
|
||||
organizationId: string;
|
||||
pluginId: string;
|
||||
packageName: string;
|
||||
}> => {
|
||||
return {
|
||||
type: ReduxActionTypes.ADD_MOCK_DATASOURCES_INIT,
|
||||
payload: { name, packageName, pluginId, organizationId },
|
||||
};
|
||||
};
|
||||
|
||||
export const initDatasourcePane = (
|
||||
pluginType: string,
|
||||
urlId?: string,
|
||||
|
|
|
|||
|
|
@ -83,6 +83,11 @@ export const initCanvasLayout = (
|
|||
};
|
||||
};
|
||||
|
||||
export const setLastUpdatedTime = (payload: number): ReduxAction<number> => ({
|
||||
type: ReduxActionTypes.SET_LAST_UPDATED_TIME,
|
||||
payload,
|
||||
});
|
||||
|
||||
export const savePageSuccess = (payload: SavePageResponse) => {
|
||||
return {
|
||||
type: ReduxActionTypes.SAVE_PAGE_SUCCESS,
|
||||
|
|
|
|||
|
|
@ -31,11 +31,12 @@ export interface ApplicationResponsePayload {
|
|||
pages?: ApplicationPagePayload[];
|
||||
appIsExample: boolean;
|
||||
appLayout?: AppLayoutConfig;
|
||||
unreadCommentThreads?: number;
|
||||
}
|
||||
|
||||
// export interface FetchApplicationResponse extends ApiResponse {
|
||||
// data: ApplicationResponsePayload & { pages: ApplicationPagePayload[] };
|
||||
// }
|
||||
export interface FetchApplicationResponse extends ApiResponse {
|
||||
data: ApplicationResponsePayload & { pages: ApplicationPagePayload[] };
|
||||
}
|
||||
|
||||
export interface FetchApplicationsResponse extends ApiResponse {
|
||||
data: Array<ApplicationResponsePayload & { pages: ApplicationPagePayload[] }>;
|
||||
|
|
@ -154,13 +155,13 @@ class ApplicationApi extends Api {
|
|||
|
||||
static fetchApplication(
|
||||
applicationId: string,
|
||||
): AxiosPromise<FetchApplicationsResponse> {
|
||||
): AxiosPromise<FetchApplicationResponse> {
|
||||
return Api.get(ApplicationApi.baseURL + applicationId);
|
||||
}
|
||||
|
||||
static fetchApplicationForViewMode(
|
||||
applicationId: string,
|
||||
): AxiosPromise<FetchApplicationsResponse> {
|
||||
): AxiosPromise<FetchApplicationResponse> {
|
||||
return Api.get(ApplicationApi.baseURL + `view/${applicationId}`);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,11 +77,6 @@ class CommentsApi extends Api {
|
|||
data: request,
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
static fetchUnreadCommentThreads(applicationId: string) {
|
||||
return Promise.resolve({ data: { count: 10 } });
|
||||
}
|
||||
}
|
||||
|
||||
export default CommentsApi;
|
||||
|
|
|
|||
|
|
@ -63,6 +63,26 @@ class DatasourcesApi extends API {
|
|||
DatasourcesApi.url + `/${id}/structure?ignoreCache=${ignoreCache}`,
|
||||
);
|
||||
}
|
||||
|
||||
static fetchMockDatasources(): AxiosPromise<
|
||||
GenericApiResponse<Datasource[]>
|
||||
> {
|
||||
return API.get(DatasourcesApi.url + "/mocks");
|
||||
}
|
||||
|
||||
static addMockDbToDatasources(
|
||||
name: string,
|
||||
organizationId: string,
|
||||
pluginId: string,
|
||||
packageName: string,
|
||||
): Promise<any> {
|
||||
return API.post(DatasourcesApi.url + `/mocks`, {
|
||||
name,
|
||||
organizationId,
|
||||
pluginId,
|
||||
packageName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default DatasourcesApi;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="8" height="6" viewBox="0 0 8 6" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.92006 1.18808L7.51929 0.805547C7.46589 0.754465 7.40437 0.729004 7.33482 0.729004C7.26545 0.729004 7.20396 0.754465 7.15055 0.805547L4.00004 3.81269L0.849671 0.805628C0.796241 0.754545 0.734752 0.729084 0.665289 0.729084C0.595798 0.729084 0.534309 0.754545 0.480906 0.805628L0.0801884 1.18818C0.0266733 1.23916 1.18406e-09 1.29785 1.32213e-09 1.36419C1.46009e-09 1.43046 0.0267575 1.48916 0.0801884 1.54013L3.81566 5.10589C3.86906 5.15689 3.93058 5.18238 4.00004 5.18238C4.06951 5.18238 4.13091 5.15689 4.18428 5.10589L7.92006 1.54013C7.97347 1.48913 8 1.43044 8 1.36419C8 1.29785 7.97347 1.23916 7.92006 1.18808Z" fill="#090707"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 743 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="14" height="9" viewBox="0 0 14 9" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.1603 0.969728L9.53673 1.56501L12.3118 4.21393L-5.87492e-07 4.21393L-7.39946e-07 5.05579L12.3118 5.0558L9.53673 7.70468L10.1603 8.29997L14 4.63483L10.1603 0.969728Z" fill="#090707"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 296 B |
|
Before Width: | Height: | Size: 730 B After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" >
|
||||
<path d="M13.3327 7.33341H5.21935L8.94601 3.60675L7.99935 2.66675L2.66602 8.00008L7.99935 13.3334L8.93935 12.3934L5.21935 8.66675H13.3327V7.33341Z" fill="#A2A6A8" id="svg_1" class="" transform="rotate(-180 7.999359607696534,8.00007438659668) "/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 329 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none" stroke="#a2a6a8" strokeWidth="2"><path d="M1.784 8h11.351"/><path fill="#a2a6a8" d="M6.605 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 1 1-7 0z"/></svg>
|
||||
|
After Width: | Height: | Size: 213 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="none">
|
||||
<path fill="#A2A6A8" fill-opacity="1" stroke="#A2A6A8" stroke-opacity="1" stroke-width="2" stroke-dasharray="none" stroke-linejoin="miter" stroke-linecap="butt" fill-rule="nonzero" opacity="1" d="M2.3243240536870218,7.999999884012587 L13.675675405038383,7.999999884012587 " />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 359 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.5 0H3.14706V1.41176H14.1747V13.6471H15.5V0ZM12.8529 2.82353H0.5V16H12.8529V2.82353Z" fill="#090707"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 258 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0.8956 1.29262C1.95273 0.235792 3.66667 0.235792 4.7238 1.29262C5.78093 2.34945 5.78093 4.06291 4.7238 5.11975C4.60724 5.23627 4.4827 5.33995 4.35193 5.43078L5.448 6.019L6.08506 5.32306L12.1055 2.08076C13.0124 1.59232 14.1323 1.88302 14.6929 2.72638L14.7766 2.8641L7.397 6.838L14.7766 10.8125L14.6929 10.9502C14.1323 11.7935 13.0124 12.0842 12.1055 11.5958L6.08506 8.35351L5.586 7.807L4.21474 8.5457C4.30828 8.60855 4.39877 8.67787 4.48558 8.75365L4.62104 8.88025C5.67817 9.93709 5.67817 11.6505 4.62105 12.7074C3.56392 13.7642 1.84998 13.7642 0.792846 12.7074C-0.264282 11.6505 -0.264282 9.93709 0.792846 8.88025C1.12818 8.54502 1.5296 8.31612 1.95519 8.19356L4.264 6.95L2.21011 5.84551C1.78495 5.7494 1.3785 5.54968 1.03107 5.24634L0.8956 5.11975C-0.161527 4.06291 -0.161527 2.34946 0.8956 1.29262ZM3.52628 9.75678C3.00747 9.34607 2.25163 9.38029 1.77233 9.85946C1.25615 10.3755 1.25616 11.2121 1.77233 11.7282C2.2885 12.2442 3.12539 12.2442 3.64156 11.7282C4.15774 11.2121 4.15774 10.3755 3.64156 9.85946L3.52628 9.75678ZM12.0683 6.07527L12.1767 6.08198C12.5771 6.1361 12.8876 6.44438 12.9408 6.8351L12.9489 6.95462C12.9489 7.44005 12.5549 7.83397 12.0693 7.83397C11.5837 7.83397 11.1897 7.44005 11.1897 6.96244C11.1853 6.5546 11.4597 6.2104 11.8336 6.107L11.962 6.08307L12.0683 6.07527ZM15.1194 6.06362L15.2271 6.07033C15.6282 6.12445 15.9387 6.43272 15.9919 6.82345L16 6.94297C16 7.4284 15.606 7.82232 15.1204 7.82232C14.6348 7.82232 14.2408 7.4284 14.2408 6.95078C14.2364 6.54294 14.5108 6.19875 14.8847 6.09534L15.0131 6.07141L15.1194 6.06362ZM3.62903 2.16915C3.11022 1.75843 2.35439 1.79266 1.87508 2.27183C1.35891 2.78786 1.35891 3.62451 1.87508 4.14054C2.39126 4.65657 3.22814 4.65657 3.74432 4.14054C4.26049 3.62451 4.26049 2.78786 3.74432 2.27183L3.62903 2.16915Z" fill="#4B4848"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -0,0 +1 @@
|
|||
<svg width="48" height="24" fill="#fff"><path d="M11 16v-3h10v-2H11V8l-4 4 4 4zm-8 4h2V4H3v16zM36.378 7.73v3h-10v2h10v3l4-4-4-4zm8-4h-2v16h2v-16z"/></svg>
|
||||
|
After Width: | Height: | Size: 154 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none">
|
||||
<path fill="#fff" d="M8 11H11V21H13V11H16L12 7L8 11ZM4 3V5H20V3H4Z" class="selected" transform="rotate(-90 12.000000000000002,12) "/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 216 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none">
|
||||
<path fill="#fff" d="M8 11H11V21H13V11H16L12 7L8 11ZM4 3V5H20V3H4Z" transform="rotate(90 12,12.000000000000002) "/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 200 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H1.4H2.54546V1.39814H1.4V2.54546H0V1.39814V0ZM3.81818 0H6.36364V1.39814H3.81818V0ZM1.4 3.81818H0V6.36364H1.4V3.81818ZM1.4 7.63636H0V10.1818H1.4V7.63636ZM12.6 3.81818H14V6.36364H12.6V3.81818ZM14 7.63636H12.6V10.1818H14V7.63636ZM7.63636 0H10.1818V1.39814H7.63636V0ZM6.36364 12.6H3.81818V14H6.36364V12.6ZM7.63636 12.6H10.1818V14H7.63636V12.6ZM0 14H1.4H2.54546V12.6H1.4V11.4546H0V12.6V14ZM12.6 0H14V1.39814V2.54546H12.6V1.39814H11.4546V0H12.6ZM14 14H12.6H11.4546V12.6H12.6V11.4546H14V12.6V14Z" fill="#4B4848"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 663 B |
|
|
@ -0,0 +1,5 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 6H10V9H6V6Z" fill="#939090"/>
|
||||
<path d="M13 6H15V9H13V6Z" fill="#939090"/>
|
||||
<path d="M1 6H3V9H1V6Z" fill="#939090"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 229 B |
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#939090"><path d="M13 7.5a1 1 0 1 1 2 0v1a1 1 0 1 1-2 0v-1zm-12 0a1 1 0 1 1 2 0v1a1 1 0 1 1-2 0v-1zm8.5.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 1 1 3 0z"/></svg>
|
||||
|
After Width: | Height: | Size: 220 B |
|
|
@ -0,0 +1,4 @@
|
|||
<svg width="9" height="12" viewBox="0 0 9 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.79102 11.1094H4.11328V10.2832H3.87891C3.05273 10.2832 2.71875 9.90234 2.71875 8.96484V7.07812C2.71875 6.23438 2.29688 5.79492 1.43555 5.70703V5.56641C2.29688 5.47852 2.71875 5.03906 2.71875 4.19531V2.32031C2.71875 1.38281 3.05273 1.00195 3.87891 1.00195H4.11328V0.175781H3.79102C2.4082 0.175781 1.75781 0.826172 1.75781 2.19141V3.83203C1.75781 4.76953 1.43555 5.08008 0.46875 5.08008V6.19336C1.43555 6.19336 1.75781 6.50391 1.75781 7.44141V9.09375C1.75781 10.459 2.4082 11.1094 3.79102 11.1094Z" fill="#4B4848"/>
|
||||
<path d="M8.13305 11.1094H8.45531V10.2832H8.22094C7.39477 10.2832 7.06078 9.90234 7.06078 8.96484V7.07812C7.06078 6.23438 6.63891 5.79492 5.77758 5.70703V5.56641C6.63891 5.47852 7.06078 5.03906 7.06078 4.19531V2.32031C7.06078 1.38281 7.39477 1.00195 8.22094 1.00195H8.45531V0.175781H8.13305C6.75023 0.175781 6.09984 0.826172 6.09984 2.19141V3.83203C6.09984 4.76953 5.77758 5.08008 4.81078 5.08008V6.19336C5.77758 6.19336 6.09984 6.50391 6.09984 7.44141V9.09375C6.09984 10.459 6.75023 11.1094 8.13305 11.1094Z" fill="#4B4848"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="13" height="12" viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.38281 11.8271C6.71777 11.8271 6.99805 11.5605 6.99805 11.2324V6.68652H11.4141C11.7422 6.68652 12.0225 6.40625 12.0225 6.07129C12.0225 5.73633 11.7422 5.46289 11.4141 5.46289H6.99805V0.910156C6.99805 0.582031 6.71777 0.31543 6.38281 0.31543C6.04785 0.31543 5.77441 0.582031 5.77441 0.910156V5.46289H1.35156C1.02344 5.46289 0.743164 5.73633 0.743164 6.07129C0.743164 6.40625 1.02344 6.68652 1.35156 6.68652H5.77441V11.2324C5.77441 11.5605 6.04785 11.8271 6.38281 11.8271Z" fill="#4B4848"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 603 B |
|
|
@ -0,0 +1 @@
|
|||
<svg width="20" height="4" viewBox="0 0 20 4" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="20" height="3"/></svg>
|
||||
|
After Width: | Height: | Size: 128 B |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.9545 4L14.2934 8.12197C14.4556 8.44862 14.8847 8.75905 15.2469 8.81139L20 9.75293L16.6923 13.2405C16.43 13.4949 16.2662 13.9968 16.3281 14.3559L16.8704 19.0588L12.5437 17.0908C12.2195 16.9211 11.6892 16.9211 11.3649 17.0908L7.03854 19.0572L7.58084 14.3543C7.64276 13.9952 7.47897 13.4929 7.21665 13.2388L4 9.75127L8.6621 8.80973C9.02462 8.75739 9.45373 8.44697 9.61552 8.12031L11.9545 4Z" fill="#EAEAEA"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 521 B |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 3.6 KiB |
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20 10.474V13.5261C20 13.7558 19.8138 13.9423 19.5838 13.9423H13.9423V19.5838C13.9423 19.8139 13.7558 20 13.5261 20H10.474C10.2444 20 10.0578 19.8138 10.0578 19.5838V13.9423H4.41619C4.18625 13.9423 4 13.7558 4 13.5261V10.474C4 10.2442 4.18625 10.0578 4.41619 10.0578H10.0578V4.41619C10.0578 4.18616 10.2443 4 10.474 4H13.5261C13.7558 4 13.9423 4.18616 13.9423 4.41619V10.0578H19.5838C19.8139 10.0578 20 10.2442 20 10.474Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 550 B |
|
|
@ -203,16 +203,25 @@ const reduceReactions = (
|
|||
reactions.reduce(
|
||||
(res: Record<string, ComponentReaction>, reaction: Reaction) => {
|
||||
const { byUsername, emoji } = reaction;
|
||||
const sameAsCurrent = byUsername === username;
|
||||
if (res[reaction.emoji]) {
|
||||
res[reaction.emoji].count++;
|
||||
if (!sameAsCurrent) {
|
||||
res[reaction.emoji].users = [
|
||||
...(res[reaction.emoji].users || []),
|
||||
byUsername,
|
||||
];
|
||||
}
|
||||
} else {
|
||||
const users = !sameAsCurrent ? [byUsername] : [];
|
||||
res[emoji] = {
|
||||
count: 1,
|
||||
reactionEmoji: emoji,
|
||||
users,
|
||||
} as ComponentReaction;
|
||||
}
|
||||
|
||||
if (byUsername === username) {
|
||||
if (sameAsCurrent) {
|
||||
res[reaction.emoji].active = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import ScrollToLatest from "./ScrollToLatest";
|
|||
|
||||
import {
|
||||
addCommentToThreadRequest,
|
||||
markThreadAsReadRequest,
|
||||
resetVisibleThread,
|
||||
setCommentResolutionRequest,
|
||||
} from "actions/commentActions";
|
||||
|
|
@ -21,6 +22,7 @@ import { RawDraftContentState } from "draft-js";
|
|||
import styled from "styled-components";
|
||||
import { animated } from "react-spring";
|
||||
import { AppState } from "reducers";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const ThreadContainer = styled(animated.div)<{
|
||||
visible?: boolean;
|
||||
|
|
@ -79,6 +81,12 @@ function CommentThreadContainer({
|
|||
state.ui.comments.visibleCommentThreadId === commentThreadId,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isVisible && !commentThread?.isViewed) {
|
||||
dispatch(markThreadAsReadRequest(commentThreadId));
|
||||
}
|
||||
}, [isVisible, commentThread?.isViewed]);
|
||||
|
||||
// Check if the comments window is scrolled to the bottom
|
||||
// We don't autoscroll for the user receiving the updates
|
||||
// for better UX, instead we'd show a helper message to indicate
|
||||
|
|
|
|||
|
|
@ -13,9 +13,10 @@ function ShowcaseCarouselModal({ children }: { children: React.ReactNode }) {
|
|||
isOpen
|
||||
left={25}
|
||||
onClose={() => {
|
||||
console.log("handle close");
|
||||
// TODO (rishabh) handle close
|
||||
}}
|
||||
overlayClassName="comments-onboarding-carousel"
|
||||
portalClassName="comments-onboarding-carousel-portal"
|
||||
scrollContents
|
||||
width={325}
|
||||
zIndex={Layers.appComments}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,6 @@ import Text, { TextType } from "components/ads/Text";
|
|||
import ShowcaseCarousel, { Steps } from "components/ads/ShowcaseCarousel";
|
||||
import ProfileForm, { PROFILE_FORM } from "./ProfileForm";
|
||||
import CommentsCarouselModal from "./CommentsCarouselModal";
|
||||
import CommentsOnboardingStep1 from "assets/images/comments-onboarding/step-1.png";
|
||||
import CommentsOnboardingStep2 from "assets/images/comments-onboarding/step-2.png";
|
||||
import CommentsOnboardingStep3 from "assets/images/comments-onboarding/step-3.png";
|
||||
import CommentsOnboardingStep4 from "assets/images/comments-onboarding/step-4.png";
|
||||
import CommentsOnboardingStep5 from "assets/images/comments-onboarding/step-5.png";
|
||||
|
||||
import styled, { withTheme } from "styled-components";
|
||||
import { Theme } from "constants/DefaultTheme";
|
||||
|
|
@ -26,37 +21,42 @@ import { setCommentsIntroSeen } from "utils/storage";
|
|||
|
||||
import { updateUserDetails } from "actions/userActions";
|
||||
|
||||
import { S3_BUCKET_URL } from "constants/ThirdPartyConstants";
|
||||
|
||||
const getBanner = (step: number) =>
|
||||
`${S3_BUCKET_URL}/comments/step-${step}.png`;
|
||||
|
||||
const introSteps = [
|
||||
{
|
||||
title: "Introducing Live Comments",
|
||||
content:
|
||||
"We are introducing live comments. From now on you will be able to comment on your apps, tag other people and exchange thoughts in threads. Click ‘Next’ to learn more about comments and start commenting.",
|
||||
banner: CommentsOnboardingStep1,
|
||||
banner: getBanner(1),
|
||||
hideBackBtn: true,
|
||||
},
|
||||
{
|
||||
title: "Give feedback",
|
||||
content:
|
||||
"Comment on your co-worker’s work and share your thoughts on what works and what needs change.",
|
||||
banner: CommentsOnboardingStep2,
|
||||
banner: getBanner(2),
|
||||
},
|
||||
{
|
||||
title: "Invite other people to your conversations",
|
||||
content:
|
||||
"When leaving a comment you can tag other people by writing ‘@’ and their name. This way the person you tagged will get a notification and an e-mail that you tagged them in a comment.",
|
||||
banner: CommentsOnboardingStep3,
|
||||
banner: getBanner(3),
|
||||
},
|
||||
{
|
||||
title: "Tag a comment to a widget",
|
||||
content:
|
||||
"If you click on a component while in a comment mode you will tag that comment to that widget. This way if the widget is moved the comment will be moved as well. You can disconnect the comment and widget y simply moving the the comment away from the widget.",
|
||||
banner: CommentsOnboardingStep4,
|
||||
banner: getBanner(4),
|
||||
},
|
||||
{
|
||||
title: "You are all set!",
|
||||
content:
|
||||
"By clicking on the comments icon in the top right corner you will activate the ‘collaboration mode’ and will be able to start a thread or answer to someone else’s comment.",
|
||||
banner: CommentsOnboardingStep5,
|
||||
banner: getBanner(5),
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ const StyledImg = styled.img`
|
|||
function IntroStep(props: {
|
||||
title: string;
|
||||
content: string;
|
||||
banner: typeof CommentsOnboardingStep1;
|
||||
banner: string;
|
||||
theme: Theme;
|
||||
}) {
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ const defaultProps = {
|
|||
size: Size.small,
|
||||
isLoading: false,
|
||||
disabled: false,
|
||||
fill: false,
|
||||
fill: undefined,
|
||||
tag: "a",
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ const ColorTab = styled.div<{ color: string }>`
|
|||
`;
|
||||
|
||||
const defaultColors: string[] = [
|
||||
"rgb(3, 179, 101)",
|
||||
"#03b365",
|
||||
"#FFC13D",
|
||||
"#38AFF4",
|
||||
"#DD4B34",
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import React, { useState, useEffect, useCallback } from "react";
|
||||
import React, { ReactNode, useState, useEffect, useCallback } from "react";
|
||||
import Icon, { IconName, IconSize } from "./Icon";
|
||||
import { CommonComponentProps, Classes } from "./common";
|
||||
import Text, { TextType } from "./Text";
|
||||
import { Popover, Position } from "@blueprintjs/core";
|
||||
import styled from "constants/DefaultTheme";
|
||||
import { Colors } from "constants/Colors";
|
||||
|
||||
export type DropdownOption = {
|
||||
label?: string;
|
||||
|
|
@ -16,6 +17,22 @@ export type DropdownOption = {
|
|||
onSelect?: (value?: string) => void;
|
||||
};
|
||||
|
||||
export interface DefaultDropDownValueNodeProps {
|
||||
selected: DropdownOption;
|
||||
showLabelOnly?: boolean;
|
||||
isOpen?: boolean;
|
||||
}
|
||||
|
||||
export interface RenderDropdownOptionType {
|
||||
option: DropdownOption;
|
||||
optionClickHandler?: (dropdownOption: DropdownOption) => void;
|
||||
}
|
||||
|
||||
type RenderOption = ({
|
||||
option,
|
||||
optionClickHandler,
|
||||
}: RenderDropdownOptionType) => ReactNode;
|
||||
|
||||
export type DropdownProps = CommonComponentProps & {
|
||||
options: DropdownOption[];
|
||||
selected: DropdownOption;
|
||||
|
|
@ -25,7 +42,10 @@ export type DropdownProps = CommonComponentProps & {
|
|||
showLabelOnly?: boolean;
|
||||
optionWidth?: string;
|
||||
showDropIcon?: boolean;
|
||||
headerLabel?: string;
|
||||
SelectedValueNode?: typeof DefaultDropDownValueNode;
|
||||
bgColor?: string;
|
||||
renderOption?: RenderOption;
|
||||
};
|
||||
|
||||
export const DropdownContainer = styled.div<{ width: string; height: string }>`
|
||||
|
|
@ -38,12 +58,15 @@ const Selected = styled.div<{
|
|||
isOpen: boolean;
|
||||
disabled?: boolean;
|
||||
height: string;
|
||||
bgColor?: string;
|
||||
}>`
|
||||
padding: ${(props) => props.theme.spaces[2]}px
|
||||
${(props) => props.theme.spaces[3]}px;
|
||||
background: ${(props) =>
|
||||
props.disabled
|
||||
? props.theme.colors.dropdown.header.disabledBg
|
||||
: !!props.bgColor
|
||||
? props.bgColor
|
||||
: props.theme.colors.dropdown.header.bg};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -53,21 +76,31 @@ const Selected = styled.div<{
|
|||
cursor: pointer;
|
||||
${(props) =>
|
||||
props.isOpen
|
||||
? `border: 1px solid ${props.theme.colors.info.main}`
|
||||
? `border: 1px solid ${
|
||||
!!props.bgColor ? props.bgColor : props.theme.colors.info.main
|
||||
}`
|
||||
: props.disabled
|
||||
? `border: 1px solid ${props.theme.colors.dropdown.header.disabledBg}`
|
||||
: `border: 1px solid ${props.theme.colors.dropdown.header.bg}`};
|
||||
: `border: 1px solid ${
|
||||
!!props.bgColor
|
||||
? props.bgColor
|
||||
: props.theme.colors.dropdown.header.bg
|
||||
}`};
|
||||
${(props) =>
|
||||
props.isOpen && !props.disabled ? "box-sizing: border-box" : null};
|
||||
${(props) =>
|
||||
props.isOpen && !props.disabled
|
||||
props.isOpen && !props.disabled && !props.bgColor
|
||||
? "box-shadow: 0px 0px 4px 4px rgba(203, 72, 16, 0.18)"
|
||||
: null};
|
||||
.${Classes.TEXT} {
|
||||
${(props) =>
|
||||
props.disabled
|
||||
? `color: ${props.theme.colors.dropdown.header.disabledText}`
|
||||
: `color: ${props.theme.colors.dropdown.header.text}`};
|
||||
: `color: ${
|
||||
!!props.bgColor
|
||||
? Colors.WHITE
|
||||
: props.theme.colors.dropdown.header.text
|
||||
}`};
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
@ -159,6 +192,12 @@ const StyledSubText = styled(Text)`
|
|||
}
|
||||
`;
|
||||
|
||||
const HeaderWrapper = styled.div`
|
||||
color: #6d6d6d;
|
||||
font-size: 10px;
|
||||
padding: 0px 7px 7px 7px;
|
||||
`;
|
||||
|
||||
const SelectedDropDownHolder = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -185,17 +224,17 @@ const SelectedIcon = styled(Icon)`
|
|||
}
|
||||
`;
|
||||
|
||||
function DefaultDropDownValueNode({
|
||||
export function DefaultDropDownValueNode({
|
||||
selected,
|
||||
showLabelOnly,
|
||||
}: {
|
||||
selected: DropdownOption;
|
||||
showLabelOnly?: boolean;
|
||||
}) {
|
||||
}: DefaultDropDownValueNodeProps) {
|
||||
return (
|
||||
<SelectedDropDownHolder>
|
||||
{selected.icon ? (
|
||||
<SelectedIcon name={selected.icon} size={IconSize.XXS} />
|
||||
<SelectedIcon
|
||||
name={selected.icon}
|
||||
size={selected.iconSize || IconSize.XXS}
|
||||
/>
|
||||
) : null}
|
||||
<Text type={TextType.P1}>
|
||||
{showLabelOnly ? selected.label : selected.value}
|
||||
|
|
@ -209,6 +248,7 @@ export default function Dropdown(props: DropdownProps) {
|
|||
onSelect,
|
||||
showDropIcon = true,
|
||||
SelectedValueNode = DefaultDropDownValueNode,
|
||||
renderOption,
|
||||
} = { ...props };
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||
const [selected, setSelected] = useState<DropdownOption>(props.selected);
|
||||
|
|
@ -242,6 +282,7 @@ export default function Dropdown(props: DropdownProps) {
|
|||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Selected
|
||||
bgColor={props.bgColor}
|
||||
className={props.className}
|
||||
disabled={props.disabled}
|
||||
height={props.height || "38px"}
|
||||
|
|
@ -249,13 +290,23 @@ export default function Dropdown(props: DropdownProps) {
|
|||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
<SelectedValueNode
|
||||
isOpen={isOpen}
|
||||
selected={selected}
|
||||
showLabelOnly={props.showLabelOnly}
|
||||
/>
|
||||
{showDropIcon && <Icon name="downArrow" size={IconSize.XXS} />}
|
||||
</Selected>
|
||||
<DropdownWrapper width={props.optionWidth || "260px"}>
|
||||
{props.headerLabel && (
|
||||
<HeaderWrapper>{props.headerLabel}</HeaderWrapper>
|
||||
)}
|
||||
{props.options.map((option: DropdownOption, index: number) => {
|
||||
if (renderOption) {
|
||||
return renderOption({
|
||||
option,
|
||||
optionClickHandler,
|
||||
});
|
||||
}
|
||||
return (
|
||||
<OptionWrapper
|
||||
className="t--dropdown-option"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import React from "react";
|
|||
import styled from "styled-components";
|
||||
import EmojiPicker from "./EmojiPicker";
|
||||
import { IconSize } from "./Icon";
|
||||
import TooltipComponent from "./Tooltip";
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
|
|
@ -46,10 +47,29 @@ const Count = styled.div<{ active?: boolean }>`
|
|||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const ReactionsByContainer = styled.span`
|
||||
max-width: 200px;
|
||||
display: inline-block;
|
||||
`;
|
||||
|
||||
function ReactionsBy(props: { reaction: Reaction }) {
|
||||
const { reaction } = props;
|
||||
|
||||
if (!reaction?.users) return null;
|
||||
const isSliced = reaction?.users.length > 5;
|
||||
const users = reaction?.users.slice(0, 5);
|
||||
|
||||
if (isSliced) users.push("...");
|
||||
if (reaction.active) users.unshift("You");
|
||||
|
||||
return <ReactionsByContainer>{users.join(", ")}</ReactionsByContainer>;
|
||||
}
|
||||
|
||||
export type Reaction = {
|
||||
count: number;
|
||||
reactionEmoji: string;
|
||||
active?: boolean;
|
||||
users?: Array<string>;
|
||||
};
|
||||
|
||||
export type Reactions = Record<string, Reaction>;
|
||||
|
|
@ -98,6 +118,7 @@ function EmojiReactions({
|
|||
};
|
||||
if (reactions[emojiData].count === 0) delete reactions[emojiData];
|
||||
} else {
|
||||
addOrRemove = ReactionOperation.ADD;
|
||||
reactions[emojiData] = {
|
||||
active: true,
|
||||
reactionEmoji: emojiData,
|
||||
|
|
@ -124,16 +145,23 @@ function EmojiReactions({
|
|||
<Container>
|
||||
{!hideReactions &&
|
||||
transformReactions(reactions).map((reaction: Reaction) => (
|
||||
<Bubble
|
||||
active={reaction.active}
|
||||
<TooltipComponent
|
||||
boundary={"viewport"}
|
||||
content={<ReactionsBy reaction={reaction} />}
|
||||
disabled={!reaction.users || reaction.users.length === 0}
|
||||
key={reaction.reactionEmoji}
|
||||
onClick={(e) => handleSelectReaction(e, reaction.reactionEmoji)}
|
||||
modifiers={{ preventOverflow: { enabled: true } }}
|
||||
>
|
||||
<span>{reaction.reactionEmoji}</span>
|
||||
{reaction.count > 1 && (
|
||||
<Count active={reaction.active}>{reaction.count}</Count>
|
||||
)}
|
||||
</Bubble>
|
||||
<Bubble
|
||||
active={reaction.active}
|
||||
onClick={(e) => handleSelectReaction(e, reaction.reactionEmoji)}
|
||||
>
|
||||
<span>{reaction.reactionEmoji}</span>
|
||||
{reaction.count > 1 && (
|
||||
<Count active={reaction.active}>{reaction.count}</Count>
|
||||
)}
|
||||
</Bubble>
|
||||
</TooltipComponent>
|
||||
))}
|
||||
{!hideReactions ? (
|
||||
<Bubble>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { ReactComponent as DeleteIcon } from "assets/icons/ads/delete.svg";
|
|||
import { ReactComponent as BookIcon } from "assets/icons/ads/book.svg";
|
||||
import { ReactComponent as BugIcon } from "assets/icons/ads/bug.svg";
|
||||
import { ReactComponent as CancelIcon } from "assets/icons/ads/cancel.svg";
|
||||
import { ReactComponent as ExpandMore } from "assets/icons/ads/expand-more.svg";
|
||||
import { ReactComponent as CrossIcon } from "assets/icons/ads/cross.svg";
|
||||
import { ReactComponent as OpenIcon } from "assets/icons/ads/open.svg";
|
||||
import { ReactComponent as UserIcon } from "assets/icons/ads/user.svg";
|
||||
|
|
@ -39,6 +40,7 @@ import { ReactComponent as NoResponseIcon } from "assets/icons/ads/no-response.s
|
|||
import { ReactComponent as LightningIcon } from "assets/icons/ads/lightning.svg";
|
||||
import { ReactComponent as AddMoreIcon } from "assets/icons/ads/add-more.svg";
|
||||
import { ReactComponent as RightArrowIcon } from "assets/icons/ads/right-arrow.svg";
|
||||
import { ReactComponent as TrendingFlat } from "assets/icons/ads/trending-flat.svg";
|
||||
import { ReactComponent as DatasourceIcon } from "assets/icons/ads/datasource.svg";
|
||||
import { ReactComponent as PlayIcon } from "assets/icons/ads/play.svg";
|
||||
import { ReactComponent as DesktopIcon } from "assets/icons/ads/desktop.svg";
|
||||
|
|
@ -66,6 +68,11 @@ import { ReactComponent as Reaction } from "assets/icons/comments/reaction.svg";
|
|||
import { ReactComponent as Reaction2 } from "assets/icons/comments/reaction-2.svg";
|
||||
import { ReactComponent as Upload } from "assets/icons/ads/upload.svg";
|
||||
import { ReactComponent as Download } from "assets/icons/ads/download.svg";
|
||||
import { ReactComponent as ArrowForwardIcon } from "assets/icons/control/arrow_forward.svg";
|
||||
import { ReactComponent as CapSolidIcon } from "assets/icons/control/cap_solid.svg";
|
||||
import { ReactComponent as CapDotIcon } from "assets/icons/control/cap_dot.svg";
|
||||
import { ReactComponent as LineDottedIcon } from "assets/icons/control/line_dotted.svg";
|
||||
import { ReactComponent as LineDashedIcon } from "assets/icons/control/line_dashed.svg";
|
||||
import styled from "styled-components";
|
||||
import { CommonComponentProps, Classes } from "./common";
|
||||
import { noop } from "lodash";
|
||||
|
|
@ -126,6 +133,7 @@ export const IconCollection = [
|
|||
"cancel",
|
||||
"cross",
|
||||
"delete",
|
||||
"expand-more",
|
||||
"open",
|
||||
"user",
|
||||
"general",
|
||||
|
|
@ -162,6 +170,7 @@ export const IconCollection = [
|
|||
"lightning",
|
||||
"add-more",
|
||||
"right-arrow",
|
||||
"trending-flat",
|
||||
"datasource",
|
||||
"play",
|
||||
"desktop",
|
||||
|
|
@ -193,6 +202,11 @@ export const IconCollection = [
|
|||
"unpin",
|
||||
"reaction",
|
||||
"reaction-2",
|
||||
"arrow-forward",
|
||||
"cap-solid",
|
||||
"cap-dot",
|
||||
"line-dotted",
|
||||
"line-dashed",
|
||||
] as const;
|
||||
|
||||
export type IconName = typeof IconCollection[number];
|
||||
|
|
@ -274,6 +288,9 @@ const Icon = forwardRef(
|
|||
case "delete":
|
||||
returnIcon = <DeleteIcon />;
|
||||
break;
|
||||
case "expand-more":
|
||||
returnIcon = <ExpandMore />;
|
||||
break;
|
||||
case "open":
|
||||
returnIcon = <OpenIcon />;
|
||||
break;
|
||||
|
|
@ -386,6 +403,9 @@ const Icon = forwardRef(
|
|||
case "right-arrow":
|
||||
returnIcon = <RightArrowIcon />;
|
||||
break;
|
||||
case "trending-flat":
|
||||
returnIcon = <TrendingFlat />;
|
||||
break;
|
||||
case "datasource":
|
||||
returnIcon = <DatasourceIcon />;
|
||||
break;
|
||||
|
|
@ -489,6 +509,26 @@ const Icon = forwardRef(
|
|||
returnIcon = <Download />;
|
||||
break;
|
||||
|
||||
case "arrow-forward":
|
||||
returnIcon = <ArrowForwardIcon />;
|
||||
break;
|
||||
|
||||
case "cap-solid":
|
||||
returnIcon = <CapSolidIcon />;
|
||||
break;
|
||||
|
||||
case "cap-dot":
|
||||
returnIcon = <CapDotIcon />;
|
||||
break;
|
||||
|
||||
case "line-dotted":
|
||||
returnIcon = <LineDottedIcon />;
|
||||
break;
|
||||
|
||||
case "line-dashed":
|
||||
returnIcon = <LineDashedIcon />;
|
||||
break;
|
||||
|
||||
default:
|
||||
returnIcon = null;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ import styled from "styled-components";
|
|||
import { ControlIcons, ControlIconName } from "icons/ControlIcons";
|
||||
|
||||
const ItemWrapper = styled.div<{ selected: boolean }>`
|
||||
width: 32px;
|
||||
width: auto;
|
||||
padding: 0 5px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -30,6 +31,7 @@ const FlexWrapper = styled.div`
|
|||
export interface IconTabOption {
|
||||
icon: string;
|
||||
value: string;
|
||||
width?: number;
|
||||
}
|
||||
|
||||
interface IconTabsComponentProps {
|
||||
|
|
@ -41,21 +43,23 @@ interface IconTabsComponentProps {
|
|||
function IconTabsComponent(props: IconTabsComponentProps) {
|
||||
return (
|
||||
<FlexWrapper>
|
||||
{props.options.map((option: IconTabOption, index: number) => {
|
||||
const controlIconName: ControlIconName = option.icon;
|
||||
const ControlIcon = ControlIcons[controlIconName];
|
||||
const isSelected = props.value === option.value;
|
||||
return (
|
||||
<ItemWrapper
|
||||
className={`t--icon-tab-${option.value}`}
|
||||
key={index}
|
||||
onClick={() => props.selectOption(option.value)}
|
||||
selected={isSelected}
|
||||
>
|
||||
<ControlIcon height={24} width={24} />
|
||||
</ItemWrapper>
|
||||
);
|
||||
})}
|
||||
{props.options.map(
|
||||
({ icon, value, width = 24 }: IconTabOption, index: number) => {
|
||||
const controlIconName: ControlIconName = icon;
|
||||
const ControlIcon = ControlIcons[controlIconName];
|
||||
const isSelected = props.value === value;
|
||||
return (
|
||||
<ItemWrapper
|
||||
className={`t--icon-tab-${value}`}
|
||||
key={index}
|
||||
onClick={() => props.selectOption(value)}
|
||||
selected={isSelected}
|
||||
>
|
||||
<ControlIcon height={24} width={width} />
|
||||
</ItemWrapper>
|
||||
);
|
||||
},
|
||||
)}
|
||||
</FlexWrapper>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@ export type TabProp = {
|
|||
iconSize?: IconSize;
|
||||
};
|
||||
|
||||
const TabsWrapper = styled.div<{ shouldOverflow?: boolean }>`
|
||||
const TabsWrapper = styled.div<{
|
||||
shouldOverflow?: boolean;
|
||||
vertical?: boolean;
|
||||
}>`
|
||||
border-radius: 0px;
|
||||
height: 100%;
|
||||
.react-tabs {
|
||||
|
|
@ -27,7 +30,8 @@ const TabsWrapper = styled.div<{ shouldOverflow?: boolean }>`
|
|||
.react-tabs__tab-list {
|
||||
margin: 0px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: ${(props) => (!!props.vertical ? "column" : "row")};
|
||||
align-items: ${(props) => (!!props.vertical ? "flex-start" : "center")};
|
||||
border-bottom: none;
|
||||
color: ${(props) => props.theme.colors.tabs.normal};
|
||||
path {
|
||||
|
|
@ -45,8 +49,10 @@ const TabsWrapper = styled.div<{ shouldOverflow?: boolean }>`
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: ${(props) => props.theme.spaces[3] - 1}px 0
|
||||
${(props) => props.theme.spaces[4]}px 0;
|
||||
padding: ${(props) => props.theme.spaces[3] - 1}px
|
||||
${(props) => (props.vertical ? `${props.theme.spaces[4] - 1}px` : 0)}
|
||||
${(props) => props.theme.spaces[4]}px
|
||||
${(props) => (props.vertical ? `${props.theme.spaces[4] - 1}px` : 0)};
|
||||
margin-right: ${(props) => props.theme.spaces[12] - 3}px;
|
||||
text-align: center;
|
||||
display: inline-flex;
|
||||
|
|
@ -72,10 +78,15 @@ const TabsWrapper = styled.div<{ shouldOverflow?: boolean }>`
|
|||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: ${(props) => props.theme.spaces[0] - 1}px;
|
||||
width: ${(props) =>
|
||||
props.vertical ? `${props.theme.spaces[1] - 2}px` : "100%"};
|
||||
bottom: ${(props) =>
|
||||
props.vertical ? "0%" : `${props.theme.spaces[0] - 1}px`};
|
||||
top: ${(props) =>
|
||||
props.vertical ? `${props.theme.spaces[0] - 1}px` : "100%"};
|
||||
left: ${(props) => props.theme.spaces[0]}px;
|
||||
height: ${(props) => props.theme.spaces[1] - 2}px;
|
||||
height: ${(props) =>
|
||||
props.vertical ? "100%" : `${props.theme.spaces[1] - 2}px`};
|
||||
background-color: ${(props) => props.theme.colors.info.main};
|
||||
}
|
||||
}
|
||||
|
|
@ -83,10 +94,15 @@ const TabsWrapper = styled.div<{ shouldOverflow?: boolean }>`
|
|||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: ${(props) => props.theme.spaces[0] - 1}px;
|
||||
width: ${(props) =>
|
||||
props.vertical ? `${props.theme.spaces[1] - 2}px` : "100%"};
|
||||
bottom: ${(props) =>
|
||||
props.vertical ? "0%" : `${props.theme.spaces[0] - 1}px`};
|
||||
top: ${(props) =>
|
||||
props.vertical ? `${props.theme.spaces[0] - 1}px` : "100%"};
|
||||
left: ${(props) => props.theme.spaces[0]}px;
|
||||
height: ${(props) => props.theme.spaces[1] - 2}px;
|
||||
height: ${(props) =>
|
||||
props.vertical ? "100%" : `${props.theme.spaces[1] - 2}px`};
|
||||
background-color: ${(props) => props.theme.colors.info.main};
|
||||
}
|
||||
box-shadow: none;
|
||||
|
|
@ -101,7 +117,7 @@ const TabTitleWrapper = styled.div`
|
|||
display: flex;
|
||||
align-items: center;
|
||||
.${Classes.ICON} {
|
||||
margin-right: ${(props) => props.theme.spaces[3]}px;
|
||||
margin-right: ${(props) => props.theme.spaces[2]}px;
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
@ -110,7 +126,7 @@ export const TabTitle = styled.span`
|
|||
font-weight: ${(props) => props.theme.typography.h5.fontWeight};
|
||||
line-height: ${(props) => props.theme.typography.h5.lineHeight - 3}px;
|
||||
letter-spacing: ${(props) => props.theme.typography.h5.letterSpacing}px;
|
||||
margin: 0 5px;
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
export const TabCount = styled.div`
|
||||
|
|
@ -127,6 +143,7 @@ type TabbedViewComponentType = CommonComponentProps & {
|
|||
selectedIndex?: number;
|
||||
onSelect?: (tabIndex: number) => void;
|
||||
overflow?: boolean;
|
||||
vertical?: boolean;
|
||||
};
|
||||
|
||||
export function TabComponent(props: TabbedViewComponentType) {
|
||||
|
|
@ -134,6 +151,7 @@ export function TabComponent(props: TabbedViewComponentType) {
|
|||
<TabsWrapper
|
||||
data-cy={props.cypressSelector}
|
||||
shouldOverflow={props.overflow}
|
||||
vertical={props.vertical}
|
||||
>
|
||||
<Tabs
|
||||
onSelect={(index: number) => {
|
||||
|
|
@ -143,7 +161,7 @@ export function TabComponent(props: TabbedViewComponentType) {
|
|||
>
|
||||
<TabList>
|
||||
{props.tabs.map((tab) => (
|
||||
<Tab key={tab.key}>
|
||||
<Tab data-cy={`t--tab-${tab.key}`} key={tab.key}>
|
||||
<TabTitleWrapper>
|
||||
{tab.icon ? (
|
||||
<Icon
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ type Variant = "dark" | "light";
|
|||
|
||||
type TooltipProps = CommonComponentProps & {
|
||||
content: JSX.Element | string;
|
||||
disabled?: boolean;
|
||||
position?: Position;
|
||||
children: JSX.Element | React.ReactNode;
|
||||
variant?: Variant;
|
||||
|
|
@ -30,6 +31,7 @@ function TooltipComponent(props: TooltipProps) {
|
|||
autoFocus={props.autoFocus}
|
||||
boundary={props.boundary || "scrollParent"}
|
||||
content={props.content}
|
||||
disabled={props.disabled}
|
||||
hoverOpenDelay={props.hoverOpenDelay}
|
||||
isOpen={props.isOpen}
|
||||
minimal={props.minimal}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export type TreeDropdownOption = DropdownOption & {
|
|||
children?: TreeDropdownOption[];
|
||||
className?: string;
|
||||
type?: string;
|
||||
icon?: React.ReactNode;
|
||||
};
|
||||
|
||||
type Setter = (value: TreeDropdownOption, defaultVal?: string) => void;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,14 @@ const StyledContainerComponent = styled.div<
|
|||
box-shadow: ${(props) =>
|
||||
props.selected ? "0px 0px 0px 3px rgba(59,130,246,0.5)" : "none"};
|
||||
z-index: ${(props) => (props.focused ? "3" : props.selected ? "2" : "1")};
|
||||
${(props) => (props.shouldScrollContents ? scrollContents : "")}
|
||||
${(props) =>
|
||||
props.shouldScrollContents === true
|
||||
? scrollContents
|
||||
: props.shouldScrollContents === false
|
||||
? css`
|
||||
overflow: hidden;
|
||||
`
|
||||
: ""}
|
||||
|
||||
&:hover {
|
||||
z-index: ${(props) => (props.onClickCapture ? "2" : "1")};
|
||||
|
|
|
|||
|
|
@ -2,9 +2,11 @@ import * as React from "react";
|
|||
import { ComponentProps } from "./BaseComponent";
|
||||
import styled from "styled-components";
|
||||
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
|
||||
import { Colors } from "constants/Colors";
|
||||
|
||||
export interface StyledImageProps {
|
||||
defaultImageUrl: string;
|
||||
enableRotation?: boolean;
|
||||
imageUrl?: string;
|
||||
backgroundColor?: string;
|
||||
showHoverPointer?: boolean;
|
||||
|
|
@ -42,6 +44,46 @@ const Wrapper = styled.div`
|
|||
}
|
||||
`;
|
||||
|
||||
const ControlBtnWrapper = styled.div`
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
padding: 5px 0px;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: white;
|
||||
`;
|
||||
|
||||
const ControlBtn = styled.div`
|
||||
height: 25px;
|
||||
width: 45px;
|
||||
color: white;
|
||||
padding: 0px 10px;
|
||||
display: inline-block;
|
||||
|
||||
&.separator {
|
||||
border-right: 1px solid ${Colors.ALTO2};
|
||||
}
|
||||
|
||||
& > div {
|
||||
cursor: pointer;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 4px;
|
||||
transition: background 0.2s linear;
|
||||
|
||||
& > svg {
|
||||
height: 16px;
|
||||
width: 17px;
|
||||
}
|
||||
&: hover {
|
||||
background: #ebebeb;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
enum ZoomingState {
|
||||
MAX_ZOOMED_OUT = "MAX_ZOOMED_OUT",
|
||||
MAX_ZOOMED_IN = "MAX_ZOOMED_IN",
|
||||
|
|
@ -50,6 +92,8 @@ class ImageComponent extends React.Component<
|
|||
ImageComponentProps,
|
||||
{
|
||||
imageError: boolean;
|
||||
showImageControl: boolean;
|
||||
imageRotation: number;
|
||||
zoomingState: ZoomingState;
|
||||
}
|
||||
> {
|
||||
|
|
@ -59,11 +103,14 @@ class ImageComponent extends React.Component<
|
|||
this.isPanning = false;
|
||||
this.state = {
|
||||
imageError: false,
|
||||
showImageControl: false,
|
||||
imageRotation: 0,
|
||||
zoomingState: ZoomingState.MAX_ZOOMED_OUT,
|
||||
};
|
||||
}
|
||||
render() {
|
||||
const { maxZoomLevel } = this.props;
|
||||
const { imageRotation } = this.state;
|
||||
const zoomActive =
|
||||
maxZoomLevel !== undefined && maxZoomLevel > 1 && !this.isPanning;
|
||||
const isZoomingIn = this.state.zoomingState === ZoomingState.MAX_ZOOMED_OUT;
|
||||
|
|
@ -72,7 +119,10 @@ class ImageComponent extends React.Component<
|
|||
cursor = isZoomingIn ? "zoom-in" : "zoom-out";
|
||||
}
|
||||
return (
|
||||
<Wrapper>
|
||||
<Wrapper
|
||||
onMouseEnter={this.onMouseEnter}
|
||||
onMouseLeave={this.onMouseLeave}
|
||||
>
|
||||
<TransformWrapper
|
||||
defaultScale={1}
|
||||
doubleClick={{
|
||||
|
|
@ -122,45 +172,162 @@ class ImageComponent extends React.Component<
|
|||
}}
|
||||
>
|
||||
{({ zoomIn, zoomOut }: any) => (
|
||||
<TransformComponent>
|
||||
<StyledImage
|
||||
className={this.props.isLoading ? "bp3-skeleton" : ""}
|
||||
imageError={this.state.imageError}
|
||||
{...this.props}
|
||||
data-testid="styledImage"
|
||||
onClick={(event: React.MouseEvent<HTMLElement>) => {
|
||||
if (!this.isPanning) {
|
||||
if (isZoomingIn) {
|
||||
zoomIn(event);
|
||||
} else {
|
||||
zoomOut(event);
|
||||
<>
|
||||
{this.renderImageControl()}
|
||||
<TransformComponent>
|
||||
<StyledImage
|
||||
className={this.props.isLoading ? "bp3-skeleton" : ""}
|
||||
imageError={this.state.imageError}
|
||||
{...this.props}
|
||||
data-testid="styledImage"
|
||||
onClick={(event: React.MouseEvent<HTMLElement>) => {
|
||||
if (!this.isPanning) {
|
||||
if (isZoomingIn) {
|
||||
zoomIn(event);
|
||||
} else {
|
||||
zoomOut(event);
|
||||
}
|
||||
this.props.onClick && this.props.onClick(event);
|
||||
}
|
||||
this.props.onClick && this.props.onClick(event);
|
||||
}
|
||||
this.isPanning = false;
|
||||
}}
|
||||
style={{
|
||||
cursor,
|
||||
}}
|
||||
>
|
||||
{/* Used for running onImageError and onImageLoad Functions since Background Image doesn't have the functionality */}
|
||||
<img
|
||||
alt={this.props.widgetName}
|
||||
onError={this.onImageError}
|
||||
onLoad={this.onImageLoad}
|
||||
src={this.props.imageUrl}
|
||||
style={{
|
||||
display: "none",
|
||||
this.isPanning = false;
|
||||
}}
|
||||
/>
|
||||
</StyledImage>
|
||||
</TransformComponent>
|
||||
style={{
|
||||
cursor,
|
||||
transform: `rotate(${imageRotation}deg)`,
|
||||
}}
|
||||
>
|
||||
{/* Used for running onImageError and onImageLoad Functions since Background Image doesn't have the functionality */}
|
||||
<img
|
||||
alt={this.props.widgetName}
|
||||
onError={this.onImageError}
|
||||
onLoad={this.onImageLoad}
|
||||
src={this.props.imageUrl}
|
||||
style={{
|
||||
display: "none",
|
||||
}}
|
||||
/>
|
||||
</StyledImage>
|
||||
</TransformComponent>
|
||||
</>
|
||||
)}
|
||||
</TransformWrapper>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
renderImageControl = () => {
|
||||
const { enableDownload, enableRotation } = this.props;
|
||||
const { showImageControl } = this.state;
|
||||
|
||||
if (showImageControl && (enableRotation || enableDownload)) {
|
||||
return (
|
||||
<ControlBtnWrapper>
|
||||
{enableRotation && (
|
||||
<>
|
||||
<ControlBtn onClick={this.handleImageRotate(false)}>
|
||||
<div>
|
||||
<svg
|
||||
fill="none"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
width="12"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2.28492 1.81862C3.27446 0.939565 4.57489 0.400391 6.00002 0.400391C9.08724 0.400391 11.6 2.91317 11.6 6.00039C11.6 9.08761 9.08724 11.6004 6.00002 11.6004C2.91281 11.6004 0.400024 9.08761 0.400024 6.00039H1.33336C1.33336 8.58317 3.41724 10.6671 6.00002 10.6671C8.58281 10.6671 10.6667 8.58317 10.6667 6.00039C10.6667 3.41761 8.58281 1.33372 6.00002 1.33372C4.82777 1.33372 3.76447 1.7682 2.94573 2.47943L4.13336 3.66706H1.33336V0.867057L2.28492 1.81862Z"
|
||||
fill="#858282"
|
||||
stroke="#858282"
|
||||
strokeWidth="0.5"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</ControlBtn>
|
||||
<ControlBtn
|
||||
className="separator"
|
||||
onClick={this.handleImageRotate(true)}
|
||||
>
|
||||
<div>
|
||||
<svg
|
||||
fill="none"
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
width="12"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M0.400024 6.00039C0.400024 2.91317 2.91281 0.400391 6.00002 0.400391C7.42515 0.400391 8.72559 0.939565 9.71513 1.81862L10.6667 0.867057V3.66706H7.86669L9.05432 2.47943C8.23558 1.7682 7.17228 1.33372 6.00002 1.33372C3.41724 1.33372 1.33336 3.41761 1.33336 6.00039C1.33336 8.58317 3.41724 10.6671 6.00002 10.6671C8.58281 10.6671 10.6667 8.58317 10.6667 6.00039H11.6C11.6 9.08761 9.08724 11.6004 6.00002 11.6004C2.91281 11.6004 0.400024 9.08761 0.400024 6.00039Z"
|
||||
fill="#858282"
|
||||
stroke="#858282"
|
||||
strokeWidth="0.5"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</ControlBtn>
|
||||
</>
|
||||
)}
|
||||
{enableDownload && (
|
||||
<ControlBtn onClick={this.handleImageDownload}>
|
||||
<div>
|
||||
<svg fill="none" height="20" viewBox="0 0 20 20" width="20">
|
||||
<path
|
||||
clipRule="evenodd"
|
||||
d="M15.4547 16.4284H13.117H6.88326H4.54559C2.8243 16.4284 1.42871 14.8933 1.42871 12.9999C1.42871 11.3987 2.43157 10.0641 3.7804 9.68786C3.93001 6.28329 6.47884 3.57129 9.61053 3.57129C12.7072 3.57129 15.2349 6.22243 15.4352 9.57386C17.183 9.56015 18.5716 11.1167 18.5716 12.9999C18.5716 14.8933 17.176 16.4284 15.4547 16.4284ZM12.7266 11.4286L9.99929 14.8572L7.27202 11.4286L8.83045 11.4286L8.83045 8.00004L11.1681 8.00003V11.4286L12.7266 11.4286Z"
|
||||
fill="#939090"
|
||||
fillRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</ControlBtn>
|
||||
)}
|
||||
</ControlBtnWrapper>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
handleImageRotate = (rotateRight: boolean) => (e: any) => {
|
||||
const { imageRotation } = this.state;
|
||||
|
||||
const nextRotation = rotateRight ? imageRotation + 90 : imageRotation - 90;
|
||||
|
||||
this.setState({ imageRotation: nextRotation % 360 });
|
||||
|
||||
if (!!e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
handleImageDownload = (e: any) => {
|
||||
const { imageUrl, widgetId } = this.props;
|
||||
const fileName = `${widgetId}-download`;
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", imageUrl, true);
|
||||
xhr.responseType = "blob";
|
||||
|
||||
xhr.onload = function() {
|
||||
const urlCreator = window.URL || window.webkitURL;
|
||||
const imageUrl = urlCreator.createObjectURL(this.response);
|
||||
const tag = document.createElement("a");
|
||||
tag.href = imageUrl;
|
||||
tag.download = fileName;
|
||||
document.body.appendChild(tag);
|
||||
tag.click();
|
||||
document.body.removeChild(tag);
|
||||
window.URL.revokeObjectURL(imageUrl);
|
||||
};
|
||||
xhr.send();
|
||||
|
||||
if (!!e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
onMouseEnter = () => this.setState({ showImageControl: true });
|
||||
|
||||
onMouseLeave = () => this.setState({ showImageControl: false });
|
||||
|
||||
onImageError = () => {
|
||||
this.setState({
|
||||
imageError: true,
|
||||
|
|
@ -180,6 +347,8 @@ export interface ImageComponentProps extends ComponentProps {
|
|||
isLoading: boolean;
|
||||
showHoverPointer?: boolean;
|
||||
maxZoomLevel: number;
|
||||
enableRotation?: boolean;
|
||||
enableDownload?: boolean;
|
||||
objectFit: string;
|
||||
disableDrag: (disabled: boolean) => void;
|
||||
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ export function PositionedContainer(props: PositionedContainerProps) {
|
|||
className={containerClassName}
|
||||
data-testid="test-widget"
|
||||
id={props.widgetId}
|
||||
key={`positioned-container-${props.widgetId}`}
|
||||
onClick={stopEventPropagation}
|
||||
// Positioned Widget is the top enclosure for all widgets and clicks on/inside the widget should not be propogated/bubbled out of this Container.
|
||||
onClickCapture={openPropPane}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ describe("TableUtilities", () => {
|
|||
"url",
|
||||
true,
|
||||
);
|
||||
// console.log(JSON.stringify(sortedTableData));
|
||||
expect(sortedTableData).toStrictEqual(expected);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import React, { useRef } from "react";
|
||||
import { reduce } from "lodash";
|
||||
import {
|
||||
useTable,
|
||||
usePagination,
|
||||
useBlockLayout,
|
||||
useResizeColumns,
|
||||
useRowSelect,
|
||||
Row,
|
||||
} from "react-table";
|
||||
import {
|
||||
TableWrapper,
|
||||
|
|
@ -12,7 +14,12 @@ import {
|
|||
TableHeaderInnerWrapper,
|
||||
} from "./TableStyledWrappers";
|
||||
import { ReactTableFilter } from "components/designSystems/appsmith/TableComponent/TableFilters";
|
||||
import { TableHeaderCell, renderEmptyRows } from "./TableUtilities";
|
||||
import {
|
||||
TableHeaderCell,
|
||||
renderEmptyRows,
|
||||
renderCheckBoxCell,
|
||||
renderCheckBoxHeaderCell,
|
||||
} from "./TableUtilities";
|
||||
import TableHeader from "./TableHeader";
|
||||
import { Classes } from "@blueprintjs/core";
|
||||
import {
|
||||
|
|
@ -47,6 +54,7 @@ interface TableProps {
|
|||
}) => void;
|
||||
pageNo: number;
|
||||
updatePageNo: (pageNo: number, event?: EventType) => void;
|
||||
multiRowSelection?: boolean;
|
||||
nextPageClick: () => void;
|
||||
prevPageClick: () => void;
|
||||
serverSidePaginationEnabled: boolean;
|
||||
|
|
@ -54,6 +62,10 @@ interface TableProps {
|
|||
selectedRowIndices: number[];
|
||||
disableDrag: () => void;
|
||||
enableDrag: () => void;
|
||||
toggleAllRowSelect: (
|
||||
isSelect: boolean,
|
||||
pageData: Row<Record<string, unknown>>[],
|
||||
) => void;
|
||||
triggerRowSelection: boolean;
|
||||
searchTableData: (searchKey: any) => void;
|
||||
filters?: ReactTableFilter[];
|
||||
|
|
@ -166,6 +178,29 @@ export function Table(props: TableProps) {
|
|||
const tableWrapperRef = useRef<HTMLDivElement | null>(null);
|
||||
const tableBodyRef = useRef<HTMLDivElement | null>(null);
|
||||
const tableHeaderWrapperRef = React.createRef<HTMLDivElement>();
|
||||
const rowSelectionState = React.useMemo(() => {
|
||||
// return : 0; no row selected | 1; all row selected | 2: some rows selected
|
||||
if (!props.multiRowSelection) return null;
|
||||
const selectedRowCount = reduce(
|
||||
subPage,
|
||||
(count, row) => {
|
||||
return selectedRowIndices.includes(row.index) ? count + 1 : count;
|
||||
},
|
||||
0,
|
||||
);
|
||||
const result =
|
||||
selectedRowCount === 0 ? 0 : selectedRowCount === subPage.length ? 1 : 2;
|
||||
return result;
|
||||
}, [selectedRowIndices, subPage]);
|
||||
const handleAllRowSelectClick = (
|
||||
e: React.MouseEvent<HTMLDivElement, MouseEvent>,
|
||||
) => {
|
||||
// if all / some rows are selected we remove selection on click
|
||||
// else select all rows
|
||||
props.toggleAllRowSelect(!Boolean(rowSelectionState), subPage);
|
||||
// loop over subPage rows and toggleRowSelected if required
|
||||
e.stopPropagation();
|
||||
};
|
||||
const isHeaderVisible =
|
||||
props.isVisibleSearch ||
|
||||
props.isVisibleFilters ||
|
||||
|
|
@ -250,30 +285,37 @@ export function Table(props: TableProps) {
|
|||
onMouseLeave={props.enableDrag}
|
||||
onMouseOver={props.disableDrag}
|
||||
>
|
||||
{headerGroups.map((headerGroup: any, index: number) => (
|
||||
<div
|
||||
{...headerGroup.getHeaderGroupProps()}
|
||||
className="tr"
|
||||
key={index}
|
||||
>
|
||||
{headerGroup.headers.map(
|
||||
(column: any, columnIndex: number) => {
|
||||
return (
|
||||
<TableHeaderCell
|
||||
column={column}
|
||||
columnIndex={columnIndex}
|
||||
columnName={column.Header}
|
||||
isAscOrder={column.isAscOrder}
|
||||
isHidden={column.isHidden}
|
||||
isResizingColumn={isResizingColumn.current}
|
||||
key={columnIndex}
|
||||
sortTableColumn={props.sortTableColumn}
|
||||
/>
|
||||
);
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{headerGroups.map((headerGroup: any, index: number) => {
|
||||
const headerRowProps = {
|
||||
...headerGroup.getHeaderGroupProps(),
|
||||
style: { display: "flex" },
|
||||
};
|
||||
return (
|
||||
<div {...headerRowProps} className="tr" key={index}>
|
||||
{props.multiRowSelection &&
|
||||
renderCheckBoxHeaderCell(
|
||||
handleAllRowSelectClick,
|
||||
rowSelectionState,
|
||||
)}
|
||||
{headerGroup.headers.map(
|
||||
(column: any, columnIndex: number) => {
|
||||
return (
|
||||
<TableHeaderCell
|
||||
column={column}
|
||||
columnIndex={columnIndex}
|
||||
columnName={column.Header}
|
||||
isAscOrder={column.isAscOrder}
|
||||
isHidden={column.isHidden}
|
||||
isResizingColumn={isResizingColumn.current}
|
||||
key={columnIndex}
|
||||
sortTableColumn={props.sortTableColumn}
|
||||
/>
|
||||
);
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{headerGroups.length === 0 &&
|
||||
renderEmptyRows(
|
||||
1,
|
||||
|
|
@ -281,6 +323,7 @@ export function Table(props: TableProps) {
|
|||
props.width,
|
||||
subPage,
|
||||
prepareRow,
|
||||
props.multiRowSelection,
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
|
|
@ -292,18 +335,17 @@ export function Table(props: TableProps) {
|
|||
>
|
||||
{subPage.map((row, rowIndex) => {
|
||||
prepareRow(row);
|
||||
const rowProps = {
|
||||
...row.getRowProps(),
|
||||
style: { display: "flex" },
|
||||
};
|
||||
const isRowSelected =
|
||||
row.index === selectedRowIndex ||
|
||||
selectedRowIndices.includes(row.index);
|
||||
return (
|
||||
<div
|
||||
{...row.getRowProps()}
|
||||
className={
|
||||
"tr" +
|
||||
`${
|
||||
row.index === selectedRowIndex ||
|
||||
selectedRowIndices.includes(row.index)
|
||||
? " selected-row"
|
||||
: ""
|
||||
}`
|
||||
}
|
||||
{...rowProps}
|
||||
className={"tr" + `${isRowSelected ? " selected-row" : ""}`}
|
||||
key={rowIndex}
|
||||
onClick={(e) => {
|
||||
row.toggleRowSelected();
|
||||
|
|
@ -311,6 +353,8 @@ export function Table(props: TableProps) {
|
|||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{props.multiRowSelection &&
|
||||
renderCheckBoxCell(isRowSelected)}
|
||||
{row.cells.map((cell, cellIndex) => {
|
||||
return (
|
||||
<div
|
||||
|
|
@ -334,6 +378,7 @@ export function Table(props: TableProps) {
|
|||
props.width,
|
||||
subPage,
|
||||
prepareRow,
|
||||
props.multiRowSelection,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,13 +10,14 @@ interface TableActionProps {
|
|||
title: string;
|
||||
children: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
titleColor?: string;
|
||||
}
|
||||
|
||||
export const TableIconWrapper = styled.div<{
|
||||
selected?: boolean;
|
||||
disabled?: boolean;
|
||||
titleColor?: string;
|
||||
}>`
|
||||
background: ${(props) => (props.selected ? Colors.Gallery : "transparent")};
|
||||
height: 38px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
@ -26,6 +27,8 @@ export const TableIconWrapper = styled.div<{
|
|||
color: ${(props) => (props.selected ? Colors.CODE_GRAY : Colors.GRAY)};
|
||||
.action-title {
|
||||
margin-left: 4px;
|
||||
white-space: nowrap;
|
||||
color: ${(props) => props.titleColor || Colors.GRAY};
|
||||
}
|
||||
position: relative;
|
||||
margin-left: 5px;
|
||||
|
|
@ -48,9 +51,10 @@ function TableAction(props: TableActionProps) {
|
|||
className={props.className}
|
||||
onClick={handleIconClick}
|
||||
selected={props.selected}
|
||||
titleColor={props.titleColor}
|
||||
>
|
||||
<IconWrapper
|
||||
color={props.selected ? Colors.CODE_GRAY : Colors.GRAY}
|
||||
color={props.titleColor ? props.titleColor : Colors.GRAY}
|
||||
height={20}
|
||||
width={20}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -184,7 +184,8 @@ function TableFilters(props: TableFilterProps) {
|
|||
selectMenu(selected);
|
||||
}}
|
||||
selected={selected}
|
||||
title="Filters"
|
||||
title={`Filters${hasAnyFilters ? ` (${filters.length})` : ""}`}
|
||||
titleColor={hasAnyFilters ? Colors.CODE_GRAY : Colors.GRAY}
|
||||
>
|
||||
<FilterIcon />
|
||||
</TableAction>
|
||||
|
|
|
|||
|
|
@ -49,9 +49,6 @@ export const TableWrapper = styled.div<{
|
|||
display: table;
|
||||
width: 100%;
|
||||
${hideScrollbar};
|
||||
.tr {
|
||||
width: 100%;
|
||||
}
|
||||
.thead,
|
||||
.tbody {
|
||||
overflow: hidden;
|
||||
|
|
@ -62,22 +59,15 @@ export const TableWrapper = styled.div<{
|
|||
width: 100%;
|
||||
overflow-y: auto;
|
||||
${hideScrollbar};
|
||||
.tr {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.tr {
|
||||
width: calc(100% - 8px);
|
||||
overflow: hidden;
|
||||
cursor: ${(props) => props.triggerRowSelection && "pointer"};
|
||||
background: ${Colors.WHITE};
|
||||
&.selected-row {
|
||||
& > .td > div {
|
||||
background: ${Colors.POLAR}!important;
|
||||
}
|
||||
background: ${Colors.POLAR}!important;
|
||||
background: ${Colors.OPAQ_BLUE}!important;
|
||||
&:hover {
|
||||
background: ${Colors.POLAR};
|
||||
background: ${Colors.OPAQ_BLUE};
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
|
|
@ -429,6 +419,46 @@ export const CellWrapper = styled.div<{
|
|||
}
|
||||
`;
|
||||
|
||||
export const CellCheckboxWrapper = styled(CellWrapper)<{ isChecked?: boolean }>`
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
background: ${Colors.WHITE};
|
||||
& > div {
|
||||
background: ${(props) => (props.isChecked ? Colors.DANUBE : "")};
|
||||
}
|
||||
|
||||
${(props) =>
|
||||
props.isChecked
|
||||
? `
|
||||
background: #FAFAFA;
|
||||
&:hover {
|
||||
background: ${Colors.OPAQ_BLUE};
|
||||
}
|
||||
`
|
||||
: `
|
||||
&:hover {
|
||||
& > div {
|
||||
background: ${Colors.Gallery};
|
||||
}
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
export const CellCheckbox = styled.div`
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
border: 0.5px solid ${Colors.GEYSER_LIGHT};
|
||||
position: relative;
|
||||
.th-svg {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: -1px;
|
||||
top: -1px;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const TableHeaderWrapper = styled.div<{
|
||||
serverSidePaginationEnabled: boolean;
|
||||
width: number;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import React, { useState } from "react";
|
|||
import { MenuItem, Classes, Button as BButton } from "@blueprintjs/core";
|
||||
import {
|
||||
CellWrapper,
|
||||
CellCheckboxWrapper,
|
||||
CellCheckbox,
|
||||
ActionWrapper,
|
||||
SortIconWrapper,
|
||||
DraggableHeaderWrapper,
|
||||
|
|
@ -196,20 +198,86 @@ function TableAction(props: {
|
|||
);
|
||||
}
|
||||
|
||||
function CheckBoxLineIcon() {
|
||||
return (
|
||||
<svg
|
||||
className="th-svg t--table-multiselect-header-half-check-svg"
|
||||
fill="none"
|
||||
height="15"
|
||||
width="15"
|
||||
>
|
||||
<path
|
||||
d="M11.183673404886235,7.5 H3.81632661819458 "
|
||||
stroke="white"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeOpacity="0.9"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function CheckBoxCheckIcon() {
|
||||
return (
|
||||
<svg className="th-svg" fill="none" height="15" width="15">
|
||||
<path
|
||||
d="M3.523326302644791,8.068102895600848 L5.7957131234862,10.340476082148596 L11.476673358442884,4.659524027768102 "
|
||||
stroke="white"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeOpacity="0.9"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export const renderCheckBoxCell = (isChecked: boolean) => (
|
||||
<CellCheckboxWrapper
|
||||
className="td t--table-multiselect"
|
||||
isChecked={isChecked}
|
||||
>
|
||||
<CellCheckbox>{isChecked && <CheckBoxCheckIcon />}</CellCheckbox>
|
||||
</CellCheckboxWrapper>
|
||||
);
|
||||
|
||||
export const renderCheckBoxHeaderCell = (
|
||||
onClick: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void,
|
||||
checkState: number | null,
|
||||
) => (
|
||||
<CellCheckboxWrapper
|
||||
className="th header-reorder t--table-multiselect-header"
|
||||
isChecked={!!checkState}
|
||||
onClick={onClick}
|
||||
role="columnheader"
|
||||
style={{ padding: "0px", justifyContent: "center" }}
|
||||
>
|
||||
<CellCheckbox>
|
||||
{checkState === 1 && <CheckBoxCheckIcon />}
|
||||
{checkState === 2 && <CheckBoxLineIcon />}
|
||||
</CellCheckbox>
|
||||
</CellCheckboxWrapper>
|
||||
);
|
||||
|
||||
export const renderEmptyRows = (
|
||||
rowCount: number,
|
||||
columns: any,
|
||||
tableWidth: number,
|
||||
page: any,
|
||||
prepareRow: any,
|
||||
multiRowSelection = false,
|
||||
) => {
|
||||
const rows: string[] = new Array(rowCount).fill("");
|
||||
if (page.length) {
|
||||
const row = page[0];
|
||||
return rows.map((item: string, index: number) => {
|
||||
prepareRow(row);
|
||||
const rowProps = {
|
||||
...row.getRowProps(),
|
||||
style: { display: "flex" },
|
||||
};
|
||||
return (
|
||||
<div {...row.getRowProps()} className="tr" key={index}>
|
||||
<div {...rowProps} className="tr" key={index}>
|
||||
{multiRowSelection && renderCheckBoxCell(false)}
|
||||
{row.cells.map((cell: any, cellIndex: number) => {
|
||||
return (
|
||||
<div {...cell.getCellProps()} className="td" key={cellIndex} />
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import {
|
|||
ReactTableColumnProps,
|
||||
ReactTableFilter,
|
||||
} from "components/designSystems/appsmith/TableComponent/Constants";
|
||||
import { Row } from "react-table";
|
||||
import Table from "components/designSystems/appsmith/TableComponent/Table";
|
||||
import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
|
||||
import { isEqual } from "lodash";
|
||||
|
|
@ -46,6 +47,8 @@ interface ReactTableComponentProps {
|
|||
disableDrag: (disable: boolean) => void;
|
||||
onRowClick: (rowData: Record<string, unknown>, rowIndex: number) => void;
|
||||
onCommandClick: (dynamicTrigger: string, onComplete: () => void) => void;
|
||||
selectAllRow: (pageData: Row<Record<string, unknown>>[]) => void;
|
||||
unSelectAllRow: (pageData: Row<Record<string, unknown>>[]) => void;
|
||||
updatePageNo: (pageNo: number, event?: EventType) => void;
|
||||
sortTableColumn: (column: string, asc: boolean) => void;
|
||||
nextPageClick: () => void;
|
||||
|
|
@ -91,6 +94,7 @@ function ReactTableComponent(props: ReactTableComponentProps) {
|
|||
isVisibleFilters,
|
||||
isVisiblePagination,
|
||||
isVisibleSearch,
|
||||
multiRowSelection,
|
||||
nextPageClick,
|
||||
onRowClick,
|
||||
pageNo,
|
||||
|
|
@ -98,12 +102,14 @@ function ReactTableComponent(props: ReactTableComponentProps) {
|
|||
prevPageClick,
|
||||
searchKey,
|
||||
searchTableData,
|
||||
selectAllRow,
|
||||
selectedRowIndex,
|
||||
selectedRowIndices,
|
||||
serverSidePaginationEnabled,
|
||||
sortTableColumn: _sortTableColumn,
|
||||
tableData,
|
||||
triggerRowSelection,
|
||||
unSelectAllRow,
|
||||
updateCompactMode,
|
||||
updatePageNo,
|
||||
widgetId,
|
||||
|
|
@ -222,6 +228,17 @@ function ReactTableComponent(props: ReactTableComponentProps) {
|
|||
onRowClick(row.original, row.index);
|
||||
};
|
||||
|
||||
const toggleAllRowSelect = (
|
||||
isSelect: boolean,
|
||||
pageData: Row<Record<string, unknown>>[],
|
||||
) => {
|
||||
if (isSelect) {
|
||||
selectAllRow(pageData);
|
||||
} else {
|
||||
unSelectAllRow(pageData);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Table
|
||||
applyFilter={applyFilter}
|
||||
|
|
@ -245,6 +262,7 @@ function ReactTableComponent(props: ReactTableComponentProps) {
|
|||
isVisibleFilters={isVisibleFilters}
|
||||
isVisiblePagination={isVisiblePagination}
|
||||
isVisibleSearch={isVisibleSearch}
|
||||
multiRowSelection={multiRowSelection}
|
||||
nextPageClick={nextPageClick}
|
||||
pageNo={pageNo - 1}
|
||||
pageSize={pageSize || 1}
|
||||
|
|
@ -256,6 +274,7 @@ function ReactTableComponent(props: ReactTableComponentProps) {
|
|||
selectedRowIndices={selectedRowIndices}
|
||||
serverSidePaginationEnabled={serverSidePaginationEnabled}
|
||||
sortTableColumn={sortTableColumn}
|
||||
toggleAllRowSelect={toggleAllRowSelect}
|
||||
triggerRowSelection={triggerRowSelection}
|
||||
updateCompactMode={updateCompactMode}
|
||||
updatePageNo={updatePageNo}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ const AccentColorMap: Record<ButtonStyleName, string> = {
|
|||
};
|
||||
|
||||
const ButtonWrapper = styled((props: ButtonStyleProps & IButtonProps) => (
|
||||
<Button {..._.omit(props, ["accent", "filled"])} />
|
||||
<Button {..._.omit(props, ["accent", "filled", "disabled"])} />
|
||||
))<ButtonStyleProps>`
|
||||
&&&& {
|
||||
${ButtonColorStyles};
|
||||
|
|
@ -131,7 +131,10 @@ type ButtonStyleProps = {
|
|||
|
||||
// To be used in any other part of the app
|
||||
export function BaseButton(props: IButtonProps & ButtonStyleProps) {
|
||||
return <ButtonWrapper {...props} />;
|
||||
const className = props.disabled
|
||||
? `${props.className} bp3-disabled`
|
||||
: props.className;
|
||||
return <ButtonWrapper {...props} className={className} />;
|
||||
}
|
||||
|
||||
BaseButton.defaultProps = {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,191 @@
|
|||
import React from "react";
|
||||
import { ComponentProps } from "components/designSystems/appsmith/BaseComponent";
|
||||
import styled from "styled-components";
|
||||
import { isUndefined } from "lodash";
|
||||
|
||||
const DividerWrapper = styled.div<{
|
||||
isHorizontal: boolean;
|
||||
thickness: number;
|
||||
showStartCap: boolean;
|
||||
showEndCap: boolean;
|
||||
}>`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
${(props) => {
|
||||
const paddingVal = props.thickness / 2;
|
||||
let padStyle: string;
|
||||
if (props.isHorizontal) {
|
||||
padStyle = `padding: 0px ${
|
||||
props.showEndCap ? paddingVal + "px" : "0px"
|
||||
} 0px ${props.showStartCap ? paddingVal + "px" : "0px"};`;
|
||||
} else {
|
||||
padStyle = `padding: ${
|
||||
props.showStartCap ? paddingVal + "px" : "0px"
|
||||
} 0px ${props.showEndCap ? paddingVal + "px" : "0px"} 0px;`;
|
||||
}
|
||||
return padStyle;
|
||||
}}
|
||||
`;
|
||||
const HorizontalDivider = styled.div<Partial<DividerComponentProps>>`
|
||||
height: 0px;
|
||||
width: 100%;
|
||||
border-top: ${(props) =>
|
||||
`${props.thickness || 1}px ${props.strokeStyle ||
|
||||
"solid"} ${props.dividerColor || "black"};`};
|
||||
`;
|
||||
const VerticalDivider = styled.div<Partial<DividerComponentProps>>`
|
||||
width: 0px;
|
||||
height: 100%;
|
||||
border-right: ${(props) =>
|
||||
`${props.thickness || 1}px ${props.strokeStyle ||
|
||||
"solid"} ${props.dividerColor || "black"};`};
|
||||
`;
|
||||
|
||||
const CapWrapper = styled.div<{
|
||||
isHorizontal: boolean;
|
||||
isStartCap: boolean;
|
||||
size: number;
|
||||
}>`
|
||||
position: absolute;
|
||||
height: ${(props) => props.size}px;
|
||||
width: ${(props) => props.size}px;
|
||||
${(props) =>
|
||||
props.isStartCap
|
||||
? props.isHorizontal
|
||||
? "left: 0px;"
|
||||
: "top: 0px;"
|
||||
: props.isHorizontal
|
||||
? "right: 0px;"
|
||||
: "bottom: 0px;"}
|
||||
${(props) => (props.isHorizontal ? "top" : "left")}: 50%;
|
||||
${(props) =>
|
||||
props.isHorizontal
|
||||
? "transform: translateY(-50%);"
|
||||
: "transform: translateX(-50%);"};
|
||||
|
||||
svg {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
&.arrow {
|
||||
${(
|
||||
props, // rotate icon according to its pos
|
||||
) =>
|
||||
props.isStartCap
|
||||
? props.isHorizontal
|
||||
? "transform: rotate(0deg);"
|
||||
: "transform: rotate(90deg);"
|
||||
: props.isHorizontal
|
||||
? "transform: rotate(180deg);"
|
||||
: "transform: rotate(270deg);"}
|
||||
|
||||
path {
|
||||
transform: translateX(-3px);
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
class DividerComponent extends React.Component<DividerComponentProps> {
|
||||
render() {
|
||||
const {
|
||||
capSide,
|
||||
capType,
|
||||
dividerColor,
|
||||
orientation,
|
||||
strokeStyle,
|
||||
thickness,
|
||||
} = this.props;
|
||||
const showStartCap =
|
||||
capType !== "nc" && (isUndefined(capSide) ? false : capSide <= 0);
|
||||
const showEndCap =
|
||||
capType !== "nc" && (isUndefined(capSide) ? false : capSide >= 0);
|
||||
|
||||
return (
|
||||
<DividerWrapper
|
||||
className="t--divider-widget"
|
||||
isHorizontal={orientation === "horizontal"}
|
||||
showEndCap={showEndCap}
|
||||
showStartCap={showStartCap}
|
||||
thickness={thickness || 1}
|
||||
>
|
||||
{orientation === "horizontal" ? (
|
||||
<HorizontalDivider
|
||||
data-testid="dividerHorizontal"
|
||||
dividerColor={dividerColor}
|
||||
strokeStyle={strokeStyle}
|
||||
thickness={thickness}
|
||||
/>
|
||||
) : (
|
||||
<VerticalDivider
|
||||
data-testid="dividerVertical"
|
||||
dividerColor={dividerColor}
|
||||
strokeStyle={strokeStyle}
|
||||
thickness={thickness}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showStartCap && this.renderCap(true)}
|
||||
{showEndCap && this.renderCap(false)}
|
||||
</DividerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
renderCap = (isStartCap: boolean) => {
|
||||
const { capType, dividerColor, orientation, thickness } = this.props;
|
||||
const isHorizontal = orientation === "horizontal";
|
||||
// size calculations
|
||||
const strokeSize = thickness || 1;
|
||||
const sizeMultiplier = capType === "dot" ? 3.5 : 5;
|
||||
const capSize = strokeSize * sizeMultiplier;
|
||||
const halfCapSize = capSize / 2;
|
||||
|
||||
return (
|
||||
<CapWrapper
|
||||
isHorizontal={isHorizontal}
|
||||
isStartCap={isStartCap}
|
||||
size={capSize}
|
||||
>
|
||||
{capType === "dot" ? (
|
||||
<svg>
|
||||
<circle
|
||||
cx={halfCapSize}
|
||||
cy={halfCapSize}
|
||||
fill={dividerColor}
|
||||
r={halfCapSize}
|
||||
/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="arrow" height="14" viewBox="0 0 8 14" width="8">
|
||||
<path
|
||||
d="M7 13L1 7L7 1"
|
||||
fill="none"
|
||||
stroke={dividerColor}
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</CapWrapper>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export interface DividerComponentProps extends ComponentProps {
|
||||
capType: string;
|
||||
capSide?: number;
|
||||
orientation: string;
|
||||
strokeStyle?: "solid" | "dashed" | "dotted";
|
||||
dividerColor?: string;
|
||||
thickness?: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default DividerComponent;
|
||||