feat: App Theming (#9714)

* fix style bugs

* fix select styles

* test: fix font size issue for cypress tests

* incorporate ashit feedback

* test: addresed review comments for cypress tests

* add analytics events

* height issue in view mode

* incorporate code review feedbacks

* incorporate code review feedbacks

* refactor: addressed review comments; removed border radius and box shadow for text widget; Updated migrations

* feat: Makes shadow and radius controls keyboard accessible (#11547)

* makes shadow and radius controls keyboard accessible

* removes unused imports

* moves options out of render method

* fix: changed the misnomer background property name to the relevant property name

* fix: border radius issue for the map widget

* address qa bugs

* address qa bugs

* fix ux of theming pane when widget is selected

* fix:
* added backgroundColor to the video widget
* restricted pop-over border radius to 0.375rem
* added box shadow for the input group for select widget

* fix: added delete icon in the delete theme modal

* address qa bugs

* change checkbox column size in config

* add js convertible to button color

* remove unused imports

* test: fixed jest tests

* fix primary color typo

* fix: migrations for the theming

* fix:
* Removed background color from MultiTreeSelect and TreeSelect component.
* grouped button's menu button pop over border radius restricting to 0.375rem.

* test: updated Dsl migration UT

* address qa bugs

* address qa bugs

* fix: address qa comments

* address qa bugs

* fix:
* migration issue;
* unit test cases;

* fix rating widget scroll issue

* fix youtube video border radius bug

* fix select widget

* fix select widgets styles

* address qa bugs

* merge conflicts

* makes the reset button keyboard accessible (#12134)

* -resolved merge conflicts

* address qa bugs

* fix: labelTextSize migration fixes

* refactor:
* made changes to the fontSizeUtils function
* fixed the issue related to unit tests

* fix button group widget

* remove unused imports

* fix: fixed the text size migration for the table widget

* refactor: addressed review comments for the table widget theming migration

* fix button group widget

* add init calls for view mode

* json form init theme changes

* fix: added migration for boxShadow, borderRadius and textSizes for table widget

* fix broken fields

* test: fixed unit tests

* wip

* inconsistancy fixes and schemaItem update in updateHook/fieldConfiguration

* feat: init json form migration theming

* json form primaryColor -> accentColor

* update table widget

* update table widget

* object field label styling

* fix: migration related to the JSON form

* fix: fixed labelTextSize migration for JSON form nested widgets

* property control nested stylesheet lookup

* JSONForm label styles form array items

* show label for checkbox field array item

* fix button group widget

* wip

* refactor: addressed table widget review comments

* refactor: addressed ashit review comments;
* added childStylesheet for widgets

* feat: Keyboard navigable Color Picker control (#11797)

* Makes ColorPicker keyboard accessible

* seperate out keyboard and mouse interactions

* fix issue with not focusing back to input

* Adds test for Color picker

* chore: added comment for the boxShadow property

* fix:
* added unit test cases for the widget and property utils
* resolved warning messages

* wip

* theme config update

* fix merge conflicts

* refactor: moved theming migration inside the migrations folder

* fix qa bugs

* fix jest test

* fix: unit test cases

* fix table column creation logic

* refactor: addressed review comments for migrations

* fix: Overriding margin and padding for custom render in the dropdown component (#12875)

* * fix for custom render padding and margin in ADS dropdown

* * fix for removing padding from normal render options

* refactor: moved the boxShadow condition to the variable

* fix qa bugs

* fix: migration QA callouts for audio recorder widget

* refactor: added updated comments for boxShadow migration for table widget

* fix theme binfings for JSONForm fields under Object

* fix table widget theming bug

* fix: addressed code review comments

* fix: unit test cases

* fix: qa migration callouts

* fix table widget theming bug

* fix JSONForm currency input dropdown not submit form

* Added new tests - AppThemingSpec

* fix qa bugs

* fix unit test

* fix JSONForm cellBorderWidth to have default value post migration

* fix unit test

* fix qa bugs

* remove unused imports

* fix qa bugs

* fix JSONForm input height issue

* fix qa bugs

* Updating Theming spec

* * dropdown color fixes (#13249)

* fix caching issue
;

* Fixed Theming tests

* fix tests

* fix tab widget tests

* fix: json form children level migration issue

* fix table widget tests

* Updated test

* updated tests

* updated test

* updated tests

* updated tests

* updated pageload

* fix cypress tests

* remove cypress created files

* fix color picker issues

* Failure fixes

* Fixed some more tests

* fix: cypress test failures

* fix tests

* remove consoles

* fix table tests

* fix qa bugs

* updating snapshots for AppPageLayout_spec as per new UI

* fix rating widget bug

* fix qa bugs

* fix:
* cypress failing tests
* Migration QA callouts
* Removed unused imports

* update constract check algo

* fix color contrast issue

* fix: cypress failure test cases

* update font sizes labels

* fix regression bugs

* fix:
* JSON form labelTextSize issue fix
* Updated comment for the fontSizeUtility function
* migrations issues related to table widget borderRadius and boxShadow

* fix: default labelTextSize issue for the Input and Select families

* fix regression bugs

* fix regression bugs

* PassingParams spec - added wait time

* fix: font family default value issue on JS toggle

* fix js toggle issue in text widget

* fix tests

* fix tests

* fix tests

* fix cypress tests

* fix regression bugs

* fix regression bugs

* fix:
* refactored table widget migration function as per review comments,
* added default value to the widget

* fix: failing unit test cases

* fix theming spec

* fix cypress tests

* test: fixed failed cypress test

* incorporate ashit feedback

* fix cypress tests

* fix: addressed review comments

* comment out table cypress test

* fix merge conflicts

* comment out color picker tests

Co-authored-by: Pawan Kumar <pawankumar@Pawans-MacBook-Pro.local>
Co-authored-by: keyurparalkar <keyur@appsmith.com>
Co-authored-by: Aswath K <aswath@appsmith.com>
Co-authored-by: Nayan <nayan@appsmith.com>
Co-authored-by: Ashit Rath <ashit@appsmith.com>
Co-authored-by: balajisoundar <balaji@appsmith.com>
Co-authored-by: albinAppsmith <87797149+albinAppsmith@users.noreply.github.com>
Co-authored-by: Aishwarya UR <aishwarya@appsmith.com>
Co-authored-by: apple <nandan@thinkify.io>
Co-authored-by: Parthvi Goswami <parthvigoswami@Parthvis-MacBook-Pro.local>
This commit is contained in:
Pawan Kumar 2022-05-04 15:15:57 +05:30 committed by GitHub
parent 8163becacc
commit 809a633306
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
375 changed files with 16014 additions and 4615 deletions

View File

@ -2,4 +2,4 @@
"MySQL":1,
"Mongo":1,
"Edition": 0
}
}

View File

@ -108,9 +108,9 @@
"TextLabelValue": "Test Text Label",
"TextLabelValueScrollable": "Test Text Label to check scroll feature",
"TextName": "TestTextBox",
"TextLabel": "Paragraph",
"TextBody": "Heading 2",
"TextHeading": "Heading 1",
"TextLabel": "S",
"TextBody": "L",
"TextHeading": "M",
"Datepickername": "Datepicker",
"DatepickerLable": "date",
"RichTextEditorName": "RichtextEditor",
@ -317,5 +317,5 @@
"image": "https://wallpaperaccess.com/full/812632.jpg",
"userName": "Toby William"
}
]
]
}

View File

@ -203,4 +203,4 @@
}
]
}
}
}

View File

@ -0,0 +1,210 @@
const commonlocators = require("../../../../locators/commonlocators.json");
const widgetLocators = require("../../../../locators/publishWidgetspage.json");
const widgetsPage = require("../../../../locators/Widgets.json");
const explorer = require("../../../../locators/explorerlocators.json");
const publish = require("../../../../locators/publishWidgetspage.json");
const dsl = require("../../../../fixtures/replay.json");
describe("App Theming funtionality", function() {
/**
* Test cases; Check:
* 1. If theme can be changed*
* 2. If the theme can edited*
* 4. If the save theme can be used.
* 5. If the theme can be deleled
*/
before(() => {
cy.addDsl(dsl);
});
it("checks if theme can be changed", function() {
cy.get(commonlocators.changeThemeBtn).click({ force: true });
// select a theme
cy.get(commonlocators.themeCard)
.last()
.click({ force: true });
// check for alert
cy.get(`${commonlocators.themeCard}`)
.last()
.siblings("div")
.first()
.invoke("text")
.then((text) => {
cy.get(commonlocators.toastmsg).contains(`Theme ${text} Applied`);
});
// check if color of canvas is same as theme bg color
cy.get(`${commonlocators.themeCard} > main`)
.last()
.invoke("css", "background-color")
.then((backgroudColor) => {
cy.get(commonlocators.canvas).should(
"have.css",
"background-color",
backgroudColor,
);
});
});
it("checks if theme can be edited", function() {
// drop a button widget and click on body
cy.get(explorer.addWidget).click();
cy.dragAndDropToCanvas("buttonwidget", { x: 300, y: 80 });
cy.wait(5000);
cy.get("#canvas-selection-0").click({ force: true });
//Click the back button
//cy.get(commonlocators.selectThemeBackBtn).click({ force: true });
//Click the border radius toggle
// change app border radius
cy.get(commonlocators.themeAppBorderRadiusBtn)
.eq(1)
.click({ force: true });
// check if border radius is changed on button
cy.get(`${commonlocators.themeAppBorderRadiusBtn} > div`)
.eq(1)
.invoke("css", "border-top-left-radius")
.then((borderRadius) => {
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"border-radius",
borderRadius,
);
// publish the app
// cy.PublishtheApp();
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"border-radius",
borderRadius,
);
});
cy.contains("Border").click({ force: true });
//Change the font
cy.get("span[name='expand-more']").then(($elem) => {
cy.get($elem).click({ force: true });
cy.wait(250);
cy.get(".ads-dropdown-options-wrapper div")
.children()
.eq(2)
.then(($childElem) => {
cy.get($childElem).click({ force: true });
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"font-family",
$childElem
.children()
.last()
.text(),
);
});
});
cy.contains("Font").click({ force: true });
//Change the shadow
cy.contains("App Box Shadow")
.siblings("div")
.children("span")
.last()
.then(($elem) => {
cy.get($elem).click({ force: true });
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"box-shadow",
$elem.css("box-shadow"),
);
});
cy.contains("Shadow").click({ force: true });
//Change the primary color:
cy.get(".border-2")
.first()
.click({ force: true });
cy.get(".t--colorpicker-v2-popover input").click({ force: true });
cy.get(widgetsPage.colorPickerV2Color)
.eq(-3)
.then(($elem) => {
cy.get($elem).click({ force: true });
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"background-color",
$elem.css("background-color"),
);
});
//Change the background color:
cy.get(".border-2")
.last()
.click({ force: true });
cy.get(".t--colorpicker-v2-popover input").click({ force: true });
cy.get(widgetsPage.colorPickerV2Color)
.first()
.then(($elem) => {
cy.get($elem).click({ force: true });
cy.get(commonlocators.canvas).should(
"have.css",
"background-color",
$elem.css("background-color"),
);
});
});
it("Checks if the theme can be saved", () => {
//Click on dropDown elipses
cy.get(".t--property-pane-sidebar .remixicon-icon")
.first()
.click({ force: true });
// .then(($elem) => {
// cy.get(`${$elem} button`).click({ force: true });
// })
cy.wait(1000);
//Click on save theme dropdown option
cy.contains("Save theme").click({ force: true });
cy.wait(200);
//Type the name of the theme:
cy.get("input[placeholder='My theme']").type("testtheme");
//Click on save theme button
cy.get("a[type='submit']").click({ force: true });
cy.wait(200);
//Click on change theme:
cy.get(commonlocators.changeThemeBtn).click({ force: true });
//Check if the saved theme is present under 'Yours Themes' section
cy.contains("Your Themes")
.siblings()
.find(".t--theme-card")
.parent()
.should("contain.text", "testtheme");
});
it("Checks if the theme can be deleted", () => {
cy.wait(300);
//Check if the saved theme is present under 'Yours Themes' section
cy.contains("Your Themes")
.siblings()
.find(".t--theme-card")
.parent()
.find("button")
.click({ force: true });
cy.contains("Delete").click({ force: true });
//check for delete alert
cy.wait(1000);
cy.get(commonlocators.toastMsg).contains("Theme testtheme Deleted");
});
});

View File

@ -0,0 +1,969 @@
const commonlocators = require("../../../../locators/commonlocators.json");
const widgetsPage = require("../../../../locators/Widgets.json");
const explorer = require("../../../../locators/explorerlocators.json");
const publish = require("../../../../locators/publishWidgetspage.json");
const dsl = require("../../../../fixtures/replay.json");
import { ObjectsRegistry } from "../../../../support/Objects/Registry";
let ee = ObjectsRegistry.EntityExplorer;
describe("App Theming funtionality", function() {
before(() => {
cy.addDsl(dsl);
});
let themesSection = (sectionName, themeName) =>
"//*[text()='" +
sectionName +
"']/following-sibling::div//*[text()='" +
themeName +
"']";
let applyTheme = (sectionName, themeName) =>
themesSection(sectionName, themeName) +
"/parent::div/following-sibling::div[contains(@class, 't--theme-card')]//div[text()='Apply Theme']";
let themesDeletebtn = (sectionName, themeName) =>
themesSection(sectionName, themeName) + "/following-sibling::button";
it("1. Checks if theme can be changed to one of the existing themes", function() {
cy.get(commonlocators.changeThemeBtn).click({ force: true });
// select a theme
cy.get(commonlocators.themeCard)
.last()
.click({ force: true });
// check for alert
cy.get(`${commonlocators.themeCard}`)
.last()
.siblings("div")
.first()
.invoke("text")
.then((text) => {
cy.get(commonlocators.toastmsg).contains(`Theme ${text} Applied`);
});
// check if color of canvas is same as theme bg color
cy.get(`${commonlocators.themeCard} > main`)
.last()
.invoke("css", "background-color")
.then((backgroudColor) => {
cy.get(commonlocators.canvas).should(
"have.css",
"background-color",
backgroudColor,
);
});
});
it("2. Checks if theme can be edited", function() {
cy.get(commonlocators.selectThemeBackBtn).click({ force: true });
// drop a button widget and click on body
cy.get(explorer.widgetSwitchId).click();
cy.dragAndDropToCanvas("buttonwidget", { x: 200, y: 200 }); //iconbuttonwidget
cy.assertPageSave();
cy.get("canvas")
.first(0)
.trigger("click", { force: true });
//Click the back button //Commenting below since expanded by default
//cy.get(commonlocators.selectThemeBackBtn).click({ force: true });
//Click the border radius toggle
// cy.contains("Border")
// .click({ force: true })
// .wait(500);
// change app border radius
cy.get(commonlocators.themeAppBorderRadiusBtn)
.eq(1)
.click({ force: true });
// check if border radius is changed on button
cy.get(`${commonlocators.themeAppBorderRadiusBtn} > div`)
.eq(1)
.invoke("css", "border-top-left-radius")
.then((borderRadius) => {
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"border-radius",
borderRadius,
);
// publish the app
// cy.PublishtheApp();
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"border-radius",
borderRadius,
);
});
//Change the color://Commenting below since expanded by default
//cy.contains("Color").click({ force: true });
//Change the primary color:
cy.get(".border-2")
.first()
.click({ force: true });
cy.wait(500);
cy.get(widgetsPage.colorPickerV2Popover)
.click({ force: true })
.click();
cy.get(widgetsPage.colorPickerV2Color)
.eq(-3)
.then(($elem) => {
cy.get($elem).click({ force: true });
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"background-color",
$elem.css("background-color"),
);
});
//Change the background color:
cy.get(".border-2")
.last()
.click({ force: true });
cy.wait(500);
cy.get(widgetsPage.colorPickerV2Popover)
.click({ force: true })
.click();
cy.get(widgetsPage.colorPickerV2Color)
.first()
.then(($elem) => {
cy.get($elem).click({ force: true });
cy.get(commonlocators.canvas).should(
"have.css",
"background-color",
$elem.css("background-color"),
);
});
//Change the shadow //Commenting below since expanded by default
//cy.contains("Shadow").click({ force: true });
cy.contains("App Box Shadow")
.siblings("div")
.children("span")
.last()
.then(($elem) => {
cy.get($elem).click({ force: true });
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"box-shadow",
$elem.css("box-shadow"),
);
});
//Change the font //Commenting below since expanded by default
//cy.contains("Font").click({ force: true });
cy.get("span[name='expand-more']").then(($elem) => {
cy.get($elem).click({ force: true });
cy.wait(250);
cy.get(".ads-dropdown-options-wrapper div")
.children()
.eq(2)
.then(($childElem) => {
cy.get($childElem).click({ force: true });
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"font-family",
$childElem
.children()
.last()
.text(),
);
});
});
});
it("3. Checks if the theme can be saved", () => {
//Click on dropDown elipses
cy.contains("Theme Properties")
.closest("div")
.siblings()
.first()
.find("button")
.click({ force: true });
// .then(($elem) => {
// cy.get(`${$elem} button`).click({ force: true });
// })
cy.wait(300);
//Click on save theme dropdown option
cy.contains("Save theme").click({ force: true });
cy.wait(200);
//Type the name of the theme:
cy.get("input[placeholder='My theme']").type("testtheme");
//Click on save theme button
cy.get("a[type='submit']").click({ force: true });
cy.wait(200);
cy.get(commonlocators.toastMsg).contains("Theme testtheme Saved");
});
it("4. Verify Save Theme after changing all properties & widgets conform to the selected theme", () => {
cy.get(explorer.widgetSwitchId).click();
cy.dragAndDropToCanvas("iconbuttonwidget", { x: 200, y: 300 });
cy.assertPageSave();
cy.get("canvas")
.first(0)
.trigger("click", { force: true });
//#region Change Font & verify widgets:
// cy.contains("Font")
// .click({ force: true })
// .wait(200);//Commenting below since expanded by default
cy.get("span[name='expand-more']").then(($elem) => {
cy.get($elem).click({ force: true });
cy.wait(250);
cy.get(".ads-dropdown-options-wrapper div")
.children()
.eq(4)
.then(($childElem) => {
cy.get($childElem).click({ force: true });
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"font-family",
$childElem
.children()
.last()
.text(),
);
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"font-family",
$childElem
.children()
.last()
.text(),
);
});
});
//#endregion
//#region Change Color & verify widgets:
//Change the primary color:
// cy.contains("Color")
// .click({ force: true })
// .wait(200);
cy.get(".border-2")
.first()
.click({ force: true });
cy.wait(500);
cy.get(widgetsPage.colorPickerV2Popover)
.click({ force: true })
.click();
cy.get(widgetsPage.colorPickerV2Color)
.eq(-15)
.then(($elem) => {
cy.get($elem).click({ force: true });
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"background-color",
$elem.css("background-color"),
);
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"background-color",
$elem.css("background-color"),
);
});
//Change the background color:
cy.get(".border-2")
.last()
.click({ force: true });
cy.wait(500);
cy.get(widgetsPage.colorPickerV2Popover)
.click({ force: true })
.click();
cy.get(widgetsPage.colorPickerV2Color)
.eq(23)
.then(($elem) => {
cy.get($elem).click({ force: true });
cy.get(commonlocators.canvas).should(
"have.css",
"background-color",
$elem.css("background-color"),
);
});
//#endregion
//#region Change Border radius & verify widgets
// cy.contains("Border")
// .click({ force: true })
// .wait(200);
cy.get(commonlocators.themeAppBorderRadiusBtn)
.eq(2)
.click({ force: true });
cy.get(`${commonlocators.themeAppBorderRadiusBtn} > div`)
.eq(2)
.invoke("css", "border-top-left-radius")
.then((borderRadius) => {
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"border-radius",
borderRadius,
);
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"border-radius",
borderRadius,
);
});
//#endregion
//#region Change the shadow & verify widgets
//cy.contains("Shadow").click({ force: true });
cy.contains("App Box Shadow")
.siblings("div")
.children("span")
.first()
.then(($elem) => {
cy.get($elem).click({ force: true });
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"box-shadow",
$elem.css("box-shadow"),
);
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"box-shadow",
$elem.css("box-shadow"),
);
});
//#endregion
//#region Click on dropDown elipses
cy.contains("Theme Properties")
.closest("div")
.siblings()
.first()
.find("button")
.click({ force: true });
cy.wait(300);
//#endregion
//Click on save theme dropdown option & close it
cy.contains("Save theme").click({ force: true });
cy.wait(200);
cy.xpath("//*[text()='Save Theme']/following-sibling::button").click();
//Click on save theme dropdown option & cancel it
cy.contains("Theme Properties")
.closest("div")
.siblings()
.first()
.find("button")
.click({ force: true });
cy.wait(300);
cy.contains("Save theme").click({ force: true });
cy.wait(200);
cy.xpath("//span[text()='Cancel']/parent::a").click();
//Click on save theme dropdown option, give duplicte name & save it
cy.contains("Theme Properties")
.closest("div")
.siblings()
.first()
.find("button")
.click({ force: true });
cy.wait(300);
cy.contains("Save theme").click({ force: true });
cy.wait(200);
//Type the name of the theme:
cy.get("input[placeholder='My theme']").type("testtheme");
cy.contains("Name must be unique");
cy.get("input[placeholder='My theme']")
.clear()
.type("VioletYellowTheme");
//Click on save theme button
cy.xpath("//span[text()='Save theme']/parent::a").click({ force: true });
cy.wait(200);
cy.get(commonlocators.toastMsg).contains("Theme VioletYellowTheme Saved");
});
it("5. Verify Themes exists under respective section when ChangeTheme button is cicked in properties with Apply Theme & Trash as applicable", () => {
//Click on change theme:
cy.get(commonlocators.changeThemeBtn).click({ force: true });
cy.xpath(applyTheme("Your Themes", "testtheme"))
.click({ force: true })
.wait(1000); //Changing to testtheme
cy.contains("Current Theme")
.click()
.parent()
.siblings()
.find(".t--theme-card > main > main")
.invoke("css", "background-color")
.then((backgroudColor) => {
expect(backgroudColor).to.eq("rgb(131, 24, 67)");
});
//Check if the saved theme is present under 'Yours Themes' section with Trash button
cy.xpath(applyTheme("Your Themes", "testtheme")).should("exist");
cy.xpath(themesDeletebtn("Your Themes", "testtheme")).should("exist");
cy.xpath(applyTheme("Your Themes", "VioletYellowTheme")).should("exist");
cy.xpath(themesDeletebtn("Your Themes", "VioletYellowTheme")).should(
"exist",
);
cy.xpath(applyTheme("Featured Themes", "Classic")).should("exist");
cy.xpath(themesDeletebtn("Featured Themes", "Classic")).should("not.exist");
cy.xpath(applyTheme("Featured Themes", "Modern")).should("exist");
cy.xpath(themesDeletebtn("Featured Themes", "Modern")).should("not.exist");
cy.xpath(applyTheme("Featured Themes", "Sharp")).should("exist");
cy.xpath(themesDeletebtn("Featured Themes", "Sharp")).should("not.exist");
cy.xpath(applyTheme("Featured Themes", "Rounded")).should("exist");
cy.xpath(themesDeletebtn("Featured Themes", "Rounded")).should("not.exist");
// cy.contains("Featured Themes")
// .siblings()
// .find(".t--theme-card")
// .siblings()
// .should("contain.text", "Rounded").siblings()
// .contains('Apply Theme');
});
it("6. Verify the custom theme can be deleted", () => {
//Check if the saved theme is present under 'Yours Themes' section
// cy.contains("Your Themes")
// .siblings()
// .find(".t--theme-card")
// .parent()
// .find("button").eq(0)
// .click({ force: true });
// cy.wait(200);
cy.xpath(themesDeletebtn("Your Themes", "testtheme"))
.click()
.wait(200);
cy.contains(
"Do you really want to delete this theme? This process cannot be undone.",
);
//Click on Delete theme trash icon & close it
cy.xpath("//*[text()='Are you sure?']/following-sibling::button").click();
cy.get(commonlocators.toastMsg).should("not.exist");
//Click on Delete theme trash icon & cancel it
cy.xpath(themesDeletebtn("Your Themes", "testtheme"))
.click()
.wait(200);
cy.xpath("//span[text()='Cancel']/parent::a").click();
cy.get(commonlocators.toastMsg).should("not.exist");
//Click on Delete theme trash icon & delete it
cy.xpath(themesDeletebtn("Your Themes", "testtheme"))
.click()
.wait(200);
cy.contains("Delete").click({ force: true });
//check for delete alert
cy.wait(500);
cy.get(commonlocators.toastMsg).contains("Theme testtheme Deleted");
cy.xpath(applyTheme("Your Themes", "testtheme")).should("not.exist");
});
it("7. Verify user able to change between saved theme & already existing Featured themes", () => {
//#region Modern
cy.xpath(applyTheme("Featured Themes", "Modern"))
.click({ force: true })
.wait(1000); //Changing to one of featured themes
cy.contains("Current Theme")
.click()
.parent()
.siblings()
.find(".t--theme-card > main > section > div > main")
.eq(0)
.invoke("css", "background-color")
.then((backgroudColor) => {
expect(backgroudColor).to.eq("rgb(85, 61, 233)");
});
cy.contains("Current Theme")
.click()
.parent()
.siblings()
.find(".t--theme-card > main > section > div > main")
.eq(1)
.invoke("css", "background-color")
.then((backgroudColor) => {
expect(backgroudColor).to.eq("rgb(246, 246, 246)");
});
//#endregion
//#region Classic
cy.xpath(applyTheme("Featured Themes", "Classic"))
.click({ force: true })
.wait(1000); //Changing to one of featured themes
cy.contains("Current Theme")
.click()
.parent()
.siblings()
.find(".t--theme-card > main > section > div > main")
.eq(0)
.invoke("css", "background-color")
.then((backgroudColor) => {
expect(backgroudColor).to.eq("rgb(22, 163, 74)");
});
cy.contains("Current Theme")
.click()
.parent()
.siblings()
.find(".t--theme-card > main > section > div > main")
.eq(1)
.invoke("css", "background-color")
.then((backgroudColor) => {
expect(backgroudColor).to.eq("rgb(246, 246, 246)");
});
//#endregion
//#region Sharp
cy.xpath(applyTheme("Featured Themes", "Sharp"))
.click({ force: true })
.wait(1000); //Changing to one of featured themes
cy.contains("Current Theme")
.click()
.parent()
.siblings()
.find(".t--theme-card > main > section > div > main")
.eq(0)
.invoke("css", "background-color")
.then((backgroudColor) => {
expect(backgroudColor).to.eq("rgb(59, 125, 221)");
});
cy.contains("Current Theme")
.click()
.parent()
.siblings()
.find(".t--theme-card > main > section > div > main")
.eq(1)
.invoke("css", "background-color")
.then((backgroudColor) => {
expect(backgroudColor).to.eq("rgb(255, 255, 255)");
});
//#endregion
//#region Rounded
cy.xpath(applyTheme("Featured Themes", "Rounded"))
.click({ force: true })
.wait(1000); //Changing to one of featured themes
cy.contains("Current Theme")
.click()
.parent()
.siblings()
.find(".t--theme-card > main > section > div > main")
.eq(0)
.invoke("css", "background-color")
.then((backgroudColor) => {
expect(backgroudColor).to.eq("rgb(222, 21, 147)");
});
cy.contains("Current Theme")
.click()
.parent()
.siblings()
.find(".t--theme-card > main > section > div > main")
.eq(1)
.invoke("css", "background-color")
.then((backgroudColor) => {
expect(backgroudColor).to.eq("rgb(246, 246, 246)");
});
//#endregion
//#region VioletYellowTheme
cy.xpath(applyTheme("Your Themes", "VioletYellowTheme"))
.click({ force: true })
.wait(1000); //Changing to created test theme
cy.contains("Current Theme")
.click()
.parent()
.siblings()
.find(".t--theme-card > main > section > div > main")
.eq(0)
.invoke("css", "background-color")
.then((backgroudColor) => {
expect(backgroudColor).to.eq("rgb(126, 34, 206)");
});
cy.contains("Current Theme")
.click()
.parent()
.siblings()
.find(".t--theme-card > main > section > div > main")
.eq(1)
.invoke("css", "background-color")
.then((backgroudColor) => {
expect(backgroudColor).to.eq("rgb(253, 224, 71)");
});
//#endregion
});
it("8. Verify widgets conform to the selected theme in Publish mode", () => {
cy.PublishtheApp();
cy.wait(2000); //for theme to settle
cy.get("body").should("have.css", "font-family", "Montserrat"); //Font
cy.xpath("//div[@id='root']//section/parent::div").should(
"have.css",
"background-color",
"rgb(253, 224, 71)",
); //Background Color
cy.get(widgetsPage.widgetBtn).should(
"have.css",
"background-color",
"rgb(126, 34, 206)",
); //Widget Color
cy.get(publish.iconWidgetBtn).should(
"have.css",
"background-color",
"rgb(126, 34, 206)",
); //Widget Color
cy.get(widgetsPage.widgetBtn).should("have.css", "border-radius", "24px"); //Border Radius
cy.get(publish.iconWidgetBtn).should("have.css", "border-radius", "24px"); //Border Radius
cy.get(widgetsPage.widgetBtn).should("have.css", "box-shadow", "none"); //Shadow
cy.get(publish.iconWidgetBtn).should("have.css", "box-shadow", "none"); //Shadow
//Verify Share button
cy.contains("Share").should(
"have.css",
"border-top-color",
"rgb(126, 34, 206)",
); //Color
cy.contains("Share")
.closest("div")
.should("have.css", "font-family", "Montserrat"); //Font
//Verify Edit App button
cy.contains("Edit App").should(
"have.css",
"background-color",
"rgb(126, 34, 206)",
); //Color
cy.contains("Edit App")
.closest("div")
.should("have.css", "font-family", "Montserrat"); //Font
cy.get(publish.backToEditor)
.click({ force: true })
.wait(3000);
});
it("9. Verify Adding new Individual widgets & it can change Color, Border radius, Shadow & can revert [Color/Border Radius] to already selected theme", () => {
cy.get(explorer.widgetSwitchId).click();
cy.dragAndDropToCanvas("buttonwidget", { x: 200, y: 400 }); //another button widget
cy.assertPageSave();
//Change Color & verify
cy.get(widgetsPage.colorPickerV2Popover)
.click({ force: true })
.click();
cy.get(widgetsPage.colorPickerV2Color)
.eq(35)
.then(($elem) => {
cy.get($elem).click({ force: true });
cy.get(widgetsPage.widgetBtn)
.eq(1)
.should(
"have.css",
"background-color",
$elem.css("background-color"), //rgb(134, 239, 172)
); //new widget with its own color
cy.get(widgetsPage.widgetBtn)
.eq(0)
.should("have.css", "background-color", "rgb(126, 34, 206)"); //old widgets still conforming to theme color
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"background-color",
"rgb(126, 34, 206)",
);
});
//Change Border & verify
cy.get(".t--button-tab-0px").click();
cy.get(".t--button-tab-0px")
.eq(0)
.invoke("css", "border-top-left-radius")
.then((borderRadius) => {
cy.get(widgetsPage.widgetBtn)
.eq(1)
.should(
"have.css",
"border-radius",
borderRadius, //0px
);
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"border-radius",
"24px",
);
cy.get(widgetsPage.widgetBtn)
.eq(0)
.should("have.css", "border-radius", "24px");
});
//Change Shadow & verify
cy.get(".t--button-tab-0.10px").click();
cy.get(".t--button-tab-0.10px div")
.eq(0)
.invoke("css", "box-shadow")
.then((boxshadow) => {
cy.get(widgetsPage.widgetBtn)
.eq(1)
.should(
"have.css",
"box-shadow",
boxshadow, //rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px
);
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"box-shadow",
"none",
);
cy.get(widgetsPage.widgetBtn)
.eq(0)
.should("have.css", "box-shadow", "none");
});
cy.assertPageSave();
//the new widget with changed styles is not showin in deploy mode - hence commenting below
// cy.PublishtheApp();
// //Verify Background color
// cy.get(widgetsPage.widgetBtn).eq(1).should(
// "have.css",
// "background-color",
// "rgb(134, 239, 172)", //rgb(134, 239, 172)
// ); //new widget with its own color
// cy.get(widgetsPage.widgetBtn).eq(0).should(
// "have.css",
// "background-color",
// "rgb(126, 34, 206)",
// ); //old widgets still conforming to theme color
// cy.get(widgetsPage.iconWidgetBtn).should(
// "have.css",
// "background-color",
// "rgb(126, 34, 206)",
// );
// //Verify Border radius
// cy.get(widgetsPage.widgetBtn).eq(1).should(
// "have.css",
// "border-radius",
// "0px"
// );
// cy.get(widgetsPage.iconWidgetBtn).should(
// "have.css",
// "border-radius",
// "24px",
// );
// cy.get(widgetsPage.widgetBtn).eq(0).should(
// "have.css",
// "border-radius",
// "24px",
// );
// //Verify Box shadow
// cy.get(widgetsPage.widgetBtn).eq(1).should(
// "have.css",
// "box-shadow",
// "rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px"
// );
// cy.get(widgetsPage.iconWidgetBtn).should(
// "have.css",
// "box-shadow",
// "none",
// );
// cy.get(widgetsPage.widgetBtn).eq(0).should(
// "have.css",
// "box-shadow",
// "none",
// );
// cy.get(publish.backToEditor).click({ force: true }).wait(1000);
//Resetting back to theme
ee.NavigateToSwitcher("explorer");
ee.expandCollapseEntity("WIDGETS"); //to expand widgets
ee.SelectEntityByName("Button2");
cy.get(".t--property-control-buttoncolor .reset-button").then(($elem) => {
$elem[0].removeAttribute("display: none");
$elem[0].click();
});
cy.get(widgetsPage.widgetBtn)
.eq(1)
.should("have.css", "background-color", "rgb(126, 34, 206)"); //verify widget reverted to theme color
cy.get(".t--property-control-borderradius .reset-button").then(($elem) => {
$elem[0].removeAttribute("display: none");
$elem[0].click();
});
cy.get(widgetsPage.widgetBtn)
.eq(1)
.should("have.css", "border-radius", "24px");
//the new widget with reverted styles is not showin in deploy mode - hence commenting below
// cy.PublishtheApp();
// cy.wait(2000)//for theme to settle
// cy.get('body').should('have.css', "font-family", "Montserrat")//Font
// cy.xpath("//div[@id='root']//section/parent::div").should('have.css', "background-color", "rgb(253, 224, 71)")//Background Color
// cy.get(widgetsPage.widgetBtn).eq(0).should("have.css", "background-color", "rgb(126, 34, 206)");//Widget Color
// cy.get(widgetsPage.widgetBtn).eq(1).should("have.css", "background-color", "rgb(126, 34, 206)");//Widget Color
// cy.get(publish.iconWidgetBtn).should("have.css", "background-color", "rgb(126, 34, 206)",);//Widget Color
// cy.get(widgetsPage.widgetBtn).eq(0).should("have.css", "border-radius", "24px",);//Border Radius
// cy.get(widgetsPage.widgetBtn).eq(1).should("have.css", "border-radius", "24px",);//Border Radius
// cy.get(publish.iconWidgetBtn).should("have.css", "border-radius", "24px",);//Border Radius
// cy.get(widgetsPage.widgetBtn).eq(0).should("have.css", "box-shadow", "none");//Shadow
// cy.get(widgetsPage.widgetBtn).eq(1).should("have.css", "box-shadow", "none");//Shadow
// cy.get(publish.iconWidgetBtn).should("have.css", "box-shadow", "none");//Shadow
// //Verify Share button
// cy.contains('Share').should("have.css", "border-top-color", "rgb(126, 34, 206)")//Color
// cy.contains('Share').closest('div').should("have.css", "font-family", "Montserrat")//Font
// //Verify Edit App button
// cy.contains('Edit App').should("have.css", "background-color", "rgb(126, 34, 206)")//Color
// cy.contains('Edit App').closest('div').should("have.css", "font-family", "Montserrat")//Font
// cy.get(publish.backToEditor).click({ force: true }).wait(1000);
});
it("10. Verify Chainging theme should not affect Individual widgets with changed Color, Border radius, Shadow & can revert to newly selected theme", () => {
cy.get("canvas")
.first(0)
.trigger("click", { force: true });
cy.get(commonlocators.changeThemeBtn).click({ force: true });
//Changing to one of featured themes & then changing individual widget properties
cy.xpath(applyTheme("Featured Themes", "Rounded"))
.click({ force: true })
.wait(1000);
//Change individual widget properties for Button1
ee.SelectEntityByName("Button1");
//Change Color & verify
cy.get(widgetsPage.colorPickerV2Popover)
.click({ force: true })
.click();
cy.get(widgetsPage.colorPickerV2Color)
.eq(17)
.then(($elem) => {
cy.get($elem).click({ force: true });
cy.get(widgetsPage.widgetBtn)
.eq(0)
.should(
"have.css",
"background-color",
$elem.css("background-color"), //rgb(134, 239, 172)
); //new widget with its own color
cy.get(widgetsPage.widgetBtn)
.eq(1)
.should("have.css", "background-color", "rgb(222, 21, 147)"); //old widgets still conforming to theme color
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"background-color",
"rgb(222, 21, 147)",
);
});
//Change Border & verify
cy.get(".t--button-tab-0\\.375rem")
.click()
.wait(500);
cy.get(".t--button-tab-0\\.375rem div")
.eq(0)
.invoke("css", "border-top-left-radius")
.then((borderRadius) => {
cy.get(widgetsPage.widgetBtn)
.eq(0)
.should(
"have.css",
"border-radius",
borderRadius, //6px
);
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"border-radius",
"24px",
);
cy.get(widgetsPage.widgetBtn)
.eq(1)
.should("have.css", "border-radius", "24px");
});
//Change Shadow & verify
cy.get(".t--button-tab-0.1px")
.click()
.wait(500);
cy.get(".t--button-tab-0.1px div")
.invoke("css", "box-shadow")
.then((boxshadow) => {
cy.get(widgetsPage.widgetBtn)
.eq(0)
.should(
"have.css",
"box-shadow",
boxshadow, //rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px
);
cy.get(widgetsPage.iconWidgetBtn).should(
"have.css",
"box-shadow",
"none",
);
cy.get(widgetsPage.widgetBtn)
.eq(1)
.should(
"have.css",
"box-shadow",
//same value as previous box shadow selection
//since revertion is not possible for box shadow - hence this widget maintains the same value
"rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px",
);
});
cy.assertPageSave();
//Add deploy mode verification here also!
});
});

View File

@ -72,7 +72,9 @@ describe("Fork application across orgs", function() {
}
cy.PublishtheApp();
cy.get(homePage.shareButton).click();
cy.get("button:contains('Share')")
.first()
.click({ force: true });
cy.enablePublicAccess();
cy.url().then((url) => {
@ -81,15 +83,19 @@ describe("Fork application across orgs", function() {
cy.get(homePage.signOutIcon).click();
cy.visit(forkableAppUrl);
cy.get(applicationLocators.forkButton).click();
cy.get(applicationLocators.forkButton)
.first()
.click({ force: true });
cy.get(loginPageLocators.signupLink).click();
cy.generateUUID().then((uid) => {
cy.get(signupPageLocators.username).type(`${uid}@appsmith.com`);
cy.get(signupPageLocators.password).type(uid);
cy.get(signupPageLocators.submitBtn).click();
cy.wait(1000);
cy.wait(10000);
cy.get(applicationLocators.forkButton)
.first()
.click({ force: true });
cy.get(homePage.forkAppOrgButton).should("be.visible");
});
});

View File

@ -56,8 +56,6 @@ describe("Binding the API with pageOnLoad and input Widgets", function() {
.last()
.invoke("attr", "value")
.should("contain", "23");
cy.get(publish.backToEditor)
.first()
.click();
cy.get(publish.backToEditor).click();
});
});

View File

@ -52,7 +52,9 @@ describe("Addwidget from Query and bind with other widgets", function() {
cy.url().then((url) => {
currentUrl = url;
cy.log("Published url is: " + currentUrl);
cy.get(publish.backToEditor).click();
cy.get(publish.backToEditor)
.first()
.click();
cy.wait(2000);
cy.visit(currentUrl);
cy.wait("@getPagesForViewApp").should(

View File

@ -27,7 +27,7 @@ describe("Binding the input Widget with tab Widget", function() {
cy.get(publish.tabWidget)
.contains("Tab 2")
.click({ force: true })
.should("be.selected");
.should("have.class", "is-selected");
cy.get(publish.inputWidget + " " + "input")
.first()
@ -36,7 +36,7 @@ describe("Binding the input Widget with tab Widget", function() {
cy.get(publish.tabWidget)
.contains("Tab 1")
.click({ force: true })
.should("be.selected");
.should("have.class", "is-selected");
cy.get(publish.inputWidget + " " + "input")
.first()

View File

@ -33,9 +33,7 @@ describe("Binding the button Widgets and validating NavigateTo Page functionalit
cy.wait(500);
cy.get(publish.buttonWidget).should("not.exist");
cy.go("back");
cy.get(publish.backToEditor)
.first()
.click();
cy.get(publish.backToEditor).click();
cy.wait("@getPage").should(
"have.nested.property",
"response.body.responseMeta.status",

View File

@ -41,9 +41,9 @@ describe("Table Widget property pane feature validation", function() {
.click({ force: true });
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000);
cy.selectTextSize("Heading 1");
cy.selectTxtSize("XL");
cy.readTabledataValidateCSS("0", "0", "font-size", "24px");
cy.readTabledataValidateCSS("0", "0", "font-size", "30px");
});
it("Table widget toggle test for text size", function() {

View File

@ -43,9 +43,7 @@ describe("Text-Table Binding Functionality", function() {
});
});
it("Text-Table Binding Functionality For Email", function() {
cy.get(publish.backToEditor)
.first()
.click();
cy.get(publish.backToEditor).click();
cy.isSelectRow(2);
cy.openPropertyPane("textwidget");
cy.testJsontext("text", "{{Table1.selectedRow.email}}");
@ -71,9 +69,7 @@ describe("Text-Table Binding Functionality", function() {
});
});
it("Text-Table Binding Functionality For Total Length", function() {
cy.get(publish.backToEditor)
.first()
.click();
cy.get(publish.backToEditor).click();
cy.openPropertyPane("textwidget");
cy.testJsontext("text", "{{Table1.pageSize}}");
cy.get(commonlocators.TableRow)
@ -97,9 +93,7 @@ describe("Text-Table Binding Functionality", function() {
});
});
it("Table Widget Functionality To Verify Default Row Selection is working", function() {
cy.get(publish.backToEditor)
.first()
.click();
cy.get(publish.backToEditor).click();
cy.openPropertyPane("tablewidget");
cy.testJsontext("defaultselectedrow", "2");
cy.wait("@updateLayout");
@ -118,9 +112,7 @@ describe("Text-Table Binding Functionality", function() {
});
});
it("Text-Table Binding Functionality For Username", function() {
cy.get(publish.backToEditor)
.first()
.click();
cy.get(publish.backToEditor).click();
/**
* @param(Index) Provide index value to select the row.
*/

View File

@ -62,9 +62,7 @@ describe("Binding the multiple input Widget", function() {
cy.xpath(testdata.input2)
.invoke("attr", "value")
.should("contain", testdata.defaultdata);
cy.get(publish.backToEditor)
.first()
.click();
cy.get(publish.backToEditor).click();
});
it("4. Binding third input widget with first input widget and validating", function() {

View File

@ -501,7 +501,7 @@ describe("Migration Validate", function() {
.first()
.invoke("attr", "value")
.should("contain", "#FFC13D");
cy.get(widgetsPage.selectedTextSize).should("have.text", "24px");
cy.validateCodeEditorContent(".t--property-control-textsize", "1.5rem");
});
// it("2. Add dsl and Validate Migration on pageload", function () {

View File

@ -23,7 +23,9 @@ describe("Statbox Widget Functionality", function() {
// changing the background color of statbox and verying it
cy.get(".t--property-pane-section-general").then(() => {
cy.get(".bp3-input-group")
.first()
.clear()
.wait(400)
.type("#FFC13D");
cy.get(".bp3-input").should("have.value", "#FFC13D");
});

View File

@ -11,18 +11,13 @@ describe("Table Widget property pane feature validation", function() {
// Open property pane
cy.openPropertyPane("tablewidget");
// Click on text color input field
cy.get(widgetsPage.textColor)
.first()
.click({ force: true });
// Select green color
cy.get(widgetsPage.greenColor)
.last()
.click();
cy.selectColor("textcolor");
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);
cy.wait("@updateLayout");
// Verify the text color is green
cy.readTabledataValidateCSS("1", "0", "color", "rgb(3, 179, 101)");
cy.readTabledataValidateCSS("1", "0", "color", "rgb(126, 34, 206)");
// Change the text color and enter purple in input field
cy.get(widgetsPage.textColor)
.scrollIntoView()
@ -32,16 +27,11 @@ describe("Table Widget property pane feature validation", function() {
// Verify the text color is purple
cy.readTabledataValidateCSS("1", "0", "color", "rgb(128, 0, 128)");
// Click on cell background color
cy.get(`${widgetsPage.cellBackground} input`)
.first()
.scrollIntoView()
.click({ force: true });
cy.selectColor("cellbackgroundcolor");
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);
// select the green color
cy.get(widgetsPage.greenColor)
.last()
.click();
cy.wait("@updateLayout");
cy.assertPageSave();
cy.PublishtheApp();
@ -52,7 +42,7 @@ describe("Table Widget property pane feature validation", function() {
"1",
"1",
"background-color",
"rgb(3, 179, 101)",
"rgb(126, 34, 206)",
);
cy.get(publish.backToEditor).click();
cy.openPropertyPane("tablewidget");

View File

@ -139,15 +139,15 @@ describe("Table Widget property pane feature validation", function() {
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000);
// Select Heading 1 text size
cy.selectTextSize("Heading 1");
cy.selectTxtSize("L");
// Verify the font size is 24px
cy.readTabledataValidateCSS("0", "0", "font-size", "24px");
cy.readTabledataValidateCSS("0", "0", "font-size", "20px");
// close propert pane
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000);
// Verify the font size is 24px
cy.readTabledataValidateCSS("0", "0", "font-size", "24px");
cy.readTabledataValidateCSS("0", "0", "font-size", "20px");
});
it("8. Test to validate open new tab icon shows when URL type data validate link text ", function() {

View File

@ -273,41 +273,34 @@ describe("Table Widget property pane feature validation", function() {
cy.readTabledataValidateCSS("0", "0", "align-items", "flex-end", true);
});
it("11. Test to validate text color and text background", function() {
cy.get(widgetsPage.textColor)
.first()
.click({ force: true });
// Changing text color to GREEN and validate
cy.get(widgetsPage.greenColor)
.last()
.click();
it("Test to validate text color and text background", function() {
cy.openPropertyPane("tablewidget");
// Changing text color to rgb(126, 34, 206) and validate
cy.selectColor("textcolor");
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(5000);
cy.wait("@updateLayout");
cy.readTabledataValidateCSS("1", "0", "color", "rgb(3, 179, 101)", true);
cy.readTabledataValidateCSS("1", "0", "color", "rgb(126, 34, 206)");
// Changing text color to PURPLE and validate using JS
cy.get(widgetsPage.toggleJsColor).click();
cy.testCodeMirrorLast("purple");
cy.wait("@updateLayout");
cy.readTabledataValidateCSS("1", "0", "color", "rgb(128, 0, 128)", true);
// Changing Cell backgroud color to GREEN and validate
cy.get(widgetsPage.backgroundColor)
.first()
.click({ force: true });
cy.get(widgetsPage.greenColor)
.last()
.click();
cy.wait("@updateLayout");
cy.readTabledataValidateCSS("1", "0", "color", "rgb(128, 0, 128)");
// Changing Cell backgroud color to rgb(126, 34, 206) and validate
cy.selectColor("cellbackground");
cy.readTabledataValidateCSS(
"1",
"0",
"0",
"background",
"rgb(3, 179, 101) none repeat scroll 0% 0% / auto padding-box border-box",
"rgb(126, 34, 206) none repeat scroll 0% 0% / auto padding-box border-box",
true,
);
// Changing Cell backgroud color to PURPLE and validate using JS
cy.get(widgetsPage.toggleJsBcgColor).click();
cy.testCodeMirrorLast("purple");
cy.updateCodeInput(".t--property-control-cellbackground", "purple");
cy.wait("@updateLayout");
cy.readTabledataValidateCSS(
"0",
@ -343,94 +336,94 @@ describe("Table Widget property pane feature validation", function() {
cy.get(widgetsPage.selectedRow).should(
"have.css",
"background-color",
"rgb(236, 249, 243)",
"rgb(224, 251, 234)",
);
cy.get(publish.backToEditor).click();
});
it("14. Verify table column type button with button variant", function() {
// Open property pane
cy.openPropertyPane("tablewidget");
// Add new column in the table with name "CustomColumn"
cy.addColumn("CustomColumn");
// it("14. Verify table column type button with button variant", function() {
// // Open property pane
// cy.openPropertyPane("tablewidget");
// // Add new column in the table with name "CustomColumn"
// cy.addColumn("CustomColumn");
cy.tableColumnDataValidation("customColumn2"); //To be updated later
// cy.tableColumnDataValidation("customColumn2"); //To be updated later
cy.editColumn("customColumn2");
cy.changeColumnType("Button");
// default selected opts
cy.get(commonlocators.tableButtonVariant + " span[type='p1']").should(
"have.text",
"Primary",
);
cy.getTableDataSelector("1", "6").then((selector) => {
cy.get(selector + " button").should(
"have.css",
"background-color",
"rgb(3, 179, 101)",
);
cy.get(selector + " button > span").should(
"have.css",
"color",
"rgb(255, 255, 255)",
);
});
cy.selectDropdownValue(commonlocators.tableButtonVariant, "Secondary");
cy.get(commonlocators.tableButtonVariant + " span[type='p1']").should(
"have.text",
"Secondary",
);
cy.getTableDataSelector("1", "6").then((selector) => {
cy.get(selector + " button").should(
"have.css",
"background-color",
"rgba(0, 0, 0, 0)",
);
cy.get(selector + " button > span").should(
"have.css",
"color",
"rgb(3, 179, 101)",
);
cy.get(selector + " button").should(
"have.css",
"border",
"1px solid rgb(3, 179, 101)",
);
});
cy.selectDropdownValue(commonlocators.tableButtonVariant, "Tertiary");
cy.get(commonlocators.tableButtonVariant + " span[type='p1']").should(
"have.text",
"Tertiary",
);
cy.getTableDataSelector("1", "6").then((selector) => {
cy.get(selector + " button").should(
"have.css",
"background-color",
"rgba(0, 0, 0, 0)",
);
cy.get(selector + " button > span").should(
"have.css",
"color",
"rgb(3, 179, 101)",
);
cy.get(selector + " button").should(
"have.css",
"border",
"0px none rgb(24, 32, 38)",
);
});
cy.closePropertyPane();
});
// cy.editColumn("customColumn2");
// cy.changeColumnType("Button");
// // default selected opts
// cy.get(commonlocators.tableButtonVariant + " span[type='p1']").should(
// "have.text",
// "Primary",
// );
// cy.getTableDataSelector("1", "6").then((selector) => {
// cy.get(selector + " button").should(
// "have.css",
// "background-color",
// "rgb(22, 163, 74)",
// );
// cy.get(selector + " button > span").should(
// "have.css",
// "color",
// "rgb(255, 255, 255)",
// );
// });
// cy.selectDropdownValue(commonlocators.tableButtonVariant, "Secondary");
// cy.get(commonlocators.tableButtonVariant + " span[type='p1']").should(
// "have.text",
// "Secondary",
// );
// cy.getTableDataSelector("1", "6").then((selector) => {
// cy.get(selector + " button").should(
// "have.css",
// "background-color",
// "rgba(0, 0, 0, 0)",
// );
// cy.get(selector + " button > span").should(
// "have.css",
// "color",
// "rgb(22, 163, 74)",
// );
// cy.get(selector + " button").should(
// "have.css",
// "border",
// `1px solid rgb(22, 163, 74)`,
// );
// });
// cy.selectDropdownValue(commonlocators.tableButtonVariant, "Tertiary");
// cy.get(commonlocators.tableButtonVariant + " span[type='p1']").should(
// "have.text",
// "Tertiary",
// );
// cy.getTableDataSelector("1", "6").then((selector) => {
// cy.get(selector + " button").should(
// "have.css",
// "background-color",
// "rgba(0, 0, 0, 0)",
// );
// cy.get(selector + " button > span").should(
// "have.css",
// "color",
// "rgb(22, 163, 74)",
// );
// cy.get(selector + " button").should(
// "have.css",
// "border",
// "0px none rgb(24, 32, 38)",
// );
// });
// cy.closePropertyPane();
// });
it("15. Table-Delete Verification", function() {
// Open property pane
cy.openPropertyPane("tablewidget");
// Delete the Table widget
cy.deleteWidget(widgetsPage.tableWidget);
cy.PublishtheApp();
// Verify the Table widget is deleted
cy.get(widgetsPage.tableWidget).should("not.exist");
});
// it("15. Table-Delete Verification", function() {
// // Open property pane
// cy.openPropertyPane("tablewidget");
// // Delete the Table widget
// cy.deleteWidget(widgetsPage.tableWidget);
// cy.PublishtheApp();
// // Verify the Table widget is deleted
// cy.get(widgetsPage.tableWidget).should("not.exist");
// });
afterEach(() => {
// put your clean up code if any

View File

@ -292,6 +292,8 @@ describe("Table Widget property pane feature validation", function() {
//cy.closePropertyPane();
cy.closePropertyPane();
// disable menu item 3
//cy.openPropertyPane("tablewidget");
@ -352,14 +354,15 @@ describe("Table Widget property pane feature validation", function() {
cy.get(".t--property-pane-back-btn").click({ force: true });
});
it("9. Table widget test on button when transparent", () => {
it("8. Table widget test on button when transparent", () => {
cy.openPropertyPane("tablewidget");
// Open column details of "id".
cy.editColumn("id");
// Changing column "Button" color to transparent
cy.get(widgetsPage.buttonColor).click({ force: true });
cy.xpath(widgetsPage.transparent).click();
cy.wait(2000);
cy.get(widgetsPage.transparent).click({ force: true });
cy.get(".td[data-colindex=5][data-rowindex=0] .bp3-button").should(
"have.css",
"background-color",

View File

@ -134,7 +134,6 @@ describe("Table Widget Functionality", function() {
it("Table Widget Functionality To Verify The Visiblity mode functionality", function() {
cy.get(publish.backToEditor)
.first()
.click();
cy.isSelectRow(1);
cy.readTabledataPublish("1", "3").then(tabData => {

View File

@ -52,7 +52,6 @@ describe("Table Widget", function() {
});
cy.get(publish.backToEditor)
.first()
.click()
.wait(1000);
cy.wait(30000);

View File

@ -52,7 +52,7 @@ describe("Text Widget color/font/alignment Functionality", function() {
cy.PublishtheApp();
cy.get(commonlocators.headingTextStyle)
.should("have.text", this.data.TextLabelValueScrollable)
.should("have.css", "font-size", "24px");
.should("have.css", "font-size", "16px");
cy.get(publishPage.backToEditor).click({ force: true });
});
@ -74,31 +74,39 @@ describe("Text Widget color/font/alignment Functionality", function() {
cy.get(widgetsPage.textColor)
.first()
.click({ force: true });
cy.get(widgetsPage.greenColor)
.last()
.click();
cy.selectColor("textcolor");
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);
cy.wait("@updateLayout");
cy.readTextDataValidateCSS("color", "rgb(3, 179, 101)");
cy.readTextDataValidateCSS("color", "rgb(126, 34, 206)");
cy.get(widgetsPage.textColor)
.clear({ force: true })
.type("purple", { force: true });
cy.wait("@updateLayout");
cy.readTextDataValidateCSS("color", "rgb(128, 0, 128)");
//Checks the cell background with color picker
cy.get(`${widgetsPage.cellBackground} input`)
.first()
.click({ force: true });
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);
cy.get(widgetsPage.greenColor)
.last()
.click();
cy.selectColor("cellbackgroundcolor");
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);
cy.wait("@updateLayout");
cy.PublishtheApp();
cy.get(publishPage.backToEditor).click({ force: true });
cy.get(`${widgetsPage.textWidget} .bp3-ui-text`).should(
"have.css",
"background-color",
"rgb(126, 34, 206)",
);
//Toggle JS check with cell background:
cy.get(widgetsPage.cellBackgroundToggle).click({ force: true });
cy.updateCodeInput(widgetsPage.cellBackground, "purple");
cy.wait("@updateLayout");
cy.readTextDataValidateCSS("color", "rgb(128, 0, 128)");
});
it("Test to validate text alignment", function() {
@ -134,20 +142,13 @@ describe("Text Widget color/font/alignment Functionality", function() {
});
it("Test border width, color and verity", function() {
cy.testJsontext("borderwidth", "10");
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}'] div`,
)
.should("have.css", "border-width")
.and("eq", "10px");
cy.get(widgetsPage.borderColorPickerNew)
.first()
.click({ force: true });
cy.xpath(widgetsPage.yellowColor).click();
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}'] div`,
)
.should("have.css", "border-color")
.and("eq", "rgb(255, 193, 61)");
cy.wait("@updateLayout");
cy.get(`${widgetsPage.textWidget} .t--text-widget-container`).should(
"have.css",
"border-width",
"10px",
);
cy.selectColor("bordercolor");
cy.readTextDataValidateCSS("border-color", "rgb(229, 231, 235)");
});
});

View File

@ -33,7 +33,7 @@ describe("Text Widget Functionality", function() {
cy.PublishtheApp();
cy.get(commonlocators.headingTextStyle)
.should("have.text", this.data.TextLabelValue)
.should("have.css", "font-size", "24px");
.should("have.css", "font-size", "16px");
});
it("Text Email Parsing Validation", function() {
@ -70,7 +70,7 @@ describe("Text Widget Functionality", function() {
cy.PublishtheApp();
cy.get(commonlocators.bodyTextStyle)
.should("have.text", this.data.TextLabelValue)
.should("have.css", "font-size", "18px");
.should("have.css", "font-size", "20px");
});
it("Text widget depends on itself", function() {

View File

@ -177,7 +177,7 @@ describe("Select Widget Functionality", function() {
}`,
);
cy.PublishtheApp();
cy.get(".bp3-button")
cy.get(".bp3-button.select-button")
.eq(0)
.should("be.visible")
.click({ force: true });

View File

@ -32,9 +32,9 @@ describe("Select Widget Functionality", function() {
cy.get(".bp3-disabled").should("be.visible");
cy.get(widgetsPage.disable).scrollIntoView({ force: true });
cy.get(widgetsPage.selectWidgetDisabled).click({ force: true });
cy.get(".bp3-button").should("be.visible");
cy.get(".t--widget-selectwidget .bp3-button").should("be.visible");
cy.PublishtheApp();
cy.get(".bp3-button")
cy.get(".t--widget-selectwidget .bp3-button")
.should("be.visible")
.click({ force: true });
cy.get(commonlocators.singleSelectActiveMenuItem).should(

View File

@ -12,8 +12,7 @@ describe("Entity explorer Drag and Drop widgets testcases", function() {
it("Drag and drop form widget and validate", function() {
cy.log("Login Successful");
cy.reload(); // To remove the rename tooltip
cy.wait(40000);
cy.get(explorer.addWidget).click();
cy.get(explorer.addWidget).click({ force: true });
cy.get(commonlocators.entityExplorersearch).should("be.visible");
cy.get(commonlocators.entityExplorersearch)
.clear()
@ -33,15 +32,10 @@ describe("Entity explorer Drag and Drop widgets testcases", function() {
/**
* @param{Text} Random Colour
*/
cy.get(widgetsPage.backgroundcolorPickerNew)
.first()
.click({ force: true });
cy.get(widgetsPage.greenColor)
.last()
.click();
cy.selectColor("backgroundcolor");
cy.get(formWidgetsPage.formD)
.should("have.css", "background-color")
.and("eq", "rgb(3, 179, 101)");
.and("eq", "rgb(126, 34, 206)");
/**
* @param{toggleButton Css} Assert to be checked
*/

View File

@ -67,7 +67,7 @@ describe("Tab widget test", function() {
});
it("Tab Widget Functionality To Unchecked Visible Widget", function() {
cy.get(publish.backToEditor).click();
cy.get(publish.backToEditor).first().click();
cy.openPropertyPane("tabswidget");
cy.closePropertyPane();
cy.get(Layoutpage.tabWidget)

View File

@ -24,9 +24,11 @@ describe("In a button group widget, menu button width", function() {
.then((targetWidth) => {
expect(targetWidth).to.be.lessThan(minWidth);
// Check if popover width is set to its target width
cy.get(
`.bp3-popover2.menu-button-width-${widgetId}-${menuButtonId}`,
).should("have.css", "width", `${minWidth}px`);
cy.get(`.bp3-popover2.button-group-${widgetId}`).should(
"have.css",
"width",
`${minWidth}px`,
);
});
});
@ -48,9 +50,11 @@ describe("In a button group widget, menu button width", function() {
.then((targetWidth) => {
expect(targetWidth).to.be.greaterThan(minWidth);
// Check if popover width is set to its target width
cy.get(
`.bp3-popover2.menu-button-width-${widgetId}-${menuButtonId}`,
).should("have.css", "width", `${targetWidth}px`);
cy.get(`.bp3-popover2.button-group-${widgetId}`).should(
"have.css",
"width",
`${targetWidth}px`,
);
});
});
@ -75,9 +79,11 @@ describe("In a button group widget, menu button width", function() {
.then((targetWidth) => {
expect(targetWidth).to.be.greaterThan(minWidth);
// Check if popover width is set to its target width
cy.get(
`.bp3-popover2.menu-button-width-${widgetId}-${menuButtonId}`,
).should("have.css", "width", `${targetWidth}px`);
cy.get(`.bp3-popover2.button-group-${widgetId}`).should(
"have.css",
"width",
`${targetWidth}px`,
);
});
});
@ -108,9 +114,11 @@ describe("In a button group widget, menu button width", function() {
.then((targetWidth) => {
expect(targetWidth).to.be.greaterThan(minWidth);
// Check if popover width is set to its target width
cy.get(
`.bp3-popover2.menu-button-width-${widgetId}-${menuButtonId}`,
).should("have.css", "width", `${targetWidth}px`);
cy.get(`.bp3-popover2.button-group-${widgetId}`).should(
"have.css",
"width",
`${targetWidth}px`,
);
});
});
@ -136,9 +144,11 @@ describe("In a button group widget, menu button width", function() {
.invoke("outerWidth")
.then((targetWidth) => {
// Check if popover width is set to its target width
cy.get(
`.bp3-popover2.menu-button-width-${widgetId}-${menuButtonId}`,
).should("have.css", "width", `${targetWidth}px`);
cy.get(`.bp3-popover2.button-group-${widgetId}`).should(
"have.css",
"width",
`${targetWidth}px`,
);
});
});

View File

@ -74,6 +74,7 @@ describe("Checkbox Group Widget Functionality", function() {
cy.openPropertyPane("checkboxgroupwidget");
cy.togglebar(commonlocators.visibleCheckbox);
cy.PublishtheApp();
cy.wait(500);
cy.get(publish.checkboxGroupWidget + " " + "input")
.eq(0)
.should("exist");

View File

@ -66,7 +66,7 @@ describe("Currency widget - ", () => {
].forEach((d) => {
enterAndTest(d[0], d[1]);
});
cy.get(".currency-type-trigger").should("contain", "$");
cy.get(".currency-change-dropdown-trigger").should("contain", "$");
cy.openPropertyPane(widgetName);
cy.selectDropdownValue(
@ -74,7 +74,7 @@ describe("Currency widget - ", () => {
"INR - Indian Rupee",
);
enterAndTest("100.22", "100.22:100.22:true:string:number:IN:INR");
cy.get(".currency-type-trigger").should("contain", "₹");
cy.get(".currency-change-dropdown-trigger").should("contain", "₹");
cy.openPropertyPane(widgetName);
cy.get(".t--property-control-allowcurrencychange label")

View File

@ -20,7 +20,7 @@ describe("Text Widget Cell Background and Text Size Validation", function() {
cy.get(`${widgetsPage.textWidget} .bp3-ui-text`).should(
"have.css",
"background-color",
"rgb(3, 179, 101)",
"rgb(126, 34, 206)",
);
//Toggle to JS mode
@ -73,12 +73,12 @@ describe("Text Widget Cell Background and Text Size Validation", function() {
.click({ force: true });
cy.wait(100);
cy.selectTextSize("Heading 1");
cy.selectTextSize("S");
cy.get(`${widgetsPage.textWidget} .bp3-ui-text`).should(
"have.css",
"font-size",
"24px",
"14px",
);
//Toggle JS mode
@ -87,7 +87,7 @@ describe("Text Widget Cell Background and Text Size Validation", function() {
.wait(200);
//Check if the typed size HEADING2 is reflecting in the background color and in the evaluated value
cy.updateCodeInput(".t--property-control-textsize", "HEADING2");
cy.updateCodeInput(".t--property-control-textsize", "18px");
cy.get(`${widgetsPage.textWidget} .bp3-ui-text`).should(
"have.css",
@ -95,24 +95,17 @@ describe("Text Widget Cell Background and Text Size Validation", function() {
"18px",
);
cy.EvaluateCurrentValue("HEADING2");
//Check for if the text size changes to default size when set to blank in JS mode:
cy.updateCodeInput(".t--property-control-textsize", "");
cy.get(`${widgetsPage.textWidget} .bp3-ui-text`).should(
"have.css",
"font-size",
"14px",
"16px",
);
cy.get(commonlocators.evaluatedCurrentValue)
.first()
.should("not.be.visible");
//Check the values not allowed error message
cy.updateCodeInput(".t--property-control-textsize", "HEADING10");
cy.evaluateErrorMessage("Disallowed value: HEADING10");
});
});

View File

@ -9,6 +9,7 @@ describe("Dropdown Widget Functionality", function() {
it("Verify dropdown width of Select widgets and menu button", function() {
// Select
cy.wait(450);
cy.get(formWidgetsPage.selectwidget)
.find(widgetLocators.dropdownSingleSelect)
.invoke("outerWidth")
@ -26,7 +27,9 @@ describe("Dropdown Widget Functionality", function() {
cy.get(formWidgetsPage.menuButtonWidget)
.find(widgetLocators.menuButton)
.invoke("outerWidth")
.should("eq", 147.1875);
.then((width) => {
expect(parseInt(width)).to.equal(147);
});
cy.get(formWidgetsPage.menuButtonWidget)
.find(widgetLocators.menuButton)
.click({
@ -34,7 +37,9 @@ describe("Dropdown Widget Functionality", function() {
});
cy.get(".menu-button-popover")
.invoke("outerWidth")
.should("eq", 147.1875);
.then((width) => {
expect(parseInt(width)).to.equal(147);
});
// MultiSelect
cy.get(formWidgetsPage.multiselectwidgetv2)

View File

@ -25,10 +25,11 @@ describe("Container Widget Functionality", function() {
*/
cy.get(widgetsPage.borderColorPickerNew)
.first()
.click({ force: true });
cy.xpath(widgetsPage.yellowColor).click();
.click({ force: true })
.clear()
.type(widgetsPage.yellowColorHex);
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}'] div`,
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}']`,
)
.should("have.css", "border-color")
.and("eq", "rgb(255, 193, 61)");
@ -37,13 +38,15 @@ describe("Container Widget Functionality", function() {
*/
cy.get(widgetsPage.backgroundcolorPickerNew)
.first()
.click({ force: true });
cy.get(widgetsPage.greenColor)
.last()
.click();
.click({ force: true })
.clear()
.type(widgetsPage.greenColorHex);
cy.get(widgetsPage.containerD)
.should("have.css", "background-color")
.and("eq", "rgb(3, 179, 101)");
.should("have.css", "background")
.and(
"eq",
"rgb(3, 179, 101) none repeat scroll 0% 0% / auto padding-box border-box",
);
/**
* @param{toggleButton Css} Assert to be checked
*/
@ -57,8 +60,11 @@ describe("Container Widget Functionality", function() {
it("Container Widget Functionality To Verify The Colour", function() {
cy.get(widgetsPage.containerD)
.eq(0)
.should("have.css", "background-color")
.and("eq", "rgb(3, 179, 101)");
.should("have.css", "background")
.and(
"eq",
"rgb(3, 179, 101) none repeat scroll 0% 0% / auto padding-box border-box",
);
});
it("Test border width and verity", function() {
@ -67,64 +73,42 @@ describe("Container Widget Functionality", function() {
cy.testJsontext("borderwidth", "10");
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}'] div`,
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}']`,
)
.should("have.css", "border-width")
.and("eq", "10px");
});
it("Test border radius and verity", function() {
cy.testJsontext("borderradius", "10");
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}'] div`,
)
.should("have.css", "border-radius")
.and("eq", "10px");
// should have overflow : hidden to show border edges
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}'] div`,
)
.should("have.css", "overflow")
.and("eq", "hidden");
// wrapper should have same border radius
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}']`,
)
.should("have.css", "border-radius")
.and("eq", "10px");
// check if border radius is changed on button
cy.get(`.t--property-control-borderradius button > div`)
.eq(0)
.click({ force: true });
cy.get(`.t--property-control-borderradius button > div`)
.eq(0)
.invoke("css", "border-top-left-radius")
.then((borderRadius) => {
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}']`,
).should("have.css", "border-radius", borderRadius);
});
});
it("Test Box shadow and verity", function() {
cy.get(widgetsPage.boxShadow)
.children()
.eq(3)
cy.get(`.t--property-control-boxshadow button > div`)
.eq(0)
.click({ force: true });
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}']`,
)
.should("have.css", "box-shadow")
.and("eq", "rgba(0, 0, 0, 0.5) 0px 1px 3px 0px");
// change shadow color and check box-shadow again
cy.get(widgetsPage.boxShadowColorPicker)
.first()
.click({ force: true });
cy.xpath(widgetsPage.blueColor).click();
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}']`,
)
.should("have.css", "box-shadow")
.and("eq", "rgb(51, 102, 255) 0px 1px 3px 0px");
});
it("Test overflow of widget boundaries", function() {
cy.testJsontext("borderwidth", "500");
// prevent overflow of widget boundaries
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}']`,
)
.should("have.css", "overflow")
.and("eq", "hidden");
cy.get(`.t--property-control-boxshadow button > div`)
.eq(0)
.invoke("css", "box-shadow")
.then((boxShadow) => {
cy.get(
`div[data-testid='container-wrapper-${dsl.dsl.children[0].widgetId}']`,
).should("have.css", "box-shadow", boxShadow);
});
});
afterEach(() => {

View File

@ -172,7 +172,7 @@ describe("Container Widget Functionality", function() {
cy.CheckAndUnfoldEntityItem("WIDGETS");
cy.selectEntityByName("List1");
// Scroll down to Styles and Add background colour
cy.selectColor("backgroundcolor");
cy.selectColor("background");
cy.wait(1000);
cy.selectColor("itembackgroundcolor");
// Click on Deploy and ensure it is deployed appropriately
@ -181,13 +181,13 @@ describe("Container Widget Functionality", function() {
cy.get(widgetsPage.listWidget).should(
"have.css",
"background-color",
"rgb(3, 179, 101)",
"rgb(126, 34, 206)",
);
// Verify List Item Background Color
cy.get(widgetsPage.itemContainerWidget).should(
"have.css",
"background-color",
"rgb(3, 179, 101)",
"rgb(126, 34, 206)",
);
cy.get(publishPage.backToEditor).click({ force: true });
});
@ -198,7 +198,7 @@ describe("Container Widget Functionality", function() {
cy.selectEntityByName("List1");
// Scroll down to Styles and Add background colour
cy.get(widgetsPage.backgroundColorToggle).click({ force: true });
cy.testJsontext("backgroundcolor", "#FFC13D");
cy.testJsontext("background", "#FFC13D");
cy.wait(1000);
cy.get(widgetsPage.itemBackgroundColorToggle).click({ force: true });
cy.testJsontext("itembackgroundcolor", "#38AFF4");

View File

@ -53,7 +53,7 @@ describe("Tab widget test", function() {
cy.get(publish.tabWidget)
.contains(this.data.tabName)
.click({ force: true })
.should("be.selected");
.should("have.class", "is-selected");
cy.get(publish.backToEditor).click();
});
it("Tab Widget Functionality To Unchecked Visible Widget", function() {
@ -133,20 +133,21 @@ describe("Tab widget test", function() {
*/
it("Tabs widget should have navigation arrows if tabs don't fit", function() {
const rightNavButtonSelector =
Layoutpage.tabWidget + " button.scroll-nav-right-button";
Layoutpage.tabWidget + " .scroll-nav-right-button";
const leftNavButtonSelector =
Layoutpage.tabWidget + " button.scroll-nav-left-button";
Layoutpage.tabWidget + " .scroll-nav-left-button";
cy.openPropertyPane("tabswidget");
// Add a new tab
cy.get(Layoutpage.tabButton).click({ force: true });
cy.tabVerify(2, "Tab3-for-testing-scroll-navigation-controls");
cy.get(Layoutpage.tabButton).click({ force: true });
cy.tabVerify(3, "Tab3-for-testing-scroll-navigation-controls");
// Should show off right navigation arrow
cy.get(rightNavButtonSelector).should("exist");
// Click on the right navigation arrow
cy.get(rightNavButtonSelector).click({ force: true });
// Should show off left navigation arrow
cy.get(leftNavButtonSelector).should("exist");
// Click on the right navigation arrow
cy.get(leftNavButtonSelector).click({ force: true });
// Should show off left navigation arrow
cy.get(rightNavButtonSelector).should("exist");
});
});

View File

@ -31,7 +31,6 @@ describe("Page Load tests", () => {
.parent()
.parent()
.parent()
.parent()
.should("have.class", "is-active");
// Assert active page DSL
cy.get(commonlocators.headingTextStyle).should(
@ -48,7 +47,6 @@ describe("Page Load tests", () => {
.parent()
.parent()
.parent()
.parent()
.should("have.class", "is-active");
// Assert active page DSL
cy.get(commonlocators.headingTextStyle).should(
@ -67,7 +65,6 @@ describe("Page Load tests", () => {
.parent()
.parent()
.parent()
.parent()
.should("have.class", "is-active");
// Assert active page DSL
cy.get(commonlocators.headingTextStyle).should(

View File

@ -170,16 +170,12 @@ describe("Undo/Redo functionality", function() {
it("checks undo/redo for color picker", function() {
cy.dragAndDropToCanvas("textwidget", { x: 100, y: 100 });
cy.get(widgetsPage.textColor)
.first()
.click({ force: true });
cy.get(widgetsPage.greenColor)
.last()
.click();
cy.selectColor("textcolor");
cy.get("body").click({ force: true });
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);
cy.wait("@updateLayout");
cy.readTextDataValidateCSS("color", "rgb(3, 179, 101)");
cy.readTextDataValidateCSS("color", "rgb(126, 34, 206)");
cy.get("body").type(`{${modifierKey}}z`);
cy.get(widgetsPage.textColor)
@ -192,7 +188,7 @@ describe("Undo/Redo functionality", function() {
cy.get(widgetsPage.textColor)
.first()
.invoke("attr", "value")
.should("contain", "#03b365");
.should("contain", "#7e22ce");
});
it("checks undo/redo for option control for radio button", function() {

View File

@ -9,12 +9,9 @@ describe("Create a rest datasource", function() {
cy.NavigateToAPI_Panel();
cy.CreateAPI("Testapi");
cy.enterDatasourceAndPath(testdata.baseUrl, testdata.methods);
cy.get(".t--store-as-datasource").click();
cy.get(".t--store-as-datasource").click({ force: true });
cy.saveDatasource();
cy.contains(".datasource-highlight", "https://mock-api.appsmith.com");
cy.SaveAndRunAPI();
});
});

View File

@ -1,7 +1,7 @@
import { ObjectsRegistry } from "../../../../support/Objects/Registry";
let dsl: any;
let agHelper = ObjectsRegistry.AggregateHelper,
const agHelper = ObjectsRegistry.AggregateHelper,
homePage = ObjectsRegistry.HomePage,
ee = ObjectsRegistry.EntityExplorer,
apiPage = ObjectsRegistry.ApiPage,
@ -61,7 +61,7 @@ describe("Layout OnLoad Actions tests", function() {
//apiPage.RunAPI();
//Adding dependency in right order matters!
ee.SelectEntityByName("WIDGETS");
ee.expandCollapseEntity("WIDGETS");
ee.SelectEntityByName("Image1");
jsEditor.EnterJSContext("Image", `{{RandomFlora.data}}`, true);

View File

@ -56,7 +56,7 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break",
agHelper.SelectDropDown("7");
agHelper.ClickButton("Submit");
agHelper.ValidateNetworkExecutionSuccess("@postExecute");
table.ReadTableRowColumnData(0, 0).then((cellData) => {
table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => {
expect(cellData).to.be.equal("7");
});
@ -72,7 +72,7 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break",
agHelper.SelectDropDown("9");
agHelper.ClickButton("Submit");
agHelper.ValidateNetworkExecutionSuccess("@postExecute");
table.ReadTableRowColumnData(0, 0).then((cellData) => {
table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => {
expect(cellData).to.be.equal("9");
});
agHelper.NavigateBacktoEditor()
@ -87,7 +87,7 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break",
agHelper.SelectDropDown("7");
agHelper.ClickButton("Submit");
agHelper.ValidateNetworkExecutionSuccess("@postExecute");
table.ReadTableRowColumnData(0, 0).then((cellData) => {
table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => {
expect(cellData).to.be.equal("7");
});
agHelper.NavigateBacktoEditor()
@ -102,7 +102,7 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break",
agHelper.SelectDropDown("9");
agHelper.ClickButton("Submit");
agHelper.ValidateNetworkExecutionSuccess("@postExecute");
table.ReadTableRowColumnData(0, 0).then((cellData) => {
table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => {
expect(cellData).to.be.equal("9");
});
agHelper.NavigateBacktoEditor()
@ -117,7 +117,7 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break",
agHelper.SelectDropDown("7");
agHelper.ClickButton("Submit");
agHelper.ValidateNetworkExecutionSuccess("@postExecute");
table.ReadTableRowColumnData(0, 0).then((cellData) => {
table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => {
expect(cellData).to.be.equal("7");
});
agHelper.NavigateBacktoEditor()
@ -132,7 +132,7 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break",
agHelper.SelectDropDown("9");
agHelper.ClickButton("Submit");
agHelper.ValidateNetworkExecutionSuccess("@postExecute");
table.ReadTableRowColumnData(0, 0).then((cellData) => {
table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => {
expect(cellData).to.be.equal("9");
});
agHelper.NavigateBacktoEditor()
@ -147,7 +147,7 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break",
agHelper.SelectDropDown("7");
agHelper.ClickButton("Submit");
agHelper.ValidateNetworkExecutionSuccess("@postExecute");
table.ReadTableRowColumnData(0, 0).then((cellData) => {
table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => {
expect(cellData).to.be.equal("7");
});
agHelper.NavigateBacktoEditor()
@ -162,7 +162,7 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break",
agHelper.SelectDropDown("8");
agHelper.ClickButton("Submit");
agHelper.ValidateNetworkExecutionSuccess("@postExecute");
table.ReadTableRowColumnData(0, 0).then((cellData) => {
table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => {
expect(cellData).to.be.equal("8");
});
agHelper.NavigateBacktoEditor()
@ -177,7 +177,7 @@ describe("[Bug] - 10784 - Passing params from JS to SQL query should not break",
agHelper.SelectDropDown("9");
agHelper.ClickButton("Submit");
agHelper.ValidateNetworkExecutionSuccess("@postExecute");
table.ReadTableRowColumnData(0, 0).then((cellData) => {
table.ReadTableRowColumnData(0, 0, 2000).then((cellData) => {
expect(cellData).to.be.equal("9");
});
agHelper.NavigateBacktoEditor()

View File

@ -17,7 +17,6 @@
"removeWidget": ".t--delete-widget svg",
"propertypaneText": ".t--propertypane .t--property-pane-view",
"formButtonWidget": ".t--widget-formbuttonwidget",
"selectwidget": ".t--widget-selectwidget",
"textWidget": ".t--draggable-textwidget",
"tableWidget": ".t--draggable-tablewidget",
"jsonFormWidget": ".t--draggable-jsonformwidget",
@ -100,8 +99,10 @@
"boxShadow": ".t--property-control-boxshadow .bp3-button-group",
"backgroundcolorPicker": ".t--property-control-backgroundcolour input",
"backgroundcolorPickerNew": ".t--property-control-backgroundcolor input",
"greenColor": "div[color='#03B365']",
"transparent": "//div[@color='transparent']",
"greenColorHex": "#03b365",
"yellowColorHex": "#FFC13D",
"greenColor": "//div[@color='#03b365']",
"transparent": ".diagnol-cross",
"yellowColor": "//div[@color='#FFC13D']",
"blueColor": "//div[@color='#3366FF']",
"toggleJsColor": ".t--property-control-textcolor .t--js-toggle",
@ -123,8 +124,8 @@
"inputToggleOnClick": ".t--property-control-onclick div.CodeMirror-lines",
"tableBtn": ".t--draggable-tablewidget .bp3-button",
"tableIconBtn": ".t--draggable-tablewidget .bp3-icon",
"toastAction": ".Toastify__toast-container--top-right .t--toast-action",
"toastActionText": ".Toastify__toast-container--top-right .t--toast-action span",
"toastAction": ".t--toast-action",
"toastActionText": ".t--toast-action span",
"defaultColName": "[data-rbd-draggable-id='customColumn1'] input",
"selectWidget": ".t--open-dropdown-Select-Widget",
"switchWidget": ".t--widget-switchwidget",
@ -169,10 +170,12 @@
"selectwidget": ".t--draggable-selectwidget",
"selectWidgetDisabled": ".t--property-control-disabled input",
"itemBackgroundColorToggle": ".t--property-control-itembackgroundcolor .t--js-toggle",
"backgroundColorToggle": ".t--property-control-backgroundcolor .t--js-toggle",
"backgroundColorToggle": ".t--property-control-background .t--js-toggle",
"cellBackground": ".t--property-control-cellbackgroundcolor",
"cellBackgroundToggle": ".t--property-control-cellbackgroundcolor .t--js-toggle",
"borderColorPickerNew": ".t--property-control-bordercolor input",
"selectedTextSize": ".t--property-control-textsize .bp3-popover-target .sub-text",
"colorPickerV2Popover": ".t--colorpicker-v2-popover",
"colorPickerV2Color": ".t--colorpicker-v2-color",
"modalCloseButton": ".t--draggable-iconbuttonwidget .bp3-button"
}

View File

@ -170,5 +170,11 @@
"filepickerv2": ".t--draggable-filepickerwidgetv2",
"dashboardItemName": ".uppy-Dashboard-Item-name",
"mapChartShowLabels": ".t--property-control-showlabels input",
"widgetSection": ".t--entity.widgets > .t--entity-item > a.t--entity-collapse-toggle"
"widgetSection": ".t--entity.widgets > .t--entity-item > a.t--entity-collapse-toggle",
"changeThemeBtn": ".t--change-theme-btn",
"editThemeBtn": ".t--edit-theme-btn",
"themeCard": ".t--theme-card",
"saveThemeBtn": ".t--save-theme-btn",
"selectThemeBackBtn": ".t--theme-select-back-btn",
"themeAppBorderRadiusBtn": ".t--theme-appBorderRadius"
}

View File

@ -0,0 +1,215 @@
// const commonlocators = require("../../locators/commonlocators.json");
// const widgetLocators = require("../../locators/publishWidgetspage.json");
// const widgetsPage = require("../../locators/Widgets.json");
// const explorer = require("../../locators/explorerlocators.json");
// const publish = require("../../locators/publishWidgetspage.json");
// const dsl = require("../../fixtures/replay.json");
// describe("App Theming funtionality", function() {
// /**
// * Test cases; Check:
// * 1. If theme can be changed*
// * 2. If the theme can edited*
// * 4. If the save theme can be used.
// * 5. If the theme can be deleled
// */
// before(() => {
// cy.addDsl(dsl);
// });
// it("checks if theme can be changed", function() {
// cy.get(commonlocators.changeThemeBtn).click({ force: true });
// // select a theme
// cy.get(commonlocators.themeCard)
// .last()
// .click({ force: true });
// // check for alert
// cy.get(`${commonlocators.themeCard}`)
// .last()
// .siblings("div")
// .first()
// .invoke("text")
// .then((text) => {
// cy.get(commonlocators.toastmsg).contains(`Theme ${text} Applied`);
// });
// // check if color of canvas is same as theme bg color
// cy.get(`${commonlocators.themeCard} > main`)
// .last()
// .invoke("css", "background-color")
// .then((backgroudColor) => {
// cy.get(commonlocators.canvas).should(
// "have.css",
// "background-color",
// backgroudColor,
// );
// });
// });
// it("checks if theme can be edited", function() {
// // drop a button widget and click on body
// cy.get(explorer.addWidget).click();
// cy.dragAndDropToCanvas("buttonwidget", { x: 200, y: 200 });
// cy.get("body").click();
// //Click the back button
// cy.get(commonlocators.selectThemeBackBtn).click({ force: true });
// //Click the border radius toggle
// cy.contains("Border").click({ force: true });
// // change app border radius
// cy.get(commonlocators.themeAppBorderRadiusBtn)
// .eq(1)
// .click({ force: true });
// // check if border radius is changed on button
// cy.get(`${commonlocators.themeAppBorderRadiusBtn} > div`)
// .eq(1)
// .invoke("css", "border-top-left-radius")
// .then((borderRadius) => {
// cy.get(widgetsPage.widgetBtn).should(
// "have.css",
// "border-radius",
// borderRadius,
// );
// // publish the app
// // cy.PublishtheApp();
// cy.get(widgetsPage.widgetBtn).should(
// "have.css",
// "border-radius",
// borderRadius,
// );
// });
// //Change the color:
// cy.contains("Colour").click({ force: true });
// //Change the primary color:
// cy.get(".border-2")
// .first()
// .click({ force: true });
// cy.get(widgetsPage.colorPickerV2Popover).click({ force: true });
// cy.get(widgetsPage.colorPickerV2Color)
// .eq(-3)
// .then(($elem) => {
// cy.get($elem).click({ force: true });
// cy.get(widgetsPage.widgetBtn).should(
// "have.css",
// "background-color",
// $elem.css("background-color"),
// );
// });
// //Change the background color:
// cy.get(".border-2")
// .last()
// .click({ force: true });
// cy.get(widgetsPage.colorPickerV2Popover).click({ force: true });
// cy.get(widgetsPage.colorPickerV2Color)
// .first()
// .then(($elem) => {
// cy.get($elem).click({ force: true });
// cy.get(commonlocators.canvas).should(
// "have.css",
// "background-color",
// $elem.css("background-color"),
// );
// });
// //Change the shadow
// cy.contains("Shadow").click({ force: true });
// cy.contains("App Box Shadow")
// .siblings("div")
// .children("span")
// .last()
// .then(($elem) => {
// cy.get($elem).click({ force: true });
// cy.get(widgetsPage.widgetBtn).should(
// "have.css",
// "box-shadow",
// $elem.css("box-shadow"),
// );
// });
// //Change the font
// cy.contains("Font").click({ force: true });
// cy.get("span[name='expand-more']").then(($elem) => {
// cy.get($elem).click({ force: true });
// cy.wait(250);
// cy.get(".ads-dropdown-options-wrapper div")
// .children()
// .eq(2)
// .then(($childElem) => {
// cy.get($childElem).click({ force: true });
// cy.get(widgetsPage.widgetBtn).should(
// "have.css",
// "font-family",
// $childElem
// .children()
// .last()
// .text(),
// );
// });
// });
// });
// it("Checks if the theme can be saved", () => {
// //Click on dropDown elipses
// cy.contains("Theme Properties")
// .siblings()
// .first()
// .find("button")
// .click({ force: true });
// // .then(($elem) => {
// // cy.get(`${$elem} button`).click({ force: true });
// // })
// cy.wait(300);
// //Click on save theme dropdown option
// cy.contains("Save theme").click({ force: true });
// cy.wait(200);
// //Type the name of the theme:
// cy.get("input[placeholder='My theme']").type("test theme");
// //Click on save theme button
// cy.get("a[type='submit']").click({ force: true });
// cy.wait(200);
// //Click on change theme:
// cy.get(commonlocators.changeThemeBtn).click({ force: true });
// //Check if the saved theme is present under 'Yours Themes' section
// cy.contains("Your Themes")
// .siblings()
// .find(".t--theme-card")
// .parent()
// .should("contain.text", "test theme");
// });
// it("Checks if the theme can be deleted", () => {
// cy.wait(300);
// //Check if the saved theme is present under 'Yours Themes' section
// cy.contains("Your Themes")
// .siblings()
// .find(".t--theme-card")
// .parent()
// .find("button")
// .click({ force: true });
// cy.contains("Delete").click({ force: true });
// //check for delete alert
// cy.wait(300);
// cy.get(commonlocators.toastMsg).contains("Theme test theme Deleted");
// });
// });

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -131,13 +131,18 @@ Cypress.Commands.add("shareAndPublic", (email, role) => {
});
Cypress.Commands.add("enablePublicAccess", () => {
cy.get(homePage.enablePublicAccess).click();
cy.get(homePage.enablePublicAccess)
.first()
.click({ force: true });
cy.wait("@changeAccess").should(
"have.nested.property",
"response.body.responseMeta.status",
200,
);
cy.get(homePage.closeBtn).click();
cy.wait(10000);
cy.get(homePage.closeBtn)
.first()
.click({ force: true });
});
Cypress.Commands.add("deleteUserFromOrg", (orgName) => {

View File

@ -98,6 +98,7 @@ export class JSEditor {
input.type(JSCode, {
parseSpecialCharSequences: false,
delay: 150,
force: true
});
}
});

View File

@ -231,7 +231,7 @@ export class Table {
cy.xpath(this._liCurrentSelectedPage).invoke('text').then($currentPageNo =>
curPageNo = Number($currentPageNo))
cy.get(this._liNextPage).click()
cy.scrollTo('top', { easing: 'linear' })
//cy.scrollTo('top', { easing: 'linear' })
cy.xpath(this._liCurrentSelectedPage).invoke('text').then($newPageNo =>
expect(Number($newPageNo)).to.eq(curPageNo + 1))
}
@ -241,7 +241,7 @@ export class Table {
cy.xpath(this._liCurrentSelectedPage).invoke('text').then($currentPageNo =>
curPageNo = Number($currentPageNo))
cy.get(this._liPreviousPage).click()
cy.scrollTo('top', { easing: 'linear' })
//cy.scrollTo('top', { easing: 'linear' })
cy.xpath(this._liCurrentSelectedPage).invoke('text').then($newPageNo =>
expect(Number($newPageNo)).to.eq(curPageNo - 1))
}

View File

@ -942,7 +942,7 @@ Cypress.Commands.add("startServerAndRoutes", () => {
cy.route("PUT", "/api/v1/organizations/*").as("updateOrganization");
cy.route("GET", "/api/v1/pages/view/application/*").as("viewApp");
cy.route("GET", "/api/v1/pages/*/view").as("viewPage");
cy.route("GET", "/api/v1/pages/*/view?*").as("viewPage");
cy.route("POST", "/api/v1/organizations/*/logo").as("updateLogo");
cy.route("DELETE", "/api/v1/organizations/*/logo").as("deleteLogo");
cy.route("POST", "/api/v1/applications/*/fork/*").as("postForkAppOrg");

View File

@ -431,9 +431,12 @@ Cypress.Commands.add("selectColor", (GivenProperty) => {
).click({
force: true,
});
cy.get(widgetsPage.colorsAvailable)
.first()
.click({ force: true });
cy.get(widgetsPage.colorPickerV2Color)
.eq(-15)
.then(($elem) => {
cy.get($elem).click({ force: true });
});
});
Cypress.Commands.add("toggleJsAndUpdate", (endp, value) => {
@ -785,6 +788,12 @@ Cypress.Commands.add("selectTextSize", (text) => {
.click({ force: true });
});
Cypress.Commands.add("selectTxtSize", (text) => {
cy.get(".t--dropdown-option")
.contains(text)
.click({ force: true });
});
Cypress.Commands.add("getAlert", (alertcss) => {
cy.get(commonlocators.dropdownSelectButton).click({ force: true });
cy.get(widgetsPage.menubar)

View File

@ -24,6 +24,7 @@
"@sentry/tracing": "^6.2.4",
"@sentry/webpack-plugin": "^1.12.1",
"@tinymce/tinymce-react": "^3.13.0",
"@types/webfontloader": "^1.6.33",
"@uppy/core": "^1.16.0",
"@uppy/dashboard": "^1.16.0",
"@uppy/file-input": "^1.4.22",
@ -55,6 +56,7 @@
"fast-deep-equal": "^3.1.1",
"fast-xml-parser": "^3.17.5",
"flow-bin": "^0.148.0",
"focus-trap-react": "^8.9.2",
"fuse.js": "^3.4.5",
"fusioncharts": "^3.18.0",
"fusionmaps": "^3.18.0",
@ -158,6 +160,7 @@
"typescript": "^4.1.3",
"unescape-js": "^1.1.4",
"url-search-params-polyfill": "^8.0.0",
"webfontloader": "^1.6.28",
"worker-loader": "^3.0.2",
"yjs": "^13.5.12",
"zipcelx": "^1.6.2"

View File

@ -59,6 +59,7 @@ import { ERROR_CODES } from "@appsmith/constants/ApiConstants";
import TemplatesListLoader from "pages/Templates/loader";
import { fetchFeatureFlagsInit } from "actions/userActions";
import FeatureFlags from "entities/FeatureFlags";
import WDSPage from "components/wds/Showcase";
const SentryRoute = Sentry.withSentryRouting(Route);
@ -121,6 +122,7 @@ function AppRouter(props: {
<SentryRoute component={OrganizationLoader} path={ORG_URL} />
<SentryRoute component={Users} exact path={USERS_URL} />
<SentryRoute component={UserAuth} path={USER_AUTH_URL} />
<SentryRoute component={WDSPage} path="/wds" />
<SentryRoute
component={ApplicationListLoader}
exact

View File

@ -0,0 +1,168 @@
import { AppTheme } from "entities/AppTheming";
import { AppThemingMode } from "selectors/appThemingSelectors";
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
/**
* ----------------------------------------------------------------------------
* ACTION TYPES
* ----------------------------------------------------------------------------
*/
export type FetchAppThemesAction = {
applicationId: string;
};
export type FetchSelectedAppThemeAction = {
applicationId: string;
};
export type UpdateSelectedAppThemeAction = {
applicationId: string;
theme: AppTheme;
shouldReplay?: boolean;
};
export type ChangeSelectedAppThemeAction = {
applicationId: string;
theme: AppTheme;
shouldReplay?: boolean;
};
export type HydrateSelectedAppThemeAction = {
theme: AppTheme;
};
export type SaveAppThemeAction = {
applicationId: string;
name: string;
};
export type DeleteAppThemeAction = {
themeId: string;
name: string;
};
/**
* ----------------------------------------------------------------------------
* ACTIONS
* ----------------------------------------------------------------------------
*/
/**
* set theming mode
*
* @param mode
* @returns
*/
export const setAppThemingModeStackAction = (stack: AppThemingMode[]) => ({
type: ReduxActionTypes.SET_APP_THEMING_STACK,
payload: stack,
});
/**
* fetches themes
*
* @param mode
* @returns
*/
export const fetchAppThemesAction = (applicationId: string) => ({
type: ReduxActionTypes.FETCH_APP_THEMES_INIT,
payload: {
applicationId,
},
});
/**
* fetch selected theme
*
* @param mode
* @returns
*/
export const fetchSelectedAppThemeAction = (applicationId: string) => ({
type: ReduxActionTypes.FETCH_SELECTED_APP_THEME_INIT,
payload: {
applicationId,
},
});
/**
* update selected theme
*
* @param payload
* @returns
*/
export const updateSelectedAppThemeAction = (
payload: UpdateSelectedAppThemeAction,
) => ({
type: ReduxActionTypes.UPDATE_SELECTED_APP_THEME_INIT,
payload,
});
/**
* change selected theme
*
* @param payload
* @returns
*/
export const changeSelectedAppThemeAction = (
payload: ChangeSelectedAppThemeAction,
) => ({
type: ReduxActionTypes.CHANGE_SELECTED_APP_THEME_INIT,
payload,
});
/**
* set the preview theme
*
* @param payload
* @returns
*/
export const setPreviewAppThemeAction = (payload?: AppTheme) => ({
type: ReduxActionTypes.SET_PREVIEW_APP_THEME,
payload,
});
/**
* set the preview theme
*
* @param payload
* @returns
*/
export const saveSelectedThemeAction = (payload?: SaveAppThemeAction) => ({
type: ReduxActionTypes.SAVE_APP_THEME_INIT,
payload,
});
/**
* delete app theme
*
* @param payload
* @returns
*/
export const deleteAppThemeAction = (payload?: DeleteAppThemeAction) => ({
type: ReduxActionTypes.DELETE_APP_THEME_INIT,
payload,
});
/**
* close beta card
*
* @returns
*/
export const closeAppThemingBetaCard = () => {
return {
type: ReduxActionTypes.CLOSE_BETA_CARD_SHOWN,
};
};
/**
* close beta card
*
* @returns
*/
export const updateisBetaCardShownAction = (payload: boolean) => {
return {
type: ReduxActionTypes.UPDATE_BETA_CARD_SHOWN,
payload,
};
};

View File

@ -0,0 +1,18 @@
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
/**
* ----------------------------------------------------------------------------
* ACTIONS
* ----------------------------------------------------------------------------
*/
/**
* set app view header height
*
* @param mode
* @returns
*/
export const setAppViewHeaderHeight = (height: number) => ({
type: ReduxActionTypes.SET_APP_VIEWER_HEADER_HEIGHT,
payload: height,
});

View File

@ -61,6 +61,7 @@ export const setWidgetDynamicProperty = (
widgetId: string,
propertyPath: string,
isDynamic: boolean,
shouldRejectDynamicBindingPathList = true,
): ReduxAction<SetWidgetDynamicPropertyPayload> => {
return {
type: ReduxActionTypes.SET_WIDGET_DYNAMIC_PROPERTY,
@ -68,6 +69,7 @@ export const setWidgetDynamicProperty = (
widgetId,
propertyPath,
isDynamic,
shouldRejectDynamicBindingPathList,
},
};
};
@ -97,6 +99,7 @@ export interface SetWidgetDynamicPropertyPayload {
widgetId: string;
propertyPath: string;
isDynamic: boolean;
shouldRejectDynamicBindingPathList?: boolean;
}
export interface DeleteWidgetPropertyPayload {

View File

@ -61,6 +61,10 @@ export const EVALUATE_REDUX_ACTIONS = [
ReduxActionTypes.RESET_WIDGET_META,
// Batches
ReduxActionTypes.BATCH_UPDATES_SUCCESS,
// App Theme
ReduxActionTypes.UPDATE_SELECTED_APP_THEME_SUCCESS,
ReduxActionTypes.CHANGE_SELECTED_APP_THEME_SUCCESS,
ReduxActionTypes.SET_PREVIEW_APP_THEME,
];
// Topics used for datsource and query form evaluations
export const FORM_EVALUATION_REDUX_ACTIONS = [

View File

@ -0,0 +1,102 @@
import API from "api/Api";
import { AxiosPromise } from "axios";
import { AppTheme } from "entities/AppTheming";
import { GenericApiResponse } from "./ApiResponses";
class AppThemingApi extends API {
static baseUrl = "/v1";
/**
* fires api to get all themes
*
* @returns
*/
static fetchThemes(
applicationId: string,
): AxiosPromise<GenericApiResponse<AppTheme[]>> {
return API.get(
`${AppThemingApi.baseUrl}/themes/applications/${applicationId}`,
);
}
/**
* fires api to fetch selected theme
*
* @param applicationId
* @returns
*/
static fetchSelected(
applicationId: string,
mode = "EDIT",
): AxiosPromise<GenericApiResponse<AppTheme[]>> {
return API.get(
`${AppThemingApi.baseUrl}/themes/applications/${applicationId}/current?mode=${mode}`,
);
}
/**
* fires api to updating current theme
*
* @param applicationId
* @param theme
* @returns
*/
static updateTheme(
applicationId: string,
theme: AppTheme,
): AxiosPromise<GenericApiResponse<AppTheme[]>> {
return API.put(
`${AppThemingApi.baseUrl}/themes/applications/${applicationId}`,
theme,
);
}
/**
* fires api to updating current theme
*
* @param applicationId
* @param theme
* @returns
*/
static changeTheme(
applicationId: string,
theme: AppTheme,
): AxiosPromise<GenericApiResponse<AppTheme[]>> {
return API.patch(
`${AppThemingApi.baseUrl}/applications/${applicationId}/themes/${theme.id}`,
theme,
);
}
/**
* fires api for saving current theme
*
* @param applicationId
* @param theme
* @returns
*/
static saveTheme(
applicationId: string,
payload: { name: string },
): AxiosPromise<GenericApiResponse<AppTheme[]>> {
return API.patch(
`${AppThemingApi.baseUrl}/themes/applications/${applicationId}`,
payload,
);
}
/**
* fires api for deleting theme
*
* @param applicationId
* @param theme
* @returns
*/
static deleteTheme(
themeId: string,
): AxiosPromise<GenericApiResponse<AppTheme[]>> {
return API.delete(`${AppThemingApi.baseUrl}/themes/${themeId}`);
}
}
export default AppThemingApi;

View File

@ -0,0 +1,3 @@
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.5 8.5C9.85 8.5 7.45 9.49 5.6 11.1L2 7.5V16.5H11L7.38 12.88C8.77 11.72 10.54 11 12.5 11C16.04 11 19.05 13.31 20.1 16.5L22.47 15.72C21.08 11.53 17.15 8.5 12.5 8.5Z" fill="#575757"/>
</svg>

After

Width:  |  Height:  |  Size: 296 B

View File

@ -6,7 +6,7 @@
* ---------------------------------------------------------------------------------------------------
*/
body, html {
@apply overflow-x-hidden;
@apply w-full h-full overflow-x-hidden;
}
@ -28,6 +28,7 @@ body, html {
* {
scrollbar-width: thin;
scrollbar-color: rgba(209, 213, 219, var(--tw-bg-opacity)) white;
}
::-webkit-scrollbar {
@ -46,3 +47,22 @@ body, html {
:hover::-webkit-scrollbar-thumb {
@apply bg-gray-300;
}
.diagnol-cross {
background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' version='1.1' preserveAspectRatio='none' viewBox='0 0 100 100'><path d='M0 99 L99 0 L100 1 L1 100' fill='red' /></svg>");
background-repeat:no-repeat;
background-position:center center;
background-size: 100% 100%, auto;
}
.hidden-scrollbar {
-ms-overflow-style: none; /* for Internet Explorer, Edge */
scrollbar-width: none; /* for Firefox */
overflow-y: scroll;
}
.hidden-scrollbar::-webkit-scrollbar {
display: none; /* for Chrome, Safari, and Opera */
}

View File

@ -0,0 +1,12 @@
<svg width="783" height="131" viewBox="0 0 783 131" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M510.51 17.21C509.564 16.309 508.816 15.2218 508.312 14.017C507.807 12.8122 507.558 11.5159 507.58 10.21C507.58 7.55784 508.634 5.0143 510.509 3.13894C512.384 1.26357 514.928 0.210005 517.58 0.210005C518.886 0.188131 520.182 0.437208 521.387 0.941511C522.592 1.44581 523.679 2.19439 524.58 3.14C525.508 4.06766 526.239 5.17263 526.73 6.38903C527.222 7.60542 527.463 8.90832 527.44 10.22C527.462 11.5259 527.213 12.8222 526.708 14.027C526.204 15.2318 525.456 16.319 524.51 17.22C523.605 18.1384 522.523 18.865 521.331 19.3564C520.139 19.8478 518.86 20.0939 517.57 20.08C516.261 20.0985 514.962 19.854 513.749 19.361C512.536 18.868 511.435 18.1365 510.51 17.21V17.21Z" fill="#1A1A1A"/>
<path d="M52.81 102.78C51.95 101.253 51.2833 98.2533 50.81 93.78C45.85 100.94 37.9333 104.517 27.06 104.51C18.9467 104.51 12.4833 102.557 7.67001 98.65C2.85668 94.7433 0.450012 89.3033 0.450012 82.33C0.450012 68.8833 9.89335 61.2167 28.78 59.33L39.94 58.33C43.66 57.86 46.33 56.97 47.94 55.69C48.7694 54.9797 49.421 54.0853 49.843 53.0782C50.2649 52.0711 50.4454 50.9793 50.37 49.89C50.37 46.7433 49.3467 44.43 47.3 42.95C45.2534 41.47 41.7933 40.73 36.92 40.73C31.68 40.73 27.9134 41.6133 25.62 43.38C23.33 45.15 21.99 48.17 21.62 52.47H3.87002C4.91668 35.6833 15.9833 27.2867 37.07 27.28C57.5833 27.28 67.8367 34.6733 67.83 49.46V88.81C67.83 95.3033 68.83 99.97 70.83 102.81L52.81 102.78ZM45.08 87C48.6134 83.9 50.3767 79.44 50.37 73.62V66.9C48.66 68.43 45.84 69.43 41.93 69.9L32.2 71C27.4 71.58 24 72.7033 22 74.37C20.9609 75.2535 20.1416 76.3668 19.607 77.6216C19.0725 78.8765 18.8372 80.2385 18.92 81.6C18.8401 82.9965 19.0868 84.3924 19.6405 85.677C20.1943 86.9615 21.0398 88.0993 22.11 89C24.2633 90.76 27.34 91.64 31.34 91.64C36.9733 91.62 41.5534 90.0733 45.08 87Z" fill="#1A1A1A"/>
<path d="M157.26 37.67C163.18 44.73 166.137 54.1267 166.13 65.86C166.123 77.5933 163.167 86.9267 157.26 93.86C151.26 100.92 143.2 104.45 133.08 104.45C122.96 104.45 115.327 100.683 110.18 93.15V130.78H92.18V28.94H109.49V39.53C114.823 31.33 122.693 27.2267 133.1 27.22C143.2 27.22 151.253 30.7033 157.26 37.67V37.67ZM128.79 89.61C134.697 89.61 139.363 87.4633 142.79 83.17C146.217 78.8767 147.883 72.9167 147.79 65.29C147.79 57.75 146.143 52 142.85 48.04C139.557 44.08 134.86 42.1 128.76 42.1C122.76 42.1 118.093 44.1533 114.76 48.26C111.427 52.3667 109.76 58.2333 109.76 65.86C109.76 73.58 111.477 79.4467 114.91 83.46C118.15 87.56 122.777 89.61 128.79 89.61Z" fill="#1A1A1A"/>
<path d="M250 37.67C255.92 44.73 258.877 54.1267 258.87 65.86C258.863 77.5933 255.907 86.9267 250 93.86C244 100.92 235.94 104.45 225.82 104.45C215.7 104.45 208.067 100.683 202.92 93.15V130.78H184.92V28.94H202.24V39.53C207.573 31.33 215.443 27.2267 225.85 27.22C235.91 27.22 243.96 30.7033 250 37.67ZM221.53 89.61C227.437 89.61 232.103 87.4633 235.53 83.17C238.957 78.8767 240.623 72.9167 240.53 65.29C240.53 57.75 238.883 52 235.59 48.04C232.297 44.08 227.6 42.1 221.5 42.1C215.5 42.1 210.833 44.1533 207.5 48.26C204.167 52.3667 202.5 58.2333 202.5 65.86C202.5 73.58 204.22 79.4467 207.66 83.46C210.86 87.56 215.467 89.61 221.48 89.61H221.53Z" fill="#1A1A1A"/>
<path d="M275.13 79.31H292.73C293.21 83.89 294.713 87.1333 297.24 89.04C299.767 90.9467 303.75 91.9 309.19 91.9C318.923 91.9 323.79 88.9433 323.79 83.03C323.842 81.82 323.579 80.6172 323.027 79.5392C322.475 78.4613 321.652 77.545 320.64 76.88C318.54 75.4467 314.77 74.2533 309.33 73.3L300.89 71.87C284.77 69.2033 276.71 61.7633 276.71 49.55C276.71 42.5833 279.377 37.12 284.71 33.16C290.043 29.2 297.58 27.22 307.32 27.22C328.4 27.22 339.277 35.5533 339.95 52.22H322.95C322.757 47.84 321.373 44.7167 318.8 42.85C316.227 40.9833 312.41 40.0533 307.35 40.06C298.763 40.06 294.47 42.9233 294.47 48.65C294.425 49.7772 294.663 50.8978 295.163 51.9092C295.662 52.9207 296.407 53.7908 297.33 54.44C299.243 55.82 302.393 56.8933 306.78 57.66L316.08 59.09C325.047 60.71 331.557 63.2867 335.61 66.82C339.663 70.3533 341.69 75.2667 341.69 81.56C341.69 89 338.83 94.7233 333.11 98.73C327.39 102.737 319.327 104.737 308.92 104.73C287.36 104.763 276.097 96.29 275.13 79.31V79.31Z" fill="#1A1A1A"/>
<path d="M465.09 34.38C469.617 39.1533 471.883 45.6867 471.89 53.98V102.78H453.89V56.84C453.89 47.5067 449.643 42.84 441.15 42.84C439.04 42.7592 436.939 43.1604 435.007 44.0128C433.075 44.8653 431.363 46.1468 430 47.76C427.193 51.0467 425.787 55.84 425.78 62.14V102.78H407.92V56.84C407.92 47.5067 403.63 42.84 395.05 42.84C392.97 42.7797 390.903 43.1931 389.006 44.0489C387.109 44.9047 385.431 46.1806 384.1 47.78C381.28 51.0667 379.873 55.86 379.88 62.16V102.8H361.88V28.94H379.34V39.53C384.387 31.33 391.387 27.2267 400.34 27.22C411.6 27.22 419.233 31.8 423.24 40.96C428.767 31.8 436.397 27.22 446.13 27.22C454.243 27.22 460.563 29.6067 465.09 34.38V34.38Z" fill="#1A1A1A"/>
<path d="M610 42.68H587V82.6C587 84.98 587.643 86.6467 588.93 87.6C590.217 88.5533 592.373 89.02 595.4 89H608.57V102.74C604.66 103.12 594.5 103.31 592.11 103.31C584.11 103.31 578.267 101.807 574.58 98.8C570.893 95.7933 569.057 91 569.07 84.42V42.68H552.33V28.94H569.07V4.29001H587V28.94H610V42.68Z" fill="#1A1A1A"/>
<path d="M690.66 34.16C695.293 38.7933 697.607 45.3533 697.6 53.84V102.78H679.6V56.84C679.6 47.5067 674.973 42.84 665.72 42.84C663.531 42.7946 661.357 43.2091 659.338 44.0566C657.319 44.9041 655.501 46.1657 654 47.76C650.813 51.0467 649.217 55.6 649.21 61.42V102.78H631.21V4.29001H649.21V38.81C654.837 31.09 662.323 27.2267 671.67 27.22C679.71 27.22 686.04 29.5333 690.66 34.16V34.16Z" fill="#1A1A1A"/>
<path d="M526.75 89.04V28.94H488.84V42.68H508.73V89.04H488.84V102.78H546.64V89.04H526.75Z" fill="#1A1A1A"/>
<path d="M712.16 127.1V113.36H782.56V127.1H712.16Z" fill="#FF6D2D"/>
</svg>

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -678,6 +678,20 @@ export const ReduxActionTypes = {
DELETE_ORG_SUCCESS: "DELETE_ORG_SUCCESS",
SET_USER_CURRENT_GEO_LOCATION: "SET_USER_CURRENT_GEO_LOCATION",
SET_DISCONNECTING_GIT_APPLICATION: "SET_DISCONNECTING_GIT_APPLICATION",
SET_APP_THEMING_STACK: "SET_APP_THEMING_STACK",
FETCH_APP_THEMES_INIT: "FETCH_APP_THEMES_INIT",
FETCH_APP_THEMES_SUCCESS: "FETCH_APP_THEMES_SUCCESS",
FETCH_SELECTED_APP_THEME_INIT: "FETCH_SELECTED_APP_THEME_INIT",
FETCH_SELECTED_APP_THEME_SUCCESS: "FETCH_SELECTED_APP_THEME_SUCCESS",
UPDATE_SELECTED_APP_THEME_INIT: "UPDATE_SELECTED_APP_THEME_INIT",
UPDATE_SELECTED_APP_THEME_SUCCESS: "UPDATE_SELECTED_APP_THEME_SUCCESS",
CHANGE_SELECTED_APP_THEME_INIT: "CHANGE_SELECTED_APP_THEME_INIT",
CHANGE_SELECTED_APP_THEME_SUCCESS: "CHANGE_SELECTED_APP_THEME_SUCCESS",
SET_PREVIEW_APP_THEME: "SET_PREVIEW_APP_THEME",
SAVE_APP_THEME_INIT: "SAVE_APP_THEME_INIT",
SAVE_APP_THEME_SUCCESS: "SAVE_APP_THEME_SUCCESS",
DELETE_APP_THEME_INIT: "DELETE_APP_THEME_INIT",
DELETE_APP_THEME_SUCCESS: "DELETE_APP_THEME_SUCCESS",
GET_ALL_TEMPLATES_INIT: "GET_ALL_TEMPLATES_INIT",
GET_ALL_TEMPLATES_SUCCESS: "GET_ALL_TEMPLATES_SUCCESS",
UPDATE_TEMPLATE_FILTERS: "UPDATE_TEMPLATE_FILTERS",
@ -693,6 +707,9 @@ export const ReduxActionTypes = {
ENTITY_UPDATE_STARTED: "ENTITY_UPDATE_STARTED",
ENTITY_UPDATE_SUCCESS: "ENTITY_UPDATE_SUCCESS",
FETCH_PLUGIN_AND_JS_ACTIONS_SUCCESS: "FETCH_PLUGIN_AND_JS_ACTIONS_SUCCESS",
SET_APP_VIEWER_HEADER_HEIGHT: "SET_APP_VIEWER_HEADER_HEIGHT",
UPDATE_BETA_CARD_SHOWN: "UPDATE_BETA_CARD_SHOWN",
CLOSE_BETA_CARD_SHOWN: "CLOSE_BETA_CARD_SHOWN",
GET_DEFAULT_PLUGINS_REQUEST: "GET_DEFAULT_PLUGINS_REQUEST",
GET_DEFAULT_PLUGINS_SUCCESS: "GET_DEFAULT_PLUGINS_SUCCESS",
GET_TEMPLATE_INIT: "GET_TEMPLATES_INIT",
@ -861,9 +878,15 @@ export const ReduxActionErrorTypes = {
FETCH_RELEASES_ERROR: "FETCH_RELEASES_ERROR",
RESTART_SERVER_ERROR: "RESTART_SERVER_ERROR",
UPDATE_JS_ACTION_BODY_ERROR: "UPDATE_JS_ACTION_BODY_ERROR",
FETCH_APP_THEMES_ERROR: "FETCH_APP_THEMES_ERROR",
FETCH_SELECTED_APP_THEME_ERROR: "FETCH_SELECTED_APP_THEME_ERROR",
UPDATE_SELECTED_APP_THEME_ERROR: "UPDATE_SELECTED_APP_THEME_ERROR",
CHANGE_SELECTED_APP_THEME_ERROR: "CHANGE_SELECTED_APP_THEME_ERROR",
UPDATE_JS_FUNCTION_PROPERTY_ERROR: "UPDATE_JS_FUNCTION_PROPERTY_ERROR",
DELETE_ORG_ERROR: "DELETE_ORG_ERROR",
REFLOW_BETA_FLAGS_INIT_ERROR: "REFLOW_BETA_FLAGS_INIT_ERROR",
SAVE_APP_THEME_ERROR: "SAVE_APP_THEME_ERROR",
DELETE_APP_THEME_ERROR: "DELETE_APP_THEME_ERROR",
GET_ALL_TEMPLATES_ERROR: "GET_ALL_TEMPLATES_ERROR",
GET_SIMILAR_TEMPLATES_ERROR: "GET_SIMILAR_TEMPLATES_ERROR",
IMPORT_TEMPLATE_TO_ORGANISATION_ERROR:

View File

@ -25,6 +25,7 @@ export const VALID_FUNCTION_NAME_ERROR = () =>
`Must be a valid variable name (camelCase)`;
export const UNIQUE_NAME_ERROR = () => `Name must be unique`;
export const NAME_SPACE_ERROR = () => `Name must not have spaces`;
export const SPECIAL_CHARACTER_ERROR = () => `Name must be alphanumeric`;
export const FORM_VALIDATION_EMPTY_EMAIL = () => `Please enter an email`;
export const FORM_VALIDATION_INVALID_EMAIL = () =>
@ -1049,7 +1050,20 @@ export const TABLE_WIDGET_TOTAL_RECORD_TOOLTIP = () =>
export const CREATE_DATASOURCE_TOOLTIP = () => "Add a new datasource";
export const ADD_QUERY_JS_TOOLTIP = () => "Create New";
// Add datasource
export const GENERATE_APPLICATION_TITLE = () => "Generate Page";
export const GENERATE_APPLICATION_DESCRIPTION = () =>
"Quickly generate a page to perform CRUD operations on your database tables";
export const DELETE_ORG_SUCCESSFUL = () => "Organization deleted successfully";
// theming
export const CHANGE_APP_THEME = (name: string) => `Theme ${name} Applied`;
export const SAVE_APP_THEME = (name: string) => `Theme ${name} Saved`;
export const DELETE_APP_THEME = (name: string) => `Theme ${name} Deleted`;
export const DELETE_APP_THEME_WARNING = () =>
`Do you really want to delete this theme? This process cannot be undone.`;
export const APP_THEME_BETA_CARD_HEADING = () => `🎨 Theme your app`;
export const APP_THEME_BETA_CARD_CONTENT = () =>
`Customize your app's look through global styles. Full widget support coming soon`;
export const UPGRADE_TO_EE = (authLabel: string) =>
`Hello, I would like to upgrade and start using ${authLabel} authentication.`;

View File

@ -444,7 +444,7 @@ function CommentCard({
<CommentHeader data-cy="comments-card-header">
<HeaderSection>
<ProfileImage
side={25}
size={25}
source={profilePhotoUrl}
userName={authorName || ""}
/>

View File

@ -1,7 +1,8 @@
import React, { useState } from "react";
import styled from "styled-components";
import { Colors } from "constants/Colors";
import { ControlIcons, ControlIconName } from "icons/ControlIcons";
import { ControlIcons } from "icons/ControlIcons";
import _ from "lodash";
const ItemWrapper = styled.div<{ selected: boolean }>`
min-width: 32px;
@ -39,7 +40,7 @@ const FlexWrapper = styled.div`
`;
export interface ButtonTabOption {
icon: string;
icon: string | JSX.Element;
value: string;
width?: number;
}
@ -94,8 +95,13 @@ function ButtonTabComponent(props: ButtonTabComponentProps) {
>
{props.options.map(
({ icon, value, width = 24 }: ButtonTabOption, index: number) => {
const controlIconName: ControlIconName = icon;
const ControlIcon = ControlIcons[controlIconName];
let ControlIcon;
if (_.isString(icon)) {
const Icon = ControlIcons[icon];
ControlIcon = <Icon height={24} width={width} />;
} else {
ControlIcon = icon;
}
const isSelected = valueSet.has(value);
return (
<ItemWrapper
@ -111,7 +117,7 @@ function ButtonTabComponent(props: ButtonTabComponentProps) {
role="tab"
selected={isSelected}
>
<ControlIcon height={24} width={width} />
{ControlIcon}
</ItemWrapper>
);
},

View File

@ -0,0 +1,208 @@
import React from "react";
import store from "store";
import { Provider } from "react-redux";
import "@testing-library/jest-dom";
import {
render,
screen,
waitForElementToBeRemoved,
} from "@testing-library/react";
import { ThemeProvider } from "constants/DefaultTheme";
import ColorPickerComponent from "./ColorPickerComponentV2";
import { lightTheme } from "selectors/themeSelectors";
import userEvent from "@testing-library/user-event";
const getTestComponent = (handleOnChange: any = undefined) => (
<Provider store={store}>
<ThemeProvider theme={lightTheme}>
<ColorPickerComponent
changeColor={handleOnChange}
color="#ffffff"
showApplicationColors
showThemeColors
/>
</ThemeProvider>
</Provider>
);
describe("<ColorPicker />", () => {
it("Clicking the input should open the colorpicker", () => {
render(getTestComponent());
expect(screen.queryByTestId("color-picker")).not.toBeInTheDocument();
screen.getByRole("textbox").click();
expect(screen.getByTestId("color-picker")).toBeInTheDocument();
});
it("Clicking the color inside input should open the colorpicker", () => {
render(getTestComponent());
expect(screen.queryByTestId("color-picker")).not.toBeInTheDocument();
(screen.getByRole("textbox")?.previousSibling as HTMLElement)?.click();
expect(screen.getByTestId("color-picker")).toBeInTheDocument();
});
it("Focusing the input using mouse should open the colorpicker and keep the focus on the input", () => {
render(getTestComponent());
expect(screen.queryByTestId("color-picker")).not.toBeInTheDocument();
// Simulating clicking and focus
screen.getByRole("textbox").focus();
screen.getByRole("textbox").click();
expect(screen.getByRole("textbox")).toHaveFocus();
expect(screen.getByTestId("color-picker")).toBeInTheDocument();
});
});
describe("<ColorPicker /> - Keyboard Navigation", () => {
it("Pressing tab should focus the component", () => {
render(getTestComponent());
userEvent.tab();
expect(screen.getByRole("textbox")).toHaveFocus();
});
it("Pressing {Enter} should open the colorpicker", async () => {
render(getTestComponent());
userEvent.tab();
expect(screen.queryByTestId("color-picker")).toBeNull();
userEvent.keyboard("{Enter}");
expect(screen.queryByTestId("color-picker")).toBeInTheDocument();
});
it("Pressing {Escape} should close the colorpicker", async () => {
render(getTestComponent());
userEvent.tab();
expect(screen.queryByTestId("color-picker")).toBeNull();
userEvent.keyboard("{Enter}");
expect(screen.queryByTestId("color-picker")).toBeInTheDocument();
userEvent.keyboard("{Escape}");
await waitForElementToBeRemoved(screen.queryByTestId("color-picker"));
});
// it("Pressing {Tab} should shift sections in the colorpicker", async () => {
// render(getTestComponent());
// userEvent.tab();
// userEvent.keyboard("{Enter}");
// userEvent.tab();
// expect(
// document.querySelectorAll("[tabindex='0'].t--colorpicker-v2-color")[0],
// ).toHaveFocus();
// userEvent.tab();
// expect(
// document.querySelectorAll("[tabindex='0'].t--colorpicker-v2-color")[1],
// ).toHaveFocus();
// // Back to first color
// userEvent.tab();
// expect(
// document.querySelectorAll("[tabindex='0'].t--colorpicker-v2-color")[0],
// ).toHaveFocus();
// });
// it("Pressing {ArrowRight} should shift focus to color to the right", () => {
// render(getTestComponent());
// userEvent.tab();
// userEvent.keyboard("{Enter}");
// userEvent.tab();
// userEvent.tab();
// expect(
// document.querySelectorAll("[tabindex='0'].t--colorpicker-v2-color")[1],
// ).toHaveFocus();
// userEvent.keyboard("{ArrowRight}");
// expect(
// document.querySelectorAll("[tabindex='0'].t--colorpicker-v2-color")[1]
// .parentElement?.childNodes[1],
// ).toHaveFocus();
// });
// it("Pressing {ArrowLeft} should shift focus to color to the left", () => {
// render(getTestComponent());
// userEvent.tab();
// userEvent.keyboard("{Enter}");
// userEvent.tab();
// userEvent.tab();
// expect(
// document.querySelectorAll("[tabindex='0'].t--colorpicker-v2-color")[1],
// ).toHaveFocus();
// userEvent.keyboard("{ArrowRight}");
// userEvent.keyboard("{ArrowRight}");
// expect(
// document.querySelectorAll("[tabindex='0'].t--colorpicker-v2-color")[1]
// .parentElement?.childNodes[2],
// ).toHaveFocus();
// userEvent.keyboard("{ArrowLeft}");
// expect(
// document.querySelectorAll("[tabindex='0'].t--colorpicker-v2-color")[1]
// .parentElement?.childNodes[1],
// ).toHaveFocus();
// });
// it("Pressing {ArrowDown} should shift focus to color to the bottom", () => {
// render(getTestComponent());
// userEvent.tab();
// userEvent.keyboard("{Enter}");
// userEvent.tab();
// userEvent.tab();
// expect(
// document.querySelectorAll("[tabindex='0'].t--colorpicker-v2-color")[1],
// ).toHaveFocus();
// userEvent.keyboard("{ArrowDown}");
// expect(
// document.querySelectorAll("[tabindex='0'].t--colorpicker-v2-color")[1]
// .parentElement?.childNodes[10],
// ).toHaveFocus();
// });
// it("Pressing {ArrowUp} should shift focus to color to the top", () => {
// render(getTestComponent());
// userEvent.tab();
// userEvent.keyboard("{Enter}");
// userEvent.tab();
// userEvent.tab();
// expect(
// document.querySelectorAll("[tabindex='0'].t--colorpicker-v2-color")[1],
// ).toHaveFocus();
// userEvent.keyboard("{ArrowRight}");
// userEvent.keyboard("{ArrowDown}");
// userEvent.keyboard("{ArrowDown}");
// expect(
// document.querySelectorAll("[tabindex='0'].t--colorpicker-v2-color")[1]
// .parentElement?.childNodes[21],
// ).toHaveFocus();
// userEvent.keyboard("{ArrowUp}");
// expect(
// document.querySelectorAll("[tabindex='0'].t--colorpicker-v2-color")[1]
// .parentElement?.childNodes[11],
// ).toHaveFocus();
// });
// it("Pressing {Enter} should select the color in focus", async () => {
// const onColorChange = jest.fn();
// render(getTestComponent(onColorChange));
// userEvent.tab();
// userEvent.keyboard("{Enter}");
// userEvent.tab();
// userEvent.tab();
// userEvent.keyboard("{ArrowRight}");
// userEvent.keyboard("{Enter}");
// expect(onColorChange).toBeCalled();
// await waitForElementToBeRemoved(screen.queryByTestId("color-picker"));
// });
});

View File

@ -0,0 +1,502 @@
import React, { useEffect, useRef, useMemo, useState } from "react";
import styled from "styled-components";
import {
Popover,
InputGroup,
PopoverInteractionKind,
Classes,
} from "@blueprintjs/core";
import { ReactComponent as ColorPickerIcon } from "assets/icons/control/color-picker.svg";
import { debounce, get } from "lodash";
import { Colors } from "constants/Colors";
import { useSelector } from "store";
import { getSelectedAppThemeProperties } from "selectors/appThemingSelectors";
import {
colorsPropertyName,
getThemePropertyBinding,
} from "constants/ThemeConstants";
import { getWidgets } from "sagas/selectors";
import { extractColorsFromString } from "utils/helpers";
import { TAILWIND_COLORS } from "constants/ThemeConstants";
const FocusTrap = require("focus-trap-react");
const MAX_COLS = 10;
/**
* ----------------------------------------------------------------------------
* TYPES
*-----------------------------------------------------------------------------
*/
interface ColorPickerProps {
color: string;
changeColor: (color: string) => void;
showThemeColors?: boolean;
showApplicationColors?: boolean;
evaluatedColorValue?: string;
autoFocus?: boolean;
}
/**
* ----------------------------------------------------------------------------
* STYLED
*-----------------------------------------------------------------------------
*/
const ColorIcon = styled.div<{ color: string }>`
width: 24px;
height: 24px;
border: 3px solid ${(props) => props.theme.colors.propertyPane.bg};
position: absolute;
z-index: 1;
top: 6px;
left: 6px;
background: ${(props) => (props.color ? props.color : "transparent")};
`;
const ColorPickerIconContainer = styled.div`
position: absolute;
top: 6px;
left: 6px;
height: 24px;
width: 24px;
z-index: 1;
`;
const StyledInputGroup = styled(InputGroup)`
.${Classes.INPUT} {
box-shadow: none;
border-radius: 0;
&:focus {
box-shadow: none;
}
}
&&& input {
padding-left: 36px;
height: 36px;
border: 1px solid ${Colors.GREY_5};
background: ${(props) =>
props.theme.colors.propertyPane.multiDropdownBoxHoverBg};
color: ${(props) => props.theme.colors.propertyPane.label};
&:focus {
border: 1px solid ${Colors.GREY_9};
}
}
`;
const COLOR_BOX_CLASSES = `w-6 h-6 transform border rounded-full cursor-pointer hover:ring-1 ring-gray-500 t--colorpicker-v2-color focus:ring-2`;
interface ColorPickerPopupProps {
color: string;
containerRef: React.MutableRefObject<HTMLDivElement | null>;
setColor: (color: string) => unknown;
setIsOpen: (isOpen: boolean) => unknown;
changeColor: (color: string) => unknown;
showThemeColors?: boolean;
showApplicationColors?: boolean;
}
function ColorPickerPopup(props: ColorPickerPopupProps) {
const themeColors = useSelector(getSelectedAppThemeProperties).colors;
const widgets = useSelector(getWidgets);
const DSLStringified = JSON.stringify(widgets);
const applicationColors = useMemo(() => {
return extractColorsFromString(DSLStringified);
}, [DSLStringified]);
const {
changeColor,
color,
containerRef,
setColor,
setIsOpen,
showApplicationColors,
showThemeColors,
} = props;
const isClick = useRef(false);
const [isFocusTrapped, setIsFocusTrapped] = useState(false);
function handleFocus() {
if (!isClick.current) setIsFocusTrapped(true);
}
function handleClick() {
isClick.current = true;
}
function handleKeyDown() {
isClick.current = false;
}
const popup = (
<div
className="p-3 space-y-2 w-72"
data-testid="color-picker"
onClick={handleClick}
onFocus={handleFocus}
onKeyDown={handleKeyDown}
ref={containerRef}
>
{showThemeColors && (
<div className="space-y-2">
<h2 className="pb-2 font-semibold border-b">Color Styles</h2>
<section className="space-y-2">
<h3 className="text-xs">Theme Colors</h3>
<div className="grid grid-cols-10 gap-2">
{Object.keys(themeColors).map((colorKey, colorIndex) => (
<div
className={`${COLOR_BOX_CLASSES} ${
props.color === themeColors[colorKey] ? "ring-1" : ""
}`}
key={`color-picker-v2-${colorKey}`}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
setColor(themeColors[colorKey]);
setIsOpen(false);
changeColor(
getThemePropertyBinding(
`${colorsPropertyName}.${colorKey}`,
),
);
}}
style={{ backgroundColor: themeColors[colorKey] }}
tabIndex={colorIndex === 0 ? 0 : -1}
/>
))}
</div>
</section>
</div>
)}
{showApplicationColors && applicationColors.length > 0 && (
<section className="space-y-2">
<h3 className="text-xs">Application Colors</h3>
<div className="grid grid-cols-10 gap-2">
{Object.values(applicationColors).map(
(colorCode: string, colorIndex) => (
<div
className={`${COLOR_BOX_CLASSES} ring-gray-500 ${
color === colorCode ? "ring-1" : ""
}`}
key={colorCode}
onClick={() => {
setColor(colorCode);
setIsOpen(false);
changeColor(colorCode);
}}
style={{ backgroundColor: colorCode }}
tabIndex={colorIndex === 0 ? 0 : -1}
/>
),
)}
</div>
</section>
)}
<section className="space-y-2">
<h3 className="text-xs">All Colors</h3>
<div className="grid grid-cols-10 gap-2">
{Object.keys(TAILWIND_COLORS).map((colorKey, rowIndex) =>
Object.keys(get(TAILWIND_COLORS, `${colorKey}`)).map(
(singleColorKey, colIndex) => (
<div
className={`${COLOR_BOX_CLASSES} ${
color === TAILWIND_COLORS[colorKey][singleColorKey]
? "ring-1"
: ""
}`}
key={`all-colors-${colorKey}-${singleColorKey}`}
onClick={(e) => {
setIsOpen(false);
e.stopPropagation();
setColor(TAILWIND_COLORS[colorKey][singleColorKey]);
changeColor(TAILWIND_COLORS[colorKey][singleColorKey]);
}}
style={{
backgroundColor: TAILWIND_COLORS[colorKey][singleColorKey],
}}
tabIndex={rowIndex === 0 && colIndex === 0 ? 0 : -1}
/>
),
),
)}
<div
className={`${COLOR_BOX_CLASSES} ${
color === "#fff" ? "ring-1" : ""
}`}
onClick={() => {
setColor("#fff");
changeColor("#fff");
}}
tabIndex={-1}
/>
<div
className={`${COLOR_BOX_CLASSES} diagnol-cross ${
color === "transparent" ? "ring-1" : ""
}`}
onClick={() => {
setColor("transparent");
changeColor("transparent");
}}
tabIndex={-1}
/>
</div>
</section>
</div>
);
return (
<FocusTrap
active={isFocusTrapped}
focusTrapOptions={{
onDeactivate: () => {
setIsFocusTrapped(false);
},
clickOutsideDeactivates: true,
returnFocusOnDeactivate: true,
}}
>
{popup}
</FocusTrap>
);
}
/**
* ----------------------------------------------------------------------------
* COMPONENT
*-----------------------------------------------------------------------------
*/
interface LeftIconProps {
color: string;
handleInputClick?: () => void;
}
function LeftIcon(props: LeftIconProps) {
return props.color ? (
<ColorIcon
className="rounded-full cursor-pointer"
color={props.color}
onClick={props.handleInputClick}
/>
) : (
<ColorPickerIconContainer
className="cursor-pointer"
onClick={props.handleInputClick}
>
<ColorPickerIcon />
</ColorPickerIconContainer>
);
}
const DEBOUNCE_TIMER = 250;
const POPOVER_MODFIER = {
offset: {
offset: "0, 10px",
},
};
function ColorPickerComponent(props: ColorPickerProps) {
const inputRef = useRef<HTMLDivElement>(null);
const popupRef = useRef<HTMLDivElement>(null);
const inputGroupRef = useRef<HTMLInputElement>(null);
// isClick is used to track whether the input field is in focus by mouse click or by keyboard
// This is used since we open the popup only on mouse click not on keyboard focus
const isClick = useRef(false);
const [isOpen, setIsOpen] = React.useState(false);
const [color, setColor] = React.useState(
props.evaluatedColorValue || props.color,
);
const debouncedOnChange = React.useCallback(
debounce((color: string) => {
props.changeColor(color);
}, DEBOUNCE_TIMER),
[],
);
const currentFocus = useRef(0);
const handleKeydown = (e: KeyboardEvent) => {
if (isOpen) {
switch (e.key) {
case "Escape":
setIsOpen(false);
setTimeout(() => {
inputGroupRef.current?.focus();
}, 300);
e.stopPropagation();
break;
case "Tab":
currentFocus.current = 0;
if (document.activeElement === inputGroupRef.current) {
setTimeout(() => {
const firstElement = popupRef.current?.querySelectorAll(
"[tabindex='0']",
)?.[0] as any;
firstElement?.focus();
});
}
break;
case "Enter":
case " ":
(document.activeElement as any)?.click();
setTimeout(() => {
inputGroupRef.current?.focus();
}, 300);
e.preventDefault();
break;
case "ArrowRight": {
const totalColors =
document.activeElement?.parentElement?.childElementCount ?? 0;
currentFocus.current = currentFocus.current + 1;
if (
currentFocus.current % MAX_COLS === 0 ||
currentFocus.current >= totalColors
)
currentFocus.current =
currentFocus.current % MAX_COLS === 0
? currentFocus.current - MAX_COLS
: totalColors - (totalColors % MAX_COLS);
(document.activeElement?.parentElement?.childNodes[
currentFocus.current
] as any).focus();
break;
}
case "ArrowLeft": {
const totalColors =
document.activeElement?.parentElement?.childElementCount ?? 0;
currentFocus.current = currentFocus.current - 1;
if (
currentFocus.current < 0 ||
currentFocus.current % MAX_COLS === MAX_COLS - 1
) {
currentFocus.current = currentFocus.current + MAX_COLS;
if (currentFocus.current > totalColors)
currentFocus.current = totalColors - 1;
}
(document.activeElement?.parentElement?.childNodes[
currentFocus.current
] as any).focus();
break;
}
case "ArrowDown": {
const totalColors =
document.activeElement?.parentElement?.childElementCount ?? 0;
if (totalColors < MAX_COLS) break;
currentFocus.current = currentFocus.current + MAX_COLS;
if (currentFocus.current >= totalColors)
currentFocus.current = currentFocus.current % MAX_COLS;
(document.activeElement?.parentElement?.childNodes[
currentFocus.current
] as any).focus();
break;
}
case "ArrowUp": {
const totalColors =
document.activeElement?.parentElement?.childElementCount ?? 0;
if (totalColors < MAX_COLS) break;
currentFocus.current = currentFocus.current - MAX_COLS;
if (currentFocus.current < 0) {
const factor = Math.floor(totalColors / MAX_COLS) * MAX_COLS;
const nextIndex = factor + currentFocus.current + MAX_COLS;
if (nextIndex >= totalColors)
currentFocus.current = nextIndex - MAX_COLS;
else currentFocus.current = nextIndex;
}
(document.activeElement?.parentElement?.childNodes[
currentFocus.current
] as any).focus();
break;
}
}
} else if (document.activeElement === inputGroupRef.current) {
switch (e.key) {
case "Enter":
setIsOpen(true);
const firstElement = popupRef.current?.querySelectorAll(
"[tabindex='0']",
)?.[0] as any;
firstElement?.focus();
break;
case "Escape":
inputGroupRef.current?.blur();
}
}
};
useEffect(() => {
document.body.addEventListener("keydown", handleKeydown);
return () => {
document.body.removeEventListener("keydown", handleKeydown);
};
}, [handleKeydown]);
const handleChangeColor = (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value;
debouncedOnChange(value);
setColor(value);
};
// if props.color changes and state color is different,
// sets the state color to props color
useEffect(() => {
if (props.color !== color) {
setColor(props.color);
}
}, [props.color]);
const handleInputClick = () => {
isClick.current = true;
};
const handleOnInteraction = (nextOpenState: boolean) => {
if (isOpen !== nextOpenState) {
if (isClick.current) setIsOpen(true);
else setIsOpen(nextOpenState);
isClick.current = false;
}
};
return (
<div
className="popover-target-colorpicker t--colorpicker-v2-popover"
ref={inputRef}
>
<Popover
autoFocus={false}
boundary="viewport"
enforceFocus={false}
interactionKind={PopoverInteractionKind.CLICK}
isOpen={isOpen}
minimal
modifiers={POPOVER_MODFIER}
onInteraction={handleOnInteraction}
>
<StyledInputGroup
autoFocus={props.autoFocus}
inputRef={inputGroupRef}
leftIcon={
<LeftIcon color={color} handleInputClick={handleInputClick} />
}
onChange={handleChangeColor}
onClick={handleInputClick}
placeholder="enter color name or hex"
value={color}
/>
<ColorPickerPopup
changeColor={props.changeColor}
color={color}
containerRef={popupRef}
setColor={setColor}
setIsOpen={setIsOpen}
showApplicationColors={props.showApplicationColors}
showThemeColors={props.showThemeColors}
/>
</Popover>
</div>
);
}
export default ColorPickerComponent;

View File

@ -44,6 +44,7 @@ export interface DropdownSearchProps {
enableSearch?: boolean;
searchPlaceholder?: string;
onSearch?: (value: any) => void;
searchAutoFocus?: boolean;
}
export interface RenderDropdownOptionType {
@ -56,7 +57,7 @@ export interface RenderDropdownOptionType {
optionWidth: string;
}
type RenderOption = ({
export type RenderOption = ({
hasError,
index,
option,
@ -89,6 +90,7 @@ export type DropdownProps = CommonComponentProps &
errorMsg?: string; // If errorMsg is defined, we show dropDown's error state with the message.
placeholder?: string;
helperText?: string;
wrapperBgColor?: string;
/**
* if fillOptions is true,
* dropdown popover width will be same as dropdown width
@ -102,6 +104,7 @@ export type DropdownProps = CommonComponentProps &
defaultIcon?: IconName;
allowDeselection?: boolean; //prevents de-selection of the selected option
truncateOption?: boolean; // enabled wrapping and adding tooltip on option item of dropdown menu
portalClassName?: string;
customBadge?: JSX.Element;
selectedHighlightBg?: string;
};
@ -137,8 +140,7 @@ const DropdownTriggerWrapper = styled.div<{
props.isOpen && !props.disabled
? `
box-sizing: border-box;
border: 1px solid ${Colors.GREEN_1};
box-shadow: 0px 0px 0px 2px ${Colors.GREEN_2};
border: 1px solid var(--appsmith-color-black-900);
`
: null};
.${Classes.TEXT} {
@ -252,7 +254,8 @@ const Selected = styled.div<{
? props.hasError
? Colors.FAIR_PINK
: props.theme.colors.dropdown.hovered.bg
: Colors.WHITE}
: Colors.WHITE};
}
`;
export const DropdownContainer = styled.div<{ width: string; height?: string }>`
@ -282,11 +285,12 @@ const DropdownSelect = styled.div``;
export const DropdownWrapper = styled.div<{
width: string;
isOpen: boolean;
wrapperBgColor?: string;
}>`
width: ${(props) => props.width};
height: fit-content;
z-index: 1;
background-color: ${(props) => props.theme.colors.dropdown.menu.bg};
background-color: ${(props) => props.wrapperBgColor};
border: 1px solid ${(props) => props.theme.colors.dropdown.menu.border};
padding: ${(props) => props.theme.spaces[3]}px 0;
overflow: hidden;
@ -302,8 +306,7 @@ export const DropdownWrapper = styled.div<{
padding-left: 36px !important;
&:focus {
border: 1.2px solid ${Colors.GREEN_1};
box-shadow: 0px 0px 0px 2px ${Colors.GREEN_2};
border: 1.2px solid var(--appsmith-color-black-900);
}
}
@ -323,7 +326,7 @@ export const DropdownWrapper = styled.div<{
`;
const SearchComponentWrapper = styled.div`
margin: 0px 5px;
margin: 0px 8px 8px 8px;
`;
const DropdownOptionsWrapper = styled.div<{
@ -349,8 +352,7 @@ const OptionWrapper = styled.div<{
align-items: center;
min-height: 36px;
background-color: ${(props) =>
props.selected ? props.selectedHighlightBg || Colors.GREEN_3 : null};
props.selected ? `var(--appsmith-color-black-200)` : null};
&&& svg {
rect {
fill: ${(props) => props.theme.colors.dropdownIconBg};
@ -381,7 +383,7 @@ const OptionWrapper = styled.div<{
}
&:hover {
background-color: ${(props) => props.selectedHighlightBg || Colors.GREEN_3};
background-color: ${(props) => props.theme.colors.dropdown.menu.hover};
&&& svg {
rect {
@ -459,6 +461,14 @@ const SelectedDropDownHolder = styled.div`
overflow: hidden;
text-overflow: ellipsis;
}
&.custom-render-option > * {
// below if to override any custom margin and padding added in the render option
// because the above container already comes with a padding
// which will result broken UI
margin: 0 !important;
padding: 0 !important;
}
`;
const SelectedIcon = styled(Icon)`
@ -491,7 +501,6 @@ const SelectedIcon = styled(Icon)`
`;
const DropdownIcon = styled(Icon)`
margin-right: 7px;
svg {
fill: ${(props) =>
props.fillColor ? props.fillColor : props.theme.colors.dropdown.icon};
@ -606,7 +615,9 @@ function DefaultDropDownValueNode({
}
return (
<SelectedDropDownHolder>
<SelectedDropDownHolder
className={renderNode ? "custom-render-option" : ""}
>
{renderNode ? (
renderNode({
isSelectedNode: true,
@ -653,6 +664,7 @@ interface DropdownOptionsProps extends DropdownProps, DropdownSearchProps {
headerLabel?: string;
selected: DropdownOption | DropdownOption[];
optionWidth: string;
wrapperBgColor?: string;
isMultiSelect?: boolean;
allowDeselection?: boolean;
isOpen: boolean; // dropdown popover options flashes when closed, this prop helps to make sure it never happens again.
@ -686,10 +698,12 @@ export function RenderDropdownOptions(props: DropdownOptionsProps) {
data-testid="dropdown-options-wrapper"
isOpen={props.isOpen}
width={optionWidth}
wrapperBgColor={props.wrapperBgColor}
>
{props.enableSearch && (
<SearchComponentWrapper>
<SearchComponent
autoFocus={props.searchAutoFocus}
onSearch={onOptionSearch}
placeholder={props.searchPlaceholder || ""}
value={searchValue}
@ -727,7 +741,7 @@ export function RenderDropdownOptions(props: DropdownOptionsProps) {
return !option.isSectionHeader ? (
<OptionWrapper
aria-selected={isSelected}
className="t--dropdown-option"
className={`t--dropdown-option ${isSelected ? "selected" : ""}`}
key={index}
onClick={
// users should be able to unselect a selected option by clicking the option again.
@ -817,6 +831,7 @@ export default function Dropdown(props: DropdownProps) {
helperText,
removeSelectedOption,
hasError,
wrapperBgColor,
closeOnSpace = true,
} = { ...props };
const [isOpen, setIsOpen] = useState<boolean>(false);
@ -1055,6 +1070,7 @@ export default function Dropdown(props: DropdownProps) {
modifiers={{ arrow: { enabled: true } }}
onInteraction={(state) => !disabled && setIsOpen(state)}
popoverClassName={`${props.className} none-shadow-popover`}
portalClassName={props.portalClassName}
position={Position.BOTTOM_LEFT}
usePortal={!props.dontUsePortal}
>
@ -1068,6 +1084,7 @@ export default function Dropdown(props: DropdownProps) {
optionWidth={dropdownOptionWidth}
selected={selected ? selected : { id: undefined, value: undefined }}
selectedOptionClickHandler={selectedOptionClickHandler}
wrapperBgColor={wrapperBgColor}
/>
</Popover>
</DropdownContainer>

View File

@ -0,0 +1,87 @@
import React from "react";
import styled from "styled-components";
import {
Popover,
Menu,
MenuItem,
IMenuProps,
IMenuItemProps,
IPopoverProps,
} from "@blueprintjs/core";
/**
* ----------------------------------------------------------------------------
* TYPES
*-----------------------------------------------------------------------------
*/
type Props = {
children: React.ReactElement[] | React.ReactElement;
};
/**
* ----------------------------------------------------------------------------
* STYLED
*-----------------------------------------------------------------------------
*/
const StyledMenuItem = styled(MenuItem)`
margin: 0;
padding: 8px;
`;
const StyledMenu = styled(Menu)`
margin: 0;
padding: 0;
`;
/**
* ----------------------------------------------------------------------------
* COMPONENTS
*-----------------------------------------------------------------------------
*/
function Dropdown(props: IPopoverProps & Props) {
const { children, ...rest } = props;
const menus =
(Array.isArray(children) &&
children.find(
(child: any) => child.type.displayName === "DropdownList",
)) ||
undefined;
const trigger =
Array.isArray(children) &&
children.find((child: any) => child.type.displayName === "DropdownTrigger");
return (
<Popover
{...rest}
content={menus}
popoverClassName="dropdown-v2"
transitionDuration={-1}
>
{trigger}
</Popover>
);
}
function DropdownList(props: IMenuProps) {
return <StyledMenu {...props} />;
}
DropdownList.displayName = "DropdownList";
function DropdownTrigger(props: any) {
return <div {...props} />;
}
DropdownTrigger.displayName = "DropdownTrigger";
function DropdownItem(props: IMenuItemProps) {
return <StyledMenuItem {...props} />;
}
DropdownItem.displayName = "DropdownItem";
export { Dropdown, DropdownList, DropdownItem, DropdownTrigger };

View File

@ -150,6 +150,7 @@ import EditBoxLineIcon from "remixicon-react/EditBoxLineIcon";
import StarLineIcon from "remixicon-react/StarLineIcon";
import StarFillIcon from "remixicon-react/StarFillIcon";
import Settings2LineIcon from "remixicon-react/Settings2LineIcon";
import DownloadIcon from "remixicon-react/DownloadLineIcon";
import UploadCloud2LineIcon from "remixicon-react/UploadCloud2LineIcon";
import DownloadLineIcon from "remixicon-react/DownloadLineIcon";
import FileListLineIcon from "remixicon-react/FileListLineIcon";
@ -380,6 +381,7 @@ const ICON_LOOKUP = {
warning: <WarningIcon />,
widget: <WidgetIcon />,
workspace: <WorkspaceIcon />,
download2: <DownloadIcon />,
upgrade: <DvdLineIcon />,
};

View File

@ -3,11 +3,7 @@ import styled, { css } from "styled-components";
import { Alignment, Classes, Label, Position } from "@blueprintjs/core";
import { LabelPosition } from "components/constants";
import {
FontStyleTypes,
TextSize,
TEXT_SIZES,
} from "constants/WidgetConstants";
import { FontStyleTypes } from "constants/WidgetConstants";
import Tooltip from "./Tooltip";
import { isEllipsisActive } from "utils/helpers";
import { Colors } from "constants/Colors";
@ -20,7 +16,7 @@ export interface LabelWithTooltipProps {
color?: string;
compact: boolean;
disabled?: boolean;
fontSize?: TextSize;
fontSize?: string;
fontStyle?: string;
helpText?: string;
cyHelpTextClassName?: string;
@ -45,7 +41,7 @@ export interface StyledLabelProps {
color?: string;
compact: boolean;
disabled?: boolean;
fontSize?: TextSize;
fontSize?: string;
fontStyle?: string;
hasHelpText: boolean;
position?: LabelPosition;
@ -178,7 +174,7 @@ export const StyledLabel = styled(Label)<StyledLabelProps>`
${({ color, disabled, fontSize, fontStyle }) => `
color: ${disabled ? Colors.GREY_8 : color || "inherit"};
font-size: ${fontSize ? TEXT_SIZES[fontSize] : TEXT_SIZES.PARAGRAPH};
font-size: ${fontSize ?? "inherit"};
font-weight: ${
fontStyle?.includes(FontStyleTypes.BOLD) ? "bold" : "normal"
};

View File

@ -131,7 +131,7 @@ function SuggestionComponent(props: EntryComponentProps) {
<StyledSuggestionsComponent ref={mentionRef} {...parentProps}>
<div style={{ flexShrink: 0 }}>
<ProfileImage
side={25}
size={25}
source={`/api/${USER_PHOTO_URL}/${user?.username}`}
userName={user?.username || ""}
/>

View File

@ -337,8 +337,9 @@ const TextInput = forwardRef(
setInputValue(inputValue);
const inputValueValidation =
props.validator && props.validator(inputValue);
if (inputValueValidation && inputValueValidation.isValid) {
if (inputValueValidation) {
props.validator && setValidation(inputValueValidation);
return (
inputValueValidation.isValid &&
props.onChange &&

View File

@ -233,6 +233,7 @@ export const Toaster = {
pauseOnFocusLoss: !config.dispatchableAction && !config.hideProgressBar,
autoClose: false,
closeOnClick: true,
position: "top-center",
hideProgressBar: config.hideProgressBar,
},
);

View File

@ -23,6 +23,7 @@ export type TooltipProps = CommonComponentProps & {
modifiers?: Modifiers;
isOpen?: boolean;
onOpening?: typeof noop;
popoverClassName?: string;
donotUsePortal?: boolean;
};
@ -45,7 +46,8 @@ function TooltipComponent(props: TooltipProps) {
}}
onOpening={props.onOpening}
openOnTargetFocus={props.openOnTargetFocus}
popoverClassName={GLOBAL_STYLE_TOOLTIP_CLASSNAME}
popoverClassName={`${GLOBAL_STYLE_TOOLTIP_CLASSNAME} ${props.popoverClassName ??
""}`}
portalContainer={portalContainer as HTMLDivElement}
position={props.position}
usePortal={!props.donotUsePortal}

View File

@ -30,6 +30,7 @@ export enum ButtonBorderRadiusTypes {
ROUNDED = "ROUNDED",
CIRCLE = "CIRCLE",
}
export type ButtonBorderRadius = keyof typeof ButtonBorderRadiusTypes;
export enum ButtonBoxShadowTypes {

View File

@ -12,7 +12,6 @@ import { ThemeProp } from "components/ads/common";
import _ from "lodash";
import {
ButtonStyleTypes,
ButtonBoxShadow,
ButtonBoxShadowTypes,
ButtonBorderRadius,
ButtonBorderRadiusTypes,
@ -279,8 +278,7 @@ type ButtonStyleProps = {
buttonStyle?: ButtonStyleType;
prevButtonStyle?: ButtonStyleType;
buttonVariant?: ButtonVariant;
boxShadow?: ButtonBoxShadow;
boxShadowColor?: string;
boxShadow?: string;
borderRadius?: ButtonBorderRadius;
iconName?: IconName;
iconAlign?: Alignment;
@ -291,7 +289,6 @@ export function BaseButton(props: IButtonProps & ButtonStyleProps) {
const {
borderRadius,
boxShadow,
boxShadowColor,
buttonColor,
buttonStyle,
buttonVariant,
@ -313,7 +310,6 @@ export function BaseButton(props: IButtonProps & ButtonStyleProps) {
alignText={iconName ? Alignment.LEFT : Alignment.CENTER}
borderRadius={borderRadius}
boxShadow={boxShadow}
boxShadowColor={boxShadowColor}
buttonColor={buttonColor}
buttonStyle={buttonStyle}
buttonVariant={buttonVariant}
@ -335,7 +331,6 @@ export function BaseButton(props: IButtonProps & ButtonStyleProps) {
alignText={iconName ? Alignment.RIGHT : Alignment.CENTER}
borderRadius={borderRadius}
boxShadow={boxShadow}
boxShadowColor={boxShadowColor}
buttonColor={buttonColor}
buttonStyle={buttonStyle}
buttonVariant={buttonVariant}

View File

@ -1,27 +1,9 @@
import styled from "styled-components";
/**
* Common component, mostly use to show loader / message
*
* Used By:
* AppViewerPageContainer
* - parent component AppViewer -> AppViewerBody's height calculated good enough
* - inherited height works fine here.
* CanvasContainer
* - calculated height looks good
* DefaultOrgPage
* - calculated height looks good
*/
export default styled.div<{
isInheritedHeight?: boolean;
}>`
height: ${(props) =>
props.isInheritedHeight
? "inherit"
: `calc(100vh - ${props.theme.smallHeaderHeight})`};
export default styled.div`
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
`;

View File

@ -10,6 +10,7 @@ interface SearchProps {
placeholder: string;
value: string;
className?: string;
autoFocus?: boolean;
}
const SearchComponentWrapper = styled.div`
@ -112,6 +113,7 @@ class SearchComponent extends React.Component<
return (
<SearchComponentWrapper>
<SearchInputWrapper
autoFocus={this.props.autoFocus}
className={`${this.props.className} t--search-input`}
leftIcon="search"
onChange={this.handleSearch}

View File

@ -2,7 +2,6 @@ import React, { ReactNode } from "react";
import styled from "styled-components";
import { ContainerStyle } from "widgets/ContainerWidget/component";
import { Color } from "constants/Colors";
import { Theme } from "constants/DefaultTheme";
export enum BoxShadowTypes {
NONE = "NONE",
@ -23,55 +22,19 @@ export interface WidgetStyleContainerProps {
borderWidth?: number;
borderRadius?: number;
boxShadow?: BoxShadow;
boxShadowColor?: string;
className?: string;
}
// get box shadow style string based on boxShadow and boxShadowColor
const getBoxShadow = ({
boxShadow,
boxShadowColor,
theme,
}: {
boxShadow?: BoxShadow;
boxShadowColor?: string;
theme: Theme;
}) => {
switch (boxShadow) {
case BoxShadowTypes.VARIANT1:
return `0px 0px 4px 3px ${boxShadowColor ||
theme.colors.button.boxShadow.default.variant1}`;
case BoxShadowTypes.VARIANT2:
return `3px 3px 4px ${boxShadowColor ||
theme.colors.button.boxShadow.default.variant2}`;
case BoxShadowTypes.VARIANT3:
return `0px 1px 3px ${boxShadowColor ||
theme.colors.button.boxShadow.default.variant3}`;
case BoxShadowTypes.VARIANT4:
return `2px 2px 0px ${boxShadowColor ||
theme.colors.button.boxShadow.default.variant4}`;
case BoxShadowTypes.VARIANT5:
return `-2px -2px 0px ${boxShadowColor ||
theme.colors.button.boxShadow.default.variant5}`;
default:
return "none";
}
};
const WidgetStyle = styled.div<WidgetStyleContainerProps>`
height: 100%;
width: 100%;
overflow: hidden;
border-radius: ${(props) => props.borderRadius}px;
box-shadow: ${(props) => getBoxShadow(props)} !important;
border-radius: ${({ borderRadius }) => borderRadius};
box-shadow: ${(props) => props.boxShadow} !important;
border-width: ${(props) => props.borderWidth}px;
border-color: ${(props) => props.borderColor || "transparent"};
border-style: solid;
& > div {
${(props) =>
props.containerStyle !== "none"
? `
border-width: ${props.borderWidth}px;
border-radius: ${props.borderRadius}px;
border-color: ${props.borderColor || "transparent"};
border-style: solid;`
: ""}
height: 100%;
width: 100%;
overflow: hidden;

View File

@ -0,0 +1,11 @@
import React from "react";
function BetaCard() {
return (
<div className="py-0.5 px-1 text-xs font-semibold text-gray-700 uppercase border border-gray-700">
beta
</div>
);
}
export default BetaCard;

View File

@ -29,8 +29,11 @@ const buttonStyles = css<Partial<ButtonProps>>`
border-radius: 0;
background: ${(props) =>
props.filled || props.outline ? "inherit" : "transparent"};
border-radius: ${({ borderRadius }) => borderRadius};
box-shadow: ${({ boxShadow }) => `${boxShadow}`} !important;
width: ${(props) => (props.fluid ? "100%" : "auto")};
height: 100%;
padding: 0 10px;
}
&&&&&& {
&.bp3-button span {
@ -86,6 +89,9 @@ export type ButtonProps = {
fluid?: boolean;
skin?: Skin;
target?: string;
borderRadius?: string;
boxShadow?: string;
boxShadowColor?: string;
};
export const Button = (props: ButtonProps) => {
@ -129,6 +135,9 @@ export const Button = (props: ButtonProps) => {
} else
return (
<StyledButton
borderRadius={props.borderRadius}
boxShadow={props.boxShadow}
boxShadowColor={props.boxShadowColor}
icon={icon}
rightIcon={rightIcon}
{...baseProps}

View File

@ -17,6 +17,8 @@ import { commentModeSelector } from "selectors/commentsSelectors";
import { getIsDraggingForSelection } from "selectors/canvasSelectors";
import MultiSelectPropertyPane from "pages/Editor/MultiSelectPropertyPane";
import { getWidgets } from "sagas/selectors";
import { ThemePropertyPane } from "pages/Editor/ThemePropertyPane";
import { getAppThemingStack } from "selectors/appThemingSelectors";
type Props = {
width: number;
@ -40,6 +42,7 @@ export const PropertyPaneSidebar = memo((props: Props) => {
const canvasWidgets = useSelector(getWidgets);
const isPreviewMode = useSelector(previewModeSelector);
const isCommentMode = useSelector(commentModeSelector);
const themingStack = useSelector(getAppThemingStack);
const selectedWidgetIds = useSelector(getSelectedWidgets);
const selectedWidgets = useMemo(
() =>
@ -64,16 +67,18 @@ export const PropertyPaneSidebar = memo((props: Props) => {
*/
const propertyPane = useMemo(() => {
switch (true) {
case selectedWidgets.length == 0:
return <CanvasPropertyPane />;
case selectedWidgets.length > 1:
return <MultiSelectPropertyPane />;
case selectedWidgets.length === 1:
return <WidgetPropertyPane />;
case themingStack.length > 0:
return <ThemePropertyPane />;
case selectedWidgets.length === 0:
return <CanvasPropertyPane />;
default:
return <CanvasPropertyPane />;
}
}, [selectedWidgets.length, isDraggingForSelection]);
}, [selectedWidgets.length, isDraggingForSelection, themingStack.join(",")]);
return (
<div className="relative">

View File

@ -190,7 +190,7 @@ export const EntityExplorerSidebar = memo((props: Props) => {
return (
<div
className={classNames({
[`js-entity-explorer t--entity-explorer transform transition-all flex h-full duration-400 border-r border-gray-200 ${tailwindLayers.entityExplorer}`]: true,
[`js-entity-explorer t--entity-explorer transform transition-all flex h-full duration-400 border-r border-gray-200 ${tailwindLayers.entityExplorer}`]: true,
"relative ": pinned && !isPreviewMode,
"-translate-x-full": (!pinned && !active) || isPreviewMode,
"shadow-xl": !pinned,

View File

@ -1,114 +1,64 @@
import * as React from "react";
import styled from "styled-components";
import { Button, ButtonGroup, IButtonProps } from "@blueprintjs/core";
import TooltipComponent from "components/ads/Tooltip";
import BaseControl, { ControlProps } from "./BaseControl";
import { ControlIcons } from "icons/ControlIcons";
import { ThemeProp } from "components/ads/common";
import {
ButtonBorderRadius,
ButtonBorderRadiusTypes,
} from "components/constants";
import { replayHighlightClass } from "globalStyles/portals";
const StyledButtonGroup = styled(ButtonGroup)`
height: 33px;
`;
const StyledButton = styled(Button)<ThemeProp & IButtonProps>`
border: ${(props) =>
props.active ? `1px solid #6A86CE` : `1px solid #A9A7A7`};
border-radius: 0;
box-shadow: none !important;
background-image: none !important;
background-color: #ffffff !important;
& > div {
display: flex;
}
&.bp3-active {
box-shadow: none !important;
background-color: #ffffff !important;
}
&:hover {
background-color: #ffffff !important;
}
`;
import { borderRadiusOptions } from "constants/ThemeConstants";
import { ButtonTabComponent } from "components/ads";
/**
* ----------------------------------------------------------------------------
* TYPES
*-----------------------------------------------------------------------------
*/
export interface BorderRadiusOptionsControlProps extends ControlProps {
propertyValue: ButtonBorderRadius | undefined;
onChange: (borderRaidus: ButtonBorderRadius) => void;
options: any[];
propertyValue: string | undefined;
}
const options = Object.keys(borderRadiusOptions).map((optionKey) => ({
icon: (
<TooltipComponent
content={
<div>
<div>{optionKey}</div>
</div>
}
key={optionKey}
openOnTargetFocus={false}
>
<button>
<div
className="w-5 h-5 border-t-2 border-l-2 border-gray-500"
style={{ borderTopLeftRadius: borderRadiusOptions[optionKey] }}
/>
</button>
</TooltipComponent>
),
value: borderRadiusOptions[optionKey],
}));
/**
* ----------------------------------------------------------------------------
* COMPONENT
*-----------------------------------------------------------------------------
*/
class BorderRadiusOptionsControl extends BaseControl<
BorderRadiusOptionsControlProps
> {
constructor(props: BorderRadiusOptionsControlProps) {
super(props);
}
static getControlType() {
return "BORDER_RADIUS_OPTIONS";
}
public render() {
const { options, propertyValue } = this.props;
return (
<StyledButtonGroup className={replayHighlightClass} fill>
{options.map((option: ButtonBorderRadius) => {
const active =
option === ButtonBorderRadiusTypes.SHARP
? propertyValue === option || propertyValue === undefined
: propertyValue === option;
const icon =
option === ButtonBorderRadiusTypes.SHARP ? (
<ControlIcons.BORDER_RADIUS_SHARP color="#979797" width={15} />
) : option === ButtonBorderRadiusTypes.ROUNDED ? (
<ControlIcons.BORDER_RADIUS_ROUNDED color="#979797" width={15} />
) : (
<ControlIcons.BORDER_RADIUS_CIRCLE color="#979797" width={15} />
);
return (
<StyledButton
active={active}
icon={icon}
key={option}
large
onClick={() => this.toggleOption(option)}
/>
);
})}
{/* <StyledButton
active={propertyValue === ButtonBorderRadiusTypes.SHARP || undefined}
icon={<ControlIcons.BORDER_RADIUS_SHARP color="#979797" width={15} />}
large
onClick={() => this.toggleOption(ButtonBorderRadiusTypes.SHARP)}
/>
<StyledButton
active={propertyValue === ButtonBorderRadiusTypes.ROUNDED}
icon={
<ControlIcons.BORDER_RADIUS_ROUNDED color="#979797" width={15} />
}
large
onClick={() => this.toggleOption(ButtonBorderRadiusTypes.ROUNDED)}
/>
<StyledButton
active={propertyValue === ButtonBorderRadiusTypes.CIRCLE}
icon={
<ControlIcons.BORDER_RADIUS_CIRCLE color="#979797" width={15} />
}
large
onClick={() => this.toggleOption(ButtonBorderRadiusTypes.CIRCLE)}
/> */}
</StyledButtonGroup>
<ButtonTabComponent
options={options}
selectButton={(value) => {
this.updateProperty(this.props.propertyName, value);
}}
values={this.props.evaluatedValue ? [this.props.evaluatedValue] : []}
/>
);
}
private toggleOption = (option: ButtonBorderRadius) => {
this.updateProperty(this.props.propertyName, option);
};
}
export default BorderRadiusOptionsControl;

View File

@ -1,141 +1,58 @@
import * as React from "react";
import styled from "styled-components";
import { Button, ButtonGroup, IButtonProps } from "@blueprintjs/core";
import BaseControl, { ControlProps } from "./BaseControl";
import { ControlIcons } from "icons/ControlIcons";
import { ThemeProp } from "components/ads/common";
import { ButtonBoxShadow, ButtonBoxShadowTypes } from "components/constants";
import { replayHighlightClass } from "globalStyles/portals";
const StyledButtonGroup = styled(ButtonGroup)`
display: grid !important;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
height: 100%;
`;
const StyledButton = styled(Button)<ThemeProp & IButtonProps>`
margin-right: 0 !important;
border: ${(props) =>
props.active ? `1px solid #6A86CE` : `1px solid #E0DEDE`};
border-radius: 0;
box-shadow: none !important;
background-image: none;
background-color: #ffffff !important;
& > div {
display: flex;
}
&.bp3-active {
box-shadow: none !important;
background-color: #ffffff !important;
}
&:hover {
background-color: #ffffff !important;
}
`;
import TooltipComponent from "components/ads/Tooltip";
import { boxShadowOptions } from "constants/ThemeConstants";
import CloseLineIcon from "remixicon-react/CloseLineIcon";
import { ButtonTabComponent } from "components/ads";
export interface BoxShadowOptionsControlProps extends ControlProps {
propertyValue: ButtonBoxShadow | undefined;
propertyValue: string | undefined;
}
const buttonConfigs = [
{
variant: ButtonBoxShadowTypes.NONE,
icon: {
element: ControlIcons.BOX_SHADOW_NONE,
color: "#CACACA",
width: 16,
},
},
{
variant: ButtonBoxShadowTypes.VARIANT1,
icon: {
element: ControlIcons.BOX_SHADOW_VARIANT1,
height: 32,
width: 40,
},
},
{
variant: ButtonBoxShadowTypes.VARIANT2,
icon: {
element: ControlIcons.BOX_SHADOW_VARIANT2,
height: 28,
width: 36,
},
},
{
variant: ButtonBoxShadowTypes.VARIANT3,
icon: {
element: ControlIcons.BOX_SHADOW_VARIANT3,
height: 27,
width: 32,
},
},
{
variant: ButtonBoxShadowTypes.VARIANT4,
icon: {
element: ControlIcons.BOX_SHADOW_VARIANT4,
height: 26,
width: 34,
},
},
{
variant: ButtonBoxShadowTypes.VARIANT5,
icon: {
element: ControlIcons.BOX_SHADOW_VARIANT5,
height: 26,
width: 34,
},
},
];
const options = Object.keys(boxShadowOptions).map((optionKey) => ({
icon: (
<TooltipComponent
content={
<div>
<div>{optionKey}</div>
</div>
}
key={optionKey}
openOnTargetFocus={false}
>
<button>
<div
className="flex items-center justify-center w-5 h-5 bg-white"
style={{ boxShadow: boxShadowOptions[optionKey] }}
>
{boxShadowOptions[optionKey] === "none" && (
<CloseLineIcon className="text-gray-700" />
)}
</div>
</button>
</TooltipComponent>
),
value: boxShadowOptions[optionKey],
}));
class BoxShadowOptionsControl extends BaseControl<
BoxShadowOptionsControlProps
> {
constructor(props: BoxShadowOptionsControlProps) {
super(props);
}
static getControlType() {
return "BOX_SHADOW_OPTIONS";
}
public render() {
const { propertyValue } = this.props;
return (
<StyledButtonGroup className={replayHighlightClass} fill>
{buttonConfigs.map(({ icon, variant }) => {
const active =
variant === ButtonBoxShadowTypes.NONE
? propertyValue === variant || propertyValue === undefined
: propertyValue === variant;
return (
<StyledButton
active={active}
icon={
<icon.element
color={icon.color}
height={icon.height}
keepColors
width={icon.width}
/>
}
key={variant}
large
onClick={() => this.toggleOption(variant)}
/>
);
})}
</StyledButtonGroup>
<ButtonTabComponent
options={options}
selectButton={(value) => {
this.updateProperty(this.props.propertyName, value);
}}
values={this.props.evaluatedValue ? [this.props.evaluatedValue] : []}
/>
);
}
private toggleOption = (option: ButtonBoxShadow) => {
this.updateProperty(this.props.propertyName, option);
};
}
export default BoxShadowOptionsControl;

View File

@ -24,6 +24,7 @@ const StyledButton = styled(Button)<ThemeProp & IButtonProps>`
box-shadow: none !important;
background-image: none !important;
background-color: #ffffff !important;
min-height: 100% !important;
& > div {
display: flex;
}

View File

@ -9,7 +9,6 @@ import orderBy from "lodash/orderBy";
import isString from "lodash/isString";
import isUndefined from "lodash/isUndefined";
import { Category, Size } from "components/ads/Button";
import { Colors } from "constants/Colors";
import { ButtonPlacementTypes } from "components/constants";
import { DraggableListCard } from "components/ads/DraggableListCard";
@ -179,6 +178,7 @@ class ButtonListControl extends BaseControl<ControlProps, State> {
"Group Button ",
groupButtonsArray.map((groupButton: any) => groupButton.label),
);
groupButtons = {
...groupButtons,
[newGroupButtonId]: {
@ -187,11 +187,12 @@ class ButtonListControl extends BaseControl<ControlProps, State> {
label: newGroupButtonLabel,
menuItems: {},
buttonType: "SIMPLE",
buttonColor: Colors.GREEN,
placement: ButtonPlacementTypes.CENTER,
widgetId: generateReactKey(),
isDisabled: false,
isVisible: true,
buttonColor: this.props.widgetProperties.childStylesheet.button
.buttonColor,
},
};

View File

@ -1,20 +1,29 @@
import React from "react";
import BaseControl, { ControlProps } from "./BaseControl";
import ColorPickerComponent from "components/ads/ColorPickerComponent";
import ColorPickerComponent from "components/ads/ColorPickerComponentV2";
import { isDynamicValue } from "utils/DynamicBindingUtils";
class ColorPickerControl extends BaseControl<ColorPickerControlProps> {
handleChangeColor = (color: string) => {
this.updateProperty(this.props.propertyName, color);
};
render() {
const computedEvaluatedValue = Array.isArray(this.props.evaluatedValue)
? this.props.evaluatedValue[0]
: this.props.evaluatedValue;
return (
<ColorPickerComponent
changeColor={this.handleChangeColor}
color={
this.props.propertyValue
? this.props.propertyValue
: this.props.defaultColor
this.props.propertyValue && isDynamicValue(this.props.propertyValue)
? computedEvaluatedValue
: this.props.propertyValue || ""
}
showApplicationColors
showThemeColors
/>
);
}

View File

@ -3,6 +3,7 @@ import BaseControl, { ControlProps } from "./BaseControl";
import { StyledDropDown, StyledDropDownContainer } from "./StyledControls";
import { DropdownOption } from "components/ads/Dropdown";
import { isNil } from "lodash";
import { isDynamicValue } from "utils/DynamicBindingUtils";
class DropDownControl extends BaseControl<DropDownControlProps> {
render() {
@ -19,8 +20,14 @@ class DropDownControl extends BaseControl<DropDownControlProps> {
);
}
const computedValue =
!isNil(this.props.propertyValue) &&
isDynamicValue(this.props.propertyValue)
? this.props.evaluatedValue
: this.props.propertyValue;
const selected: DropdownOption = options.find(
(option) => option.value === this.props.propertyValue,
(option) => option.value === computedValue,
);
if (selected) {

View File

@ -19,6 +19,7 @@ import { DraggableListCard } from "components/ads/DraggableListCard";
import { StyledPropertyPaneButton } from "./StyledControls";
import { getNextEntityName } from "utils/AppsmithUtils";
import { InputText } from "./InputTextControl";
import { JSONFormWidgetProps } from "widgets/JSONFormWidget/widget";
type DroppableItem = BaseItemProps & {
index: number;
@ -119,7 +120,10 @@ class FieldConfigurationControl extends BaseControl<ControlProps, State> {
if (this.isArrayItem()) return;
const { propertyValue = {}, propertyName, widgetProperties } = this.props;
const { widgetName } = widgetProperties;
const {
childStylesheet,
widgetName,
} = widgetProperties as JSONFormWidgetProps;
const schema: Schema = propertyValue;
const existingKeys = getKeysFromSchema(schema, ["identifier", "accessor"]);
const schemaItems = Object.values(schema);
@ -132,19 +136,33 @@ class FieldConfigurationControl extends BaseControl<ControlProps, State> {
isCustomField: true,
skipDefaultValueProcessing: true,
identifier: nextFieldKey,
fieldThemeStylesheets: childStylesheet,
});
schemaItem.position = lastSchemaItemPosition + 1;
const path = `${propertyName}.${nextFieldKey}`;
if (isEmpty(widgetProperties.schema)) {
const newSchema = {
schema: SchemaParser.parse(widgetProperties.widgetName, {}),
};
set(newSchema, `${propertyName}.${nextFieldKey}`, schemaItem);
set(newSchema, path, schemaItem);
this.updateProperty("schema", newSchema.schema);
} else {
this.updateProperty(`${propertyName}.${nextFieldKey}`, schemaItem);
/**
* TODO(Ashit): Not suppose to update the whole schema but just
* the path within the schema. This is just a hack to make sure
* the new added paths gets into the dynamicBindingPathList until
* the updateProperty function is fixed.
*/
const updatedSchema = {
schema: klona(widgetProperties.schema),
};
set(updatedSchema, path, schemaItem);
this.updateProperty("schema", updatedSchema.schema);
}
};

View File

@ -20,6 +20,7 @@ const IconSelectContainerStyles = createGlobalStyle<{
}>`
.bp3-select-popover {
width: ${({ targetWidth }) => targetWidth}px;
background: white;
.bp3-input-group {
margin: 5px !important;

View File

@ -209,7 +209,7 @@ class PrimaryColumnsControl extends BaseControl<ControlProps, State> {
const columnProps: ColumnProperties = getDefaultColumnProperties(
newColumnName,
nextIndex,
this.props.widgetProperties.widgetName,
this.props.widgetProperties,
true,
);
const tableStyles = getTableStyles(this.props.widgetProperties);

View File

@ -35,6 +35,9 @@ export const ControlWrapper = styled.div<ControlWrapperProps>`
&&& > label {
display: inline-block;
}
&:focus-within .reset-button {
display: block;
}
`;
export const ControlPropertyLabelContainer = styled.div`

View File

@ -0,0 +1,180 @@
import React from "react";
import styled from "styled-components";
import {
IButtonProps,
MaybeElement,
Button as BlueprintButton,
} from "@blueprintjs/core";
import { IconName } from "@blueprintjs/icons";
import { withTooltip } from "components/wds";
import { Colors } from "constants/Colors";
import _ from "lodash";
import {
ButtonPlacement,
ButtonVariant,
ButtonVariantTypes,
} from "components/constants";
import {
getComplementaryGrayscaleColor,
lightenColor,
} from "widgets/WidgetUtils";
import { borderRadiusOptions } from "constants/ThemeConstants";
import withRecaptcha, { RecaptchaProps } from "./withRecaptcha";
type ButtonStyleProps = {
buttonColor?: string;
buttonVariant?: ButtonVariant;
iconName?: IconName;
placement?: ButtonPlacement;
justifyContent?:
| "flex-start"
| "flex-end"
| "center"
| "space-between"
| "space-around"
| "space-evenly";
};
export interface ButtonProps
extends IButtonProps,
ButtonStyleProps,
RecaptchaProps {
variant?: keyof typeof VariantTypes;
boxShadow?: string;
borderRadius?: string;
tooltip?: string;
children?: React.ReactNode;
leftIcon?: IconName | MaybeElement;
isDisabled?: boolean;
isLoading?: boolean;
}
enum VariantTypes {
solid = "solid",
outline = "outline",
ghost = "ghost",
link = "link",
}
export const StyledButton = styled((props) => (
<BlueprintButton
{..._.omit(props, [
"borderRadius",
"boxShadow",
"buttonColor",
"buttonVariant",
"variant",
"justifyContent",
])}
/>
))<ButtonProps>`
gap: 8px;
height: 100%;
outline: none;
padding: 0px 10px;
background-image: none !important;
border-radius: ${({ borderRadius }) => borderRadius};
box-shadow: ${({ boxShadow }) => `${boxShadow}`} !important;
justify-content: ${({ justifyContent }) => `${justifyContent}`} !important;
flex-direction: ${({ iconAlign }) => `${iconAlign}`};
${({ buttonColor }) => `
&.button--solid {
&:enabled {
background: ${buttonColor};
color: ${getComplementaryGrayscaleColor(buttonColor)}
}
}
&.button--outline {
&:enabled {
background: none;
border: 1px solid ${buttonColor};
color: ${buttonColor};
}
&:enabled:hover {
background: ${lightenColor(buttonColor)};
}
}
&.button--ghost {
&:enabled {
background: none;
color: ${buttonColor};
}
&:enabled:hover {
background: ${lightenColor(buttonColor)};
}
}
&.button--link {
&:enabled {
background: none;
color: ${buttonColor};
}
&:enabled:hover {
text-decoration: underline;
}
}
&:disabled {
background-color: ${Colors.GREY_1} !important;
color: ${Colors.GREY_9} !important;
box-shadow: none !important;
pointer-events: none;
border-color: ${Colors.GREY_1} !important;
> span {
color: ${Colors.GREY_9} !important;
}
}
& > * {
margin-right: 0;
}
& > span, & > span.bp3-icon {
max-height: 100%;
max-width: 99%;
text-overflow: ellipsis;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
line-height: normal;
color: inherit;
}
`}
`;
function Button(props: ButtonProps) {
const { children, isDisabled, isLoading, leftIcon, ...rest } = props;
return (
<StyledButton
{...rest}
className={`button--${props.variant} ${props.className}`}
disabled={isDisabled}
icon={leftIcon}
loading={isLoading}
text={children}
/>
);
}
Button.defaultProps = {
buttonVariant: ButtonVariantTypes.PRIMARY,
disabled: false,
text: "Button Text",
minimal: true,
variant: "solid",
buttonColor: "#553DE9",
borderRadius: borderRadiusOptions.md,
justifyContent: "center",
} as ButtonProps;
export default withRecaptcha(withTooltip(Button));

View File

@ -0,0 +1,172 @@
import React, { useRef, useState } from "react";
import styled from "styled-components";
import { useScript, ScriptStatus, AddScriptTo } from "utils/hooks/useScript";
import {
GOOGLE_RECAPTCHA_KEY_ERROR,
GOOGLE_RECAPTCHA_DOMAIN_ERROR,
createMessage,
} from "@appsmith/constants/messages";
import { RecaptchaType, RecaptchaTypes } from "components/constants";
import ReCAPTCHA from "react-google-recaptcha";
import { Variant } from "components/ads/common";
const RecaptchaWrapper = styled.div`
position: relative;
.grecaptcha-badge {
visibility: hidden;
}
`;
export interface RecaptchaProps {
googleRecaptchaKey?: string;
clickWithRecaptcha?: (token: string) => void;
handleRecaptchaV2Loading?: (isLoading: boolean) => void;
recaptchaType?: RecaptchaType;
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
}
import { Toaster } from "components/ads/Toast";
export default function withRecaptcha<
T extends RecaptchaProps = RecaptchaProps
>(WrappedComponent: React.ComponentType<T>) {
const displayName =
WrappedComponent.displayName || WrappedComponent.name || "Component";
function ComponentWithRecaptcha(props: T) {
if (!props.googleRecaptchaKey) {
return <WrappedComponent {...props} {...(props as T)} />;
}
const handleError = (
event: React.MouseEvent<HTMLElement>,
error: string,
) => {
Toaster.show({
text: error,
variant: Variant.danger,
});
props.onClick && props.onClick(event);
};
if (props.recaptchaType === RecaptchaTypes.V2) {
return (
<RecaptchaV2Component {...props} handleError={handleError}>
<WrappedComponent {...props} {...(props as T)} />
</RecaptchaV2Component>
);
} else {
return (
<RecaptchaV3Component {...props} handleError={handleError}>
<WrappedComponent {...props} {...(props as T)} />
</RecaptchaV3Component>
);
}
}
ComponentWithRecaptcha.displayName = `withRecaptcha(${displayName})`;
return ComponentWithRecaptcha;
}
function RecaptchaV2Component(
props: {
children: any;
recaptchaType?: RecaptchaType;
handleError: (event: React.MouseEvent<HTMLElement>, error: string) => void;
} & RecaptchaProps,
) {
const recaptchaRef = useRef<ReCAPTCHA>(null);
const [isInvalidKey, setInvalidKey] = useState(false);
const handleRecaptchaLoading = (isloading: boolean) => {
props.handleRecaptchaV2Loading && props.handleRecaptchaV2Loading(isloading);
};
const handleBtnClick = async (event: React.MouseEvent<HTMLElement>) => {
if (isInvalidKey) {
// Handle incorrent google recaptcha site key
props.handleError(event, createMessage(GOOGLE_RECAPTCHA_KEY_ERROR));
} else {
handleRecaptchaLoading(true);
try {
await recaptchaRef?.current?.reset();
const token = await recaptchaRef?.current?.executeAsync();
if (token && typeof props.clickWithRecaptcha === "function") {
props.clickWithRecaptcha(token);
} else {
// Handle incorrent google recaptcha site key
props.handleError(event, createMessage(GOOGLE_RECAPTCHA_KEY_ERROR));
}
handleRecaptchaLoading(false);
} catch (err) {
handleRecaptchaLoading(false);
// Handle error due to google recaptcha key of different domain
props.handleError(event, createMessage(GOOGLE_RECAPTCHA_DOMAIN_ERROR));
}
}
};
return (
<RecaptchaWrapper onClick={handleBtnClick}>
{props.children}
<ReCAPTCHA
onErrored={() => setInvalidKey(true)}
ref={recaptchaRef}
sitekey={props.googleRecaptchaKey || ""}
size="invisible"
/>
</RecaptchaWrapper>
);
}
function RecaptchaV3Component(
props: {
children: any;
recaptchaType?: RecaptchaType;
handleError: (event: React.MouseEvent<HTMLElement>, error: string) => void;
} & RecaptchaProps,
) {
// Check if a string is a valid JSON string
const checkValidJson = (inputString: string): boolean => {
return !inputString.includes('"');
};
const handleBtnClick = (event: React.MouseEvent<HTMLElement>) => {
if (status === ScriptStatus.READY) {
(window as any).grecaptcha.ready(() => {
try {
(window as any).grecaptcha
.execute(props.googleRecaptchaKey, {
action: "submit",
})
.then((token: any) => {
if (typeof props.clickWithRecaptcha === "function") {
props.clickWithRecaptcha(token);
}
})
.catch(() => {
// Handle incorrent google recaptcha site key
props.handleError(
event,
createMessage(GOOGLE_RECAPTCHA_KEY_ERROR),
);
});
} catch (err) {
// Handle error due to google recaptcha key of different domain
props.handleError(
event,
createMessage(GOOGLE_RECAPTCHA_DOMAIN_ERROR),
);
}
});
}
};
let validGoogleRecaptchaKey = props.googleRecaptchaKey;
if (validGoogleRecaptchaKey && !checkValidJson(validGoogleRecaptchaKey)) {
validGoogleRecaptchaKey = undefined;
}
const status = useScript(
`https://www.google.com/recaptcha/api.js?render=${validGoogleRecaptchaKey}`,
AddScriptTo.HEAD,
);
return <div onClick={handleBtnClick}>{props.children}</div>;
}

View File

@ -0,0 +1,115 @@
import styled from "styled-components";
import { Checkbox as BlueprintCheckbox } from "@blueprintjs/core";
import { Colors } from "constants/Colors";
import { lightenColor, darkenColor } from "widgets/WidgetUtils";
type StyledCheckboxProps = {
checked?: boolean;
disabled?: boolean;
backgroundColor?: string;
borderRadius?: string;
indeterminate?: boolean;
hasError?: boolean;
inputRef?: (el: HTMLInputElement | null) => any;
};
const DISABLED_ICON_SVG =
"data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill-rule='evenodd' clip-rule='evenodd' d='M11 7H5c-.55 0-1 .45-1 1s.45 1 1 1h6c.55 0 1-.45 1-1s-.45-1-1-1z' fill='white'/%3e%3c/svg%3e";
const CHECKED_ICON_SVG =
"data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 14 14' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='14' height='14' /%3E%3Cpath d='M10.1039 3.5L11 4.40822L5.48269 10L2.5 6.97705L3.39613 6.06883L5.48269 8.18305L10.1039 3.5Z' fill='white'/%3E%3C/svg%3E%0A";
const Checkbox = styled(BlueprintCheckbox)<StyledCheckboxProps>`
${({ backgroundColor, borderRadius, checked, hasError }) => `
margin: 0;
padding: 0;
height: auto;
display: flex;
align-items: center;
gap: 10px;
color: ${checked ? Colors.GREY_10 : Colors.GREY_9};
&.bp3-control.bp3-checkbox .bp3-control-indicator {
margin: 0;
border: none;
box-shadow: 0px 0px 0px 1px ${Colors.GREY_3};
outline: none !important;
background: transparent;
border-radius: ${borderRadius};
// ERROR state ( needed when checkbox is required )
${hasError && `box-shadow: 0px 0px 0px 1px ${Colors.ERROR_RED};`};
}
&.bp3-control.bp3-checkbox input:checked ~ .bp3-control-indicator,
&.bp3-control.bp3-checkbox input:indeterminate ~ .bp3-control-indicator {
background: ${backgroundColor} !important;
background-image: none;
border: none !important;
box-shadow: none;
}
// ACTIVE
&.bp3-control.bp3-checkbox:active .bp3-control-indicator {
background: ${lightenColor(backgroundColor)} !important;
box-shadow:
0px 0px 0px 1px ${backgroundColor},
0px 0px 0px 3px ${lightenColor(backgroundColor)} !important;
}
// ACTIVE WHEN DISABLED
&.bp3-control.bp3-checkbox:active input:disabled ~ .bp3-control-indicator {
box-shadow: 0px 0px 0px 1px ${Colors.GREY_3} !important;
}
// DISABLED
&.bp3-control.bp3-checkbox input:disabled ~ .bp3-control-indicator {
opacity: 0.5;
background: ${Colors.GREY_5} !important;
color: ${Colors.GREY_8};
&::before {
background-image: url("${DISABLED_ICON_SVG}") !important;
}
}
&.bp3-control.bp3-checkbox input:checked ~ .bp3-control-indicator {
&::before {
background-image: url("${CHECKED_ICON_SVG}") !important;
}
}
// CHECKED
&.bp3-control.bp3-checkbox input:checked ~ .bp3-control-indicator {
background: ${backgroundColor} !important;
}
// HOVER WHEN CHECKED
&.bp3-control.bp3-checkbox:hover input:checked ~ .bp3-control-indicator {
box-shadow: none;
background: ${darkenColor(backgroundColor)} !important;
}
// HOVER WHEN UNCHECKED
&.bp3-control.bp3-checkbox:hover :not(input:checked) ~ .bp3-control-indicator {
box-shadow: 0px 0px 0px 1px ${Colors.GREY_5};
}
// INDETERMINATE
&.bp3-control.bp3-checkbox input:indeterminate ~ .bp3-control-indicator {
box-shadow: none;
}
// BLUEPRINT DEFAULT ISSUES
&.bp3-control:not(.bp3-align-right) {
padding-left: 0;
}
`}
`;
Checkbox.defaultProps = {
backgroundColor: "#553DE9",
borderRadius: "0.375rem",
};
export { Checkbox };

View File

@ -0,0 +1,53 @@
import React from "react";
import {
Popover,
Menu as BMenu,
MenuItem as BMenuItem,
IMenuProps,
IMenuItemProps,
} from "@blueprintjs/core";
type Props = {
children: React.ReactElement[] | React.ReactElement;
};
function Menu(props: Props) {
const menus =
(Array.isArray(props.children) &&
props.children.find(
(child: any) => child.type.displayName === "MenuList",
)) ||
undefined;
const trigger =
Array.isArray(props.children) &&
props.children.find(
(child: any) => child.type.displayName === "MenuTrigger",
);
return (
<Popover content={menus} popoverClassName="Menu-v2" transitionDuration={-1}>
{trigger}
</Popover>
);
}
function MenuList(props: IMenuProps) {
return <BMenu {...props} />;
}
MenuList.displayName = "MenuList";
function MenuTrigger(props: any) {
return <div {...props} />;
}
MenuTrigger.displayName = "MenuTrigger";
function MenuItem(props: IMenuItemProps) {
return <BMenuItem {...props} />;
}
MenuItem.displayName = "MenuItem";
export { Menu, MenuList, MenuItem, MenuTrigger };

View File

@ -0,0 +1,57 @@
import React from "react";
import {
Popover,
Menu,
MenuItem,
IMenuProps,
IMenuItemProps,
} from "@blueprintjs/core";
type Props = {
children: React.ReactElement[] | React.ReactElement;
};
function Select(props: Props) {
const menus =
(Array.isArray(props.children) &&
props.children.find(
(child: any) => child.type.displayName === "SelectList",
)) ||
undefined;
const trigger =
Array.isArray(props.children) &&
props.children.find(
(child: any) => child.type.displayName === "SelectTrigger",
);
return (
<Popover
content={menus}
popoverClassName="Select-v2"
transitionDuration={-1}
>
{trigger}
</Popover>
);
}
function SelectList(props: IMenuProps) {
return <Menu {...props} />;
}
SelectList.displayName = "SelectList";
function SelectTrigger(props: any) {
return <div {...props} />;
}
SelectTrigger.displayName = "SelectTrigger";
function SelectOption(props: IMenuItemProps) {
return <MenuItem {...props} />;
}
SelectOption.displayName = "SelectOption";
export { Select, SelectList, SelectOption, SelectTrigger };

View File

@ -0,0 +1,149 @@
import React, { useState } from "react";
import { Checkbox, Button } from "components/wds";
import { borderRadiusOptions } from "constants/ThemeConstants";
function Showcase() {
const [borderRadius, setBorderRadius] = useState("0px");
const theme = {
borderRadius,
};
return (
<div className="container min-h-screen pt-12 mx-auto">
<h1 className="mt-12 space-y-8 text-3xl font-bold">
Widgets Design System
</h1>
<h1>Theme Options</h1>
<div>Border radius</div>
<div className="flex gap-2">
{Object.keys(borderRadiusOptions).map((optionKey) => (
<button
className={`flex items-center justify-center w-8 h-8 bg-trueGray-100 ring-gray-700 cursor-pointer hover:bg-trueGray-50 ${
borderRadius === borderRadiusOptions[optionKey] ? "ring-1" : ""
}`}
key={optionKey}
onClick={() => {
setBorderRadius(borderRadiusOptions[optionKey]);
}}
>
<div
className="w-4 h-4 border-t-2 border-l-2 border-gray-600 rounded-"
style={{ borderTopLeftRadius: borderRadiusOptions[optionKey] }}
/>
</button>
))}
</div>
<div className="space-y-5">
<div className="mt-5">
<h2 className="my-2 text-xl font-semibold">Checkbox</h2>
<div className="space-y-3">
<div className="space-y-1">
<h3 className="text-gray-500">States</h3>
<div className="flex space-x-3">
<Checkbox checked label="Active" {...theme} />
<Checkbox
checked={false}
disabled
label="Disabled"
{...theme}
/>
<Checkbox checked={false} hasError label="Error" {...theme} />
<Checkbox
checked={false}
indeterminate
label="Indeterminate"
{...theme}
/>
</div>
</div>
</div>
</div>
{/* checkbox end */}
{/* buttons */}
<div className="">
<h2 className="my-2 text-xl font-semibold">Buttons</h2>
<div className="space-y-3">
<div className="space-y-1">
<h3 className="text-gray-500">Types</h3>
<div className="flex space-x-3">
<Button leftIcon="download" {...theme} />
<Button variant="solid" {...theme}>
Solid
</Button>
<Button variant="outline" {...theme}>
Outline
</Button>
<Button variant="ghost" {...theme}>
Ghost
</Button>
<Button variant="link" {...theme}>
Link
</Button>
</div>
</div>
<div className="space-y-1">
<h3 className="text-gray-500">States</h3>
<div className="flex space-x-3">
<Button {...theme}>Default</Button>
<Button isDisabled {...theme}>
Disalbed
</Button>
<Button isLoading {...theme}>
Loading
</Button>
</div>
</div>
<div className="space-y-1">
<h3 className="text-gray-500">Icon and Alignment</h3>
<div className="flex space-x-3">
<Button className="w-40" leftIcon="download" {...theme}>
With Icon
</Button>
<Button
className="w-40"
justifyContent="space-between"
leftIcon="download"
{...theme}
>
With Icon
</Button>
<Button
className="w-40"
justifyContent="flex-start"
leftIcon="download"
{...theme}
>
With Icon
</Button>
<Button
className="w-40"
justifyContent="flex-end"
leftIcon="download"
{...theme}
>
With Icon
</Button>
</div>
</div>
<div className="space-y-1">
<h3 className="text-gray-500">Misc</h3>
<div className="flex space-x-3">
<Button tooltip="This is tooltip content" {...theme}>
Tooltip
</Button>
</div>
</div>
</div>
</div>
{/*button end */}
</div>
</div>
);
}
export default Showcase;

View File

@ -0,0 +1 @@
export { default as withTooltip } from "./withTooltip";

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