Merge branch 'release' of https://github.com/appsmithorg/appsmith into feature/table-excel-export

This commit is contained in:
vicky-primathon.in 2021-04-28 18:54:26 +05:30
commit e78b4db3b2
754 changed files with 23168 additions and 8065 deletions

View File

@ -56,3 +56,5 @@ APPSMITH_MAIL_SMTP_TLS_ENABLED=
#APPSMITH_SENTRY_DSN=
#APPSMITH_SENTRY_ENVIRONMENT=
# Configure cloud services
# APPSMITH_CLOUD_SERVICES_BASE_URL

View File

@ -6,24 +6,43 @@ labels: Epic, Product Note
assignees: Nikhil-Nandagopal
---
## Problem statement
Why is this needed?
What does it hope to achieve?
# Objective
Here you fill in the objective of the feature/product that you are writing about.
## Related issues
[ ] #issue1
## Success criteria
# Success Metrics
List of all metrics you are tracking and the desired goal.
| Goal | Metric |
| ------------- | ------------- |
| e.g. Simplify user experience | Customer satisfaction score increases |
| e.g. Simplify onboarding flow | Decrease churn rate down to 30% |
How will we know the project succeeded?
# Assumptions
List any assumptions that you have about your users, technical constraints, or business goals (e.g., Most users will access this feature from tablet).
## User story
- Assumption 1
- Assumption 2
- Assumption 3
How does a user use this feature? How does it relate to the problem?
# Requirements
| Requirement | User Story | Importance | Notes |
| ------------- | ------------- | ------------- | ------------- |
| e.g. Must be mobile responsive | e.g. as a user, I want to be able to access the platform via mobile phone | High, Low or Medium | Content Cell |
| e.g. The user should be able to leave a comment | e.g as a user, I want to be able to communicate with the other members on the canvas | High, Low or Medium | Content Cell |
## Details
# Out of Scope
List the things that are out of cope or might be revisited after the first release.
- Item 1
- Item 2
- Item 3
What are the specifications of the implementation?
Product notes, designs etc.
# Developer Handoff Document in Figma
Link to the developer Handoff Document:
# Questions
| Question | Answer | Date Answered |
| ------------- | ------------- | ------------- |
| e.g. How might we ensure that the comments section doesn't cover the canvas | Content Cell | Content Cell |
| Content Cell | Content Cell | Content Cell |

10
.github/config.json vendored
View File

@ -185,6 +185,11 @@
"color": "79e53b",
"description": "An unexpected or annoying bug"
},
"List Widget": {
"name": "List Widget",
"color": "79e53b",
"description": "Issues Related to the list widget"
},
"Map Widget": {
"name": "Map Widget",
"color": "7eef7a",
@ -564,6 +569,11 @@
"type": "hasLabel",
"value": true
},
{
"label": "List Widget",
"type": "hasLabel",
"value": true
},
{
"label": "Map Widget",
"type": "hasLabel",

View File

@ -99,7 +99,7 @@ jobs:
- name: Run the jest tests
if: github.event_name == 'pull_request'
uses: hetunandu/Jest-Coverage-Diff@fix/new-delete-file
uses: hetunandu/Jest-Coverage-Diff@feature/better-report-comments
with:
fullCoverageDiff: false
runCommand: cd app/client && REACT_APP_ENVIRONMENT=${{steps.vars.outputs.REACT_APP_ENVIRONMENT}} yarn run test:unit

View File

@ -38,7 +38,7 @@ jobs:
- name: Set up JDK 1.11
uses: actions/setup-java@v1
with:
java-version: 1.11
java-version: "11.0.10"
# Retrieve maven dependencies from cache. After a successful run, these dependencies are cached again
- name: Cache maven dependencies

View File

@ -50,7 +50,7 @@ jobs:
- name: Set up JDK 1.11
uses: actions/setup-java@v1
with:
java-version: 1.11
java-version: "11.0.10"
# Retrieve maven dependencies from cache. After a successful run, these dependencies are cached again
- name: Cache maven dependencies

3
.gitignore vendored
View File

@ -2,6 +2,7 @@
.idea
*.iml
.env
.vscode/*
# test coverage
coverage-summary.json
coverage-summary.json

View File

@ -6,7 +6,7 @@
</p>
<p align="center">
<a href="https://app.appsmith.com/signup"><strong>Try Online Sandbox</strong></a>
<a href="https://app.appsmith.com/signup?utm_source=github&utm_medium=social&utm_content=website&utm_campaign=null&utm_term=website"><strong>Try Online Sandbox</strong></a>
</p>
<p align="center">
<a href="https://docs.appsmith.com/">Documentation</a>
@ -63,7 +63,7 @@ But if youd rather check out some real applications that can be built with Ap
The following steps introduce you to building a simple user-list dashboard on Appsmith.
1. [Sign up on Appsmith Cloud](https://bit.ly/appsmith-signup-github) or [Deploy Appsmith](https://docs.appsmith.com/setup).
1. [Sign up on Appsmith Cloud](https://app.appsmith.com/signup?utm_source=github&utm_medium=social&utm_content=website&utm_campaign=null&utm_term=website) or [Deploy Appsmith](https://docs.appsmith.com/setup).
2. Create a new app within the organization that has already been created for you.
3. Click on the `+` icon next to the `Queries` section to add a new query in the mock database
1. Name the query `usersQuery`.
@ -79,12 +79,12 @@ Connect your own data to build apps for your team. [Read more here.](https://doc
## 📚 Tutorials
1. [Building an Admin Panel on MongoDB using Appsmith](https://blog.appsmith.com/building-an-admin-panel-with-mongodb-using-appsmith) ([Video](https://www.youtube.com/watch?v=tisUaIgI86k))
2. [Building a customer support dashboard in Appsmith](https://www.youtube.com/watch?v=-O_6OLREEzo&t=272s)
3. [Running CI/CD jobs manually using Appsmith](https://blog.appsmith.com/how-to-run-manual-jobs-in-gitlab-cicd) ([Video](https://www.youtube.com/watch?v=CYdeJcD4I8A))
4. [Building a calendly clone in Appsmith](https://blog.appsmith.com/how-to-build-a-calendly-clone-in-30-minutes)
5. [Building Internal Tools with Appsmith](https://youtu.be/eYYYfuW-kEE) `Community`
6. [Building an Issue Tracker with Appsmith](https://dev.to/pjmantoss/how-to-build-an-issue-tracker-with-appsmith-204e) `Community`
2. [Building a Customer Support Dashboard in Appsmith](https://www.youtube.com/watch?v=-O_6OLREEzo&t=272s)
3. [Building a Store Catalogue Management System using Appsmith and GraphQL](https://blog.appsmith.com/building-a-store-catalogue-management-system-using-appsmith-and-graphql)
4. [Running CI/CD Jobs Manually using Appsmith](https://blog.appsmith.com/how-to-run-manual-jobs-in-gitlab-cicd) ([Video](https://www.youtube.com/watch?v=CYdeJcD4I8A))
5. [Building a Calendly Clone in Appsmith](https://blog.appsmith.com/how-to-build-a-calendly-clone-in-30-minutes)
6. [Building Internal Tools with Appsmith](https://youtu.be/eYYYfuW-kEE) `Community`
7. [Building an Issue Tracker with Appsmith](https://dev.to/pjmantoss/how-to-build-an-issue-tracker-with-appsmith-204e) `Community`
## 📕 Support & Troubleshooting

View File

@ -28,7 +28,13 @@
"react/prop-types": 0,
"@typescript-eslint/explicit-module-boundary-types": 0,
"cypress/no-unnecessary-waiting": 0,
"cypress/no-assigning-return-values": 0
"cypress/no-assigning-return-values": 0,
"react/function-component-definition": "warn",
"react/jsx-boolean-value": "error",
"react/self-closing-comp": "error",
"react/jsx-sort-props": "error",
"react/jsx-fragments": "error",
"react/jsx-no-useless-fragment": "error"
},
"settings": {
"react": {

View File

@ -43,3 +43,5 @@ storybook-static/*
build-storybook.log
.eslintcache
.vscode
TODO

View File

@ -7,4 +7,3 @@
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
For details on setting up your development machine, please refer to the [Setup Guide](https://github.com/appsmithorg/appsmith/blob/release/contributions/ClientSetup.md)

View File

@ -2,6 +2,9 @@
"env": {
"cypress/globals": true
},
"rules": {
"cypress/no-unnecessary-waiting": 0
},
"extends": [
"plugin:cypress/recommended"
]

View File

@ -58,7 +58,9 @@
"chartType": "LINE_CHART",
"chartName": "Sales on working days",
"allowHorizontalScroll": false,
"chartData": [{"seriesName":"Sales","data":""}],
"chartData": {
"some-random-id": {"seriesName":"Sales","data": []}
},
"xAxisName": "Last Week",
"yAxisName": "Total Order Revenue $",
"type": "CHART_WIDGET",

View File

@ -102,6 +102,7 @@
"AlertModalName": "Alert_Modal",
"FormModalName": "Form_Modal",
"TextLabelValue": "Test Text Label",
"TextLabelValueScrollable": "Test Text Label to check scroll feature",
"TextName": "TestTextBox",
"TextLabel": "Paragraph",
"TextBody": "Heading 2",

View File

@ -114,7 +114,7 @@
"inputType": "TEXT",
"label": "Endpoint",
"widgetName": "EndpointInput",
"defaultText": "todos",
"defaultText": "offers",
"type": "INPUT_WIDGET",
"isLoading": false,
"parentColumnSpace": 71.75,

View File

@ -0,0 +1,161 @@
{
"dsl": {
"widgetName": "MainContainer",
"backgroundColor": "none",
"rightColumn": 1224,
"snapColumns": 16,
"detachFromLayout": true,
"widgetId": "0",
"topRow": 0,
"bottomRow": 1280,
"containerStyle": "none",
"snapRows": 33,
"parentRowSpace": 1,
"type": "CANVAS_WIDGET",
"canExtend": true,
"version": 9,
"minHeight": 1292,
"parentColumnSpace": 1,
"dynamicBindingPathList": [],
"leftColumn": 0,
"children": [
{
"isVisible": true,
"enhancements": true,
"backgroundColor": "",
"gridType": "vertical",
"gridGap": 0,
"items": "[\n {\n \"id\": 1,\n \"email\": \"michael.lawson@reqres.in\",\n \"first_name\": \"Michael\",\n \"last_name\": \"Lawson\",\n \"avatar\": \"https://reqres.in/img/faces/7-image.jpg\"\n },\n {\n \"id\": 2,\n \"email\": \"lindsay.ferguson@reqres.in\",\n \"first_name\": \"Lindsay\",\n \"last_name\": \"Ferguson\",\n \"avatar\": \"https://reqres.in/img/faces/8-image.jpg\"\n },\n {\n \"id\": 3,\n \"email\": \"brock.lesnar@reqres.in\",\n \"first_name\": \"Brock\",\n \"last_name\": \"Lesnar\",\n \"avatar\": \"https://reqres.in/img/faces/8-image.jpg\"\n }\n]",
"widgetName": "List1",
"children": [
{
"isVisible": true,
"widgetName": "Canvas1",
"containerStyle": "none",
"canExtend": false,
"detachFromLayout": true,
"dropDisabled": true,
"children": [
{
"isVisible": true,
"backgroundColor": "white",
"widgetName": "Container1",
"containerStyle": "card",
"children": [
{
"isVisible": true,
"widgetName": "Canvas2",
"containerStyle": "none",
"canExtend": false,
"detachFromLayout": true,
"children": [
{
"isVisible": true,
"text": "Label",
"textStyle": "LABEL",
"textAlign": "LEFT",
"widgetName": "Text1",
"type": "TEXT_WIDGET",
"isLoading": false,
"parentColumnSpace": 32,
"parentRowSpace": 40,
"leftColumn": 2,
"rightColumn": 6,
"topRow": 0,
"bottomRow": 1,
"parentId": "dinv2tsatk",
"widgetId": "k6ct7dxg4w"
},
{
"isVisible":true,
"text":"Submit",
"buttonStyle":"PRIMARY_BUTTON",
"widgetName":"Button1",
"isDisabled":false,
"isDefaultClickDisabled":true,
"version":1,
"type":"BUTTON_WIDGET",
"isLoading":false,
"parentColumnSpace":29.25,
"parentRowSpace":40,
"leftColumn":6,
"rightColumn":8,
"topRow":1,
"bottomRow":2,
"parentId":"dinv2tsatk",
"widgetId":"fuw9p7cuek"
}
],
"minHeight": null,
"type": "CANVAS_WIDGET",
"isLoading": false,
"parentColumnSpace": 1,
"parentRowSpace": 1,
"leftColumn": 0,
"rightColumn": null,
"topRow": 0,
"bottomRow": null,
"parentId": "4ruj7xl5ri",
"widgetId": "dinv2tsatk"
}
],
"dragDisabled": true,
"isDeletable": false,
"disablePropertyPane": true,
"type": "CONTAINER_WIDGET",
"isLoading": false,
"leftColumn": 0,
"rightColumn": 16,
"topRow": 0,
"bottomRow": 4,
"parentId": "0pvmmqr77m",
"widgetId": "4ruj7xl5ri"
}
],
"minHeight": 400,
"type": "CANVAS_WIDGET",
"isLoading": false,
"parentColumnSpace": 1,
"parentRowSpace": 1,
"leftColumn": 0,
"rightColumn": 592,
"topRow": 0,
"bottomRow": 400,
"parentId": "5bwz8xcvhj",
"widgetId": "0pvmmqr77m"
}
],
"type": "LIST_WIDGET",
"isLoading": false,
"parentColumnSpace": 74,
"parentRowSpace": 40,
"leftColumn": 0,
"rightColumn": 8,
"topRow": 0,
"bottomRow": 10,
"parentId": "0",
"widgetId": "5bwz8xcvhj",
"dynamicBindingPathList": [],
"template": {
"Text1": {
"isVisible": true,
"text": "Label",
"textStyle": "LABEL",
"textAlign": "LEFT",
"widgetName": "Text1",
"type": "TEXT_WIDGET",
"isLoading": false,
"parentColumnSpace": 32,
"parentRowSpace": 40,
"leftColumn": 0,
"rightColumn": 4,
"topRow": 0,
"bottomRow": 1,
"parentId": "dinv2tsatk",
"widgetId": "k6ct7dxg4w"
}
}
}
]
}
}

Binary file not shown.

View File

@ -0,0 +1,45 @@
{
"dsl": {
"widgetName": "MainContainer",
"backgroundColor": "none",
"rightColumn": 966,
"snapColumns": 16,
"detachFromLayout": true,
"widgetId": "0",
"topRow": 0,
"bottomRow": 240,
"containerStyle": "none",
"snapRows": 33,
"parentRowSpace": 1,
"type": "CANVAS_WIDGET",
"canExtend": true,
"version": 16,
"minHeight": 280,
"parentColumnSpace": 1,
"dynamicTriggerPathList": [],
"dynamicBindingPathList": [],
"leftColumn": 0,
"children": [
{
"isVisible": true,
"text": "Label",
"fontSize": "PARAGRAPH",
"fontStyle": "BOLD",
"textAlign": "LEFT",
"textColor": "#231F20",
"widgetName": "Text1",
"version": 1,
"type": "TEXT_WIDGET",
"isLoading": false,
"parentColumnSpace": 57.875,
"parentRowSpace": 40,
"leftColumn": 4,
"rightColumn": 8,
"topRow": 1,
"bottomRow": 2,
"parentId": "0",
"widgetId": "266vj9u1mr"
}
]
}
}

View File

@ -1,33 +1,56 @@
const dsl = require("../../../../fixtures/executionParamsDsl.json");
const publishPage = require("../../../../locators/publishWidgetspage.json");
const commonlocators = require("../../../../locators/commonlocators.json");
const queryLocators = require("../../../../locators/QueryEditor.json");
const datasource = require("../../../../locators/DatasourcesEditor.json");
describe("API Panel Test Functionality", function() {
let datasourceName;
before(() => {
cy.addDsl(dsl);
});
it("Will pass execution params", function() {
// Create the Api
cy.NavigateToAPI_Panel();
cy.CreateAPI("MultiApi");
cy.enterDatasourceAndPath(
"https://jsonplaceholder.typicode.com/",
"{{this.params.endpoint || 'posts'}}",
);
cy.WaitAutoSave();
// Run it
cy.RunAPI();
beforeEach(() => {
cy.startRoutesForDatasource();
});
it("Create a postgres datasource", function() {
cy.NavigateToDatasourceEditor();
cy.get(datasource.PostgreSQL).click();
cy.getPluginFormsAndCreateDatasource();
cy.fillPostgresDatasourceForm();
cy.testSaveDatasource();
cy.get("@createDatasource").then((httpResponse) => {
datasourceName = httpResponse.response.body.data.name;
});
});
it("Create and runs query", () => {
cy.NavigateToQueryEditor();
cy.contains(".t--datasource-name", datasourceName)
.find(queryLocators.createQuery)
.click();
cy.get(queryLocators.templateMenu).click();
cy.get(".CodeMirror textarea")
.first()
.focus()
.type("select * from {{ this.params.tableName || 'users' }} limit 10", {
force: true,
parseSpecialCharSequences: false,
});
cy.WaitAutoSave();
cy.runQuery();
});
it("Will pass execution params", function() {
// Bind the table
cy.SearchEntityandOpen("Table1");
cy.testJsontext("tabledata", "{{MultiApi.data", false);
cy.testJsontext("tabledata", "{{Query1.data}}");
// Assert 'posts' data (default)
cy.readTabledataPublish("0", "2").then((cellData) => {
expect(cellData).to.be.equal(
"sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
);
cy.readTabledataPublish("0", "1").then((cellData) => {
expect(cellData).to.be.equal("Ximenez Kainz");
});
// Choose static button
cy.SearchEntityandOpen("StaticButton");
// toggle js of onClick
@ -37,8 +60,7 @@ describe("API Panel Test Functionality", function() {
// Bind with MultiApi with static value
cy.testJsontext(
"onclick",
"{{MultiApi.run(undefined, undefined, { endpoint: 'users",
false,
"{{Query1.run(undefined, undefined, { tableName: 'orders' })}}",
);
cy.get(commonlocators.editPropCrossButton).click({ force: true });
@ -51,8 +73,7 @@ describe("API Panel Test Functionality", function() {
// Bind with MultiApi with dynamicValue value
cy.testJsontext(
"onclick",
"{{MultiApi.run(undefined, undefined, { endpoint: EndpointInput.text",
false,
"{{Query1.run(undefined, undefined, { tableName: EndpointInput.text })}}",
);
// Publish the app
@ -60,10 +81,8 @@ describe("API Panel Test Functionality", function() {
cy.wait("@postExecute");
// Assert on load data in table
cy.readTabledataPublish("0", "2").then((cellData) => {
expect(cellData).to.be.equal(
"sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
);
cy.readTabledataPublish("0", "1").then((cellData) => {
expect(cellData).to.be.equal("Ximenez Kainz");
});
// Click Static button
@ -74,7 +93,7 @@ describe("API Panel Test Functionality", function() {
cy.wait(2000);
// Assert statically bound "users" data
cy.readTabledataPublish("1", "1").then((cellData) => {
expect(cellData).to.be.equal("Ervin Howell");
expect(cellData).to.be.equal("OUT_FOR_DELIVERY");
});
// Click dynamic button
@ -84,8 +103,8 @@ describe("API Panel Test Functionality", function() {
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(2000);
// Assert dynamically bound "todos" data
cy.readTabledataPublish("0", "2").then((cellData) => {
expect(cellData).to.be.equal("delectus aut autem");
cy.readTabledataPublish("0", "1").then((cellData) => {
expect(cellData).to.be.equal("DISCOUNT");
});
});
});

View File

@ -15,6 +15,11 @@ describe("API Panel Test Functionality ", function() {
.click({ force: true });
cy.CopyAPIToHome();
cy.GlobalSearchEntity("FirstAPICopy");
// click on learn how link
cy.get(".t--learn-how-apis-link").click();
// this should open in a global search modal
cy.get(commonlocators.globalSearchModal);
cy.get("body").click(0, 0);
cy.DeleteAPIFromSideBar();
});
});

View File

@ -1,9 +1,7 @@
const testdata = require("../../../../fixtures/testdata.json");
const apiwidget = require("../../../../locators/apiWidgetslocator.json");
const commonlocators = require("../../../../locators/commonlocators.json");
const formWidgetsPage = require("../../../../locators/FormWidgets.json");
const dsl = require("../../../../fixtures/MultipleInput.json");
const pages = require("../../../../locators/Pages.json");
const widgetsPage = require("../../../../locators/Widgets.json");
const publish = require("../../../../locators/publishWidgetspage.json");

View File

@ -0,0 +1,14 @@
const dsl = require("../../../../fixtures/buttondsl.json");
describe("Debugger logs", function() {
before(() => {
cy.addDsl(dsl);
});
it("Modifying widget properties should log the same", function() {
cy.openPropertyPane("buttonwidget");
cy.testJsontext("label", "Test");
cy.get(".t--debugger").click();
cy.get(".t--debugger-log-state").contains("Test");
});
});

View File

@ -1,5 +1,4 @@
const dsl = require("../../../../fixtures/ModalDsl.json");
const publishPage = require("../../../../locators/publishWidgetspage.json");
const explorer = require("../../../../locators/explorerlocators.json");
describe("Modal Widget Functionality", function() {

View File

@ -10,21 +10,8 @@ describe("Table Widget property pane feature validation", function() {
cy.addDsl(dsl);
});
it("Check collapse section feature in property pane", function() {
cy.openPropertyPane("tablewidget");
//check open and collapse
cy.get(commonlocators.collapsesection)
.first()
.should("be.visible")
.click();
cy.assertControlVisibility("tabledata");
});
it("Check open section and column data in property pane", function() {
cy.get(commonlocators.collapsesection)
.scrollIntoView()
.first()
.click();
cy.openPropertyPane("tablewidget");
cy.tableColumnDataValidation("id");
cy.tableColumnDataValidation("email");
cy.tableColumnDataValidation("userName");

View File

@ -0,0 +1,109 @@
const commonlocators = require("../../../../locators/commonlocators.json");
const widgetsPage = require("../../../../locators/Widgets.json");
const publishPage = require("../../../../locators/publishWidgetspage.json");
const dsl = require("../../../../fixtures/textDsl.json");
const pages = require("../../../../locators/Pages.json");
describe("Text Widget color/font/alignment Functionality", function() {
before(() => {
cy.addDsl(dsl);
});
beforeEach(() => {
cy.openPropertyPane("textwidget");
});
it("Text-TextStyle Heading, Text Name Validation", function() {
//changing the Text Name and verifying
cy.widgetText(
this.data.TextName,
widgetsPage.textWidget,
widgetsPage.textWidget + " " + commonlocators.widgetNameTag,
);
//Changing the text label
cy.testCodeMirror(this.data.TextLabelValueScrollable);
cy.ChangeTextStyle(
this.data.TextHeading,
commonlocators.headingTextStyle,
this.data.TextLabelValueScrollable,
);
cy.wait("@updateLayout");
cy.PublishtheApp();
cy.get(commonlocators.headingTextStyle)
.should("have.text", this.data.TextLabelValueScrollable)
.should("have.css", "font-size", "24px");
cy.get(publishPage.backToEditor).click({ force: true });
});
it("Test to validate text format", function() {
//Changing the Text Style's and validating
cy.get(widgetsPage.italics).click({ force: true });
cy.readTextDataValidateCSS("font-style", "italic");
cy.get(widgetsPage.bold).click({ force: true });
cy.readTextDataValidateCSS("font-weight", "400");
cy.get(widgetsPage.bold).click({ force: true });
cy.readTextDataValidateCSS("font-weight", "700");
cy.get(widgetsPage.italics).click({ force: true });
cy.readTextDataValidateCSS("font-style", "normal");
cy.closePropertyPane();
});
it("Test to validate color changes in text and background", function() {
//Changing the Text Style's and validating
cy.get(widgetsPage.textColor)
.first()
.click({ force: true });
cy.xpath(widgetsPage.greenColor).click();
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);
cy.wait("@updateLayout");
cy.readTextDataValidateCSS("color", "rgb(3, 179, 101)");
cy.get(widgetsPage.textColor)
.clear({ force: true })
.type("purple", { force: true });
cy.wait("@updateLayout");
cy.readTextDataValidateCSS("color", "rgb(128, 0, 128)");
cy.get(widgetsPage.backgroundColor)
.first()
.click({ force: true });
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);
cy.xpath(widgetsPage.greenColor)
.first()
.click();
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);
cy.wait("@updateLayout");
cy.PublishtheApp();
cy.get(publishPage.backToEditor).click({ force: true });
});
it("Test to validate text alignment", function() {
cy.get(widgetsPage.centerAlign)
.first()
.click({ force: true });
cy.readTextDataValidateCSS("text-align", "center");
cy.get(widgetsPage.rightAlign)
.first()
.click({ force: true });
cy.readTextDataValidateCSS("text-align", "right");
cy.get(widgetsPage.leftAlign)
.first()
.click({ force: true });
cy.readTextDataValidateCSS("text-align", "left");
cy.closePropertyPane();
});
it("Test to validate enable scroll feature", function() {
cy.get(".t--property-control-enablescroll .bp3-switch").click({
force: true,
});
cy.wait("@updateLayout");
cy.get(commonlocators.headingTextStyle).trigger("mouseover", {
force: true,
});
cy.get(commonlocators.headingTextStyle).scrollIntoView({ duration: 2000 });
});
});

View File

@ -43,6 +43,29 @@ describe("FilePicker Widget Functionality", function() {
cy.get("button").contains("1 files selected");
});
it("It checks the deletion of filepicker works as expected", function() {
cy.get(commonlocators.filePickerButton).click();
cy.get(commonlocators.filePickerInput)
.first()
.attachFile("testFile.mov");
cy.get(commonlocators.filePickerUploadButton).click();
//eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);
cy.get("button").contains("1 files selected");
cy.get(commonlocators.filePickerButton).click();
//eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(200);
cy.get("button.uppy-Dashboard-Item-action--remove").click();
cy.get("button.uppy-Dashboard-browse").click();
cy.get(commonlocators.filePickerInput)
.first()
.attachFile("testFile2.mov");
cy.get(commonlocators.filePickerUploadButton).click();
//eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);
cy.get("button").contains("1 files selected");
});
afterEach(() => {
// put your clean up code if any
});

View File

@ -54,14 +54,12 @@ describe("GlobalSearch", function() {
cy.get(commonlocators.globalSearchTrigger).click({ force: true });
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000);
cy.get(commonlocators.globalSearchClearInput).click({ force: true });
cy.get(commonlocators.globalSearchInput).type("Page1");
cy.get("body").type("{enter}");
cy.get(commonlocators.globalSearchTrigger).click({ force: true });
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000);
cy.get(commonlocators.globalSearchClearInput).click({ force: true });
cy.get(commonlocators.globalSearchInput).type("SomeApi");
cy.get("body").type("{enter}");
cy.window()
@ -90,14 +88,12 @@ describe("GlobalSearch", function() {
cy.get(commonlocators.globalSearchTrigger).click({ force: true });
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000); // modal open transition should be deterministic
cy.get(commonlocators.globalSearchClearInput).click({ force: true });
cy.get(commonlocators.globalSearchInput).type("Page1");
cy.get("body").type("{enter}");
cy.get(commonlocators.globalSearchTrigger).click({ force: true });
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000); // modal open transition should be deterministic
cy.get(commonlocators.globalSearchClearInput).click({ force: true });
cy.get(commonlocators.globalSearchInput).type(expectedDatasource.name);
cy.get("body").type("{enter}");
cy.location().should((loc) => {
@ -111,7 +107,6 @@ describe("GlobalSearch", function() {
cy.get(commonlocators.globalSearchTrigger).click({ force: true });
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000);
cy.get(commonlocators.globalSearchClearInput).click({ force: true });
cy.get(commonlocators.globalSearchInput).type("Page1");
cy.get("body").type("{enter}");
cy.window()

View File

@ -22,10 +22,13 @@ describe("Container Widget Functionality", function() {
/**
* @param{Text} Random Colour
*/
cy.testCodeMirror(this.data.colour);
cy.get(widgetsPage.backgroundcolorPicker)
.first()
.click({ force: true });
cy.xpath(widgetsPage.greenColor).click();
cy.get(widgetsPage.containerD)
.should("have.css", "background-color")
.and("eq", this.data.rgbValue);
.and("eq", "rgb(3, 179, 101)");
/**
* @param{toggleButton Css} Assert to be checked
*/
@ -41,7 +44,7 @@ describe("Container Widget Functionality", function() {
cy.get(widgetsPage.containerD)
.eq(0)
.should("have.css", "background-color")
.and("eq", this.data.rgbValue);
.and("eq", "rgb(3, 179, 101)");
});
afterEach(() => {
// put your clean up code if any

View File

@ -0,0 +1,56 @@
const datasource = require("../../../../locators/DatasourcesEditor.json");
const queryLocators = require("../../../../locators/QueryEditor.json");
const commonlocators = require("../../../../locators/commonlocators.json");
describe("Check datasource doc links", function() {
let postgresDatasourceName;
beforeEach(() => {
cy.startRoutesForDatasource();
});
it("Create postgres datasource", function() {
cy.NavigateToDatasourceEditor();
cy.get(datasource.PostgreSQL).click();
cy.generateUUID().then((uid) => {
postgresDatasourceName = uid;
cy.get(".t--edit-datasource-name").click();
cy.get(".t--edit-datasource-name input")
.clear()
.type(postgresDatasourceName, { force: true })
.should("have.value", postgresDatasourceName)
.blur();
});
cy.wait("@saveDatasource").should(
"have.nested.property",
"response.body.responseMeta.status",
200,
);
cy.fillPostgresDatasourceForm();
cy.testSaveDatasource();
});
it("Check that documentation opens global modal", function() {
cy.NavigateToQueryEditor();
cy.contains(".t--datasource-name", postgresDatasourceName)
.find(queryLocators.createQuery)
.click();
cy.get(".t--datasource-documentation-link").click();
cy.get(commonlocators.globalSearchModal);
cy.get("body").click(0, 0);
});
it("Delete the query and datasources", function() {
cy.get(queryLocators.deleteQuery).click();
cy.wait("@deleteAction").should(
"have.nested.property",
"response.body.responseMeta.status",
200,
);
cy.deleteDatasource(postgresDatasourceName);
});
});

View File

@ -0,0 +1,89 @@
const commonlocators = require("../../../locators/commonlocators.json");
const widgetsPage = require("../../../locators/Widgets.json");
const dsl = require("../../../fixtures/listdsl.json");
const publishPage = require("../../../locators/publishWidgetspage.json");
describe("Container Widget Functionality", function() {
const items = JSON.parse(dsl.dsl.children[0].items);
before(() => {
cy.addDsl(dsl);
});
it("checks if list shows correct no. of items", function() {
cy.get(commonlocators.containerWidget).then(function($lis) {
expect($lis).to.have.length(2);
});
});
it("checks currentItem binding", function() {
cy.SearchEntityandOpen("Text1");
cy.getCodeMirror().then(($cm) => {
cy.get(".CodeMirror textarea")
.first()
.type(`{{currentItem.first_name}}`, {
force: true,
parseSpecialCharSequences: false,
});
});
cy.wait(1000);
cy.closePropertyPane();
cy.get(commonlocators.TextInside).then(function($lis) {
expect($lis.eq(0)).to.contain(items[0].first_name);
expect($lis.eq(1)).to.contain(items[1].first_name);
});
});
it("checks button action", function() {
cy.SearchEntityandOpen("Button1");
cy.getCodeMirror().then(($cm) => {
cy.get(".CodeMirror textarea")
.first()
.type(`{{currentItem.first_name}}`, {
force: true,
parseSpecialCharSequences: false,
});
});
cy.addAction("{{currentItem.first_name}}");
cy.PublishtheApp();
cy.get(`${widgetsPage.widgetBtn}`)
.first()
.click();
cy.get(commonlocators.toastmsg).contains(items[0].first_name);
});
it("it checks onListItem click action", function() {
cy.get(publishPage.backToEditor).click({ force: true });
cy.SearchEntityandOpen("List1");
cy.addAction("{{currentItem.first_name}}");
cy.PublishtheApp();
cy.get(
"div[type='LIST_WIDGET'] .t--widget-containerwidget:first-child",
).click();
cy.get(commonlocators.toastmsg).contains(items[0].first_name);
});
it("it checks pagination", function() {
// clicking on second pagination button
cy.get(`${commonlocators.paginationButton}-2`).click();
// now we are on the second page which shows first the 3rd item in the list
cy.get(commonlocators.TextInside).then(function($lis) {
expect($lis.eq(0)).to.contain(items[2].first_name);
});
});
afterEach(() => {
// put your clean up code if any
});
});

View File

@ -184,7 +184,7 @@ describe("API Panel Test Functionality", function() {
);
cy.WaitAutoSave();
cy.RunAPI();
cy.validateRequest(testdata.baseUrl, testdata.methods, testdata.Get);
cy.validateRequest(testdata.baseUrl, testdata.methods, testdata.Get, true);
cy.ResponseStatusCheck("5000");
cy.log("Response code check successful");
cy.ResponseCheck("Invalid value for Content-Type");

View File

@ -24,7 +24,7 @@ describe("Add widget", function() {
.focus()
.type("select * from configs");
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);
cy.WaitAutoSave();
cy.get(queryEditor.runQuery).click();
cy.wait("@postExecute").should(
"have.nested.property",

View File

@ -31,10 +31,8 @@ describe("Create a query with a empty datasource, run, save the query", function
cy.EvaluateCurrentValue("select * from users limit 10");
cy.runQuery();
cy.get(".react-tabs p")
.last()
.contains(
"[Missing endpoint., Missing username for authentication., Missing password for authentication.]",
);
cy.get(".t--query-error").contains(
"[Missing endpoint., Missing username for authentication., Missing password for authentication.]",
);
});
});

View File

@ -65,6 +65,7 @@
"verticalCenter": ".t--icon-tab-CENTER",
"verticalBottom": ".t--icon-tab-BOTTOM",
"textColor": ".t--property-control-textcolor input",
"backgroundcolorPicker": ".t--property-control-backgroundcolor input",
"greenColor": "//div[@color='rgb(3, 179, 101)']",
"toggleJsColor": ".t--property-control-textcolor .t--js-toggle",
"backgroundColor": ".t--property-control-cellbackground input",

View File

@ -100,11 +100,13 @@
"filePickerUploadButton": ".uppy-StatusBar-actionBtn--upload",
"filePickerOnFilesSelected": ".t--property-control-onfilesselected",
"dataType": ".t--property-control-datatype .bp3-popover-target",
"evaluateMsg": ".t--CodeEditor-evaluatedValue p",
"evaluateMsg": ".t--evaluatedPopup-error",
"globalSearchModal": ".t--global-search-modal",
"globalSearchInput": ".t--global-search-input",
"globalSearchTrigger": ".t--global-search-modal-trigger",
"globalSearchClearInput": ".t--global-clear-input",
"containerWidget": ".t--widget-containerwidget",
"paginationButton": ".rc-pagination-item",
"switchWidgetActive": ".t--switch-widget-active",
"switchWidgetInActive": ".t--switch-widget-inactive",
"switchWidgetLoading": ".t--switch-widget-loading"

View File

@ -12,13 +12,11 @@ describe("Table functionality ", function() {
// Navigate to add background colour and Text colour
// Ensure the row colour gets overlapped on table colour
});
it("Collapse the tabs of Property pane", function() {
// Add a table
// Click on the property pane
// Collapse the General ,Action and Tab option
});
it("Bind the column with same name", function() {
// Add a table
// Click on the property pane

View File

@ -577,16 +577,26 @@ Cypress.Commands.add("SaveAndRunAPI", () => {
cy.RunAPI();
});
Cypress.Commands.add("validateRequest", (baseurl, path, verb) => {
cy.xpath(apiwidget.Request)
.should("be.visible")
.click({ force: true });
cy.xpath(apiwidget.RequestURL).contains(baseurl.concat(path));
cy.xpath(apiwidget.RequestMethod).contains(verb);
cy.xpath(apiwidget.Responsetab)
.should("be.visible")
.click({ force: true });
});
Cypress.Commands.add(
"validateRequest",
(baseurl, path, verb, error = false) => {
cy.get(".react-tabs__tab")
.contains("Logs")
.click();
if (!error) {
cy.get(".object-key")
.last()
.contains("request")
.click();
}
cy.get(".string-value").contains(baseurl.concat(path));
cy.get(".string-value").contains(verb);
cy.xpath(apiwidget.Responsetab)
.should("be.visible")
.click({ force: true });
},
);
Cypress.Commands.add("SelectAction", (action) => {
cy.get(ApiEditor.ApiVerb)
@ -1409,6 +1419,14 @@ Cypress.Commands.add(
},
);
Cypress.Commands.add("readTextDataValidateCSS", (cssProperty, cssValue) => {
cy.get(commonlocators.headingTextStyle).should(
"have.css",
cssProperty,
cssValue,
);
});
Cypress.Commands.add("evaluateErrorMessage", (value) => {
cy.get(commonlocators.evaluateMsg)
.first()
@ -1921,7 +1939,7 @@ Cypress.Commands.add("deleteDatasource", (datasourceName) => {
});
Cypress.Commands.add("runQuery", () => {
cy.get(queryEditor.runQuery).click();
cy.get(queryEditor.runQuery).click({ force: true });
cy.wait("@postExecute").should(
"have.nested.property",
"response.body.responseMeta.status",

View File

@ -47,6 +47,7 @@ server {
sub_filter __APPSMITH_INTERCOM_APP_ID__ '${APPSMITH_INTERCOM_APP_ID}';
sub_filter __APPSMITH_MAIL_ENABLED__ '${APPSMITH_MAIL_ENABLED}';
sub_filter __APPSMITH_DISABLE_TELEMETRY__ '${APPSMITH_DISABLE_TELEMETRY}';
sub_filter __APPSMITH_CLOUD_SERVICES_BASE_URL__ '${APPSMITH_CLOUD_SERVICES_BASE_URL}';
}
location /f {

View File

@ -15,7 +15,7 @@ module.exports = {
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node", "css"],
moduleDirectories: ["node_modules", "src", "test"],
transformIgnorePatterns: [
"<rootDir>/node_modules/(?!codemirror|react-dnd|dnd-core|@babel|(@blueprintjs/core/lib/esnext)|(@blueprintjs/core/lib/esm)|@github)",
"<rootDir>/node_modules/(?!codemirror|react-dnd|dnd-core|@babel|(@blueprintjs/core/lib/esnext)|(@blueprintjs/core/lib/esm)|@github|lodash-es)",
],
moduleNameMapper: {
"\\.(css|less)$": "<rootDir>/test/__mocks__/styleMock.js",
@ -23,6 +23,7 @@ module.exports = {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
"<rootDir>/test/__mocks__/fileMock.js",
"^worker-loader!": "<rootDir>/test/__mocks__/workerMock.js",
"^!!raw-loader!": "<rootDir>/test/__mocks__/derivedMock.js",
"test/(.*)": "<rootDir>/test/$1",
},
globals: {

View File

@ -89,6 +89,7 @@
"popper.js": "^1.15.0",
"prettier": "^1.18.2",
"prismjs": "^1.23.0",
"rc-pagination": "^3.1.3",
"re-reselect": "^3.4.0",
"react": "^16.12.0",
"react-base-table": "^1.9.1",
@ -131,7 +132,8 @@
"tinycolor2": "^1.4.1",
"toposort": "^2.0.2",
"ts-loader": "^6.0.4",
"typescript": "^3.9.2",
"tslib": "^2.1.0",
"typescript": "^4.1.3",
"unescape-js": "^1.1.4",
"url-search-params-polyfill": "^8.0.0",
"worker-loader": "^3.0.2",
@ -150,6 +152,7 @@
"start-prod": "REACT_APP_ENVIRONMENT=PRODUCTION craco start",
"cytest": "REACT_APP_TESTING=TESTING REACT_APP_ENVIRONMENT=DEVELOPMENT craco start & ./node_modules/.bin/cypress open",
"test:unit": "$(npm bin)/jest -b --colors --no-cache --coverage --collectCoverage=true --coverageDirectory='../../' --coverageReporters='json-summary'",
"test:jest": "$(npm bin)/jest --watch",
"storybook": "start-storybook -p 9009 -s public",
"build-storybook": "build-storybook -s public"
},
@ -179,7 +182,7 @@
"@storybook/preset-create-react-app": "^3.1.4",
"@storybook/react": "^5.3.19",
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.2.5",
"@testing-library/react": "^11.2.6",
"@testing-library/user-event": "^13.1.1",
"@types/codemirror": "^0.0.96",
"@types/deep-diff": "^1.0.0",
@ -189,14 +192,15 @@
"@types/react-beautiful-dnd": "^11.0.4",
"@types/react-select": "^3.0.5",
"@types/react-tabs": "^2.3.1",
"@types/react-test-renderer": "^17.0.1",
"@types/react-window": "^1.8.2",
"@types/redux-form": "^8.1.9",
"@types/redux-mock-store": "^1.0.2",
"@types/styled-system": "^5.1.9",
"@types/tern": "0.22.0",
"@types/toposort": "^2.0.3",
"@typescript-eslint/eslint-plugin": "^4.6.0",
"@typescript-eslint/parser": "^4.6.0",
"@typescript-eslint/eslint-plugin": "^4.15.0",
"@typescript-eslint/parser": "^4.15.0",
"babel-loader": "^8.1.0",
"babel-plugin-styled-components": "^1.10.7",
"craco-babel-loader": "^0.1.4",
@ -212,6 +216,7 @@
"eslint-plugin-prettier": "^3.1.4",
"eslint-plugin-react": "^7.21.3",
"eslint-plugin-react-hooks": "^2.3.0",
"factory.ts": "^0.5.1",
"jest-canvas-mock": "^2.3.1",
"mocha": "^7.1.0",
"mocha-junit-reporter": "^1.23.3",

View File

@ -36,6 +36,46 @@
<div id="header-root"></div>
<div id="root"></div>
<script type="text/javascript">
// Ref: https://github.com/Modernizr/Modernizr/blob/94592f279a410436530c7c06acc42a6e90c20150/feature-detects/storage/localstorage.js
const getIsLocalStorageSupported = () => {
try {
window.localStorage.setItem("test", "testA");
window.localStorage.removeItem("test");
return true;
} catch (e) {
return false;
}
};
const isLocalStorageSupported = getIsLocalStorageSupported();
const handleLocalStorageNotSupportedError = () => {
console.error("Localstorage storage is not supported on your device.");
}
const localStorageUtil = {
getItem: (key) => {
if (!isLocalStorageSupported) {
handleLocalStorageNotSupportedError();
return;
}
return window.localStorage.getItem(key);
},
removeItem: (key) => {
if (!isLocalStorageSupported) {
handleLocalStorageNotSupportedError();
return
}
return window.localStorage.removeItem(key);
},
setItem: (key, value) => {
if (!isLocalStorageSupported) {
handleLocalStorageNotSupportedError();
return;
}
return window.localStorage.setItem(key, value);
}
};
// Note: The following handler is necessary for when we have a new deployment.
// What happens?
@ -60,11 +100,11 @@
if(regex.test(url) && url.indexOf(window.location.host) > -1 && message.includes(chunkLoadErrorMessage)) {
console.log("chunk load failed!", url);
// First check if we've set the flag to not reload on chunk load fail
const donotReload = window.localStorage.getItem("donotReloadOnChunkLoadFail");
const donotReload = localStorageUtil.getItem("donotReloadOnChunkLoadFail");
if(!donotReload){
// If donotReload is set to false or undefined, we can set it to true and reload
// This signifies that this is the first time this happened in this session.
window.localStorage.setItem("donotReloadOnChunkLoadFail", true);
localStorageUtil.setItem("donotReloadOnChunkLoadFail", true);
window.location.assign(window.location.href);
}
}
@ -75,7 +115,7 @@
window.addEventListener("load", (event) => {
// This signifies that we've had a chunk load failure in this session
// We clear the entry, as otherwise, we will have a repeated reload scenario.
window.localStorage.removeItem("donotReloadOnChunkLoadFail");
localStorageUtil.removeItem("donotReloadOnChunkLoadFail");
document.getElementById("loader").style.width = "100vw";
setTimeout(() => {
@ -159,6 +199,7 @@
intercomAppID: APP_ID,
mailEnabled: parseConfig("__APPSMITH_MAIL_ENABLED__"),
disableTelemetry: DISABLE_TELEMETRY === "" || DISABLE_TELEMETRY,
cloudServicesBaseUrl: parseConfig("__APPSMITH_CLOUD_SERVICES_BASE_URL__")
};
</script>

View File

@ -72,7 +72,7 @@
<p>At appsmith, accessible from www.appsmith.com, one of our main priorities is the privacy of
our visitors.
This Privacy Policy document contains types of information that is collected and recorded by
appsmith and
appsmith and its "Services" and
how we use it.</p>
<p>If you have additional questions or require more information about our Privacy Policy, do not
hesitate to
@ -99,13 +99,20 @@
<h2>How we use your information</h2>
<p>We use the information we collect in various ways, including to:</p>
<ul>
<li>Provide, operate, and maintain our webste</li>
<li>Improve, personalize, and expand our webste</li>
<li>Understand and analyze how you use our webste</li>
<li>Provide, operate, and maintain our website</li>
<li>The information that you provide, or that we receive from third party integrations,
are used to retrieve and process your data as part of our services.
We do not store any of your data on our servers. We may however,
retain your identifiers to provide unhindered access to these services. These identifiers
could be your Google access tokens, for example.
Wherever our services do this, our documentation will contain a reminder for such
access.</li>
<li>Improve, personalize, and expand our website</li>
<li>Understand and analyze how you use our website</li>
<li>Develop new products, services, features, and functionality</li>
<li>Communicate with you, either directly or through one of our partners, including for
customer service,
to provide you with updates and other information relating to the webste, and for
to provide you with updates and other information relating to the website, and for
marketing and
promotional purposes</li>
<li>Send you emails</li>

View File

@ -51,7 +51,7 @@ class AppErrorBoundary extends Component {
if (this.state.hasError) {
return (
<Wrapper>
<img src={AppCrashImage} alt="App crashed" />
<img alt="App crashed" src={AppCrashImage} />
<div>
<p className="bold-text">Oops! Something went wrong</p>
<p>

View File

@ -90,24 +90,24 @@ class AppRouter extends React.Component<any, any> {
<>
<AppHeader />
<Switch>
<SentryRoute exact path={BASE_URL} component={LandingScreen} />
<SentryRoute component={LandingScreen} exact path={BASE_URL} />
<Redirect exact from={BASE_LOGIN_URL} to={AUTH_LOGIN_URL} />
<Redirect exact from={BASE_SIGNUP_URL} to={SIGN_UP_URL} />
<SentryRoute path={ORG_URL} component={OrganizationLoader} />
<SentryRoute exact path={USERS_URL} component={Users} />
<SentryRoute path={USER_AUTH_URL} component={UserAuth} />
<SentryRoute component={OrganizationLoader} path={ORG_URL} />
<SentryRoute component={Users} exact path={USERS_URL} />
<SentryRoute component={UserAuth} path={USER_AUTH_URL} />
<SentryRoute
component={ApplicationListLoader}
exact
path={APPLICATIONS_URL}
component={ApplicationListLoader}
/>
<SentryRoute path={BUILDER_URL} component={EditorLoader} />
<SentryRoute component={EditorLoader} path={BUILDER_URL} />
<SentryRoute
path={getApplicationViewerPageURL()}
component={AppViewerLoader}
path={getApplicationViewerPageURL()}
/>
<SentryRoute exact path={PROFILE} component={UserProfile} />
<SentryRoute path={APP_VIEW_URL} component={AppViewerLoader} />
<SentryRoute component={UserProfile} exact path={PROFILE} />
<SentryRoute component={AppViewerLoader} path={APP_VIEW_URL} />
<SentryRoute component={PageNotFound} />
</Switch>
</>

View File

@ -13,7 +13,7 @@ type Props = {
authError: string;
};
const LandingScreen = (props: Props) => {
function LandingScreen(props: Props) {
if (props.user && window.location.pathname === BASE_URL) {
if (props.user.email === ANONYMOUS_USERNAME) {
return <Redirect to={AUTH_LOGIN_URL} />;
@ -25,7 +25,7 @@ const LandingScreen = (props: Props) => {
return <ServerUnavailable />;
}
return <PageLoadingBar />;
};
}
const mapStateToProps = (state: AppState) => ({
user: getCurrentUser(state),

View File

@ -3,6 +3,8 @@ import {
ReduxActionTypes,
ReduxAction,
ReduxActionErrorTypes,
EvaluationReduxAction,
ReduxActionWithoutPayload,
} from "constants/ReduxActionConstants";
import { Action } from "entities/Action";
import { batchAction } from "actions/batchActions";
@ -27,10 +29,12 @@ export type FetchActionsPayload = {
export const fetchActions = (
applicationId: string,
): ReduxAction<FetchActionsPayload> => {
postEvalActions: Array<ReduxAction<unknown> | ReduxActionWithoutPayload>,
): EvaluationReduxAction<unknown> => {
return {
type: ReduxActionTypes.FETCH_ACTIONS_INIT,
payload: { applicationId },
postEvalActions,
};
};

View File

@ -22,6 +22,7 @@ export const updateWidgetPropertyRequest = (
export interface BatchPropertyUpdatePayload {
modify?: Record<string, unknown>; //Key value pairs of paths and values to update
remove?: string[]; //Array of paths to delete
triggerPaths?: string[]; // Array of paths in the modify and remove list which are trigger paths
}
export const batchUpdateWidgetProperty = (

View File

@ -5,6 +5,7 @@ import {
} from "constants/ReduxActionConstants";
import { CreateDatasourceConfig } from "api/DatasourcesApi";
import { Datasource } from "entities/Datasource";
import { PluginType } from "entities/Action";
export const createDatasourceFromForm = (payload: CreateDatasourceConfig) => {
return {
@ -44,12 +45,14 @@ export const updateDatasourceSuccess = (
export const redirectAuthorizationCode = (
pageId: string,
datasourceId: string,
pluginType: PluginType,
) => {
return {
type: ReduxActionTypes.REDIRECT_AUTHORIZATION_CODE,
payload: {
pageId,
datasourceId,
pluginType,
},
};
};
@ -105,10 +108,16 @@ export const testDatasource = (payload: Partial<Datasource>) => {
};
};
export const deleteDatasource = (payload: Partial<Datasource>) => {
export const deleteDatasource = (
payload: Partial<Datasource>,
onSuccess?: ReduxAction<unknown>,
onError?: ReduxAction<unknown>,
): ReduxActionWithCallbacks<Partial<Datasource>, unknown, unknown> => {
return {
type: ReduxActionTypes.DELETE_DATASOURCE_INIT,
payload,
onSuccess,
onError,
};
};
@ -128,15 +137,6 @@ export const fetchDatasources = () => {
};
};
export const selectPlugin = (pluginId: string) => {
return {
type: ReduxActionTypes.SELECT_PLUGIN,
payload: {
pluginId,
},
};
};
export const initDatasourcePane = (
pluginType: string,
urlId?: string,
@ -153,8 +153,14 @@ export const storeAsDatasource = () => {
};
};
export const getOAuthAccessToken = (datasourceId: string) => {
return {
type: ReduxActionTypes.SAAS_GET_OAUTH_ACCESS_TOKEN,
payload: { datasourceId },
};
};
export default {
fetchDatasources,
initDatasourcePane,
selectPlugin,
};

View File

@ -0,0 +1,31 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
import { Message } from "entities/AppsmithConsole";
export const debuggerLogInit = (payload: Message) => ({
type: ReduxActionTypes.DEBUGGER_LOG_INIT,
payload,
});
export const debuggerLog = (payload: Message) => ({
type: ReduxActionTypes.DEBUGGER_LOG,
payload,
});
export const clearLogs = () => ({
type: ReduxActionTypes.CLEAR_DEBUGGER_LOGS,
});
export const showDebugger = (payload?: boolean) => ({
type: ReduxActionTypes.SHOW_DEBUGGER,
payload,
});
export const errorLog = (payload: Message) => ({
type: ReduxActionTypes.DEBUGGER_ERROR_LOG,
payload,
});
export const updateErrorLog = (payload: Message) => ({
type: ReduxActionTypes.DEBUGGER_UPDATE_ERROR_LOG,
payload,
});

View File

@ -1,15 +1,16 @@
import { FetchPageRequest, PageLayout, SavePageResponse } from "api/PageApi";
import { WidgetOperation } from "widgets/BaseWidget";
import { WidgetType } from "constants/WidgetConstants";
import {
EvaluationReduxAction,
ReduxAction,
ReduxActionTypes,
ReduxActionWithoutPayload,
UpdateCanvasPayload,
} from "constants/ReduxActionConstants";
import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { WidgetOperation } from "widgets/BaseWidget";
import { FetchPageRequest, PageLayout, SavePageResponse } from "api/PageApi";
import { APP_MODE, UrlDataState } from "reducers/entityReducers/appReducer";
import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
export interface FetchPageListPayload {
applicationId: string;
@ -46,18 +47,14 @@ export const fetchPublishedPage = (pageId: string, bustCache = false) => ({
},
});
export const fetchPageSuccess = (
postEvalActions: ReduxAction<unknown>[],
): EvaluationReduxAction<unknown> => {
export const fetchPageSuccess = (): ReduxActionWithoutPayload => {
return {
type: ReduxActionTypes.FETCH_PAGE_SUCCESS,
payload: {},
postEvalActions,
};
};
export const fetchPublishedPageSuccess = (
postEvalActions: ReduxAction<unknown>[],
postEvalActions: Array<ReduxAction<unknown> | ReduxActionWithoutPayload>,
): EvaluationReduxAction<undefined> => ({
type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS,
postEvalActions,
@ -230,12 +227,23 @@ export type WidgetAddChildren = {
}>;
};
export type WidgetUpdateProperty = {
widgetId: string;
propertyPath: string;
propertyValue: any;
};
export const updateWidget = (
operation: WidgetOperation,
widgetId: string,
payload: any,
): ReduxAction<
WidgetAddChild | WidgetMove | WidgetResize | WidgetDelete | WidgetAddChildren
| WidgetAddChild
| WidgetMove
| WidgetResize
| WidgetDelete
| WidgetAddChildren
| WidgetUpdateProperty
> => {
return {
type: ReduxActionTypes["WIDGET_" + operation],

View File

@ -4,6 +4,7 @@ import {
ReduxActionWithoutPayload,
} from "constants/ReduxActionConstants";
import { PluginFormPayload } from "api/PluginApi";
import { DependencyMap } from "utils/DynamicBindingUtils";
export const fetchPlugins = (): ReduxActionWithoutPayload => ({
type: ReduxActionTypes.FETCH_PLUGINS_REQUEST,
@ -17,6 +18,7 @@ export type PluginFormsPayload = {
formConfigs: Record<string, any[]>;
editorConfigs: Record<string, any[]>;
settingConfigs: Record<string, any[]>;
dependencies: Record<string, DependencyMap>;
};
export const fetchPluginFormConfigsSuccess = (

View File

@ -0,0 +1,11 @@
import * as actions from "./propertyPaneActions";
import { ReduxActionTypes } from "constants/ReduxActionConstants";
describe("property pane action actions", () => {
it("should create an action hide Property Pane", () => {
const expectedAction = {
type: ReduxActionTypes.HIDE_PROPERTY_PANE,
};
expect(actions.hidePropertyPane()).toEqual(expectedAction);
});
});

View File

@ -8,3 +8,9 @@ export const updateWidgetName = (widgetId: string, newName: string) => {
},
};
};
export const hidePropertyPane = () => {
return {
type: ReduxActionTypes.HIDE_PROPERTY_PANE,
};
};

View File

@ -0,0 +1,15 @@
import { ReduxActionTypes } from "constants/ReduxActionConstants";
export const historyPush = (url: string) => ({
type: ReduxActionTypes.HISTORY_PUSH,
payload: {
url,
},
});
export const windowRedirect = (url: string) => ({
type: ReduxActionTypes.REDIRECT_WINDOW_LOCATION,
payload: {
url,
},
});

View File

@ -2,11 +2,11 @@ import {
ReduxActionTypes,
ReduxAction,
ReduxActionErrorTypes,
ReduxActionWithoutPayload,
} from "constants/ReduxActionConstants";
import {
ExecuteActionPayload,
ExecuteErrorPayload,
PageAction,
} from "constants/AppsmithActionConstants/ActionConstants";
import { BatchAction, batchAction } from "actions/batchActions";
import PerformanceTracker, {
@ -30,11 +30,8 @@ export const executeActionError = (
};
};
export const executePageLoadActions = (
payload: PageAction[][],
): ReduxAction<PageAction[][]> => ({
export const executePageLoadActions = (): ReduxActionWithoutPayload => ({
type: ReduxActionTypes.EXECUTE_PAGE_LOAD_ACTIONS,
payload,
});
export const disableDragAction = (

View File

@ -108,11 +108,7 @@ class ActionAPI extends API {
static apiUpdateCancelTokenSource: CancelTokenSource;
static queryUpdateCancelTokenSource: CancelTokenSource;
static fetchAPI(id: string): AxiosPromise<GenericApiResponse<Action>> {
return API.get(`${ActionAPI.url}/${id}`);
}
static createAPI(
static createAction(
apiConfig: Partial<Action>,
): AxiosPromise<ActionCreateUpdateResponse> {
return API.post(ActionAPI.url, apiConfig);
@ -136,7 +132,7 @@ class ActionAPI extends API {
return API.get(ActionAPI.url, { pageId });
}
static updateAPI(
static updateAction(
apiConfig: Partial<Action>,
): AxiosPromise<ActionCreateUpdateResponse> {
if (ActionAPI.apiUpdateCancelTokenSource) {
@ -159,24 +155,6 @@ class ActionAPI extends API {
return API.delete(`${ActionAPI.url}/${id}`);
}
static createQuery(
createQuery: CreateActionRequest<QueryConfig>,
): AxiosPromise<ActionCreateUpdateResponse> {
return API.post(ActionAPI.url, createQuery);
}
static updateQuery(
updateQuery: UpdateActionRequest<QueryConfig>,
): AxiosPromise<ActionCreateUpdateResponse> {
if (ActionAPI.queryUpdateCancelTokenSource) {
ActionAPI.queryUpdateCancelTokenSource.cancel();
}
ActionAPI.queryUpdateCancelTokenSource = axios.CancelToken.source();
return API.post(ActionAPI.url, updateQuery, undefined, {
cancelToken: ActionAPI.queryUpdateCancelTokenSource.token,
});
}
static executeAction(
executeAction: ExecuteActionRequest,
timeout?: number,
@ -192,12 +170,6 @@ class ActionAPI extends API {
});
}
static executeQuery(
executeAction: any,
): AxiosPromise<ActionExecutionResponse> {
return API.post(ActionAPI.url + "/execute", executeAction);
}
static toggleActionExecuteOnLoad(actionId: string, shouldExecute: boolean) {
return API.put(ActionAPI.url + `/executeOnLoad/${actionId}`, undefined, {
flag: shouldExecute.toString(),

View File

@ -0,0 +1,6 @@
import { getAppsmithConfigs } from "configs";
const { cloudServicesBaseUrl: BASE_URL } = getAppsmithConfigs();
export const authorizeSaasWithAppsmithToken = (appsmithToken: string) =>
`${BASE_URL}/api/v1/integrations/oauth/authorize?appsmithToken=${appsmithToken}`;

View File

@ -1,11 +1,13 @@
import Api from "api/Api";
import { AxiosPromise } from "axios";
import { GenericApiResponse } from "api/ApiResponses";
import { PluginType } from "entities/Action";
import { DependencyMap } from "utils/DynamicBindingUtils";
export interface Plugin {
id: string;
name: string;
type: "API" | "DB";
type: PluginType;
packageName: string;
iconLocation?: string;
uiComponent: "ApiEditorForm" | "RapidApiEditorForm" | "DbEditorForm";
@ -20,6 +22,7 @@ export interface PluginFormPayload {
form: any[];
editor: any[];
setting: any[];
dependencies: DependencyMap;
}
class PluginsApi extends Api {

View File

@ -0,0 +1,25 @@
import Api from "./Api";
import { AxiosPromise } from "axios";
import { GenericApiResponse } from "api/ApiResponses";
import { Datasource } from "entities/Datasource";
class SaasApi extends Api {
static url = "v1/saas";
static getAppsmithToken(
datasourceId: string,
pageId: string,
): AxiosPromise<GenericApiResponse<string>> {
return Api.post(`${SaasApi.url}/${datasourceId}/pages/${pageId}/oauth`);
}
static getAccessToken(
datasourceId: string,
token: string,
): AxiosPromise<GenericApiResponse<Datasource>> {
return Api.post(
`${SaasApi.url}/${datasourceId}/token?appsmithToken=${token}`,
);
}
}
export default SaasApi;

View File

@ -0,0 +1,3 @@
<svg width="20" height="18" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.1935 0C8.53666 0 7.19351 1.34315 7.19351 3H13.3474C13.3474 1.34315 12.0042 0 10.3474 0H10.1935ZM15.0329 4.2H4.96715L1.67954 2.06384L0.399787 3.93616L4.11659 6.3512V8.625H0V10.875H4.11659V11.8462C4.11659 12.2648 4.15839 12.6736 4.23804 13.0688L0.399787 15.5627L1.67954 17.4351L5.11276 15.2043C6.21093 16.8875 8.11076 18 10.2704 18C12.3244 18 14.1434 16.9937 15.2612 15.4473L18.3205 17.4351L19.6002 15.5627L16.2329 13.3748C16.3579 12.886 16.4243 12.3738 16.4243 11.8462V10.875H20V8.625H16.4243V5.99976L19.6002 3.93616L18.3205 2.06384L15.0329 4.2Z" fill="#716E6E"/>
</svg>

After

Width:  |  Height:  |  Size: 719 B

View File

@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 0C3.14027 0 0 3.14027 0 7C0 10.8597 3.14027 14 7 14C10.8597 14 14 10.8597 14 7C14 3.14027 10.8597 0 7 0ZM7 12.25C4.10525 12.25 1.75 9.89475 1.75 7C1.75 5.89515 2.08496 4.85067 2.72185 3.95943L10.0406 11.2781C9.14933 11.915 8.10485 12.25 7 12.25ZM11.2781 10.0406L3.95943 2.72185C4.85067 2.08496 5.89515 1.75 7 1.75C9.89475 1.75 12.25 4.10525 12.25 7C12.25 8.10485 11.915 9.14933 11.2781 10.0406Z" fill="#858282"/>
</svg>

After

Width:  |  Height:  |  Size: 528 B

View File

@ -0,0 +1,4 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13 1L1 13" stroke="#716E6E" stroke-width="1.5"/>
<path d="M13 13L1 1" stroke="#716E6E" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 221 B

View File

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="2.5" y="2.5" width="11" height="11" rx="1.5" stroke="#858282" stroke-opacity="0.6"/>
<path d="M6 10L10 6M10 6H6.92308M10 6V9.07692" stroke="#858282" stroke-opacity="0.6" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 331 B

View File

@ -0,0 +1,10 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.879 0.418515C10.879 0.649535 11.067 0.83703 11.2976 0.83703C11.5286 0.83703 11.7161 1.02411 11.7161 1.25554C11.7161 1.48698 11.5286 1.67406 11.2976 1.67406C11.067 1.67406 10.879 1.86155 10.879 2.09257C10.879 2.32401 10.6915 2.51109 10.4605 2.51109C10.2295 2.51109 10.042 2.32401 10.042 2.09257C10.042 1.86155 9.8541 1.67406 9.6235 1.67406C9.39248 1.67406 9.20498 1.48698 9.20498 1.25554C9.20498 1.02411 9.39248 0.83703 9.6235 0.83703C9.8541 0.83703 10.042 0.649535 10.042 0.418515C10.042 0.187076 10.2295 0 10.4605 0C10.6915 0 10.879 0.187076 10.879 0.418515ZM11.0968 2.91647L13.8833 5.70297L3.58631 16L0.799805 13.2135L11.0968 2.91647ZM11.1158 6.71379L10.1201 5.71815L11.1158 4.7225L12.1114 5.71815L11.1158 6.71379ZM2.58583 13.2519L3.58147 14.2475L10.2271 7.60191L9.23142 6.60627L2.58583 13.2519ZM4.21032 5.71869C4.55622 5.71869 4.83809 5.99993 4.83809 6.34646C4.83809 6.69362 5.11933 6.97423 5.46586 6.97423C5.8124 6.97423 6.09364 6.69362 6.09364 6.34646C6.09364 5.99993 6.37551 5.71869 6.72141 5.71869C7.06794 5.71869 7.34918 5.43807 7.34918 5.09092C7.34918 4.74376 7.06794 4.46314 6.72141 4.46314C6.37551 4.46314 6.09364 4.1819 6.09364 3.83537C6.09364 3.48821 5.8124 3.2076 5.46586 3.2076C5.11933 3.2076 4.83809 3.48821 4.83809 3.83537C4.83809 4.1819 4.55622 4.46314 4.21032 4.46314C3.86379 4.46314 3.58255 4.74376 3.58255 5.09092C3.58255 5.43807 3.86379 5.71869 4.21032 5.71869ZM13.2079 9.38031C13.2079 9.66909 13.4428 9.90345 13.731 9.90345C14.0198 9.90345 14.2542 10.1373 14.2542 10.4266C14.2542 10.7159 14.0198 10.9497 13.731 10.9497C13.4428 10.9497 13.2079 11.1841 13.2079 11.4729C13.2079 11.7622 12.9735 11.996 12.6847 11.996C12.396 11.996 12.1616 11.7622 12.1616 11.4729C12.1616 11.1841 11.9267 10.9497 11.6384 10.9497C11.3497 10.9497 11.1153 10.7159 11.1153 10.4266C11.1153 10.1373 11.3497 9.90345 11.6384 9.90345C11.9267 9.90345 12.1616 9.66909 12.1616 9.38031C12.1616 9.09101 12.396 8.85717 12.6847 8.85717C12.9735 8.85717 13.2079 9.09101 13.2079 9.38031ZM15.3003 7.39249C15.185 7.39249 15.091 7.29874 15.091 7.18323C15.091 7.06751 14.9973 6.97398 14.8818 6.97398C14.7663 6.97398 14.6725 7.06751 14.6725 7.18323C14.6725 7.29874 14.5786 7.39249 14.4633 7.39249C14.3478 7.39249 14.254 7.48603 14.254 7.60175C14.254 7.71747 14.3478 7.81101 14.4633 7.81101C14.5786 7.81101 14.6725 7.90475 14.6725 8.02026C14.6725 8.13598 14.7663 8.22952 14.8818 8.22952C14.9973 8.22952 15.091 8.13598 15.091 8.02026C15.091 7.90475 15.185 7.81101 15.3003 7.81101C15.4158 7.81101 15.5096 7.71747 15.5096 7.60175C15.5096 7.48603 15.4158 7.39249 15.3003 7.39249Z" fill="#E22C2C"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,4 @@
<svg width="72" height="73" viewBox="0 0 72 73" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M42.3253 4.44421L71.3309 63.0473C72.8988 66.2151 71.6018 70.0542 68.434 71.6221C67.5514 72.059 66.5798 72.2862 65.595 72.2862H6.4C2.86538 72.2862 0 69.4209 0 65.8863C0 64.8666 0.243617 63.8618 0.710563 62.9553L30.9 4.35225C32.5187 1.21006 36.3782 -0.0249646 39.5204 1.59374C40.7358 2.21989 41.7188 3.21883 42.3253 4.44421Z" fill="#E8E8E8"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M35.9033 53.8863C38.1125 53.8863 39.9033 55.6772 39.9033 57.8863C39.9033 60.0955 38.1125 61.8863 35.9033 61.8863C33.6942 61.8863 31.9033 60.0955 31.9033 57.8863C31.9033 55.6772 33.6942 53.8863 35.9033 53.8863ZM35.9033 23.4863C37.9863 23.4863 39.6749 25.1749 39.6749 27.2579C39.6749 27.3318 39.6728 27.4057 39.6684 27.4794L38.4443 48.2905C38.3651 49.6358 37.251 50.6863 35.9033 50.6863C34.5556 50.6863 33.4415 49.6358 33.3624 48.2905L32.1382 27.4794C32.0159 25.4 33.6024 23.6152 35.6818 23.4928C35.7556 23.4885 35.8294 23.4863 35.9033 23.4863Z" fill="#979797"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,3 @@
<svg width="24" height="20" viewBox="0 0 24 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.79999 11C9.46273 11 10 11.5372 10 12.2V18.8C10 19.4628 9.46273 20 8.79999 20H1.20001C0.53727 20 0 19.4628 0 18.8V12.2C0 11.5372 0.53727 11 1.20001 11H8.79999ZM20 16V19H12V16H20ZM6.24258 15.5072C6.22756 15.5186 6.21421 15.5321 6.20297 15.5472L4.85718 17.3627C4.7914 17.4514 4.66612 17.47 4.57739 17.4042C4.5678 17.3971 4.55889 17.3891 4.55072 17.3804L3.52838 16.2915C3.45278 16.211 3.32619 16.207 3.24567 16.2826C3.23763 16.2901 3.23023 16.2984 3.22354 16.3071L1.74039 18.2513C1.67339 18.3392 1.69026 18.4646 1.77808 18.5316C1.81293 18.5582 1.85555 18.5726 1.89938 18.5726H8.42206C8.53252 18.5726 8.62204 18.4831 8.62204 18.3727C8.62204 18.3289 8.60772 18.2864 8.58124 18.2516L6.52283 15.5453C6.45596 15.4574 6.3305 15.4403 6.24258 15.5072ZM24 11V14H12V11H24ZM8.79999 0C9.46273 0 10 0.537211 10 1.19995V7.80005C10 8.46279 9.46273 9 8.79999 9H1.20001C0.53727 9 0 8.46279 0 7.80005V1.19995C0 0.537211 0.53727 0 1.20001 0H8.79999ZM20 5V8H12V5H20ZM6.24258 4.5072C6.22756 4.51862 6.21421 4.53209 6.20297 4.54724L4.85718 6.36267C4.7914 6.4514 4.66612 6.46995 4.57739 6.40417C4.5678 6.39706 4.55889 6.38907 4.55072 6.38037L3.52838 5.2915C3.45278 5.21098 3.32619 5.20699 3.24567 5.28259C3.23763 5.29014 3.23023 5.29837 3.22354 5.30713L1.74039 7.25134C1.67339 7.33917 1.69026 7.46463 1.77808 7.53162C1.81293 7.55821 1.85555 7.57263 1.89938 7.57263H8.42206C8.53252 7.57263 8.62204 7.48314 8.62204 7.37268C8.62204 7.32894 8.60772 7.2864 8.58124 7.25159L6.52283 4.54529C6.45596 4.45737 6.3305 4.44033 6.24258 4.5072ZM24 0V3H12V0H24Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -223,7 +223,7 @@ export type AppIconProps = CommonComponentProps & {
name: AppIconName;
};
const AppIcon = (props: AppIconProps) => {
function AppIcon(props: AppIconProps) {
const styledProps = useMemo(() => appSizeHandler(props.size || Size.medium), [
props,
]);
@ -484,6 +484,6 @@ const AppIcon = (props: AppIconProps) => {
{returnIcon}
</IconWrapper>
) : null;
};
}
export default AppIcon;

View File

@ -376,13 +376,13 @@ const IconSizeProp = (size?: Size) => {
return size ? sizeMapping[size] : IconSize.SMALL;
};
const TextLoadingState = ({ text }: { text?: string }) => (
<VisibilityWrapper>{text}</VisibilityWrapper>
);
function TextLoadingState({ text }: { text?: string }) {
return <VisibilityWrapper>{text}</VisibilityWrapper>;
}
const IconLoadingState = ({ size, icon }: { size?: Size; icon?: IconName }) => (
<Icon name={icon} size={IconSizeProp(size)} invisible />
);
function IconLoadingState({ size, icon }: { size?: Size; icon?: IconName }) {
return <Icon invisible name={icon} size={IconSizeProp(size)} />;
}
const getIconContent = (props: ButtonProps) =>
props.icon ? (
@ -410,39 +410,44 @@ const getButtonContent = (props: ButtonProps) => (
</>
);
const ButtonComponent = (props: ButtonProps) => (
<StyledButton
className={props.className}
data-cy={props.cypressSelector}
{...props}
onClick={(e: React.MouseEvent<HTMLElement>) =>
props.onClick && props.onClick(e)
}
>
{getButtonContent(props)}
</StyledButton>
);
function ButtonComponent(props: ButtonProps) {
return (
<StyledButton
className={props.className}
data-cy={props.cypressSelector}
{...props}
onClick={(e: React.MouseEvent<HTMLElement>) =>
props.onClick && props.onClick(e)
}
>
{getButtonContent(props)}
</StyledButton>
);
}
const LinkButtonComponent = (props: ButtonProps) => (
<StyledLinkButton
href={props.href}
className={props.className}
data-cy={props.cypressSelector}
{...props}
onClick={(e: React.MouseEvent<HTMLElement>) =>
props.onClick && props.onClick(e)
}
>
{getButtonContent(props)}
</StyledLinkButton>
);
function LinkButtonComponent(props: ButtonProps) {
return (
<StyledLinkButton
className={props.className}
data-cy={props.cypressSelector}
href={props.href}
{...props}
onClick={(e: React.MouseEvent<HTMLElement>) =>
props.onClick && props.onClick(e)
}
>
{getButtonContent(props)}
</StyledLinkButton>
);
}
const Button = (props: ButtonProps) =>
props.tag === "button" ? (
function Button(props: ButtonProps) {
return props.tag === "button" ? (
<ButtonComponent {...props} />
) : (
<LinkButtonComponent {...props} />
);
}
export default Button;

View File

@ -41,7 +41,7 @@ interface ButtonTabComponentProps {
selectButton: (value: string) => void;
}
const ButtonTabComponent = (props: ButtonTabComponentProps) => {
function ButtonTabComponent(props: ButtonTabComponentProps) {
return (
<FlexWrapper>
{props.options.map((option: ButtonTabOption, index: number) => {
@ -50,17 +50,17 @@ const ButtonTabComponent = (props: ButtonTabComponentProps) => {
const isSelected = props.values.includes(option.value);
return (
<ItemWrapper
key={index}
selected={isSelected}
onClick={() => props.selectButton(option.value)}
className={`t--button-tab-${option.value}`}
key={index}
onClick={() => props.selectButton(option.value)}
selected={isSelected}
>
<ControlIcon width={24} height={24} />
<ControlIcon height={24} width={24} />
</ItemWrapper>
);
})}
</FlexWrapper>
);
};
}
export default ButtonTabComponent;

View File

@ -73,14 +73,14 @@ Callout.defaultProps = {
function Callout(props: CalloutProps) {
return (
<CalloutContainer variant={props.variant || Variant.info} fill={props.fill}>
<CalloutContainer fill={props.fill} variant={props.variant || Variant.info}>
{props.text && props.variant !== Variant.info ? (
<Icon name={props.variant} size={IconSize.MEDIUM} />
) : null}
<Text type={TextType.P2}>{props.text}</Text>
{props.label ? props.label : null}
{props.closeButton ? (
<Label variant={props.variant || Variant.info} onClick={props.onClose}>
<Label onClick={props.onClose} variant={props.variant || Variant.info}>
<Icon name="close-modal" />
</Label>
) : null}

View File

@ -106,7 +106,7 @@ const useUpdate = (intitialValue?: boolean) => {
return [checked, setChecked] as const;
};
const Checkbox = (props: CheckboxProps) => {
function Checkbox(props: CheckboxProps) {
const [checked, setChecked] = useUpdate(props.isDefaultChecked);
const onChangeHandler = (checked: boolean) => {
@ -121,20 +121,20 @@ const Checkbox = (props: CheckboxProps) => {
{props.info ? <Text type={TextType.P3}>{props.info}</Text> : null}
</LabelContainer>
<input
type="checkbox"
disabled={props.disabled}
checked={checked}
disabled={props.disabled}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
onChangeHandler(e.target.checked)
}
type="checkbox"
/>
<Checkmark
disabled={props.disabled}
isChecked={checked}
info={props.info}
isChecked={checked}
/>
</StyledCheckbox>
);
};
}
export default Checkbox;

View File

@ -93,14 +93,14 @@ const EmptyColorIconWrapper = styled.div`
}
`;
const ColorBoard = (props: ColorBoardProps) => {
function ColorBoard(props: ColorBoardProps) {
return (
<ColorsWrapper>
{defaultColors.map((color: string, index: number) => (
<ColorTab
key={index}
color={color}
className={Classes.POPOVER_DISMISS}
color={color}
key={index}
onClick={() => props.selectColor(color)}
>
{props.selectedColor === color && <CheckedIcon />}
@ -113,7 +113,7 @@ const ColorBoard = (props: ColorBoardProps) => {
</EmptyColorIconWrapper>
</ColorsWrapper>
);
};
}
const NoColorIconWrapper = styled.div`
position: absolute;
@ -148,7 +148,7 @@ interface ColorPickerProps {
changeColor: (color: string) => void;
}
const ColorPickerComponent = (props: ColorPickerProps) => {
function ColorPickerComponent(props: ColorPickerProps) {
const [color, setColor] = React.useState(props.color);
const debouncedOnChange = React.useCallback(
debounce(props.changeColor, 500),
@ -161,16 +161,16 @@ const ColorPickerComponent = (props: ColorPickerProps) => {
};
return (
<Popover
minimal
usePortal
enforceFocus={false}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM}
minimal
modifiers={{
offset: {
offset: "0, 24px",
},
}}
position={Position.BOTTOM}
usePortal
>
<StyledInputGroup
leftIcon={
@ -189,14 +189,14 @@ const ColorPickerComponent = (props: ColorPickerProps) => {
value={color}
/>
<ColorBoard
selectedColor={color}
selectColor={(color) => {
setColor(color);
props.changeColor(color);
}}
selectedColor={color}
/>
</Popover>
);
};
}
export default ColorPickerComponent;

View File

@ -56,7 +56,7 @@ const ColorBox = styled.div<{ selected: string; color: string }>`
`}
`;
const ColorSelector = (props: ColorSelectorProps) => {
function ColorSelector(props: ColorSelectorProps) {
const [selected, setSelected] = useState<string>(
props.defaultValue || props.colorPalette[0],
);
@ -68,28 +68,28 @@ const ColorSelector = (props: ColorSelectorProps) => {
}, [props.defaultValue]);
return (
<Palette fill={props.fill} data-cy={props.cypressSelector}>
<Palette data-cy={props.cypressSelector} fill={props.fill}>
{props.colorPalette.map((hex: string, index: number) => {
return (
<ColorBox
key={index}
selected={selected}
className={
selected === hex ? "t--color-selected" : "t--color-not-selected"
}
color={hex}
key={index}
onClick={() => {
if (selected !== hex) {
setSelected(hex);
props.onSelect && props.onSelect(hex);
}
}}
className={
selected === hex ? "t--color-selected" : "t--color-not-selected"
}
selected={selected}
/>
);
})}
</Palette>
);
};
}
ColorSelector.defaultProps = {
fill: false,

View File

@ -15,7 +15,7 @@ const Wrapper = styled.div`
}
`;
const CopyToClipboard = (props: any) => {
function CopyToClipboard(props: any) {
const { copyText } = props;
const copyURLInput = createRef<HTMLInputElement>();
const [isCopied, setIsCopied] = useState(false);
@ -36,25 +36,25 @@ const CopyToClipboard = (props: any) => {
return (
<Wrapper>
<TextInput
defaultValue={copyText}
fill
ref={copyURLInput}
readOnly
onChange={() => {
selectText();
}}
defaultValue={copyText}
readOnly
ref={copyURLInput}
/>
<Button
text={isCopied ? "Copied" : "Copy"}
category={Category.tertiary}
onClick={() => {
copyToClipboard(copyText);
}}
size={Size.large}
text={isCopied ? "Copied" : "Copy"}
/>
</Wrapper>
);
};
}
export default CopyToClipboard;

View File

@ -100,25 +100,25 @@ interface DatePickerComponentProps {
parseDate?: (dateStr: string) => Date;
}
const DatePickerComponent = (props: DatePickerComponentProps) => {
function DatePickerComponent(props: DatePickerComponentProps) {
return (
<StyledDateInput
className={Classes.DATE_PICKER_OVARLAY}
closeOnSelection={props.closeOnSelection}
formatDate={props.formatDate}
highlightCurrentDay={props.highlightCurrentDay}
maxDate={props.maxDate}
minDate={props.minDate}
onChange={props.onChange}
parseDate={props.parseDate}
placeholder={props.placeholder}
popoverProps={{ usePortal: true }}
showActionsBar={props.showActionsBar}
timePrecision={props.timePrecision}
closeOnSelection={props.closeOnSelection}
value={props.value}
highlightCurrentDay={props.highlightCurrentDay}
onChange={props.onChange}
formatDate={props.formatDate}
parseDate={props.parseDate}
className={Classes.DATE_PICKER_OVARLAY}
popoverProps={{ usePortal: true }}
/>
);
};
}
DatePickerComponent.defaultProps = {
highlightCurrentDay: true,

View File

@ -93,7 +93,7 @@ type DialogComponentProps = {
className?: string;
};
export const DialogComponent = (props: DialogComponentProps) => {
export function DialogComponent(props: DialogComponentProps) {
const [isOpen, setIsOpen] = useState(!!props.isOpen);
const onClose = () => {
@ -107,7 +107,7 @@ export const DialogComponent = (props: DialogComponentProps) => {
const getHeader = props.getHeader;
return (
<React.Fragment>
<>
<TriggerWrapper
className="ads-dialog-trigger"
onClick={() => {
@ -118,23 +118,23 @@ export const DialogComponent = (props: DialogComponentProps) => {
{props.trigger}
</TriggerWrapper>
<StyledDialog
canOutsideClickClose={!!props.canOutsideClickClose}
canEscapeKeyClose={!!props.canEscapeKeyClose}
title={props.title}
onClose={onClose}
isOpen={isOpen}
width={props.width}
setMaxWidth={props.setMaxWidth}
maxHeight={props.maxHeight}
onOpening={props.onOpening}
showHeaderUnderline={props.showHeaderUnderline}
canOutsideClickClose={!!props.canOutsideClickClose}
className={props.className}
isOpen={isOpen}
maxHeight={props.maxHeight}
onClose={onClose}
onOpening={props.onOpening}
setMaxWidth={props.setMaxWidth}
showHeaderUnderline={props.showHeaderUnderline}
title={props.title}
width={props.width}
>
{getHeader && getHeader()}
<div className={Classes.DIALOG_BODY}>{props.children}</div>
</StyledDialog>
</React.Fragment>
</>
);
};
}
export default DialogComponent;

View File

@ -59,7 +59,7 @@ const DraggableListWrapper = styled.div`
}
`;
const DraggableList = ({ items, ItemRenderer, onUpdate, itemHeight }: any) => {
function DraggableList({ items, ItemRenderer, onUpdate, itemHeight }: any) {
// order of items in the list
const order = useRef<any>(items.map((_: any, index: any) => index));
@ -111,12 +111,12 @@ const DraggableList = ({ items, ItemRenderer, onUpdate, itemHeight }: any) => {
});
return (
<DraggableListWrapper
className="content"
onMouseDown={() => {
// set events to null to stop other parent draggable elements execution(ex: Property pane)
document.onmouseup = null;
document.onmousemove = null;
}}
className="content"
style={{ height: items.length * itemHeight }}
>
{springs.map(({ zIndex, y, scale }, i) => (
@ -134,13 +134,13 @@ const DraggableList = ({ items, ItemRenderer, onUpdate, itemHeight }: any) => {
}}
>
<div>
<ItemRenderer item={items[i]} index={i} />
<ItemRenderer index={i} item={items[i]} />
</div>
</animated.div>
))}
</DraggableListWrapper>
);
};
}
DraggableList.displayName = "DraggableList";
export default DraggableList;

View File

@ -185,22 +185,24 @@ const SelectedIcon = styled(Icon)`
}
`;
const DefaultDropDownValueNode = ({
function DefaultDropDownValueNode({
selected,
showLabelOnly,
}: {
selected: DropdownOption;
showLabelOnly?: boolean;
}) => (
<SelectedDropDownHolder>
{selected.icon ? (
<SelectedIcon name={selected.icon} size={IconSize.XXS} />
) : null}
<Text type={TextType.P1}>
{showLabelOnly ? selected.label : selected.value}
</Text>
</SelectedDropDownHolder>
);
}) {
return (
<SelectedDropDownHolder>
{selected.icon ? (
<SelectedIcon name={selected.icon} size={IconSize.XXS} />
) : null}
<Text type={TextType.P1}>
{showLabelOnly ? selected.label : selected.value}
</Text>
</SelectedDropDownHolder>
);
}
export default function Dropdown(props: DropdownProps) {
const {
@ -226,25 +228,25 @@ export default function Dropdown(props: DropdownProps) {
);
return (
<DropdownContainer
tabIndex={0}
data-cy={props.cypressSelector}
width={props.width || "260px"}
height={props.height || "38px"}
tabIndex={0}
width={props.width || "260px"}
>
<Popover
boundary="scrollParent"
isOpen={isOpen && !props.disabled}
minimal
onInteraction={(state) => setIsOpen(state)}
popoverClassName={props.className}
position={Position.BOTTOM_LEFT}
isOpen={isOpen && !props.disabled}
onInteraction={(state) => setIsOpen(state)}
boundary="scrollParent"
>
<Selected
isOpen={isOpen}
disabled={props.disabled}
onClick={() => setIsOpen(!isOpen)}
className={props.className}
disabled={props.disabled}
height={props.height || "38px"}
isOpen={isOpen}
onClick={() => setIsOpen(!isOpen)}
>
<SelectedValueNode
selected={selected}
@ -256,15 +258,15 @@ export default function Dropdown(props: DropdownProps) {
{props.options.map((option: DropdownOption, index: number) => {
return (
<OptionWrapper
key={index}
selected={selected.value === option.value}
onClick={() => optionClickHandler(option)}
className="t--dropdown-option"
key={index}
onClick={() => optionClickHandler(option)}
selected={selected.value === option.value}
>
{option.icon ? (
<SelectedIcon
name={option.icon}
fillColor={option?.iconColor}
name={option.icon}
size={option.iconSize || IconSize.XXS}
/>
) : null}

View File

@ -152,7 +152,7 @@ const IconWrapper = styled.div`
justify-content: flex-end;
`;
export const EditableText = (props: EditableTextProps) => {
export function EditableText(props: EditableTextProps) {
const {
onBlur,
onTextChanged,
@ -268,36 +268,36 @@ export const EditableText = (props: EditableTextProps) => {
return (
<EditableTextWrapper
filled={!!props.fill}
onMouseEnter={nonEditMode}
onDoubleClick={
props.editInteractionKind === EditInteractionKind.DOUBLE
? editMode
: noop
}
onClick={
props.editInteractionKind === EditInteractionKind.SINGLE
? editMode
: noop
}
onDoubleClick={
props.editInteractionKind === EditInteractionKind.DOUBLE
? editMode
: noop
}
onMouseEnter={nonEditMode}
>
<TextContainer
bgColor={bgColor}
className="editable-text-container"
data-cy={props.cypressSelector}
isInvalid={!!isInvalid}
isEditing={isEditing}
bgColor={bgColor}
isInvalid={!!isInvalid}
underline={props.underline}
>
<BlueprintEditableText
className={props.className}
disabled={!isEditing}
isEditing={isEditing}
onCancel={onConfirm}
onChange={onInputchange}
onConfirm={onConfirm}
value={value}
selectAllOnFocus
placeholder={props.placeholder || defaultValue}
className={props.className}
onCancel={onConfirm}
selectAllOnFocus
value={value}
/>
{savingState === SavingState.STARTED ? (
@ -317,6 +317,6 @@ export const EditableText = (props: EditableTextProps) => {
) : null}
</EditableTextWrapper>
);
};
}
export default EditableText;

View File

@ -86,23 +86,16 @@ export default function EditableTextWrapper(props: EditableTextWrapperProps) {
return (
<Container
isEditing={isEditing}
savingState={props.savingState}
isInvalid={isValid}
savingState={props.savingState}
>
<EditableText
className={props.className}
defaultValue={props.defaultValue}
editInteractionKind={props.editInteractionKind}
placeholder={props.placeholder}
fill={!!props.fill}
hideEditIcon={props.hideEditIcon}
isEditingDefault={props.isNewApp}
savingState={props.savingState}
fill={!!props.fill}
onBlur={(value) => {
setIsEditing(false);
props.onBlur && props.onBlur(value);
}}
className={props.className}
onTextChanged={() => setIsEditing(true)}
isInvalid={(value: string) => {
setIsEditing(true);
if (props.isInvalid) {
@ -118,6 +111,13 @@ export default function EditableTextWrapper(props: EditableTextWrapperProps) {
return false;
}
}}
onBlur={(value) => {
setIsEditing(false);
props.onBlur && props.onBlur(value);
}}
onTextChanged={() => setIsEditing(true)}
placeholder={props.placeholder}
savingState={props.savingState}
/>
</Container>
);

View File

@ -158,7 +158,7 @@ export function CloudinaryUploader(
});
}
const FilePickerComponent = (props: FilePickerProps) => {
function FilePickerComponent(props: FilePickerProps) {
const { logoUploadError } = props;
const [fileInfo, setFileInfo] = useState<{ name: string; size: number }>({
name: "",
@ -293,64 +293,64 @@ const FilePickerComponent = (props: FilePickerProps) => {
return (
<ContainerDiv
isActive={isActive}
canDrop={canDrop}
isActive={isActive}
isUploaded={isUploaded}
ref={drop}
>
<div ref={bgRef} className="bg-image">
<div className="bg-image" ref={bgRef}>
<div className="button-wrapper" ref={fileContainerRef}>
<UploadIcon />
<Text type={TextType.P2} className="drag-drop-text">
<Text className="drag-drop-text" type={TextType.P2}>
Drag & Drop files to upload or
</Text>
<form>
<input
type="file"
accept=".jpeg,.png,.svg"
id="fileInput"
multiple={false}
ref={inputRef}
accept=".jpeg,.png,.svg"
value={""}
onChange={(el) => handleFileUpload(el.target.files)}
ref={inputRef}
type="file"
value={""}
/>
<Button
text="Browse"
category={Category.tertiary}
size={Size.medium}
onClick={(el) => ButtonClick(el)}
size={Size.medium}
text="Browse"
/>
</form>
</div>
<div className="file-description" ref={fileDescRef} id="fileDesc">
<div className="file-description" id="fileDesc" ref={fileDescRef}>
<div className="file-spec">
<Text type={TextType.H6}>{fileInfo.name}</Text>
<Text type={TextType.H6}>{fileInfo.size}KB</Text>
</div>
<div className="progress-container">
<div className="progress-inner" ref={progressRef}></div>
<div className="progress-inner" ref={progressRef} />
</div>
</div>
</div>
<div className="remove-button">
<Button
text="remove"
icon="delete"
size={Size.medium}
category={Category.tertiary}
icon="delete"
onClick={() => removeFile()}
size={Size.medium}
text="remove"
/>
</div>
</ContainerDiv>
);
};
}
const FilePicker = (props: FilePickerProps) => {
function FilePicker(props: FilePickerProps) {
return (
<DndProvider backend={HTML5Backend}>
<FilePickerComponent {...props} />
</DndProvider>
);
};
}
export default FilePicker;

View File

@ -52,24 +52,20 @@ const Overlay = styled.div`
transition: 0.5s ease;
`;
const GifPlayerComponent = (props: GifPlayerProps) => {
function GifPlayerComponent(props: GifPlayerProps) {
const [startGif, setStartGif] = useState(false);
return (
<React.Fragment>
{!startGif ? (
<ThumnailContainer onClick={() => setStartGif(!startGif)}>
<Overlay />
<img src={props.thumbnail} />
<PlayButton>
<Icon name="play" size={IconSize.XXXL} />
<Text type={TextType.P3}>Click to play</Text>
</PlayButton>
</ThumnailContainer>
) : (
<img src={props.gif} />
)}
</React.Fragment>
return !startGif ? (
<ThumnailContainer onClick={() => setStartGif(!startGif)}>
<Overlay />
<img src={props.thumbnail} />
<PlayButton>
<Icon name="play" size={IconSize.XXXL} />
<Text type={TextType.P3}>Click to play</Text>
</PlayButton>
</ThumnailContainer>
) : (
<img src={props.gif} />
);
};
}
export default GifPlayerComponent;

View File

@ -1,6 +1,10 @@
import React, { forwardRef, Ref } from "react";
import { ReactComponent as DeleteIcon } from "assets/icons/ads/delete.svg";
import { ReactComponent as BookIcon } from "assets/icons/ads/book.svg";
import { ReactComponent as BugIcon } from "assets/icons/ads/bug.svg";
import { ReactComponent as CancelIcon } from "assets/icons/ads/cancel.svg";
import { ReactComponent as CrossIcon } from "assets/icons/ads/cross.svg";
import { ReactComponent as OpenIcon } from "assets/icons/ads/open.svg";
import { ReactComponent as UserIcon } from "assets/icons/ads/user.svg";
import { ReactComponent as GeneralIcon } from "assets/icons/ads/general.svg";
import { ReactComponent as BillingIcon } from "assets/icons/ads/billing.svg";
@ -11,6 +15,7 @@ import { ReactComponent as SuccessIcon } from "assets/icons/ads/success.svg";
import { ReactComponent as SearchIcon } from "assets/icons/ads/search.svg";
import { ReactComponent as CloseIcon } from "assets/icons/ads/close.svg";
import { ReactComponent as WarningIcon } from "assets/icons/ads/warning.svg";
import { ReactComponent as WarningTriangleIcon } from "assets/icons/ads/warning-triangle.svg";
import { ReactComponent as DownArrow } from "assets/icons/ads/down_arrow.svg";
import { ReactComponent as ShareIcon } from "assets/icons/ads/share.svg";
import { ReactComponent as RocketIcon } from "assets/icons/ads/launch.svg";
@ -37,6 +42,7 @@ import { ReactComponent as RightArrowIcon } from "assets/icons/ads/right-arrow.s
import { ReactComponent as DatasourceIcon } from "assets/icons/ads/datasource.svg";
import { ReactComponent as PlayIcon } from "assets/icons/ads/play.svg";
import { ReactComponent as DesktopIcon } from "assets/icons/ads/desktop.svg";
import { ReactComponent as WandIcon } from "assets/icons/ads/wand.svg";
import { ReactComponent as MobileIcon } from "assets/icons/ads/mobile.svg";
import { ReactComponent as TabletIcon } from "assets/icons/ads/tablet.svg";
import { ReactComponent as FluidIcon } from "assets/icons/ads/fluid.svg";
@ -95,7 +101,11 @@ export const sizeHandler = (size?: IconSize) => {
export const IconCollection = [
"book",
"bug",
"cancel",
"cross",
"delete",
"open",
"user",
"general",
"billing",
@ -114,6 +124,7 @@ export const IconCollection = [
"view-all",
"view-less",
"warning",
"warning-triangle",
"downArrow",
"context-menu",
"duplicate",
@ -133,6 +144,7 @@ export const IconCollection = [
"datasource",
"play",
"desktop",
"wand",
"mobile",
"tablet",
"fluid",
@ -154,17 +166,29 @@ export const IconWrapper = styled.span<IconProps>`
svg {
width: ${(props) => sizeHandler(props.size)}px;
height: ${(props) => sizeHandler(props.size)}px;
${(props) =>
!props.keepColors
? `
path {
fill: ${(props) => props.fillColor || props.theme.colors.icon.normal};
fill: ${props.fillColor || props.theme.colors.icon.normal};
}
}
circle {
fill: ${props.fillColor || props.theme.colors.icon.normal};
}
`
: ""}
${(props) => (props.invisible ? `visibility: hidden;` : null)};
&:hover {
cursor: pointer;
${(props) =>
!props.keepColors
? `
path {
fill: ${(props) => props.theme.colors.icon.hover};
fill: ${props.theme.colors.icon.hover};
}
`
: ""}
}
&:active {
@ -182,6 +206,7 @@ export type IconProps = {
className?: string;
onClick?: (e: React.MouseEvent) => void;
fillColor?: string;
keepColors?: boolean;
};
const Icon = forwardRef(
@ -191,9 +216,21 @@ const Icon = forwardRef(
case "book":
returnIcon = <BookIcon />;
break;
case "bug":
returnIcon = <BugIcon />;
break;
case "cancel":
returnIcon = <CancelIcon />;
break;
case "cross":
returnIcon = <CrossIcon />;
break;
case "delete":
returnIcon = <DeleteIcon />;
break;
case "open":
returnIcon = <OpenIcon />;
break;
case "user":
returnIcon = <UserIcon />;
break;
@ -233,6 +270,9 @@ const Icon = forwardRef(
case "rocket":
returnIcon = <RocketIcon />;
break;
case "wand":
returnIcon = <WandIcon />;
break;
case "workspace":
returnIcon = <WorkspaceIcon />;
break;
@ -263,6 +303,9 @@ const Icon = forwardRef(
case "warning":
returnIcon = <WarningIcon />;
break;
case "warning-triangle":
returnIcon = <WarningTriangleIcon />;
break;
case "arrow-left":
returnIcon = <ArrowLeft />;
break;
@ -321,7 +364,7 @@ const Icon = forwardRef(
case "PARAGRAPH":
case "PARAGRAPH_TWO":
const ControlIcon = ControlIcons[props.name];
returnIcon = <ControlIcon width={24} height={24} />;
returnIcon = <ControlIcon height={24} width={24} />;
break;
default:

View File

@ -58,7 +58,7 @@ const IconBox = styled.div<{ selectedColor?: string }>`
: null};
`;
const IconSelector = (props: IconSelectorProps) => {
function IconSelector(props: IconSelectorProps) {
const iconRef = useRef<HTMLDivElement>(null);
const [selected, setSelected] = useState<AppIconName>(firstSelectedIcon());
const iconPaletteRef = React.createRef<HTMLDivElement>();
@ -87,8 +87,8 @@ const IconSelector = (props: IconSelectorProps) => {
return (
<IconPalette
fill={props.fill}
data-cy={props.cypressSelector}
fill={props.fill}
ref={iconPaletteRef}
>
{props.iconPalette &&
@ -96,19 +96,19 @@ const IconSelector = (props: IconSelectorProps) => {
return (
<IconBox
{...(selected === iconName ? { ref: iconRef } : {})}
key={index}
selectedColor={selected === iconName ? props.selectedColor : ""}
className={
selected === iconName
? "t--icon-selected"
: "t--icon-not-selected"
}
key={index}
onClick={() => {
if (iconName !== selected) {
setSelected(iconName);
props.onSelect && props.onSelect(iconName);
}
}}
selectedColor={selected === iconName ? props.selectedColor : ""}
>
<AppIcon name={iconName} size={Size.small} />
</IconBox>
@ -117,7 +117,7 @@ const IconSelector = (props: IconSelectorProps) => {
<ScrollIndicator containerRef={iconPaletteRef} mode="DARK" />
</IconPalette>
);
};
}
IconSelector.defaultProps = {
fill: false,

View File

@ -38,7 +38,7 @@ interface IconTabsComponentProps {
selectOption: (value: string) => void;
}
const IconTabsComponent = (props: IconTabsComponentProps) => {
function IconTabsComponent(props: IconTabsComponentProps) {
return (
<FlexWrapper>
{props.options.map((option: IconTabOption, index: number) => {
@ -47,17 +47,17 @@ const IconTabsComponent = (props: IconTabsComponentProps) => {
const isSelected = props.value === option.value;
return (
<ItemWrapper
key={index}
selected={isSelected}
onClick={() => props.selectOption(option.value)}
className={`t--icon-tab-${option.value}`}
key={index}
onClick={() => props.selectOption(option.value)}
selected={isSelected}
>
<ControlIcon width={24} height={24} />
<ControlIcon height={24} width={24} />
</ItemWrapper>
);
})}
</FlexWrapper>
);
};
}
export default IconTabsComponent;

View File

@ -24,18 +24,18 @@ const MenuOption = styled.div`
font-family: ${(props) => props.theme.fonts[3]};
`;
const Menu = (props: MenuProps) => {
function Menu(props: MenuProps) {
return (
<Popover
minimal
position={props.position || Position.BOTTOM}
onOpening={props.onOpening}
onClosing={props.onClosing}
className={props.className}
portalClassName={props.className}
data-cy={props.cypressSelector}
disabled={props.disabled}
minimal
modifiers={props.modifiers}
onClosing={props.onClosing}
onOpening={props.onOpening}
portalClassName={props.className}
position={props.position || Position.BOTTOM}
>
{props.target}
<MenuWrapper>
@ -46,6 +46,6 @@ const Menu = (props: MenuProps) => {
</MenuWrapper>
</Popover>
);
};
}
export default Menu;

View File

@ -90,7 +90,7 @@ const IconContainer = styled.span`
const MenuItem = forwardRef(
(props: MenuItemProps, ref: Ref<HTMLAnchorElement>) => {
return props.ellipsize && props.text.length > props.ellipsize ? (
<TooltipComponent position={Position.BOTTOM} content={props.text}>
<TooltipComponent content={props.text} position={Position.BOTTOM}>
<MenuItemContent ref={ref} {...props} />
</TooltipComponent>
) : (
@ -102,13 +102,13 @@ const MenuItemContent = forwardRef(
(props: MenuItemProps, ref: Ref<HTMLAnchorElement>) => {
return (
<ItemRow
data-cy={props.cypressSelector}
disabled={props.disabled}
href={props.href}
onClick={props.onSelect}
disabled={props.disabled}
data-cy={props.cypressSelector}
type={props.type}
ref={ref}
selected={props.selected}
type={props.type}
>
<IconContainer className={props.className}>
{props.icon ? <Icon name={props.icon} size={IconSize.LARGE} /> : null}

View File

@ -55,10 +55,10 @@ export default function MultiSwitch<T>(props: MultiSwitchProps<T>) {
{props.tabs.map((tab) => (
<Tab
key={tab.key}
selected={props.selected.value === tab.key}
onClick={() => props.onSelect(tab.title)}
selected={props.selected.value === tab.key}
>
<Text type={TextType.P3} case={Case.UPPERCASE}>
<Text case={Case.UPPERCASE} type={TextType.P3}>
{tab.title}
</Text>
</Tab>

View File

@ -166,7 +166,7 @@ type DropdownProps = CommonComponentProps & {
selectAllQuantifier?: string;
};
const MultiSelectDropdown = (props: DropdownProps) => {
function MultiSelectDropdown(props: DropdownProps) {
const [isOpen, setIsOpen] = useState<boolean>(false);
const [containerWidth, setContainerWidth] = useState<string>("0px");
@ -229,23 +229,23 @@ const MultiSelectDropdown = (props: DropdownProps) => {
return (
<DropdownContainer
tabIndex={0}
data-cy={props.cypressSelector}
ref={measuredRef}
tabIndex={0}
width={props.width}
>
<Popover
minimal
position={Position.TOP_LEFT}
isOpen={isOpen && !props.disabled}
onInteraction={(state) => setIsOpen(state)}
boundary="scrollParent"
isOpen={isOpen && !props.disabled}
minimal
onInteraction={(state) => setIsOpen(state)}
position={Position.TOP_LEFT}
>
<Selected
isOpen={isOpen}
disabled={props.disabled}
onClick={() => setIsOpen(!isOpen)}
className={props.className}
disabled={props.disabled}
isOpen={isOpen}
onClick={() => setIsOpen(!isOpen)}
>
<Text type={TextType.P1}>
{props.selected.length
@ -260,12 +260,12 @@ const MultiSelectDropdown = (props: DropdownProps) => {
{props.options.map((option: DropdownOption, index: number) => {
return (
<MultiOptionWrapper
className="t--multi-dropdown-option"
key={index}
selected={isItemSelected(option.value)}
onClick={() => {
optionClickHandler(option.value as string);
}}
className="t--multi-dropdown-option"
selected={isItemSelected(option.value)}
>
<SquareBox className={Classes.MULTI_SELECT_BOX} />
{option.icon ? (
@ -288,6 +288,6 @@ const MultiSelectDropdown = (props: DropdownProps) => {
</Popover>
</DropdownContainer>
);
};
}
export default MultiSelectDropdown;

View File

@ -129,29 +129,29 @@ export default function RadioComponent(props: RadioProps) {
return (
<RadioGroup
data-cy={props.cypressSelector}
rows={props.rows}
columns={props.columns}
onChange={(e: any) => onChangeHandler(e.target.value)}
className={props.className}
columns={props.columns}
data-cy={props.cypressSelector}
onChange={(e: any) => onChangeHandler(e.target.value)}
rows={props.rows}
>
{props.options.map((option: OptionProps, index: number) => (
<Radio
key={index}
columns={props.columns}
rows={props.rows}
disabled={props.disabled || option.disabled}
key={index}
rows={props.rows}
>
{option.label}
<input
checked={selected === option.value}
disabled={props.disabled || option.disabled}
name="radio"
onChange={(e) => option.onSelect && option.onSelect(e.target.value)}
type="radio"
value={option.value}
disabled={props.disabled || option.disabled}
onChange={(e) => option.onSelect && option.onSelect(e.target.value)}
checked={selected === option.value}
name="radio"
/>
<span className="checkbox"></span>
<span className="checkbox" />
</Radio>
))}
</RadioGroup>

View File

@ -100,14 +100,13 @@ export default function Switch(props: SwitchProps) {
return (
<StyledSwitch
className={props.className}
data-cy={props.cypressSelector}
firstRender={firstRender}
isLoading={props.isLoading}
value={value}
className={props.className}
firstRender={firstRender}
>
<input
type="checkbox"
checked={value}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
if (!firstRender) {
@ -115,8 +114,9 @@ export default function Switch(props: SwitchProps) {
}
onChangeHandler(e.target.checked);
}}
type="checkbox"
/>
<span className="slider"></span>
<span className="slider" />
<Light value={value}>
<Text type={TextType.H6}>Light</Text>
</Light>

View File

@ -47,7 +47,7 @@ interface Props {
right?: string;
mode?: "DARK" | "LIGHT";
}
const ScrollIndicator = ({ containerRef, top, bottom, right }: Props) => {
function ScrollIndicator({ containerRef, top, bottom, right }: Props) {
const [{ thumbPosition }, setThumbPosition] = useSpring<{
thumbPosition: number;
config: {
@ -105,10 +105,10 @@ const ScrollIndicator = ({ containerRef, top, bottom, right }: Props) => {
return (
<ScrollTrack
isVisible={isScrollVisible}
top={top}
bottom={bottom}
isVisible={isScrollVisible}
right={right}
top={top}
>
<ScrollThumb
ref={thumbRef}
@ -121,6 +121,6 @@ const ScrollIndicator = ({ containerRef, top, bottom, right }: Props) => {
/>
</ScrollTrack>
);
};
}
export default ScrollIndicator;

View File

@ -118,30 +118,30 @@ const SearchInput = forwardRef(
return (
<InputWrapper
data-cy={props.cypressSelector}
value={searchValue}
isFocused={isFocused}
variant={props.variant}
fill={props.fill}
isFocused={isFocused}
value={searchValue}
variant={props.variant}
>
<SearchIcon value={searchValue} isFocused={isFocused}>
<SearchIcon isFocused={isFocused} value={searchValue}>
<Icon name="search" size={IconSize.SMALL} />
</SearchIcon>
<StyledInput
type="text"
ref={ref}
isFocused={isFocused}
ref={ref}
type="text"
{...props}
placeholder={props.placeholder ? props.placeholder : ""}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
onChange={memoizedChangeHandler}
onFocus={() => setIsFocused(true)}
placeholder={props.placeholder ? props.placeholder : ""}
/>
{searchValue && props.variant === SearchVariant.BACKGROUND ? (
<CloseIcon>
<Icon
name="close"
size={IconSize.MEDIUM}
onClick={() => setSearchValue("")}
size={IconSize.MEDIUM}
/>
</CloseIcon>
) : null}

View File

@ -48,11 +48,11 @@ Spinner.defaultProp = {
export default function Spinner(props: SpinnerProp) {
return (
<SvgContainer
viewBox="0 0 50 50"
className={Classes.SPINNER}
size={props.size}
viewBox="0 0 50 50"
>
<SvgCircle cx="25" cy="25" r="20" fill="none"></SvgCircle>
<SvgCircle cx="25" cy="25" fill="none" r="20" />
</SvgContainer>
);
}

View File

@ -63,7 +63,7 @@ interface StepComponentProps {
onChange: (value: number) => void;
}
export const StepComponent = (props: StepComponentProps) => {
export function StepComponent(props: StepComponentProps) {
function decrease() {
if (props.value < props.min) {
return;
@ -80,11 +80,11 @@ export const StepComponent = (props: StepComponentProps) => {
}
return (
<StepWrapper>
<StyledDecreaseIcon height={2} width={12} onClick={decrease} />
<StyledDecreaseIcon height={2} onClick={decrease} width={12} />
<InputWrapper>{props.displayFormat(props.value)}</InputWrapper>
<StyledIncreaseIcon height={12} width={12} onClick={increase} />
<StyledIncreaseIcon height={12} onClick={increase} width={12} />
</StepWrapper>
);
};
}
export default StepComponent;

View File

@ -138,8 +138,8 @@ function Table(props: TableProps) {
return (
<td
{...cell.getCellProps()}
key={index}
data-colindex={index}
key={index}
>
{cell.render("Cell")}
</td>

View File

@ -1,14 +1,14 @@
import React, { Fragment, useState } from "react";
import { CommonComponentProps, Classes } from "./common";
import Text, { TextType } from "./Text";
import styled from "styled-components";
import { Position } from "@blueprintjs/core/lib/esm/common/position";
import {
Popover,
PopoverInteractionKind,
} from "@blueprintjs/core/lib/esm/components/popover/popover";
import { Position } from "@blueprintjs/core/lib/esm/common/position";
import React, { useState } from "react";
import styled from "styled-components";
import { Classes, CommonComponentProps } from "./common";
import Icon, { IconSize } from "./Icon";
import Spinner from "./Spinner";
import Text, { TextType } from "./Text";
type DropdownOption = {
name: string;
@ -79,7 +79,7 @@ const Content = styled.div<{ isLoading?: boolean }>`
}
`;
const TableDropdown = (props: DropdownProps) => {
function TableDropdown(props: DropdownProps) {
const [selectedIndex, setSelectedIndex] = useState(props.selectedIndex);
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [selectedOption, setSelectedOption] = useState(
@ -93,41 +93,37 @@ const TableDropdown = (props: DropdownProps) => {
setIsDropdownOpen(false);
};
return (
<Fragment>
{props.isLoading ? (
<Spinner size={IconSize.LARGE} />
) : (
<Popover
data-cy={props.cypressSelector}
usePortal={false}
position={props.position || Position.BOTTOM_LEFT}
isOpen={isDropdownOpen}
onInteraction={(state) => setIsDropdownOpen(state)}
interactionKind={PopoverInteractionKind.CLICK}
>
<Content isLoading={props.isLoading}>
<SelectedItem className="selected-item">
<Text type={TextType.P1}>{selectedOption.name}</Text>
<Icon name="downArrow" size={IconSize.XXS} />
</SelectedItem>
</Content>
<OptionsWrapper>
{props.options.map((el: DropdownOption, index: number) => (
<DropdownOption
key={index}
isSelected={selectedIndex === index}
onClick={() => optionSelector(index)}
>
<Text type={TextType.H5}>{el.name}</Text>
<Text type={TextType.P3}>{el.desc}</Text>
</DropdownOption>
))}
</OptionsWrapper>
</Popover>
)}
</Fragment>
return props.isLoading ? (
<Spinner size={IconSize.LARGE} />
) : (
<Popover
data-cy={props.cypressSelector}
interactionKind={PopoverInteractionKind.CLICK}
isOpen={isDropdownOpen}
onInteraction={(state) => setIsDropdownOpen(state)}
position={props.position || Position.BOTTOM_LEFT}
usePortal={false}
>
<Content isLoading={props.isLoading}>
<SelectedItem className="selected-item">
<Text type={TextType.P1}>{selectedOption.name}</Text>
<Icon name="downArrow" size={IconSize.XXS} />
</SelectedItem>
</Content>
<OptionsWrapper>
{props.options.map((el: DropdownOption, index: number) => (
<DropdownOption
isSelected={selectedIndex === index}
key={index}
onClick={() => optionSelector(index)}
>
<Text type={TextType.H5}>{el.name}</Text>
<Text type={TextType.P3}>{el.desc}</Text>
</DropdownOption>
))}
</OptionsWrapper>
</Popover>
);
};
}
export default TableDropdown;

View File

@ -17,9 +17,6 @@ const TabsWrapper = styled.div<{ shouldOverflow?: boolean }>`
user-select: none;
border-radius: 0px;
height: 100%;
.${Classes.ICON} {
margin-right: ${(props) => props.theme.spaces[3]}px;
}
.react-tabs {
height: 100%;
}
@ -104,6 +101,9 @@ const TabsWrapper = styled.div<{ shouldOverflow?: boolean }>`
const TabTitleWrapper = styled.div`
display: flex;
align-items: center;
.${Classes.ICON} {
margin-right: ${(props) => props.theme.spaces[3]}px;
}
`;
const TabTitle = styled.span`
@ -130,17 +130,17 @@ type TabbedViewComponentType = CommonComponentProps & {
overflow?: boolean;
};
export const TabComponent = (props: TabbedViewComponentType) => {
export function TabComponent(props: TabbedViewComponentType) {
return (
<TabsWrapper
shouldOverflow={props.overflow}
data-cy={props.cypressSelector}
shouldOverflow={props.overflow}
>
<Tabs
selectedIndex={props.selectedIndex}
onSelect={(index: number) => {
props.onSelect && props.onSelect(index);
}}
selectedIndex={props.selectedIndex}
>
<TabList>
{props.tabs.map((tab) => (
@ -163,4 +163,4 @@ export const TabComponent = (props: TabbedViewComponentType) => {
</Tabs>
</TabsWrapper>
);
};
}

View File

@ -62,7 +62,7 @@ type TagInputProps = {
* On addition or removal of tags, passes the comman separated string to input.onChange prop
* @param props : TagInputProps
*/
const TagInputComponent = (props: TagInputProps) => {
function TagInputComponent(props: TagInputProps) {
const _values =
props.input.value && props.input.value.length > 0
? props.input.value.split(",")
@ -145,25 +145,25 @@ const TagInputComponent = (props: TagInputProps) => {
return (
<TagInputWrapper intent={props.intent}>
<TagInput
addOnPaste
inputProps={{
type: props.type,
value: currentValue,
onBlur: handleInputBlur,
}}
onInputChange={handleInputChange}
placeholder={props.placeholder}
values={_values || [""]}
separator={props.separator || ","}
addOnPaste
large={false}
onChange={onTagsChange}
onInputChange={handleInputChange}
onKeyDown={onKeyDown}
placeholder={props.placeholder}
separator={props.separator || ","}
tagProps={{
round: true,
}}
large={false}
values={_values || [""]}
/>
</TagInputWrapper>
);
};
}
export default TagInputComponent;

View File

@ -88,11 +88,11 @@ const StyledInput = styled((props) => {
return props.asyncControl ? (
<AsyncControllableInput
{...inputProps}
inputRef={inputRef}
dataType={dataType}
inputRef={inputRef}
/>
) : (
<input {...inputProps} />
<input ref={inputRef} {...inputProps} />
);
})<TextInputProps & { inputStyle: boxReturnType; isValid: boolean }>`
width: ${(props) => (props.fill ? "100%" : "320px")};
@ -200,17 +200,17 @@ const TextInput = forwardRef(
return (
<InputWrapper>
<StyledInput
type={props.dataType || "text"}
ref={ref}
defaultValue={props.defaultValue}
inputStyle={inputStyle}
isValid={validation.isValid}
defaultValue={props.defaultValue}
ref={ref}
type={props.dataType || "text"}
{...props}
placeholder={props.placeholder}
onChange={memoizedChangeHandler}
readOnly={props.readOnly}
data-cy={props.cypressSelector}
inputRef={ref}
onChange={memoizedChangeHandler}
placeholder={props.placeholder}
readOnly={props.readOnly}
/>
{ErrorMessage}
</InputWrapper>

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