ci: Release v1.9.3 (#19545)

this PR is for Release v1.9.3
This commit is contained in:
Arpit Mohan 2023-01-09 16:56:35 +01:00 committed by GitHub
commit 56f4e8eae2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
407 changed files with 7148 additions and 3493 deletions

2
.github/config.json vendored

File diff suppressed because one or more lines are too long

View File

@ -218,11 +218,12 @@ jobs:
run: echo "I'm alive!" && exit 0
# Setup Java
- name: Set up JDK 1.11
- name: Set up JDK 17
if: steps.run_result.outputs.run_result != 'success'
uses: actions/setup-java@v1
uses: actions/setup-java@v3
with:
java-version: "11.0.10"
distribution: 'temurin'
java-version: '17'
# Retrieve maven dependencies from cache. After a successful run, these dependencies are cached again
- name: Cache maven dependencies
@ -485,11 +486,12 @@ jobs:
run: echo ${{ steps.run_result.outputs.run_result }}
# Setup Java
- name: Set up JDK 1.11
- name: Set up JDK 17
if: steps.run_result.outputs.run_result != 'success'
uses: actions/setup-java@v1
uses: actions/setup-java@v3
with:
java-version: "11.0.10"
distribution: 'temurin'
java-version: '17'
- name: Download the react build artifact
uses: actions/download-artifact@v2

View File

@ -114,11 +114,11 @@ jobs:
with:
fetch-depth: 0
- name: Set up JDK 1.11
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: "11.0.10"
distribution: adopt
distribution: 'temurin'
java-version: '17'
# Retrieve maven dependencies from cache. After a successful run, these dependencies are cached again
- name: Cache maven dependencies

View File

@ -151,12 +151,12 @@ jobs:
run: echo ${{ steps.run_result.outputs.run_result }}
# Setup Java
- name: Set up JDK 1.11
- name: Set up JDK 17
if: steps.run_result.outputs.run_result != 'success'
uses: actions/setup-java@v3
with:
java-version: "11.0.10"
distribution: adopt
distribution: 'temurin'
java-version: '17'
- name: Download the client build artifact
uses: actions/download-artifact@v3
@ -226,6 +226,7 @@ jobs:
-v "$PWD/stacks:/appsmith-stacks" -e APPSMITH_LICENSE_KEY=$APPSMITH_LICENSE_KEY \
-e APPSMITH_AUDITLOG_ENABLED=true \
-e APPSMITH_CLOUD_SERVICES_BASE_URL=http://host.docker.internal:5001 \
--add-host=host.docker.internal:host-gateway \
fatcontainer
- name: Use Node.js 16.14.0
@ -585,6 +586,13 @@ jobs:
if: steps.run_result.outputs.run_result != 'success'
run: yarn install --frozen-lockfile
# Setup Java
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Download the react build artifact
if: steps.run_result.outputs.run_result != 'success'
uses: actions/download-artifact@v3

View File

@ -19,5 +19,5 @@ jobs:
- uses: docker://agilepathway/pull-request-label-checker:latest
with:
# Needs to have a Test Plan Approved label if not part of any of the pods below
any_of: Test Plan Approved,App Viewers Pod,User Education Pod,New Developers Pod,Team Managers Pod,UI Builders Pod,Performance,Release Promotion,CI,Design System Pod,DevOps Pod,skip-testPlan
any_of: Test Plan Approved,User Education Pod,DevOps Pod,Performance,CI,Release Promotion,skip-testPlan
repo_token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -121,6 +121,14 @@ jobs:
if: steps.run_result.outputs.run_result != 'success'
run: yarn install --frozen-lockfile
# Setup Java
- name: Set up JDK 17
if: steps.run_result.outputs.run_result != 'success'
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Download the react build artifact
if: steps.run_result.outputs.run_result != 'success'
uses: actions/download-artifact@v3

View File

@ -85,12 +85,12 @@ jobs:
run: echo "I'm alive!" && exit 0
# Setup Java
- name: Set up JDK 1.11
- name: Set up JDK 17
if: steps.run_result.outputs.run_result != 'success'
uses: actions/setup-java@v3
with:
java-version: "11.0.10"
distribution: adopt
distribution: 'temurin'
java-version: '17'
# Retrieve maven dependencies from cache. After a successful run, these dependencies are cached again
- name: Cache maven dependencies
@ -125,6 +125,7 @@ jobs:
- name: Build and test
if: steps.run_result.outputs.run_result != 'success'
env:
ACTIVE_PROFILE: test
APPSMITH_MONGODB_URI: "mongodb://localhost:27017/mobtools"
APPSMITH_CLOUD_SERVICES_BASE_URL: "https://release-cs.appsmith.com"
APPSMITH_REDIS_URL: "redis://127.0.0.1:6379"

View File

@ -227,12 +227,12 @@ jobs:
run: echo "I'm alive!" && exit 0
# Setup Java
- name: Set up JDK 1.11
- name: Set up JDK 17
if: steps.run_result.outputs.run_result != 'success'
uses: actions/setup-java@v3
with:
java-version: "11.0.10"
distribution: adopt
distribution: 'temurin'
java-version: '17'
# Retrieve maven dependencies from cache. After a successful run, these dependencies are cached again
- name: Cache maven dependencies
@ -399,15 +399,235 @@ jobs:
- name: Save the status of the run
run: echo "run_result=success" >> $GITHUB_OUTPUT > ~/run_result
perf-test:
needs: [buildClient, buildServer, buildRts]
# Only run if the build step is successful
if: success()
name: perf-test
uses: ./.github/workflows/perf-test.yml
secrets: inherit
with:
pr: ${{ github.event.client_payload.pull_request.number }}
runs-on: ubuntu-latest-4-cores
defaults:
run:
working-directory: app/client
shell: bash
# Service containers to run with this job. Required for running tests
services:
# Label used to access the service container
redis:
# Docker Hub image for Redis
image: redis
ports:
# Opens tcp port 6379 on the host and service container
- 6379:6379
mongo:
image: mongo
ports:
- 27017:27017
steps:
- name: Checkout the merged commit from PR and base branch
if: github.event_name == 'pull_request_review'
uses: actions/checkout@v3
with:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
- name: Checkout the head commit of the branch
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
uses: actions/checkout@v3
# Timestamp will be used to create cache key
- id: timestamp
run: echo "timestamp=$(date +'%Y-%m-%dT%H:%M:%S')" >> $GITHUB_OUTPUT
# In case this is second attempt try restoring status of the prior attempt from cache
- name: Restore the previous run result
uses: actions/cache@v3
with:
path: |
~/run_result
key: ${{ github.run_id }}-${{ github.job }}-${{ steps.timestamp.outputs.timestamp }}
restore-keys: |
${{ github.run_id }}-${{ github.job }}-
# Fetch prior run result
- name: Get the previous run result
id: run_result
run: cat ~/run_result 2>/dev/null || echo 'default'
# In case of prior failure run the job
- if: steps.run_result.outputs.run_result != 'success'
run: echo "I'm alive!" && exit 0
# Set status = success
- name: Save the status of the run
run: echo "run_result=success" >> $GITHUB_OUTPUT > ~/run_result
- name: Use Node.js 16.14.0
if: steps.run_result.outputs.run_result != 'success'
uses: actions/setup-node@v3
with:
node-version: "16.14.0"
- name: Get yarn cache directory path
if: steps.run_result.outputs.run_result != 'success'
id: yarn-dep-cache-dir-path
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
# Retrieve npm dependencies from cache. After a successful run, these dependencies are cached again
- name: Cache npm dependencies
if: steps.run_result.outputs.run_result != 'success'
id: yarn-dep-cache
uses: actions/cache@v3
env:
cache-name: cache-yarn-dependencies
with:
path: |
${{ steps.yarn-dep-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-dep-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-dep-
# Install all the dependencies
- name: Install dependencies
if: steps.run_result.outputs.run_result != 'success'
run: yarn install --frozen-lockfile
- name: Download the react build artifact
if: steps.run_result.outputs.run_result != 'success'
uses: actions/download-artifact@v3
with:
name: client-build
path: app/client/build
- name: Download the server build artifact
if: steps.run_result.outputs.run_result != 'success'
uses: actions/download-artifact@v3
with:
name: server-build
path: app/server/dist
# Start server
- name: start server
if: steps.run_result.outputs.run_result != 'success'
working-directory: app/server
env:
APPSMITH_MONGODB_URI: "mongodb://localhost:27017/mobtools"
APPSMITH_REDIS_URL: "redis://127.0.0.1:6379"
APPSMITH_ENCRYPTION_PASSWORD: "password"
APPSMITH_ENCRYPTION_SALT: "salt"
APPSMITH_IS_SELF_HOSTED: false
APPSMITH_CLOUD_SERVICES_BASE_URL: https://release-cs.appsmith.com
APPSMITH_CLOUD_SERVICES_USERNAME: ""
APPSMITH_CLOUD_SERVICES_PASSWORD: ""
APPSMITH_GIT_ROOT: "./container-volumes/git-storage"
APPSMITH_AUDITLOG_ENABLED: true
run: |
ls -l
ls -l scripts/
ls -l dist/
# Run the server in the background and redirect logs to a log file
./scripts/start-dev-server.sh &> server-logs.log &
- name: Wait for 30 seconds for server to start
if: steps.run_result.outputs.run_result != 'success'
run: |
sleep 30s
- name: Installing Yarn serve
if: steps.run_result.outputs.run_result != 'success'
run: |
yarn global add serve
echo "$(yarn global bin)" >> $GITHUB_PATH
# Start rts
- name: Start RTS Server
if: steps.run_result.outputs.run_result != 'success'
working-directory: ./app/rts
run: |
cp .env.example .env
./start-server.sh &
- name: Setting up the perf tests
if: steps.run_result.outputs.run_result != 'success'
shell: bash
env:
APPSMITH_SSL_CERTIFICATE: ${{ secrets.APPSMITH_SSL_CERTIFICATE }}
APPSMITH_SSL_KEY: ${{ secrets.APPSMITH_SSL_KEY }}
CYPRESS_URL: ${{ secrets.CYPRESS_URL }}
CYPRESS_USERNAME: ${{ secrets.CYPRESS_USERNAME }}
CYPRESS_PASSWORD: ${{ secrets.CYPRESS_PASSWORD }}
CYPRESS_TESTUSERNAME1: ${{ secrets.CYPRESS_TESTUSERNAME1 }}
CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }}
CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }}
CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }}
CYPRESS_TESTUSERNAME3: ${{ secrets.CYPRESS_TESTUSERNAME3 }}
CYPRESS_TESTPASSWORD3: ${{ secrets.CYPRESS_TESTPASSWORD3 }}
CYPRESS_TESTUSERNAME4: ${{ secrets.CYPRESS_TESTUSERNAME4 }}
CYPRESS_TESTPASSWORD4: ${{ secrets.CYPRESS_TESTPASSWORD4 }}
CYPRESS_S3_ACCESS_KEY: ${{ secrets.CYPRESS_S3_ACCESS_KEY }}
CYPRESS_S3_SECRET_KEY: ${{ secrets.CYPRESS_S3_SECRET_KEY }}
CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_ID }}
CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GOOGLE_CLIENT_SECRET }}
CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_ID: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_ID }}
CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET: ${{ secrets.CYPRESS_APPSMITH_OAUTH2_GITHUB_CLIENT_SECRET }}
APPSMITH_DISABLE_TELEMETRY: true
APPSMITH_GOOGLE_MAPS_API_KEY: ${{ secrets.APPSMITH_GOOGLE_MAPS_API_KEY }}
POSTGRES_PASSWORD: postgres
run: |
./cypress/setup-test.sh
- name: Checkout Performance Infra code
uses: actions/checkout@v3
with:
repository: appsmithorg/performance-infra
token: ${{ secrets.APPSMITH_PERF_INFRA_REPO_PAT }}
ref: main
path: app/client/perf
- name: Installing performance tests dependencies
if: steps.run_result.outputs.run_result != 'success'
working-directory: app/client/perf
shell: bash
run: yarn install --frozen-lockfile
- name: Change test script permissions
if: steps.run_result.outputs.run_result != 'success'
working-directory: app/client/perf
run: chmod +x ./start-test.sh
- name: Run performance tests
if: steps.run_result.outputs.run_result != 'success'
working-directory: app/client/perf
shell: bash
env:
APPSMITH_SSL_CERTIFICATE: ${{ secrets.APPSMITH_SSL_CERTIFICATE }}
APPSMITH_SSL_KEY: ${{ secrets.APPSMITH_SSL_KEY }}
CYPRESS_TESTUSERNAME1: ${{ secrets.CYPRESS_TESTUSERNAME9 }}
CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD9 }}
APPSMITH_PERFORMANCE_DB_CONFIG: ${{ secrets.APPSMITH_PERFORMANCE_DB_CONFIG }}
APPSMITH_PERFORMANCE_S3_KEY: ${{secrets.APPSMITH_PERFORMANCE_S3_KEY}}
APPSMITH_PERFORMANCE_S3_SECRET: ${{secrets.APPSMITH_PERFORMANCE_S3_SECRET}}
APPSMITH_PERFORMANCE_DB_PASSWORD: ${{secrets.APPSMITH_PERFORMANCE_DB_PASSWORD}}
APPSMITH_PERFORMANCE_DB_HOST: ${{secrets.APPSMITH_PERFORMANCE_DB_HOST}}
PERF_GITHUB_PAT: ${{ secrets.APPSMITH_PERF_INFRA_REPO_PAT }}
APPSMITH_DISABLE_TELEMETRY: true
POSTGRES_PASSWORD: postgres
NODE_TLS_REJECT_UNAUTHORIZED: "0"
MACHINE: ubuntu-latest-4-cores
run: ./start-test.sh
# Restore the previous built bundle if present. If not push the newly built into the cache
- name: Restore the previous bundle
uses: actions/cache@v3
with:
path: |
app/client/perf/traces
key: ${{ github.run_id }}-${{ github.job }}-${{ steps.timestamp.outputs.timestamp }}
restore-keys: |
${{ github.run_id }}-${{ github.job }}
- uses: actions/upload-artifact@v3
with:
name: performance-summaries
path: app/client/perf/traces
# Set status = success
- name: Save the status of the run
run: echo "run_result=success" >> $GITHUB_OUTPUT > ~/run_result
fat-container-test:
needs: [buildClient, buildServer, buildRts]
@ -514,12 +734,12 @@ jobs:
run: echo ${{ steps.run_result.outputs.run_result }}
# Setup Java
- name: Set up JDK 1.11
- name: Set up JDK 17
if: steps.run_result.outputs.run_result != 'success'
uses: actions/setup-java@v3
with:
java-version: "11.0.10"
distribution: adopt
distribution: 'temurin'
java-version: '17'
- name: Download the react build artifact
uses: actions/download-artifact@v3
@ -587,8 +807,9 @@ jobs:
cd fatcontainerlocal
docker run -d --name appsmith -p 80:80 -p 9001:9001 \
-v "$PWD/stacks:/appsmith-stacks" -e APPSMITH_LICENSE_KEY=$APPSMITH_LICENSE_KEY \
-e APPSMITH_CLOUD_SERVICES_BASE_URL=http://host.docker.internal:5001 \
-e APPSMITH_AUDITLOG_ENABLED=true \
-e APPSMITH_CLOUD_SERVICES_BASE_URL=http://host.docker.internal:5001 \
--add-host=host.docker.internal:host-gateway \
fatcontainer
- name: Use Node.js 16.14.0
@ -945,12 +1166,12 @@ jobs:
run: echo ${{ steps.run_result.outputs.run_result }}
# Setup Java
- name: Set up JDK 1.11
- name: Set up JDK 17
if: steps.run_result.outputs.run_result != 'success'
uses: actions/setup-java@v3
with:
java-version: "11.0.10"
distribution: adopt
distribution: 'temurin'
java-version: '17'
- name: Download the server build artifact
if: steps.run_result.outputs.run_result != 'success'

View File

@ -13,9 +13,12 @@ ENV LC_ALL C.UTF-8
RUN apt-get update \
&& apt-get upgrade --yes \
&& DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends --yes \
supervisor curl cron certbot nginx gnupg netcat openssh-client \
software-properties-common gettext openjdk-11-jdk \
supervisor curl cron certbot nginx gnupg wget netcat openssh-client \
software-properties-common gettext \
python3-pip python-setuptools git ca-certificates-java \
&& wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | apt-key add - \
&& echo "deb https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | tee /etc/apt/sources.list.d/adoptium.list \
&& apt-get update && apt-get install --no-install-recommends --yes temurin-17-jdk \
&& pip install --no-cache-dir git+https://github.com/coderanger/supervisor-stdout@973ba19967cdaf46d9c1634d1675fc65b9574f6e \
&& apt-get remove --yes git python3-pip
@ -67,7 +70,7 @@ COPY ./deploy/docker/templates/nginx/* \
templates/
# Add bootstrapfile
COPY ./deploy/docker/entrypoint.sh ./deploy/docker/scripts/* ./scripts/start-backend.sh ./
COPY ./deploy/docker/entrypoint.sh ./deploy/docker/scripts/* ./
# Add util tools
COPY ./deploy/docker/utils ./utils
@ -81,7 +84,7 @@ COPY ./deploy/docker/templates/supervisord/ templates/supervisord/
COPY ./deploy/docker/templates/cron.d /etc/cron.d/
RUN chmod 0644 /etc/cron.d/*
RUN chmod +x entrypoint.sh renew-certificate.sh healthcheck.sh start-backend.sh
RUN chmod +x entrypoint.sh renew-certificate.sh healthcheck.sh
# Disable setuid/setgid bits for the files inside container.
RUN find / \( -path /proc -prune \) -o \( \( -perm -2000 -o -perm -4000 \) -print -exec chmod -s '{}' + \) || true

View File

@ -180,54 +180,55 @@ Lets build great software together.
[![nayan-rafiq](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/83352306?v=4&w=50&h=50&mask=circle)](https://github.com/nayan-rafiq)
[![jsartisan](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/6636360?v=4&w=50&h=50&mask=circle)](https://github.com/jsartisan)
[![yaldram](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13429535?v=4&w=50&h=50&mask=circle)](https://github.com/yaldram)
[![Rishabh-Rathod](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/23132741?v=4&w=50&h=50&mask=circle)](https://github.com/Rishabh-Rathod)
[![aswathkk](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/10436935?v=4&w=50&h=50&mask=circle)](https://github.com/aswathkk)
[![ankitakinger](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/28362912?v=4&w=50&h=50&mask=circle)](https://github.com/ankitakinger)
[![Rishabh-Rathod](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/23132741?v=4&w=50&h=50&mask=circle)](https://github.com/Rishabh-Rathod)
[![sbalaji1192](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5328605?v=4&w=50&h=50&mask=circle)](https://github.com/sbalaji1192)
[![ohansFavour](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/46670083?v=4&w=50&h=50&mask=circle)](https://github.com/ohansFavour)
[![Aishwarya-U-R](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/91450662?v=4&w=50&h=50&mask=circle)](https://github.com/Aishwarya-U-R)
[![Irongade](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/37867493?v=4&w=50&h=50&mask=circle)](https://github.com/Irongade)
[![SatishGandham](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/441914?v=4&w=50&h=50&mask=circle)](https://github.com/SatishGandham)
[![prsidhu](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5424788?v=4&w=50&h=50&mask=circle)](https://github.com/prsidhu)
[![pranavkanade](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13262095?v=4&w=50&h=50&mask=circle)](https://github.com/pranavkanade)
[![SatishGandham](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/441914?v=4&w=50&h=50&mask=circle)](https://github.com/SatishGandham)
[![yatinappsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/84702014?v=4&w=50&h=50&mask=circle)](https://github.com/yatinappsmith)
[![somangshu](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/11089579?v=4&w=50&h=50&mask=circle)](https://github.com/somangshu)
[![ApekshaBhosale](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/7846888?v=4&w=50&h=50&mask=circle)](https://github.com/ApekshaBhosale)
[![yatinappsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/84702014?v=4&w=50&h=50&mask=circle)](https://github.com/yatinappsmith)
[![sidhantgoel](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/3933675?v=4&w=50&h=50&mask=circle)](https://github.com/sidhantgoel)
[![rahulramesha](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/71900764?v=4&w=50&h=50&mask=circle)](https://github.com/rahulramesha)
[![IAmAnubhavSaini](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1573771?v=4&w=50&h=50&mask=circle)](https://github.com/IAmAnubhavSaini)
[![Parthvi12](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/80334441?v=4&w=50&h=50&mask=circle)](https://github.com/Parthvi12)
[![marks0351](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/35134347?v=4&w=50&h=50&mask=circle)](https://github.com/marks0351)
[![albinAppsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/87797149?v=4&w=50&h=50&mask=circle)](https://github.com/albinAppsmith)
[![ayushpahwa](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/8526215?v=4&w=50&h=50&mask=circle)](https://github.com/ayushpahwa)
[![AmanAgarwal041](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/7565635?v=4&w=50&h=50&mask=circle)](https://github.com/AmanAgarwal041)
[![ayushpahwa](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/8526215?v=4&w=50&h=50&mask=circle)](https://github.com/ayushpahwa)
[![ashit-rath](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/88306433?v=4&w=50&h=50&mask=circle)](https://github.com/ashit-rath)
[![areyabhishek](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/30255708?v=4&w=50&h=50&mask=circle)](https://github.com/areyabhishek)
[![ankurrsinghal](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/17961105?v=4&w=50&h=50&mask=circle)](https://github.com/ankurrsinghal)
[![rimildeyjsr](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/10229595?v=4&w=50&h=50&mask=circle)](https://github.com/rimildeyjsr)
[![vishnu-gp](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/9128194?v=4&w=50&h=50&mask=circle)](https://github.com/vishnu-gp)
[![cokoghenun](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/17744578?v=4&w=50&h=50&mask=circle)](https://github.com/cokoghenun)
[![keyurparalkar](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/14138515?v=4&w=50&h=50&mask=circle)](https://github.com/keyurparalkar)
[![eco-monk](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/66776129?v=4&w=50&h=50&mask=circle)](https://github.com/eco-monk)
[![cokoghenun](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/17744578?v=4&w=50&h=50&mask=circle)](https://github.com/cokoghenun)
[![vihar](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/16307796?v=4&w=50&h=50&mask=circle)](https://github.com/vihar)
[![ChandanBalajiBP](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/104058110?v=4&w=50&h=50&mask=circle)](https://github.com/ChandanBalajiBP)
[![dhruvikn](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/22471214?v=4&w=50&h=50&mask=circle)](https://github.com/dhruvikn)
[![berzerkeer](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/74818788?v=4&w=50&h=50&mask=circle)](https://github.com/berzerkeer)
[![souma-ghosh](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/103924539?v=4&w=50&h=50&mask=circle)](https://github.com/souma-ghosh)
[![dhruvikn](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/22471214?v=4&w=50&h=50&mask=circle)](https://github.com/dhruvikn)
[![tanvibhakta](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13763558?v=4&w=50&h=50&mask=circle)](https://github.com/tanvibhakta)
[![subrata71](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/3524599?v=4&w=50&h=50&mask=circle)](https://github.com/subrata71)
[![sum35h](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20785806?v=4&w=50&h=50&mask=circle)](https://github.com/sum35h)
[![tanvibhakta](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13763558?v=4&w=50&h=50&mask=circle)](https://github.com/tanvibhakta)
[![sneha122](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/30018882?v=4&w=50&h=50&mask=circle)](https://github.com/sneha122)
[![nsarupr](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20905988?v=4&w=50&h=50&mask=circle)](https://github.com/nsarupr)
[![pratapaprasanna](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/15846947?v=4&w=50&h=50&mask=circle)](https://github.com/pratapaprasanna)
[![sondermanish](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/107841575?v=4&w=50&h=50&mask=circle)](https://github.com/sondermanish)
[![Pranay105](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/48308728?v=4&w=50&h=50&mask=circle)](https://github.com/Pranay105)
[![vaibh1297](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/40293928?v=4&w=50&h=50&mask=circle)](https://github.com/vaibh1297)
[![ankitsrivas14](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/67647761?v=4&w=50&h=50&mask=circle)](https://github.com/ankitsrivas14)
[![NilanshBansal](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/25542733?v=4&w=50&h=50&mask=circle)](https://github.com/NilanshBansal)
[![ravikp7](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13567359?v=4&w=50&h=50&mask=circle)](https://github.com/ravikp7)
[![gitstart-appsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/114667275?v=4&w=50&h=50&mask=circle)](https://github.com/gitstart-appsmith)
[![kocharrahul7](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20532920?v=4&w=50&h=50&mask=circle)](https://github.com/kocharrahul7)
[![rohitagarwal88](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/890915?v=4&w=50&h=50&mask=circle)](https://github.com/rohitagarwal88)
[![vaibh1297](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/40293928?v=4&w=50&h=50&mask=circle)](https://github.com/vaibh1297)
[![Pranay105](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/48308728?v=4&w=50&h=50&mask=circle)](https://github.com/Pranay105)
[![NilanshBansal](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/25542733?v=4&w=50&h=50&mask=circle)](https://github.com/NilanshBansal)
[![ankitsrivas14](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/67647761?v=4&w=50&h=50&mask=circle)](https://github.com/ankitsrivas14)
[![gitstart-appsmith](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/114667275?v=4&w=50&h=50&mask=circle)](https://github.com/gitstart-appsmith)
[![ravikp7](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13567359?v=4&w=50&h=50&mask=circle)](https://github.com/ravikp7)
[![kocharrahul7](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20532920?v=4&w=50&h=50&mask=circle)](https://github.com/kocharrahul7)
[![ramsaptami](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/79509062?v=4&w=50&h=50&mask=circle)](https://github.com/ramsaptami)
[![AS-Laguna](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/101155659?v=4&w=50&h=50&mask=circle)](https://github.com/AS-Laguna)
[![vivonk](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/25587962?v=4&w=50&h=50&mask=circle)](https://github.com/vivonk)
@ -238,7 +239,6 @@ Lets build great software together.
[![tejasahluwalia](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/39881648?v=4&w=50&h=50&mask=circle)](https://github.com/tejasahluwalia)
[![Vijetha-Kaja](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/119562824?v=4&w=50&h=50&mask=circle)](https://github.com/Vijetha-Kaja)
[![appsmithguru](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/116803038?v=4&w=50&h=50&mask=circle)](https://github.com/appsmithguru)
[![IAmAnubhavSaini](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1573771?v=4&w=50&h=50&mask=circle)](https://github.com/IAmAnubhavSaini)
[![hiteshjoshi](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/217911?v=4&w=50&h=50&mask=circle)](https://github.com/hiteshjoshi)
[![rlnorthcutt](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/673633?v=4&w=50&h=50&mask=circle)](https://github.com/rlnorthcutt)
[![tomjose92](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5265702?v=4&w=50&h=50&mask=circle)](https://github.com/tomjose92)
@ -334,6 +334,7 @@ Lets build great software together.
[![Bhavin789](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/15359575?v=4&w=50&h=50&mask=circle)](https://github.com/Bhavin789)
[![bhuvanaindukuri](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/29200083?v=4&w=50&h=50&mask=circle)](https://github.com/bhuvanaindukuri)
[![donno2048](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/61805754?v=4&w=50&h=50&mask=circle)](https://github.com/donno2048)
[![jacobwgillespie](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/130874?v=4&w=50&h=50&mask=circle)](https://github.com/jacobwgillespie)
[![reachtokish](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/10685261?v=4&w=50&h=50&mask=circle)](https://github.com/reachtokish)
[![nuwan94](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/25694570?v=4&w=50&h=50&mask=circle)](https://github.com/nuwan94)
[![OmkarPh](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/48476025?v=4&w=50&h=50&mask=circle)](https://github.com/OmkarPh)
@ -341,6 +342,7 @@ Lets build great software together.
[![rafaeelaudibert](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/32079912?v=4&w=50&h=50&mask=circle)](https://github.com/rafaeelaudibert)
[![samyakjain10](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/56185041?v=4&w=50&h=50&mask=circle)](https://github.com/samyakjain10)
[![Jain-Sanchit](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/31254015?v=4&w=50&h=50&mask=circle)](https://github.com/Jain-Sanchit)
[![irfan-ansari-au28](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/18440293?v=4&w=50&h=50&mask=circle)](https://github.com/irfan-ansari-au28)
[![sheetal2001p](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/76812440?v=4&w=50&h=50&mask=circle)](https://github.com/sheetal2001p)
[![Hard-Coder05](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/54059881?v=4&w=50&h=50&mask=circle)](https://github.com/Hard-Coder05)
[![vj-codes](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/60894542?v=4&w=50&h=50&mask=circle)](https://github.com/vj-codes)
@ -370,7 +372,6 @@ Lets build great software together.
[![felixsuarez0727](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/25110207?v=4&w=50&h=50&mask=circle)](https://github.com/felixsuarez0727)
[![gitstart](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1501599?v=4&w=50&h=50&mask=circle)](https://github.com/gitstart)
[![ishaanmehta4](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/61118240?v=4&w=50&h=50&mask=circle)](https://github.com/ishaanmehta4)
[![jacobwgillespie](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/130874?v=4&w=50&h=50&mask=circle)](https://github.com/jacobwgillespie)
[![JarLob](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/26652396?v=4&w=50&h=50&mask=circle)](https://github.com/JarLob)
[![JeffResc](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5752328?v=4&w=50&h=50&mask=circle)](https://github.com/JeffResc)
[![jmakhack](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1442227?v=4&w=50&h=50&mask=circle)](https://github.com/jmakhack)
@ -421,6 +422,7 @@ Lets build great software together.
[![nupur-singhal1992](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/35266222?v=4&w=50&h=50&mask=circle)](https://github.com/nupur-singhal1992)
[![nzidol](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13618973?v=4&w=50&h=50&mask=circle)](https://github.com/nzidol)
[![onifs10](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/48095055?v=4&w=50&h=50&mask=circle)](https://github.com/onifs10)
[![ps-xaf](https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/4478450?v=4&w=50&h=50&mask=circle)](https://github.com/ps-xaf)
## License

View File

@ -0,0 +1,230 @@
{
"responseMeta": {
"status": 200,
"success": true
},
"data": {
"user": {
"id": "63abf1f3cf1ed12127ce3b01",
"userPermissions": [],
"name": "test1@sharklasers.com",
"email": "test1@sharklasers.com",
"source": "FORM",
"isEnabled": true,
"groupIds": [],
"permissions": [],
"isAnonymous": false,
"tenantId": "63abe1a2cf1ed12127ce3ac4",
"enabled": true,
"username": "test1@sharklasers.com",
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"claims": {},
"address": {},
"new": false
},
"workspaceApplications": [
{
"workspace": {
"id": "63ad2c156e7a9012ada704c7",
"userPermissions": [
"inviteUsers:workspace",
"read:workspaces",
"execute:workspaceDatasources",
"read:workspaceApplications"
],
"name": "Untitled workspace 1",
"email": "test@sharklasers.com",
"plugins": [
{
"userPermissions": [],
"pluginId": "63abe19ecf1ed12127ce3a5a",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe1c5cf1ed12127ce3af5",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe19fcf1ed12127ce3a69",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe19ecf1ed12127ce3a55",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe1c5cf1ed12127ce3af4",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe19ccf1ed12127ce3a38",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe19fcf1ed12127ce3a64",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe1a0cf1ed12127ce3a92",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe1c5cf1ed12127ce3af6",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe19ecf1ed12127ce3a4c",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe1a1cf1ed12127ce3aa3",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe1a3cf1ed12127ce3ae9",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe19ccf1ed12127ce3a39",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe19fcf1ed12127ce3a6d",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe19ecf1ed12127ce3a5c",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe1a0cf1ed12127ce3a8a",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe19fcf1ed12127ce3a84",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe19ccf1ed12127ce3a3a",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe19fcf1ed12127ce3a79",
"status": "FREE",
"new": true
},
{
"userPermissions": [],
"pluginId": "63abe19ecf1ed12127ce3a57",
"status": "FREE",
"new": true
}
],
"slug": "untitled-workspace-1",
"tenantId": "63abe1a2cf1ed12127ce3ac4",
"logoUrl": "/api/v1/assets/null",
"new": false
},
"applications": [
{
"id": "63ad2c166e7a9012ada704cb",
"modifiedBy": "test@sharklasers.com",
"userPermissions": [
"canComment:applications",
"read:applications"
],
"name": "Untitled application 1",
"workspaceId": "63ad2c156e7a9012ada704c7",
"isPublic": false,
"pages": [
{
"id": "63ad2c166e7a9012ada704ce",
"isDefault": true,
"slug": "page1",
"defaultPageId": "63ad2c166e7a9012ada704ce",
"default": true
}
],
"appIsExample": false,
"unreadCommentThreads": 0,
"color": "#FBF4ED",
"icon": "dollar",
"slug": "untitled-application-1",
"unpublishedCustomJSLibs": [],
"publishedCustomJSLibs": [],
"evaluationVersion": 2,
"applicationVersion": 2,
"isManualUpdate": true,
"isAutoUpdate": false,
"new": false,
"modifiedAt": "2022-12-29T05:56:41.772Z"
}
],
"users": [
{
"userId": "63abf1edcf1ed12127ce3af7",
"username": "test@sharklasers.com",
"name": "test@sharklasers.com",
"permissionGroupName": "Administrator - Untitled workspace 1",
"permissionGroupId": "63ad2c156e7a9012ada704c8"
},
{
"userId": "63abf1f3cf1ed12127ce3b01",
"username": "test1@sharklasers.com",
"name": "test1@sharklasers.com",
"permissionGroupName": "App Viewer - Untitled workspace 1",
"permissionGroupId": "63ad2c156e7a9012ada704ca"
}
]
}
],
"newReleasesCount": "1",
"releaseItems": [
{
"tagName": "v1.8.15",
"name": "Release v1.8.15 🌈",
"url": "https://github.com/appsmithorg/appsmith/releases/tag/v1.8.15",
"descriptionHtml": "<h2>💪🏾 Improvements</h2>\n<ul>\n<li>Were your larger applications starting to feel like they took forever to load? We've shipped out a performance improvement to load these pages faster! [<a href=\"https://github.com/appsmithorg/appsmith/pull/19273\" data-hovercard-type=\"pull_request\" data-hovercard-url=\"/appsmithorg/appsmith/pull/19273/hovercard\">#19273</a>]</li>\n</ul>",
"publishedAt": "2022-12-28T12:36:48Z"
}
]
}
}

View File

@ -0,0 +1,146 @@
{
"dsl": {
"widgetName": "MainContainer",
"backgroundColor": "none",
"rightColumn": 4896.0,
"snapColumns": 64.0,
"detachFromLayout": true,
"widgetId": "0",
"topRow": 0.0,
"bottomRow": 1292.0,
"containerStyle": "none",
"snapRows": 125.0,
"parentRowSpace": 1.0,
"type": "CANVAS_WIDGET",
"canExtend": true,
"version": 74.0,
"minHeight": 1292.0,
"dynamicTriggerPathList": [],
"parentColumnSpace": 1.0,
"dynamicBindingPathList": [],
"leftColumn": 0.0,
"children": [
{
"resetFormOnClick": false,
"boxShadow": "none",
"widgetName": "Button1",
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
"displayName": "Button",
"iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg",
"searchTags": ["click", "submit"],
"topRow": 8.0,
"bottomRow": 12.0,
"parentRowSpace": 10.0,
"type": "BUTTON_WIDGET",
"hideCard": false,
"animateLoading": true,
"parentColumnSpace": 10.0625,
"dynamicTriggerPathList": [],
"leftColumn": 7.0,
"dynamicBindingPathList": [
{ "key": "buttonColor" },
{ "key": "borderRadius" },
{ "key": "text" }
],
"text": "{{JSObject1.myFun1.data}}",
"isDisabled": false,
"key": "a1lbivvht8",
"isDeprecated": false,
"rightColumn": 23.0,
"isDefaultClickDisabled": true,
"widgetId": "wxkz5y9ypf",
"isVisible": true,
"recaptchaType": "V3",
"version": 1.0,
"parentId": "0",
"renderMode": "CANVAS",
"isLoading": false,
"disabledWhenInvalid": false,
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
"buttonVariant": "PRIMARY",
"placement": "CENTER"
},
{
"resetFormOnClick": false,
"boxShadow": "none",
"widgetName": "Button2",
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
"displayName": "Button",
"iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg",
"searchTags": ["click", "submit"],
"topRow": 8.0,
"bottomRow": 12.0,
"parentRowSpace": 10.0,
"type": "BUTTON_WIDGET",
"hideCard": false,
"animateLoading": true,
"parentColumnSpace": 10.0625,
"dynamicTriggerPathList": [],
"leftColumn": 39.0,
"dynamicBindingPathList": [
{ "key": "buttonColor" },
{ "key": "borderRadius" },
{ "key": "text" }
],
"text": "{{JSObject1.myFun2.data}}",
"isDisabled": false,
"key": "a1lbivvht8",
"isDeprecated": false,
"rightColumn": 55.0,
"isDefaultClickDisabled": true,
"widgetId": "ih110zq4c4",
"isVisible": true,
"recaptchaType": "V3",
"version": 1.0,
"parentId": "0",
"renderMode": "CANVAS",
"isLoading": false,
"disabledWhenInvalid": false,
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
"buttonVariant": "PRIMARY",
"placement": "CENTER"
},
{
"resetFormOnClick": false,
"boxShadow": "none",
"widgetName": "Button3",
"onClick": "{{JSObject1.myFun2()\n}}",
"buttonColor": "{{appsmith.theme.colors.primaryColor}}",
"dynamicPropertyPathList": [{ "key": "onClick" }],
"displayName": "Button",
"iconSVG": "/static/media/icon.cca026338f1c8eb6df8ba03d084c2fca.svg",
"searchTags": ["click", "submit"],
"topRow": 23.0,
"bottomRow": 27.0,
"parentRowSpace": 10.0,
"type": "BUTTON_WIDGET",
"hideCard": false,
"animateLoading": true,
"parentColumnSpace": 10.0625,
"dynamicTriggerPathList": [{ "key": "onClick" }],
"leftColumn": 24.0,
"dynamicBindingPathList": [
{ "key": "buttonColor" },
{ "key": "borderRadius" }
],
"text": "Submit",
"isDisabled": false,
"key": "a1lbivvht8",
"isDeprecated": false,
"rightColumn": 40.0,
"isDefaultClickDisabled": true,
"widgetId": "irxds8yzxq",
"isVisible": true,
"recaptchaType": "V3",
"version": 1.0,
"parentId": "0",
"renderMode": "CANVAS",
"isLoading": false,
"disabledWhenInvalid": false,
"borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}",
"buttonVariant": "PRIMARY",
"placement": "CENTER"
}
]
}
}

View File

@ -30,6 +30,7 @@
"type": "TEXT_WIDGET",
"hideCard": false,
"animateLoading": true,
"useSourceData": false,
"parentColumnSpace": 18.0625,
"leftColumn": 22,
"text": "Label",

View File

@ -30,6 +30,7 @@
"type": "TEXT_WIDGET",
"hideCard": false,
"animateLoading": true,
"useSourceData": false,
"parentColumnSpace": 18.0625,
"leftColumn": 22,
"text": "Label",

View File

@ -0,0 +1,46 @@
const dsl = require("../../../../fixtures/jsFunctionTriggerDsl.json");
import { ObjectsRegistry } from "../../../../support/Objects/Registry";
const agHelper = ObjectsRegistry.AggregateHelper;
const jsEditor = ObjectsRegistry.JSEditor;
const apiPage = ObjectsRegistry.ApiPage;
const ee = ObjectsRegistry.EntityExplorer;
describe("JS data update on button click", function() {
before(() => {
agHelper.AddDsl(dsl);
});
it("Populates js function data when triggered via button click", function() {
apiPage.CreateAndFillApi(
"https://jsonplaceholder.typicode.com/posts",
"Api1",
);
const jsObjectString = `export default {
myVar1: [],
myVar2: {},
myFun1: async () => {
//write code here
const data = await Api1.run()
return "myFun1 Data"
},
myFun2: async () => {
//use async-await or promises
await this.myFun1()
return "myFun2 Data"
}
}`;
jsEditor.CreateJSObject(jsObjectString, {
paste: true,
completeReplace: true,
toRun: false,
shouldCreateNewJSObj: true,
});
ee.SelectEntityByName("Button2", "Widgets");
agHelper.ClickButton("Submit");
agHelper.AssertContains("myFun1 Data", "exist");
agHelper.AssertContains("myFun2 Data", "exist");
});
});

View File

@ -1,24 +0,0 @@
import { ObjectsRegistry } from "../../../../support/Objects/Registry";
let dsName: any;
const agHelper = ObjectsRegistry.AggregateHelper,
dataSources = ObjectsRegistry.DataSources;
describe("Bug 18664: datasource unsaved changes popup shows even without changes", function() {
it("1. Create postgres datasource, save it and edit it and go back, now unsaved changes popup should not be shown", () => {
dataSources.NavigateToDSCreateNew();
agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
dataSources.CreatePlugIn("PostgreSQL");
dsName = "Postgres" + uid;
agHelper.RenameWithInPane(dsName, false);
dataSources.SaveDatasource();
cy.wait(1000);
dataSources.EditDatasource();
agHelper.GoBack();
agHelper.AssertElementVisible(dataSources._activeDS);
dataSources.DeleteDatasouceFromActiveTab(dsName);
});
});
});

View File

@ -0,0 +1,12 @@
import { ObjectsRegistry } from "../../../../support/Objects/Registry";
const dataSources = ObjectsRegistry.DataSources;
describe("Testing empty datasource without saving should not throw 404", function() {
it("Bug 19426: Create empty S3 datasource, test it", function() {
dataSources.NavigateToDSCreateNew();
dataSources.CreatePlugIn("S3");
dataSources.TestDatasource(false);
dataSources.SaveDSFromDialog(false);
});
});

View File

@ -0,0 +1,96 @@
import { ObjectsRegistry } from "../../../../support/Objects/Registry";
let dsName: any;
const agHelper = ObjectsRegistry.AggregateHelper,
dataSources = ObjectsRegistry.DataSources;
const testString = "test";
describe("datasource unsaved changes popup shows even without changes", function() {
// In case of postgres and other plugins, host address and port key values are initialized by default making form dirty
it("1. Bug 18664: Create postgres datasource, save it and edit it and go back, now unsaved changes popup should not be shown", () => {
dataSources.NavigateToDSCreateNew();
agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
// using CreatePlugIn function instead of CreateDatasource,
// because I do not need to fill the datasource form and use the same default data
dataSources.CreatePlugIn("PostgreSQL");
dsName = "Postgres" + uid;
agHelper.RenameWithInPane(dsName, false);
dataSources.SaveDatasource();
agHelper.Sleep();
dataSources.EditDatasource();
agHelper.GoBack();
agHelper.AssertElementVisible(dataSources._activeDS);
dataSources.DeleteDatasouceFromActiveTab(dsName);
});
});
// In case of Auth DS, headers, query parameters and custom query parameters are being initialized, which makes form dirty
it("2. Bug 18962: Create REST API datasource, save it and edit it and go back, now unsaved changes popup should not be shown", () => {
dataSources.NavigateToDSCreateNew();
agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
// using CreatePlugIn function instead of CreateDatasource,
// because I do not need to fill the datasource form and use the same default data
dataSources.CreatePlugIn("Authenticated API");
dsName = "AuthDS" + uid;
agHelper.RenameWithInPane(dsName, false);
dataSources.FillAuthAPIUrl();
dataSources.SaveDatasource();
agHelper.Sleep();
// Edit DS for the first time, we shouldnt see discard popup on back button
// Even if headers, and query parameters are being initialized, we shouldnt see the popup
// as those are not initialized by user
dataSources.EditDatasource();
agHelper.GoBack();
agHelper.AssertElementVisible(dataSources._activeDS);
// Edit DS from active tab and add oauth2 details
dataSources.EditDSFromActiveTab(dsName);
dataSources.AddOAuth2AuthorizationCodeDetails(
testString,
testString,
testString,
testString,
);
dataSources.UpdateDatasource();
agHelper.Sleep();
// Now edit DS, and ensure that discard popup is not shown on back button click
// Even if custom authentication params are being initialized, we shouldnt see the popup
// as those are not initialized by user
dataSources.EditDatasource();
agHelper.GoBack();
agHelper.AssertElementVisible(dataSources._activeDS);
dataSources.DeleteDatasouceFromActiveTab(dsName);
});
});
it("3. Bug 18998: Create mongoDB datasource, save it and edit it and change connection URI param, discard popup should be shown as user has made valid change", () => {
dataSources.NavigateToDSCreateNew();
agHelper.GenerateUUID();
cy.get("@guid").then((uid) => {
// using CreatePlugIn function instead of CreateDatasource,
// because I do not need to fill the datasource form and use the same default data
dataSources.CreatePlugIn("MongoDB");
dsName = "Mongo" + uid;
agHelper.RenameWithInPane(dsName, false);
dataSources.FillMongoDSForm();
dataSources.SaveDatasource();
agHelper.Sleep();
// Edit datasource, change connection string uri param and click on back button
dataSources.EditDatasource();
dataSources.FillMongoDatasourceFormWithURI(testString);
// Assert that popup is visible
dataSources.SaveDSFromDialog(false);
dataSources.DeleteDatasouceFromActiveTab(dsName);
});
});
});

View File

@ -166,7 +166,7 @@ describe("MaintainContext&Focus", function() {
it("9. Datasource edit mode has to be maintained", () => {
ee.SelectEntityByName("Appsmith", "Datasources");
dataSources.EditDatasource();
dataSources.SaveDSFromDialog(false);
agHelper.GoBack();
ee.SelectEntityByName("Github", "Datasources");
dataSources.AssertViewMode();
ee.SelectEntityByName("Appsmith", "Datasources");
@ -175,7 +175,7 @@ describe("MaintainContext&Focus", function() {
it("10. Datasource collapse state has to be maintained", () => {
// Create datasource 1
dataSources.SaveDSFromDialog(false);
agHelper.GoBack();
dataSources.NavigateToDSCreateNew();
dataSources.CreatePlugIn("PostgreSQL");
agHelper.RenameWithInPane("Postgres1", false);
@ -197,7 +197,7 @@ describe("MaintainContext&Focus", function() {
dataSources.AssertSectionCollapseState(1, false);
});
it("10. Maintain focus of form control inputs", () => {
it("11. Maintain focus of form control inputs", () => {
ee.SelectEntityByName("SQL_Query");
dataSources.ToggleUsePreparedStatement(false);
cy.SearchEntityandOpen("S3_Query");
@ -207,7 +207,8 @@ describe("MaintainContext&Focus", function() {
cy.SearchEntityandOpen("SQL_Query");
cy.get(".t--form-control-SWITCH input").should("be.focused");
cy.SearchEntityandOpen("S3_Query");
cy.get(queryLocators.querySettingsTab).click();
agHelper.Sleep();
agHelper.GetNClick(dataSources._queryResponse("SETTINGS"));
cy.xpath(queryLocators.queryTimeout).should("be.focused");
});
});

View File

@ -12,7 +12,7 @@ describe("Creating new app after discontinuing guided tour should not start the
cy.get(commonlocators.homeIcon)
.click({ force: true })
.wait(8000); //for page to settle!
datasources.CloseReconnectDataSourceModal() // Check if reconnect data source modal is visible and close it
datasources.CloseReconnectDataSourceModal(); // Check if reconnect data source modal is visible and close it
cy.get(guidedTourLocators.welcomeTour)
.click()
.wait(2000);

View File

@ -1,5 +1,7 @@
const commonlocators = require("../../../../locators/commonlocators.json");
const templateLocators = require("../../../../locators/TemplatesLocators.json");
import { ObjectsRegistry } from "../../../../support/Objects/Registry";
const { AggregateHelper, HomePage } = ObjectsRegistry;
describe("Fork a template to an workspace", () => {
it("Fork a template to an workspace", () => {
@ -30,4 +32,21 @@ describe("Fork a template to an workspace", () => {
expect(location.search).to.eq("?showForkTemplateModal=true");
});
});
it("Hide template fork button if user does not have a valid workspace to fork", () => {
HomePage.NavigateToHome();
// Mock user with App Viewer permission
cy.intercept("/api/v1/applications/new", {
fixture: "Templates/MockAppViewerUser.json",
});
AggregateHelper.RefreshPage();
HomePage.SwitchToTemplatesTab();
AggregateHelper.AssertElementExist(templateLocators.templateCard);
AggregateHelper.AssertElementAbsence(templateLocators.templateForkButton);
AggregateHelper.GetNClick(templateLocators.templateCard);
AggregateHelper.AssertElementExist(templateLocators.templateCard);
AggregateHelper.AssertElementAbsence(
templateLocators.templateViewForkButton,
);
});
});

View File

@ -819,8 +819,8 @@ describe("App Theming funtionality", function() {
//Change Border & verify
cy.get(".t--button-tab-0px").click();
cy.get(".t--button-tab-0px")
cy.get(".t--button-group-0px").click();
cy.get(".t--button-group-0px")
.eq(0)
.invoke("css", "border-top-left-radius")
.then((borderRadius) => {
@ -842,8 +842,8 @@ describe("App Theming funtionality", function() {
});
//Change Shadow & verify
cy.get(".t--button-tab-0.10px").click();
cy.get(".t--button-tab-0.10px div")
cy.get(".t--button-group-0.10px").click();
cy.get(".t--button-group-0.10px div")
.eq(0)
.invoke("css", "box-shadow")
.then((boxshadow) => {
@ -1051,10 +1051,10 @@ describe("App Theming funtionality", function() {
//Change Border & verify
cy.get(".t--button-tab-0\\.375rem")
cy.get(".t--button-group-0\\.375rem")
.click()
.wait(500);
cy.get(".t--button-tab-0\\.375rem div")
cy.get(".t--button-group-0\\.375rem div")
.eq(0)
.invoke("css", "border-top-left-radius")
.then((borderRadius) => {
@ -1076,10 +1076,10 @@ describe("App Theming funtionality", function() {
});
//Change Shadow & verify
cy.get(".t--button-tab-0.1px")
cy.get(".t--button-group-0.1px")
.click()
.wait(500);
cy.get(".t--button-tab-0.1px div")
cy.get(".t--button-group-0.1px div")
.invoke("css", "box-shadow")
.then((boxshadow) => {
cy.get(widgetsPage.widgetBtn)

View File

@ -60,7 +60,7 @@ describe("In a button group widget, menu button width", function() {
const menuButtonId = "groupButton1";
// Change the first button type to menu
cy.editColumn(menuButtonId);
cy.get(".t--button-tab-MENU").click({ force: true });
cy.get(".t--button-group-MENU").click({ force: true });
cy.get(".t--add-menu-item-btn").click();
// Get the newly converted menu button
cy.get(`.appsmith_widget_${widgetId} div.t--buttongroup-widget`)
@ -126,7 +126,7 @@ describe("In a button group widget, menu button width", function() {
.click();
cy.moveToStyleTab();
// Change its orientation to vetical
cy.get(".t--button-tab-vertical").click({ force: true });
cy.get(".t--button-group-vertical").click({ force: true });
// Get the default menu button
cy.get(`.appsmith_widget_${widgetId} div.t--buttongroup-widget`)
.children()

View File

@ -97,7 +97,7 @@ describe("Button Group Widget Functionality", function() {
it("Update icon alignment and Verify buttons alignments", function() {
// align right
cy.get(".t--property-control-position .t--button-tab-left")
cy.get(".t--property-control-position .t--button-group-left")
.first()
.click();
cy.wait(200);
@ -106,7 +106,7 @@ describe("Button Group Widget Functionality", function() {
.eq(1)
.should("have.css", "flex-direction", "row");
// align left
cy.get(".t--property-control-position .t--button-tab-right")
cy.get(".t--property-control-position .t--button-group-right")
.last()
.click();
cy.wait(200);

View File

@ -31,7 +31,7 @@ describe("Button Widget Functionality", function() {
// Assert if the icon exists
cy.get(`${widgetsPage.buttonWidget} .bp3-icon-add`).should("exist");
// Change icon alignment to right
cy.get(`${iconAlignmentProperty} .t--button-tab-right`)
cy.get(`${iconAlignmentProperty} .t--button-group-right`)
.last()
.click({
force: true,

View File

@ -119,7 +119,7 @@ describe("Code Scanner widget's functionality", () => {
// Select scanner layout as CLICK_TO_SCAN
cy.get(
`${commonlocators.codeScannerScannerLayout} .t--button-tab-CLICK_TO_SCAN`,
`${commonlocators.codeScannerScannerLayout} .t--button-group-CLICK_TO_SCAN`,
)
.last()
.click({

View File

@ -34,7 +34,7 @@ describe("Checkbox Widget Functionality", function() {
);
// align right
cy.get(".t--property-control-alignment .t--button-tab-RIGHT")
cy.get(".t--property-control-alignment .t--button-group-RIGHT")
.first()
.click();
cy.get(publish.checkboxWidget + " " + ".t--checkbox-widget-label").should(
@ -63,7 +63,7 @@ describe("Checkbox Widget Functionality", function() {
.last()
.click({ force: true });
cy.wait(200);
cy.get(".t--button-tab-Left").click({ force: true });
cy.get(".t--button-group-Left").click({ force: true });
cy.wait(200);
cy.PublishtheApp();
@ -110,7 +110,7 @@ describe("Checkbox Widget Functionality", function() {
it("6. Checkbox Functionality To change label style of checkbox", function() {
cy.openPropertyPane("checkboxwidget");
cy.moveToStyleTab();
cy.get(".t--property-control-emphasis .t--button-tab-BOLD").click({
cy.get(".t--property-control-emphasis .t--button-group-BOLD").click({
force: true,
});
cy.PublishtheApp();

View File

@ -190,4 +190,54 @@ describe("JSON Form Widget Form Bindings", () => {
cy.get(publishPage.backToEditor).click({ force: true });
});
it("8. Form value should contain hidden fields value if useSourceData is set to true", () => {
cy.addDsl(dslWithoutSchema);
const schema = {
name: "",
age: 10,
};
cy.openPropertyPane("jsonformwidget");
cy.testJsontext("sourcedata", JSON.stringify(schema));
cy.togglebar(`${propertyControlPrefix}-usesourcedata input`);
cy.openFieldConfiguration("age");
cy.togglebarDisable(`${propertyControlPrefix}-visible input`);
cy.openPropertyPane("textwidget");
cy.testJsontext("text", "{{JSON.stringify(JSONForm1.formData)}}");
cy.get(".t--widget-textwidget .bp3-ui-text").contains(
JSON.stringify(schema),
);
});
it("9. Form value should not contain hidden fields value if useSourceData is set to false", () => {
cy.addDsl(dslWithoutSchema);
const name = "JOHN";
const schema = {
name,
age: 10,
};
cy.openPropertyPane("jsonformwidget");
cy.testJsontext("sourcedata", JSON.stringify(schema));
cy.togglebarDisable(`${propertyControlPrefix}-usesourcedata input`);
cy.openFieldConfiguration("age");
cy.togglebarDisable(`${propertyControlPrefix}-visible input`);
cy.openPropertyPane("textwidget");
cy.testJsontext("text", "{{JSON.stringify(JSONForm1.formData)}}");
cy.get(".t--widget-textwidget .bp3-ui-text").contains(
JSON.stringify({ name }),
);
});
});

View File

@ -23,7 +23,7 @@ describe("Menu Button Widget Functionality", () => {
// Assert if the icon exists
cy.get(`${formWidgetsPage.menuButtonWidget} .bp3-icon-add`).should("exist");
// Change its icon alignment to right
cy.get(".t--property-control-position .t--button-tab-right")
cy.get(".t--property-control-position .t--button-group-right")
.last()
.click({ force: true });
cy.wait(200);
@ -110,7 +110,9 @@ describe("Menu Button Widget Functionality", () => {
cy.moveToContentTab();
// Select menu items source as Dynamic
cy.get(`${commonlocators.menuButtonMenuItemsSource} .t--button-tab-DYNAMIC`)
cy.get(
`${commonlocators.menuButtonMenuItemsSource} .t--button-group-DYNAMIC`,
)
.last()
.click({
force: true,

View File

@ -66,7 +66,7 @@ describe("Progress Widget", function() {
// Circular progress
it("Property: type, Change type to Circular", function() {
// Switch to circular mode
cy.get(".t--button-tab-circular").click({ force: true });
cy.get(".t--button-group-circular").click({ force: true });
cy.get("[data-cy='circular']").should("exist");
});
it("Property: isIndeterminate, Toggle infinite loading", function() {

View File

@ -246,10 +246,10 @@ describe("RichTextEditor Widget Functionality", function() {
// Changing the input type to markdown and again testing the cursor position
cy.openPropertyPane("richtexteditorwidget");
cy.get(".t--button-tab-markdown").click({ force: true });
cy.get(".t--button-group-markdown").click({ force: true });
setRTEContent(testString);
testCursorPoistion(testStringLen, tinyMceId);
cy.get(".t--button-tab-html").click({ force: true });
cy.get(".t--button-group-html").click({ force: true });
});
it("13. Check if different font size texts are supported inside the RTE widget", function() {

View File

@ -84,7 +84,7 @@ describe("Switch Widget Functionality", function() {
"left",
);
// align right
cy.get(".t--property-control-alignment .t--button-tab-RIGHT")
cy.get(".t--property-control-alignment .t--button-group-RIGHT")
.first()
.click();
cy.wait(200);
@ -106,7 +106,7 @@ describe("Switch Widget Functionality", function() {
.last()
.click({ force: true });
cy.wait(200);
cy.get(".t--button-tab-Left").click({ force: true });
cy.get(".t--button-group-Left").click({ force: true });
cy.wait(200);
cy.PublishtheApp();
cy.get(publish.switchwidget + " " + ".bp3-align-right").should("exist");
@ -150,7 +150,7 @@ describe("Switch Widget Functionality", function() {
it("11. Switch Functionality To change label style of switch", function() {
cy.openPropertyPane("switchwidget");
cy.moveToStyleTab();
cy.get(".t--property-control-emphasis .t--button-tab-BOLD").click({
cy.get(".t--property-control-emphasis .t--button-group-BOLD").click({
force: true,
});
cy.PublishtheApp();

View File

@ -14,17 +14,17 @@ describe("Table Widget Image Resize feature validation", function() {
cy.editColumn("image");
cy.moveToStyleTab();
cy.get(".t--button-tab-MEDIUM").click();
cy.get(".t--button-group-MEDIUM").click();
cy.getTableV2DataSelector("1", "3").then((selector) => {
cy.get(`${selector} img`).should("have.css", "height", "64px");
});
cy.get(".t--button-tab-LARGE").click();
cy.get(".t--button-group-LARGE").click();
cy.getTableV2DataSelector("1", "3").then((selector) => {
cy.get(`${selector} img`).should("have.css", "height", "128px");
});
cy.get(".t--button-tab-DEFAULT").click();
cy.get(".t--button-group-DEFAULT").click();
cy.getTableV2DataSelector("1", "3").then((selector) => {
cy.get(`${selector} img`).should("have.css", "height", "32px");
});
@ -47,17 +47,17 @@ describe("Table Widget Image Resize feature validation", function() {
cy.editColumn("image");
cy.moveToStyleTab();
cy.get(".t--button-tab-MEDIUM").click();
cy.get(".t--button-group-MEDIUM").click();
cy.getTableV2DataSelector("1", "3").then((selector) => {
cy.get(`${selector} img`).should("have.css", "height", "64px");
});
cy.get(".t--button-tab-LARGE").click();
cy.get(".t--button-group-LARGE").click();
cy.getTableV2DataSelector("1", "3").then((selector) => {
cy.get(`${selector} img`).should("have.css", "height", "128px");
});
cy.get(".t--button-tab-DEFAULT").click();
cy.get(".t--button-group-DEFAULT").click();
cy.getTableV2DataSelector("1", "3").then((selector) => {
cy.get(`${selector} img`).should("have.css", "height", "32px");
});

View File

@ -307,26 +307,26 @@ describe("Table widget inline editing functionality", () => {
cy.get(".t--property-control-updatemode .bp3-popover-target")
.last()
.click();
cy.get(".t--button-tab-CUSTOM").click({ force: true });
cy.get(".t--button-group-CUSTOM").click({ force: true });
cy.get("[data-rbd-draggable-id='EditActions1']").should("not.exist");
cy.makeColumnEditable("task");
cy.get("[data-rbd-draggable-id='EditActions1']").should("not.exist");
cy.get(".t--property-control-updatemode .bp3-popover-target")
.last()
.click();
cy.get(".t--button-tab-ROW_LEVEL").click({ force: true });
cy.get(".t--button-group-ROW_LEVEL").click({ force: true });
cy.get("[data-rbd-draggable-id='EditActions1']").should("exist");
cy.get(".t--property-control-updatemode .bp3-popover-target")
.last()
.click();
cy.get(".t--button-tab-CUSTOM").click({ force: true });
cy.get(".t--button-group-CUSTOM").click({ force: true });
cy.get("[data-rbd-draggable-id='EditActions1']").should("not.exist");
cy.makeColumnEditable("step");
cy.makeColumnEditable("task");
cy.get(".t--property-control-updatemode .bp3-popover-target")
.last()
.click();
cy.get(".t--button-tab-ROW_LEVEL").click({ force: true });
cy.get(".t--button-group-ROW_LEVEL").click({ force: true });
cy.get("[data-rbd-draggable-id='EditActions1']").should("not.exist");
});
@ -787,7 +787,7 @@ describe("Table widget inline editing functionality", () => {
cy.openPropertyPane("tablewidgetv2");
cy.makeColumnEditable("step");
cy.get(".t--button-tab-CUSTOM").click({ force: true });
cy.get(".t--button-group-CUSTOM").click({ force: true });
cy.wait(1000);
// case 1: check if updatedRowIndex is 0, when cell at row 0 is updated.

View File

@ -186,7 +186,7 @@ describe("Table Widget property pane feature validation", function() {
cy.get(widgetsPage.rowHeight)
.last()
.click({ force: true });
cy.get(".t--button-tab-SHORT").click({ force: true });
cy.get(".t--button-group-SHORT").click({ force: true });
cy.wait(2000);
cy.PublishtheApp();
cy.readTableV2dataValidateCSS("0", "1", "height", "29px", true);

View File

@ -195,7 +195,7 @@ describe("Table Widget V2 Functionality", function() {
cy.get(".t--property-pane-back-btn").click();
cy.makeColumnEditable("step");
cy.get(".t--button-tab-ROW_LEVEL").click();
cy.get(".t--button-group-ROW_LEVEL").click();
cy.get(".t--table-filter-columns-dropdown").click();
cy.get(".t--dropdown-option").should("not.contain", "Save / Discard");
});

View File

@ -30,7 +30,29 @@ describe("Table Widget text wrapping functionality", function() {
});
});
it("2. should check that other cells in the row is not wrapped when one of the cell is wrapped", () => {
it("2. should check that a tooltip is shown when hovered on a ellipsised content", () => {
cy.get(
`.td[data-colindex=2][data-rowindex=0] .t--table-cell-tooltip-target`,
).trigger("mouseenter");
cy.get(".bp3-tooltip").should("exist");
cy.get(".bp3-tooltip .bp3-popover-content").should(
"contain",
"michael.lawson@reqres.in",
);
cy.get(
`.td[data-colindex=2][data-rowindex=1] .t--table-cell-tooltip-target`,
).trigger("mouseenter", { force: true });
cy.get(".bp3-tooltip").should("exist");
cy.get(".bp3-tooltip .bp3-popover-content").should(
"contain",
"lindsay.ferguson@reqres.in",
);
});
it("3. should check that other cells in the row is not wrapped when one of the cell is wrapped", () => {
cy.getTableCellHeight(2, 0).then((height) => {
expect(height).to.equal("28px");
});
@ -52,7 +74,7 @@ describe("Table Widget text wrapping functionality", function() {
});
});
it("3. should check that cell wrapping option is only available for plain text, number, date and URL", () => {
it("4. should check that cell wrapping option is only available for plain text, number, date and URL", () => {
cy.openPropertyPane("tablewidgetv2");
cy.editColumn("email");
[
@ -105,7 +127,7 @@ describe("Table Widget text wrapping functionality", function() {
});
});
it("4. should check that plain text, number, date and URL column is getting wrapped when cell wrapping is enabled", () => {
it("5. should check that plain text, number, date and URL column is getting wrapped when cell wrapping is enabled", () => {
cy.openPropertyPane("tablewidgetv2");
cy.editColumn("id");
@ -134,7 +156,7 @@ describe("Table Widget text wrapping functionality", function() {
});
});
it("5. should check that pageSize does not change when cell wrapping is enabled", () => {
it("6. should check that pageSize does not change when cell wrapping is enabled", () => {
cy.openPropertyPane("tablewidgetv2");
cy.editColumn("image");
let pageSizeBeforeWrapping;

View File

@ -71,7 +71,7 @@ describe("Checkbox column type funtionality test", () => {
.contains("STYLE")
.click({ force: true });
// Check horizontal alignment
cy.get(".t--property-control-horizontalalignment .t--button-tab-CENTER")
cy.get(".t--property-control-horizontalalignment .t--button-group-CENTER")
.first()
.click();
@ -80,7 +80,7 @@ describe("Checkbox column type funtionality test", () => {
});
// Check vertical alignment
cy.get(".t--property-control-verticalalignment .t--button-tab-BOTTOM")
cy.get(".t--property-control-verticalalignment .t--button-group-BOTTOM")
.first()
.click();

View File

@ -71,7 +71,7 @@ describe("Switch column type funtionality test", () => {
.contains("STYLE")
.click({ force: true });
// Check horizontal alignment
cy.get(".t--property-control-horizontalalignment .t--button-tab-CENTER")
cy.get(".t--property-control-horizontalalignment .t--button-group-CENTER")
.first()
.click();
@ -80,7 +80,7 @@ describe("Switch column type funtionality test", () => {
});
// Check vertical alignment
cy.get(".t--property-control-verticalalignment .t--button-tab-BOTTOM")
cy.get(".t--property-control-verticalalignment .t--button-group-BOTTOM")
.first()
.click();

View File

@ -11,13 +11,13 @@ describe("Table widget v2", function() {
cy.openPropertyPane("tablewidgetv2");
cy.moveToStyleTab();
cy.get(".t--button-tab-SHORT").click({ force: true });
cy.get(".t--button-group-SHORT").click({ force: true });
cy.get(".t--widget-textwidget .bp3-ui-text").should("contain", "7");
cy.get(".t--button-tab-DEFAULT").click({ force: true });
cy.get(".t--button-group-DEFAULT").click({ force: true });
cy.get(".t--widget-textwidget .bp3-ui-text").should("contain", "5");
cy.get(".t--button-tab-TALL").click({ force: true });
cy.get(".t--button-group-TALL").click({ force: true });
cy.get(".t--widget-textwidget .bp3-ui-text").should("contain", "4");
});
});

View File

@ -137,7 +137,7 @@ describe("Text Widget color/font/alignment Functionality", function() {
it("Test to validate enable scroll feature", function() {
cy.moveToContentTab();
cy.get(".t--button-tab-SCROLL").click({ force: true });
cy.get(".t--button-group-SCROLL").click({ force: true });
cy.wait("@updateLayout");
cy.get(commonlocators.headingTextStyle).trigger("mouseover", {
force: true,

View File

@ -8,7 +8,7 @@ describe("Text Widget Truncate Functionality", function() {
it("Check default overflow property is No overflow", function() {
cy.openPropertyPane("textwidget");
cy.get(".t--button-tab-NONE")
cy.get(".t--button-group-NONE")
.last()
.should("have.attr", "aria-selected", "true");
cy.closePropertyPane();
@ -34,7 +34,7 @@ describe("Text Widget Truncate Functionality", function() {
it("Enable Truncate Text option and Validate", function() {
cy.wait(2000);
cy.get("body").type("{esc}");
cy.get(".t--button-tab-TRUNCATE").click({ force: true });
cy.get(".t--button-group-TRUNCATE").click({ force: true });
cy.wait("@updateLayout");
cy.get(
`.appsmith_widget_${dsl.dsl.children[0].widgetId} .t--widget-textwidget-truncate`,

View File

@ -166,4 +166,4 @@ describe("Widget Copy paste", function() {
//verify a pasted list widget
cy.get(widgetsPage.listWidget).should("have.length", 1);
});
});
});

View File

@ -63,10 +63,8 @@ describe("MySQL noise test", function() {
expect(response.body.data.statusCode).to.eq("200 OK");
});
cy.wait("@postExecute").then(({ response }) => {
expect(response.body.data.statusCode).to.eq("5004");
expect(response.body.data.title).to.eq(
"Datasource configuration is invalid",
);
expect(response.body.data.statusCode).to.eq("5000");
expect(response.body.data.title).to.eq("Query execution error");
});
});
});

View File

@ -437,13 +437,15 @@ describe("JS Function Execution", function() {
it("9. Bug 13197: Verify converting async functions to sync resets all settings", () => {
const asyncJSCode = `export default {
name: "Appsmith",
asyncToSync : async ()=>{
return "yes";`;
const syncJSCode = `export default {
name: "Appsmith",
asyncToSync : ()=>{
return "yes";
}
},
}`;
jsEditor.CreateJSObject(asyncJSCode, {

View File

@ -40,7 +40,7 @@ describe("JSObjects OnLoad Actions tests", function() {
AssertJSOnPageLoad(
"runWorldCountries",
false,
"UncaughtPromiseRejection: getWorldCountries is not defined",
"ReferenceError: getWorldCountries is not defined",
);
});
});
@ -51,7 +51,7 @@ describe("JSObjects OnLoad Actions tests", function() {
AssertJSOnPageLoad(
"runWorldCountries",
false,
"UncaughtPromiseRejection: getWorldCountries is not defined",
"ReferenceError: getWorldCountries is not defined",
);
homePage.NavigateToHome();
@ -59,7 +59,7 @@ describe("JSObjects OnLoad Actions tests", function() {
AssertJSOnPageLoad(
"runWorldCountries",
false,
"UncaughtPromiseRejection: getWorldCountries is not defined",
"ReferenceError: getWorldCountries is not defined",
);
});
@ -244,7 +244,7 @@ describe("JSObjects OnLoad Actions tests", function() {
function AssertJSOnPageLoad(
jsMethod: string,
shouldCheckImport = false,
faliureMsg: string = "",
faliureMsg = "",
) {
agHelper.AssertElementVisible(
jsEditor._dialogBody("JSObject1." + jsMethod),

View File

@ -40,7 +40,7 @@ describe("Cyclic Dependency Informational Error Messagaes", function() {
.last()
.find(`${datasource.createQuery}`)
.click({ force: true });
//Step5.1: Click the editing field
cy.get(".t--action-name-edit-field").click({ force: true });
@ -170,4 +170,4 @@ describe("Cyclic Dependency Informational Error Messagaes", function() {
1,
);
});
});
});

View File

@ -58,7 +58,7 @@
"deleteWidget": ".t--modal-widget>div .t--widget-delete-control",
"textbuttonWidget": ".t--draggable-buttonwidget button.bp3-button[type='button']",
"textInputval": ".t--draggable-textwidget span.t--widget-name",
"textCenterAlign": ".t--property-control-alignment .t--button-tab-CENTER",
"textCenterAlign": ".t--property-control-alignment .t--button-group-CENTER",
"ColumnAction": ".t--property-control-rowbutton button",
"SearchTextChangeAction": ".t--property-control-onsearchtextchanged button",
"tableSearchTextChangeSelected": ".t--property-control-onsearchtextchanged",
@ -86,15 +86,15 @@
"editCreatedColumn": ".t--property-control-createdcolumns input",
"alignOpt": ".t--dropdown-option",
"tableCol": ".draggable-header ",
"centerAlign": ".t--button-tab-CENTER",
"rightAlign": ".t--button-tab-RIGHT",
"leftAlign": ".t--button-tab-LEFT",
"bold": ".t--button-tab-BOLD",
"italics": ".t--button-tab-ITALIC",
"underline": ".t--button-tab-UNDERLINE",
"verticalTop": ".t--button-tab-TOP",
"verticalCenter": ".t--button-tab-CENTER",
"verticalBottom": ".t--button-tab-BOTTOM",
"centerAlign": ".t--button-group-CENTER",
"rightAlign": ".t--button-group-RIGHT",
"leftAlign": ".t--button-group-LEFT",
"bold": ".t--button-group-BOLD",
"italics": ".t--button-group-ITALIC",
"underline": ".t--button-group-UNDERLINE",
"verticalTop": ".t--button-group-TOP",
"verticalCenter": ".t--button-group-CENTER",
"verticalBottom": ".t--button-group-BOTTOM",
"textColor": ".t--property-control-textcolor input",
"boadercolorPicker": ".t--property-control-bordercolour input",
"boxShadowColorPicker": ".t--property-control-shadowcolor input",

View File

@ -219,7 +219,7 @@ const mySqlData = {
["a", "abcdefghijklmnopqrst", "12345678912345", "true", "null"],
["a", "abcdefghij", "012345", "false", "NulL"],
["a", "b", "c"],
["0", "1"],
["false", "true"],
[{"abc": "123"}, {}, [1, 2, 3, 4], [], ["a",true,0,12.34]],
],
query: {

View File

@ -579,8 +579,8 @@ export class AggregateHelper {
cy.get(selector)
.contains(containsText)
.eq(index)
.click()
.wait(200);
.click({ force: true })
.wait(500);
}
public CheckUncheck(selector: string, check = true) {

View File

@ -115,6 +115,22 @@ export class DataSources {
private _queryTimeout =
"//input[@name='actionConfiguration.timeoutInMillisecond']";
_getStructureReq = "/api/v1/datasources/*/structure?ignoreCache=true";
_editDatasourceFromActiveTab = (dsName: string) =>
".t--datasource-name:contains('" + dsName + "')";
private _urlInputControl = "input[name='url']";
// Authenticated API locators
private _authType = "[data-cy=authType]";
private _oauth2 = ".t--dropdown-option:contains('OAuth 2.0')";
private _accessTokenUrl = "[data-cy='authentication.accessTokenUrl'] input";
private _clientID = "[data-cy='authentication.clientId'] input";
private _clientSecret = "[data-cy='authentication.clientSecret'] input";
private _authorizationCode =
".t--dropdown-option:contains('Authorization Code')";
private _grantType = "[data-cy='authentication.grantType']";
private _authorizationURL =
"[data-cy='authentication.authorizationUrl'] input";
public _datasourceModalSave = ".t--datasource-modal-save";
public _datasourceModalDoNotSave = ".t--datasource-modal-do-not-save";
@ -372,7 +388,9 @@ export class DataSources {
public TestDatasource(expectedRes = true) {
this.agHelper.GetNClick(this._testDs, 0, false, 0);
this.agHelper.ValidateNetworkDataSuccess("@testDatasource", expectedRes);
this.agHelper.AssertContains("datasource is valid");
if (expectedRes) {
this.agHelper.AssertContains("datasource is valid");
}
}
public SaveDatasource() {
@ -391,7 +409,7 @@ export class DataSources {
this.agHelper.ValidateNetworkStatus("@saveDatasource", 201);
}
public updateDatasource() {
public UpdateDatasource() {
this.agHelper.GetNClick(this._saveDs);
// this.agHelper.ValidateNetworkStatus("@updateDatasource", 200);
this.agHelper.AssertContains("datasource updated");
@ -519,11 +537,11 @@ export class DataSources {
}
public CloseReconnectDataSourceModal() {
cy.get('body').then(($ele) =>{
if($ele.find(this._reconnectDataSourceModal).length){
this.agHelper.GetNClick(this._closeDataSourceModal)
cy.get("body").then(($ele) => {
if ($ele.find(this._reconnectDataSourceModal).length) {
this.agHelper.GetNClick(this._closeDataSourceModal);
}
})
});
}
RunQuery(
@ -760,7 +778,7 @@ export class DataSources {
) {
cy.intercept("GET", this._getStructureReq).as("getDSStructure");
if (isUpdate) {
this.updateDatasource();
this.UpdateDatasource();
} else {
this.SaveDatasource();
}
@ -772,6 +790,8 @@ export class DataSources {
public SaveDSFromDialog(save = true) {
this.agHelper.GoBack();
this.agHelper.AssertElementVisible(this._datasourceModalDoNotSave);
this.agHelper.AssertElementVisible(this._datasourceModalSave);
if (save) {
this.agHelper.GetNClick(
this.locator._visibleTextSpan("SAVE"),
@ -790,7 +810,40 @@ export class DataSources {
);
}
public getDSEntity(dSName: string){
public getDSEntity(dSName: string) {
return `[data-guided-tour-id="explorer-entity-${dSName}"]`;
}
public FillAuthAPIUrl() {
const URL = datasourceFormData["authenticatedApiUrl"];
this.agHelper.TypeText(this._urlInputControl, URL);
}
public AddOAuth2AuthorizationCodeDetails(
accessTokenUrl: string,
clientId: string,
clientSecret: string,
authURL: string,
) {
this.agHelper.GetNClick(this._authType);
this.agHelper.GetNClick(this._oauth2);
this.agHelper.GetNClick(this._grantType);
this.agHelper.GetNClick(this._authorizationCode);
this.agHelper.TypeText(this._accessTokenUrl, accessTokenUrl);
this.agHelper.TypeText(this._clientID, clientId);
this.agHelper.TypeText(this._clientSecret, clientSecret);
this.agHelper.TypeText(this._authorizationURL, authURL);
}
public EditDSFromActiveTab(dsName: string) {
this.agHelper.GetNClick(this._editDatasourceFromActiveTab(dsName));
}
public FillMongoDatasourceFormWithURI(uri: string) {
this.ValidateNSelectDropdown("Use Mongo Connection String URI", "", "Yes");
this.agHelper.TypeText(
this.locator._inputFieldByName("Connection String URI") + "//input",
uri,
);
}
}

View File

@ -80,6 +80,16 @@ export class HomePage {
private _deleteAppConfirm = '[data-cy="t--delete"]';
private _wsAction = (action: string) =>
"//span[text()='" + action + "']/ancestor::a";
private _homeTab = ".t--apps-tab";
private _templatesTab = ".t--templates-tab";
public SwitchToAppsTab() {
this.agHelper.GetNClick(this._homeTab);
}
public SwitchToTemplatesTab() {
this.agHelper.GetNClick(this._templatesTab);
}
public CreateNewWorkspace(workspaceNewName: string) {
let oldName = "";

View File

@ -1673,7 +1673,7 @@ Cypress.Commands.add("checkLabelForWidget", (options) => {
const containerSelector = `${widgetSelector} ${options.containerSelector}`;
const labelPositionSelector = ".t--property-control-position";
const labelAlignmentRightSelector =
".t--property-control-alignment .t--button-tab-right";
".t--property-control-alignment .t--button-group-right";
const labelWidth = options.labelWidth;
// Drag a widget
@ -1690,17 +1690,17 @@ Cypress.Commands.add("checkLabelForWidget", (options) => {
.contains(labelText);
// Set the label position: Auto
cy.get(".t--button-tab-Auto").click({ force: true });
cy.get(".t--button-group-Auto").click({ force: true });
// Assert label position: Auto
cy.get(containerSelector).should("have.css", "flex-direction", "column");
// Change the label position to Top
cy.get(".t--button-tab-Top").click({ force: true });
cy.get(".t--button-group-Top").click({ force: true });
// Assert label position: Top
cy.get(containerSelector).should("have.css", "flex-direction", "column");
// Change the label position to Left
cy.get(".t--button-tab-Left").click({ force: true });
cy.get(".t--button-group-Left").click({ force: true });
// Assert label position: Left
cy.get(containerSelector).should("have.css", "flex-direction", "row");

View File

@ -46,7 +46,7 @@
"cypress-log-to-output": "^1.1.2",
"dayjs": "^1.10.6",
"deep-diff": "^1.0.2",
"design-system": "npm:@appsmithorg/design-system@1.0.41",
"design-system": "npm:@appsmithorg/design-system@1.0.44",
"downloadjs": "^1.4.7",
"draft-js": "^0.11.7",
"exceljs-lightweight": "^1.14.0",

View File

@ -338,6 +338,25 @@ export const toggleSaveActionFromPopupFlag = (isDSSavedFromPopup: boolean) => {
};
};
// This function stores the config property for key value pairs in
// datasource form which are initialized by default
export const setDefaultKeyValPairFlag = (defaultKeyValArrayConfig: string) => {
return {
type: ReduxActionTypes.SET_DATASOURCE_DEFAULT_KEY_VALUE_PAIR_SET,
payload: defaultKeyValArrayConfig,
};
};
// This function resets the config property stored in redux for key value pairs in
// datasource form which are initialized by default, once these key value pairs are initialized
// in the datasource config form, store needs to reset those values
export const resetDefaultKeyValPairFlag = () => {
return {
type: ReduxActionTypes.RESET_DATASOURCE_DEFAULT_KEY_VALUE_PAIR_SET,
payload: [],
};
};
export default {
fetchDatasources,
initDatasourcePane,

View File

@ -61,7 +61,7 @@ export const EVALUATE_REDUX_ACTIONS = [
ReduxActionTypes.FETCH_JS_ACTIONS_VIEW_MODE_SUCCESS,
ReduxActionErrorTypes.FETCH_JS_ACTIONS_VIEW_MODE_ERROR,
ReduxActionTypes.UPDATE_JS_ACTION_BODY_SUCCESS,
ReduxActionTypes.EXECUTE_JS_FUNCTION_SUCCESS,
ReduxActionTypes.SET_JS_FUNCTION_EXECUTION_DATA,
// App Data
ReduxActionTypes.SET_APP_MODE,
ReduxActionTypes.FETCH_USER_DETAILS_SUCCESS,

View File

@ -557,6 +557,7 @@ export const ReduxActionTypes = {
FETCH_JS_ACTIONS_VIEW_MODE_SUCCESS: "FETCH_JS_ACTIONS_VIEW_MODE_SUCCESS",
EXECUTE_JS_FUNCTION_INIT: "EXECUTE_JS_FUNCTION_INIT",
EXECUTE_JS_FUNCTION_SUCCESS: "EXECUTE_JS_FUNCTION_SUCCESS",
SET_JS_FUNCTION_EXECUTION_DATA: "SET_JS_FUNCTION_EXECUTION_DATA",
GET_PLUGIN_FORM_CONFIG_INIT: "GET_PLUGIN_FORM_CONFIG_INIT",
EXECUTE_DATASOURCE_QUERY_INIT: "EXECUTE_DATASOURCE_QUERY_INIT",
EXECUTE_DATASOURCE_QUERY_SUCCESS: "EXECUTE_DATASOURCE_QUERY_SUCCESS",
@ -752,6 +753,10 @@ export const ReduxActionTypes = {
SET_DATASOURCE_SAVE_ACTION_FLAG: "SET_DATASOURCE_SAVE_ACTION_FLAG",
SET_DATASOURCE_SAVE_ACTION_FROM_POPUP_FLAG:
"SET_DATASOURCE_SAVE_ACTION_FROM_POPUP_FLAG",
SET_DATASOURCE_DEFAULT_KEY_VALUE_PAIR_SET:
"SET_DATASOURCE_DEFAULT_KEY_VALUE_PAIR_SET",
RESET_DATASOURCE_DEFAULT_KEY_VALUE_PAIR_SET:
"RESET_DATASOURCE_DEFAULT_KEY_VALUE_PAIR_SET",
};
export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes];

View File

@ -1190,6 +1190,8 @@ export const ADMIN_BRANDING_COLOR_TOOLTIP_FONT = () =>
`Used as text color for the buttons.`;
export const ADMIN_BRANDING_COLOR_TOOLTIP_DISABLED = () =>
`Used as background color for disabled buttons.`;
export const ADMIN_BRANDING_UPGRADE_INTERCOM_MESSAGE = () =>
`I would like to enable Custom Branding for my workspace and am interested in Appsmith Business.`;
// Guided tour
// -- STEPS ---

View File

@ -212,7 +212,7 @@ export function AuthPage({ authMethods }: { authMethods: AuthMethodType[] }) {
</MethodDetailsWrapper>
<StyledAuthButton
category={
method.isConnected ? Category.primary : Category.tertiary
method.isConnected ? Category.primary : Category.secondary
}
className={`t--settings-sub-category-${
method.needsUpgrade

View File

@ -4,6 +4,7 @@ import { Button } from "design-system";
import {
ADMIN_BRANDING_SETTINGS_SUBTITLE,
ADMIN_BRANDING_SETTINGS_TITLE,
ADMIN_BRANDING_UPGRADE_INTERCOM_MESSAGE,
createMessage,
} from "@appsmith/constants/messages";
import useOnUpgrade from "utils/hooks/useOnUpgrade";
@ -15,8 +16,7 @@ import {
const UpgradeBanner = () => {
const { onUpgrade } = useOnUpgrade({
logEventName: "BRANDING_UPGRADE_CLICK",
intercomMessage:
"Hello, I would like to upgrade my appsmith instance to use the custom branding feature",
intercomMessage: createMessage(ADMIN_BRANDING_UPGRADE_INTERCOM_MESSAGE),
});
return (

View File

@ -636,6 +636,7 @@ export default connect(
applicationId?: string;
workspaceId?: string;
isApplicationInvite?: boolean;
placeholder?: string;
}
>({
validate,

View File

@ -152,7 +152,7 @@ const StyledButton = styled((props) => (
} !important;
}
&:hover:enabled, &:active:enabled {
&:hover:enabled, &:active:enabled. &:focus:enabled {
background: ${
buttonStyle === ButtonStyleTypes.WARNING
? buttonVariant === ButtonVariantTypes.SECONDARY

View File

@ -22,7 +22,10 @@ import {
import { POSITIONED_WIDGET } from "constants/componentClassNameConstants";
import equal from "fast-deep-equal";
const PositionedWidget = styled.div<{ zIndexOnHover: number }>`
const PositionedWidget = styled.div<{
zIndexOnHover: number;
disabled?: boolean;
}>`
&:hover {
z-index: ${(props) => props.zIndexOnHover} !important;
}
@ -42,6 +45,8 @@ export type PositionedContainerProps = {
noContainerOffset?: boolean;
leftColumn: number;
parentColumnSpace: number;
isDisabled?: boolean;
isVisible?: boolean;
};
export const checkIsDropTarget = memoize(function isDropTarget(
@ -163,7 +168,9 @@ export function PositionedContainer(props: PositionedContainerProps) {
return (
<PositionedWidget
className={containerClassName}
data-hidden={!props.isVisible || undefined}
data-testid="test-widget"
disabled={props.isDisabled}
id={props.widgetId}
key={`positioned-container-${props.widgetId}`}
// Positioned Widget is the top enclosure for all widgets and clicks on/inside the widget should not be propagated/bubbled out of this Container.

View File

@ -96,7 +96,7 @@ const addMarksForChildren = (
tokenEnd: number,
editor: CodeMirror.Editor,
) => {
const childNodes = navigationData.children;
const childNodes = navigationData.children || {};
if (Object.keys(childNodes).length) {
const token = editor.getTokenAt(
{

View File

@ -21,6 +21,8 @@ import {
TextInputProps,
TextType,
} from "design-system";
import { setDefaultKeyValPairFlag } from "actions/datasourceActions";
import { useDispatch } from "react-redux";
export interface KeyValueArrayControlProps extends ControlProps {
name: string;
label: string;
@ -88,6 +90,7 @@ function KeyValueRow(
const keyName = getFieldName(extraData[0]?.configProperty);
const valueName = getFieldName(extraData[1]?.configProperty);
const keyFieldProps = extraData[0];
const dispatch = useDispatch();
const addRow = useCallback(() => {
if (keyName && valueName) {
@ -102,6 +105,9 @@ function KeyValueRow(
if (props.fields.length < 1) {
for (let i = props.fields.length; i < 1; i += 1) {
addRow();
// Since we are initializing one default key value pair, it needs to stored in redux store
// so that it can be used to initilize datasource config form as well
dispatch(setDefaultKeyValPairFlag(props.configProperty));
}
}
}, [props.fields, keyName, valueName]);

View File

@ -1,9 +1,8 @@
import * as React from "react";
import { TooltipComponent } from "design-system";
import { ButtonGroup, TooltipComponent } from "design-system";
import BaseControl, { ControlData, ControlProps } from "./BaseControl";
import { borderRadiusOptions } from "constants/ThemeConstants";
import { ButtonTab } from "design-system";
import {
DSEventDetail,
DSEventTypes,
@ -64,7 +63,7 @@ class BorderRadiusOptionsControl extends BaseControl<
handleAdsEvent = (e: CustomEvent<DSEventDetail>) => {
if (
e.detail.component === "ButtonTab" &&
e.detail.component === "ButtonGroup" &&
e.detail.event === DSEventTypes.KEYPRESS
) {
emitInteractionAnalyticsEvent(this.componentRef.current, {
@ -80,7 +79,7 @@ class BorderRadiusOptionsControl extends BaseControl<
public render() {
return (
<ButtonTab
<ButtonGroup
options={options}
ref={this.componentRef}
selectButton={(value, isUpdatedViaKeyboard = false) => {

View File

@ -1,10 +1,9 @@
import * as React from "react";
import BaseControl, { ControlData, ControlProps } from "./BaseControl";
import { TooltipComponent } from "design-system";
import { ButtonGroup, TooltipComponent } from "design-system";
import { boxShadowOptions } from "constants/ThemeConstants";
import CloseLineIcon from "remixicon-react/CloseLineIcon";
import { ButtonTab } from "design-system";
import {
DSEventDetail,
DSEventTypes,
@ -58,7 +57,7 @@ class BoxShadowOptionsControl extends BaseControl<
handleAdsEvent = (e: CustomEvent<DSEventDetail>) => {
if (
e.detail.component === "ButtonTab" &&
e.detail.component === "ButtonGroup" &&
e.detail.event === DSEventTypes.KEYPRESS
) {
emitInteractionAnalyticsEvent(this.componentRef.current, {
@ -74,7 +73,7 @@ class BoxShadowOptionsControl extends BaseControl<
public render() {
return (
<ButtonTab
<ButtonGroup
options={options}
ref={this.componentRef}
selectButton={(value, isUpdatedViaKeyboard = false) => {

View File

@ -1,6 +1,6 @@
import React from "react";
import BaseControl, { ControlData, ControlProps } from "./BaseControl";
import { ButtonTab, ButtonTabOption } from "design-system";
import { ButtonGroup, ButtonGroupOption } from "design-system";
import produce from "immer";
import {
DSEventDetail,
@ -28,7 +28,7 @@ class ButtonTabControl extends BaseControl<ButtonTabControlProps> {
handleAdsEvent = (e: CustomEvent<DSEventDetail>) => {
if (
e.detail.component === "ButtonTab" &&
e.detail.component === "ButtonGroup" &&
e.detail.event === DSEventTypes.KEYPRESS
) {
emitInteractionAnalyticsEvent(this.componentRef.current, {
@ -67,7 +67,7 @@ class ButtonTabControl extends BaseControl<ButtonTabControlProps> {
render() {
const { options, propertyValue } = this.props;
return (
<ButtonTab
<ButtonGroup
options={options}
ref={this.componentRef}
selectButton={this.selectButton}
@ -77,7 +77,7 @@ class ButtonTabControl extends BaseControl<ButtonTabControlProps> {
}
static getControlType() {
return "BUTTON_TABS";
return "BUTTON_GROUP";
}
static canDisplayValueInUI(config: ControlData, value: any): boolean {
@ -98,7 +98,7 @@ class ButtonTabControl extends BaseControl<ButtonTabControlProps> {
}
export interface ButtonTabControlProps extends ControlProps {
options: ButtonTabOption[];
options: ButtonGroupOption[];
defaultValue: string;
}

View File

@ -1,6 +1,6 @@
import React from "react";
import BaseControl, { ControlData, ControlProps } from "./BaseControl";
import { ButtonTab, ButtonTabOption } from "design-system";
import { ButtonGroup, ButtonGroupOption } from "design-system";
import {
DSEventDetail,
DSEventTypes,
@ -27,7 +27,7 @@ class IconTabControl extends BaseControl<IconTabControlProps> {
handleAdsEvent = (e: CustomEvent<DSEventDetail>) => {
if (
e.detail.component === "ButtonTab" &&
e.detail.component === "ButtonGroup" &&
e.detail.event === DSEventTypes.KEYPRESS
) {
emitInteractionAnalyticsEvent(this.componentRef.current, {
@ -52,7 +52,7 @@ class IconTabControl extends BaseControl<IconTabControlProps> {
render() {
const { fullWidth, options, propertyValue } = this.props;
return (
<ButtonTab
<ButtonGroup
fullWidth={fullWidth}
options={options}
ref={this.componentRef}
@ -78,7 +78,7 @@ class IconTabControl extends BaseControl<IconTabControlProps> {
}
export interface IconTabControlProps extends ControlProps {
options: ButtonTabOption[];
options: ButtonGroupOption[];
defaultValue: string;
fullWidth: boolean;
}

View File

@ -3,7 +3,7 @@ import styled from "styled-components";
import { Alignment } from "@blueprintjs/core";
import BaseControl, { ControlProps } from "./BaseControl";
import { ButtonTab, ButtonTabOption } from "design-system";
import { ButtonGroup, ButtonGroupOption } from "design-system";
import {
DSEventDetail,
DSEventTypes,
@ -22,7 +22,7 @@ const ControlContainer = styled.div`
export interface LabelAlignmentOptionsControlProps extends ControlProps {
propertyValue?: Alignment;
options: ButtonTabOption[];
options: ButtonGroupOption[];
defaultValue: Alignment;
}
@ -52,7 +52,7 @@ class LabelAlignmentOptionsControl extends BaseControl<
handleAdsEvent = (e: CustomEvent<DSEventDetail>) => {
if (
e.detail.component === "ButtonTab" &&
e.detail.component === "ButtonGroup" &&
e.detail.event === DSEventTypes.KEYPRESS
) {
emitInteractionAnalyticsEvent(this.componentRef.current, {
@ -70,7 +70,7 @@ class LabelAlignmentOptionsControl extends BaseControl<
const { options, propertyValue } = this.props;
return (
<ControlContainer>
<ButtonTab
<ButtonGroup
options={options}
ref={this.componentRef}
selectButton={this.handleAlign}

View File

@ -18,6 +18,8 @@ import {
stringToJS,
} from "components/editorComponents/ActionCreator/utils";
import { AdditionalDynamicDataTree } from "utils/autocomplete/customTreeTypeDefCreator";
import { ColumnProperties } from "widgets/TableWidgetV2/component/Constants";
import { getUniqueKeysFromSourceData } from "widgets/MenuButtonWidget/widget/helper";
const PromptMessage = styled.span`
line-height: 17px;
@ -93,18 +95,34 @@ class MenuButtonDynamicItemsControl extends BaseControl<
label,
propertyValue,
theme,
widgetProperties,
} = this.props;
const menuButtonId = this.props.widgetProperties.widgetName;
const widgetName = widgetProperties.widgetName;
const widgetType = widgetProperties.type;
const value =
propertyValue && isDynamicValue(propertyValue)
? MenuButtonDynamicItemsControl.getInputComputedValue(
propertyValue,
menuButtonId,
widgetName,
widgetType,
widgetProperties.primaryColumns,
)
: propertyValue
? propertyValue
: defaultValue;
const keys = this.props.widgetProperties.sourceDataKeys || [];
let sourceData;
if (widgetType === "TABLE_WIDGET_V2") {
sourceData =
widgetProperties?.__evaluation__?.evaluatedValues?.primaryColumns?.[
`${Object.keys(widgetProperties.primaryColumns)[0]}`
]?.sourceData;
} else if (widgetType === "MENU_BUTTON_WIDGET") {
sourceData =
widgetProperties?.__evaluation__?.evaluatedValues?.sourceData;
}
const keys = getUniqueKeysFromSourceData(sourceData);
const currentItem: { [key: string]: any } = {};
Object.values(keys).forEach((key) => {
@ -115,6 +133,7 @@ class MenuButtonDynamicItemsControl extends BaseControl<
if (value && !propertyValue) {
this.onTextChange(value);
}
return (
<InputText
additionalDynamicData={{
@ -131,30 +150,61 @@ class MenuButtonDynamicItemsControl extends BaseControl<
);
}
static getBindingPrefix = (menuButtonId: string) => {
return `{{${menuButtonId}.sourceData.map((currentItem, currentIndex) => ( `;
static getBindingPrefix = (
widgetName: string,
widgetType?: string,
primaryColumns?: Record<string, ColumnProperties>,
) => {
if (widgetType === "TABLE_WIDGET_V2" && primaryColumns) {
const columnName = Object.keys(primaryColumns)?.[0];
return `{{${widgetName}.processedTableData.map((currentRow, currentRowIndex) => {
let primaryColumnData = [];
if (${widgetName}.primaryColumns.${columnName}.sourceData[currentRowIndex].length) {
primaryColumnData = ${widgetName}.primaryColumns.${columnName}.sourceData[currentRowIndex];
} else if (${widgetName}.primaryColumns.${columnName}.sourceData.length) {
primaryColumnData = ${widgetName}.primaryColumns.${columnName}.sourceData;
}
return primaryColumnData.map((currentItem, currentIndex) => `;
} else {
return `{{${widgetName}.sourceData.map((currentItem, currentIndex) => ( `;
}
};
static bindingSuffix = `))}}`;
static getBindingSuffix = (widgetType?: string) =>
widgetType === "TABLE_WIDGET_V2" ? `);});}}` : `))}}`;
static getInputComputedValue = (
propertyValue: string,
menuButtonId: string,
widgetName: string,
widgetType?: string,
primaryColumns?: Record<string, ColumnProperties>,
) => {
if (!propertyValue.includes(this.getBindingPrefix(menuButtonId))) {
if (
!propertyValue.includes(
this.getBindingPrefix(widgetName, widgetType, primaryColumns),
)
) {
return propertyValue;
}
const value = `${propertyValue.substring(
this.getBindingPrefix(menuButtonId).length,
propertyValue.length - this.bindingSuffix.length,
this.getBindingPrefix(widgetName, widgetType, primaryColumns).length,
propertyValue.length - this.getBindingSuffix(widgetType).length,
)}`;
const stringValue = JSToString(value);
return stringValue;
};
getComputedValue = (value: string, menuButtonId: string) => {
getComputedValue = (
value: string,
widgetName: string,
widgetType?: string,
primaryColumns?: Record<string, ColumnProperties>,
) => {
if (!isDynamicValue(value)) {
return value;
}
@ -166,8 +216,12 @@ class MenuButtonDynamicItemsControl extends BaseControl<
}
return `${MenuButtonDynamicItemsControl.getBindingPrefix(
menuButtonId,
)}${stringToEvaluate}${MenuButtonDynamicItemsControl.bindingSuffix}`;
widgetName,
widgetType,
primaryColumns,
)}${stringToEvaluate}${MenuButtonDynamicItemsControl.getBindingSuffix(
widgetType,
)}`;
};
onTextChange = (event: React.ChangeEvent<HTMLTextAreaElement> | string) => {
@ -181,6 +235,8 @@ class MenuButtonDynamicItemsControl extends BaseControl<
const output = this.getComputedValue(
value,
this.props.widgetProperties.widgetName,
this.props.widgetProperties.type,
this.props.widgetProperties.primaryColumns,
);
this.updateProperty(this.props.propertyName, output);

View File

@ -44,7 +44,7 @@ class OpenConfigPanelControl extends BaseControl<OpenConfigPanelControlProps> {
<Wrapper>
<StyledPropertyPaneButtonWrapper>
<OpenNextPannelButton
category={Category.tertiary}
category={Category.secondary}
className={`t--${widgetName}-open-next-panel-button`}
icon={icon}
iconPosition="right"

View File

@ -177,11 +177,13 @@ class ComputeTablePropertyControlV2 extends BaseControl<
onTextChange = (event: React.ChangeEvent<HTMLTextAreaElement> | string) => {
let value = "";
if (typeof event !== "string") {
value = event.target?.value;
} else {
value = event;
}
if (isString(value)) {
const output = this.getComputedValue(
value,

View File

@ -59,7 +59,8 @@ const Checkbox = styled(BlueprintCheckbox)<StyledCheckboxProps>`
}
// hover
&.bp3-control.bp3-checkbox:hover input:not(:checked) ~ .bp3-control-indicator {
&.bp3-control.bp3-checkbox:hover input:not(:checked) ~ .bp3-control-indicator,
input:not(:checked):focus ~ .bp3-control-indicator {
box-shadow: 0px 0px 0px 1px ${
hasError
? "var(--wds-color-border-danger-hover)"
@ -68,7 +69,10 @@ const Checkbox = styled(BlueprintCheckbox)<StyledCheckboxProps>`
}
// hover on checked
&.bp3-control.bp3-checkbox:hover input:checked ~ .bp3-control-indicator, &.bp3-control.bp3-checkbox:hover input:indeterminate ~ .bp3-control-indicator {
&.bp3-control.bp3-checkbox:hover input:checked ~ .bp3-control-indicator,
&.bp3-control.bp3-checkbox:hover input:indeterminate ~ .bp3-control-indicator,
&.bp3-control.bp3-checkbox input:checked:focus ~ .bp3-control-indicator,
&.bp3-control.bp3-checkbox input:indeterminate:focus ~ .bp3-control-indicator {
box-shadow: none;
background: ${darkenColor(accentColor)} !important;
}

View File

@ -105,10 +105,9 @@ export const BlueprintControlTransform = css`
border: 1px solid var(--wds-color-border-disabled) !important;
}
&:hover {
& input:not(:checked):not(:disabled) ~ .bp3-control-indicator {
border: 1px solid ${Colors.GREY_6} !important;
}
&:hover input:not(:checked):not(:disabled) ~ .bp3-control-indicator,
& input:not(:checked):not(:disabled):focus ~ .bp3-control-indicator {
border: 1px solid var(--wds-color-bg-disabled-strong) !important;
}
}
@ -137,11 +136,10 @@ export const BlueprintControlTransform = css`
}
}
&:hover {
& input:not(:checked):not(:disabled) ~ .bp3-control-indicator {
background: var(--wds-color-bg-strong-hover);
border: 1px solid var(--wds-color-border-hover) !important;
}
&:hover input:not(:checked):not(:disabled) ~ .bp3-control-indicator,
input:not(:checked):not(:disabled):focus ~ .bp3-control-indicator {
background: var(--wds-color-bg-strong-hover);
border: 1px solid var(--wds-color-border-hover) !important;
}
}

View File

@ -70,7 +70,7 @@ export const layoutConfigurations: LayoutConfigurations = {
FLUID: { minWidth: -1, maxWidth: -1 },
};
export const LATEST_PAGE_VERSION = 74;
export const LATEST_PAGE_VERSION = 75;
export const GridDefaults = {
DEFAULT_CELL_SIZE: 1,

View File

@ -1,8 +1,8 @@
export function getStickyCanvasName(widgetId: string) {
export function getSlidingArenaName(widgetId: string) {
return `div-selection-${widgetId}`;
}
export function getSlidingCanvasName(widgetId: string) {
export function getStickyCanvasName(widgetId: string) {
return `canvas-selection-${widgetId}`;
}

View File

@ -70,7 +70,7 @@ export interface Oauth2Common {
isAuthorizationHeader: boolean;
audience: string;
resource: string;
sendScopeWithRefreshToken: string;
sendScopeWithRefreshToken: boolean;
refreshTokenClientCredentialsLocation: string;
useSelfSignedCert?: boolean;
}

View File

@ -49,6 +49,7 @@ import {
checkContainersForAutoHeightAction,
updateWidgetAutoHeightAction,
} from "actions/autoHeightActions";
import useWidgetFocus from "utils/hooks/useWidgetFocus/useWidgetFocus";
const AppViewerBody = styled.section<{
hasPages: boolean;
@ -96,6 +97,8 @@ function AppViewer(props: Props) {
const prevValues = usePrevious({ branch, location: props.location, pageId });
const { hideWatermark } = getAppsmithConfigs();
const focusRef = useWidgetFocus();
/**
* initializes the widgets factory and registers all widgets
*/
@ -268,6 +271,7 @@ function AppViewer(props: Props) {
className={CANVAS_SELECTOR}
hasPages={pages.length > 1}
headerHeight={headerHeight}
ref={focusRef}
showGuidedTourMessage={showGuidedTourMessage}
>
{isInitialized && registered && <AppViewerPageContainer />}

View File

@ -104,7 +104,7 @@ function ImportApplicationModal(props: ImportApplicationModalProps) {
</FilePickerWrapper>
<ButtonWrapper>
<ImportButton
// category={ButtonCategory.tertiary}
// category={ButtonCategory.secondary}
cypressSelector={"t--workspace-import-app-button"}
disabled={!appFileToBeUploaded}
isLoading={importingApplication}

View File

@ -20,6 +20,7 @@ import { getCurrentGitBranch } from "selectors/gitSyncSelectors";
import { getSelectedAppTheme } from "selectors/appThemingSelectors";
import { getPageLevelSocketRoomId } from "sagas/WebsocketSagas/utils";
import { previewModeSelector } from "selectors/editorSelectors";
import useWidgetFocus from "utils/hooks/useWidgetFocus";
interface CanvasProps {
widgetsStructure: CanvasWidgetStructure;
@ -101,6 +102,8 @@ const Canvas = memo((props: CanvasProps) => {
backgroundForCanvas = selectedTheme.properties.colors.backgroundColor;
}
const focusRef = useWidgetFocus();
try {
return (
<Container
@ -118,6 +121,7 @@ const Canvas = memo((props: CanvasProps) => {
);
!!data && delayedShareMousePointer(data);
}}
ref={focusRef}
style={{
width: canvasWidth,
}}

View File

@ -56,7 +56,7 @@ function SaveOrDiscardDatasourceModal(props: SaveOrDiscardModalProps) {
<div className="">
<div className="flex items-center justify-end space-x-3">
<Button
category={Category.tertiary}
category={Category.secondary}
className="t--datasource-modal-do-not-save"
onClick={onDiscard}
size={Size.medium}

View File

@ -1,6 +1,6 @@
import React from "react";
import { connect } from "react-redux";
import { getFormValues, isDirty } from "redux-form";
import { getFormInitialValues, getFormValues, isDirty } from "redux-form";
import { AppState } from "@appsmith/reducers";
import _ from "lodash";
import {
@ -16,6 +16,7 @@ import {
toggleSaveActionFlag,
toggleSaveActionFromPopupFlag,
createTempDatasourceFromForm,
resetDefaultKeyValPairFlag,
} from "actions/datasourceActions";
import {
DATASOURCE_DB_FORM,
@ -72,6 +73,9 @@ interface ReduxStateProps {
triggerSave: boolean;
isFormDirty: boolean;
datasource: Datasource | undefined;
defaultKeyValueArrayConfig: Array<string>;
restAPIFormData: Datasource;
initialValue: Datasource | undefined;
}
interface DatasourcEditorProps {
@ -204,25 +208,17 @@ const mapStateToProps = (state: AppState, props: any): ReduxStateProps => {
const datasource = getDatasource(state, datasourceId);
const { formConfigs } = plugins;
const formData = getFormValues(DATASOURCE_DB_FORM)(state) as Datasource;
const restAPIFormData = getFormValues(DATASOURCE_REST_API_FORM)(
state,
) as Datasource;
const pluginId = _.get(datasource, "pluginId", "");
const plugin = getPlugin(state, pluginId);
const { applicationSlug, pageSlug } = selectURLSlugs(state);
const formName =
plugin?.type === "API" ? DATASOURCE_REST_API_FORM : DATASOURCE_DB_FORM;
// for plugins, where 1 default endpoint is initialized,
// added this check so that form isnt considered dirty with default endpoint
const defaultEndpoints: Array<{
host: string;
port: string;
}> = (formData?.datasourceConfiguration as any)?.endpoints || [];
const isDefaultEndpoint =
defaultEndpoints.length === 1 &&
defaultEndpoints[0].host === "" &&
defaultEndpoints[0].port === "";
const isFormDirty =
datasourceId === TEMP_DATASOURCE_ID
? true
: isDirty(formName)(state) && !isDefaultEndpoint;
datasourceId === TEMP_DATASOURCE_ID ? true : isDirty(formName)(state);
const initialValue = getFormInitialValues(formName)(state) as Datasource;
return {
datasourceId,
@ -248,6 +244,9 @@ const mapStateToProps = (state: AppState, props: any): ReduxStateProps => {
triggerSave: datasources.isDatasourceBeingSavedFromPopup,
isFormDirty,
datasource,
defaultKeyValueArrayConfig: datasourcePane.defaultKeyValueArrayConfig,
restAPIFormData,
initialValue,
};
};
@ -272,6 +271,7 @@ const mapDispatchToProps = (
dispatch(toggleSaveActionFromPopupFlag(flag)),
createTempDatasource: (data: any) =>
dispatch(createTempDatasourceFromForm(data)),
resetDefaultKeyValPairFlag: () => dispatch(resetDefaultKeyValPairFlag()),
});
export interface DatasourcePaneFunctions {
@ -283,6 +283,7 @@ export interface DatasourcePaneFunctions {
toggleSaveActionFlag: (flag: boolean) => void;
toggleSaveActionFromPopupFlag: (flag: boolean) => void;
createTempDatasource: (data: any) => void;
resetDefaultKeyValPairFlag: () => void;
}
class DatasourceEditorRouter extends React.Component<Props, State> {
@ -316,6 +317,7 @@ class DatasourceEditorRouter extends React.Component<Props, State> {
this.closeDialogAndUnblockRoutes();
}
this.setViewModeFromQueryParams();
this.initializeFormWithDefaults();
}
componentDidMount() {
@ -370,6 +372,7 @@ class DatasourceEditorRouter extends React.Component<Props, State> {
this.props.discardTempDatasource();
this.props.deleteTempDSFromDraft();
!!this.state.unblock && this.state.unblock();
this.props.resetDefaultKeyValPairFlag();
}
routesBlockFormChangeCallback() {
@ -432,6 +435,31 @@ class DatasourceEditorRouter extends React.Component<Props, State> {
!!this.state.unblock && this.state.unblock();
}
initializeFormWithDefaults() {
// This is required because once datasource form is loaded, we initialize key value pairs to have default empty values
// This initialization makes the form dirty and shows the discard popup on back button click even if the user has not made any changes
// This function will initialize redux form with those default empty key value pairs, so when it checks if the form is dirty or not
// Both initial and current value will be same and form will not be dirty as expected by user.
if (
this.props.defaultKeyValueArrayConfig.length > 0 &&
!!this.props.initialValue
) {
const formData: Datasource =
this.props.pluginType === "API"
? this.props.restAPIFormData
: this.props.formData;
for (const prop of this.props.defaultKeyValueArrayConfig) {
const propPath: string[] = prop.split("[*].");
const newValues = _.get(formData, propPath[0], []);
_.set(this.props.initialValue, propPath[0], newValues);
}
// since we have consumed key value pair config to initialize datasource form,
// below function will reset the config array in redux store
this.props.resetDefaultKeyValPairFlag();
}
}
renderSaveDisacardModal() {
return (
<SaveOrDiscardDatasourceModal

View File

@ -339,7 +339,7 @@ export default function OnboardingChecklist() {
() => ONBOARDING_CHECKLIST_ACTIONS.CONNECT_A_DATASOURCE,
)
? Category.primary
: Category.tertiary
: Category.secondary
}
className="t--checklist-datasource-button"
data-testid="checklist-datasource-button"
@ -389,7 +389,7 @@ export default function OnboardingChecklist() {
suggestedNextAction ===
createMessage(() => ONBOARDING_CHECKLIST_ACTIONS.CREATE_A_QUERY)
? Category.primary
: Category.tertiary
: Category.secondary
}
className="t--checklist-action-button"
data-testid="checklist-action-button"
@ -441,7 +441,7 @@ export default function OnboardingChecklist() {
suggestedNextAction ===
createMessage(() => ONBOARDING_CHECKLIST_ACTIONS.ADD_WIDGETS)
? Category.primary
: Category.tertiary
: Category.secondary
}
className="t--checklist-widget-button"
data-testid="checklist-widget-button"
@ -491,7 +491,7 @@ export default function OnboardingChecklist() {
() => ONBOARDING_CHECKLIST_ACTIONS.CONNECT_DATA_TO_WIDGET,
)
? Category.primary
: Category.tertiary
: Category.secondary
}
className="t--checklist-connection-button"
data-testid="checklist-connection-button"
@ -534,7 +534,7 @@ export default function OnboardingChecklist() {
() => ONBOARDING_CHECKLIST_ACTIONS.DEPLOY_APPLICATIONS,
)
? Category.primary
: Category.tertiary
: Category.secondary
}
className="t--checklist-deploy-button"
data-testid="checklist-deploy-button"

View File

@ -313,7 +313,7 @@ function NewApiScreen(props: Props) {
src={authApiPlugin.iconLocation}
/>
</div>
<p className="textBtn">Authenticated API</p>
<p className="t--plugin-name textBtn">Authenticated API</p>
</CardContentWrapper>
</ApiCard>
)}

View File

@ -1,7 +1,7 @@
import React, { useCallback } from "react";
import { AppTheme } from "entities/AppTheming";
import { ButtonTab, TooltipComponent } from "design-system";
import { ButtonGroup, TooltipComponent } from "design-system";
import { invertedBorderRadiusOptions } from "constants/ThemeConstants";
interface ThemeBorderRadiusControlProps {
@ -39,7 +39,7 @@ function ThemeBorderRadiusControl(props: ThemeBorderRadiusControlProps) {
? [invertedBorderRadiusOptions[selectedOption]]
: [];
const buttonTabOptions = Object.keys(options).map((optionKey) => ({
const buttonGroupOptions = Object.keys(options).map((optionKey) => ({
icon: (
<TooltipComponent
content={optionKey}
@ -56,8 +56,8 @@ function ThemeBorderRadiusControl(props: ThemeBorderRadiusControlProps) {
}));
return (
<ButtonTab
options={buttonTabOptions}
<ButtonGroup
options={buttonGroupOptions}
selectButton={onChangeBorder}
values={selectedOptionKey}
/>

View File

@ -1,8 +1,6 @@
import React, { useCallback } from "react";
import { ButtonTab } from "design-system";
import { AppTheme } from "entities/AppTheming";
import { TooltipComponent } from "design-system";
import { ButtonGroup, TooltipComponent } from "design-system";
import CloseLineIcon from "remixicon-react/CloseLineIcon";
import { invertedBoxShadowOptions } from "constants/ThemeConstants";
@ -42,7 +40,7 @@ function ThemeBoxShadowControl(props: ThemeBoxShadowControlProps) {
? [invertedBoxShadowOptions[selectedOption]]
: [];
const buttonTabOptions = Object.keys(options).map((optionKey) => ({
const buttonGroupOptions = Object.keys(options).map((optionKey) => ({
icon: (
<TooltipComponent
content={optionKey}
@ -63,8 +61,8 @@ function ThemeBoxShadowControl(props: ThemeBoxShadowControlProps) {
}));
return (
<ButtonTab
options={buttonTabOptions}
<ButtonGroup
options={buttonGroupOptions}
selectButton={onChangeShadow}
values={selectedOptionKey}
/>

View File

@ -316,6 +316,13 @@ function ReconnectDatasourceModal() {
const [datasource, setDatasource] = useState<Datasource | null>(null);
const [isImport, setIsImport] = useState(queryIsImport);
const [isTesting, setIsTesting] = useState(false);
const queryDS = datasources.find((ds) => ds.id === queryDatasourceId);
const dsName = queryDS?.name;
const orgId = queryDS?.workspaceId;
let pluginName = "";
if (!!queryDS?.pluginId) {
pluginName = pluginNames[queryDS.pluginId];
}
// when redirecting from oauth, processing the status
if (isImport) {
@ -330,6 +337,13 @@ function ReconnectDatasourceModal() {
? OAUTH_AUTHORIZATION_APPSMITH_ERROR
: OAUTH_AUTHORIZATION_FAILED;
Toaster.show({ text: display_message || message, variant });
const oAuthStatus = status;
AnalyticsUtil.logEvent("UPDATE_DATASOURCE", {
dsName,
oAuthStatus,
orgId,
pluginName,
});
} else if (queryDatasourceId) {
dispatch(getOAuthAccessToken(queryDatasourceId));
}

View File

@ -28,6 +28,8 @@ import WidgetInfo from "../WidgetInfo";
import ForkTemplate from "../ForkTemplate";
import { templateIdUrl } from "RouteBuilder";
import { useQuery } from "pages/Editor/utils";
import { useSelector } from "react-redux";
import { getForkableWorkspaces } from "selectors/templatesSelectors";
export const DescriptionWrapper = styled.div`
display: flex;
@ -94,6 +96,7 @@ function TemplateDescription(props: TemplateDescriptionProps) {
}>();
const history = useHistory();
const query = useQuery();
const workspaceList = useSelector(getForkableWorkspaces);
const onForkButtonTrigger = () => {
history.replace(
@ -114,7 +117,7 @@ function TemplateDescription(props: TemplateDescriptionProps) {
{template.description}
</Text>
</div>
{!props.hideForkButton && (
{!props.hideForkButton && !!workspaceList.length && (
<ForkTemplate
onClose={onForkModalClose}
showForkModal={!!query.get(SHOW_FORK_MODAL_PARAM)}

View File

@ -18,6 +18,8 @@ import {
} from "@appsmith/constants/messages";
import { templateIdUrl } from "RouteBuilder";
import { Position } from "@blueprintjs/core";
import { getForkableWorkspaces } from "selectors/templatesSelectors";
import { useSelector } from "react-redux";
const TemplateWrapper = styled.div`
border: 1px solid ${Colors.GEYSER_LIGHT};
@ -129,6 +131,7 @@ export function TemplateLayout(props: TemplateLayoutProps) {
title,
} = props.template;
const [showForkModal, setShowForkModal] = useState(false);
const workspaceList = useSelector(getForkableWorkspaces);
const onClick = () => {
if (props.onClick) {
props.onClick(id);
@ -176,26 +179,29 @@ export function TemplateLayout(props: TemplateLayoutProps) {
);
})}
</TemplateDatasources>
<div onClick={onForkButtonTrigger}>
<ForkTemplateDialog
onClose={onForkModalClose}
showForkModal={showForkModal}
templateId={id}
>
<Tooltip
content={createMessage(FORK_THIS_TEMPLATE)}
minimal
position={Position.BOTTOM}
{!!workspaceList.length && (
<div onClick={onForkButtonTrigger}>
<ForkTemplateDialog
onClose={onForkModalClose}
showForkModal={showForkModal}
templateId={id}
>
<StyledButton
className="t--fork-template fork-button"
icon="plus"
size={Size.medium}
tag="button"
/>
</Tooltip>
</ForkTemplateDialog>
</div>
<Tooltip
content={createMessage(FORK_THIS_TEMPLATE)}
minimal
position={Position.BOTTOM}
>
<StyledButton
className="t--fork-template fork-button"
icon="plus"
onClick={onForkButtonTrigger}
size={Size.medium}
tag="button"
/>
</Tooltip>
</ForkTemplateDialog>
</div>
)}
</TemplateContentFooter>
</TemplateContent>
</TemplateWrapper>

View File

@ -23,8 +23,8 @@ import { getIsDraggingForSelection } from "selectors/canvasSelectors";
import { StickyCanvasArena } from "./StickyCanvasArena";
import { getAbsolutePixels } from "utils/helpers";
import {
getSlidingCanvasName,
getStickyCanvasName,
getSlidingArenaName,
} from "constants/componentClassNameConstants";
export interface SelectedArenaDimensions {
@ -482,10 +482,10 @@ export function CanvasSelectionArena({
return shouldShow ? (
<StickyCanvasArena
canExtend={canExtend}
canvasId={getSlidingCanvasName(widgetId)}
canvasId={getStickyCanvasName(widgetId)}
canvasPadding={canvasPadding}
getRelativeScrollingParent={getNearestParentCanvas}
id={getStickyCanvasName(widgetId)}
id={getSlidingArenaName(widgetId)}
ref={canvasRef}
showCanvas={shouldShow}
snapColSpace={snapColumnSpace}

View File

@ -1,4 +1,7 @@
import React from "react";
import { useSelector } from "react-redux";
import { getTenantConfig } from "@appsmith/selectors/tenantSelectors";
import { getComplementaryGrayscaleColor } from "widgets/WidgetUtils";
type PageProps = {
children?: React.ReactNode;
@ -11,9 +14,16 @@ type PageProps = {
function Page(props: PageProps) {
const { cta, description, errorCode, title } = props;
const tenantConfig = useSelector(getTenantConfig);
const backgroundColor = tenantConfig.brandColors.background;
const textColor = getComplementaryGrayscaleColor(backgroundColor);
return (
<div className="absolute inset-0 flex flex-col items-center justify-center space-y-6 bg-[color:var(--ads-color-background-secondary)]">
<div
className={`absolute inset-0 flex flex-col items-center justify-center space-y-6 bg-[color:var(--ads-color-background-secondary)] ${
textColor === "white" ? "text-white" : ""
}`}
>
{errorCode && (
<div className="-mt-8 flex items-center font-bold text-3xl justify-center w-28 bg-white border aspect-square text-[color:var(--ads-color-brand)]">
{errorCode}

View File

@ -4,6 +4,7 @@ import { ActionButton } from "pages/Editor/DataSourceEditor/JSONtoForm";
import { useDispatch, useSelector } from "react-redux";
import {
getEntities,
getPluginNameFromId,
getPluginTypeFromDatasourceId,
} from "selectors/entitiesSelector";
import {
@ -124,8 +125,11 @@ function DatasourceAuth({
? formData?.authType
: formData?.datasourceConfiguration?.authentication?.authenticationType;
const { id: datasourceId, isDeleting } = datasource;
const { id: datasourceId, isDeleting, pluginId } = datasource;
const applicationId = useSelector(getCurrentApplicationId);
const pluginName = useSelector((state: AppState) =>
getPluginNameFromId(state, pluginId),
);
const datasourcePermissions = datasource.userPermissions || [];
@ -144,6 +148,8 @@ function DatasourceAuth({
const pageId = (pageIdQuery || pageIdProp) as string;
const [confirmDelete, setConfirmDelete] = useState(false);
const dsName = datasource?.name;
const orgId = datasource?.workspaceId;
useEffect(() => {
if (confirmDelete) {
@ -172,6 +178,13 @@ function DatasourceAuth({
? OAUTH_AUTHORIZATION_APPSMITH_ERROR
: OAUTH_AUTHORIZATION_FAILED;
Toaster.show({ text: display_message || message, variant });
const oAuthStatus = status;
AnalyticsUtil.logEvent("UPDATE_DATASOURCE", {
dsName,
oAuthStatus,
orgId,
pluginName,
});
} else {
dispatch(getOAuthAccessToken(datasourceId));
}

View File

@ -21,10 +21,14 @@ import useWorkspace from "utils/hooks/useWorkspace";
import TooltipWrapper from "pages/Applications/EmbedSnippet/TooltipWrapper";
import {
createMessage,
INVITE_USERS_PLACEHOLDER,
IN_APP_EMBED_SETTING,
MAKE_APPLICATION_PUBLIC,
MAKE_APPLICATION_PUBLIC_TOOLTIP,
} from "@appsmith/constants/messages";
import { getAppsmithConfigs } from "@appsmith/configs";
const { cloudHosting } = getAppsmithConfigs();
const ShareToggle = styled.div`
flex-basis: 46px;
@ -85,6 +89,7 @@ function AppInviteUsersForm(props: any) {
{canInviteToWorkspace && (
<WorkspaceInviteUsersForm
isApplicationInvite
placeholder={createMessage(INVITE_USERS_PLACEHOLDER, cloudHosting)}
workspaceId={props.workspaceId}
/>
)}

View File

@ -26,6 +26,15 @@ export interface PartialActionData {
isExecuting: Record<string, boolean>;
}
interface JSExecutionData {
data: unknown;
collectionId: string;
actionId: string;
}
// Object of collectionIds to JSExecutionData[]
export type BatchedJSExecutionData = Record<string, JSExecutionData[]>;
const jsActionsReducer = createReducer(initialState, {
[ReduxActionTypes.FETCH_JS_ACTIONS_SUCCESS]: (
state: JSCollectionDataState,
@ -292,7 +301,6 @@ const jsActionsReducer = createReducer(initialState, {
[ReduxActionTypes.EXECUTE_JS_FUNCTION_SUCCESS]: (
state: JSCollectionDataState,
action: ReduxAction<{
results: any;
collectionId: string;
actionId: string;
isDirty: boolean;
@ -302,10 +310,6 @@ const jsActionsReducer = createReducer(initialState, {
if (a.config.id === action.payload.collectionId) {
return {
...a,
data: {
...a.data,
[action.payload.actionId]: action.payload.results,
},
isExecuting: {
...a.isExecuting,
[action.payload.actionId]: false,
@ -318,6 +322,26 @@ const jsActionsReducer = createReducer(initialState, {
}
return a;
}),
[ReduxActionTypes.SET_JS_FUNCTION_EXECUTION_DATA]: (
state: JSCollectionDataState,
action: ReduxAction<BatchedJSExecutionData>,
): JSCollectionDataState =>
state.map((jsCollectionData) => {
const collectionId = jsCollectionData.config.id;
if (collectionId in action.payload) {
let data = {
...jsCollectionData.data,
};
action.payload[collectionId].forEach((item) => {
data = { ...data, [item.actionId]: item.data };
});
return {
...jsCollectionData,
data,
};
}
return jsCollectionData;
}),
[ReduxActionTypes.UPDATE_JS_FUNCTION_PROPERTY_SUCCESS]: (
state: JSCollectionDataState,
action: ReduxAction<{ collection: JSCollection }>,

View File

@ -13,6 +13,7 @@ const initialState: DatasourcePaneReduxState = {
newDatasource: "",
viewMode: true,
collapsibleState: {},
defaultKeyValueArrayConfig: [],
};
export interface DatasourcePaneReduxState {
@ -27,6 +28,7 @@ export interface DatasourcePaneReduxState {
newDatasource: string;
viewMode: boolean;
collapsibleState: Record<string, boolean>;
defaultKeyValueArrayConfig: Array<string>;
}
const datasourcePaneReducer = createReducer(initialState, {
@ -137,6 +139,25 @@ const datasourcePaneReducer = createReducer(initialState, {
expandDatasourceId: action.payload,
};
},
[ReduxActionTypes.SET_DATASOURCE_DEFAULT_KEY_VALUE_PAIR_SET]: (
state: DatasourcePaneReduxState,
action: ReduxAction<string>,
) => {
return {
...state,
defaultKeyValueArrayConfig: state.defaultKeyValueArrayConfig.concat(
action.payload,
),
};
},
[ReduxActionTypes.RESET_DATASOURCE_DEFAULT_KEY_VALUE_PAIR_SET]: (
state: DatasourcePaneReduxState,
) => {
return {
...state,
defaultKeyValueArrayConfig: [],
};
},
});
export default datasourcePaneReducer;

View File

@ -536,7 +536,9 @@ function* testDatasourceSaga(actionPayload: ReduxAction<Datasource>) {
id: actionPayload.payload.id as any,
};
if (!equal(initialValues, values)) {
// when datasource is not yet saved by user, datasource id is temporary
// for temporary datasource, we do not need to pass datasource id in test api call
if (!equal(initialValues, values) || datasource?.id === TEMP_DATASOURCE_ID) {
delete payload.id;
}

View File

@ -111,6 +111,8 @@ import {
EvalTreeRequestData,
EvalTreeResponseData,
} from "workers/Evaluation/types";
import { BatchedJSExecutionData } from "reducers/entityReducers/jsActionsReducer";
import { sortJSExecutionDataByCollectionId } from "workers/Evaluation/JSObject/utils";
import { MessageType, TMessage } from "utils/MessageUtil";
const evalWorker = new GracefulWorkerService(
@ -365,6 +367,16 @@ export function* handleEvalWorkerMessage(message: TMessage<any>) {
);
break;
}
case MAIN_THREAD_ACTION.PROCESS_JS_FUNCTION_EXECUTION: {
const sortedData: BatchedJSExecutionData = yield sortJSExecutionDataByCollectionId(
data.JSData as Record<string, unknown>,
);
yield put({
type: ReduxActionTypes.SET_JS_FUNCTION_EXECUTION_DATA,
payload: sortedData,
});
break;
}
case MAIN_THREAD_ACTION.PROCESS_TRIGGER: {
const { eventType, trigger, triggerMeta } = data;
log.debug({ trigger: data.trigger });

View File

@ -379,7 +379,6 @@ export function* handleExecuteJSFunctionSaga(data: {
yield put({
type: ReduxActionTypes.EXECUTE_JS_FUNCTION_SUCCESS,
payload: {
results: result,
collectionId,
actionId,
isDirty,
@ -394,6 +393,20 @@ export function* handleExecuteJSFunctionSaga(data: {
},
state: { response: result },
});
if (!action.actionConfiguration.isAsync) {
yield put({
type: ReduxActionTypes.SET_JS_FUNCTION_EXECUTION_DATA,
payload: {
[collectionId]: [
{
data: result,
collectionId,
actionId,
},
],
},
});
}
appMode === APP_MODE.EDIT &&
!isDirty &&
Toaster.show({

View File

@ -98,7 +98,7 @@ function logLatestEvalPropertyErrors(
}
const idField = isWidget(entity) ? entity.widgetId : entity.actionId;
const nameField = isWidget(entity) ? entity.widgetName : entity.name;
const nameField = isWidget(entity) ? entity.widgetName : entityName;
const entityType = isWidget(entity)
? ENTITY_TYPE.WIDGET
: isAction(entity)

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