Merge branch 'release/v1.11.0'

This commit is contained in:
Akshay Sasidharan 2022-04-28 17:14:00 +05:30
commit de01d9d057
241 changed files with 14042 additions and 3461 deletions

View file

@ -1,6 +1,6 @@
# Create .env from this example file and replace values for the environment.
# The application expects a separate .env.test for test environment configuration
# Get detailed information about each variable here: https://docs.tooljet.com/docs/deployment/env-vars
# Get detailed information about each variable here: https://docs.tooljet.com/docs/setup/env-vars
TOOLJET_HOST=http://localhost:8082
LOCKBOX_MASTER_KEY=0000000000000000000000000000000000000000000000000000000000000000

View file

@ -0,0 +1,22 @@
---
name: 🧪 Test Specification
about: Issue template for adding test
labels: "cypress,test"
---
### Specify the kind of test
<!--
Provide the kind of test
-->
(Cypress integration-test)
### Describe the test
<!--
Provide a clear description of the test
-->
(Write your answer here.)

49
.github/workflows/packer-build.yml vendored Normal file
View file

@ -0,0 +1,49 @@
name: AWS AMI build using Packer config
on:
release:
types: [published]
jobs:
packer:
runs-on: ubuntu-latest
name: packer
steps:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-1
# Initialize Packer templates
- name: Initialize Packer Template
uses: hashicorp/packer-github-actions@master
with:
command: init
target: .
working_directory: deploy/ec2
# validate templates
- name: Validate Template
uses: hashicorp/packer-github-actions@master
with:
command: validate
arguments: -syntax-only
target: .
working_directory: deploy/ec2
# build artifact
- name: Build Artifact
uses: hashicorp/packer-github-actions@master
with:
command: build
arguments: "-color=false -on-error=abort -var ami_name=${GITHUB_REF##*/}"
target: .
working_directory: deploy/ec2
env:
PACKER_LOG: 1

View file

@ -1 +1 @@
1.10.3
1.11.0

View file

@ -52,7 +52,7 @@ ToolJet is an **open-source low-code framework** to build and deploy internal to
- *Desktop & mobile*: ;layout widths can be customised to support different screens.
- *Self-host:* (supports Docker, Kubernetes, Heroku, AWS EC2, Google Cloud Run, and more).
- *Collaborate:* add comments anywhere on the canvas and tag your team members.
- *Extend with plugins:*: use our [commandline tool](https://www.npmjs.com/package/tooljet) to easily boostrap new connectors.
- *Extend with plugins:*: use our [commandline tool](https://www.npmjs.com/package/@tooljet/cli) to easily boostrap new connectors.
- *Version control:* every application have different versions with proper release cycle.
- *Run JS code:* ability custom JavaScript snippets
- *Granular access control* on organization-level and app-level.
@ -66,7 +66,7 @@ ToolJet is an **open-source low-code framework** to build and deploy internal to
<hr>
## Quickstart
The easiest way to get started with ToolJet is by creating a [ToolJet Cloud](https://tooljet.com) account. ToolJet Cloud offers a hosted solution of ToolJet. If you want to self-host ToolJet, kindly proceed to [deployment documentation](https://docs.tooljet.com/docs/deployment/architecture).
The easiest way to get started with ToolJet is by creating a [ToolJet Cloud](https://tooljet.com) account. ToolJet Cloud offers a hosted solution of ToolJet. If you want to self-host ToolJet, kindly proceed to [deployment documentation](https://docs.tooljet.com/docs/setup/architecture).
You can deploy ToolJet on Heroku for free using the one-click-deployment button below.
<p align="center">
@ -92,13 +92,13 @@ You can use ToolJet cloud for a fully managed solution. If you want to self-host
| Provider | Documentation |
| ------------- | ------------- |
| AWS EC2 | [Link](https://docs.tooljet.com/docs/deployment/ec2) |
| AWS EKS (Kubernetes) | [Link](https://docs.tooljet.com/docs/deployment/kubernetes) |
| GCP GKE (Kubernetes) | [Link](https://docs.tooljet.com/docs/deployment/kubernetes-gke) |
| Azure AKS (Kubernetes) | [Link](https://docs.tooljet.com/docs/deployment/kubernetes-aks) |
| Heroku | [Link](https://docs.tooljet.com/docs/deployment/heroku) |
| Docker | [Link](https://docs.tooljet.com/docs/deployment/docker) |
| Google Cloud Run | [Link](https://docs.tooljet.com/docs/deployment/google-cloud-run) |
| AWS EC2 | [Link](https://docs.tooljet.com/docs/setup/ec2) |
| AWS EKS (Kubernetes) | [Link](https://docs.tooljet.com/docs/setup/kubernetes) |
| GCP GKE (Kubernetes) | [Link](https://docs.tooljet.com/docs/setup/kubernetes-gke) |
| Azure AKS (Kubernetes) | [Link](https://docs.tooljet.com/docs/setup/kubernetes-aks) |
| Heroku | [Link](https://docs.tooljet.com/docs/setup/heroku) |
| Docker | [Link](https://docs.tooljet.com/docs/setup/docker) |
| Google Cloud Run | [Link](https://docs.tooljet.com/docs/setup/google-cloud-run) |
## Community support
For general help using ToolJet, please refer to the official [documentation](https://docs.tooljet.com/docs/intro/). For additional help, you can use one of these channels to ask a question:

View file

@ -1,33 +1,13 @@
{
"baseUrl": "http://localhost:8082",
"env": {
"apiUrl": "http://localhost:3000"
},
"viewportWidth": 1536,
"viewportHeight": 960,
"testFiles": [
"auth.spec.js",
"dashboard/empty-state-dashboard.spec.js",
"dashobard/dashboard.spec.js",
"dashboard/apps-page-operations.spec.js",
"editor/editor-navigation-bar.spec.js",
"editor/editor-datasource-postgres.spec.js",
"editor/widgets/editor-widget-checkbox.spec.js",
"editor/widgets/editor-widget-table.spec.js",
"editor/widgets/editor-widget-button.spec.js",
"editor/widgets/editor-widget-chart.spec.js",
"editor/widgets/editor-widget-modal.spec.js",
"editor/widgets/editor-widget-text-input.spec.js",
"editor/widgets/editor-widget-date-picker.spec.js",
"editor/widgets/editor-widget-toggle-switch.spec.js",
"editor/widgets/editor-widget-text-area.spec.js",
"editor/widgets/editor-widget-text.spec.js",
"editor/widgets/editor-widget-text-editor.spec.js",
"editor/widgets/editor-widget-image.spec.js",
"editor/widgets/editor-widget-container.spec.js",
"editor/widgets/editor-widget-dropdown.spec.js",
"editor/widgets/editor-widget-multiselect.spec.js",
"editor/widgets/editor-widget-map.spec.js",
"editor/widgets/editor-widget-qr-scanner.spec.js"
]
"baseUrl": "http://localhost:8082",
"execTimeout": 1800000,
"defaultCommandTimeout": 30000,
"requestTimeout": 10000,
"pageLoadTimeout": 20000,
"responseTimeout": 10000,
"viewportWidth": 1200,
"viewportHeight": 960,
"testFiles": "**/*.spec.js",
"chromeWebSecurity": true
}

View file

@ -0,0 +1,3 @@
export const commonSelectors={
toastMessage: ".go318386747"
}

View file

@ -0,0 +1,16 @@
export const loginSelectors={
logo: "[data-cy=login-page-logo]",
cardTitle: "[data-cy=login-page-header]",
emailLabel: "[data-cy=email-label]",
emailField: "[data-cy=email-text-field]",
passwordLabel: "[data-cy=password-label]",
forgotPassword: "[data-cy=forgot-password-link]",
passwordField: "[data-cy=password-text-field]",
checkBox: "[data-cy=checkbox-input]",
showPassword: "[data-cy=show-password-label]",
signInButton: "[data-cy=login-button]",
signUpText: "[data-cy=sign-up-message]",
signUpLink: "[data-cy=sign-up-link]",
homePage: "[data-cy=home-page-logo]"
}

View file

@ -0,0 +1,3 @@
export const path={
loginPath:"/login"
}

View file

@ -0,0 +1,11 @@
export const loginTexts={
cardTitle:"Login to your account",
emailLabel:"Email address",
passwordLabel:"Password",
forgotPassword:"Forgot password",
showPassword:"show password",
signIn:"Sign in",
signUpText:"Don't have account yet?",
signUpLink:"Sign up",
toastMessage:"Invalid email or password",
}

View file

@ -0,0 +1,5 @@
{
"email": "dev@tooljet.io",
"password": "password"
}

13
cypress/fixtures/fake.js Normal file
View file

@ -0,0 +1,13 @@
import faker from 'faker'
export let fake ={};
function email(){
return (`${faker.name.findName()}@example.com`)
}
function password(){
return(faker.internet.password())
}
Object.defineProperty(fake, "email", {get:email});
Object.defineProperty(fake, "password", {get:password});

View file

@ -1,4 +0,0 @@
{
"email": "dev@tooljet.io",
"password": "password"
}

View file

@ -1,57 +0,0 @@
describe('User login', () => {
it('should take user to login page', () => {
cy.visit('/login');
cy.get('.card-title').should('have.text', 'Login to your account');
});
it('should redirect unauthenticated user to login page', () => {
cy.visit('/');
cy.location('pathname').should('equal', '/login');
});
it('should display invalid email or password error', () => {
cy.login('fake_email', 'abcdefg');
cy.checkToastMessage('toast-login-auth-error', 'Invalid email or password');
});
it('should take user to the forgot password page', () => {
cy.visit('/forgot-password');
cy.get('.card-title').should('have.text', 'Forgot Password');
});
it('should take user to the signup page', () => {
cy.visit('/signup');
cy.get('.card-title').should('have.text', 'Create a ToolJet account');
});
it('should sign in the user', () => {
cy.visit('/login');
cy.login('dev@tooljet.io', 'password');
cy.location('pathname').should('equal', '/');
cy.get('.page-title').should('have.text', 'All applications');
});
it('should display error if email is not found for "Forgot password"', () => {
cy.visit('/forgot-password');
cy.get('[data-testid="emailField"]').type('abc@def.com');
cy.get('[data-testid="submitButton"').click();
cy.checkToastMessage(
'toast-forgot-password-email-error',
'Email address is not associated with a ToolJet cloud account.'
);
});
it('should send reset password confirmation code to email', () => {
cy.intercept('POST', '/password/forgot').as('forgotPasswordConfirmationCode');
cy.visit('/forgot-password');
cy.get('[data-testid="emailField"]').type('dev@tooljet.io');
cy.get('[data-testid="submitButton"').click();
cy.wait('@forgotPasswordConfirmationCode').its('response.statusCode').should('eq', 200);
cy.checkToastMessage(
'toast-forgot-password-confirmation-code',
"We've sent the confirmation code to your email address"
);
});
});

View file

@ -0,0 +1,49 @@
import { loginSelectors} from "Selectors/login";
import {commonSelectors} from "Selectors/common";
import {loginTexts} from "Texts/login";
import { fake } from "Fixtures/fake";
import * as login from "Support/utils/login";
describe("Login functionality",()=>{
let user;
const invalidEmail = fake.email;
const invalidPassword = fake.password;
before(()=>{
cy.fixture("credentials/login.json").then(login=>{
user = login;
});
cy.visit("/");
});
it("Should verify elements on the login page", ()=>{
login.loginPageElements();
});
it("Should not be able to login with invalid credentials", ()=>{
cy.get(loginSelectors.signInButton).click();
cy.verifyToastMessage(commonSelectors.toastMessage, loginTexts.toastMessage);
cy.clearAndType(loginSelectors.emailField, invalidEmail);
cy.get(loginSelectors.signInButton).click();
cy.verifyToastMessage(commonSelectors.toastMessage, loginTexts.toastMessage);
cy.get(loginSelectors.emailField).clear();
cy.clearAndType(loginSelectors.passwordField, invalidPassword);
cy.get(loginSelectors.signInButton).click();
cy.verifyToastMessage(commonSelectors.toastMessage, loginTexts.toastMessage);
cy.clearAndType(loginSelectors.emailField, user.email);
cy.get(loginSelectors.passwordField).clear();
cy.get(loginSelectors.signInButton).click();
cy.verifyToastMessage(commonSelectors.toastMessage, loginTexts.toastMessage);
cy.get(loginSelectors.emailField).clear();
cy.clearAndType(loginSelectors.passwordField, user.password);
cy.get(loginSelectors.signInButton).click();
cy.verifyToastMessage(commonSelectors.toastMessage, loginTexts.toastMessage);
});
it("Should be able to login with valid credentials", ()=>{
cy.login(user.email,user.password);
});
});

View file

@ -1,68 +0,0 @@
describe('Dashboard operations on Apps', () => {
const currentDate = new Date();
const folderName = 'folder ' + currentDate.toJSON();
beforeEach(() => {
//read data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
});
it('should open app in app builder using Edit button', () => {
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should show Tooltip "App does not have a deployed version" on Launch button', () => {
cy.get('tbody a:nth-child(2)').find('span[class="badge bg-light-grey mx-2"]')
.trigger('mouseover')
.should('have.attr', 'aria-describedby', 'button-tooltip')
cy.get('div[id="button-tooltip"]').should('have.text', 'App does not have a deployed version')
});
it('should launch app and show Tooltip -"Open in app viewer", when App is deployed with a single version', () => {
//Create and save App with version 1.0
cy.get('.badge').contains('Edit').click();
cy.deployAppWithSingleVersion();
//Go back to dashboard
cy.go('back');
//Check Tooltip text
cy.get('tbody a:nth-child(2)').find('span[class="badge bg-light-grey mx-2"]')
.trigger('mouseover')
.should('have.attr', 'aria-describedby', 'button-tooltip')
cy.get('div[id="button-tooltip"]').should('have.text', 'Open in app viewer');
//Click to launch app
cy.get('tbody a:nth-child(2)').find('span[class="badge bg-light-grey mx-2"]').click();
});
it('should be able to add app to a folder', () => {
//Pre-requisite: Create folder
cy.get('a[class="mx-3"]').contains('+ Folder').click();
cy.get('input[placeholder="folder name"]').should('have.attr', 'placeholder', 'folder name').type(folderName);
cy.get('.btn').contains('Save').click();
//Steps to select the folder name and add app to folder.
cy.get('span[role="button"]').click();
cy.get('span[role="button"]').contains('Add to folder ').click();
cy.get('input[placeholder="Select folder"]').type(folderName);
cy.get('[data-index="0"] > .select-search__option').click();
});
it('should be able to delete app', () => {
cy.get('td img.svg-icon').eq(0).click();
cy.get('[role="button"]').contains('Delete app').click();
cy.get('.modal-body').should(
'have.text',
'The app and the associated data will be permanently deleted, do you want to continue?'
);
cy.get('.btn').contains('Yes').click();
cy.get('.Toastify__toast-body').should('have.text', 'App deleted successfully.')
});
});

View file

@ -1,50 +0,0 @@
describe('Dashboard', () => {
beforeEach(() => {
//read data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
});
it('should show site header with nav items', () => {
cy.get('.navbar').find('.navbar-nav').should('be.visible');
});
it('should navigate to users page using users tab', () => {
cy.get('.navbar').find('.navbar-nav').find('li').not('.active').click();
cy.location('pathname').should('equal', '/users');
cy.get('.page-title').should('have.text', 'Users & Permissions');
cy.get('[data-testid="usersTable"]').should('be.visible');
});
it('should navigate to apps page using apps tab', () => {
cy.get('.navbar').find('.navbar-nav').find('li.active').click();
cy.location('pathname').should('equal', '/');
cy.get('.page-title').should('have.text', 'All applications');
cy.get('[data-testid="appsTable"]').should('be.visible');
});
it('should show user avatar and logout the user when user clicks logout', () => {
cy.get('[data-testid="userAvatarHeader"]').should('be.visible');
// TODO - Add functionality to detect when user hovers over the avatar,
// Issues with hover functionality and hide/show of dom elements
});
it('should show list of application folders', () => {
cy.get('[data-testid="applicationFoldersList"]').should('be.visible');
});
it('should show correct number of applications in the count bubble of "All Applications" list', () => {
cy.get('[data-testid="allApplicationsCount"]').then(($countBubble) => {
cy.get('[data-testid="appsTable"]')
.wait(500)
.find('tr')
.then((row) => {
expect(Number($countBubble.text())).to.equal(row.length);
});
});
});
});

View file

@ -1,35 +0,0 @@
describe('Empty state of dashboard', () => {
beforeEach(() => {
//read data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
});
it('should show empty screen when there are no apps', () => {
cy.wait(1000);
cy.get('body').then(($title) => {
//if user has not created any app yet
if ($title.text().includes("You haven't created any apps yet.")) {
//image for empty state should be visible
cy.get('.empty-img').should('be.visible');
//empty title should be visible
cy.log('Testing empty state dashboard view.');
cy.get('.empty-title').should('be.visible').and('have.text', "You haven't created any apps yet.");
//Read Documentation button should be present and working
cy.get('a.btn')
.eq(1)
.should('have.attr', 'href', 'https://docs.tooljet.io')
.and('have.text', 'Read documentation');
//test Create your first app button should be visible and working
cy.get('a.btn').eq(0).should('be.visible').and('have.text', 'Create your first app').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
} else {
cy.log('User has already created few apps hence this test will be skipped.');
}
});
});
});

View file

@ -1,42 +0,0 @@
describe('Editor- Add "PostgreSQL" datasource', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it.only('should add First data source successfully', () => {
//test database icon
cy.get('.left-sidebar')
.find('.svg-icon[src="/assets/images/icons/editor/left-sidebar/database.svg"]')
.should('be.visible')
.click()
cy.get('.table-responsive')
.find('.p-2')
.should('have.text', "You haven't added any datasources yet. ")
cy.get('div[class="table-responsive"] button[class="btn btn-sm btn-outline-azure mt-3"]')
.should('have.text', 'Add datasource')
.click();
//create database
cy.addPostgresDataSource();
//verify if you can see postgres database in the list now.
cy.get('.left-sidebar')
.find('.svg-icon[src="/assets/images/icons/editor/left-sidebar/database.svg"]')
.should('be.visible')
.click()
cy.get('.table-responsive')
.find('tr td')
.contains('PostgreSQL')
});
});

View file

@ -1,117 +0,0 @@
describe('Editor - Navigation Bar', () => {
beforeEach(() => {
//read data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should show site header with nav items', () => {
cy.get('.navbar').find('.navbar-nav').should('be.visible');
});
it('should show tooljet brand image and clicking on it should take user to dashboard', () => {
cy.get('.navbar')
.find('.navbar-brand-image')
.should('be.visible')
.click()
.get('title')
.should('have.text', 'ToolJet - Dashboard');
});
it('should hide query editor', () => {
//check query pane is visible
cy.get('.query-pane').should('be.visible');
//click on Hide query editor button
cy.get('.editor-buttons')
.find('[data-tip="Hide query editor"] img')
.click()
.get('[data-tip="Show query editor"] img');
//check the query editor pane should not be visible
cy.get('.query-pane').find('.row.main-row').should('not.be.visible');
});
it('should resize canvas', () => {
//default size should be 100%
cy.get('.sidebar-zoom').should('have.text', '100 %');
cy.get('.sidebar-zoom').click();
//check minimize button
var scale;
var styleString = 'transform: scale(1);';
for (var i = 100; i >= 60; i = i - 10) {
cy.get('.sidebar-zoom').click();
cy.get('div[class="card popover zoom-popover show"]')
.find('tbody tr')
.contains(i + '%')
.click();
scale = i / 100;
styleString = 'transform: scale(' + scale + ');';
cy.get('.canvas-container.align-items-center').should('have.attr', 'style', styleString);
}
});
it('should switch from desktop layout to mobile view ', () => {
//check canvas default(desktop view) dimensions
cy.get('.real-canvas').should('have.css', 'width').and('eq', '1292px');
cy.get('.real-canvas').should('have.css', 'height').and('eq', '2400px');
//check default layout(Desktop view) button. it should be disabled
cy.get('.layout-buttons').find('button:nth-child(1)').should('be.disabled');
//mobile button should be enabled.
cy.get('.layout-buttons').find('button:nth-child(2)').should('be.enabled').click();
//clicking on layout button should change view to mobile canvas. check canvas dimensions
cy.get('.real-canvas').should('have.css', 'width').and('eq', '450px');
cy.get('.real-canvas').should('have.css', 'height').and('eq', '2400px');
});
it('should switch to dark theme', () => {
cy.get('.main-wrapper');
cy.get('div:nth-child(3) > svg:nth-child(1)').should('have.attr', 'color', '#808080').click();
cy.get('div:nth-child(3) > svg:nth-child(1)').should('have.attr', 'color', '#fff').click();
});
it('should be able to share app', () => {
//check share button
cy.get('.navbar')
.find('.navbar-nav')
.find('.nav-item')
.find('button[class="btn btn-sm"]')
.should('have.text', 'Share')
.and('be.visible')
.click();
//check clicking on share should open sharing dialog
cy.get('.modal-content').find('.modal-header').find('.modal-title').should('have.text', 'Share');
cy.get('.form-label').should('have.text', 'Get shareable link for this application');
cy.get('.input-group').find('.btn.btn-secondary.btn-sm').should('have.text', 'Copy').click(); //check how to validate clipboard content
});
it('should deploy app', () => {
cy.deployAppWithSingleVersion();
});
it('should launch app', () => {
cy.get('.navbar-nav.flex-row.order-md-last')
.find('a[target="_blank"]')
.should('have.text', 'Launch')
.invoke('removeAttr', 'target')
.click();
cy.url().should('include', '/applications');
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Test Button widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop button to canvas', () => {
cy.get('input[placeholder="Search…"]').type('button');
cy.get('.draggable-box').contains('Button').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Test chart widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop chart to canvas', () => {
cy.get('input[placeholder="Search…"]').type('chart');
cy.get('.draggable-box').contains('Chart').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

View file

@ -1,27 +0,0 @@
describe('Editor- Test checkbox widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should drag and drop checkbox to canvas', () => {
cy.get('input[placeholder="Search…"]').type('checkbox');
cy.get('.draggable-box').drag('.real-canvas', { force: true, position: 'topLeft' });
});
it('should be able to set checkbox value to true', () => {
cy.get('input[placeholder="Search…"]').type('checkbox');
cy.get('.draggable-box').drag('.real-canvas', { force: true, position: 'topLeft' });
cy.get('.form-check-label').click();
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Test Container widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop image to canvas', () => {
cy.get('input[placeholder="Search…"]').type('Container');
cy.get('.draggable-box').contains('Container').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Test Button widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop data-picker to canvas', () => {
cy.get('input[placeholder="Search…"]').type('date picker');
cy.get('.draggable-box').contains('Date Picker').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Test dropdown widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop dropdown to canvas', () => {
cy.get('input[placeholder="Search…"]').type('dropdown');
cy.get('.draggable-box').contains('Dropdown').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Test Image widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop image to canvas', () => {
cy.get('input[placeholder="Search…"]').type('image');
cy.get('.draggable-box').contains('Image').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Test map widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop map to canvas', () => {
cy.get('input[placeholder="Search…"]').type('map');
cy.get('.draggable-box').contains('Map').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Test Modal widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop Modal to canvas', () => {
cy.get('input[placeholder="Search…"]').type('modal');
cy.get('.draggable-box').contains('Modal').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Test multiselect widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop multiselect to canvas', () => {
cy.get('input[placeholder="Search…"]').type('multiselect');
cy.get('.draggable-box').contains('Multiselect').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Test QR scanner widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop QR Scanner to canvas', () => {
cy.get('input[placeholder="Search…"]').type('QR');
cy.get('.draggable-box').contains('QR Scanner').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Test radio button widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop data-picker to canvas', () => {
cy.get('input[placeholder="Search…"]').type('radio');
cy.get('.draggable-box').contains('Radio button').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Test Table widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop table to canvas', () => {
cy.get('input[placeholder="Search…"]').type('table');
cy.get('.draggable-box').contains('Table').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Test text area widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop text-area to canvas', () => {
cy.get('input[placeholder="Search…"]').type('textarea');
cy.get('.draggable-box').contains('Textarea').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Test text editor widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop text-editor to canvas', () => {
cy.get('input[placeholder="Search…"]').type('text editor');
cy.get('.draggable-box').contains('Text Editor').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Test text-input widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop text-input to canvas', () => {
cy.get('input[placeholder="Search…"]').type('text input');
cy.get('.draggable-box').contains('Text Input').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Test text widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop text to canvas', () => {
cy.get('input[placeholder="Search…"]').type('text');
cy.get('.draggable-box').contains('Text').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

View file

@ -1,19 +0,0 @@
describe('Editor- Toggle switch widget', () => {
beforeEach(() => {
//read login data from fixtures
cy.fixture('login-data').then(function (testdata) {
cy.login(testdata.email, testdata.password);
});
cy.wait(1000);
cy.createAppIfEmptyDashboard();
cy.wait(2000);
cy.get('.badge').contains('Edit').click();
cy.get('title').should('have.text', 'ToolJet - Dashboard');
});
it('should be able to drag and drop button to canvas', () => {
cy.get('input[placeholder="Search…"]').type('toggle');
cy.get('.draggable-box').contains('Toggle Switch').drag('.real-canvas', { force: true, position: 'topLeft' });
});
});

12
cypress/jsconfig.json Normal file
View file

@ -0,0 +1,12 @@
{
"compilerOptions": {
"paths": {
"Texts/*": [
"./constants/texts/*"
],
"Selectors/*": [
"./constants/selectors/*"
]
}
}
}

View file

@ -1,3 +1,4 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
@ -14,12 +15,16 @@
/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
// modify env value
config.env = process.env;
// return config
return config;
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}
const webpack = require("@cypress/webpack-preprocessor");
module.exports = (on) => {
const options = {
webpackOptions: require("../webpack.config"),
watchOptions: {}
};
on("file:preprocessor", webpack(options));
};

View file

@ -1,100 +1,19 @@
Cypress.Commands.add('login', (email, password) => {
cy.visit('/login');
cy.get('[data-testid="emailField"]').type(email);
cy.get('[data-testid="passwordField"]').type(password);
cy.get('[data-testid="loginButton"').click();
import { loginSelectors} from "Selectors/login";
Cypress.Commands.add("login",(email,password)=>{
cy.visit("/");
cy.clearAndType(loginSelectors.emailField, email);
cy.clearAndType(loginSelectors.passwordField, password);
cy.get(loginSelectors.signInButton).click();
cy.get(loginSelectors.homePage).should("be.visible");
})
Cypress.Commands.add("clearAndType", (selector, text) => {
cy.get(selector).clear().type(text);
});
Cypress.Commands.add('checkToastMessage', (toastId, message) => {
cy.get(`[id=${toastId}]`).should('contain', message);
});
Cypress.Commands.add('addPostgresDataSource', fn => {
cy.get('div[class="modal-title h4"] span[class="text-muted"]')
.should('have.text', 'Add new datasource')
.and('be.visible')
cy.get('.modal-body')
.find('div[class="row row-deck"]')
.find('h4[class="text-muted mb-2"]')
.should('have.text', 'DATABASES')
cy.get('.modal-body')
.find('.col-md-2')
.contains('PostgreSQL')
.and('be.visible')
.click()
cy.get('.row.mt-3')
.find('.col-md-4')
.find('.form-label')
.contains('Database Name')
cy.get('div[class="row mt-3"] div:nth-child(1)')
.find('.form-control')
.should('have.attr', 'type', 'text')
.type(Cypress.env('TEST_PG_DB'))
cy.get('.row.mt-3')
.find('.col-md-4')
.find('.form-label')
.contains('Username')
cy.get('div[class="row mt-3"] div:nth-child(2)')
.find('.form-control')
.should('have.attr', 'type', 'text')
.type(Cypress.env('TEST_PG_USERNAME'))
cy.get('.row.mt-3')
.find('.col-md-4')
.find('.form-label')
.contains('Password')
cy.get('div[class="row mt-3"] div:nth-child(3)')
.find('.form-control')
.should('have.attr', 'type', 'password')
.type(Cypress.env('TEST_PG_PASSWORD'))
cy.get('input[type="checkbox"]')
.uncheck()
cy.get('button[class="m-2 btn btn-success"]')
.should('have.text', 'Test Connection')
.click()
cy.get('.badge')
.should('have.text', 'connection verified')
cy.get('div[class="col-auto"] button[type="button"]')
.should('have.text', 'Save')
.click()
});
Cypress.Commands.add('createAppIfEmptyDashboard', fn => {
cy.get('body').then(($title => {
//check you are not running tests on empty dashboard state
if ($title.text().includes('You haven\'t created any apps yet.')) {
cy.get('a.btn').eq(0).should('have.text', 'Create your first app')
.click()
cy.go('back')
}
}))
});
Cypress.Commands.add('deployAppWithSingleVersion', fn => {
cy.get('.navbar')
.find('.navbar-nav')
.find('.nav-item')
.find('button[class="btn btn-primary btn-sm"]')
.should('have.text', 'Deploy')
.and('be.visible')
.click();
cy.get('.modal-title.h4').should('have.text', 'Versions and deployments').and('be.visible');
cy.get('.btn.btn-primary.btn-sm.mx-2').contains('+ Version').click();
cy.get('input[placeholder="version name"]').type('1.0');
cy.get('button[class="btn btn-primary"]').should('have.text', 'Create').click();
cy.get('table').contains('td', 'save').click().contains('td', 'deploy').click();
});
Cypress.Commands.add("verifyToastMessage", (selector,message) => {
cy.get(selector)
.should("be.visible")
.should("have.text", message);
})

View file

@ -15,12 +15,6 @@
// Import commands.js using ES2015 syntax:
import './commands'
import '@4tw/cypress-drag-drop'
///<reference types="cypress" />
// Alternatively you can use CommonJS syntax:
// require('./commands')
Cypress.on('uncaught:exception', (err, runnable) => {
// returning false here prevents Cypress from
// failing the test
return false
})

View file

@ -0,0 +1,21 @@
import { loginSelectors } from "Selectors/login";
import { loginTexts} from "Texts/login";
import { path } from "Texts/common";
export const loginPageElements=()=>{
cy.url().should("include",path.loginPath);
cy.get(loginSelectors.logo).should("be.visible");
cy.get(loginSelectors.cardTitle).should("be.visible").should("have.text", loginTexts.cardTitle);
cy.get(loginSelectors.emailLabel).should("be.visible").should("have.text", loginTexts.emailLabel);
cy.get(loginSelectors.passwordLabel).should(($el) => {
expect($el.contents().first().text().trim()).to.eq(loginTexts.passwordLabel);}).should("be.visible");
cy.get(loginSelectors.forgotPassword).should("be.visible").should("have.text", loginTexts.forgotPassword);
cy.get(loginSelectors.showPassword).should("have.text", loginTexts.showPassword);
cy.get(loginSelectors.signUpText).should("be.visible",);
cy.get(loginSelectors.checkBox).check();
cy.get(loginSelectors.checkBox).uncheck();
cy.get(loginSelectors.signUpText).should(($el) => {
expect($el.contents().first().text().trim() ).to.eq(loginTexts.signUpText);}).should("be.visible");
cy.get(loginSelectors.signUpLink).should("be.visible").should("have.text", loginTexts.signUpLink);
}

14
cypress/webpack.config.js Normal file
View file

@ -0,0 +1,14 @@
var path = require("path");
module.exports = {
resolve: {
alias: {
Fixtures: path.resolve(__dirname, "fixtures"),
Plugins: path.resolve(__dirname, "plugins"),
Support: path.resolve(__dirname, "support"),
Texts: path.resolve(__dirname, "constants/texts"),
Selectors: path.resolve(__dirname, "constants/selectors"),
Constants: path.resolve(__dirname, "constants")
},
}
};

View file

@ -1,6 +1,6 @@
# Create .env from this example file and replace values for the environment.
# The application expects a separate .env.test for test environment configuration
# Get detailed information about each variable here: https://docs.tooljet.com/docs/deployment/env-vars
# Get detailed information about each variable here: https://docs.tooljet.com/docs/setup/env-vars
TOOLJET_HOST=http://localhost:8082
LOCKBOX_MASTER_KEY=replace_with_lockbox_master_key

View file

@ -1,4 +1,4 @@
# https://docs.tooljet.io/docs/deployment/env-vars
# https://docs.tooljet.io/docs/setup/env-vars
TOOLJET_HOST=__required__
LOCKBOX_MASTER_KEY=__required__
SECRET_KEY_BASE=__required__

View file

@ -3,7 +3,7 @@ sidebar_position: 1
---
# Mac OS
Follow these steps to setup and run ToolJet on macOS for development purposes. Open terminal and run the commands below. We recommend reading our guide on [architecture](/docs/deployment/architecture) of ToolJet before proceeding.
Follow these steps to setup and run ToolJet on macOS for development purposes. Open terminal and run the commands below. We recommend reading our guide on [architecture](/docs/setup/architecture) of ToolJet before proceeding.
## Setting up
@ -39,7 +39,7 @@ Follow these steps to setup and run ToolJet on macOS for development purposes. O
2. Set up environment variables
Create a `.env` file by copying `.env.example`. More information on the variables that can be set is given in the [environment variables reference](/docs/deployment/env-vars)
Create a `.env` file by copying `.env.example`. More information on the variables that can be set is given in the [environment variables reference](/docs/setup/env-vars)
```bash
cp .env.example .env
```

View file

@ -36,7 +36,7 @@ Please find more information [here](https://docs.docker.com/desktop/windows/wsl/
git clone https://github.com/tooljet/tooljet.git
```
2. Create a `.env` file by copying `.env.example`. More information on the variables that can be set is given in the [environment variables reference](/docs/deployment/env-vars)
2. Create a `.env` file by copying `.env.example`. More information on the variables that can be set is given in the [environment variables reference](/docs/setup/env-vars)
```bash
cp .env.example .env
cp .env.example .env.test

View file

@ -26,7 +26,7 @@ Follow these steps to setup and run ToolJet on Ubuntu. Open terminal and run the
2. Set up environment variables
Create a `.env` file by copying `.env.example`. More information on the variables that can be set is given in the [environment variables reference](/docs/deployment/env-vars)
Create a `.env` file by copying `.env.example`. More information on the variables that can be set is given in the [environment variables reference](/docs/setup/env-vars)
```bash
cp .env.example .env
```

View file

@ -1,5 +1,6 @@
---
sidebar_position: 3
sidebar_label: Baserow
---
# Baserow
@ -8,31 +9,39 @@ sidebar_position: 3
ToolJet can connect to your Baserow account to read and write data. Baserow API token is required to create an Baserow data source on ToolJet. You can follow the steps to create API token from [this link](https://baserow.io/api-docs).
<img class="screenshot-full" src="/img/datasource-reference/baserow/baserow-intro.gif" alt="ToolJet - Data source - Baserow" height="420" />
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Baserow](/img/datasource-reference/baserow/baserow-intro.gif)
</div>
:::tip
This guide assumes that you have already gone through [Adding a data source](/docs/tutorial/adding-a-datasource) tutorial.
:::
Supported queries:
## Supported queries
- List fields
- List rows
- Get row
- Create row
- Update row
- Move row
- Delete row
- [List fields](#list-fields)
- [List rows](#list-rows)
- [Get row](#get-row)
- [Create row](#create-row)
- [Update row](#update-row)
- [Move row](#move-row)
- [Delete row](#delete-row)
## List fields
### List fields
This query lists all the fields in a table.
Required parameters:
#### Required parameters:
- Table ID
- **Table ID**
<img class="screenshot-full" src="/img/datasource-reference/baserow/baserow-list-fields.png" alt="ToolJet - Baserow List Fields Operarion" height="420" />
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Baserow](/img/datasource-reference/baserow/baserow-list-fields.png)
</div>
Example response from Baserow:
@ -75,15 +84,19 @@ Example response from Baserow:
]
```
## List rows
### List rows
This query lists all the rows in a table.
Required parameters:
#### Required parameters:
- Table ID
- **Table ID**
<img class="screenshot-full" src="/img/datasource-reference/baserow/baserow-list-rows.png" alt="ToolJet - Baserow List Rows Operarion" height="420" />
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Baserow](/img/datasource-reference/baserow/baserow-list-rows.png)
</div>
Example response from Baserow:
@ -121,14 +134,18 @@ Example response from Baserow:
}
```
## Get row
### Get row
Required parameters:
#### Required parameters:
- Table ID
- Row ID
- **Table ID**
- **Row ID**
<img class="screenshot-full" src="/img/datasource-reference/baserow/baserow-get-row.png" alt="ToolJet - Baserow Row List Row Operarion" height="420" />
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Baserow](/img/datasource-reference/baserow/baserow-get-row.png)
</div>
Example response from Baserow:
@ -143,17 +160,18 @@ Example response from Baserow:
}
```
## Create row
### Create row
Required parameters:
#### Required parameters:
- Table ID
- **Table ID**
- **Records**
Optional parameters:
<div style={{textAlign: 'center'}}>
- Before ID (The created row will be placed before the entered ID)
![ToolJet - Data source - Baserow](/img/datasource-reference/baserow/baserow-create-row.png)
<img class="screenshot-full" src="/img/datasource-reference/baserow/baserow-create-row.png" alt="ToolJet - Baserow Create Row Operarion" height="420" />
</div>
#### Example Records:
@ -179,14 +197,19 @@ Example response from Baserow:
}
```
## Update row
### Update row
Required parameters:
#### Required parameters:
- Table ID
- Row ID
- **Table ID**
- **Row ID**
- **Records**
<img class="screenshot-full" src="/img/datasource-reference/baserow/baserow-update-row.png" alt="ToolJet - Baserow Update Row Operarion" height="420" />
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Baserow](/img/datasource-reference/baserow/baserow-update-row.png)
</div>
#### Example Records:
@ -212,18 +235,22 @@ Example response from Baserow:
}
```
## Move row
### Move row
Required parameters:
#### Required parameters:
- Table ID
- Row ID
- **Table ID**
- **Row ID**
Optional parameters:
#### Optional parameters:
- Before ID (The row will be moved before the entered ID. If not provided, then the row will be moved to the end )
- **Before ID** (The row will be moved before the entered ID. If not provided, then the row will be moved to the end )
<img class="screenshot-full" src="/img/datasource-reference/baserow/baserow-move-row.png" alt="ToolJet - Baserow Move Row Operarion" height="420" />
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Baserow](/img/datasource-reference/baserow/baserow-move-row.png)
</div>
Example response from Baserow:
@ -238,13 +265,17 @@ Example response from Baserow:
}
```
## Delete row
### Delete row
Required parameters:
#### Required parameters:
- Table ID
- Row ID
- **Table ID**
- **Row ID**
<img class="screenshot-full" src="/img/datasource-reference/baserow/baserow-delete-row.png" alt="ToolJet - Baserow Delete List Row Operarion" height="420" />
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - Baserow](/img/datasource-reference/baserow/baserow-delete-row.png)
</div>
While deleting a row, the response will be either success or failure from Baserow

View file

@ -1,38 +1,41 @@
---
sidebar_position: 4.1
sidebar_label: CouchDB
---
# Couchdb
# CouchDB
ToolJet can connect to CouchDB databases to read and write data. CocuhDB uses basic auth for authentication , username and password for the database is required to create an CouchDB data source on ToolJet. For more info visit [CouchDB docs](https://docs.couchdb.org/en/stable/).
ToolJet can connect to CouchDB databases to read and write data. CocuhDB uses basic auth for authentication , username and password for the database is required to create an CouchDB data source on ToolJet. For more info visit https://docs.couchdb.org/en/stable/
<div style={{textAlign: 'center'}}>
<img class="screenshot-full" src="/img/datasource-reference/couchdb/auth_couch.gif" alt="ToolJet - Data source - CouchDb" height="420" />
![ToolJet - Data source - CouchDb](/img/datasource-reference/couchdb/auth_couch.gif)
Supported queries:
</div>
- Listing records
- Retrieving a record
- Creating a record
- Updating a record
- Deleting a record
- Find
- Retrieving a view
## Supported queries:
- [Listing records](#listing-records)
- [Retrieving a record](#retrieving-a-record)
- [Creating a record](#creating-a-record)
- [Updating a record](#updating-a-record)
- [Deleting a record](#deleting-a-record)
- [Find](#find)
- [Retrieving a view](#retrieving-a-view)
:::info
NOTE: Record ID is same as document ID("_id") .
:::
## Listing records
### Listing records
This query lists all the records in a database.
Optional parameters:
- Descending order
- Limit
- Skip
- Include docs
#### Optional parameters:
- **Include docs**
- **Descending order**
- **Limit**
- **Skip**
:::info
descending (boolean) Return the documents in descending order by key. Default is false.
@ -45,7 +48,11 @@ include_docs (boolean) include_docs key is set to false by default , if true
:::
<img class="screenshot-full" src="/img/datasource-reference/couchdb/list.png" alt="ToolJet - CouchDb Delete Operarion" height="420" />
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - CouchDb](/img/datasource-reference/couchdb/listing.png)
</div>
Example response from CouchDb:
@ -80,14 +87,17 @@ Example response from CouchDb:
}
```
## Retrieving a record
### Retrieving a record
Required parameters:
#### Required parameters:
- Record ID
- **Record ID**
<div style={{textAlign: 'center'}}>
<img class="screenshot-full" src="/img/datasource-reference/couchdb/retreive.png" alt="ToolJet - Data source - CouchDb Retrieve Operation" height="420" />
![ToolJet - Data source - CouchDb](/img/datasource-reference/couchdb/retrieving.png)
</div>
Example response from CouchDb:
@ -104,20 +114,20 @@ Example response from CouchDb:
The returned JSON is the JSON of the document, including the document ID and revision number:
## Creating a record
### Creating a record
<img class="screenshot-full" src="/img/datasource-reference/couchdb/create.png" alt="ToolJet - Data source - CouchDb Create Operarion" height="420"/>
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - CouchDb](/img/datasource-reference/couchdb/creating.png)
</div>
#### Example Records:
```json
[{"name":"tooljet"}]
```
Click on the `run` button to run the query.
:::info
@ -135,25 +145,25 @@ Example response from CouchDb:
```
## Updating a record
### Updating a record
You can get the revision id value, by sending a GET request to get the document details.
You get the document as JSON in the response. For each update to the document, the revision field "_rev" gets changed.
#### Required parameters:
- **Revision ID**
- **Record ID**
Required parameters:
- Revision ID
- Record ID
<div style={{textAlign: 'center'}}>
<img class="screenshot-full" src="/img/datasource-reference/couchdb/update.png" alt="ToolJet - Data source - CouchDb Update Operarion" height="420"/>
![ToolJet - Data source - CouchDb](/img/datasource-reference/couchdb/updating.png)
</div>
#### Example body:
```json
[{"name":"tooljet"}]
```
@ -165,21 +175,24 @@ NOTE: Query must be saved before running.
Example response from CouchDb:
```json
{
{
"ok": true,
"id": "23212104e60a71edb42ebc509f0049a2",
"rev": "2-b0a625abc4e21ee554737920156e911f"
}
```
## Deleting a record
### Deleting a record
Required parameters:
- Revision ID
- Record ID
#### Required parameters:
- **Revision ID**
- **Record ID**
<img class="screenshot-full" src="/img/datasource-reference/couchdb/delete.png" alt="ToolJet - CouchDb Delete Operarion" height="420" />
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - CouchDb](/img/datasource-reference/couchdb/deleting.png)
</div>
Click on the `run` button to run the query.
@ -195,19 +208,23 @@ Example response from CouchDb:
}
```
## Find
### Find
Find documents using a declarative JSON querying syntax.
Required parameters:
- Selector
#### Required parameters:
- **Selector**
:::info
NOTE:
selector syntax: https://pouchdb.com/guides/mango-queries.html
:::
<img class="screenshot-full" src="/img/datasource-reference/couchdb/find.png" alt="ToolJet - Data source - CouchDb Find Operarion" height="420"/>
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - CouchDb](/img/datasource-reference/couchdb/find.png)
</div>
#### Example body:
@ -231,26 +248,34 @@ More information : https://docs.couchdb.org/en/stable/api/database/find.html
:::
Example response from CouchDb:
<img class="screenshot-full" src="/img/datasource-reference/couchdb/find_response.png" alt="ToolJet - Data source - CouchDb Find Operarion" height="420"/>
## Retrieving a view
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - CouchDb](/img/datasource-reference/couchdb/find_response.png)
</div>
### Retrieving a view
Views are the primary tool used for querying and reporting on CouchDB documents.
Required parameters:
- View url
#### Required parameters:
- **View url**
Reference for view :https://docs.couchdb.org/en/3.2.0/ddocs/views/intro.html#what-is-a-view
<img class="screenshot-full" src="/img/datasource-reference/couchdb/view.png" alt="ToolJet - Data source - CouchDb View Operarion" height="420"/>
<div style={{textAlign: 'center'}}>
![ToolJet - Data source - CouchDb](/img/datasource-reference/couchdb/get_view.png)
Optional parameters:
</div>
- Start key
- End key
- Limit
- Skip
#### Optional parameters:
- **Start key**
- **End key**
- **Limit**
- **Skip**
Click on the `run` button to run the query.

View file

@ -10,7 +10,7 @@ ToolJet can connect to Google Sheet using OAuth 2.0, which helps us to limit an
If you are self-hosting the application, you will need to perform some additional steps.
1. Follow the [Google OAuth 2.0 setup steps outlined here](/docs/deployment/env-vars#google-oauth--optional-)
1. Follow the [Google OAuth 2.0 setup steps outlined here](/docs/setup/env-vars#google-oauth--optional-)
2. Set the following environment variables with the values from the previous step:
* `GOOGLE_CLIENT_ID`
* `GOOGLE_CLIENT_SECRET`

View file

@ -0,0 +1,126 @@
# notion
ToolJet can connect to a Notion workspace to do operations on notion pages, databases and blocks.
## Connection
We can easily integrate notion with tooljet using just an API token. Read [Create internal integration with notion API](https://www.notion.so/help/create-integrations-with-the-notion-api)
## Querying Notion
Notion API provides api support for database, page, block, user
### Database
- Retrieve a database
#### **Properties**
- Database ID
- Query a database
#### **Properties**
- Database ID
- Filter : This must be an object of filters
- Children : Array of sort objects
- Limit : limit for pagination
- Start Cursor : Next object id to continue pagination
- Create a database
#### **Properties**
- Database ID
- Title : Title should be an array of rich_text properties
- Properties : Properties defines the columns in a database
- Icon type : Currently notion api accepts two icon options, emoji, external URL
- Icon value: Value of selected icon type
- Icon type : Currently notion api accepts only external URL
- Cover value: Value of selected cover type
- Update a database
#### **Properties**
- Database ID
- Title
- Properties
- Icon type
- Icon value
- Icon type
- Cover value
### Page
- Retrieve a page
#### **Properties**
- Page ID
- Retrieve a page property item
#### **Properties**
- Page ID
- Property ID
- Limit
- Start cursor
- Update a page
#### **Properties**
- Page ID
- Parent type: A database parent or page parent
- Properties : Property values of this page
- Children : Page content for the new page as an array of block objects
- Icon type : Currently notion api accepts two icon options, emoji, external URL
- Icon value: Value of selected icon type
- Icon type : Currently notion api accepts only external URL
- Cover value: Value of selected cover type
- Create a page
#### **Properties**
- Page ID
- Properties : Property values of this page
- Icon type : Currently notion api accepts two icon options, emoji, external URL
- Icon value: Value of selected icon type
- Icon type : Currently notion api accepts only external URL
- Cover value: Value of selected cover type
- Archive (delete) a page
#### **Properties**
- Page ID
- Archive: Dropdown for archive and un archive the page
### Blocks
- Retrieve a block
#### **Properties**
- Block ID
- Retrieve block children
#### **Properties**
- Block ID
- Limit
- Start cursor
- Update a block
#### **Properties**
- Block ID
- Properties: The block object type value with the properties to be updated
- Archive
- Append new block children
#### **Properties**
- Block ID
- Children: Array of block objects
- Delete a block
#### **Properties**
- Block ID
### User
- Retrieve a user from current workspace
#### **Properties**
- User ID
- Retrieve list of users of a workspace
#### **Properties**
- Limit
- Start cursor
[Read more about notion API](https://developers.notion.com/reference/intro)

View file

@ -0,0 +1,21 @@
# openapi
ToolJet has a data source for generating REST API operations from OpenAPI Specs.
## Connection
- Connections are generated from OpenAPI specifications. Currently supports Basic Auth, API Key, Bearer Token, OAuth 2.0
- Also supports specifications with multiple authentications
[Read more](https://swagger.io/docs/specification/authentication/)
## Querying OpenAPI
- Operations will be generated from specifications and each one will be different from other
### Common fields
- Host (Base URL)
Some specs can have one or more base URLs/servers and specific operations may have separate Base URLs. So you can select the URL from the host select
- Operation

View file

@ -1,15 +1,24 @@
# rethinkdb
---
sidebar_position: 26
---
# Rethinkdb
ToolJet can connect to rethinkdb databases to read and write data. For more info visit::https://rethinkdb.com/api/javascript/.com/
<img class="screenshot-full" src="/img/datasource-reference/rethinkdb/rethink_auth.png" alt="ToolJet - Data source - rethinkDB" height="420" />
<img class="screenshot-full" src="/img/datasource-reference/rethink/rethink_auth.png" alt="ToolJet - Data source - rethinkDB" height="420" />
Supported queries:
## Connection
ToolJet connects to InfluxDB using :
- **Database**
- **Host**
- **Port**
- **Username**
- **Password**
## Supported queries:
- Delete database

View file

@ -1,5 +1,5 @@
---
sidebar_position: 20
sidebar_position: 1.1
---
# Amazon S3

View file

@ -1,5 +0,0 @@
{
"label": "Deployment",
"position": 4,
"collapsed": true
}

View file

@ -31,7 +31,7 @@ ToolJet binds together the data sources, queries and widgets to convert business
These resources will help you to quickly build and deploy apps using ToolJet:
- **[Setup](/docs/deployment/architecture)** - Learn how to setup ToolJet locally using docker.
- **[Setup](/docs/setup/architecture)** - Learn how to setup ToolJet locally using docker.
- **[Basic Tutorial](/docs/tutorial/creating-app)** - Learn how to build simple UI and connect to data sources.
- **[Deploy](/docs/contributing-guide/setup/docker)** - Learn how to deploy ToolJet on Heroku, Kubernetes, etc

View file

@ -1,5 +1,5 @@
---
sidebar_position: 2
sidebar_position: 3
sidebar_label: Security
---

View file

@ -0,0 +1,5 @@
{
"label": "Setup",
"position": 2,
"collapsed": true
}

View file

@ -50,7 +50,7 @@ curl -LO https://raw.githubusercontent.com/ToolJet/ToolJet/main/deploy/docker/.e
mv .env.example .env
```
Set up environment variables in `.env` file as explained in [environment variables reference](/docs/deployment/env-vars)
Set up environment variables in `.env` file as explained in [environment variables reference](/docs/setup/env-vars)
`TOOLJET_HOST` environment variable can either be the public ipv4 address of your server or a custom domain that you want to use.

View file

@ -43,7 +43,7 @@ Follow the steps below to deploy ToolJet on AWS EC2 instances.
PG_HOST=<pg host>
PG_PASS=<pg user password>
```
Read **[environment variables reference](/docs/deployment/env-vars)**
Read **[environment variables reference](/docs/setup/env-vars)**
:::info
If there are self signed HTTPS endpoints that Tooljet needs to connect to, please make sure that `NODE_EXTRA_CA_CERTS` environment variable is set to the absolute path containing the certificates.

View file

@ -58,7 +58,7 @@ If you are to use [Public IP](https://cloud.google.com/sql/docs/mysql/connect-ru
3. Create default user (Optional)
Signing up requires [SMTP configuration](https://docs.tooljet.com/docs/deployment/env-vars#smtp-configuration--optional-) to be done, but if you want to start off with default user you can run the command by modifying the `args` flag for a one time usage.
Signing up requires [SMTP configuration](https://docs.tooljet.com/docs/setup/env-vars#smtp-configuration--optional-) to be done, but if you want to start off with default user you can run the command by modifying the `args` flag for a one time usage.
```bash
gcloud run deploy <replace-service-name> \
@ -125,7 +125,7 @@ If you are to use [Public IP](https://cloud.google.com/sql/docs/mysql/connect-ru
3. Create default user **(Optional)**
Signing up requires [SMTP configuration](https://docs.tooljet.com/docs/deployment/env-vars#smtp-configuration--optional-) to be done, but if you want to start off with default user you can run the command by modifying the `args` flag for a one time usage.
Signing up requires [SMTP configuration](https://docs.tooljet.com/docs/setup/env-vars#smtp-configuration--optional-) to be done, but if you want to start off with default user you can run the command by modifying the `args` flag for a one time usage.
```bash
gcloud run deploy <replace-service-name> \

View file

@ -20,11 +20,11 @@ sidebar_label: Heroku
<div style={{textAlign: 'center'}}>
![ToolJet - Deployment- Heroku](/img/deployment/heroku/appname.png)
![ToolJet - Deployment- Heroku](/img/setup/heroku/appname.png)
</div>
3. Now let's enter the `Config vars` to configure additional [environment variables](/docs/deployment/env-vars) that are required for the installation.
3. Now let's enter the `Config vars` to configure additional [environment variables](/docs/setup/env-vars) that are required for the installation.
- **LOCKBOX_MASTER_KEY**: ToolJet server uses lockbox to encrypt datasource credentials. You should set the environment variable LOCKBOX_MASTER_KEY with a 32 byte hexadecimal string. If you have OpenSSL installed, you can run the command `openssl rand -hex 32` to generate the key.
- **NODE_ENV**: By default NODE_ENV is set to production.
- **NODE_OPTIONS**: Node options are configured to increase node memory to support app build.
@ -39,7 +39,7 @@ sidebar_label: Heroku
<div style={{textAlign: 'center'}}>
![ToolJet - Deployment- Heroku](/img/deployment/heroku/build.png)
![ToolJet - Deployment- Heroku](/img/setup/heroku/build.png)
</div>
@ -47,7 +47,7 @@ sidebar_label: Heroku
<div style={{textAlign: 'center'}}>
![ToolJet - Deployment- Heroku](/img/deployment/heroku/login.png)
![ToolJet - Deployment- Heroku](/img/setup/heroku/login.png)
</div>
@ -56,5 +56,5 @@ The one click deployment will create a **free dyno** and a **free postgresql dat
:::
:::tip
ToolJet server and client can be deployed as standalone applications. If you do not want to deploy the client on Heroku, modify `package.json` accordingly. We have a [guide](/docs/deployment/client) on deploying ToolJet client using services such as Firebase.
ToolJet server and client can be deployed as standalone applications. If you do not want to deploy the client on Heroku, modify `package.json` accordingly. We have a [guide](/docs/setup/client) on deploying ToolJet client using services such as Firebase.
:::

View file

@ -19,7 +19,7 @@ Follow the steps below to deploy ToolJet on a AKS Kubernetes cluster.
curl -LO https://raw.githubusercontent.com/ToolJet/ToolJet/main/deploy/kubernetes/AKS/deployment.yaml
```
Make sure to edit the environment variables in the `deployment.yaml`. We advise to use secrets to setup sensitive information. You can check out the available options [here](https://docs.tooljet.com/docs/deployment/env-vars).
Make sure to edit the environment variables in the `deployment.yaml`. We advise to use secrets to setup sensitive information. You can check out the available options [here](https://docs.tooljet.com/docs/setup/env-vars).
:::info
If there are self signed HTTPS endpoints that Tooljet needs to connect to, please make sure that `NODE_EXTRA_CA_CERTS` environment variable is set to the absolute path containing the certificates. You can make use of kubernetes secrets to mount the certificate file onto the containers.

View file

@ -31,7 +31,7 @@ gcloud compute addresses create tj-static-ip --global
curl -LO https://raw.githubusercontent.com/ToolJet/ToolJet/main/deploy/kubernetes/GKE/deployment.yaml
```
Make sure to edit the environment variables in the `deployment.yaml`. You can check out the available options [here](https://docs.tooljet.com/docs/deployment/env-vars).
Make sure to edit the environment variables in the `deployment.yaml`. You can check out the available options [here](https://docs.tooljet.com/docs/setup/env-vars).
:::info
If there are self signed HTTPS endpoints that Tooljet needs to connect to, please make sure that `NODE_EXTRA_CA_CERTS` environment variable is set to the absolute path containing the certificates. You can make use of kubernetes secrets to mount the certificate file onto the containers.

View file

@ -16,7 +16,7 @@ Follow the steps below to deploy ToolJet on a Kubernetes cluster.
2. Create a Kubernetes secret with name `server`. For the minimal setup, ToolJet requires `pg_host`, `pg_db`, `pg_user`, `pg_password`, `secret_key_base` & `lockbox_key` keys in the secret.
Read **[environment variables reference](/docs/deployment/env-vars)**
Read **[environment variables reference](/docs/setup/env-vars)**
3. Create a Kubernetes deployment
@ -45,5 +45,5 @@ If there are self signed HTTPS endpoints that Tooljet needs to connect to, pleas
- [GKE Ingress for HTTP(S) Load Balancing](https://cloud.google.com/kubernetes-engine/docs/concepts/ingress)
:::tip
If you want to serve ToolJet client from services such as Firebase or Netlify, please read the client deployment documentation **[here](/docs/deployment/client)**.
If you want to serve ToolJet client from services such as Firebase or Netlify, please read the client Setup documentation **[here](/docs/setup/client)**.
:::

View file

@ -1,5 +1,5 @@
{
"label": "Tutorial",
"position": 3,
"position": 4,
"collapsed": false
}

View file

@ -2,10 +2,10 @@
Button widget can be used to take actions.
<div style={{textAlign: 'center'}}>
![ToolJet - Widget - Button](/img/widgets/button/add-button.gif)
<div style={{textAlign: 'left'}}>
<figure class="video_container">
<iframe width="630" height="400" src="https://www.youtube.com/embed/zw3yxC7WUOg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</figure>
</div>
## Properties

View file

@ -8,12 +8,21 @@ The Datepicker widget allows users to select a single value for date and time fr
</div>
## Events
### Event: On select
On select event is triggered when an date is selected.
## Properties
### Default value
This value acts as placeholder for the date picker widget, if any value is not provided then the default value will be used from the picker. The default value needs to a `String` with respect to the `format` field. Ex: If format is set to `MM/YYYY` then provide default value as `04/2022`.
### Disabled dates
We can give disabled dates property which will make specific dates disabled and cannot be selected. The default value needs to a an array of`Strings`.
### Format
The format of the date selected by the date picker. Default date format is **DD/MM/YYYY**. Date format should be followed as ISO 8601 as mentioned in the [moment documentation](https://momentjs.com/docs/). This field requires a `String` input. Ex: `DD/MM`, `MM/YYYY`, `YY/MM`, `DD/MM/YYYY` etc.

View file

@ -2,10 +2,10 @@
Tables can be used for both displaying and editing data.
<div style={{textAlign: 'center'}}>
![ToolJet - Widget Reference - Table](/img/widgets/table/table.png)
<div style={{textAlign: 'left'}}>
<figure class="video_container">
<iframe width="630" height="400" src="https://www.youtube.com/embed/hTrdkUtz3aA" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
</figure>
</div>
## Table data

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 143 KiB

View file

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View file

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View file

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 3 KiB

View file

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View file

@ -0,0 +1,13 @@
<svg width="175px" height="175px" viewBox="-16.5 0 175 175" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path d="M141.02 93.0761C141.2 109.353 141.445 125.636 141.528 141.916C141.559 147.861 141.102 153.808 141.057 159.755C140.995 168.12 134.466 173.177 126.874 173.334C111.609 173.649 96.3382 174.018 81.0725 173.935C62.5447 173.835 44.0176 173.357 25.4945 172.885C21.3465 172.675 17.214 172.227 13.1178 171.541C7.98585 170.81 2.93286 164.92 2.74778 159.328C2.28046 145.194 1.74544 131.056 1.58989 116.917C1.24728 85.8065 1.18042 54.6961 0.848309 23.5856C0.797771 18.8068 0.913967 14.1772 2.89152 9.75607C4.36893 6.44876 6.48497 3.76678 10.1519 2.77637C11.4512 2.43963 12.7838 2.24844 14.1254 2.20656C19.846 1.97947 25.5779 1.96199 31.2906 1.62201C51.053 0.445851 70.8113 0.879736 90.573 1.53804C93.3775 1.63124 96.1853 1.7625 98.9872 1.70277C100.916 1.67233 102.778 2.40762 104.164 3.74773C113.401 11.9471 122.634 20.1514 131.862 28.3604C133.204 29.5543 134.47 30.8434 135.885 31.9427C138.83 34.232 139.66 37.3445 139.716 40.8651C139.921 54.0017 140.182 67.137 140.384 80.2729C140.449 84.5391 140.394 88.8053 140.394 93.0715L141.02 93.0761ZM93.7129 9.8708C91.7747 9.71197 90.1188 9.48271 88.4622 9.45449C74.3181 9.21427 60.1735 8.99702 46.0286 8.80228C39.6345 8.71368 33.2392 8.67824 26.8445 8.63886C23.5956 8.61917 20.3396 8.51612 17.098 8.68086C12.4629 8.91649 11.1002 10.2599 10.2719 14.9494C9.4804 19.1462 9.31769 23.4378 9.78901 27.6826C10.0089 29.4652 10.1241 31.2589 10.1341 33.0551C10.2089 73.1353 10.2713 113.215 10.3212 153.292C10.2807 154.301 10.3179 155.312 10.4322 156.316C11.2375 161.436 13.9363 164.546 19.5211 164.771C41.1855 165.645 62.8499 165.641 84.5209 165.276C95.5224 165.09 106.525 165 117.527 164.875C120.218 164.845 122.917 164.951 125.6 164.806C130.02 164.568 132.356 162.145 132.307 157.704C132.252 152.772 131.887 147.843 131.805 142.911C131.709 137.187 131.904 131.456 131.739 125.735C131.335 111.486 130.64 97.2452 130.368 82.9948C130.146 71.3238 130.368 59.6441 130.379 47.9679C130.379 47.026 130.265 46.084 130.231 45.5655C122.735 45.5655 115.665 45.6188 108.597 45.5329C106.467 45.5299 104.344 45.2876 102.268 44.8108C98.0198 43.7784 96.7143 42.1496 96.2201 37.8433C96.0797 36.6186 96.0724 35.3779 95.9576 34.1492C95.233 26.2213 94.4966 18.2949 93.7129 9.8708ZM105.811 37.387C111.269 37.0772 116.413 36.7912 121.557 36.4887C122.768 36.4178 123.976 36.2915 125.916 36.1275L103.053 15.3782L102.534 15.7589C103.605 22.827 104.676 29.8955 105.811 37.387Z" fill="black"/>
<path d="M111.037 128.213C110.829 125.397 109.133 124.645 107.517 123.874C106.715 123.491 105.787 123.372 104.978 122.999C104.239 122.715 103.613 122.197 103.195 121.525C102.777 120.853 102.588 120.063 102.66 119.275C102.777 118.491 103.15 117.768 103.721 117.219C104.292 116.67 105.029 116.325 105.817 116.239C108.024 115.842 110.247 115.535 112.467 115.213C114.244 114.955 115.925 115.287 116.981 116.806C118.088 118.398 117.637 119.997 116.41 121.411C116.284 121.612 116.187 121.83 116.121 122.058C116.217 122.365 116.331 122.667 116.461 122.961C119.221 128.45 116.901 133.825 110.524 135.898C100.618 139.118 88.4417 133.372 89.0855 120.08C89.4905 111.728 93.4049 105.095 100.768 100.784C102.039 100.074 103.395 99.5268 104.803 99.1567C105.233 98.9827 105.695 98.9027 106.159 98.9217C106.623 98.9401 107.077 99.0582 107.492 99.2663C107.906 99.475 108.272 99.7697 108.563 100.131C108.855 100.492 109.066 100.911 109.183 101.36C109.455 102.377 108.694 103.07 107.624 103.586C105.687 104.451 103.862 105.547 102.189 106.851C100.272 108.425 98.7042 110.383 97.5864 112.598C96.4693 114.814 95.8261 117.237 95.6988 119.715C95.1823 127.02 101.458 131.993 108.224 129.741C109.206 129.319 110.148 128.808 111.037 128.213Z" fill="black"/>
<path d="M73.1272 125.606C74.5462 120.167 75.7526 115.529 76.9675 110.895C77.5089 108.834 78.0242 106.765 78.651 104.73C79.0087 103.551 79.4812 102.411 80.0608 101.324C80.8149 99.925 82.0252 99.1328 83.6838 99.5122C85.4034 99.906 85.9409 101.247 85.9547 102.818C86.0098 103.941 85.9193 105.066 85.6863 106.165C83.4731 114.742 81.2173 123.308 78.9188 131.866C78.6241 132.949 78.2079 133.996 77.6776 134.986C76.0368 138.067 72.7341 138.449 70.5734 135.72C69.3165 134.136 68.2342 132.421 67.3456 130.604C63.8697 123.443 60.4667 116.245 57.1369 109.011C56.5204 107.567 56.089 106.052 55.8531 104.5C55.6043 103.106 55.9719 101.792 57.4244 101.112C58.6833 100.521 60.1627 100.993 61.2469 102.421C61.832 103.244 62.3398 104.119 62.7637 105.035C65.8183 111.158 68.8624 117.286 71.8966 123.419C72.1394 123.906 72.4302 124.37 73.1272 125.606Z" fill="black"/>
<path d="M44.9895 103.998C39.4106 104.433 36.9737 106.509 36.2911 111.579C37.2533 112.043 38.3164 112.58 39.3994 113.074C41.438 114.004 43.5344 114.821 45.5218 115.849C47.9507 117.061 49.9767 118.95 51.3553 121.289C55.2054 127.898 51.27 135.764 43.6571 136.62C39.2597 137.115 35.5113 135.505 32.1161 132.888C31.6418 132.586 31.2824 132.134 31.0949 131.603C30.9459 130.677 30.7371 129.462 31.1933 128.823C31.6494 128.184 32.8669 127.959 33.7857 127.849C34.3837 127.777 35.038 128.264 35.6799 128.456C37.1651 128.959 38.6747 129.387 40.2028 129.738C43.1347 130.301 45.5192 129.393 46.4052 127.544C47.2453 125.788 46.1696 123.343 43.6414 121.647C42.413 120.868 41.1247 120.188 39.7886 119.613C38.0598 118.815 36.2346 118.215 34.5432 117.35C30.4903 115.276 29.183 111.604 31.0115 107.445C32.4555 104.164 34.6272 101.396 38.0303 100.089C39.793 99.4328 41.6879 99.2123 43.5541 99.4453C46.0121 99.7728 46.4242 101.387 44.9895 103.998Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="141.113" height="173.93" fill="white" transform="translate(0.706299 0.574219)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 5.6 KiB

File diff suppressed because it is too large Load diff

View file

@ -86,6 +86,9 @@
"uuid": "8.3.2",
"webpack": "^5.55.1",
"webpack-cli": "^4.8.0",
"y-presence": "^1.0.6",
"y-websocket": "^1.4.0",
"yjs": "^13.5.28",
"yup": "^0.27.0"
},
"devDependencies": {

View file

@ -9,9 +9,7 @@ import { SignupPage } from '@/SignupPage';
import { ConfirmationPage } from '@/ConfirmationPage';
import { Authorize } from '@/Oauth2';
import { Authorize as Oauth } from '@/Oauth';
import { Editor, Viewer } from '@/Editor';
import '@/_styles/theme.scss';
import 'emoji-mart/css/emoji-mart.css';
import { Viewer } from '@/Editor';
import { ManageGroupPermissions } from '@/ManageGroupPermissions';
import { ManageOrgUsers } from '@/ManageOrgUsers';
import { ManageGroupPermissionResources } from '@/ManageGroupPermissionResources';
@ -21,6 +19,10 @@ import { ForgotPassword } from '@/ForgotPassword';
import { ResetPassword } from '@/ResetPassword';
import { lt } from 'semver';
import { Toaster } from 'react-hot-toast';
import { RealtimeEditor } from '@/Editor/RealtimeEditor';
import '@/_styles/theme.scss';
import 'emoji-mart/css/emoji-mart.css';
class App extends React.Component {
constructor(props) {
@ -140,7 +142,7 @@ class App extends React.Component {
<PrivateRoute
exact
path="/apps/:id"
component={Editor}
component={RealtimeEditor}
switchDarkMode={this.switchDarkMode}
darkMode={darkMode}
/>

View file

@ -3,7 +3,8 @@ import Modal from '../HomePage/Modal';
import { toast } from 'react-hot-toast';
import { appVersionService } from '@/_services';
import { Confirm } from './Viewer/Confirm';
import Select from '../_ui/Select';
import defaultStyle from '../_ui/Select/styles';
export const AppVersionsManager = function AppVersionsManager({
appId,
editingVersion,
@ -124,8 +125,9 @@ export const AppVersionsManager = function AppVersionsManager({
};
const selectVersion = (version) => {
setEditingAppVersion(version);
setAppDefinitionFromVersion(version);
appVersionService.getOne(appId, version.id).then((data) => {
setAppDefinitionFromVersion(data);
});
};
return (
@ -240,6 +242,37 @@ const CreateVersionModal = function CreateVersionModal({
createVersion(versionName, createAppVersionFrom);
}
};
const options = appVersions.map((version) => {
return { ...version, label: version.name, value: version.name };
});
const width = '100%';
const height = 32;
const darkMode = localStorage.getItem('darkMode') === 'true';
const customStyles = {
...defaultStyle(darkMode, width, height),
option: (provided, state) => {
return {
...provided,
backgroundColor: darkMode
? state.isSelected
? '#3650AF'
: 'rgb(31,40,55)'
: state.isSelected
? '#7A95FB'
: 'white',
color: darkMode ? '#fff' : '#232e3c',
'&:hover': {
backgroundColor: darkMode
? state.isSelected
? '#1F2E64'
: '#323C4B'
: state.isSelected
? '#3650AF'
: '#d8dce9',
},
};
},
};
return (
<Modal
show={showModal || showCreateVersionModalPrompt}
@ -264,16 +297,20 @@ const CreateVersionModal = function CreateVersionModal({
</div>
</div>
<div className="mb-3">
<div className="mb-3" style={{ padding: '2rem 0' }}>
<label className="form-label">Create version from</label>
<div className="ts-control">
<select className="form-select">
{appVersions.map((version) => (
<option className="dropdown-item" key={version.id} onClick={() => setCreateAppVersionFrom(version)}>
{version.name}
</option>
))}
</select>
<Select
options={options}
defaultValue={options[options.length - 1]}
onChange={(version) => {
setCreateAppVersionFrom(version);
}}
useMenuPortal={false}
width="100%"
maxMenuHeight={100}
styles={customStyles}
/>
</div>
</div>

View file

@ -37,6 +37,7 @@ import { CircularProgressBar } from './Components/CirularProgressbar';
import { renderTooltip } from '@/_helpers/appUtils';
import { RangeSlider } from './Components/RangeSlider';
import { Timeline } from './Components/Timeline';
import { SvgImage } from './Components/SvgImage';
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
import '@/_styles/custom.scss';
import { resolveProperties, resolveStyles } from './component-properties-resolution';
@ -80,6 +81,7 @@ const AllComponents = {
CircularProgressBar,
RangeSlider,
Timeline,
SvgImage,
};
export const Box = function Box({

View file

@ -78,6 +78,18 @@ export function onBeforeChange(editor, change, ignoreBraces = false) {
return change;
}
function keystrokeChecker(editor) {
const keyPromise = new Promise((resolve, reject) => {
editor.on('keyup', function (editor, event) {
if (event.key == 'Enter' || event.key == 'Backspace') {
resolve(true);
}
reject(false);
});
});
return keyPromise;
}
export function canShowHint(editor, ignoreBraces = false) {
if (!editor.hasFocus()) return false;
@ -111,6 +123,18 @@ export function handleChange(editor, onChange, suggestions, ignoreBraces = false
},
};
if (canShowHint(editor, ignoreBraces)) {
editor.showHint(options);
const keystrokeValue = keystrokeChecker(editor)
.then((res) => {
return res;
})
.catch((err) => {
return err;
});
const keystrokeCaller = async () => {
const returnValue = await keystrokeValue;
if (!returnValue) editor.showHint(options);
};
keystrokeCaller();
}
}

View file

@ -77,7 +77,7 @@ const CommentHeader = ({ socket, count = 0, threadId, isResolved, isThreadOwner,
<div className="ms-auto d-flex">
<span
title={isThreadOwner ? 'toggle resolved' : 'only creator of thread can resolve'}
className={cx('m-1 cursor-pointer', { disabled: !isThreadOwner })}
className={cx('m-1 cursor-pointer', { disabled: !isThreadOwner, 'd-none': !isResolved && !isThreadOwner })}
onClick={handleResolved}
>
{getResolveIcon()}

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