From fa709a6cdeffb7cffa47ccc10458b15bd4ec17b2 Mon Sep 17 00:00:00 2001 From: Shrikant Sharat Kandula Date: Wed, 7 Oct 2020 21:06:58 +0530 Subject: [PATCH 1/6] Use email as id for users and fix signup events not being reported (#1067) --- .../java/com/appsmith/server/services/AnalyticsService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/AnalyticsService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/AnalyticsService.java index 53ffba9bdc..8e83b2818d 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/AnalyticsService.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/AnalyticsService.java @@ -65,13 +65,13 @@ public class AnalyticsService { return userMono .map(user -> { - // In case the user is anonymous, return as is without raising the event. - if (user.isAnonymous()) { + // In case the user is anonymous, don't raise an event, unless it's a signup event. + if (user.isAnonymous() && !(object instanceof User && event == AnalyticsEvents.CREATE)) { return object; } HashMap analyticsProperties = new HashMap<>(); - analyticsProperties.put("id", object.getId()); + analyticsProperties.put("id", object instanceof User ? ((User) object).getUsername() : object.getId()); analyticsProperties.put("object", object.toString()); analytics.enqueue( From 44201178889e53daabc32a367965eecd855ffbeb Mon Sep 17 00:00:00 2001 From: Shrikant Sharat Kandula Date: Fri, 9 Oct 2020 12:47:27 +0530 Subject: [PATCH 2/6] Use username instead of object_id in analytics (#1082) --- .../java/com/appsmith/server/services/AnalyticsService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/AnalyticsService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/AnalyticsService.java index 8e83b2818d..a737036076 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/AnalyticsService.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/AnalyticsService.java @@ -71,8 +71,9 @@ public class AnalyticsService { } HashMap analyticsProperties = new HashMap<>(); - analyticsProperties.put("id", object instanceof User ? ((User) object).getUsername() : object.getId()); + analyticsProperties.put("id", (object instanceof User ? (User) object : user).getUsername()); analyticsProperties.put("object", object.toString()); + analyticsProperties.put("oid", object.getId()); analytics.enqueue( TrackMessage.builder(eventTag) From 8f080a05cee25757e2897f6d0312ce575995d9df Mon Sep 17 00:00:00 2001 From: Nikhil Nandagopal Date: Mon, 12 Oct 2020 14:57:30 +0530 Subject: [PATCH 3/6] added app id to execute action tracking (#1139) --- app/client/src/sagas/ActionExecutionSagas.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/client/src/sagas/ActionExecutionSagas.ts b/app/client/src/sagas/ActionExecutionSagas.ts index 4adc89db06..5544de391c 100644 --- a/app/client/src/sagas/ActionExecutionSagas.ts +++ b/app/client/src/sagas/ActionExecutionSagas.ts @@ -296,11 +296,12 @@ export function* executeActionSaga( ); try { const api: RestAction = yield select(getAction, actionId); - + const currentAppId = yield select(getCurrentApplicationId); AnalyticsUtil.logEvent("EXECUTE_ACTION", { type: api.pluginType, name: api.name, pageId: api.pageId, + appId: currentAppId, }); if (api.confirmBeforeExecute) { const confirmed = yield call(confirmRunActionSaga); @@ -614,6 +615,7 @@ function* executePageLoadAction(pageAction: PageAction) { PerformanceTransactionName.EXECUTE_PAGE_LOAD_ACTIONS, ); const pageId = yield select(getCurrentPageId); + const appId = yield select(getCurrentApplicationId); yield put(executeApiActionRequest({ id: pageAction.id })); const params: Property[] = yield call( getActionParams, @@ -627,6 +629,7 @@ function* executePageLoadAction(pageAction: PageAction) { type: pageAction.pluginType, name: pageAction.name, pageId: pageId, + appId: appId, onPageLoad: true, }); const response: ActionApiResponse = yield ActionAPI.executeAction( From 928ea68b0fc60f2010afc49c1c58f03e549aa669 Mon Sep 17 00:00:00 2001 From: Nikhil Nandagopal Date: Mon, 12 Oct 2020 18:13:35 +0530 Subject: [PATCH 4/6] Release v1.2.7 (#1144) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update README.md * Updating README to add contributor section (#870) * Remove gitlab-ci configuration files (#865) We have since moved our CI to Github Actions and no longer require Gitlab CI config files. * Updating the README in the client codebase We now reference the global contribution guide directly * Feature/share app test (#843) * Add ShareApp test * Sharing app tests and public access * Update CONTRIBUTING.md * Update install.sh fixes #889 * Update deploy install script to use api64.ipify.org which supports IPv4 (#887) Co-authored-by: Nikhil Nandagopal * Id value issue for radio groups (#895) Fixes: #661 There was issue related to id value while creating new radio group options other than the default ones. And as said I, the id field is not needed at all, but some old config still has the id field. So since ID field is not required , I have removed it. Which solves the issue. * Fix triggering of onChange when value is the same (#892) Fixes: #710 * Invite user modal in view mode can be closed by clicking outside (#906) * Update README.md Spelling correction * #678 Correction in contributing.md for client setup (#907) Co-authored-by: Saket Agrawal * Docs: Add instructions in client setup for any port conflicts with 80/443 (#918) Co-authored-by: Arpit Mohan * Fix(sign-up): change in sign up error message (#908) * Fix(backend): Wrong error message while resetting the password fixed, changed from id to email (#911) Fixes #637 * Options Validaor checks for empty values Fixes: #662 * fix: add missed error callback for download (#912) Fixes: #673 * Revert "Options Validaor checks for empty values" This reverts commit 6b49d4c4e4c0d85f80648c14a7b6fc13bbde64d4. * Update table icon (#761) * style: center login form when 3rd party auth is not enabled (#919) * style: center login form when 3rd party auth is not enabled * style: center signup form when 3rd party auth is not enabled * fix: input widget number validation #768 (#917) * Explorer fixes (#689) * Make entity icon an actionable area Persist user toggled entity collapse states * remove dependency on props.isDefaultExpanded * Hover effect for dependencies * Pass search keyword for reliable toggling of entity groups on entity search * Fix default expanded state of Datasource group entity * Remove plugingroup file, which was added due to a botched merge conflict resolution * Fix issue with radio widget crashing (#922) * fix types * Options Validaor checks for empty values Fixes: #662 Co-authored-by: Sanchit Jain <171220040@nitdelhi.ac.in> * Fixed the pull request template issue partially (#902) Fixes #826 * Feature: Invite modal (#927) * light and dark mode added in Invite modal * warnings removed * create org and invite user test cases fixed * Application invite design implemented * manage user icon added * button width fixed * PR feedback implemented * Improve PR template. * Remove troublesome test * Fix failing Radio tests (#931) * Possible fix for CI checkout issue * Possible fix for CI checkout issue * Fixed the ipify URL in PingScheduledTask (#1013) * Revert "Feature: Invite modal (#927)" (#1017) This reverts commit 63daf74a44f33569611922a21026f2bf0f00f21b. * Fix image widget not showing default image when image property is invalid (#882) Fixes: #760 * Fix CI checkouts for all pull_request_target steps * Local meta state in widgets (#851) * Update README.md * Fix for action execution (#1023) * Adding the condition to fix push vs pull_request_target commit checkouts * Input Widget: Parse regexp before regex validation (#884) Fixes #697 * Display warning icon for forgot password failure (#890) Fixes: #587 * Create Org Form: don't allow to use only spaces in names (#913) Also disables the submit button when invalid Fixes: #807, #621 * Auto Focus on email fields in auth screens (#923) Fixes: #610 Co-authored-by: Anshul * Added check for duplicate values in Dropdown and Radio Widgets (#1011) Fixes #655 * Fix Reset password not disabled after successful reset (#1021) Fixes #636 * Fix active styles for checkbox & radio (#1061) Fixes: #994 * Add source to user sign up event (#1065) * Send separate user create event for new users (#1066) * Expand datasource entity in explorer based on some conditions (#1062) * Expand datasource entity in explorer based on some conditions * Fix test * Do not show error tost when api fails * Expand datasource entity on test success * Use email as id for users and fix signup events not being reported (#1067) * Fixing typos in the README file (#871) Co-authored-by: Arpit Mohan * Forgot password submit button enable condition (#701) Fixes: #586 Co-authored-by: Anshul * Adding a new email template for sending an email when an appsmith use… (#1077) * Adding a new email template for sending an email when an appsmith user's role in an organization changes. * Minor changes in text. * Fix - Distorted UI when there are too many characters in email ID #584 (#1010) * Fix - Distorted UI when there are too many characters in email ID #584 * Fix - Distorted UI when there are too many characters in email ID #584 * Fix the hidden column list is not bounded and overflows in height (#981) (#1024) * fix(applications): add validation for empty name (#914) * Update CONTRIBUTING.md * Update CodeContributionsGuidelines.md * replaced dragdrop with dsl (#1081) * Fix datasource structure test (#1080) * Fix datasource structure test * Dummy commit * CI: Try creating build id * CI: Fix build id step: * Revert build id changes * Update README.md * Fix/new ui is distorted due to long organization name #735 (#1014) * Fix for long org names on side menu. Now ellipsizes with tooltip if Orgname is over the value set in ellipsize prop. Otherwise functions as before. * added ellipsize prop to index. Fixes #735 20 seems to be a good value that stop org names running onto next line. Tested on Firefox and chrome. Linux debian stretch. Co-authored-by: root * Use username instead of object_id in analytics (#1082) * Adding the close-label.yml file required by Github App to mark issues as QA (#1091) Fixes #1090 When a PR is successfully merged, the Github bot will add the label QA to the issue. These issues can then be picked up by the QA team for verification. Refer: https://github.com/Logerfo/close-label * Only mark applications as example applications if the application id exists in the template configuration. (#1093) * docs: add mohanarpit as a contributor (#1095) * docs: update README.md [skip ci] * docs: create .all-contributorsrc [skip ci] * Moving the contributor badge to the correct location in README Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Arpit Mohan * docs: add Nikhil-Nandagopal as a contributor (#1096) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Arpit Mohan * docs: add areyabhishek as a contributor (#1097) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> * docs: add trishaanand as a contributor (#1098) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> * docs: add riodeuno as a contributor (#1099) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> * docs: add hetunandu as a contributor (#1100) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Trisha Anand * docs: add satbir121 as a contributor (#1106) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Arpit Mohan * docs: add sharat87 as a contributor (#1102) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Arpit Mohan * docs: add aakashDesign as a contributor (#1107) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> * docs: add Debsourabh as a contributor (#1108) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> * docs: add NandanAnantharamu as a contributor (#1103) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Arpit Mohan * docs: add prapullac as a contributor (#1104) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Arpit Mohan * docs: add Saket2 as a contributor (#1109) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> * docs: add harishkotra as a contributor (#1110) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> * docs: add visibleajay as a contributor (#1111) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> * docs: add akbansa as a contributor (#1112) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> * docs: add gogetter22 as a contributor (#1113) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> * docs: add Xniveres as a contributor (#1114) * docs: update README.md [skip ci] * docs: update .all-contributorsrc [skip ci] Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> * Correcting hyperlink from contributor badge to contributor section * Show datasource structure fixes (#1078) * Tests for binding related use cases (#1116) Co-authored-by: Nandan * Feature/video widget test (#1115) Co-authored-by: nandan.anantharamu * Adding filter tests for table (#1117) Co-authored-by: nandan.anantharamu * Getting Cypress to work correctly for internal pull requests (#1121) * Hotfix to fix the client build workflow * Update README.md * Fix release cleanup issues (#1132) * Changed CSS to prevent share button from coming out (#925) Changed CSS to prevent share button from coming out. Decreased the width of the wrapper to a suitable value. * Fix issue with setting dynamic triggers on immutable widget objects (#1137) * Feature/invitation modal (#938) * light and dark mode added in Invite modal * warnings removed * create org and invite user test cases fixed * Application invite design implemented * manage user icon added * button width fixed * PR feedback implemented * test cases fixed * used blueprint classes * used calc for width distribution * copy button width fixed * prop passing fixed * copy button size reduced * readonly input field background color fixed * input background theme name ordering changed * TagInputComponent moved to ads * created DropdownWrapper for select field in orgInviteForm * Warnings created due to unique key and depdencies is fixed * Warning fixed in dropdown component * correct prop name used Co-authored-by: Rohit Kumawat * fix(codemirror-autocomplete): show selection background on hover for options (#1134) This fix if merged shows the selection background by hovering on autocomplete options. Ref: #946 * Stop expanding datasource in explorer after testing (#1138) Co-authored-by: areyabhishek Co-authored-by: Arpit Mohan Co-authored-by: Ari <36749814+ari-hacks@users.noreply.github.com> Co-authored-by: NandanAnantharamu <67676905+NandanAnantharamu@users.noreply.github.com> Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com> Co-authored-by: Sanchit Jain <171220040@nitdelhi.ac.in> Co-authored-by: Nicholas Co-authored-by: Aditya Vats Co-authored-by: Saket2 <49346036+Saket2@users.noreply.github.com> Co-authored-by: Saket Agrawal Co-authored-by: MartinT <44962077+MartinTuroci@users.noreply.github.com> Co-authored-by: Dmitriy Danilov Co-authored-by: Petro Popelyshko Co-authored-by: Hetu Nandu Co-authored-by: akash-codemonk <67054171+akash-codemonk@users.noreply.github.com> Co-authored-by: Abhinav Jha Co-authored-by: Omkar Phansopkar <48476025+OmkarPh@users.noreply.github.com> Co-authored-by: devrk96 Co-authored-by: Prashant Chaubey Co-authored-by: satbir121 <39981226+satbir121@users.noreply.github.com> Co-authored-by: Ajay Kumar Co-authored-by: Arpit Mohan Co-authored-by: dodococo Co-authored-by: Josh Mak Co-authored-by: Anshul Bansal Co-authored-by: Anshul Co-authored-by: Caitlin-Fotheringham <49273562+Caitlin-Fotheringham@users.noreply.github.com> Co-authored-by: Akash Hamirwasia Co-authored-by: Yash Joshi Co-authored-by: Shrikant Sharat Kandula Co-authored-by: Aadhitya A <59508546+alphaX86@users.noreply.github.com> Co-authored-by: Trisha Anand Co-authored-by: Kishore Co-authored-by: Adam Co-authored-by: root Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Nandan Co-authored-by: Ishaan Mehta Co-authored-by: Thakur Karthik --- .all-contributorsrc | 190 +++++++++++ .github/close-label.yml | 2 + .github/workflows/client.yml | 4 +- README.md | 53 +++- app/client/.gitlab-ci.yml | 193 ----------- app/client/README.md | 77 +---- .../cypress/fixtures/MultipleInput.json | 72 +++++ .../cypress/fixtures/MultipleWidgetDsl.json | 102 ++++++ app/client/cypress/fixtures/example.json | 20 +- .../cypress/fixtures/formWidgetdsl.json | 233 ++++++++++++++ app/client/cypress/fixtures/rundsl.json | 93 ++++++ app/client/cypress/fixtures/testdata.json | 24 +- .../cypress/fixtures/videoWidgetDsl.json | 40 +++ .../Binding/Widget_loading_spec.js | 89 ++++++ .../Widgets_Default_data_validation_spec.js | 65 ++++ .../Widgets_Dependancy_validation_spec.js | 100 ++++++ .../DisplayWidgets/Table_spec.js | 115 +++++++ .../DisplayWidgets/video_spec.js | 30 ++ ...tity_Explorer_Datasource_Structure_spec.js | 8 +- ..._Explorer_Widgets_Copy_Delete_Undo_spec.js | 14 +- ...y_Paste_Delete_Undo_Keyboard_Event_spec.js | 14 +- .../FormWidgets/FormReset_spec.js | 1 - .../Smoke_TestSuite/FormWidgets/Radio_spec.js | 5 +- .../OrganisationTests/ShareAppTests_spec.js | 128 ++++++++ app/client/cypress/locators/HomePage.json | 14 +- app/client/cypress/locators/Widgets.json | 7 +- .../cypress/locators/commonlocators.json | 7 +- .../cypress/locators/publishWidgetspage.json | 7 +- app/client/cypress/support/commands.js | 96 +++++- app/client/jest.config.js | 5 +- app/client/package.json | 4 + app/client/src/actions/datasourceActions.ts | 7 + app/client/src/assets/icons/ads/manage.svg | 4 + .../assets/icons/menu/datasource-table.svg | 17 +- app/client/src/components/ads/Button.tsx | 59 +++- app/client/src/components/ads/Callout.tsx | 21 +- app/client/src/components/ads/Dropdown.tsx | 49 ++- .../src/components/ads/EditableText.tsx | 52 +-- app/client/src/components/ads/Icon.tsx | 6 + app/client/src/components/ads/MenuItem.tsx | 23 +- .../TagInputComponent.tsx | 44 ++- app/client/src/components/ads/TextInput.tsx | 40 ++- app/client/src/components/ads/Toggle.tsx | 2 +- .../appsmith/CopyToClipBoard.tsx | 47 +-- .../designSystems/appsmith/ImageComponent.tsx | 1 + .../appsmith/TableColumnsVisibility.tsx | 14 +- .../blueprint/RadioGroupComponent.tsx | 2 +- .../CodeEditor/styledComponents.ts | 6 + .../editorComponents/InputComponent.tsx | 2 + .../form/FormDialogComponent.tsx | 34 +- .../editorComponents/form/FormTextField.tsx | 1 + .../form/fields/DropdownWrapper.tsx | 38 +++ .../form/fields/SelectField.tsx | 8 +- .../form/fields/TagListField.tsx | 2 +- .../propertyControls/ChartDataControl.tsx | 9 +- .../components/stories/Callout.stories.tsx | 4 +- .../components/stories/Dropdown.stories.tsx | 3 + app/client/src/constants/DefaultTheme.tsx | 174 ++++++++-- .../src/constants/ReduxActionConstants.tsx | 1 + app/client/src/jsExecution/RealmExecutor.ts | 1 - .../mockResponses/WidgetConfigResponse.tsx | 4 +- .../AppViewer/viewer/AppViewerHeader.tsx | 1 + .../pages/Applications/ApplicationCard.tsx | 8 +- .../Applications/CreateApplicationForm.tsx | 22 +- app/client/src/pages/Applications/helpers.ts | 2 + app/client/src/pages/Applications/index.tsx | 33 +- .../Editor/Explorer/Actions/ActionsGroup.tsx | 1 + .../Explorer/Datasources/DatasourceEntity.tsx | 35 +- .../pages/Editor/Explorer/Entity/index.tsx | 22 +- .../pages/Editor/Explorer/JSDependencies.tsx | 4 + .../pages/Editor/Explorer/Pages/PageGroup.tsx | 1 + .../Editor/Explorer/Widgets/WidgetGroup.tsx | 1 + .../src/pages/Editor/Explorer/helpers.tsx | 9 + .../src/pages/UserAuth/ForgotPassword.tsx | 10 +- app/client/src/pages/UserAuth/Login.tsx | 11 +- .../src/pages/UserAuth/ResetPassword.tsx | 3 +- app/client/src/pages/UserAuth/SignUp.tsx | 9 +- .../src/pages/UserAuth/StyledComponents.tsx | 3 + .../src/pages/common/ProfileDropdown.tsx | 6 +- .../pages/organization/AppInviteUsersForm.tsx | 46 ++- .../organization/CreateOrganizationForm.tsx | 6 +- .../pages/organization/OrgInviteUsersForm.tsx | 246 +++++++++----- .../uiReducers/datasourcePaneReducer.ts | 13 + app/client/src/sagas/ActionExecutionSagas.ts | 2 + app/client/src/sagas/DatasourcesSagas.ts | 19 +- app/client/src/sagas/ErrorSagas.tsx | 4 +- app/client/src/sagas/WidgetOperationSagas.tsx | 4 +- app/client/src/utils/Validators.ts | 16 +- app/client/src/utils/formhelpers.ts | 6 +- app/client/src/widgets/BaseWidget.tsx | 10 - app/client/src/widgets/CheckboxWidget.tsx | 22 +- app/client/src/widgets/DatePickerWidget.tsx | 22 +- app/client/src/widgets/DropdownWidget.tsx | 56 ++-- app/client/src/widgets/FilepickerWidget.tsx | 13 +- app/client/src/widgets/FormWidget.tsx | 5 +- app/client/src/widgets/ImageWidget.test.tsx | 128 ++++++++ app/client/src/widgets/ImageWidget.tsx | 2 +- app/client/src/widgets/InputWidget.tsx | 98 +++--- app/client/src/widgets/MapWidget.tsx | 54 ++-- app/client/src/widgets/MetaHOC.tsx | 120 +++++++ app/client/src/widgets/ModalWidget.tsx | 7 +- app/client/src/widgets/RadioGroupWidget.tsx | 23 +- .../src/widgets/RichTextEditorWidget.tsx | 25 +- app/client/src/widgets/TableWidget.tsx | 196 ++++++------ app/client/src/widgets/TabsWidget.tsx | 30 +- app/client/src/widgets/VideoWidget.tsx | 46 ++- app/client/test/__mocks__/fileMock.js | 1 + app/client/yarn.lock | 219 ++++++++++++- app/server/.gitlab-ci.yml | 89 ------ .../server/exceptions/AppsmithError.java | 3 +- .../services/ApplicationServiceImpl.java | 12 +- .../server/services/UserServiceImpl.java | 2 +- .../server/solutions/PingScheduledTask.java | 2 +- .../email/updateRoleExistingUserTemplate.html | 299 ++++++++++++++++++ .../ExampleApplicationsAreMarked.java | 16 +- contributions/ClientSetup.md | 13 +- contributions/CodeContributionsGuidelines.md | 4 +- contributions/docs/CONTRIBUTING.md | 7 +- 118 files changed, 3446 insertions(+), 1108 deletions(-) create mode 100644 .all-contributorsrc create mode 100644 .github/close-label.yml delete mode 100644 app/client/.gitlab-ci.yml create mode 100644 app/client/cypress/fixtures/MultipleInput.json create mode 100644 app/client/cypress/fixtures/MultipleWidgetDsl.json create mode 100644 app/client/cypress/fixtures/formWidgetdsl.json create mode 100644 app/client/cypress/fixtures/rundsl.json create mode 100644 app/client/cypress/fixtures/videoWidgetDsl.json create mode 100644 app/client/cypress/integration/Smoke_TestSuite/Binding/Widget_loading_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/Binding/Widgets_Default_data_validation_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/Binding/Widgets_Dependancy_validation_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/DisplayWidgets/video_spec.js create mode 100644 app/client/cypress/integration/Smoke_TestSuite/OrganisationTests/ShareAppTests_spec.js create mode 100644 app/client/src/assets/icons/ads/manage.svg rename app/client/src/components/{editorComponents => ads}/TagInputComponent.tsx (76%) create mode 100644 app/client/src/components/editorComponents/form/fields/DropdownWrapper.tsx create mode 100644 app/client/src/widgets/ImageWidget.test.tsx create mode 100644 app/client/src/widgets/MetaHOC.tsx create mode 100644 app/client/test/__mocks__/fileMock.js delete mode 100644 app/server/.gitlab-ci.yml create mode 100644 app/server/appsmith-server/src/main/resources/email/updateRoleExistingUserTemplate.html diff --git a/.all-contributorsrc b/.all-contributorsrc new file mode 100644 index 0000000000..f794f2f9dc --- /dev/null +++ b/.all-contributorsrc @@ -0,0 +1,190 @@ +{ + "files": [ + "README.md" + ], + "imageSize": 100, + "commit": false, + "contributors": [ + { + "login": "mohanarpit", + "name": "Arpit Mohan", + "avatar_url": "https://avatars2.githubusercontent.com/u/458946?v=4", + "profile": "http://arpitmohan.com", + "contributions": [ + "code" + ] + }, + { + "login": "Nikhil-Nandagopal", + "name": "Nikhil Nandagopal", + "avatar_url": "https://avatars2.githubusercontent.com/u/3897254?v=4", + "profile": "https://github.com/Nikhil-Nandagopal", + "contributions": [ + "doc", + "code", + "projectManagement" + ] + }, + { + "login": "areyabhishek", + "name": "areyabhishek", + "avatar_url": "https://avatars1.githubusercontent.com/u/30255708?v=4", + "profile": "https://github.com/areyabhishek", + "contributions": [ + "ideas", + "design" + ] + }, + { + "login": "trishaanand", + "name": "Trisha Anand", + "avatar_url": "https://avatars2.githubusercontent.com/u/8403079?v=4", + "profile": "https://github.com/trishaanand", + "contributions": [ + "code", + "infra", + "ideas" + ] + }, + { + "login": "hetunandu", + "name": "Hetu Nandu", + "avatar_url": "https://avatars2.githubusercontent.com/u/12022471?v=4", + "profile": "https://github.com/hetunandu", + "contributions": [ + "code", + "test", + "ideas" + ] + }, + { + "login": "riodeuno", + "name": "Abhinav Jha", + "avatar_url": "https://avatars1.githubusercontent.com/u/103687?v=4", + "profile": "https://github.com/riodeuno", + "contributions": [ + "code" + ] + }, + { + "login": "satbir121", + "name": "satbir121", + "avatar_url": "https://avatars3.githubusercontent.com/u/39981226?v=4", + "profile": "https://github.com/satbir121", + "contributions": [ + "code", + "ideas" + ] + }, + { + "login": "sharat87", + "name": "Shrikant Sharat Kandula", + "avatar_url": "https://avatars3.githubusercontent.com/u/120119?v=4", + "profile": "https://sharats.me", + "contributions": [ + "code", + "plugin" + ] + }, + { + "login": "aakashDesign", + "name": "Aakash Shrivastava", + "avatar_url": "https://avatars2.githubusercontent.com/u/65771350?v=4", + "profile": "https://github.com/aakashDesign", + "contributions": [ + "design" + ] + }, + { + "login": "Debsourabh", + "name": "Debsourabh Ghosh", + "avatar_url": "https://avatars2.githubusercontent.com/u/34486435?v=4", + "profile": "https://github.com/Debsourabh", + "contributions": [ + "design" + ] + }, + { + "login": "NandanAnantharamu", + "name": "NandanAnantharamu", + "avatar_url": "https://avatars1.githubusercontent.com/u/67676905?v=4", + "profile": "https://github.com/NandanAnantharamu", + "contributions": [ + "test" + ] + }, + { + "login": "prapullac", + "name": "prapullac", + "avatar_url": "https://avatars3.githubusercontent.com/u/71753653?v=4", + "profile": "https://github.com/prapullac", + "contributions": [ + "bug", + "test" + ] + }, + { + "login": "Saket2", + "name": "Saket Agrawal", + "avatar_url": "https://avatars0.githubusercontent.com/u/49346036?v=4", + "profile": "https://github.com/Saket2", + "contributions": [ + "bug", + "doc" + ] + }, + { + "login": "harishkotra", + "name": "Harish Kotra", + "avatar_url": "https://avatars1.githubusercontent.com/u/4999463?v=4", + "profile": "https://harishkotra.me", + "contributions": [ + "bug" + ] + }, + { + "login": "visibleajay", + "name": "Ajay Kumar", + "avatar_url": "https://avatars0.githubusercontent.com/u/13945951?v=4", + "profile": "https://github.com/visibleajay", + "contributions": [ + "bug", + "doc" + ] + }, + { + "login": "akbansa", + "name": "Anshul Bansal", + "avatar_url": "https://avatars0.githubusercontent.com/u/13042781?v=4", + "profile": "https://github.com/akbansa", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "gogetter22", + "name": "Navia Garg", + "avatar_url": "https://avatars3.githubusercontent.com/u/71608910?v=4", + "profile": "https://github.com/gogetter22", + "contributions": [ + "bug" + ] + }, + { + "login": "Xniveres", + "name": "Xniveres", + "avatar_url": "https://avatars0.githubusercontent.com/u/56609232?v=4", + "profile": "https://github.com/Xniveres", + "contributions": [ + "bug" + ] + } + ], + "contributorsPerLine": 7, + "projectName": "appsmith", + "projectOwner": "appsmithorg", + "repoType": "github", + "repoHost": "https://github.com", + "skipCi": true +} diff --git a/.github/close-label.yml b/.github/close-label.yml new file mode 100644 index 0000000000..26903e21c5 --- /dev/null +++ b/.github/close-label.yml @@ -0,0 +1,2 @@ +Bug: QA +Enhancement: QA \ No newline at end of file diff --git a/.github/workflows/client.yml b/.github/workflows/client.yml index cf91382238..093e06a738 100644 --- a/.github/workflows/client.yml +++ b/.github/workflows/client.yml @@ -190,6 +190,7 @@ jobs: CYPRESS_TESTPASSWORD1: ${{ secrets.CYPRESS_TESTPASSWORD1 }} CYPRESS_TESTUSERNAME2: ${{ secrets.CYPRESS_TESTUSERNAME2 }} CYPRESS_TESTPASSWORD2: ${{ secrets.CYPRESS_TESTPASSWORD1 }} + COMMIT_INFO_MESSAGE: ${{ github.event.pull_request.title }} with: browser: chrome headless: true @@ -197,9 +198,10 @@ jobs: install: false parallel: true group: 'Electrons on Github Action' - ci-build-id: '${{ github.sha }}-${{ github.workflow }}-${{ github.event_name }}' spec: 'cypress/integration/Smoke_TestSuite/*/*' working-directory: app/client + # tag will be either "push" or "pull_request_target" + tag: ${{ github.event_name }} # Upload the screenshots as artifacts if there's a failure - uses: actions/upload-artifact@v1 diff --git a/README.md b/README.md index d1a12eea0e..191a1aeb11 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,16 @@ Appsmith.com logo

Appsmith

-

A low code way to build internal tools.

- The only open source alternative to Power Apps, Salesforce Lightning, Service Now, Retool, Forest Admin, and many more. +

A plug and play web framework to build internal tools.

[![GitHub release](https://img.shields.io/github/v/release/appsmithorg/appsmith.svg?logo=GitHub)](https://github.com/appsmithorg/appsmith/releases/latest) [![Website](https://img.shields.io/website?url=https%3A%2F%2Fappsmith.com&logo=Appsmith)](https://appsmith.com) [![Chat on Discord](https://img.shields.io/badge/chat-Discord-violet?logo=discord)](https://discord.gg/rBTTVJp) [![Docs](https://img.shields.io/badge/docs-v1.x-brightgreen.svg?style=flat)](https://docs.appsmith.com) + +[![All Contributors](https://img.shields.io/badge/all_contributors-49-orange.svg?style=flat-square)](#-contributors) +

@@ -18,7 +20,7 @@ ----------------- -

Create apps by connecting UI widgets to any database and writing any logic in JS.

+

Build apps by connecting UI widgets to database queries or APIs. Write any logic in JS.

![UI Builder Demo](https://github.com/appsmithOrg/appsmith/blob/master/static/demo.gif)
@@ -44,21 +46,22 @@ You can try our online sandbox or deploy a Docker image on a server. * [Online sandbox](https://bit.ly/appsmith-signup-github) * [Deploy with Docker](https://bit.ly/appsmith-docker-github) -## ⁉ Why Appsmith? +## πŸ˜‡ Why Appsmith? When we build internal tools today, we turn to admin panels, UI frameworks or use a bootstrap theme. We took inspirations from the best admin panels, bootstrap themes, and brought back the easy UI builder of Visual Basic. -Appsmith is a quicker way of building internal tools by visualising them as modular blocks (**Widgets, APIs, Queries, JS**) and giving developers a simple user interface to configure them. New features, UI modification, changing dataflows, and modifying business logic becomes a [piece of cake](https://i.kym-cdn.com/photos/images/newsfeed/001/355/125/5ca.png) because you no longer have to trudge through large undocumented code bases or wrestle with HTML/CSS. We understand that while some configurations are static and are better controlled via UI, a lot of it is dynamic and should be configured through code. Appsmith doesn't take the fun out of coding, instead it treats every block as an object and exposes it via javascript so that you can read, transform and manipulate it. Whether it's a widget, API or query, you get to decide where you need to configure and where you need to code! +Appsmith is a quicker way of building internal tools by visualising them as modular blocks (**Widgets, APIs, Queries, JS**) and giving developers a simple user interface to configure them. Building new features, creating UI, changing dataflows, and modifying business logic becomes simpler because you no longer have to trudge through large undocumented code bases or wrestle with HTML/CSS. +Appsmith doesn't take the fun out of coding, because it treats every block as an object and exposes it via javascript so that you can read, transform and manipulate it. Whether it's a widget, API or query, you get to decide where you need to configure using UI and where you need to code. ## 🏭 Features * **5 minute setup**: Deploy Appsmith on your servers in 5 minutes. * **Build custom UI**: Drag & drop, resize and style widgets **without HTML / CSS**. -* **Query data**: Query & update your database directly from the UI. Connect to **postgres, mongo, MySQL, REST & GraphQL APIs**. +* **Query data**: Query & update your database directly from the UI. Connect to **PostgreSQL, MongoDB, MySQL, REST & GraphQL APIs**. * **JS Logic**: Write snippets of business logic using JS to transform data, manipuate UI or trigger workflows. Use popular libraries like lodash & moment anywhere in the app * **Data Workflows**: Simple configuration to create flows when users interact with the UI. * **Realtime Editor**: Changes in your application reflect instantly with every edit. No need to compile! -* **Works with existing, live databases**: Connect directly to any Postgres, MySQL, & Mongo db +* **Works with existing, live databases**: Connect directly to any PostgreSQL, MySQL & MongoDB * **Fine-grained access control**: Control who can edit / view your applications from a single control panel * **App management**: Build and organise multiple applications on a single platform @@ -80,3 +83,39 @@ Read our [Contribution Guide](https://github.com/appsmithorg/appsmith/blob/maste The Appsmith platform is available under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0) (Apache-2.0). +## πŸ§‘β€πŸ€β€πŸ§‘ Contributors + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Arpit Mohan

πŸ’»

Nikhil Nandagopal

πŸ“– πŸ’» πŸ“†

areyabhishek

πŸ€” 🎨

Trisha Anand

πŸ’» πŸš‡ πŸ€”

Hetu Nandu

πŸ’» ⚠️ πŸ€”

Abhinav Jha

πŸ’»

satbir121

πŸ’» πŸ€”

Shrikant Sharat Kandula

πŸ’» πŸ”Œ

Aakash Shrivastava

🎨

Debsourabh Ghosh

🎨

NandanAnantharamu

⚠️

prapullac

πŸ› ⚠️

Saket Agrawal

πŸ› πŸ“–

Harish Kotra

πŸ›

Ajay Kumar

πŸ› πŸ“–

Anshul Bansal

πŸ› πŸ’»

Navia Garg

πŸ›

Xniveres

πŸ›
+ + + + diff --git a/app/client/.gitlab-ci.yml b/app/client/.gitlab-ci.yml deleted file mode 100644 index b089ff8f21..0000000000 --- a/app/client/.gitlab-ci.yml +++ /dev/null @@ -1,193 +0,0 @@ -.only-default: &only-default - only: - - release - - master - - merge_requests - -.set_env_variables: &set_env_variables - - | - if [ "$CI_COMMIT_BRANCH" == "master" ]; then - REACT_APP_ENVIRONMENT="PRODUCTION" - REACT_APP_BASE_URL="https://api.appsmith.com" - elif [ "$CI_COMMIT_BRANCH" == "release" ]; then - REACT_APP_ENVIRONMENT="STAGING" - REACT_APP_BASE_URL="https://release-api.appsmith.com" - else - REACT_APP_ENVIRONMENT="DEVELOPMENT" - REACT_APP_BASE_URL="https://release-api.appsmith.com" - fi - -.set_automation_env: &set_automation_env - - | - REACT_APP_ENVIRONMENT="AUTOMATION" - REACT_APP_BASE_URL="https://release-api.appsmith.com" - - -# This image contains Nginx & Cypress binaries -image: appsmith/cypress-nginx - -variables: - npm_config_cache: "$CI_PROJECT_DIR/.npm" - CYPRESS_CACHE_FOLDER: "$CI_PROJECT_DIR/cache/Cypress" - DOCKER_DRIVER: overlay - DOCKER_IMAGE_NAME: $CI_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME:$CI_COMMIT_SHORT_SHA - -cache: - key: ${CI_COMMIT_REF_SLUG} - paths: - - .npm - - cache/Cypress - - node_modules - -stages: - - build - - test - - package - - deploy - -react-build-release: - image: tarampampam/node:10.16-alpine - stage: build - script: - - *set_env_variables - - echo $REACT_APP_ENVIRONMENT - - yarn install - - REACT_APP_ENVIRONMENT=$REACT_APP_ENVIRONMENT REACT_APP_BASE_URL=$REACT_APP_BASE_URL GIT_SHA=$CI_COMMIT_SHORT_SHA yarn build - artifacts: - expire_in: 1 day - paths: - - build/ - only: - - release - - feature/acl - -react-build-automation: - image: tarampampam/node:10.16-alpine - stage: build - script: - - *set_automation_env - - echo $REACT_APP_ENVIRONMENT - - yarn install - - REACT_APP_ENVIRONMENT=$REACT_APP_ENVIRONMENT REACT_APP_BASE_URL=$REACT_APP_BASE_URL GIT_SHA=$CI_COMMIT_SHORT_SHA yarn build - # Copying the build folder so that it doesn't conflict with build images created for docker packaging - - mv build/ build-automation/ - artifacts: - expire_in: 6 hours - paths: - - build-automation/ - only: - - release - - merge_requests - -unit_test: - image: tarampampam/node:10.16-alpine - stage: test - # Start running this job only when the build-automation job is complete - needs: ["react-build-automation"] - script: - - *set_automation_env - - yarn run test:unit - only: - - release - - merge_requests - except: - variables: - - $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "feature/acl" - -# all jobs that actually run tests can use the same definition -cypress-test: - image: appsmith/cypress-nginx - stage: test - # Start running this job only when the build-automation job is complete - needs: ["react-build-automation"] - # Run 6 machines in parallel for this job - parallel: 6 - script: - - *set_automation_env - # show where the Cypress test runner binaries are cached - - $(npm bin)/cypress cache path - # show all installed versions of Cypress binary - - $(npm bin)/cypress cache list - - $(npm bin)/cypress verify - # This is required in order to ensure that all the test cases pass - - echo "127.0.0.1 dev.appsmith.com" >> /etc/hosts - - serve -s build-automation -p 3000 & - - mkdir -p /var/www/appsmith /etc/certificate - - cp ./docker/nginx-linux.conf /etc/nginx/conf.d/app.conf - - cp $APPSMITH_SSL_CERTIFICATE /etc/certificate/dev.appsmith.com.pem - - cp $APPSMITH_SSL_KEY /etc/certificate/dev.appsmith.com-key.pem - - nginx - # This command configures the cypress suite to point to our custom installation of sorry-cypress that will help us parallelize our tests - - | - DEBUG=cypress:* $(npm bin)/cypress version - sed -i -e 's|api_url:.*$|api_url: "https://appsmith-cypress.herokuapp.com/"|g' /builds/theappsmith/internal-tools-client/cache/Cypress/4.1.0/Cypress/resources/app/packages/server/config/app.yml - - BUILD_ID=$CI_COMMIT_SHORT_SHA yarn test:ci - artifacts: - when: always - expire_in: 1 week - paths: - - cypress/screenshots - - cypress/videos - only: - # We don't test on master right now because of changing environment variables for REACT_APP_BASE_URL. Need to figure out a way to configure that. - - release - - merge_requests - except: - variables: - - $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "feature/acl" - -docker-package-release: - image: docker:dind - services: - - docker:dind - stage: package - script: - - *set_env_variables - - docker build -t appsmith/appsmith-editor:release . - - docker login -u $DOCKER_HUB_USERNAME -p $DOCKER_HUB_ACCESS_TOKEN - - docker push appsmith/appsmith-editor:release - only: - - release - -react-build-prod: - image: tarampampam/node:10.16-alpine - stage: build - script: - - *set_env_variables - - yarn install - - REACT_APP_ENVIRONMENT=$REACT_APP_ENVIRONMENT GIT_SHA=$CI_COMMIT_SHORT_SHA yarn build - artifacts: - when: on_success - expire_in: 1 week - paths: - - build/ - only: - - master - -docker-package-prod: - image: docker:dind - services: - - docker:dind - stage: package - script: - - *set_env_variables - - docker build -t appsmith/appsmith-editor:latest . - - docker build -t appsmith/appsmith-editor:$CI_COMMIT_SHORT_SHA . - - docker login -u $DOCKER_HUB_USERNAME -p $DOCKER_HUB_ACCESS_TOKEN - # If we don't add the tag to the `docker push` command, all the tags for this image will be pushed - - docker push appsmith/appsmith-editor - only: - - master - -docker-package-acl: - image: docker:dind - services: - - docker:dind - stage: package - script: - - *set_env_variables - - docker build -t appsmith/appsmith-editor:acl . - - docker login -u $DOCKER_HUB_USERNAME -p $DOCKER_HUB_ACCESS_TOKEN - - docker push appsmith/appsmith-editor:acl - only: - - feature/acl diff --git a/app/client/README.md b/app/client/README.md index 055787f8ff..2cb736750f 100755 --- a/app/client/README.md +++ b/app/client/README.md @@ -4,82 +4,7 @@ - `nvm` ## Dev Setup -- `cd internal-tools-client` Change directory to the project directory -- `nvm install` Install the version of `node` and `npm` required by the project using `nvm` -- `yarn` Install packages and run setup scripts -- `yarn start` Start the client locally using this - -> For more details on how to run this locally, please visit: [Notion Doc](https://www.notion.so/appsmith/How-to-run-the-code-e031545454874419b9f72cd51feb90ff) This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). -## Available Scripts - -In the project directory, you can run: - -### `npm start` - -Runs the app in the development mode.
-Open [http://localhost:3000](http://localhost:3000) to view it in the browser. - -The page will reload if you make edits.
-You will also see any lint errors in the console. - -### `npm test` - -Launches the test runner in the interactive watch mode.
-See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. - -### `npm run build` - -Builds the app for production to the `build` folder.
-It correctly bundles React in production mode and optimizes the build for the best performance. - -The build is minified and the filenames include the hashes.
-Your app is ready to be deployed! - -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. - -### `npm run eject` - -**Note: this is a one-way operation. Once you `eject`, you can’t go back!** - -If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. - -Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. - -You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. - -## Learn More - -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). - -To learn React, check out the [React documentation](https://reactjs.org/). - -### Code Splitting - -This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting - -### Analyzing the Bundle Size - -This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size - -### Making a Progressive Web App - -This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app - -### Advanced Configuration - -This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration - -### Deployment - -This section has moved here: https://facebook.github.io/create-react-app/docs/deployment - -### `npm run build` fails to minify - -This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify - -### Cypress tests via Github Actions - -Cypress is our integration test framework of choice. The Cypress tests run via Github Actions. The Github Action `.github/workflows/client.yml` pulls the `release` Docker image of the server to run as a service locally. This is to ensure that we don't face any network flakiness during tests. +For details on setting up your development machine, please refer to the [Setup Guide](https://github.com/appsmithorg/appsmith/blob/release/contributions/ClientSetup.md) diff --git a/app/client/cypress/fixtures/MultipleInput.json b/app/client/cypress/fixtures/MultipleInput.json new file mode 100644 index 0000000000..7b64b30ada --- /dev/null +++ b/app/client/cypress/fixtures/MultipleInput.json @@ -0,0 +1,72 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1224, + "snapColumns": 16, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1280, + "containerStyle": "none", + "snapRows": 33, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "dynamicBindings": {}, + "version": 6, + "minHeight": 1292, + "parentColumnSpace": 1, + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "inputType": "TEXT", + "label": "", + "widgetName": "Input1", + "type": "INPUT_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 1, + "rightColumn": 6, + "topRow": 0, + "bottomRow": 1, + "parentId": "0", + "widgetId": "ftl8h620qf" + }, + { + "isVisible": true, + "inputType": "TEXT", + "label": "", + "widgetName": "Input2", + "type": "INPUT_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 7, + "rightColumn": 12, + "topRow": 1, + "bottomRow": 2, + "parentId": "0", + "widgetId": "p2oen3muq5" + }, + { + "isVisible": true, + "inputType": "TEXT", + "label": "", + "widgetName": "Input3", + "type": "INPUT_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 1, + "rightColumn": 6, + "topRow": 2, + "bottomRow": 3, + "parentId": "0", + "widgetId": "yjpjhnsisf" + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/MultipleWidgetDsl.json b/app/client/cypress/fixtures/MultipleWidgetDsl.json new file mode 100644 index 0000000000..3b6cd2dc78 --- /dev/null +++ b/app/client/cypress/fixtures/MultipleWidgetDsl.json @@ -0,0 +1,102 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1224, + "snapColumns": 16, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1280, + "containerStyle": "none", + "snapRows": 33, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "dynamicBindings": {}, + "version": 6, + "minHeight": 1292, + "parentColumnSpace": 1, + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "inputType": "TEXT", + "label": "", + "widgetName": "Input1", + "type": "INPUT_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 4, + "rightColumn": 9, + "topRow": 0, + "bottomRow": 1, + "parentId": "0", + "widgetId": "orkla1pg88" + }, + { + "isVisible": true, + "label": "", + "selectionType": "SINGLE_SELECT", + "options": "[\n {\n \"label\": \"Vegetarian\",\n \"value\": \"VEG\"\n },\n {\n \"label\": \"Non-Vegetarian\",\n \"value\": \"NON_VEG\"\n },\n {\n \"label\": \"Vegan\",\n \"value\": \"VEGAN\"\n }\n]", + "widgetName": "Dropdown1", + "defaultOptionValue": "VEG", + "type": "DROP_DOWN_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 4, + "rightColumn": 9, + "topRow": 2, + "bottomRow": 3, + "parentId": "0", + "widgetId": "9iofg44qjm", + "dynamicBindings": { + "isValid": true, + "selectedOption": true, + "selectedOptionArr": true, + "selectedIndex": true, + "selectedIndexArr": true, + "value": true, + "selectedOptionValues": true + } + }, + { + "isVisible": true, + "defaultText": "This is the initial content of the editor", + "isDisabled": false, + "widgetName": "RichTextEditor1", + "isDefaultClickDisabled": true, + "type": "RICH_TEXT_EDITOR_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 2, + "rightColumn": 10, + "topRow": 4, + "bottomRow": 9, + "parentId": "0", + "widgetId": "p4uowm5ds3" + }, + { + "isVisible": true, + "label": "Data", + "widgetName": "Table1", + "searchKey": "", + "tableData": "[\n {\n \"id\": 2381224,\n \"email\": \"michael.lawson@reqres.in\",\n \"userName\": \"Michael Lawson\",\n \"productName\": \"Chicken Sandwich\",\n \"orderAmount\": 4.99\n },\n {\n \"id\": 2736212,\n \"email\": \"lindsay.ferguson@reqres.in\",\n \"userName\": \"Lindsay Ferguson\",\n \"productName\": \"Tuna Salad\",\n \"orderAmount\": 9.99\n },\n {\n \"id\": 6788734,\n \"email\": \"tobias.funke@reqres.in\",\n \"userName\": \"Tobias Funke\",\n \"productName\": \"Beef steak\",\n \"orderAmount\": 19.99\n }\n]", + "type": "TABLE_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 3, + "rightColumn": 11, + "topRow": 12, + "bottomRow": 19, + "parentId": "0", + "widgetId": "iu9vqkj1rd", + "dynamicBindings": {} + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/example.json b/app/client/cypress/fixtures/example.json index 76d62301fe..ce5bfcd2b5 100644 --- a/app/client/cypress/fixtures/example.json +++ b/app/client/cypress/fixtures/example.json @@ -112,11 +112,11 @@ "RichTexteditorBody": "Here is the text area to edit html", "userApi": "http://postgrest.appsmith.com:3000", "validateImage": "https://cdn.dribbble.com/users/1787323/screenshots/4563995/dribbbe_hammer-01.png", - "defaultdata":"TestData", - "label":"one", - "rgbValue":"rgb(255, 0, 0)", - "tabName":"Aditya", - "TableInput":[ + "defaultdata": "TestData", + "label": "one", + "rgbValue": "rgb(255, 0, 0)", + "tabName": "Aditya", + "TableInput": [ { "id": 2381224, "email": "michael.lawson@reqres.in", @@ -167,7 +167,11 @@ "y": 1000 } ], - "Chartval":["Test1", "Test2", "Test3"], - "paginationUrl":"https://mock-api.appsmith.com/", - "paginationParam":"users?page={{Table1.pageNo}}&size={{Table1.pageSize }}" + "Chartval": [ + "Test1", + "Test2", + "Test3" + ], + "paginationUrl": "https://mock-api.appsmith.com/", + "paginationParam": "users?page={{Table1.pageNo}}&size={{Table1.pageSize }}" } \ No newline at end of file diff --git a/app/client/cypress/fixtures/formWidgetdsl.json b/app/client/cypress/fixtures/formWidgetdsl.json new file mode 100644 index 0000000000..a06bc610ca --- /dev/null +++ b/app/client/cypress/fixtures/formWidgetdsl.json @@ -0,0 +1,233 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1224, + "snapColumns": 16, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1280, + "containerStyle": "none", + "snapRows": 33, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "dynamicBindings": {}, + "version": 6, + "minHeight": 1292, + "parentColumnSpace": 1, + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "widgetName": "Form1", + "backgroundColor": "white", + "children": [ + { + "isVisible": true, + "widgetName": "Canvas1", + "containerStyle": "none", + "canExtend": false, + "detachFromLayout": true, + "children": [ + { + "isVisible": true, + "text": "Form", + "textStyle": "HEADING", + "textAlign": "LEFT", + "widgetName": "Text1", + "type": "TEXT_WIDGET", + "isLoading": false, + "leftColumn": 0, + "rightColumn": 12, + "topRow": 0, + "bottomRow": 1, + "parentId": "sidaue1kdu", + "widgetId": "ac6cc8wmlu" + }, + { + "isVisible": true, + "widgetName": "FormButton1", + "text": "Submit", + "isDefaultClickDisabled": true, + "buttonStyle": "PRIMARY_BUTTON", + "disabledWhenInvalid": true, + "resetFormOnClick": true, + "type": "FORM_BUTTON_WIDGET", + "isLoading": false, + "leftColumn": 12, + "rightColumn": 16, + "topRow": 11, + "bottomRow": 12, + "parentId": "sidaue1kdu", + "widgetId": "xyn7t20lhv" + }, + { + "isVisible": true, + "widgetName": "FormButton2", + "text": "Reset", + "isDefaultClickDisabled": true, + "buttonStyle": "SECONDARY_BUTTON", + "disabledWhenInvalid": false, + "resetFormOnClick": true, + "type": "FORM_BUTTON_WIDGET", + "isLoading": false, + "leftColumn": 8, + "rightColumn": 12, + "topRow": 11, + "bottomRow": 12, + "parentId": "sidaue1kdu", + "widgetId": "xlrmeiioaa" + } + ], + "blueprint": { + "view": [ + { + "type": "TEXT_WIDGET", + "size": { + "rows": 1, + "cols": 12 + }, + "position": { + "top": 0, + "left": 0 + }, + "props": { + "text": "Form", + "textStyle": "HEADING" + } + }, + { + "type": "FORM_BUTTON_WIDGET", + "size": { + "rows": 1, + "cols": 4 + }, + "position": { + "top": 11, + "left": 12 + }, + "props": { + "text": "Submit", + "buttonStyle": "PRIMARY_BUTTON", + "disabledWhenInvalid": true, + "resetFormOnClick": true + } + }, + { + "type": "FORM_BUTTON_WIDGET", + "size": { + "rows": 1, + "cols": 4 + }, + "position": { + "top": 11, + "left": 8 + }, + "props": { + "text": "Reset", + "buttonStyle": "SECONDARY_BUTTON", + "disabledWhenInvalid": false, + "resetFormOnClick": true + } + } + ] + }, + "minHeight": 520, + "type": "CANVAS_WIDGET", + "isLoading": false, + "parentColumnSpace": 1, + "parentRowSpace": 1, + "leftColumn": 0, + "rightColumn": 518, + "topRow": 0, + "bottomRow": 520, + "parentId": "j77dthxf61", + "widgetId": "sidaue1kdu" + } + ], + "blueprint": { + "view": [ + { + "type": "CANVAS_WIDGET", + "position": { + "top": 0, + "left": 0 + }, + "props": { + "containerStyle": "none", + "canExtend": false, + "detachFromLayout": true, + "children": [], + "blueprint": { + "view": [ + { + "type": "TEXT_WIDGET", + "size": { + "rows": 1, + "cols": 12 + }, + "position": { + "top": 0, + "left": 0 + }, + "props": { + "text": "Form", + "textStyle": "HEADING" + } + }, + { + "type": "FORM_BUTTON_WIDGET", + "size": { + "rows": 1, + "cols": 4 + }, + "position": { + "top": 11, + "left": 12 + }, + "props": { + "text": "Submit", + "buttonStyle": "PRIMARY_BUTTON", + "disabledWhenInvalid": true, + "resetFormOnClick": true + } + }, + { + "type": "FORM_BUTTON_WIDGET", + "size": { + "rows": 1, + "cols": 4 + }, + "position": { + "top": 11, + "left": 8 + }, + "props": { + "text": "Reset", + "buttonStyle": "SECONDARY_BUTTON", + "disabledWhenInvalid": false, + "resetFormOnClick": true + } + } + ] + } + } + } + ] + }, + "type": "FORM_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 3, + "rightColumn": 10, + "topRow": 2, + "bottomRow": 15, + "parentId": "0", + "widgetId": "j77dthxf61" + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/rundsl.json b/app/client/cypress/fixtures/rundsl.json new file mode 100644 index 0000000000..e4924ce46e --- /dev/null +++ b/app/client/cypress/fixtures/rundsl.json @@ -0,0 +1,93 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1224, + "snapColumns": 16, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1280, + "containerStyle": "none", + "snapRows": 33, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "dynamicBindings": {}, + "version": 6, + "minHeight": 1292, + "parentColumnSpace": 1, + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "text": "Submit", + "buttonStyle": "PRIMARY_BUTTON", + "widgetName": "Button1", + "isDisabled": false, + "isDefaultClickDisabled": true, + "type": "BUTTON_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 1, + "rightColumn": 3, + "topRow": 3, + "bottomRow": 4, + "parentId": "0", + "widgetId": "p2jmk6tut5" + }, + { + "isVisible": true, + "inputType": "TEXT", + "label": "", + "widgetName": "Input1", + "type": "INPUT_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 4, + "rightColumn": 9, + "topRow": 0, + "bottomRow": 1, + "parentId": "0", + "widgetId": "7grthyn6qd" + }, + { + "isVisible": true, + "label": "Data", + "widgetName": "Table1", + "searchKey": "", + "tableData": "[\n {\n \"id\": 2381224,\n \"email\": \"michael.lawson@reqres.in\",\n \"userName\": \"Michael Lawson\",\n \"productName\": \"Chicken Sandwich\",\n \"orderAmount\": 4.99\n },\n {\n \"id\": 2736212,\n \"email\": \"lindsay.ferguson@reqres.in\",\n \"userName\": \"Lindsay Ferguson\",\n \"productName\": \"Tuna Salad\",\n \"orderAmount\": 9.99\n },\n {\n \"id\": 6788734,\n \"email\": \"tobias.funke@reqres.in\",\n \"userName\": \"Tobias Funke\",\n \"productName\": \"Beef steak\",\n \"orderAmount\": 19.99\n }\n]", + "type": "TABLE_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 4, + "rightColumn": 12, + "topRow": 3, + "bottomRow": 10, + "parentId": "0", + "widgetId": "2kbcadciw8", + "dynamicBindings": {} + }, + { + "isVisible": true, + "text": "Label", + "textStyle": "LABEL", + "textAlign": "LEFT", + "widgetName": "Text1", + "type": "TEXT_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 5, + "rightColumn": 9, + "topRow": 1, + "bottomRow": 2, + "parentId": "0", + "widgetId": "xzjri1a2e6" + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/fixtures/testdata.json b/app/client/cypress/fixtures/testdata.json index fd75739169..d1d01a7945 100644 --- a/app/client/cypress/fixtures/testdata.json +++ b/app/client/cypress/fixtures/testdata.json @@ -42,5 +42,27 @@ "Get": "GET", "next": "?page=2&pageSize=10", "prev": "?page=1&pageSize=10", - "apiFormDataBodyType": "x-www-form-urlencoded" + "apiFormDataBodyType": "x-www-form-urlencoded", + "defaultMoustacheData": "{{Input1.text", + "defaultInputWidget": "{{Table1.selectedRow.id", + "deafultDropDownWidget": [ + { + "label": "{{Table1.selectedRow.email}}", + "value": "VEG" + }, + { + "label": "{{Table1.tableData[1].email}}", + "value": "NONVEG" + } + ], + "defaultRichtextWidget": "{{Table1.selectedRow.userName", + "defaultDropDownValue": "1", + "defaultInputQuery": "{{Query1.data[0].id", + "defaultTableQuery": "{{Query1.data", + "labelFrmQuery": "{{Query1.data[0].name", + "command": "{command}{A}{del}", + "defaultdata": "TestData", + "input1": "(//div[@class='bp3-input-group']//input)[0]", + "input2": "(//div[@class='bp3-input-group']//input)[1]", + "input3": "(//div[@class='bp3-input-group']//input)[2]" } \ No newline at end of file diff --git a/app/client/cypress/fixtures/videoWidgetDsl.json b/app/client/cypress/fixtures/videoWidgetDsl.json new file mode 100644 index 0000000000..0ba862cf09 --- /dev/null +++ b/app/client/cypress/fixtures/videoWidgetDsl.json @@ -0,0 +1,40 @@ +{ + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 1224, + "snapColumns": 16, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 1280, + "containerStyle": "none", + "snapRows": 33, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "dynamicBindings": {}, + "version": 6, + "minHeight": 1292, + "parentColumnSpace": 1, + "leftColumn": 0, + "children": [ + { + "isVisible": true, + "widgetName": "Video1", + "url": "https://www.youtube.com/watch?v=mzqK0QIZRLs", + "autoPlay": false, + "type": "VIDEO_WIDGET", + "isLoading": false, + "parentColumnSpace": 74, + "parentRowSpace": 40, + "leftColumn": 1, + "rightColumn": 8, + "topRow": 17, + "bottomRow": 24, + "parentId": "0", + "widgetId": "fvilzim02v" + } + ] + } +} \ No newline at end of file diff --git a/app/client/cypress/integration/Smoke_TestSuite/Binding/Widget_loading_spec.js b/app/client/cypress/integration/Smoke_TestSuite/Binding/Widget_loading_spec.js new file mode 100644 index 0000000000..bdf1f5b459 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/Binding/Widget_loading_spec.js @@ -0,0 +1,89 @@ +const commonlocators = require("../../../locators/commonlocators.json"); +const formWidgetsPage = require("../../../locators/FormWidgets.json"); +const dsl = require("../../../fixtures/rundsl.json"); +const pages = require("../../../locators/Pages.json"); +const widgetsPage = require("../../../locators/Widgets.json"); +const publish = require("../../../locators/publishWidgetspage.json"); +const queryLocators = require("../../../locators/QueryEditor.json"); +const datasource = require("../../../locators/DatasourcesEditor.json"); +const apiwidget = require("../../../locators/apiWidgetslocator.json"); +const testdata = require("../../../fixtures/testdata.json"); + +const pageid = "MyPage"; +let updatedName; +let datasourceName; + +describe("Binding the multiple widgets and validating default data", function() { + before(() => { + cy.addDsl(dsl); + }); + it("Create a postgres datasource", function() { + cy.NavigateToDatasourceEditor(); + cy.get(datasource.PostgreSQL).click(); + + cy.getPluginFormsAndCreateDatasource(); + + cy.fillPostgresDatasourceForm(); + + cy.testSaveDatasource(); + + cy.get("@createDatasource").then(httpResponse => { + datasourceName = httpResponse.response.body.data.name; + }); + }); + it("Create and runs query", () => { + cy.NavigateToQueryEditor(); + cy.get(".t--datasource-name") + .contains(datasourceName) + .click(); + + cy.get(queryLocators.templateMenu).click(); + cy.get(".CodeMirror textarea") + .first() + .focus() + .type("select * from users limit 10"); + + cy.EvaluateCurrentValue("select * from users limit 10"); + cy.runQuery(); + }); + + it("Button widget test with on action query run", function() { + cy.SearchEntityandOpen("Button1"); + cy.executeDbQuery("Query1"); + cy.get(commonlocators.editPropCrossButton).click(); + cy.wait("@updateLayout").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + }); + + + it("Input widget test with default value update with query data", function() { + cy.SearchEntityandOpen("Input1"); + cy.get(widgetsPage.defaultInput).type(testdata.defaultInputQuery); + cy.get(commonlocators.editPropCrossButton).click(); + cy.wait("@updateLayout").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + }); + + it("Publish App and validate loading functionalty", function() { + cy.PublishtheApp(); + cy.wait(2000); + cy.get(widgetsPage.widgetBtn) + .first() + .click({ force: true }); + cy.wait("@postExecute").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + cy.get(publish.inputWidget + " " + "input") + .first() + .invoke("attr", "value") + .should("contain", "7"); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/Binding/Widgets_Default_data_validation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/Binding/Widgets_Default_data_validation_spec.js new file mode 100644 index 0000000000..37ddc1732b --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/Binding/Widgets_Default_data_validation_spec.js @@ -0,0 +1,65 @@ +const commonlocators = require("../../../locators/commonlocators.json"); +const formWidgetsPage = require("../../../locators/FormWidgets.json"); +const dsl = require("../../../fixtures/MultipleWidgetDsl.json"); +const pages = require("../../../locators/Pages.json"); +const widgetsPage = require("../../../locators/Widgets.json"); +const publish = require("../../../locators/publishWidgetspage.json"); +const testdata = require("../../../fixtures/testdata.json"); + +describe("Binding the multiple widgets and validating default data", function() { + before(() => { + cy.addDsl(dsl); + }); + + it("Input widget test with default value from table widget", function() { + cy.openPropertyPane("inputwidget"); + cy.get(widgetsPage.defaultInput).type(testdata.defaultInputWidget); + cy.get(commonlocators.editPropCrossButton).click(); + cy.wait("@updateLayout").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + }); + + /* + To be enabled once the single select multi select issues are resolved + it("Dropdown widget test with default value from table widget", function() { + cy.openPropertyPane("dropdownwidget"); + cy.testJsontext("options", JSON.stringify(testdata.deafultDropDownWidget)); + cy.get(commonlocators.editPropCrossButton).click(); + cy.wait("@updateLayout").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + }); +*/ + + it("validation of default data displayed in all widgets based on row selected", function() { + cy.isSelectRow(1); + cy.readTabledataPublish("1", "0").then(tabData => { + const tabValue = tabData; + expect(tabValue).to.be.equal("2736212"); + cy.log("the value is" + tabValue); + + cy.get(publish.inputWidget + " " + "input") + .first() + .invoke("attr", "value") + .should("contain", tabValue); + }); + /* + cy.readTabledataPublish("1", "1").then(tabData => { + const tabValue = tabData; + expect(tabValue).to.be.equal("lindsay.ferguson@reqres.in"); + cy.log("the value is" + tabValue); + cy.get(widgetsPage.defaultSingleSelectValue) + .invoke("text") + .then(text => { + const someText = text; + expect(someText).to.equal(tabValue); + }); + }); + */ + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/Binding/Widgets_Dependancy_validation_spec.js b/app/client/cypress/integration/Smoke_TestSuite/Binding/Widgets_Dependancy_validation_spec.js new file mode 100644 index 0000000000..e426e823c0 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/Binding/Widgets_Dependancy_validation_spec.js @@ -0,0 +1,100 @@ +const commonlocators = require("../../../locators/commonlocators.json"); +const formWidgetsPage = require("../../../locators/FormWidgets.json"); +const dsl = require("../../../fixtures/MultipleInput.json"); +const pages = require("../../../locators/Pages.json"); +const widgetsPage = require("../../../locators/Widgets.json"); +const publish = require("../../../locators/publishWidgetspage.json"); +const testdata = require("../../../fixtures/testdata.json"); + +describe("Binding the multiple input Widget", function() { + before(() => { + cy.addDsl(dsl); + }); + + Cypress.on("uncaught:exception", (err, runnable) => { + // returning false here prevents Cypress from + // failing the test + return false; + }); + + it("Cyclic depedancy error message validation", function() { + cy.openPropertyPane("inputwidget"); + cy.get(widgetsPage.defaultInput).type(testdata.defaultMoustacheData); + cy.get(commonlocators.editPropCrossButton).click(); + cy.wait("@updateLayout").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + cy.get(commonlocators.toastmsg).contains("Cyclic dependency"); + }); + + it("Binding input widget1 and validating", function() { + cy.openPropertyPane("inputwidget"); + cy.get(widgetsPage.defaultInput) + .type(testdata.command) + .type(testdata.defaultdata); + cy.get(commonlocators.editPropCrossButton).click(); + cy.wait("@updateLayout").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + cy.get(publish.inputWidget + " " + "input") + .first() + .invoke("attr", "value") + .should("contain", testdata.defaultdata); + }); + + it("Binding second input widget with first input widget and validating", function() { + cy.SearchEntityandOpen("Input2"); + cy.get(widgetsPage.defaultInput).type(testdata.defaultMoustacheData); + cy.get(commonlocators.editPropCrossButton).click(); + cy.wait("@updateLayout").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + cy.xpath(testdata.input2) + .invoke("attr", "value") + .should("contain", testdata.defaultdata); + cy.reload(); + + /* + cy.PublishtheApp(); + cy.get(publish.inputWidget + " " + "input") + .first() + .invoke("attr", "value") + .should("contain", testdata.defaultdata); + cy.xpath(testdata.input2) + .invoke("attr", "value") + .should("contain", testdata.defaultdata); + cy.get(publish.backToEditor) + .first() + .click(); + */ + }); + + it("Binding third input widget with first input widget and validating", function() { + cy.SearchEntityandOpen("Input3"); + cy.get(widgetsPage.defaultInput).type(testdata.defaultMoustacheData); + cy.get(commonlocators.editPropCrossButton).click(); + cy.wait("@updateLayout").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + cy.PublishtheApp(); + cy.get(publish.inputWidget + " " + "input") + .first() + .invoke("attr", "value") + .should("contain", testdata.defaultdata); + cy.xpath(testdata.input2) + .invoke("attr", "value") + .should("contain", testdata.defaultdata); + cy.get(publish.inputWidget + " " + "input") + .last() + .invoke("attr", "value") + .should("contain", testdata.defaultdata); + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/DisplayWidgets/Table_spec.js b/app/client/cypress/integration/Smoke_TestSuite/DisplayWidgets/Table_spec.js index 41989a93e7..c90c7359a2 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/DisplayWidgets/Table_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/DisplayWidgets/Table_spec.js @@ -110,6 +110,121 @@ describe("Table Widget Functionality", function() { const tabValue = tabData; expect(tabValue).to.be.equal("Michael Lawson"); }); + cy.get(publish.canvas) + .first() + .click(); + }); + }); + + it("TableΒ WidgetΒ FunctionalityΒ ToΒ FilterΒ TheΒ DataΒ usingΒ contains", function() { + cy.isSelectRow(1); + cy.readTabledataPublish("1", "2").then(tabData => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Lindsay Ferguson"); + cy.log("theΒ valueΒ is" + tabValue); + cy.get(publish.filterBtn).click(); + cy.get(publish.attributeDropdown).click(); + cy.get(publish.attributeValue) + .contains("userName") + .click(); + cy.get(publish.conditionDropdown).click(); + cy.get(publish.attributeValue) + .contains("contains") + .click(); + cy.get(publish.inputValue).type("Lindsay"); + cy.wait(500); + cy.get(publish.canvas) + .first() + .click(); + cy.readTabledataPublish("0", "2").then(tabData => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Lindsay Ferguson"); + }); + cy.get(publish.filterBtn).click(); + cy.get(publish.removeFilter).click(); + cy.wait(500); + cy.readTabledataPublish("0", "2").then(tabData => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Michael Lawson"); + }); + cy.get(publish.canvas) + .first() + .click(); + }); + }); + + it("TableΒ WidgetΒ FunctionalityΒ ToΒ FilterΒ TheΒ DataΒ usingΒ startsΒ withΒ ", function() { + cy.isSelectRow(1); + cy.readTabledataPublish("1", "2").then(tabData => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Lindsay Ferguson"); + cy.log("theΒ valueΒ is" + tabValue); + cy.get(publish.filterBtn).click(); + cy.get(publish.attributeDropdown).click(); + cy.get(publish.attributeValue) + .contains("userName") + .click(); + cy.get(publish.conditionDropdown).click(); + cy.get(publish.attributeValue) + .contains("starts with") + .click(); + cy.get(publish.inputValue).type("Lindsay"); + cy.wait(500); + cy.get(publish.canvas) + .first() + .click(); + cy.readTabledataPublish("0", "2").then(tabData => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Lindsay Ferguson"); + }); + cy.get(publish.filterBtn).click(); + cy.get(publish.removeFilter).click(); + cy.wait(500); + cy.readTabledataPublish("0", "2").then(tabData => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Michael Lawson"); + }); + cy.get(publish.canvas) + .first() + .click(); + }); + }); + + it("TableΒ WidgetΒ FunctionalityΒ ToΒ FilterΒ TheΒ DataΒ usingΒ endsΒ withΒ ", function() { + cy.isSelectRow(1); + cy.readTabledataPublish("1", "2").then(tabData => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Lindsay Ferguson"); + cy.log("theΒ valueΒ is" + tabValue); + cy.get(publish.filterBtn).click(); + cy.get(publish.attributeDropdown).click(); + cy.get(publish.attributeValue) + .contains("userName") + .click(); + cy.get(publish.conditionDropdown).click(); + cy.get(publish.attributeValue) + .contains("ends with") + .click(); + cy.get(publish.inputValue).type("Ferguson"); + cy.wait(500); + cy.get(publish.canvas) + .first() + .click(); + cy.readTabledataPublish("0", "2").then(tabData => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Lindsay Ferguson"); + }); + cy.get(publish.filterBtn).click(); + cy.get(publish.removeFilter).click(); + cy.wait(500); + cy.readTabledataPublish("0", "2").then(tabData => { + const tabValue = tabData; + expect(tabValue).to.be.equal("Michael Lawson"); + }); + cy.get(publish.canvas) + .first() + .click(); + }); }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/DisplayWidgets/video_spec.js b/app/client/cypress/integration/Smoke_TestSuite/DisplayWidgets/video_spec.js new file mode 100644 index 0000000000..0a21b7cf26 --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/DisplayWidgets/video_spec.js @@ -0,0 +1,30 @@ +const widgetsPage = require("../../../locators/Widgets.json"); +const commonlocators = require("../../../locators/commonlocators.json"); +const publish = require("../../../locators/publishWidgetspage.json"); +const dsl = require("../../../fixtures/videoWidgetDsl.json"); +const pages = require("../../../locators/Pages.json"); + +describe("Table Widget Functionality", function() { + before(() => { + cy.addDsl(dsl); + }); + + it("Table Widget Functionality", function() { + cy.openPropertyPane("videowidget"); + cy.widgetText("Video1", widgetsPage.videoWidget, commonlocators.videoInner); + cy.get(commonlocators.onPlay).click(); + cy.selectShowMsg(); + cy.addSuccessMessage("Play success"); + cy.get(widgetsPage.autoPlay).click(); + cy.wait("@updateLayout").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + cy.get(commonlocators.toastMsg).should("be.visible"); + cy.get(commonlocators.toastMsg).contains("Play success"); + }); + afterEach(() => { + // put your clean up code if any + }); +}); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ExplorerTests/Entity_Explorer_Datasource_Structure_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ExplorerTests/Entity_Explorer_Datasource_Structure_spec.js index f818f25f45..587af0cba4 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ExplorerTests/Entity_Explorer_Datasource_Structure_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ExplorerTests/Entity_Explorer_Datasource_Structure_spec.js @@ -16,9 +16,6 @@ describe("Entity explorer datasource structure", function() { it("Entity explorer datasource structure", function() { cy.GlobalSearchEntity(datasourceName); - cy.get(`.t--entity.datasource:contains(${datasourceName})`) - .find(explorer.collapse) - .click(); cy.wait("@getDatasourceStructure").should( "have.nested.property", "response.body.responseMeta.status", @@ -62,7 +59,6 @@ describe("Entity explorer datasource structure", function() { .find(explorer.collapse) .as("datasourceEntityCollapse"); - cy.get("@datasourceEntityCollapse").click(); cy.wait("@getDatasourceStructure").should( "have.nested.property", "response.body.responseMeta.status", @@ -93,7 +89,9 @@ describe("Entity explorer datasource structure", function() { ); cy.GlobalSearchEntity(datasourceName); - cy.get("@datasourceEntityCollapse").click(); + cy.get("@datasourceEntityCollapse") + .first() + .click(); cy.xpath(explorer.datsourceEntityPopover) .last() .click({ force: true }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js index 226fa6ca49..0250f4f034 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ExplorerTests/Entity_Explorer_Widgets_Copy_Delete_Undo_spec.js @@ -4,18 +4,17 @@ const explorer = require("../../../locators/explorerlocators.json"); const commonlocators = require("../../../locators/commonlocators.json"); const formWidgetsPage = require("../../../locators/FormWidgets.json"); const publish = require("../../../locators/publishWidgetspage.json"); +const dsl = require("../../../fixtures/formWidgetdsl.json"); const pageid = "MyPage"; +before(() => { + cy.addDsl(dsl); +}); + describe("Test Suite to validate copy/delete/undo functionalites", function() { it("Drag and drop form widget and validate copy widget via toast message", function() { - cy.log("Login Successful"); - cy.get(explorer.addWidget).click(); - cy.get(commonlocators.entityExplorersearch).should("be.visible"); - cy.get(commonlocators.entityExplorersearch) - .clear() - .type("form"); - cy.dragAndDropToCanvas("formwidget"); + cy.openPropertyPane("formwidget"); cy.widgetText( "FormTest", formWidgetsPage.formWidget, @@ -27,7 +26,6 @@ describe("Test Suite to validate copy/delete/undo functionalites", function() { .first() .contains("Copied"); cy.get(commonlocators.editPropCrossButton).click(); - cy.get(explorer.closeWidgets).click(); }); it("Delete Widget from sidebar and Undo action validation", function() { diff --git a/app/client/cypress/integration/Smoke_TestSuite/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js index cc714f1b6b..a0265613fc 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ExplorerTests/Entity_Explorer_Widgets_Copy_Paste_Delete_Undo_Keyboard_Event_spec.js @@ -5,18 +5,17 @@ const commonlocators = require("../../../locators/commonlocators.json"); const formWidgetsPage = require("../../../locators/FormWidgets.json"); const publish = require("../../../locators/publishWidgetspage.json"); const widgetsPage = require("../../../locators/Widgets.json"); +const dsl = require("../../../fixtures/formWidgetdsl.json"); const pageid = "MyPage"; +before(() => { + cy.addDsl(dsl); +}); + describe("Test Suite to validate copy/delete/undo functionalites", function() { it("Drag and drop form widget and validate copy widget via toast message", function() { - cy.log("Login Successful"); - cy.get(explorer.addWidget).click(); - cy.get(commonlocators.entityExplorersearch).should("be.visible"); - cy.get(commonlocators.entityExplorersearch) - .clear() - .type("form"); - cy.dragAndDropToCanvas("formwidget"); + cy.openPropertyPane("formwidget"); cy.widgetText( "FormTest", formWidgetsPage.formWidget, @@ -44,7 +43,6 @@ describe("Test Suite to validate copy/delete/undo functionalites", function() { cy.get(commonlocators.toastAction) .contains("UNDO") .click({ force: true }); - cy.get(explorer.closeWidgets).click(); cy.GlobalSearchEntity("Form1"); cy.get(apiwidget.propertyList).then(function($lis) { expect($lis).to.have.length(2); diff --git a/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/FormReset_spec.js b/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/FormReset_spec.js index eea5f26e11..7e3290ce81 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/FormReset_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/FormReset_spec.js @@ -22,7 +22,6 @@ describe("Form reset functionality", function() { cy.get(".tr") .eq(2) - .click() .should("not.have.class", "selected-row"); cy.get(widgetsPage.inputWidget + " " + "input") diff --git a/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/Radio_spec.js b/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/Radio_spec.js index 5d5d0a4d31..aa6edace77 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/Radio_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/FormWidgets/Radio_spec.js @@ -33,13 +33,10 @@ describe("Radio Widget Functionality", function() { cy.radioInput(2, this.data.radio2); cy.get(formWidgetsPage.labelradio) .eq(1) - .should("have.text", "test2"); + .should("have.text", this.data.radio2); cy.radioInput(3, "2"); cy.get(formWidgetsPage.radioAddButton).click({ force: true }); cy.radioInput(4, this.data.radio4); - cy.get(formWidgetsPage.labelradio) - .eq(2) - .should("have.text", "test4"); cy.get(formWidgetsPage.deleteradiovalue) .eq(2) .click({ force: true }); diff --git a/app/client/cypress/integration/Smoke_TestSuite/OrganisationTests/ShareAppTests_spec.js b/app/client/cypress/integration/Smoke_TestSuite/OrganisationTests/ShareAppTests_spec.js new file mode 100644 index 0000000000..d67219cb9c --- /dev/null +++ b/app/client/cypress/integration/Smoke_TestSuite/OrganisationTests/ShareAppTests_spec.js @@ -0,0 +1,128 @@ +// /// +// +// const homePage = require("../../../locators/HomePage.json"); +// const publish = require("../../../locators/publishWidgetspage.json"); +// +// describe("Create new org and share with a user", function() { +// let orgid; +// let appid; +// let currentUrl; +// +// it("create org and then share with a user from Application share option within application", function() { +// cy.NavigateToHome(); +// cy.generateUUID().then(uid => { +// orgid = uid; +// appid = uid; +// localStorage.setItem("OrgName", orgid); +// cy.createOrg(orgid); +// cy.CreateAppForOrg(orgid, appid); +// cy.wait("@getPagesForApp").should( +// "have.nested.property", +// "response.body.responseMeta.status", +// 200, +// ); +// cy.get("h2").contains("Drag and drop a widget here"); +// cy.get(homePage.shareApp).click(); +// cy.shareApp(Cypress.env("TESTUSERNAME1"), homePage.viewerRole); +// }); +// cy.LogOut(); +// }); +// +// it("login as invited user and then validate viewer privilage", function() { +// cy.LogintoApp(Cypress.env("TESTUSERNAME1"), Cypress.env("TESTPASSWORD1")); +// cy.get(homePage.searchInput).type(appid); +// cy.wait(2000); +// cy.get(homePage.appsContainer).contains(orgid); +// cy.xpath(homePage.ShareBtn).should("not.exist"); +// cy.get(homePage.applicationCard).trigger("mouseover"); +// cy.get(homePage.appEditIcon).should("not.exist"); +// cy.launchApp(appid); +// cy.LogOut(); +// }); +// +// it("Enable public access to Application", function() { +// cy.LoginFromAPI(Cypress.env("USERNAME"), Cypress.env("PASSWORD")); +// cy.visit("/applications"); +// cy.wait("@applications").should( +// "have.nested.property", +// "response.body.responseMeta.status", +// 200, +// ); +// cy.SearchApp(appid); +// cy.wait("@getPagesForApp").should( +// "have.nested.property", +// "response.body.responseMeta.status", +// 200, +// ); +// cy.get("h2").contains("Drag and drop a widget here"); +// cy.get(homePage.shareApp).click(); +// cy.enablePublicAccess(); +// cy.PublishtheApp(); +// currentUrl = cy.url(); +// cy.url().then(url => { +// currentUrl = url; +// cy.log(currentUrl); +// }); +// cy.get(publish.backToEditor).click(); +// cy.LogOut(); +// }); +// +// it("login as uninvited user and then validate public access of Application", function() { +// cy.LogintoApp(Cypress.env("TESTUSERNAME2"), Cypress.env("TESTPASSWORD2")); +// cy.visit(currentUrl); +// cy.wait("@getPagesForApp").should( +// "have.nested.property", +// "response.body.responseMeta.status", +// 200, +// ); +// cy.get(publish.pageInfo) +// .invoke("text") +// .then(text => { +// const someText = text; +// expect(someText).to.equal("This page seems to be blank"); +// }); +// cy.LogOut(); +// }); +// it("login as Owner and disable public access", function() { +// cy.LoginFromAPI(Cypress.env("USERNAME"), Cypress.env("PASSWORD")); +// cy.visit("/applications"); +// cy.wait("@applications").should( +// "have.nested.property", +// "response.body.responseMeta.status", +// 200, +// ); +// cy.SearchApp(appid); +// cy.wait("@getPagesForApp").should( +// "have.nested.property", +// "response.body.responseMeta.status", +// 200, +// ); +// cy.get("h2").contains("Drag and drop a widget here"); +// cy.get(homePage.shareApp).click(); +// cy.enablePublicAccess(); +// cy.LogOut(); +// }); +// +// it("login as uninvited user and then validate public access disable feature", function() { +// cy.LogintoApp(Cypress.env("TESTUSERNAME2"), Cypress.env("TESTPASSWORD2")); +// cy.visit(currentUrl); +// cy.wait("@getPagesForApp").should( +// "have.nested.property", +// "response.body.responseMeta.status", +// 404, +// ); +// cy.LogOut(); +// }); +// +// it("login as owner and delete App ", function() { +// cy.LoginFromAPI(Cypress.env("USERNAME"), Cypress.env("PASSWORD")); +// cy.visit("/applications"); +// cy.wait("@applications").should( +// "have.nested.property", +// "response.body.responseMeta.status", +// 200, +// ); +// cy.SearchApp(appid); +// cy.get("#loading").should("not.exist"); +// }); +// }); diff --git a/app/client/cypress/locators/HomePage.json b/app/client/cypress/locators/HomePage.json index 67231dedb2..048dde690b 100644 --- a/app/client/cypress/locators/HomePage.json +++ b/app/client/cypress/locators/HomePage.json @@ -25,10 +25,10 @@ "inviteUserMembersPage": "[data-cy=t--invite-users]", "email": "//input[@type='email']", "selectRole": "//span[text()='Select a role']", - "adminRole": "//div[@class='bp3-overlay bp3-overlay-open']//div[contains(text(),'Administrator')]", - "viewerRole": "//div[@class='bp3-overlay bp3-overlay-open']//div[contains(text(),'Viewer')]", - "developerRole": "//div[@class='bp3-overlay bp3-overlay-open']//div[contains(text(),'Developer')]", - "inviteBtn": "//span[text()='Invite']", + "adminRole": "//div[@class='label-title']//span[text()='Administrator']", + "viewerRole": "//div[@class='label-title']//span[text()='App Viewer']", + "developerRole": "//div[@class='label-title']//span[text()='Developer']", + "inviteBtn": "//button[text()='Invite']", "manageUsers": ".manageUsers", "DeleteBtn": "[data-cy=t--deleteUser]", "ShareBtn": "//a[text()='Share']", @@ -42,5 +42,9 @@ "orgSectionBtn": ".t--org-section .bp3-button", "shareOrg": ") a:contains('Share')", "orgSection": "a:contains(", - "createAppFrOrg": ") .t--create-app-popup" + "createAppFrOrg": ") .t--create-app-popup", + "shareApp": ".t--application-share-btn", + "enablePublicAccess": ".bp3-control-indicator", + "closeBtn": ".bp3-dialog-close-button" + } \ No newline at end of file diff --git a/app/client/cypress/locators/Widgets.json b/app/client/cypress/locators/Widgets.json index 0888b88290..425df3d71f 100644 --- a/app/client/cypress/locators/Widgets.json +++ b/app/client/cypress/locators/Widgets.json @@ -37,5 +37,10 @@ "textbuttonWidget": ".t--draggable-buttonwidget button.bp3-button[type='button']", "textInputval": ".t--draggable-textwidget span.t--widget-name", "textAlign": ".t--property-control-textalign", - "ColumnAction": ".t--property-control-rowbutton button" + "ColumnAction": ".t--property-control-rowbutton button", + "videoWidget": ".t--draggable-videowidget", + "autoPlay": ".t--property-control-autoplay > .bp3-control > .bp3-control-indicator", + "defaultOption": ".t--property-control-defaultoption .CodeMirror-code", + "defaultSingleSelectValue": ".bp3-popover-target > div > .bp3-button > .bp3-button-text", + "widgetBtn": ".t--widget-buttonwidget button" } diff --git a/app/client/cypress/locators/commonlocators.json b/app/client/cypress/locators/commonlocators.json index c718381186..ae4af7fb7a 100644 --- a/app/client/cypress/locators/commonlocators.json +++ b/app/client/cypress/locators/commonlocators.json @@ -66,8 +66,13 @@ "saveStatusError": ".t--save-status-error", "tableNextPage": ".t--table-widget-next-page", "tablePrevPage": ".t--table-widget-prev-page", + "toastmsg": ".Toastify__toast-body span", "copyWidget": ".t--copy-widget", "deleteWidget": ".t--delete-widget", "toastAction": ".t--toast-action", - "toastBody":".Toastify__toast-body" + "toastBody":".Toastify__toast-body", + "videoInner": ".t--draggable-videowidget span.t--widget-name", + "onPlay": ".t--property-control-onplay .t--open-dropdown-Select-Action", + "chooseAction": ".single-select", + "chooseMsgType": ".t--open-dropdown-Select-type" } diff --git a/app/client/cypress/locators/publishWidgetspage.json b/app/client/cypress/locators/publishWidgetspage.json index 2383588e7e..1474aa3dd1 100644 --- a/app/client/cypress/locators/publishWidgetspage.json +++ b/app/client/cypress/locators/publishWidgetspage.json @@ -19,8 +19,8 @@ "tableLength": ".t--widget-tablewidget .tbody", "mapSearch": ".t--widget-mapwidget input", "pickMyLocation": ".t--widget-mapwidget div[title='Pick My Location']", - "rectChart":".t--widget-chartwidget g rect", - "chartLab":".t--widget-chartwidget g:nth-child(5) text", + "rectChart": ".t--widget-chartwidget g rect", + "chartLab": ".t--widget-chartwidget g:nth-child(5) text", "searchInput": "input", "downloadBtn": ".t--table-download-btn", "filterBtn": ".t--table-filter-toggle-btn", @@ -34,5 +34,6 @@ "compactOpt": ".t--table-compact-mode-option", "visibilityMode": ".t--table-column-visibility-toggle-btn", "visibilityOpt": ".option-title", - "containerWidget": ".t--widget-containerwidget" + "containerWidget": ".t--widget-containerwidget", + "pageInfo": ".bp3-heading" } \ No newline at end of file diff --git a/app/client/cypress/support/commands.js b/app/client/cypress/support/commands.js index 8d44458704..e30d003dfc 100644 --- a/app/client/cypress/support/commands.js +++ b/app/client/cypress/support/commands.js @@ -88,6 +88,48 @@ Cypress.Commands.add("inviteUserForOrg", (orgName, email, role) => { .click(); }); +Cypress.Commands.add("shareApp", (email, role) => { + cy.xpath(homePage.email) + .click({ force: true }) + .type(email); + cy.xpath(homePage.selectRole).click({ force: true }); + cy.xpath(role).click({ force: true }); + cy.xpath(homePage.inviteBtn).click({ force: true }); + cy.wait("@postInvite").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + cy.contains(email); + cy.get(homePage.closeBtn).click(); +}); + +Cypress.Commands.add("shareAndPublic", (email, role) => { + cy.xpath(homePage.email) + .click({ force: true }) + .type(email); + cy.xpath(homePage.selectRole).click({ force: true }); + cy.xpath(role).click({ force: true }); + cy.xpath(homePage.inviteBtn).click({ force: true }); + cy.wait("@postInvite").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + cy.contains(email); + cy.enablePublicAccess(); +}); + +Cypress.Commands.add("enablePublicAccess", () => { + cy.get(homePage.enablePublicAccess).click(); + cy.wait("@changeAccess").should( + "have.nested.property", + "response.body.responseMeta.status", + 200, + ); + cy.get(homePage.closeBtn).click(); +}); + Cypress.Commands.add("deleteUserFromOrg", (orgName, email) => { cy.get(homePage.orgList.concat(orgName).concat(")")) .scrollIntoView() @@ -930,6 +972,43 @@ Cypress.Commands.add("testJsontext", (endp, value) => { cy.wait(200); }); +Cypress.Commands.add("selectShowMsg", value => { + cy.get(commonlocators.chooseAction) + .children() + .contains("Show Message") + .click(); +}); + +Cypress.Commands.add("addSuccessMessage", value => { + cy.get(commonlocators.chooseMsgType).click(); + cy.get(commonlocators.chooseAction) + .children() + .contains("Success") + .click(); + cy.get(".CodeMirror textarea") + .last() + .focus() + .type("{ctrl}{shift}{downarrow}") + .then($cm => { + if ($cm.val() !== "") { + cy.get(".CodeMirror textarea") + .last() + .clear({ + force: true, + }); + } + cy.get(".CodeMirror textarea") + .last() + .type(value, { + force: true, + parseSpecialCharSequences: false, + }); + cy.wait(200); + cy.get(".CodeMirror textarea") + .last() + .should("have.value", value); + }); +}); Cypress.Commands.add("SetDateToToday", () => { cy.get(formWidgetsPage.datepickerFooter) .contains("Today") @@ -1280,8 +1359,8 @@ Cypress.Commands.add("saveDatasource", () => { }); Cypress.Commands.add("testSaveDatasource", () => { - cy.testDatasource(); cy.saveDatasource(); + cy.testDatasource(); }); Cypress.Commands.add("fillMongoDatasourceForm", () => { @@ -1416,6 +1495,20 @@ Cypress.Commands.add("dragAndDropToCanvas", widgetType => { .trigger("mouseup", { force: true }); }); +Cypress.Commands.add("executeDbQuery", queryName => { + cy.get(widgetsPage.buttonOnClick) + .get(commonlocators.dropdownSelectButton) + .click({ force: true }) + .get("ul.bp3-menu") + .children() + .contains("Execute a DB Query") + .click({ force: true }) + .get("ul.bp3-menu") + .children() + .contains(queryName) + .click({ force: true }); +}); + Cypress.Commands.add("openPropertyPane", widgetType => { const selector = `.t--draggable-${widgetType}`; cy.get(selector) @@ -1585,6 +1678,7 @@ Cypress.Commands.add("startServerAndRoutes", () => { cy.route("GET", "/api/v1/users/me").as("getUser"); cy.route("POST", "/api/v1/pages").as("createPage"); cy.route("POST", "/api/v1/pages/clone/*").as("clonePage"); + cy.route("PUT", "/api/v1/applications/*/changeAccess").as("changeAccess"); }); Cypress.Commands.add("alertValidate", text => { diff --git a/app/client/jest.config.js b/app/client/jest.config.js index fee6632cf0..fa67b59307 100644 --- a/app/client/jest.config.js +++ b/app/client/jest.config.js @@ -1,14 +1,15 @@ module.exports = { roots: ["/src"], transform: { - "^.+\\.(ts|tsx)$": "ts-jest", + "^.+\\.(png|js|ts|tsx)$": "ts-jest", }, testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$", moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node", "css"], moduleDirectories: ["node_modules", "src"], - transformIgnorePatterns: ["/node_modules/(?!codemirror)"], + transformIgnorePatterns: ["/node_modules/(?!codemirror|react-dnd|dnd-core|@babel)"], moduleNameMapper: { "\\.(css|less)$": "/test/__mocks__/styleMock.js", "\\.svg$": "/test/__mocks__/svgMock.js", + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/test/__mocks__/fileMock.js", }, }; diff --git a/app/client/package.json b/app/client/package.json index 9b7cd05f65..54d6b7f8bb 100644 --- a/app/client/package.json +++ b/app/client/package.json @@ -158,6 +158,8 @@ "@storybook/addons": "^5.3.19", "@storybook/preset-create-react-app": "^3.1.4", "@storybook/react": "^5.3.19", + "@testing-library/jest-dom": "^5.11.4", + "@testing-library/react": "^11.0.4", "@types/codemirror": "^0.0.96", "@types/downloadjs": "^1.4.2", "@types/jest": "^24.0.22", @@ -165,6 +167,7 @@ "@types/react-select": "^3.0.5", "@types/react-tabs": "^2.3.1", "@types/redux-form": "^8.1.9", + "@types/redux-mock-store": "^1.0.2", "@types/styled-system": "^5.1.9", "@types/tern": "0.22.0", "@types/toposort": "^2.0.3", @@ -192,6 +195,7 @@ "react-test-renderer": "^16.11.0", "redux-devtools": "^3.5.0", "redux-devtools-extension": "^2.13.8", + "redux-mock-store": "^1.5.4", "source-map-explorer": "^2.4.2", "storybook-addon-designs": "^5.4.0", "ts-jest": "^24.3.0", diff --git a/app/client/src/actions/datasourceActions.ts b/app/client/src/actions/datasourceActions.ts index 64652244b4..83bea3120c 100644 --- a/app/client/src/actions/datasourceActions.ts +++ b/app/client/src/actions/datasourceActions.ts @@ -31,6 +31,13 @@ export const fetchDatasourceStructure = (id: string) => { }; }; +export const expandDatasourceEntity = (id: string) => { + return { + type: ReduxActionTypes.EXPAND_DATASOURCE_ENTITY, + payload: id, + }; +}; + export const refreshDatasourceStructure = (id: string) => { return { type: ReduxActionTypes.REFRESH_DATASOURCE_STRUCTURE_INIT, diff --git a/app/client/src/assets/icons/ads/manage.svg b/app/client/src/assets/icons/ads/manage.svg new file mode 100644 index 0000000000..27ba5fc70a --- /dev/null +++ b/app/client/src/assets/icons/ads/manage.svg @@ -0,0 +1,4 @@ + + + + diff --git a/app/client/src/assets/icons/menu/datasource-table.svg b/app/client/src/assets/icons/menu/datasource-table.svg index 03404eb688..76c3e9bf9b 100644 --- a/app/client/src/assets/icons/menu/datasource-table.svg +++ b/app/client/src/assets/icons/menu/datasource-table.svg @@ -1,14 +1,3 @@ - - - Tables - - - - - - - - - - - \ No newline at end of file + + + diff --git a/app/client/src/components/ads/Button.tsx b/app/client/src/components/ads/Button.tsx index 3131abcf11..164c079843 100644 --- a/app/client/src/components/ads/Button.tsx +++ b/app/client/src/components/ads/Button.tsx @@ -1,6 +1,6 @@ import React from "react"; import { CommonComponentProps, hexToRgba, ThemeProp, Classes } from "./common"; -import styled from "styled-components"; +import styled, { css } from "styled-components"; import Icon, { IconName, IconSize } from "./Icon"; import Spinner from "./Spinner"; import { mediumButton, smallButton, largeButton } from "constants/DefaultTheme"; @@ -58,6 +58,7 @@ type ButtonProps = CommonComponentProps & { size?: Size; fill?: boolean; href?: string; + tag?: "a" | "button"; }; const stateStyles = ( @@ -243,7 +244,7 @@ const btnFontStyles = (props: ThemeProp & ButtonProps): BtnFontType => { return { buttonFont, padding, height }; }; -const StyledButton = styled("a")` +const ButtonStyles = css` width: ${props => (props.fill ? "100%" : "auto")}; height: ${props => btnFontStyles(props).height}px; border: none; @@ -295,7 +296,7 @@ const StyledButton = styled("a")` align-items: center; justify-content: center; position: relative; - .new-spinner { + .${Classes.SPINNER} { position: absolute; left: 0; right: 0; @@ -304,6 +305,14 @@ const StyledButton = styled("a")` } `; +const StyledButton = styled("button")` + ${ButtonStyles} +`; + +const StyledLinkButton = styled("a")` + ${ButtonStyles} +`; + export const VisibilityWrapper = styled.div` visibility: hidden; `; @@ -327,6 +336,7 @@ Button.defaultProps = { isLoading: false, disabled: false, fill: false, + tag: "a", }; function Button(props: ButtonProps) { @@ -336,16 +346,8 @@ function Button(props: ButtonProps) { const TextLoadingState = {props.text}; - return ( - ) => - props.onClick && props.onClick(e) - } - > + const buttonContent = ( + <> {props.icon ? ( props.isLoading ? ( IconLoadingState @@ -357,8 +359,37 @@ function Button(props: ButtonProps) { {props.text ? (props.isLoading ? TextLoadingState : props.text) : null} {props.isLoading ? : null} - + ); + + if (props.tag === "button") { + return ( + ) => + props.onClick && props.onClick(e) + } + > + {buttonContent} + + ); + } else { + return ( + ) => + props.onClick && props.onClick(e) + } + > + {buttonContent} + + ); + } } export default Button; diff --git a/app/client/src/components/ads/Callout.tsx b/app/client/src/components/ads/Callout.tsx index d3ff387d38..7c5236dca3 100644 --- a/app/client/src/components/ads/Callout.tsx +++ b/app/client/src/components/ads/Callout.tsx @@ -2,13 +2,10 @@ import React from "react"; import { CommonComponentProps, Classes } from "./common"; import Text, { TextType } from "./Text"; import styled from "styled-components"; - -export type Variant = "note" | "warning"; -export type Background = "dark" | "light"; +import { Variant } from "./Button"; type CalloutProps = CommonComponentProps & { variant?: Variant; - background?: Background; text: string; fill?: boolean; }; @@ -16,14 +13,11 @@ type CalloutProps = CommonComponentProps & { const CalloutContainer = styled.div<{ variant?: Variant; fill?: boolean; - background?: Background; }>` padding: ${props => props.theme.spaces[5]}px ${props => props.theme.spaces[11] + 1}px; background: ${props => - props.variant && props.background - ? props.theme.colors.callout[props.variant][props.background].bgColor - : null}; + props.variant ? props.theme.colors.callout[props.variant].bgColor : null}; height: 42px; ${props => @@ -37,25 +31,18 @@ const CalloutContainer = styled.div<{ .${Classes.TEXT} { color: ${props => - props.variant && props.background - ? props.theme.colors.callout[props.variant][props.background].color - : null}; + props.variant ? props.theme.colors.callout[props.variant].color : null}; } `; Callout.defaultProps = { fill: false, variant: "note", - background: "dark", }; function Callout(props: CalloutProps) { return ( - + {props.text} ); diff --git a/app/client/src/components/ads/Dropdown.tsx b/app/client/src/components/ads/Dropdown.tsx index c55df20e19..e42ac67e4e 100644 --- a/app/client/src/components/ads/Dropdown.tsx +++ b/app/client/src/components/ads/Dropdown.tsx @@ -6,38 +6,42 @@ import Text, { TextType } from "./Text"; type DropdownOption = { label?: string; - value: string; + value?: string; id?: string; icon?: IconName; - onSelect?: (option: DropdownOption) => void; - children?: DropdownOption[]; + onSelect?: (value?: string) => void; }; type DropdownProps = CommonComponentProps & { options: DropdownOption[]; selected: DropdownOption; + onSelect?: (value?: string) => void; }; const DropdownContainer = styled.div` width: 260px; + position: relative; `; const Selected = styled.div<{ isOpen: boolean; disabled?: boolean }>` + height: 38px; padding: ${props => props.theme.spaces[4]}px ${props => props.theme.spaces[6]}px; background: ${props => props.disabled - ? props.theme.colors.dropdown.header.disabledText - : props.theme.colors.dropdown.header.disabledBg}; + ? props.theme.colors.dropdown.header.disabledBg + : props.theme.colors.dropdown.header.bg}; display: flex; align-items: center; justify-content: space-between; width: 100%; cursor: pointer; ${props => - props.isOpen && !props.disabled - ? `border: 1.2px solid ${props.theme.colors.info.main}` - : null}; + props.isOpen + ? `border: 1px solid ${props.theme.colors.info.main}` + : props.disabled + ? `border: 1px solid ${props.theme.colors.dropdown.header.disabledBg}` + : `border: 1px solid ${props.theme.colors.dropdown.header.bg}`}; ${props => props.isOpen && !props.disabled ? "box-sizing: border-box" : null}; ${props => @@ -47,12 +51,16 @@ const Selected = styled.div<{ isOpen: boolean; disabled?: boolean }>` .${Classes.TEXT} { ${props => props.disabled - ? `color: ${props.theme.colors.dropdown.text}` - : `color: ${props.theme.colors.dropdown.disabledText}`}; + ? `color: ${props.theme.colors.dropdown.header.disabledText}` + : `color: ${props.theme.colors.dropdown.header.text}`}; } `; const DropdownWrapper = styled.div` + position: absolute; + top: 38px; + left: 0px; + z-index: 1; margin-top: ${props => props.theme.spaces[2] - 1}px; background: ${props => props.theme.colors.dropdown.menuBg}; box-shadow: 0px 12px 28px ${props => props.theme.colors.dropdown.menuShadow}; @@ -117,6 +125,7 @@ const LabelWrapper = styled.div<{ label?: string }>` `; export default function Dropdown(props: DropdownProps) { + const { onSelect } = { ...props }; const [isOpen, setIsOpen] = useState(false); const [selected, setSelected] = useState(props.selected); @@ -124,11 +133,15 @@ export default function Dropdown(props: DropdownProps) { setSelected(props.selected); }, [props.selected]); - const optionClickHandler = useCallback((option: DropdownOption) => { - setSelected(option); - setIsOpen(false); - option.onSelect && option.onSelect(option); - }, []); + const optionClickHandler = useCallback( + (option: DropdownOption) => { + setSelected(option); + setIsOpen(false); + onSelect && onSelect(option.value); + option.onSelect && option.onSelect(option.value); + }, + [onSelect], + ); return ( setIsOpen(!isOpen)} > {selected.value} - + {isOpen && !props.disabled ? ( @@ -159,7 +172,9 @@ export default function Dropdown(props: DropdownProps) { ) : null} {option.label ? ( - {option.value} +
+ {option.value} +
) : ( {option.value} )} diff --git a/app/client/src/components/ads/EditableText.tsx b/app/client/src/components/ads/EditableText.tsx index 4eabe3af46..bf3ac6d645 100644 --- a/app/client/src/components/ads/EditableText.tsx +++ b/app/client/src/components/ads/EditableText.tsx @@ -128,15 +128,22 @@ const IconWrapper = styled.div` `; export const EditableText = (props: EditableTextProps) => { - const [isEditing, setIsEditing] = useState(!!props.isEditingDefault); - const [value, setValue] = useState(props.defaultValue); - const [lastValidValue, setLastValidValue] = useState(props.defaultValue); + const { + onBlur, + onTextChanged, + isInvalid: inputValidation, + defaultValue, + isEditingDefault, + } = props; + const [isEditing, setIsEditing] = useState(!!isEditingDefault); + const [value, setValue] = useState(defaultValue); + const [lastValidValue, setLastValidValue] = useState(defaultValue); const [isInvalid, setIsInvalid] = useState(false); const [changeStarted, setChangeStarted] = useState(false); const [savingState, setSavingState] = useState( SavingState.NOT_STARTED, ); - const valueRef = React.useRef(props.defaultValue); + const valueRef = React.useRef(defaultValue); useEffect(() => { setSavingState(props.savingState); @@ -144,18 +151,19 @@ export const EditableText = (props: EditableTextProps) => { useEffect(() => { return () => { - props.onBlur(valueRef.current); + onBlur(valueRef.current); }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { - setValue(props.defaultValue); - setIsEditing(!!props.isEditingDefault); - }, [props.defaultValue, props.isEditingDefault]); + setValue(defaultValue); + setIsEditing(!!isEditingDefault); + }, [defaultValue, isEditingDefault]); useEffect(() => { - if (props.forceDefault === true) setValue(props.defaultValue); - }, [props.forceDefault, props.defaultValue]); + if (props.forceDefault === true) setValue(defaultValue); + }, [props.forceDefault, defaultValue]); const themeDetails = useSelector(getThemeDetails); const bgColor = useMemo( @@ -167,35 +175,41 @@ export const EditableText = (props: EditableTextProps) => { const editMode = useCallback( (e: React.MouseEvent) => { setIsEditing(true); - const errorMessage = - props.isInvalid && props.isInvalid(props.defaultValue); + const errorMessage = inputValidation && inputValidation(defaultValue); setIsInvalid(errorMessage ? errorMessage : false); e.preventDefault(); e.stopPropagation(); }, - [props], + [inputValidation, defaultValue], ); const onConfirm = useCallback( (_value: string) => { if (savingState === SavingState.ERROR || isInvalid) { setValue(lastValidValue); - props.onBlur(lastValidValue); + onBlur(lastValidValue); setSavingState(SavingState.NOT_STARTED); } else if (changeStarted) { - props.onTextChanged(_value); - props.onBlur(_value); + onTextChanged(_value); + onBlur(_value); } setIsEditing(false); setChangeStarted(false); }, - [changeStarted, lastValidValue, props.onBlur, props.onTextChanged], + [ + changeStarted, + savingState, + isInvalid, + lastValidValue, + onBlur, + onTextChanged, + ], ); const onInputchange = useCallback( (_value: string) => { const finalVal: string = _value; - const errorMessage = props.isInvalid && props.isInvalid(finalVal); + const errorMessage = inputValidation && inputValidation(finalVal); const error = errorMessage ? errorMessage : false; if (!error) { setLastValidValue(finalVal); @@ -205,7 +219,7 @@ export const EditableText = (props: EditableTextProps) => { setIsInvalid(error); setChangeStarted(true); }, - [props.isInvalid], + [inputValidation], ); const iconName = diff --git a/app/client/src/components/ads/Icon.tsx b/app/client/src/components/ads/Icon.tsx index 5db54588aa..647c9cbbf5 100644 --- a/app/client/src/components/ads/Icon.tsx +++ b/app/client/src/components/ads/Icon.tsx @@ -18,6 +18,7 @@ import { ReactComponent as ViewAllIcon } from "assets/icons/ads/view-all.svg"; import { ReactComponent as ContextMenuIcon } from "assets/icons/ads/context-menu.svg"; import { ReactComponent as DuplicateIcon } from "assets/icons/ads/duplicate.svg"; import { ReactComponent as LogoutIcon } from "assets/icons/ads/logout.svg"; +import { ReactComponent as ManageIcon } from "assets/icons/ads/manage.svg"; import styled from "styled-components"; import { CommonComponentProps, Classes } from "./common"; import { noop } from "lodash"; @@ -88,6 +89,7 @@ export const IconCollection = [ "context-menu", "duplicate", "logout", + "manage", ] as const; export type IconName = typeof IconCollection[number]; @@ -97,6 +99,7 @@ const IconWrapper = styled.span` outline: none; } display: flex; + align-items: center; svg { width: ${props => sizeHandler(props.size)}px; height: ${props => sizeHandler(props.size)}px; @@ -189,6 +192,9 @@ const Icon = (props: IconProps & CommonComponentProps) => { case "logout": returnIcon = ; break; + case "manage": + returnIcon = ; + break; default: returnIcon = null; break; diff --git a/app/client/src/components/ads/MenuItem.tsx b/app/client/src/components/ads/MenuItem.tsx index 08f763eec5..66ed5a308e 100644 --- a/app/client/src/components/ads/MenuItem.tsx +++ b/app/client/src/components/ads/MenuItem.tsx @@ -3,12 +3,15 @@ import { CommonComponentProps, Classes } from "./common"; import styled from "styled-components"; import Icon, { IconName, IconSize } from "./Icon"; import Text, { TextType, FontWeight } from "./Text"; +import TooltipComponent from "components/ads/Tooltip"; +import { Position } from "@blueprintjs/core/lib/esm/common/position"; export type MenuItemProps = CommonComponentProps & { icon?: IconName; text: string; label?: ReactNode; href?: string; + ellipsize?: number; onSelect?: () => void; }; @@ -32,7 +35,7 @@ const ItemRow = styled.a<{ disabled?: boolean }>` ${props => !props.disabled - ? ` + ? ` &:hover { text-decoration: none; cursor: pointer; @@ -64,6 +67,16 @@ const IconContainer = styled.span` `; function MenuItem(props: MenuItemProps) { + return props.ellipsize && props.text.length > props.ellipsize ? ( + + + + ) : ( + + ); +} + +function MenuItemContent(props: MenuItemProps) { return ( : null} {props.text ? ( - {props.text} + {props.ellipsize + ? ellipsize(props.ellipsize, props.text) + : props.text} ) : null} @@ -84,4 +99,8 @@ function MenuItem(props: MenuItemProps) { ); } +function ellipsize(length: number, text: string) { + return text.length > length ? text.slice(0, length).concat(" ...") : text; +} + export default MenuItem; diff --git a/app/client/src/components/editorComponents/TagInputComponent.tsx b/app/client/src/components/ads/TagInputComponent.tsx similarity index 76% rename from app/client/src/components/editorComponents/TagInputComponent.tsx rename to app/client/src/components/ads/TagInputComponent.tsx index 0fd7307bd4..2bfb538882 100644 --- a/app/client/src/components/editorComponents/TagInputComponent.tsx +++ b/app/client/src/components/ads/TagInputComponent.tsx @@ -1,23 +1,35 @@ import React, { useState, useEffect } from "react"; import styled from "styled-components"; -import { TagInput } from "@blueprintjs/core"; -import { - Intent, - IntentColors, - getColorWithOpacity, -} from "constants/DefaultTheme"; +import { Classes, TagInput } from "@blueprintjs/core"; +import { Intent } from "constants/DefaultTheme"; const TagInputWrapper = styled.div<{ intent?: Intent }>` + margin-right: 8px; + &&& { - .bp3-tag { - color: ${props => props.theme.colors.textDefault}; - font-size: ${props => props.theme.fontSizes[3]}px; - background: ${props => - props.intent - ? getColorWithOpacity(IntentColors[props.intent], 0.2) - : getColorWithOpacity(IntentColors.none, 0.2)}; - border: 1px solid - ${props => - props.intent ? IntentColors[props.intent] : IntentColors.none}; + .${Classes.TAG_INPUT} { + background-color: ${props => props.theme.colors.tagInput.bg}; + min-height: 38px; + border: 1px solid ${props => props.theme.colors.tagInput.bg}; + border-radius: 0px; + } + .${Classes.TAG_INPUT}.${Classes.ACTIVE} { + border: 1px solid ${props => props.theme.colors.info.main}; + box-shadow: ${props => props.theme.colors.tagInput.shadow}; + } + .${Classes.INPUT_GHOST} { + color: ${props => props.theme.colors.tagInput.text}; + &::placeholder { + color: ${props => props.theme.colors.tagInput.placeholder}; + } + } + .${Classes.TAG} { + padding: 3px 10px; + color: ${props => props.theme.colors.tagInput.tag.text}; + background-color: ${props => props.theme.colors.info.main}; + border-radius: 0px; + font-size: 11px; + line-height: 13px; + letter-spacing: 0.4px; } } `; diff --git a/app/client/src/components/ads/TextInput.tsx b/app/client/src/components/ads/TextInput.tsx index b2bc1d87e6..482f2477b7 100644 --- a/app/client/src/components/ads/TextInput.tsx +++ b/app/client/src/components/ads/TextInput.tsx @@ -39,6 +39,7 @@ export type TextInputProps = CommonComponentProps & { defaultValue?: string; validator?: (value: string) => { isValid: boolean; message: string }; onChange?: (value: string) => void; + readOnly?: boolean; }; type boxReturnType = { @@ -61,6 +62,11 @@ const boxStyles = ( color = theme.colors.textInput.disable.text; borderColor = theme.colors.textInput.disable.border; } + if (props.readOnly) { + bgColor = theme.colors.textInput.readOnly.bg; + color = theme.colors.textInput.readOnly.text; + borderColor = theme.colors.textInput.readOnly.border; + } if (!isValid) { bgColor = hexToRgba(theme.colors.danger.main, 0.1); color = theme.colors.danger.main; @@ -77,28 +83,35 @@ const StyledInput = styled.input< outline: 0; box-shadow: none; border: 1px solid ${props => props.inputStyle.borderColor}; - padding: ${props => props.theme.spaces[4]}px - ${props => props.theme.spaces[6]}px; + padding: 0px ${props => props.theme.spaces[6]}px; + height: 38px; background-color: ${props => props.inputStyle.bgColor}; color: ${props => props.inputStyle.color}; &::placeholder { color: ${props => props.theme.colors.textInput.placeholder}; } - &:focus { - border: 1px solid - ${props => - props.isValid - ? props.theme.colors.info.main - : props.theme.colors.danger.main}; - box-shadow: ${props => - props.isValid - ? "0px 0px 4px 4px rgba(203, 72, 16, 0.18)" - : "0px 0px 4px 4px rgba(226, 44, 44, 0.18)"}; - } &:disabled { cursor: not-allowed; } + ${props => + !props.readOnly + ? ` + &:focus { + border: 1px solid + ${ + props.isValid + ? props.theme.colors.info.main + : props.theme.colors.danger.main + }; + box-shadow: ${ + props.isValid + ? "0px 0px 4px 4px rgba(203, 72, 16, 0.18)" + : "0px 0px 4px 4px rgba(226, 44, 44, 0.18)" + }; + } + ` + : null}; `; const InputWrapper = styled.div` @@ -172,6 +185,7 @@ const TextInput = forwardRef( {...props} placeholder={props.placeholder} onChange={memoizedChangeHandler} + readOnly={props.readOnly} /> {ErrorMessage} diff --git a/app/client/src/components/ads/Toggle.tsx b/app/client/src/components/ads/Toggle.tsx index edb7f9a21e..589fb704d4 100644 --- a/app/client/src/components/ads/Toggle.tsx +++ b/app/client/src/components/ads/Toggle.tsx @@ -1,4 +1,4 @@ -import { CommonComponentProps, Classes, lighten, darken } from "./common"; +import { CommonComponentProps, Classes } from "./common"; import React, { useState, useEffect } from "react"; import styled from "styled-components"; import Spinner from "./Spinner"; diff --git a/app/client/src/components/designSystems/appsmith/CopyToClipBoard.tsx b/app/client/src/components/designSystems/appsmith/CopyToClipBoard.tsx index 22bea611a6..e6ff4633a3 100644 --- a/app/client/src/components/designSystems/appsmith/CopyToClipBoard.tsx +++ b/app/client/src/components/designSystems/appsmith/CopyToClipBoard.tsx @@ -1,36 +1,17 @@ import React, { createRef, useState } from "react"; import styled from "styled-components"; import copy from "copy-to-clipboard"; -import { BaseButton } from "components/designSystems/blueprint/ButtonComponent"; +import TextInput from "components/ads/TextInput"; +import Button, { Category, Size } from "components/ads/Button"; const Wrapper = styled.div` display: flex; - flex-direction: row; -`; -const StyledInput = styled.input` - flex: 1; - border: 1px solid #d3dee3; - border-right: none; - padding: 6px 12px; - font-size: 14px; - color: #768896; - border-radius: 4px 0 0 4px; - width: 90%; - overflow: hidden; -`; -const SelectButton = styled(BaseButton)` - &&&& { - max-width: 70px; - margin: 0 0px; - min-height: 32px; - border-radius: 0px 4px 4px 0px; - font-weight: bold; - background-color: #f6f7f8; - font-size: 14px; - &.bp3-button { - padding: 0px 0px; - } + div { + flex-basis: calc(100% - 110px); + } + a { + flex-basis: 110px; } `; @@ -54,21 +35,23 @@ const CopyToClipboard = (props: any) => { }; return ( - { + onChange={() => { selectText(); }} - value={copyText} + defaultValue={copyText} /> - { copyToClipboard(copyText); }} + size={Size.large} /> ); diff --git a/app/client/src/components/designSystems/appsmith/ImageComponent.tsx b/app/client/src/components/designSystems/appsmith/ImageComponent.tsx index af7151e191..b05a024cad 100644 --- a/app/client/src/components/designSystems/appsmith/ImageComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/ImageComponent.tsx @@ -48,6 +48,7 @@ class ImageComponent extends React.Component< className={this.props.isLoading ? "bp3-skeleton" : ""} imageError={this.state.imageError} {...this.props} + data-testid="styledImage" > ` @@ -45,6 +48,13 @@ const OptionWrapper = styled.div<{ selected: boolean }>` } `; +const StyledOption = styled.div` + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 90%; +`; + const ButtonWrapper = styled.div` display: flex; width: 100%; @@ -132,7 +142,9 @@ const TableColumnsVisibility = (props: TableColumnsVisibilityProps) => { }} className="t--table-column-visibility-column-toggle" > -
{option.Header}
+ + {option.Header} + ))} diff --git a/app/client/src/components/designSystems/blueprint/RadioGroupComponent.tsx b/app/client/src/components/designSystems/blueprint/RadioGroupComponent.tsx index 1a51481f89..a6bc3d8b82 100644 --- a/app/client/src/components/designSystems/blueprint/RadioGroupComponent.tsx +++ b/app/client/src/components/designSystems/blueprint/RadioGroupComponent.tsx @@ -58,7 +58,7 @@ class RadioGroupComponent extends React.Component { className={this.props.isLoading ? "bp3-skeleton" : ""} label={option.label} value={option.value} - key={option.id} + key={option.value} /> ); })} diff --git a/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts b/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts index e8185943d4..0b121afb60 100644 --- a/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts +++ b/app/client/src/components/editorComponents/CodeEditor/styledComponents.ts @@ -47,6 +47,12 @@ export const HintStyles = createGlobalStyle<{ editorTheme: EditorTheme }>` } .CodeMirror-Tern-completion { padding-left: 22px !important; + &:hover{ + background: ${props => + props.editorTheme === EditorTheme.DARK + ? "rgba(244,244,244,0.2)" + : "rgba(128,136,141,0.2)"}; + } } .CodeMirror-Tern-completion:before { left: 4px !important; diff --git a/app/client/src/components/editorComponents/InputComponent.tsx b/app/client/src/components/editorComponents/InputComponent.tsx index 54eb6bf008..d077c5d422 100644 --- a/app/client/src/components/editorComponents/InputComponent.tsx +++ b/app/client/src/components/editorComponents/InputComponent.tsx @@ -18,6 +18,7 @@ type InputComponentProps = { type?: InputType; intent?: Intent; disabled?: boolean; + autoFocus?: boolean; }; const InputComponent = (props: InputComponentProps) => { @@ -28,6 +29,7 @@ const InputComponent = (props: InputComponentProps) => { placeholder={props.placeholder} type={props.type} intent={props.intent as BlueprintIntent} + autoFocus={props.autoFocus} /> ); }; diff --git a/app/client/src/components/editorComponents/form/FormDialogComponent.tsx b/app/client/src/components/editorComponents/form/FormDialogComponent.tsx index 7a7eb67024..b7f9d98ffd 100644 --- a/app/client/src/components/editorComponents/form/FormDialogComponent.tsx +++ b/app/client/src/components/editorComponents/form/FormDialogComponent.tsx @@ -5,12 +5,36 @@ import { isPermitted } from "pages/Applications/permissionHelpers"; const StyledDialog = styled(Dialog)<{ setMaxWidth?: boolean }>` && { - background: white; - & .bp3-dialog-header { - padding: ${props => props.theme.spaces[4]}px - ${props => props.theme.spaces[4]}px; + border-radius: 0px; + padding-bottom: 5px; + background: ${props => props.theme.colors.modal.bg}; + width: 640px; + + & .${Classes.DIALOG_HEADER} { + padding: ${props => props.theme.spaces[4]}px; + background: ${props => props.theme.colors.modal.bg}; + box-shadow: none; + .${Classes.ICON} { + color: ${props => props.theme.colors.modal.iconColor}; + } + .${Classes.HEADING} { + color: ${props => props.theme.colors.modal.headerText}; + display: flex; + justify-content: center; + margin-top: 20px; + font-size: 20px; + line-height: 24px; + font-weight: 500; + } + + .${Classes.BUTTON}.${Classes.MINIMAL}:hover { + background-color: ${props => props.theme.colors.modal.bg}; + } } - & .bp3-dialog-footer-actions { + & .${Classes.DIALOG_BODY} { + margin: ${props => props.theme.spaces[9]}px; + } + & .${Classes.DIALOG_FOOTER_ACTIONS} { display: block; } ${props => props.setMaxWidth && `width: 100vh;`} diff --git a/app/client/src/components/editorComponents/form/FormTextField.tsx b/app/client/src/components/editorComponents/form/FormTextField.tsx index 0a32000a9d..5d7b7a9ee1 100644 --- a/app/client/src/components/editorComponents/form/FormTextField.tsx +++ b/app/client/src/components/editorComponents/form/FormTextField.tsx @@ -32,6 +32,7 @@ type FormTextFieldProps = { label?: string; intent?: Intent; disabled?: boolean; + autoFocus?: boolean; }; const FormTextField = (props: FormTextFieldProps) => { diff --git a/app/client/src/components/editorComponents/form/fields/DropdownWrapper.tsx b/app/client/src/components/editorComponents/form/fields/DropdownWrapper.tsx new file mode 100644 index 0000000000..99200a880b --- /dev/null +++ b/app/client/src/components/editorComponents/form/fields/DropdownWrapper.tsx @@ -0,0 +1,38 @@ +import Dropdown from "components/ads/Dropdown"; +import React, { useEffect, useState } from "react"; + +type DropdownWrapperProps = { + placeholder: string; + input?: { + value?: string; + onChange?: (value?: string) => void; + }; + options: Array<{ id: string; value: string; label: string }>; +}; + +const DropdownWrapper = (props: DropdownWrapperProps) => { + const [selectedOption, setSelectedOption] = useState({ + value: props.placeholder, + }); + const onSelectHandler = (value?: string) => { + props.input && props.input.onChange && props.input.onChange(value); + }; + + useEffect(() => { + if (props.placeholder) { + setSelectedOption({ value: props.placeholder }); + } else if (props.input && props.input.value) { + setSelectedOption({ value: props.input.value }); + } + }, [props.input, props.placeholder]); + + return ( + + ); +}; + +export default DropdownWrapper; diff --git a/app/client/src/components/editorComponents/form/fields/SelectField.tsx b/app/client/src/components/editorComponents/form/fields/SelectField.tsx index 7d0e3dfa50..a42dd6b1ef 100644 --- a/app/client/src/components/editorComponents/form/fields/SelectField.tsx +++ b/app/client/src/components/editorComponents/form/fields/SelectField.tsx @@ -4,7 +4,7 @@ import { WrappedFieldMetaProps, WrappedFieldInputProps, } from "redux-form"; -import SelectComponent from "components/editorComponents/SelectComponent"; +import DropdownWrapper from "./DropdownWrapper"; const renderComponent = ( componentProps: SelectFieldProps & { @@ -14,15 +14,15 @@ const renderComponent = ( ) => { return ( - + ); }; type SelectFieldProps = { name: string; - placeholder?: string; - options?: Array<{ id: string; name: string; value?: string }>; + placeholder: string; + options: Array<{ id: string; value: string; label: string }>; size?: "large" | "small"; outline?: boolean; }; diff --git a/app/client/src/components/editorComponents/form/fields/TagListField.tsx b/app/client/src/components/editorComponents/form/fields/TagListField.tsx index f34a64ee55..a2d38ccb27 100644 --- a/app/client/src/components/editorComponents/form/fields/TagListField.tsx +++ b/app/client/src/components/editorComponents/form/fields/TagListField.tsx @@ -4,7 +4,7 @@ import { WrappedFieldMetaProps, WrappedFieldInputProps, } from "redux-form"; -import TagInputComponent from "components/editorComponents/TagInputComponent"; +import TagInputComponent from "components/ads/TagInputComponent"; import { Intent } from "constants/DefaultTheme"; const renderComponent = ( diff --git a/app/client/src/components/propertyControls/ChartDataControl.tsx b/app/client/src/components/propertyControls/ChartDataControl.tsx index fcd08e00b7..44a90d44d2 100644 --- a/app/client/src/components/propertyControls/ChartDataControl.tsx +++ b/app/client/src/components/propertyControls/ChartDataControl.tsx @@ -257,11 +257,10 @@ class ChartDataControl extends BaseControl { }> = this.props.propertyValue; const updatedChartData = chartData.map((item, i) => { if (index === i) { - if (propertyName === "seriesName") { - item.seriesName = updatedValue; - } else { - item.data = updatedValue; - } + return { + ...item, + [propertyName]: updatedValue, + }; } return item; }); diff --git a/app/client/src/components/stories/Callout.stories.tsx b/app/client/src/components/stories/Callout.stories.tsx index 6c61953fb0..38c8cf36bf 100644 --- a/app/client/src/components/stories/Callout.stories.tsx +++ b/app/client/src/components/stories/Callout.stories.tsx @@ -2,6 +2,7 @@ import React from "react"; import { withKnobs, select, text, boolean } from "@storybook/addon-knobs"; import Callout from "components/ads/Callout"; import { StoryWrapper } from "components/ads/common"; +import { Variant } from "components/ads/Button"; export default { title: "Callout", @@ -13,9 +14,8 @@ export const CalloutStory = () => ( ); diff --git a/app/client/src/components/stories/Dropdown.stories.tsx b/app/client/src/components/stories/Dropdown.stories.tsx index 74233c6a6a..a968cdbf68 100644 --- a/app/client/src/components/stories/Dropdown.stories.tsx +++ b/app/client/src/components/stories/Dropdown.stories.tsx @@ -32,6 +32,7 @@ export const Text = () => ( onSelect: action("selected-option"), }, ]} + onSelect={action("selected-option")} selected={{ id: select("Selected id", ["111abc", "222abc", "333abc"], "111abc"), value: text("Selected value", "First option"), @@ -64,6 +65,7 @@ export const IconAndText = () => ( onSelect: action("selected-option"), }, ]} + onSelect={action("selected-option")} selected={{ id: select("Selected id", ["111abc", "222abc", "333abc"], "111abc"), value: text("Selected value", "Delete"), @@ -102,6 +104,7 @@ export const LabelAndText = () => ( onSelect: action("selected-option"), }, ]} + onSelect={action("selected-option")} selected={{ id: select("Selected id", ["111abc", "222abc", "333abc"], "111abc"), value: text("Selected value", "Developer"), diff --git a/app/client/src/constants/DefaultTheme.tsx b/app/client/src/constants/DefaultTheme.tsx index fa26d72aad..40b073100e 100644 --- a/app/client/src/constants/DefaultTheme.tsx +++ b/app/client/src/constants/DefaultTheme.tsx @@ -70,12 +70,10 @@ export const BlueprintControlTransform = css` } & input:not(:disabled):active ~ .${Classes.CONTROL_INDICATOR} { box-shadow: none; - background: none; border: 2px solid ${Colors.SLATE_GRAY}; } & input:not(:disabled):active:checked ~ .${Classes.CONTROL_INDICATOR} { box-shadow: none; - background: none; border: 2px solid ${Colors.SLATE_GRAY}; } &:hover .${Classes.CONTROL_INDICATOR} { @@ -481,7 +479,6 @@ type ColorType = { hoverBG: Color; hoverBGOpacity: number; hoverBorder: ShadeColor; - targetBg: string; iconColor: ShadeColor; }; appCardColors: string[]; @@ -524,7 +521,7 @@ type ColorType = { dropdown: { header: { text: ShadeColor; - disabled: ShadeColor; + disabledText: ShadeColor; bg: ShadeColor; disabledBg: ShadeColor; }; @@ -565,6 +562,11 @@ type ColorType = { border: ShadeColor; }; placeholder: ShadeColor; + readOnly: { + bg: ShadeColor; + border: ShadeColor; + text: ShadeColor; + }; }; menuBorder: ShadeColor; editableText: { @@ -639,6 +641,46 @@ type ColorType = { profileDropdown: { userName: ShadeColor; }; + modal: { + bg: ShadeColor; + headerText: ShadeColor; + iconColor: string; + user: { + textColor: ShadeColor; + }; + email: { + message: ShadeColor; + desc: ShadeColor; + }; + manageUser: ShadeColor; + }; + tagInput: { + bg: ShadeColor; + tag: { + text: ShadeColor; + }; + text: ShadeColor; + placeholder: ShadeColor; + shadow: string; + }; + callout: { + info: { + color: string; + bgColor: string; + }; + success: { + color: string; + bgColor: string; + }; + danger: { + color: string; + bgColor: string; + }; + warning: { + color: string; + bgColor: string; + }; + }; }; export const dark: ColorType = { @@ -685,7 +727,6 @@ export const dark: ColorType = { hoverBG: Colors.BLACK, hoverBGOpacity: 0.5, hoverBorder: darkShades[4], - targetBg: "rgba(0, 0, 0, 0.1)", iconColor: darkShades[9], }, appCardColors: [ @@ -738,9 +779,9 @@ export const dark: ColorType = { dropdown: { header: { text: darkShades[7], - disabled: darkShades[6], - bg: darkShades[2], - disabledBg: darkShades[0], + disabledText: darkShades[6], + bg: darkShades[0], + disabledBg: darkShades[2], }, menuBg: darkShades[3], menuShadow: "rgba(0, 0, 0, 0.6)", @@ -779,6 +820,11 @@ export const dark: ColorType = { border: darkShades[0], }, placeholder: darkShades[5], + readOnly: { + bg: darkShades[0], + border: darkShades[0], + text: darkShades[7], + }, }, menuBorder: darkShades[4], editableText: { @@ -853,6 +899,46 @@ export const dark: ColorType = { profileDropdown: { userName: darkShades[9], }, + modal: { + bg: darkShades[1], + headerText: darkShades[9], + iconColor: "#6D6D6D", + user: { + textColor: darkShades[7], + }, + email: { + message: darkShades[8], + desc: darkShades[6], + }, + manageUser: darkShades[6], + }, + tagInput: { + bg: darkShades[0], + tag: { + text: darkShades[9], + }, + text: darkShades[9], + placeholder: darkShades[5], + shadow: "0px 0px 4px 4px rgba(203, 72, 16, 0.18)", + }, + callout: { + info: { + color: "#EE5A1A", + bgColor: "#241C1B", + }, + success: { + color: "#30CF89", + bgColor: "#17211E", + }, + danger: { + color: "#FF4D4D", + bgColor: "#2B1A1D", + }, + warning: { + color: "#E0B30E", + bgColor: "#29251A", + }, + }, }; export const light: ColorType = { @@ -899,7 +985,6 @@ export const light: ColorType = { hoverBG: Colors.WHITE, hoverBGOpacity: 0.7, hoverBorder: lightShades[2], - targetBg: "rgba(0, 0, 0, 0.1)", iconColor: lightShades[11], }, appCardColors: [ @@ -952,7 +1037,7 @@ export const light: ColorType = { dropdown: { header: { text: lightShades[9], - disabled: darkShades[6], + disabledText: darkShades[6], bg: lightShades[2], disabledBg: lightShades[1], }, @@ -992,7 +1077,12 @@ export const light: ColorType = { text: lightShades[9], border: lightShades[2], }, - placeholder: lightShades[6], + placeholder: lightShades[7], + readOnly: { + bg: lightShades[2], + border: lightShades[2], + text: lightShades[7], + }, }, menuBorder: lightShades[3], editableText: { @@ -1067,6 +1157,46 @@ export const light: ColorType = { profileDropdown: { userName: lightShades[9], }, + modal: { + bg: lightShades[11], + headerText: lightShades[10], + iconColor: "#A9A7A7", + user: { + textColor: lightShades[9], + }, + email: { + message: lightShades[9], + desc: lightShades[7], + }, + manageUser: lightShades[6], + }, + tagInput: { + bg: lightShades[2], + tag: { + text: lightShades[11], + }, + text: lightShades[9], + placeholder: darkShades[7], + shadow: "0px 0px 4px 4px rgba(203, 72, 16, 0.18)", + }, + callout: { + info: { + color: "#D44100", + bgColor: "#F8F3F0", + }, + success: { + color: "#007340", + bgColor: "#D9FDED", + }, + danger: { + color: "#C60707", + bgColor: "#FFE9E9", + }, + warning: { + color: "#DCAD00", + bgColor: "#FAF6E6", + }, + }, }; export const theme: Theme = { @@ -1173,28 +1303,6 @@ export const theme: Theme = { lightBg: lightShades[0], darkBg: lightShades[10], }, - callout: { - note: { - dark: { - color: "#EE5A1A", - bgColor: "#241C1B", - }, - light: { - color: "#D44100", - bgColor: "#F8F3F0", - }, - }, - warning: { - light: { - color: "#DCAD00", - bgColor: "#FAF6E6", - }, - dark: { - color: "#E0B30E", - bgColor: "#29251A", - }, - }, - }, appBackground: "#EFEFEF", primaryOld: Colors.GREEN, primaryDarker: Colors.JUNGLE_GREEN, diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index 7d141d5486..b1d0c15ed9 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -82,6 +82,7 @@ export const ReduxActionTypes: { [key: string]: string } = { FETCH_DATASOURCE_STRUCTURE_SUCCESS: "FETCH_DATASOURCE_STRUCTURE_SUCCESS", REFRESH_DATASOURCE_STRUCTURE_INIT: "REFRESH_DATASOURCE_STRUCTURE_INIT", REFRESH_DATASOURCE_STRUCTURE_SUCCESS: "REFRESH_DATASOURCE_STRUCTURE_SUCCESS", + EXPAND_DATASOURCE_ENTITY: "EXPAND_DATASOURCE_ENTITY", SELECT_PLUGIN: "SELECT_PLUGIN", TEST_DATASOURCE_INIT: "TEST_DATASOURCE_INIT", TEST_DATASOURCE_SUCCESS: "TEST_DATASOURCE_SUCCESS", diff --git a/app/client/src/jsExecution/RealmExecutor.ts b/app/client/src/jsExecution/RealmExecutor.ts index e9b587e3ce..164a522122 100644 --- a/app/client/src/jsExecution/RealmExecutor.ts +++ b/app/client/src/jsExecution/RealmExecutor.ts @@ -98,7 +98,6 @@ export default class RealmExecutor implements JSExecutor { const data = callbackData ? { ...safeData, CALLBACK_DATA: safeCallbackData } : safeData; - const { result, triggers } = this.rootRealm.evaluate(script, data); return { result: this.convertToMainScope(result), diff --git a/app/client/src/mockResponses/WidgetConfigResponse.tsx b/app/client/src/mockResponses/WidgetConfigResponse.tsx index 9db7797085..8cdc971899 100644 --- a/app/client/src/mockResponses/WidgetConfigResponse.tsx +++ b/app/client/src/mockResponses/WidgetConfigResponse.tsx @@ -155,8 +155,8 @@ const WidgetConfigResponse: WidgetConfigReducerState = { columns: 3, label: "", options: [ - { id: "1", label: "Male", value: "M" }, - { id: "2", label: "Female", value: "F" }, + { label: "Male", value: "M" }, + { label: "Female", value: "F" }, ], defaultOptionValue: "M", widgetName: "RadioGroup", diff --git a/app/client/src/pages/AppViewer/viewer/AppViewerHeader.tsx b/app/client/src/pages/AppViewer/viewer/AppViewerHeader.tsx index 867b5d4d2a..7ab816bda2 100644 --- a/app/client/src/pages/AppViewer/viewer/AppViewerHeader.tsx +++ b/app/client/src/pages/AppViewer/viewer/AppViewerHeader.tsx @@ -214,6 +214,7 @@ export const AppViewerHeader = (props: AppViewerHeaderProps) => { orgId={currentOrgId} applicationId={currentApplicationDetails.id} title={currentApplicationDetails.name} + canOutsideClickClose={true} /> {CTA} diff --git a/app/client/src/pages/Applications/ApplicationCard.tsx b/app/client/src/pages/Applications/ApplicationCard.tsx index 6f736c8995..6726b6613f 100644 --- a/app/client/src/pages/Applications/ApplicationCard.tsx +++ b/app/client/src/pages/Applications/ApplicationCard.tsx @@ -45,11 +45,10 @@ import { Classes as CsClasses } from "components/ads/common"; type NameWrapperProps = { hasReadPermission: boolean; showOverlay: boolean; - isMenuOpen: boolean; }; const NameWrapper = styled((props: HTMLDivProps & NameWrapperProps) => ( -
+
))` .bp3-card { border-radius: 0; @@ -81,7 +80,7 @@ const NameWrapper = styled((props: HTMLDivProps & NameWrapperProps) => ( & div.image-container { background: ${ - props.hasReadPermission && !props.isMenuOpen + props.hasReadPermission ? getColorWithOpacity( props.theme.colors.card.hoverBG, props.theme.colors.card.hoverBGOpacity, @@ -206,8 +205,6 @@ const ContextDropdownWrapper = styled.div` .${Classes.POPOVER_TARGET} { span { - background: ${props => props.theme.colors.card.targetBg}; - svg { path { fill: ${props => props.theme.colors.card.iconColor}; @@ -385,7 +382,6 @@ export const ApplicationCard = (props: ApplicationCardProps) => { !isMenuOpen && setShowOverlay(false); }} hasReadPermission={hasReadPermission} - isMenuOpen={isMenuOpen} className="t--application-card" > { + if (!values[CREATE_APPLICATION_FORM_NAME_FIELD]) { + return { [CREATE_APPLICATION_FORM_NAME_FIELD]: ERROR_MESSAGE_NAME_EMPTY }; + } else if (!values[CREATE_APPLICATION_FORM_NAME_FIELD].trim()) { + return { + [CREATE_APPLICATION_FORM_NAME_FIELD]: NAME_SPACE_ERROR, + }; + } + return {}; +}; + // TODO(abhinav): abstract onCancel out. export const CreateApplicationForm = (props: Props) => { - const { error, handleSubmit, pristine, submitting } = props; + const { error, handleSubmit, pristine, submitting, invalid } = props; return (
{error && !pristine && } - + void; orgId: string; initialValues: {} } >({ + validate, form: CREATE_APPLICATION_FORM_NAME, onSubmit: createApplicationFormSubmitHandler, })(CreateApplicationForm), diff --git a/app/client/src/pages/Applications/helpers.ts b/app/client/src/pages/Applications/helpers.ts index 89e08fe4a7..c2a527c107 100644 --- a/app/client/src/pages/Applications/helpers.ts +++ b/app/client/src/pages/Applications/helpers.ts @@ -5,6 +5,8 @@ export type CreateApplicationFormValues = { orgId: string; }; +export const CREATE_APPLICATION_FORM_NAME_FIELD = "applicationName"; + export const createApplicationFormSubmitHandler = ( values: CreateApplicationFormValues, dispatch: any, diff --git a/app/client/src/pages/Applications/index.tsx b/app/client/src/pages/Applications/index.tsx index ae78ae85b5..eacb75e9b1 100644 --- a/app/client/src/pages/Applications/index.tsx +++ b/app/client/src/pages/Applications/index.tsx @@ -246,6 +246,7 @@ function LeftPane() { key={org.organization.name} href={`${window.location.pathname}#${org.organization.name}`} text={org.organization.name} + ellipsize={20} /> ))} @@ -259,6 +260,28 @@ const CreateNewLabel = styled(Text)` margin-top: 18px; `; +const OrgNameElement = styled(Text)` + max-width: 500px; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + display: block; +`; + +const OrgNameHolder = styled(Text)` + display: flex; + align-items: center; +`; + +const OrgNameInMenu = styled(Text)` + max-width: 100%; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + display: block; + padding: 9px ${props => props.theme.spaces[6]}px; +`; + const OrgNameWrapper = styled.div<{ disabled?: boolean }>` cursor: ${props => (!props.disabled ? "pointer" : "inherit")}; ${props => { @@ -316,10 +339,10 @@ const ApplicationsSection = () => { const OrgName = ( - - {orgName} + + {orgName} - + ); return disabled ? ( @@ -330,7 +353,7 @@ const ApplicationsSection = () => { position={Position.BOTTOM_RIGHT} className="t--org-name" > - + {orgName} { {applications.map((application: any) => { return ( application.pages?.length > 0 && ( - + { step={props.step} disabled={!!props.searchKeyword && (!childNode || !props.actions.length)} onCreate={switchToCreateActionPage} + searchKeyword={props.searchKeyword} isDefaultExpanded={ props.config?.isGroupExpanded(params, props.page.pageId) || !!props.searchKeyword diff --git a/app/client/src/pages/Editor/Explorer/Datasources/DatasourceEntity.tsx b/app/client/src/pages/Editor/Explorer/Datasources/DatasourceEntity.tsx index a9fa4f293f..ece7f57fa6 100644 --- a/app/client/src/pages/Editor/Explorer/Datasources/DatasourceEntity.tsx +++ b/app/client/src/pages/Editor/Explorer/Datasources/DatasourceEntity.tsx @@ -4,17 +4,23 @@ import { Plugin } from "api/PluginApi"; import DataSourceContextMenu from "./DataSourceContextMenu"; import { getPluginIcon } from "../ExplorerIcons"; import { useParams } from "react-router"; -import { ExplorerURLParams, getDatasourceIdFromURL } from "../helpers"; +import { + ExplorerURLParams, + getDatasourceIdFromURL, + getQueryIdFromURL, +} from "../helpers"; import Entity, { EntityClassNames } from "../Entity"; import { DATA_SOURCES_EDITOR_ID_URL } from "constants/routes"; import history from "utils/history"; import { fetchDatasourceStructure, saveDatasourceName, + expandDatasourceEntity, } from "actions/datasourceActions"; import { useDispatch, useSelector } from "react-redux"; import { AppState } from "reducers"; import { DatasourceStructureContainer } from "./DatasourceStructureContainer"; +import { getAction } from "selectors/entitiesSelector"; type ExplorerDatasourceEntityProps = { plugin: Plugin; @@ -41,6 +47,11 @@ export const ExplorerDatasourceEntity = ( [params.applicationId, params.pageId, props.datasource.id], ); + const queryId = getQueryIdFromURL(); + const queryAction = useSelector((state: AppState) => + getAction(state, queryId || ""), + ); + const datasourceIdFromURL = getDatasourceIdFromURL(); const active = datasourceIdFromURL === props.datasource.id; @@ -51,10 +62,20 @@ export const ExplorerDatasourceEntity = ( return state.entities.datasources.structure[props.datasource.id]; }); - const getDatasourceStructure = useCallback(() => { - if (!datasourceStructure) - dispatch(fetchDatasourceStructure(props.datasource.id)); - }, [datasourceStructure, props.datasource.id, dispatch]); + const expandDatasourceId = useSelector((state: AppState) => { + return state.ui.datasourcePane.expandDatasourceId; + }); + + const getDatasourceStructure = useCallback( + isOpen => { + if (!datasourceStructure && isOpen) { + dispatch(fetchDatasourceStructure(props.datasource.id)); + } + + dispatch(expandDatasourceEntity(isOpen ? props.datasource.id : "")); + }, + [datasourceStructure, props.datasource.id, dispatch], + ); return ( any; runActionOnExpand?: boolean; onNameEdit?: (input: string, limit?: number) => string; - onToggle?: () => void; + onToggle?: (isOpen: boolean) => void; }; export const Entity = forwardRef( (props: EntityProps, ref: React.Ref) => { - const [isOpen, open] = useState( - !props.disabled && !!props.isDefaultExpanded, - ); + const [isOpen, open] = useState(!!props.isDefaultExpanded); const isUpdating = useEntityUpdateState(props.entityId); const isEditing = useEntityEditState(props.entityId); + /* eslint-disable react-hooks/exhaustive-deps */ useEffect(() => { - // If the default state must be expanded, expand to show children - if (props.isDefaultExpanded) { + if (!!props.isDefaultExpanded) { open(true); + props.onToggle && props.onToggle(true); } + }, [props.isDefaultExpanded]); + useEffect(() => { if (!props.searchKeyword && !props.isDefaultExpanded) { open(false); } - }, [props.isDefaultExpanded, open, props.searchKeyword]); + }, [props.searchKeyword]); + /* eslint-enable react-hooks/exhaustive-deps */ const toggleChildren = () => { // Make sure this entity is enabled before toggling the collpse of children. @@ -117,8 +119,8 @@ export const Entity = forwardRef( props.action && props.action(); } - if (props.onToggle && !isOpen) { - props.onToggle(); + if (props.onToggle) { + props.onToggle(!isOpen); } }; @@ -154,7 +156,7 @@ export const Entity = forwardRef( disabled={!!props.disabled} className={`${EntityClassNames.COLLAPSE_TOGGLE}`} /> - {props.icon} + {props.icon} { entityId="Pages" step={props.step} onCreate={createPageCallback} + searchKeyword={props.searchKeyword} > {pageEntities} diff --git a/app/client/src/pages/Editor/Explorer/Widgets/WidgetGroup.tsx b/app/client/src/pages/Editor/Explorer/Widgets/WidgetGroup.tsx index 660b1ae607..9abc3ea976 100644 --- a/app/client/src/pages/Editor/Explorer/Widgets/WidgetGroup.tsx +++ b/app/client/src/pages/Editor/Explorer/Widgets/WidgetGroup.tsx @@ -176,6 +176,7 @@ export const ExplorerWidgetGroup = memo((props: ExplorerWidgetGroupProps) => { (params.pageId === props.pageId && !!selectedWidget) } onCreate={props.addWidgetsFn} + searchKeyword={props.searchKeyword} > {childNode} diff --git a/app/client/src/pages/Editor/Explorer/helpers.tsx b/app/client/src/pages/Editor/Explorer/helpers.tsx index 1551b2070c..08c9c37ec1 100644 --- a/app/client/src/pages/Editor/Explorer/helpers.tsx +++ b/app/client/src/pages/Editor/Explorer/helpers.tsx @@ -40,6 +40,15 @@ export const getActionIdFromURL = () => { } }; +export const getQueryIdFromURL = () => { + const match = matchPath<{ queryId: string }>(window.location.pathname, { + path: QUERIES_EDITOR_ID_URL(), + }); + if (match?.params?.queryId) { + return match.params.queryId; + } +}; + export const getDatasourceIdFromURL = () => { const match = matchPath<{ datasourceId: string }>(window.location.pathname, { path: DATA_SOURCES_EDITOR_ID_URL(), diff --git a/app/client/src/pages/UserAuth/ForgotPassword.tsx b/app/client/src/pages/UserAuth/ForgotPassword.tsx index 6bc6d9ca80..e090f5e603 100644 --- a/app/client/src/pages/UserAuth/ForgotPassword.tsx +++ b/app/client/src/pages/UserAuth/ForgotPassword.tsx @@ -56,13 +56,11 @@ export const ForgotPassword = (props: ForgotPasswordProps) => { const { error, handleSubmit, - pristine, submitting, submitFailed, submitSucceeded, } = props; - const queryParams = new URLSearchParams(props.location.search); - const hasEmail = queryParams.get("email"); + return ( {submitSucceeded && ( @@ -86,7 +84,9 @@ export const ForgotPassword = (props: ForgotPasswordProps) => { ]} /> )} - {submitFailed && error && } + {submitFailed && error && ( + + )}

{FORGOT_PASSWORD_PAGE_TITLE}

{FORGOT_PASSWORD_PAGE_SUBTITLE}
@@ -110,7 +110,7 @@ export const ForgotPassword = (props: ForgotPasswordProps) => { intent="primary" filled size="large" - disabled={pristine && !hasEmail} + disabled={!isEmail(props.emailValue)} loading={submitting} /> diff --git a/app/client/src/pages/UserAuth/Login.tsx b/app/client/src/pages/UserAuth/Login.tsx index ad5a21180d..2bbe38c74b 100644 --- a/app/client/src/pages/UserAuth/Login.tsx +++ b/app/client/src/pages/UserAuth/Login.tsx @@ -106,7 +106,7 @@ export const Login = (props: LoginFormProps) => { {showError && ( { name={LOGIN_FORM_EMAIL_FIELD_NAME} type="email" placeholder={LOGIN_PAGE_EMAIL_INPUT_PLACEHOLDER} + autoFocus /> { /> - {SocialLoginList.length > 0 && } - + {SocialLoginList.length > 0 && ( + <> + + + + )} {LOGIN_PAGE_SIGN_UP_LINK_TEXT} diff --git a/app/client/src/pages/UserAuth/ResetPassword.tsx b/app/client/src/pages/UserAuth/ResetPassword.tsx index 427fd13665..21c867318c 100644 --- a/app/client/src/pages/UserAuth/ResetPassword.tsx +++ b/app/client/src/pages/UserAuth/ResetPassword.tsx @@ -159,6 +159,7 @@ export const ResetPassword = (props: ResetPasswordProps) => { name="password" type="password" placeholder={RESET_PASSWORD_PAGE_PASSWORD_INPUT_PLACEHOLDER} + disabled={submitSucceeded} /> @@ -170,7 +171,7 @@ export const ResetPassword = (props: ResetPasswordProps) => { type="submit" text={RESET_PASSWORD_SUBMIT_BUTTON_TEXT} intent="primary" - disabled={pristine} + disabled={pristine || submitSucceeded} loading={submitting} /> diff --git a/app/client/src/pages/UserAuth/SignUp.tsx b/app/client/src/pages/UserAuth/SignUp.tsx index ff16b7e290..8ce7c87a9d 100644 --- a/app/client/src/pages/UserAuth/SignUp.tsx +++ b/app/client/src/pages/UserAuth/SignUp.tsx @@ -130,6 +130,7 @@ export const SignUp = (props: SignUpFormProps) => { name="email" type="email" placeholder={SIGNUP_PAGE_EMAIL_INPUT_PLACEHOLDER} + autoFocus /> { /> - {SocialLoginList.length > 0 && } - + {SocialLoginList.length > 0 && ( + <> + + + + )} diff --git a/app/client/src/pages/UserAuth/StyledComponents.tsx b/app/client/src/pages/UserAuth/StyledComponents.tsx index b4b39a2584..dab5f834ae 100644 --- a/app/client/src/pages/UserAuth/StyledComponents.tsx +++ b/app/client/src/pages/UserAuth/StyledComponents.tsx @@ -83,6 +83,9 @@ export const SpacedSubmitForm = styled.form` & a { font-size: ${props => props.theme.fontSizes[3]}px; } + &:only-child { + margin-right: 0; + } `; export const FormActions = styled.div` diff --git a/app/client/src/pages/common/ProfileDropdown.tsx b/app/client/src/pages/common/ProfileDropdown.tsx index 6c6f731fb5..d88d84218a 100644 --- a/app/client/src/pages/common/ProfileDropdown.tsx +++ b/app/client/src/pages/common/ProfileDropdown.tsx @@ -21,9 +21,9 @@ type TagProps = CommonComponentProps & { userName?: string; }; -const ProfileImage = styled.div<{ backgroundColor?: string }>` - width: 30px; - height: 30px; +export const ProfileImage = styled.div<{ backgroundColor?: string }>` + width: 34px; + height: 34px; display: flex; align-items: center; border-radius: 50%; diff --git a/app/client/src/pages/organization/AppInviteUsersForm.tsx b/app/client/src/pages/organization/AppInviteUsersForm.tsx index 24ba07bdf8..0f22b81c60 100644 --- a/app/client/src/pages/organization/AppInviteUsersForm.tsx +++ b/app/client/src/pages/organization/AppInviteUsersForm.tsx @@ -12,33 +12,24 @@ import { import { getDefaultPageId } from "sagas/SagaUtils"; import { getApplicationViewerPageURL } from "constants/routes"; import OrgInviteUsersForm from "./OrgInviteUsersForm"; -import { StyledSwitch } from "components/propertyControls/StyledControls"; -import Spinner from "components/editorComponents/Spinner"; import { getCurrentUser } from "selectors/usersSelectors"; +import Text, { TextType } from "components/ads/Text"; +import Toggle from "components/ads/Toggle"; const Title = styled.div` - font-weight: bold; padding: 10px 0px; `; const ShareWithPublicOption = styled.div` - { - display: flex; - padding: 10px 0px; - justify-content: space-between; - } + display: flex; + margin-bottom: 15px; + align-items: center; + justify-content: space-between; `; const ShareToggle = styled.div` - { - &&& label { - margin-bottom: 0px; - } - &&& div { - margin-right: 5px; - } - display: flex; - } + flex-basis: 48px; + height: 23px; `; const AppInviteUsersForm = (props: any) => { @@ -84,29 +75,30 @@ const AppInviteUsersForm = (props: any) => { {canShareWithPublic && ( <> - Make the application public + Make the application public - {(isChangingViewAccess || isFetchingApplication) && ( - - )} {currentApplicationDetails && ( - { + { changeAppViewAccess( applicationId, !currentApplicationDetails.isPublic, ); }} - disabled={isChangingViewAccess || isFetchingApplication} - checked={currentApplicationDetails.isPublic} - large /> )} )} - Get Shareable link for this for this application + + <Text type={TextType.H5}> + Get Shareable link for this for this application + </Text> + {canInviteToOrg && ( diff --git a/app/client/src/pages/organization/CreateOrganizationForm.tsx b/app/client/src/pages/organization/CreateOrganizationForm.tsx index d27a4098ce..a705e5cd2c 100644 --- a/app/client/src/pages/organization/CreateOrganizationForm.tsx +++ b/app/client/src/pages/organization/CreateOrganizationForm.tsx @@ -5,6 +5,7 @@ import { CreateOrganizationFormValues, createOrganizationSubmitHandler, } from "./helpers"; +import { noSpaces } from "utils/formhelpers"; import TextField from "components/editorComponents/form/fields/TextField"; import FormGroup from "components/editorComponents/form/FormGroup"; import FormFooter from "components/editorComponents/form/FormFooter"; @@ -19,18 +20,19 @@ export const CreateApplicationForm = ( onCancel: () => void; }, ) => { - const { error, handleSubmit, pristine, submitting } = props; + const { error, handleSubmit, pristine, submitting, invalid } = props; return ( {error && !pristine && } - + props.theme.spaces[5]}px; + background: ${props => props.theme.colors.modal.bg}; &&& { - .wrapper > div { - width: 70%; + .wrapper > div:nth-child(1) { + width: 60%; + } + .wrapper > div:nth-child(2) { + width: 40%; } .bp3-input { box-shadow: none; @@ -69,10 +61,39 @@ const StyledForm = styled.form` padding-top: 5px; } } - .manageUsers { - float: right; - margin-top: 20px; +`; + +const ManageUsers = styled("a")` + margin-top: 20px; + display: inline-flex; + &&&& { + text-decoration: none; } + + .${Classes.TEXT} { + color: ${props => props.theme.colors.modal.manageUser}; + margin-right: ${props => props.theme.spaces[1]}px; + } + .${Classes.ICON} { + svg path { + fill: ${props => props.theme.colors.modal.manageUser}; + } + } + + &:hover { + .${Classes.TEXT} { + color: ${props => props.theme.colors.modal.headerText}; + } + .${Classes.ICON} { + svg path { + fill: ${props => props.theme.colors.modal.headerText}; + } + } + } +`; + +const ErrorBox = styled.div<{ message?: boolean }>` + ${props => (props.message ? `margin: ${props.theme.spaces[9]}px 0px` : null)}; `; const StyledInviteFieldGroup = styled.div` @@ -82,38 +103,53 @@ const StyledInviteFieldGroup = styled.div` .wrapper { display: flex; - width: 100%; + width: 85%; flex-direction: row; + align-items: center; justify-content: space-between; - padding-right: 5px; - border-width: 1px; + margin-right: 5px; border-right: 0px; - border-style: solid; - border-color: ${Colors.ATHENS_GRAY}; } `; const UserList = styled.div` - max-height: 200px; - margin-top: 20px; - overflow-y: scroll; - .user { - display: flex; - flex-direction: row; - justify-content: space-between; - margin-top: 8px; - margin-bottom: 8px; + margin-top: 10px; +`; + +const User = styled.div` + display: flex; + align-items: center; + height: 54px; + padding-left: 15px; + justify-content: space-between; + color: ${props => props.theme.colors.modal.user.textColor}; +`; + +const UserInfo = styled.div` + display: inline-flex; + align-items: center; +`; + +const UserRole = styled.div` + flex-basis: 25%; + .${Classes.TEXT} { + color: ${props => props.theme.colors.modal.headerText}; } `; -const StyledButton = styled(Button)` - &&&.${Classes.BUTTON} { - width: 83px; - height: 31px; - border-radius: 0px; +const UserName = styled.div` + display: flex; + flex-direction: column; + margin-left: 10px; + span:nth-child(1) { + margin-bottom: 1px; } `; +const RoleDivider = styled.div` + border-top: 1px solid ${props => props.theme.colors.menuBorder}; +`; + const Loading = styled(Spinner)` padding-top: 10px; margin: auto; @@ -123,15 +159,16 @@ const Loading = styled(Spinner)` const MailConfigContainer = styled.div` display: flex; flex-direction: column; - padding: 5px; + padding: ${props => props.theme.spaces[9]}px + ${props => props.theme.spaces[2]}px; align-items: center; && > span { - color: #2e3d49; + color: ${props => props.theme.colors.modal.email.message}; font-weight: 500; font-size: 14px; } && > a { - color: rgba(46, 61, 73, 0.5); + color: ${props => props.theme.colors.modal.email.desc}; font-size: 12px; text-decoration: underline; } @@ -208,22 +245,39 @@ const OrgInviteUsersForm = (props: any) => { const styledRoles = props.roles.map((role: any) => { return { id: role.id, - name: role.name, - content: ( - - {role.name} - {role.description} - - ), + value: role.name, + label: role.description, }; }); + const themeDetails = useSelector(getThemeDetails); + + const allUsersProfiles = React.useMemo( + () => + allUsers.map( + (user: { username: string; roleName: string; name: string }) => { + const details = getInitialsAndColorCode( + user.name || user.username, + themeDetails.theme.colors.appCardColors, + ); + return { + ...user, + imageBackground: details[1], + initials: details[0], + }; + }, + ), + [allUsers, themeDetails], + ); + return ( <> {isApplicationInvite && ( <> - Invite Users to {currentOrg?.name} + + Invite Users to {currentOrg?.name} + )} { return inviteUsersToOrg({ ...values, orgId: props.orgId }, dispatch); })} > - {submitSucceeded && ( - - )} - {submitFailed && error && ( - - )}
{ data-cy="t--invite-role-input" />
-
{isLoading ? ( @@ -286,27 +334,63 @@ const OrgInviteUsersForm = (props: any) => { )} - {allUsers.map((user: { username: string; roleName: string }) => { - return ( -
-
{user.username}
-
{user.roleName}
-
- ); - })} + {allUsersProfiles.map( + (user: { + username: string; + name: string; + roleName: string; + imageBackground: string; + initials: string; + }) => { + return ( + + + + + + {user.initials} + + + + {user.name} + {user.username} + + + + {user.roleName} + + + + + + ); + }, + )}
)} + + {submitSucceeded && ( + + )} + {submitFailed && error && ( + + )} + {!pathRegex.test(currentPath) && canManage && ( -