2025-01-10 04:51:54 +00:00
import type { ReduxAction , ReduxActionType } from "actions/ReduxActionTypes" ;
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
import {
ReduxActionErrorTypes ,
2021-01-25 08:57:26 +00:00
ReduxActionTypes ,
2021-08-03 08:06:48 +00:00
WidgetReduxActionTypes ,
2024-08-06 14:52:22 +00:00
} from "ee/constants/ReduxActionConstants" ;
import AnalyticsUtil from "ee/utils/AnalyticsUtil" ;
2024-05-09 04:29:12 +00:00
import WidgetFactory from "WidgetProvider/factory" ;
import type {
BatchUpdateDynamicPropertyUpdates ,
BatchUpdateWidgetDynamicPropertyPayload ,
DeleteWidgetPropertyPayload ,
SetWidgetDynamicPropertyPayload ,
UpdateWidgetPropertyPayload ,
UpdateWidgetPropertyRequestPayload ,
} from "actions/controlActions" ;
import {
batchUpdateWidgetProperty ,
updateMultipleWidgetPropertiesAction ,
} from "actions/controlActions" ;
2023-04-07 13:51:35 +00:00
import { resetWidgetMetaProperty } from "actions/metaActions" ;
2024-05-09 04:29:12 +00:00
import type { WidgetResize } from "actions/pageActions" ;
import { updateAndSaveLayout } from "actions/pageActions" ;
2023-04-07 13:51:35 +00:00
import { selectWidgetInitAction } from "actions/widgetSelectionActions" ;
2024-05-09 04:29:12 +00:00
import type { PasteWidgetReduxAction } from "constants/WidgetConstants" ;
2023-04-07 13:51:35 +00:00
import {
GridDefaults ,
MAIN_CONTAINER_WIDGET_ID ,
RenderModes ,
2023-07-27 13:00:23 +00:00
WIDGET_ID_SHOW_WALKTHROUGH ,
2023-04-07 13:51:35 +00:00
} from "constants/WidgetConstants" ;
2024-05-09 04:29:12 +00:00
import _ , { cloneDeep , get , isString , set , uniq } from "lodash" ;
2023-04-07 13:51:35 +00:00
import log from "loglevel" ;
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
import type {
2020-09-16 10:28:01 +00:00
CanvasWidgetsReduxState ,
2021-01-25 08:57:26 +00:00
FlattenedWidgetProps ,
2025-02-18 10:42:05 +00:00
} from "ee/reducers/entityReducers/canvasWidgetsReducer" ;
2020-03-06 09:45:21 +00:00
import {
2022-05-06 05:42:35 +00:00
actionChannel ,
2021-01-25 08:57:26 +00:00
all ,
2020-03-06 09:45:21 +00:00
call ,
2021-06-17 13:26:54 +00:00
fork ,
2020-03-06 09:45:21 +00:00
put ,
select ,
2023-01-28 02:17:06 +00:00
take ,
2020-03-06 09:45:21 +00:00
takeEvery ,
takeLatest ,
2022-05-11 09:16:22 +00:00
takeLeading ,
2020-03-06 09:45:21 +00:00
} from "redux-saga/effects" ;
2023-04-07 13:51:35 +00:00
import {
getCanvasWidth ,
2024-07-31 02:54:51 +00:00
getCurrentBasePageId ,
2023-04-07 13:51:35 +00:00
getIsAutoLayout ,
getIsAutoLayoutMobileBreakPoint ,
} from "selectors/editorSelectors" ;
2021-09-21 07:55:56 +00:00
import { convertToString } from "utils/AppsmithUtils" ;
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
import type { DynamicPath } from "utils/DynamicBindingUtils" ;
import {
2020-11-12 11:23:32 +00:00
getEntityDynamicBindingPathList ,
getWidgetDynamicPropertyPathList ,
getWidgetDynamicTriggerPathList ,
2021-01-25 08:57:26 +00:00
isChildPropertyPath ,
2020-11-12 11:23:32 +00:00
isDynamicValue ,
isPathADynamicBinding ,
2022-12-19 10:15:50 +00:00
isPathDynamicTrigger ,
2020-11-12 11:23:32 +00:00
} from "utils/DynamicBindingUtils" ;
2020-09-16 10:28:01 +00:00
import { generateReactKey } from "utils/generators" ;
2023-04-07 13:51:35 +00:00
import { getCopiedWidgets , saveCopiedWidgets } from "utils/storage" ;
2024-05-09 04:29:12 +00:00
import type { WidgetProps } from "widgets/BaseWidget" ;
2023-04-07 13:51:35 +00:00
import { getWidget , getWidgets , getWidgetsMeta } from "./selectors" ;
2021-06-17 13:26:54 +00:00
2024-08-06 14:52:22 +00:00
import { builderURL } from "ee/RouteBuilder" ;
2023-04-07 13:51:35 +00:00
import {
2024-07-05 08:27:59 +00:00
ERROR_PASTE_ANVIL_LAYOUT_SYSTEM_CONFLICT ,
ERROR_PASTE_FIXED_LAYOUT_SYSTEM_CONFLICT ,
2023-04-07 13:51:35 +00:00
ERROR_WIDGET_COPY_NOT_ALLOWED ,
ERROR_WIDGET_COPY_NO_WIDGET_SELECTED ,
ERROR_WIDGET_CUT_NOT_ALLOWED ,
ERROR_WIDGET_CUT_NO_WIDGET_SELECTED ,
WIDGET_COPY ,
WIDGET_CUT ,
createMessage ,
2024-08-06 14:52:22 +00:00
} from "ee/constants/messages" ;
import type { WidgetEntityConfig } from "ee/entities/DataTree/types" ;
import { getAllPaths } from "ee/workers/Evaluation/evaluationUtils" ;
2024-05-09 04:29:12 +00:00
import { BlueprintOperationTypes } from "WidgetProvider/constants" ;
import { generateAutoHeightLayoutTreeAction } from "actions/autoHeightActions" ;
import { stopReflowAction } from "actions/reflowActions" ;
2024-08-09 14:20:29 +00:00
import { toast } from "@appsmith/ads" ;
2024-05-09 04:29:12 +00:00
import type { ConfigTree , DataTree } from "entities/DataTree/dataTreeTypes" ;
2021-03-16 05:01:37 +00:00
import {
getAllPathsFromPropertyConfig ,
nextAvailableRowInContainer ,
} from "entities/Widget/utils" ;
2024-05-09 04:29:12 +00:00
import type { MetaState } from "reducers/entityReducers/metaReducer" ;
import type { widgetReflow } from "reducers/uiReducers/reflowReducer" ;
import type { GridProps , SpaceMap } from "reflow/reflowTypes" ;
import { SelectionRequestType } from "sagas/WidgetSelectUtils" ;
import { getConfigTree , getDataTree } from "selectors/dataTreeSelectors" ;
2023-04-07 13:51:35 +00:00
import { getSelectedWidgets } from "selectors/ui" ;
import { getReflow } from "selectors/widgetReflowSelectors" ;
2024-05-09 04:29:12 +00:00
import { flashElementsById } from "utils/helpers" ;
import history from "utils/history" ;
import { collisionCheckPostReflow } from "utils/reflowHookUtils" ;
import type { ColumnProperties } from "widgets/TableWidget/component/Constants" ;
2021-03-13 14:24:45 +00:00
import {
2023-04-07 13:51:35 +00:00
addChildToPastedFlexLayers ,
getFlexLayersForSelectedWidgets ,
getLayerIndexOfWidget ,
getNewFlexLayers ,
isStack ,
pasteWidgetInFlexLayers ,
2023-09-11 15:55:11 +00:00
} from "../layoutSystems/autolayout/utils/AutoLayoutUtils" ;
2024-05-09 04:29:12 +00:00
import { getCanvasSizeAfterWidgetMove } from "./CanvasSagas/DraggingCanvasSagas" ;
import { validateProperty } from "./EvaluationsSaga" ;
import {
partialExportSaga ,
partialImportSaga ,
} from "./PartialImportExportSagas" ;
import widgetAdditionSagas from "./WidgetAdditionSagas" ;
import {
executeWidgetBlueprintBeforeOperations ,
traverseTreeAndExecuteBlueprintChildOperations ,
} from "./WidgetBlueprintSagas" ;
import widgetDeletionSagas from "./WidgetDeletionSagas" ;
import type { CopiedWidgetGroup } from "./WidgetOperationUtils" ;
2021-04-23 05:43:13 +00:00
import {
2021-08-25 05:00:31 +00:00
createSelectedWidgetsAsCopiedWidgets ,
2023-01-28 02:17:06 +00:00
createWidgetCopy ,
doesTriggerPathsContainPropertyPath ,
2021-08-25 05:00:31 +00:00
filterOutSelectedWidgets ,
getBoundaryWidgetsFromCopiedGroups ,
2023-01-28 02:17:06 +00:00
getNextWidgetName ,
getParentWidgetIdForGrouping ,
getParentWidgetIdForPasting ,
getReflowedPositions ,
getSelectedWidgetWhenPasting ,
2022-05-06 05:42:35 +00:00
getValueFromTree ,
2023-01-28 02:17:06 +00:00
getWidgetDescendantToReset ,
groupWidgetsIntoContainer ,
2024-06-25 06:13:00 +00:00
handleIfParentIsListWidgetWhilePasting ,
2023-01-28 02:17:06 +00:00
handleSpecificCasesWhilePasting ,
2024-07-05 08:27:59 +00:00
isLayoutSystemConflictingForPaste ,
2023-01-28 02:17:06 +00:00
isSelectedWidgetsColliding ,
2022-07-14 07:02:35 +00:00
mergeDynamicPropertyPaths ,
2023-01-28 02:17:06 +00:00
purgeOrphanedDynamicPaths ,
2021-04-23 05:43:13 +00:00
} from "./WidgetOperationUtils" ;
2024-01-17 07:13:51 +00:00
import { widgetSelectionSagas } from "./WidgetSelectionSagas" ;
2023-05-19 18:37:06 +00:00
2024-05-09 04:29:12 +00:00
import { EMPTY_BINDING } from "components/editorComponents/ActionCreator/constants" ;
import { shouldShowSlashCommandMenu } from "components/editorComponents/CodeEditor/codeEditorUtils" ;
import { addSuggestedWidgetAnvilAction } from "layoutSystems/anvil/integrations/actions/draggingActions" ;
import { getIsAnvilLayout } from "layoutSystems/anvil/integrations/selectors" ;
import { updateAndSaveAnvilLayout } from "layoutSystems/anvil/utils/anvilChecksUtils" ;
import { getWidgetWidth } from "layoutSystems/autolayout/utils/flexWidgetUtils" ;
2023-05-11 04:45:14 +00:00
import {
updatePositionsOfParentAndSiblings ,
updateWidgetPositions ,
2023-09-11 15:55:11 +00:00
} from "layoutSystems/autolayout/utils/positionUtils" ;
2024-05-09 04:29:12 +00:00
import type { FlexLayer } from "layoutSystems/autolayout/utils/types" ;
2023-04-21 06:23:17 +00:00
import {
FlexLayerAlignment ,
LayoutDirection ,
2023-10-02 19:41:05 +00:00
} from "layoutSystems/common/utils/constants" ;
2024-05-09 04:29:12 +00:00
import { LayoutSystemTypes } from "layoutSystems/types" ;
2023-10-04 08:54:16 +00:00
import { getLayoutSystemType } from "selectors/layoutSystemSelectors" ;
2024-05-09 04:29:12 +00:00
import localStorage from "utils/localStorage" ;
import { getNewPositions } from "./PasteWidgetUtils" ;
2023-03-04 07:25:54 +00:00
2019-09-22 20:25:05 +00:00
export function * resizeSaga ( resizeAction : ReduxAction < WidgetResize > ) {
try {
2023-05-19 18:37:06 +00:00
toast . dismiss ( ) ;
2020-09-30 12:42:09 +00:00
const start = performance . now ( ) ;
2023-04-07 13:51:35 +00:00
const stateWidgets : CanvasWidgetsReduxState = yield select ( getWidgets ) ;
const stateWidget : FlattenedWidgetProps = yield select (
getWidget ,
resizeAction . payload . widgetId ,
) ;
const isMobile : boolean = yield select ( getIsAutoLayoutMobileBreakPoint ) ;
const widgets = { . . . stateWidgets } ;
let widget = { . . . stateWidget } ;
2019-10-02 19:42:25 +00:00
const {
2023-04-07 13:51:35 +00:00
bottomRow = widget . bottomRow ,
leftColumn = widget . leftColumn ,
mobileBottomRow = widget . mobileBottomRow ,
mobileLeftColumn = widget . mobileLeftColumn ,
mobileRightColumn = widget . mobileRightColumn ,
mobileTopRow = widget . mobileTopRow ,
2021-09-21 07:55:56 +00:00
parentId ,
2023-04-07 13:51:35 +00:00
rightColumn = widget . rightColumn ,
2022-01-13 13:21:57 +00:00
snapColumnSpace ,
snapRowSpace ,
2023-04-07 13:51:35 +00:00
topRow = widget . topRow ,
2021-05-13 08:35:39 +00:00
widgetId ,
2019-10-02 19:42:25 +00:00
} = resizeAction . payload ;
2019-09-22 20:25:05 +00:00
2023-10-11 07:14:38 +00:00
const layoutSystemType : LayoutSystemTypes =
yield select ( getLayoutSystemType ) ;
2023-04-07 13:51:35 +00:00
const mainCanvasWidth : number = yield select ( getCanvasWidth ) ;
2024-09-18 16:35:28 +00:00
2023-04-07 13:51:35 +00:00
widget = {
. . . widget ,
leftColumn ,
rightColumn ,
topRow ,
bottomRow ,
mobileLeftColumn ,
mobileRightColumn ,
mobileTopRow ,
mobileBottomRow ,
} ;
2023-10-04 08:54:16 +00:00
if ( layoutSystemType === LayoutSystemTypes . AUTO ) {
2023-04-07 13:51:35 +00:00
// Keeps track of user defined widget width in terms of percentage
if ( isMobile ) {
widget . mobileWidthInPercentage =
( getWidgetWidth ( widget , true ) * snapColumnSpace ) / mainCanvasWidth ;
} else {
widget . widthInPercentage =
( getWidgetWidth ( widget , false ) * snapColumnSpace ) / mainCanvasWidth ;
}
}
2023-02-03 05:47:40 +00:00
const movedWidgets : {
2022-01-13 13:21:57 +00:00
[ widgetId : string ] : FlattenedWidgetProps ;
} = yield call (
reflowWidgets ,
widgets ,
widget ,
snapColumnSpace ,
snapRowSpace ,
) ;
2021-09-21 07:55:56 +00:00
const updatedCanvasBottomRow : number = yield call (
getCanvasSizeAfterWidgetMove ,
parentId ,
2022-01-25 15:28:31 +00:00
[ widgetId ] ,
2021-09-21 07:55:56 +00:00
bottomRow ,
) ;
2024-09-18 16:35:28 +00:00
2023-05-11 04:45:14 +00:00
// If it is a fixed canvas, update bottomRow directly.
if (
updatedCanvasBottomRow &&
2023-10-04 08:54:16 +00:00
layoutSystemType === LayoutSystemTypes . FIXED
2023-05-11 04:45:14 +00:00
) {
2022-01-13 13:21:57 +00:00
const canvasWidget = movedWidgets [ parentId ] ;
2024-09-18 16:35:28 +00:00
2022-01-13 13:21:57 +00:00
movedWidgets [ parentId ] = {
2021-09-21 07:55:56 +00:00
. . . canvasWidget ,
bottomRow : updatedCanvasBottomRow ,
} ;
}
2024-09-18 16:35:28 +00:00
2023-06-09 08:52:27 +00:00
// If it is an auto-layout canvas, then use positionUtils to update canvas bottomRow.
2023-03-04 07:25:54 +00:00
let updatedWidgetsAfterResizing = movedWidgets ;
2024-09-18 16:35:28 +00:00
2023-10-04 08:54:16 +00:00
if ( layoutSystemType === LayoutSystemTypes . AUTO ) {
2024-07-31 15:41:28 +00:00
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2023-05-11 04:45:14 +00:00
const metaProps : Record < string , any > = yield select ( getWidgetsMeta ) ;
2024-09-18 16:35:28 +00:00
2023-04-07 13:51:35 +00:00
updatedWidgetsAfterResizing = updatePositionsOfParentAndSiblings (
2023-03-04 07:25:54 +00:00
movedWidgets ,
parentId ,
2023-04-07 13:51:35 +00:00
getLayerIndexOfWidget ( widgets [ parentId ] ? . flexLayers , widgetId ) ,
2023-03-04 07:25:54 +00:00
isMobile ,
2023-04-07 13:51:35 +00:00
mainCanvasWidth ,
2023-05-11 04:45:14 +00:00
false ,
metaProps ,
2023-03-04 07:25:54 +00:00
) ;
}
2024-09-18 16:35:28 +00:00
2020-09-30 12:42:09 +00:00
log . debug ( "resize computations took" , performance . now ( ) - start , "ms" ) ;
2022-01-13 13:21:57 +00:00
yield put ( stopReflowAction ( ) ) ;
2023-03-04 07:25:54 +00:00
yield put ( updateAndSaveLayout ( updatedWidgetsAfterResizing ) ) ;
2023-04-07 13:51:35 +00:00
// Widget resize based auto-height is only required for fixed-layout
// Auto-layout has UPDATE_WIDGET_DIMENSIONS to handle auto height
2023-10-04 08:54:16 +00:00
if ( layoutSystemType === LayoutSystemTypes . FIXED ) {
2023-04-07 13:51:35 +00:00
yield put ( generateAutoHeightLayoutTreeAction ( true , true ) ) ;
}
2019-10-02 18:13:04 +00:00
} catch ( error ) {
2019-09-22 20:25:05 +00:00
yield put ( {
2019-10-02 18:13:04 +00:00
type : ReduxActionErrorTypes . WIDGET_OPERATION_ERROR ,
payload : {
2021-08-03 08:06:48 +00:00
action : WidgetReduxActionTypes.WIDGET_RESIZE ,
2019-10-02 18:13:04 +00:00
error ,
2024-09-05 05:36:43 +00:00
logToDebugger : true ,
2019-10-02 18:13:04 +00:00
} ,
2019-09-22 20:25:05 +00:00
} ) ;
}
}
2019-09-19 22:25:37 +00:00
2022-01-13 13:21:57 +00:00
export function * reflowWidgets (
widgets : {
[ widgetId : string ] : FlattenedWidgetProps ;
} ,
widget : FlattenedWidgetProps ,
snapColumnSpace : number ,
snapRowSpace : number ,
) {
2022-04-20 13:03:30 +00:00
const reflowState : widgetReflow = yield select ( getReflow ) ;
2022-01-13 13:21:57 +00:00
const currentWidgets : {
[ widgetId : string ] : FlattenedWidgetProps ;
} = { . . . widgets , [ widget . widgetId ] : { . . . widget } } ;
if ( ! reflowState || ! reflowState . isReflowing || ! reflowState . reflowingWidgets )
return currentWidgets ;
const reflowingWidgets = reflowState . reflowingWidgets ;
const reflowWidgetKeys = Object . keys ( reflowingWidgets || { } ) ;
if ( reflowWidgetKeys . length <= 0 ) return widgets ;
for ( const reflowedWidgetId of reflowWidgetKeys ) {
const reflowWidget = reflowingWidgets [ reflowedWidgetId ] ;
const canvasWidget = { . . . currentWidgets [ reflowedWidgetId ] } ;
2024-09-18 16:35:28 +00:00
2022-01-13 13:21:57 +00:00
if ( reflowWidget . X !== undefined && reflowWidget . width !== undefined ) {
const leftColumn =
canvasWidget . leftColumn + reflowWidget . X / snapColumnSpace ;
const rightColumn = leftColumn + reflowWidget . width / snapColumnSpace ;
2024-09-18 16:35:28 +00:00
2022-01-13 13:21:57 +00:00
currentWidgets [ reflowedWidgetId ] = {
. . . canvasWidget ,
leftColumn ,
rightColumn ,
} ;
} else if (
reflowWidget . Y !== undefined &&
reflowWidget . height !== undefined
) {
const topRow = canvasWidget . topRow + reflowWidget . Y / snapRowSpace ;
const bottomRow = topRow + reflowWidget . height / snapRowSpace ;
2024-09-18 16:35:28 +00:00
2022-01-13 13:21:57 +00:00
currentWidgets [ reflowedWidgetId ] = { . . . canvasWidget , topRow , bottomRow } ;
}
}
if (
collisionCheckPostReflow ( currentWidgets , reflowWidgetKeys , widget . parentId )
) {
return currentWidgets ;
}
return widgets ;
}
2021-01-25 08:57:26 +00:00
enum DynamicPathUpdateEffectEnum {
ADD = "ADD" ,
REMOVE = "REMOVE" ,
NOOP = "NOOP" ,
}
2023-10-11 07:35:24 +00:00
interface DynamicPathUpdate {
2021-01-25 08:57:26 +00:00
propertyPath : string ;
effect : DynamicPathUpdateEffectEnum ;
2023-10-11 07:35:24 +00:00
}
2021-01-25 08:57:26 +00:00
function getDynamicTriggerPathListUpdate (
2020-03-06 09:33:20 +00:00
widget : WidgetProps ,
2020-11-12 11:23:32 +00:00
propertyPath : string ,
2020-03-06 09:33:20 +00:00
propertyValue : string ,
2021-01-25 08:57:26 +00:00
) : DynamicPathUpdate {
2022-12-19 10:15:50 +00:00
if ( propertyValue && ! isPathDynamicTrigger ( widget , propertyPath ) ) {
2021-01-25 08:57:26 +00:00
return {
propertyPath ,
effect : DynamicPathUpdateEffectEnum.ADD ,
} ;
2022-12-19 10:15:50 +00:00
} else if ( ! propertyValue && ! isPathDynamicTrigger ( widget , propertyPath ) ) {
2021-01-25 08:57:26 +00:00
return {
propertyPath ,
effect : DynamicPathUpdateEffectEnum.REMOVE ,
} ;
2019-11-06 06:35:15 +00:00
}
2024-09-18 16:35:28 +00:00
2021-01-25 08:57:26 +00:00
return {
propertyPath ,
effect : DynamicPathUpdateEffectEnum.NOOP ,
} ;
2020-03-06 09:33:20 +00:00
}
chore: misc updates to custom widget (#30114)
#### PR fixes following issue(s)
Fixes https://github.com/appsmithorg/appsmith/issues/29991
Fixes https://github.com/appsmithorg/appsmith/issues/30154
Fixes https://github.com/appsmithorg/appsmith/issues/30020
Fixes https://github.com/appsmithorg/appsmith/issues/30019
Fixes https://github.com/appsmithorg/appsmith/issues/30130
Fixes https://github.com/appsmithorg/appsmith/issues/30159
Fixes https://github.com/appsmithorg/appsmith/issues/30223
#### Media
> A video or a GIF is preferred. when using Loom, don’t embed because it
looks like it’s a GIF. instead, just link to the video
>
>
#### Type of change
> Please delete options that are not relevant.
- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- Chore (housekeeping or task changes that don't impact user perception)
- This change requires a documentation update
>
>
>
## Testing
>
#### How Has This Been Tested?
> Please describe the tests that you ran to verify your changes. Also
list any relevant details for your test configuration.
> Delete anything that is not relevant
- [ ] Manual
- [ ] JUnit
- [ ] Jest
- [ ] Cypress
>
>
#### Test Plan
> Add Testsmith test cases links that relate to this PR
>
>
#### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
>
>
>
## Checklist:
#### Dev activity
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
#### QA activity:
- [ ] [Speedbreak
features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-)
have been covered
- [ ] Test plan covers all impacted features and [areas of
interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-)
- [ ] Test plan has been peer reviewed by project stakeholders and other
QA members
- [ ] Manually tested functionality on DP
- [ ] We had an implementation alignment call with stakeholders post QA
Round 2
- [ ] Cypress test cases have been added and approved by SDET/manual QA
- [ ] Added `Test Plan Approved` label after Cypress tests were reviewed
- [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Custom widgets now support analytics events, enhancing visibility into
user interactions.
- Template selection, layout controls, and reference triggers in the
Custom Widget Builder are now integrated with analytics.
- Added new style options for custom widgets, including `primaryColor`,
`backgroundColor`, `borderRadius`, and `boxShadow`.
- **Bug Fixes**
- Corrected a typo in the constant title for better clarity.
- Updated help text for the Container Widget to accurately describe the
widget's border edge.
- **Enhancements**
- Improved user interface with additional styling for reference names in
the Custom Widget Builder.
- Enhanced debugger functionality with `useCallback` optimization and
new analytics logging.
- **Refactor**
- Streamlined the property pane by introducing a new `LabelContainer`
styled component.
- Refined the handling of dynamic binding paths to ignore certain
properties efficiently.
- **Documentation**
- Added a new constant for the default model documentation URL in the
Custom Widget Builder.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-01-16 05:22:17 +00:00
const DYNAMIC_BINDING_IGNORED_LIST = [
/* Table widget */
"primaryColumns" ,
"derivedColumns" ,
/* custom widget */
"srcDoc.html" ,
"srcDoc.css" ,
"srcDoc.js" ,
"uncompiledSrcDoc.html" ,
"uncompiledSrcDoc.css" ,
"uncompiledSrcDoc.js" ,
] ;
2021-01-25 08:57:26 +00:00
function getDynamicBindingPathListUpdate (
2020-03-06 09:33:20 +00:00
widget : WidgetProps ,
2021-01-25 08:57:26 +00:00
propertyPath : string ,
2024-07-31 15:41:28 +00:00
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2020-09-15 16:54:15 +00:00
propertyValue : any ,
2021-01-25 08:57:26 +00:00
) : DynamicPathUpdate {
2020-09-15 16:54:15 +00:00
let stringProp = propertyValue ;
2024-09-18 16:35:28 +00:00
2020-09-15 16:54:15 +00:00
if ( _ . isObject ( propertyValue ) ) {
// Stringify this because composite controls may have bindings in the sub controls
stringProp = JSON . stringify ( propertyValue ) ;
}
2021-02-16 10:29:08 +00:00
chore: misc updates to custom widget (#30114)
#### PR fixes following issue(s)
Fixes https://github.com/appsmithorg/appsmith/issues/29991
Fixes https://github.com/appsmithorg/appsmith/issues/30154
Fixes https://github.com/appsmithorg/appsmith/issues/30020
Fixes https://github.com/appsmithorg/appsmith/issues/30019
Fixes https://github.com/appsmithorg/appsmith/issues/30130
Fixes https://github.com/appsmithorg/appsmith/issues/30159
Fixes https://github.com/appsmithorg/appsmith/issues/30223
#### Media
> A video or a GIF is preferred. when using Loom, don’t embed because it
looks like it’s a GIF. instead, just link to the video
>
>
#### Type of change
> Please delete options that are not relevant.
- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- Chore (housekeeping or task changes that don't impact user perception)
- This change requires a documentation update
>
>
>
## Testing
>
#### How Has This Been Tested?
> Please describe the tests that you ran to verify your changes. Also
list any relevant details for your test configuration.
> Delete anything that is not relevant
- [ ] Manual
- [ ] JUnit
- [ ] Jest
- [ ] Cypress
>
>
#### Test Plan
> Add Testsmith test cases links that relate to this PR
>
>
#### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
>
>
>
## Checklist:
#### Dev activity
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
#### QA activity:
- [ ] [Speedbreak
features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-)
have been covered
- [ ] Test plan covers all impacted features and [areas of
interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-)
- [ ] Test plan has been peer reviewed by project stakeholders and other
QA members
- [ ] Manually tested functionality on DP
- [ ] We had an implementation alignment call with stakeholders post QA
Round 2
- [ ] Cypress test cases have been added and approved by SDET/manual QA
- [ ] Added `Test Plan Approved` label after Cypress tests were reviewed
- [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Custom widgets now support analytics events, enhancing visibility into
user interactions.
- Template selection, layout controls, and reference triggers in the
Custom Widget Builder are now integrated with analytics.
- Added new style options for custom widgets, including `primaryColor`,
`backgroundColor`, `borderRadius`, and `boxShadow`.
- **Bug Fixes**
- Corrected a typo in the constant title for better clarity.
- Updated help text for the Container Widget to accurately describe the
widget's border edge.
- **Enhancements**
- Improved user interface with additional styling for reference names in
the Custom Widget Builder.
- Enhanced debugger functionality with `useCallback` optimization and
new analytics logging.
- **Refactor**
- Streamlined the property pane by introducing a new `LabelContainer`
styled component.
- Refined the handling of dynamic binding paths to ignore certain
properties efficiently.
- **Documentation**
- Added a new constant for the default model documentation URL in the
Custom Widget Builder.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-01-16 05:22:17 +00:00
/ *
* TODO ( Balaji Soundararajan ) : This is not appropriate from the platform 's archtecture' s point of view .
* This setting should come from widget configuration
* /
2021-02-16 10:29:08 +00:00
// Figure out a holistic solutions where we donot have to stringify above.
chore: misc updates to custom widget (#30114)
#### PR fixes following issue(s)
Fixes https://github.com/appsmithorg/appsmith/issues/29991
Fixes https://github.com/appsmithorg/appsmith/issues/30154
Fixes https://github.com/appsmithorg/appsmith/issues/30020
Fixes https://github.com/appsmithorg/appsmith/issues/30019
Fixes https://github.com/appsmithorg/appsmith/issues/30130
Fixes https://github.com/appsmithorg/appsmith/issues/30159
Fixes https://github.com/appsmithorg/appsmith/issues/30223
#### Media
> A video or a GIF is preferred. when using Loom, don’t embed because it
looks like it’s a GIF. instead, just link to the video
>
>
#### Type of change
> Please delete options that are not relevant.
- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- Chore (housekeeping or task changes that don't impact user perception)
- This change requires a documentation update
>
>
>
## Testing
>
#### How Has This Been Tested?
> Please describe the tests that you ran to verify your changes. Also
list any relevant details for your test configuration.
> Delete anything that is not relevant
- [ ] Manual
- [ ] JUnit
- [ ] Jest
- [ ] Cypress
>
>
#### Test Plan
> Add Testsmith test cases links that relate to this PR
>
>
#### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
>
>
>
## Checklist:
#### Dev activity
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
#### QA activity:
- [ ] [Speedbreak
features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-)
have been covered
- [ ] Test plan covers all impacted features and [areas of
interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-)
- [ ] Test plan has been peer reviewed by project stakeholders and other
QA members
- [ ] Manually tested functionality on DP
- [ ] We had an implementation alignment call with stakeholders post QA
Round 2
- [ ] Cypress test cases have been added and approved by SDET/manual QA
- [ ] Added `Test Plan Approved` label after Cypress tests were reviewed
- [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Custom widgets now support analytics events, enhancing visibility into
user interactions.
- Template selection, layout controls, and reference triggers in the
Custom Widget Builder are now integrated with analytics.
- Added new style options for custom widgets, including `primaryColor`,
`backgroundColor`, `borderRadius`, and `boxShadow`.
- **Bug Fixes**
- Corrected a typo in the constant title for better clarity.
- Updated help text for the Container Widget to accurately describe the
widget's border edge.
- **Enhancements**
- Improved user interface with additional styling for reference names in
the Custom Widget Builder.
- Enhanced debugger functionality with `useCallback` optimization and
new analytics logging.
- **Refactor**
- Streamlined the property pane by introducing a new `LabelContainer`
styled component.
- Refined the handling of dynamic binding paths to ignore certain
properties efficiently.
- **Documentation**
- Added a new constant for the default model documentation URL in the
Custom Widget Builder.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-01-16 05:22:17 +00:00
if ( DYNAMIC_BINDING_IGNORED_LIST . includes ( propertyPath ) ) {
2021-02-16 10:29:08 +00:00
return {
propertyPath ,
effect : DynamicPathUpdateEffectEnum.NOOP ,
} ;
}
2020-09-15 16:54:15 +00:00
const isDynamic = isDynamicValue ( stringProp ) ;
2024-09-18 16:35:28 +00:00
2021-01-25 08:57:26 +00:00
if ( ! isDynamic && isPathADynamicBinding ( widget , propertyPath ) ) {
return {
propertyPath ,
effect : DynamicPathUpdateEffectEnum.REMOVE ,
} ;
} else if ( isDynamic && ! isPathADynamicBinding ( widget , propertyPath ) ) {
return {
propertyPath ,
effect : DynamicPathUpdateEffectEnum.ADD ,
} ;
2020-03-06 09:33:20 +00:00
}
2024-09-18 16:35:28 +00:00
2021-01-25 08:57:26 +00:00
return {
propertyPath ,
effect : DynamicPathUpdateEffectEnum.NOOP ,
} ;
}
function applyDynamicPathUpdates (
currentList : DynamicPath [ ] ,
update : DynamicPathUpdate ,
) : DynamicPath [ ] {
if ( update . effect === DynamicPathUpdateEffectEnum . ADD ) {
currentList . push ( {
key : update.propertyPath ,
2020-11-12 11:23:32 +00:00
} ) ;
2021-01-25 08:57:26 +00:00
} else if ( update . effect === DynamicPathUpdateEffectEnum . REMOVE ) {
2021-03-04 18:35:41 +00:00
currentList = _ . reject ( currentList , { key : update.propertyPath } ) ;
2020-03-06 09:33:20 +00:00
}
2024-09-18 16:35:28 +00:00
2021-01-25 08:57:26 +00:00
return currentList ;
2020-03-06 09:33:20 +00:00
}
function * updateWidgetPropertySaga (
updateAction : ReduxAction < UpdateWidgetPropertyRequestPayload > ,
) {
const {
2021-05-13 08:35:39 +00:00
payload : { propertyPath , propertyValue , widgetId } ,
2020-03-06 09:33:20 +00:00
} = updateAction ;
2021-01-25 08:57:26 +00:00
// Holder object to collect all updates
const updates : Record < string , unknown > = {
[ propertyPath ] : propertyValue ,
} ;
2024-09-18 16:35:28 +00:00
2021-02-25 14:00:02 +00:00
// Push these updates via the batch update
yield call (
batchUpdateWidgetPropertySaga ,
2021-03-01 09:45:54 +00:00
batchUpdateWidgetProperty ( widgetId , { modify : updates } ) ,
2020-03-06 09:33:20 +00:00
) ;
2020-02-26 12:44:56 +00:00
}
2022-05-05 06:38:27 +00:00
export function removeDynamicBindingProperties (
propertyPath : string ,
dynamicBindingPathList : DynamicPath [ ] ,
) {
/ *
2022-10-13 20:13:44 +00:00
we are doing this because when you toggle js off we only
2022-05-05 06:38:27 +00:00
receive the ` primaryColumns. ` properties not the ` derivedColumns. `
properties therefore we need just a hard - codded check .
( TODO ) - Arsalan remove this primaryColumns check when the Table widget v2 is live .
* /
if ( _ . startsWith ( propertyPath , "primaryColumns" ) ) {
// primaryColumns.customColumn1.isVisible -> customColumn1.isVisible
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
const tableProperty = propertyPath . split ( "." ) . splice ( 1 ) . join ( "." ) ;
2022-05-05 06:38:27 +00:00
const tablePropertyPathsToRemove = [
propertyPath , // primaryColumns.customColumn1.isVisible
` derivedColumns. ${ tableProperty } ` , // derivedColumns.customColumn1.isVisible
] ;
2024-09-18 16:35:28 +00:00
2022-05-05 06:38:27 +00:00
return _ . reject ( dynamicBindingPathList , ( { key } ) = >
tablePropertyPathsToRemove . includes ( key ) ,
) ;
} else {
return _ . reject ( dynamicBindingPathList , {
key : propertyPath ,
} ) ;
}
}
2023-07-26 05:38:11 +00:00
export function * handleUpdateWidgetDynamicProperty (
widget : WidgetProps ,
update : BatchUpdateDynamicPropertyUpdates ,
2020-02-26 12:44:56 +00:00
) {
2022-05-04 09:45:57 +00:00
const {
isDynamic ,
propertyPath ,
shouldRejectDynamicBindingPathList = true ,
2023-06-16 09:16:56 +00:00
skipValidation = false ,
2023-07-26 05:38:11 +00:00
} = update ;
2021-05-24 06:57:33 +00:00
2023-07-26 05:38:11 +00:00
const propertyValue = _ . get ( widget , propertyPath ) ;
2020-11-12 11:23:32 +00:00
let dynamicPropertyPathList = getWidgetDynamicPropertyPathList ( widget ) ;
2022-03-25 09:55:01 +00:00
let dynamicBindingPathList = getEntityDynamicBindingPathList ( widget ) ;
2023-07-26 05:38:11 +00:00
2020-02-26 12:44:56 +00:00
if ( isDynamic ) {
2021-03-09 05:30:57 +00:00
const keyExists =
dynamicPropertyPathList . findIndex ( ( path ) = > path . key === propertyPath ) >
- 1 ;
2024-09-18 16:35:28 +00:00
2021-03-09 05:30:57 +00:00
if ( ! keyExists ) {
dynamicPropertyPathList . push ( {
key : propertyPath ,
} ) ;
}
2024-09-18 16:35:28 +00:00
2021-03-09 05:30:57 +00:00
widget = set ( widget , propertyPath , convertToString ( propertyValue ) ) ;
2020-02-26 12:44:56 +00:00
} else {
2020-11-12 11:23:32 +00:00
dynamicPropertyPathList = _ . reject ( dynamicPropertyPathList , {
2021-01-25 08:57:26 +00:00
key : propertyPath ,
2020-11-12 11:23:32 +00:00
} ) ;
2022-05-04 09:45:57 +00:00
if ( shouldRejectDynamicBindingPathList ) {
2022-05-05 06:38:27 +00:00
dynamicBindingPathList = removeDynamicBindingProperties (
propertyPath ,
dynamicBindingPathList ,
) ;
2022-05-04 09:45:57 +00:00
}
2023-06-01 17:26:05 +00:00
2023-06-16 09:16:56 +00:00
/ *
* We need to run the validation function to parse the value present in the
* js mode to use that in the non js mode .
* - if the value is valid to be used on non js mode , we will use the same value
* - if the value is invalid to be used on non js mode , we will use the default value
* returned by the validation function .
*
* Sometimes ( eg : in one click binding control ) we don ' t want to do validation and retain the
* same value while switching from js to non js mode . use ` skipValidation ` flag to turn off validation .
* /
if ( ! skipValidation ) {
const { parsed } = yield call (
validateProperty ,
propertyPath ,
propertyValue ,
widget ,
) ;
2023-06-01 17:26:05 +00:00
2023-06-16 09:16:56 +00:00
widget = set ( widget , propertyPath , parsed ) ;
}
2020-02-26 12:44:56 +00:00
}
2024-09-18 16:35:28 +00:00
2021-03-09 05:30:57 +00:00
widget . dynamicPropertyPathList = dynamicPropertyPathList ;
2022-03-25 09:55:01 +00:00
widget . dynamicBindingPathList = dynamicBindingPathList ;
2024-09-18 16:35:28 +00:00
2023-07-26 05:38:11 +00:00
return widget ;
}
export function * batchUpdateWidgetDynamicPropertySaga (
action : ReduxAction < BatchUpdateWidgetDynamicPropertyPayload > ,
) {
const { updates , widgetId } = action . payload ;
const stateWidget : WidgetProps = yield select ( getWidget , widgetId ) ;
let widget = cloneDeep ( { . . . stateWidget } ) ;
for ( const update of updates ) {
widget = yield call ( handleUpdateWidgetDynamicProperty , widget , update ) ;
}
const stateWidgets : CanvasWidgetsReduxState = yield select ( getWidgets ) ;
const widgets = { . . . stateWidgets , [ widgetId ] : widget } ;
2024-09-18 16:35:28 +00:00
2023-07-26 05:38:11 +00:00
// Save the layout
yield put ( updateAndSaveLayout ( widgets ) ) ;
}
export function * setWidgetDynamicPropertySaga (
action : ReduxAction < SetWidgetDynamicPropertyPayload > ,
) {
const {
isDynamic ,
propertyPath ,
shouldRejectDynamicBindingPathList = true ,
skipValidation = false ,
widgetId ,
} = action . payload ;
const stateWidget : WidgetProps = yield select ( getWidget , widgetId ) ;
let widget = cloneDeep ( { . . . stateWidget } ) ;
const update = {
isDynamic ,
propertyPath ,
shouldRejectDynamicBindingPathList ,
skipValidation ,
} ;
widget = yield call ( handleUpdateWidgetDynamicProperty , widget , update ) ;
2023-09-15 15:53:51 +00:00
const propertyValue = get ( widget , propertyPath ) ;
2024-09-18 16:35:28 +00:00
2023-09-15 15:53:51 +00:00
if ( ! propertyValue && isDynamic ) {
feat: show slash command prompt on focus in property pane (#31920)
## Description
This PR adds builds on top of existing slash command menu feature. Today
when we enter JS mode for any of the properties and type in `/` command,
we are able to see the following menu.
<img width="290" alt="Screenshot 2024-03-21 at 1 31 26 PM"
src="https://github.com/appsmithorg/appsmith/assets/30018882/319d5be1-33f7-49e3-9b8d-fd8aa5fa9aed">
With the changes in this PR, now whenever we switch to JS mode for data
property of Table and JSON form widgets, if we focus on the input box,
we should be able to see the menu with options showing up as shown
below:
https://github.com/appsmithorg/appsmith/assets/30018882/30496f3c-615a-4a70-ba05-e0151c6bcdd5
**Limitations**:
- This feature is only implemented for Table and JSON form widgets data
property, based on mixpanel results we can further decide to include it
for all or not.
Fixes #31900
_or_
Fixes `Issue URL`
> [!WARNING]
> _If no issue exists, please create an issue first, and check with the
maintainers if the issue is valid._
## Automation
/ok-to-test tags="@tag.All"
### :mag: Cypress test results
<!-- This is an auto-generated comment: Cypress test results -->
> [!IMPORTANT]
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/8386142777>
> Commit: `90bea1f91b3d864c984548b11ec4d3a5bd70b3f7`
> Cypress dashboard url: <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=8386142777&attempt=2"
target="_blank">Click here!</a>
> All cypress tests have passed 🎉🎉🎉
<!-- end of auto-generated comment: Cypress test results -->
## Summary by CodeRabbit
- **New Features**
- Enhanced the code editor to dynamically show a slash command menu for
certain widgets, improving the user editing experience.
- Introduced logic to selectively enable slash command functionality for
table and JSON widgets, specifically for the `data` property.
- **Enhancements**
- Updated the code editor hint system to support additional context,
including datasource information and feature flags, enriching the
autocomplete and suggestion capabilities.
- **Bug Fixes**
- Fixed an issue where unnecessary bindings were applied to widgets,
ensuring cleaner and more relevant code generation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Enhanced `CodeEditor` with advanced properties to support diverse
functionalities including AI assistance and plugin integrations.
- Introduced a function to determine the visibility of the slash command
menu based on widget types.
- Added specific widget properties for table and JSON widgets to enable
focused functionalities.
- Implemented a new feature flag to toggle learnability and ease of
initial use improvements.
- **Tests**
- Added tests for Property Pane Suggestions functionality to ensure
reliable autocomplete features.
- **Chores**
- Improved `PropertyPane` interaction by adding a method for focusing
and asserting auto-save in text fields.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: “sneha122” <“sneha@appsmith.com”>
2024-03-22 09:44:08 +00:00
// Empty binding should not be set for table and json widgets' data property
// As these are getting populated with slash command menu on focus
if ( ! shouldShowSlashCommandMenu ( widget . type , propertyPath ) ) {
set ( widget , propertyPath , EMPTY_BINDING ) ;
}
2023-09-15 15:53:51 +00:00
}
2022-06-21 13:57:34 +00:00
const stateWidgets : CanvasWidgetsReduxState = yield select ( getWidgets ) ;
2021-01-25 08:57:26 +00:00
const widgets = { . . . stateWidgets , [ widgetId ] : widget } ;
// Save the layout
yield put ( updateAndSaveLayout ( widgets ) ) ;
}
2022-05-04 09:45:57 +00:00
export function getPropertiesToUpdate (
2021-03-04 18:35:41 +00:00
widget : WidgetProps ,
2021-03-01 09:45:54 +00:00
updates : Record < string , unknown > ,
2021-04-23 05:43:13 +00:00
triggerPaths? : string [ ] ,
2021-03-04 18:35:41 +00:00
) : {
propertyUpdates : Record < string , unknown > ;
dynamicTriggerPathList : DynamicPath [ ] ;
dynamicBindingPathList : DynamicPath [ ] ;
} {
2021-02-25 14:00:02 +00:00
// Create a
const widgetWithUpdates = _ . cloneDeep ( widget ) ;
2024-09-18 16:35:28 +00:00
2021-02-25 14:00:02 +00:00
Object . entries ( updates ) . forEach ( ( [ propertyPath , propertyValue ] ) = > {
set ( widgetWithUpdates , propertyPath , propertyValue ) ;
} ) ;
// get the flat list of all updates (in case values are objects)
const updatePaths = getAllPaths ( updates ) ;
const propertyUpdates : Record < string , unknown > = {
. . . updates ,
} ;
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
const currentDynamicTriggerPathList : DynamicPath [ ] =
getWidgetDynamicTriggerPathList ( widget ) ;
const currentDynamicBindingPathList : DynamicPath [ ] =
getEntityDynamicBindingPathList ( widget ) ;
2021-01-25 08:57:26 +00:00
const dynamicTriggerPathListUpdates : DynamicPathUpdate [ ] = [ ] ;
const dynamicBindingPathListUpdates : DynamicPathUpdate [ ] = [ ] ;
2021-02-25 14:00:02 +00:00
2023-11-14 04:33:37 +00:00
const widgetConfig = WidgetFactory . getWidgetPropertyPaneConfig (
widget . type ,
widget ,
) ;
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
const { triggerPaths : triggerPathsFromPropertyConfig = { } } =
getAllPathsFromPropertyConfig ( widgetWithUpdates , widgetConfig , { } ) ;
2021-10-27 12:58:34 +00:00
2021-02-25 14:00:02 +00:00
Object . keys ( updatePaths ) . forEach ( ( propertyPath ) = > {
2022-05-06 05:42:35 +00:00
const propertyValue = getValueFromTree ( updates , propertyPath ) ;
2024-09-18 16:35:28 +00:00
2021-02-25 14:00:02 +00:00
// only check if
if ( ! _ . isString ( propertyValue ) ) {
return ;
}
2021-01-25 08:57:26 +00:00
2021-10-27 12:58:34 +00:00
let isTriggerProperty = propertyPath in triggerPathsFromPropertyConfig ;
2021-02-16 10:29:08 +00:00
2021-04-23 05:43:13 +00:00
isTriggerProperty = doesTriggerPathsContainPropertyPath (
isTriggerProperty ,
propertyPath ,
triggerPaths ,
) ;
2021-01-25 08:57:26 +00:00
// If it is a trigger property, it will go in a different list than the general
// dynamicBindingPathList.
2021-02-25 14:00:02 +00:00
if ( isTriggerProperty ) {
2021-01-25 08:57:26 +00:00
dynamicTriggerPathListUpdates . push (
getDynamicTriggerPathListUpdate ( widget , propertyPath , propertyValue ) ,
) ;
} else {
dynamicBindingPathListUpdates . push (
getDynamicBindingPathListUpdate ( widget , propertyPath , propertyValue ) ,
) ;
}
} ) ;
2021-02-25 14:00:02 +00:00
const dynamicTriggerPathList = dynamicTriggerPathListUpdates . reduce (
2021-01-25 08:57:26 +00:00
applyDynamicPathUpdates ,
currentDynamicTriggerPathList ,
) ;
2021-02-25 14:00:02 +00:00
const dynamicBindingPathList = dynamicBindingPathListUpdates . reduce (
2021-01-25 08:57:26 +00:00
applyDynamicPathUpdates ,
currentDynamicBindingPathList ,
) ;
2021-03-01 09:45:54 +00:00
return {
propertyUpdates ,
dynamicTriggerPathList ,
dynamicBindingPathList ,
} ;
2021-01-25 08:57:26 +00:00
}
2022-11-23 09:48:23 +00:00
export function * getIsContainerLikeWidget ( widget : FlattenedWidgetProps ) {
const children = widget . children ;
2024-09-18 16:35:28 +00:00
2022-11-23 09:48:23 +00:00
if ( Array . isArray ( children ) && children . length > 0 ) {
const firstChild : FlattenedWidgetProps = yield select (
getWidget ,
children [ 0 ] ,
) ;
2024-09-18 16:35:28 +00:00
2022-11-23 09:48:23 +00:00
if ( firstChild . type === "CANVAS_WIDGET" ) return true ;
}
2024-09-18 16:35:28 +00:00
2022-11-23 09:48:23 +00:00
return false ;
}
2023-01-28 02:17:06 +00:00
2021-09-21 07:55:56 +00:00
export function * getPropertiesUpdatedWidget (
updatesObj : UpdateWidgetPropertyPayload ,
2021-01-25 08:57:26 +00:00
) {
2022-07-14 07:02:35 +00:00
const { dynamicUpdates , updates , widgetId } = updatesObj ;
2021-09-21 07:55:56 +00:00
2023-10-11 07:14:38 +00:00
const { modify = { } , postUpdateAction , remove = [ ] , triggerPaths } = updates ;
2021-01-25 08:57:26 +00:00
2021-03-01 09:45:54 +00:00
const stateWidget : WidgetProps = yield select ( getWidget , widgetId ) ;
2021-03-05 04:34:35 +00:00
// if there is no widget in the state, don't do anything
if ( ! stateWidget ) return ;
2021-03-01 09:45:54 +00:00
let widget = cloneDeep ( stateWidget ) ;
2024-09-18 16:35:28 +00:00
2021-03-01 09:45:54 +00:00
try {
if ( Object . keys ( modify ) . length > 0 ) {
const {
dynamicBindingPathList ,
2021-05-13 08:35:39 +00:00
dynamicTriggerPathList ,
propertyUpdates ,
2021-04-23 05:43:13 +00:00
} = getPropertiesToUpdate ( widget , modify , triggerPaths ) ;
2021-03-01 09:45:54 +00:00
// We loop over all updates
Object . entries ( propertyUpdates ) . forEach (
( [ propertyPath , propertyValue ] ) = > {
// since property paths could be nested, we use lodash set method
widget = set ( widget , propertyPath , propertyValue ) ;
} ,
) ;
2021-03-04 18:35:41 +00:00
widget . dynamicBindingPathList = dynamicBindingPathList ;
widget . dynamicTriggerPathList = dynamicTriggerPathList ;
2022-07-14 07:02:35 +00:00
if ( dynamicUpdates ? . dynamicPropertyPathList ? . length ) {
widget . dynamicPropertyPathList = mergeDynamicPropertyPaths (
widget . dynamicPropertyPathList ,
dynamicUpdates . dynamicPropertyPathList ,
) ;
}
2021-03-01 09:45:54 +00:00
}
} catch ( e ) {
log . debug ( "Error updating property paths: " , { e } ) ;
}
2021-01-25 08:57:26 +00:00
2021-03-01 09:45:54 +00:00
if ( Array . isArray ( remove ) && remove . length > 0 ) {
widget = yield removeWidgetProperties ( widget , remove ) ;
}
2022-02-02 09:25:16 +00:00
// Note: This may not be the best place to do this.
// If there exists another spot in this workflow, where we are iterating over the dynamicTriggerPathList and dynamicBindingPathList, after
// performing all updates to the widget, we can piggy back on that iteration to purge orphaned paths
// I couldn't find it, so here it is.
2022-11-23 09:48:23 +00:00
return {
updatedWidget : purgeOrphanedDynamicPaths ( widget ) ,
actionToDispatch : postUpdateAction ,
} ;
2021-09-21 07:55:56 +00:00
}
2021-01-25 08:57:26 +00:00
2021-09-21 07:55:56 +00:00
function * batchUpdateWidgetPropertySaga (
action : ReduxAction < UpdateWidgetPropertyPayload > ,
) {
const start = performance . now ( ) ;
const { shouldReplay , widgetId } = action . payload ;
2024-09-18 16:35:28 +00:00
2021-09-21 07:55:56 +00:00
if ( ! widgetId ) {
// Handling the case where sometimes widget id is not passed through here
return ;
}
2024-09-18 16:35:28 +00:00
2022-11-23 09:48:23 +00:00
const updatedWidgetAndActionsToDispatch : {
updatedWidget : WidgetProps ;
actionToDispatch? : ReduxActionType ;
} = yield call ( getPropertiesUpdatedWidget , action . payload ) ;
2022-06-21 13:57:34 +00:00
const stateWidgets : CanvasWidgetsReduxState = yield select ( getWidgets ) ;
2022-11-23 09:48:23 +00:00
const widgets = {
. . . stateWidgets ,
[ widgetId ] : updatedWidgetAndActionsToDispatch . updatedWidget ,
} ;
2024-09-18 16:35:28 +00:00
2021-03-01 09:45:54 +00:00
log . debug (
"Batch widget property update calculations took: " ,
2023-02-14 16:07:31 +00:00
action ,
2021-03-01 09:45:54 +00:00
performance . now ( ) - start ,
"ms" ,
) ;
2021-01-25 08:57:26 +00:00
// Save the layout
2022-08-19 10:10:36 +00:00
yield put ( updateAndSaveLayout ( widgets , { shouldReplay } ) ) ;
2024-09-18 16:35:28 +00:00
2022-11-23 09:48:23 +00:00
if ( updatedWidgetAndActionsToDispatch . actionToDispatch ) {
yield put ( {
type : updatedWidgetAndActionsToDispatch . actionToDispatch ,
payload : { widgetId } ,
} ) ;
}
2021-09-21 07:55:56 +00:00
}
function * batchUpdateMultipleWidgetsPropertiesSaga (
action : ReduxAction < { updatesArray : UpdateWidgetPropertyPayload [ ] } > ,
) {
const start = performance . now ( ) ;
const { updatesArray } = action . payload ;
2022-06-21 13:57:34 +00:00
const stateWidgets : CanvasWidgetsReduxState = yield select ( getWidgets ) ;
2022-11-23 09:48:23 +00:00
const updatedWidgetsAndActionsToDispatch : Array < {
updatedWidget : WidgetProps ;
actionToDispatch? : ReduxActionType ;
} > = yield all (
2021-09-21 07:55:56 +00:00
updatesArray . map ( ( eachUpdate ) = > {
return call ( getPropertiesUpdatedWidget , eachUpdate ) ;
} ) ,
) ;
2022-11-23 09:48:23 +00:00
const updatedStateWidgets = updatedWidgetsAndActionsToDispatch . reduce (
( allWidgets , eachUpdatedWidgetAndActionsToDispatch ) = > {
2021-09-21 07:55:56 +00:00
return {
. . . allWidgets ,
2022-11-23 09:48:23 +00:00
[ eachUpdatedWidgetAndActionsToDispatch . updatedWidget . widgetId ] :
eachUpdatedWidgetAndActionsToDispatch . updatedWidget ,
2021-09-21 07:55:56 +00:00
} ;
} ,
stateWidgets ,
) ;
2022-11-23 09:48:23 +00:00
const updatedWidgetIds = uniq (
updatedWidgetsAndActionsToDispatch . map (
( each ) = > each . updatedWidget . widgetId ,
) ,
) ;
2022-08-19 10:10:36 +00:00
2021-09-21 07:55:56 +00:00
log . debug (
"Batch multi-widget properties update calculations took: " ,
performance . now ( ) - start ,
"ms" ,
) ;
// Save the layout
2022-08-19 10:10:36 +00:00
yield put (
updateAndSaveLayout ( updatedStateWidgets , {
updatedWidgetIds ,
} ) ,
) ;
2024-09-18 16:35:28 +00:00
2022-11-23 09:48:23 +00:00
for ( const updatedWidgetAndActions of updatedWidgetsAndActionsToDispatch ) {
if ( updatedWidgetAndActions . actionToDispatch ) {
yield put ( {
type : updatedWidgetAndActions . actionToDispatch ,
payload : { widgetId : updatedWidgetAndActions.updatedWidget.widgetId } ,
} ) ;
}
}
2019-11-06 06:35:15 +00:00
}
2021-03-01 09:45:54 +00:00
function * removeWidgetProperties ( widget : WidgetProps , paths : string [ ] ) {
try {
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
let dynamicTriggerPathList : DynamicPath [ ] =
getWidgetDynamicTriggerPathList ( widget ) ;
let dynamicBindingPathList : DynamicPath [ ] =
getEntityDynamicBindingPathList ( widget ) ;
let dynamicPropertyPathList : DynamicPath [ ] =
getWidgetDynamicPropertyPathList ( widget ) ;
2021-03-01 09:45:54 +00:00
paths . forEach ( ( propertyPath ) = > {
dynamicTriggerPathList = dynamicTriggerPathList . filter ( ( dynamicPath ) = > {
return ! isChildPropertyPath ( propertyPath , dynamicPath . key ) ;
} ) ;
dynamicBindingPathList = dynamicBindingPathList . filter ( ( dynamicPath ) = > {
return ! isChildPropertyPath ( propertyPath , dynamicPath . key ) ;
} ) ;
2021-04-27 07:16:54 +00:00
dynamicPropertyPathList = dynamicPropertyPathList . filter (
( dynamicPath ) = > {
return ! isChildPropertyPath ( propertyPath , dynamicPath . key ) ;
} ,
) ;
2021-03-01 09:45:54 +00:00
} ) ;
widget . dynamicBindingPathList = dynamicBindingPathList ;
widget . dynamicTriggerPathList = dynamicTriggerPathList ;
2021-04-27 07:16:54 +00:00
widget . dynamicPropertyPathList = dynamicPropertyPathList ;
2021-03-01 09:45:54 +00:00
paths . forEach ( ( propertyPath ) = > {
widget = unsetPropertyPath ( widget , propertyPath ) as WidgetProps ;
} ) ;
} catch ( e ) {
log . debug ( "Error removing propertyPaths: " , { e } ) ;
}
return widget ;
}
function * deleteWidgetPropertySaga (
action : ReduxAction < DeleteWidgetPropertyPayload > ,
) {
2021-05-13 08:35:39 +00:00
const { propertyPaths , widgetId } = action . payload ;
2024-09-18 16:35:28 +00:00
2021-03-01 09:45:54 +00:00
if ( ! widgetId ) {
// Handling the case where sometimes widget id is not passed through here
return ;
}
yield put ( batchUpdateWidgetProperty ( widgetId , { remove : propertyPaths } ) ) ;
}
2021-02-16 10:29:08 +00:00
//TODO(abhinav): Move this to helpers and add tests
const unsetPropertyPath = ( obj : Record < string , unknown > , path : string ) = > {
const regex = /(.*)\[\d+\]$/ ;
2024-09-18 16:35:28 +00:00
2021-02-16 10:29:08 +00:00
if ( regex . test ( path ) ) {
const matches = path . match ( regex ) ;
2024-09-18 16:35:28 +00:00
2021-02-16 10:29:08 +00:00
if (
matches &&
Array . isArray ( matches ) &&
matches [ 1 ] &&
matches [ 1 ] . length > 0
) {
_ . unset ( obj , path ) ;
const arr = _ . get ( obj , matches [ 1 ] ) ;
2024-09-18 16:35:28 +00:00
2021-02-16 10:29:08 +00:00
if ( arr && Array . isArray ( arr ) ) {
_ . set ( obj , matches [ 1 ] , arr . filter ( Boolean ) ) ;
}
}
} else {
_ . unset ( obj , path ) ;
}
2024-09-18 16:35:28 +00:00
2021-02-16 10:29:08 +00:00
return obj ;
} ;
2020-03-06 09:45:21 +00:00
function * resetChildrenMetaSaga ( action : ReduxAction < { widgetId : string } > ) {
2022-06-25 05:30:54 +00:00
const { widgetId : parentWidgetId } = action . payload ;
2021-07-06 06:54:34 +00:00
const canvasWidgets : CanvasWidgetsReduxState = yield select ( getWidgets ) ;
2022-06-25 05:30:54 +00:00
const evaluatedDataTree : DataTree = yield select ( getDataTree ) ;
2023-03-20 11:04:02 +00:00
const configTree : ConfigTree = yield select ( getConfigTree ) ;
2022-12-15 08:23:09 +00:00
const widgetsMeta : MetaState = yield select ( getWidgetsMeta ) ;
const childrenList = getWidgetDescendantToReset (
2021-07-06 06:54:34 +00:00
canvasWidgets ,
parentWidgetId ,
2022-06-25 05:30:54 +00:00
evaluatedDataTree ,
2022-12-15 08:23:09 +00:00
widgetsMeta ,
2021-07-06 06:54:34 +00:00
) ;
2022-06-25 05:30:54 +00:00
for ( const childIndex in childrenList ) {
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
const { evaluatedWidget : childWidget , id : childId } =
childrenList [ childIndex ] ;
2023-03-20 11:04:02 +00:00
const evaluatedWidgetConfig =
childWidget && configTree [ childWidget ? . widgetName ] ;
2024-09-18 16:35:28 +00:00
2023-03-20 11:04:02 +00:00
yield put (
resetWidgetMetaProperty (
childId ,
childWidget ,
evaluatedWidgetConfig as WidgetEntityConfig ,
) ,
) ;
2020-03-06 09:45:21 +00:00
}
}
2020-03-27 09:02:11 +00:00
function * updateCanvasSize (
action : ReduxAction < { canvasWidgetId : string ; snapRows : number } > ,
) {
const { canvasWidgetId , snapRows } = action . payload ;
2022-11-23 09:48:23 +00:00
const canvasWidget : FlattenedWidgetProps = yield select (
getWidget ,
canvasWidgetId ,
) ;
2020-03-27 09:02:11 +00:00
const originalSnapRows = canvasWidget . bottomRow - canvasWidget . topRow ;
const newBottomRow = Math . round (
snapRows * GridDefaults . DEFAULT_GRID_ROW_HEIGHT ,
) ;
2024-09-18 16:35:28 +00:00
2020-03-27 09:02:11 +00:00
/* Update the canvas's rows, ONLY if it has changed since the last render */
if ( originalSnapRows !== newBottomRow ) {
// TODO(abhinav): This considers that the topRow will always be zero
// Check this out when non canvas widgets are updating snapRows
// erstwhile: Math.round((rows * props.snapRowSpace) / props.parentRowSpace),
2021-01-25 08:57:26 +00:00
yield put (
feat: Non auto height invisible widgets (#20118)
## Description
This PR adds another feature update we had planned for Auto Height
- [ ] For new applications, in View and Preview mode, any widget which
is invisible will let go of its space and collapse if it's either on the
main Canvas or a container-like widget which has Auto-height enabled.
- [ ] Widgets within a container-like Widget, say Tabs, that doesn't
have Auto-height enabled, will now let go of their space if they're
invisible.
- [ ] The experience in Edit mode has not changed.
TL;DR: In new applications, in the Preview and Published _AKA_ View
modes, if a widget is invisible and within an Auto-height-enabled
container like a Tab, a Modal, a Form, or the main Canvas, it will fully
collapse, allowing widgets below it to move up and take its space. This
changes the behavior today prior to the release of this PR for
Auto-height-enabled widgets.
Fixes #19983
Fixes #18681
2023-02-14 13:36:19 +00:00
updateMultipleWidgetPropertiesAction ( {
2022-11-23 09:48:23 +00:00
[ canvasWidgetId ] : [
{
propertyPath : "bottomRow" ,
propertyValue : newBottomRow ,
} ,
] ,
} ) ,
2021-01-25 08:57:26 +00:00
) ;
2020-03-27 09:02:11 +00:00
}
}
2023-04-07 13:51:35 +00:00
function * createSelectedWidgetsCopy (
selectedWidgets : FlattenedWidgetProps [ ] ,
flexLayers : FlexLayer [ ] ,
) {
2021-06-28 07:11:47 +00:00
if ( ! selectedWidgets || ! selectedWidgets . length ) return ;
2024-09-18 16:35:28 +00:00
2024-07-05 08:27:59 +00:00
const layoutSystemType : LayoutSystemTypes = yield select ( getLayoutSystemType ) ;
2021-06-28 07:11:47 +00:00
const widgetListsToStore : {
widgetId : string ;
parentId : string ;
list : FlattenedWidgetProps [ ] ;
2024-01-24 16:25:08 +00:00
hierarchy : number ;
2021-06-28 07:11:47 +00:00
} [ ] = yield all ( selectedWidgets . map ( ( each ) = > call ( createWidgetCopy , each ) ) ) ;
2021-08-25 05:00:31 +00:00
2022-06-21 13:57:34 +00:00
const saveResult : boolean = yield saveCopiedWidgets (
2023-04-07 13:51:35 +00:00
JSON . stringify ( {
2024-07-05 08:27:59 +00:00
layoutSystemType ,
2023-04-07 13:51:35 +00:00
widgets : widgetListsToStore ,
flexLayers ,
} ) ,
2022-06-21 13:57:34 +00:00
) ;
2024-09-18 16:35:28 +00:00
2022-06-21 13:57:34 +00:00
return saveResult ;
2020-12-10 07:33:43 +00:00
}
2021-04-23 05:43:13 +00:00
/ * *
* copy here actually means saving a JSON in local storage
* so when a user hits copy on a selected widget , we save widget in localStorage
*
* @param action
* @returns
* /
2020-12-10 07:33:43 +00:00
function * copyWidgetSaga ( action : ReduxAction < { isShortcut : boolean } > ) {
2023-10-11 07:14:38 +00:00
const allWidgets : { [ widgetId : string ] : FlattenedWidgetProps } =
yield select ( getWidgets ) ;
2021-06-28 07:11:47 +00:00
const selectedWidgets : string [ ] = yield select ( getSelectedWidgets ) ;
2024-09-18 16:35:28 +00:00
2021-06-28 07:11:47 +00:00
if ( ! selectedWidgets ) {
2023-05-19 18:37:06 +00:00
toast . show ( createMessage ( ERROR_WIDGET_COPY_NO_WIDGET_SELECTED ) , {
kind : "info" ,
2020-12-10 07:33:43 +00:00
} ) ;
2024-09-18 16:35:28 +00:00
2020-12-10 07:33:43 +00:00
return ;
}
2021-06-28 07:11:47 +00:00
const allAllowedToCopy = selectedWidgets . some ( ( each ) = > {
2023-06-13 12:09:40 +00:00
//should not allow canvas widgets to be copied
return (
allWidgets [ each ] &&
! allWidgets [ each ] . disallowCopy &&
allWidgets [ each ] . type !== "CANVAS_WIDGET"
) ;
2021-06-28 07:11:47 +00:00
} ) ;
if ( ! allAllowedToCopy ) {
2023-05-19 18:37:06 +00:00
toast . show ( createMessage ( ERROR_WIDGET_COPY_NOT_ALLOWED ) , {
kind : "info" ,
2021-04-23 05:43:13 +00:00
} ) ;
return ;
}
2024-09-18 16:35:28 +00:00
2021-06-28 07:11:47 +00:00
const selectedWidgetProps = selectedWidgets . map ( ( each ) = > allWidgets [ each ] ) ;
2021-04-23 05:43:13 +00:00
2023-04-07 13:51:35 +00:00
const canvasId = selectedWidgetProps ? . [ 0 ] ? . parentId || "" ;
const flexLayers : FlexLayer [ ] = getFlexLayersForSelectedWidgets (
selectedWidgets ,
canvasId ? allWidgets [ canvasId ] : undefined ,
) ;
2022-06-21 13:57:34 +00:00
const saveResult : boolean = yield createSelectedWidgetsCopy (
selectedWidgetProps ,
2023-04-07 13:51:35 +00:00
flexLayers ,
2022-06-21 13:57:34 +00:00
) ;
2020-12-10 07:33:43 +00:00
2021-06-28 07:11:47 +00:00
selectedWidgetProps . forEach ( ( each ) = > {
const eventName = action . payload . isShortcut
? "WIDGET_COPY_VIA_SHORTCUT"
: "WIDGET_COPY" ;
2024-09-18 16:35:28 +00:00
2021-06-28 07:11:47 +00:00
AnalyticsUtil . logEvent ( eventName , {
widgetName : each.widgetName ,
widgetType : each.type ,
} ) ;
2020-09-16 10:28:01 +00:00
} ) ;
2020-12-10 07:33:43 +00:00
2020-09-16 10:28:01 +00:00
if ( saveResult ) {
2023-05-19 18:37:06 +00:00
toast . show (
createMessage (
2021-06-28 07:11:47 +00:00
WIDGET_COPY ,
selectedWidgetProps . length > 1
? ` ${ selectedWidgetProps . length } Widgets `
: selectedWidgetProps [ 0 ] . widgetName ,
) ,
2023-05-19 18:37:06 +00:00
{
kind : "success" ,
} ,
) ;
2020-09-16 10:28:01 +00:00
}
}
2021-08-25 05:00:31 +00:00
/ * *
* We take the bottom most widget in the canvas , then calculate the top , left , right , bottom
* co - ordinates for the new widget , such that it can be placed at the bottom of the canvas .
*
* @param widget
* @param parentId
* @param canvasWidgets
* @param parentBottomRow
2022-05-04 07:58:57 +00:00
* @param newPastingPositionMap
* @param shouldPersistColumnPosition
* @param isThereACollision
* @param shouldGroup
2021-08-25 05:00:31 +00:00
* @returns
* /
2021-02-11 06:36:07 +00:00
export function calculateNewWidgetPosition (
2020-09-16 10:28:01 +00:00
widget : WidgetProps ,
parentId : string ,
2021-03-16 05:01:37 +00:00
canvasWidgets : { [ widgetId : string ] : FlattenedWidgetProps } ,
2021-06-28 07:11:47 +00:00
parentBottomRow? : number ,
2022-05-04 07:58:57 +00:00
newPastingPositionMap? : SpaceMap ,
2021-08-25 05:00:31 +00:00
shouldPersistColumnPosition = false ,
isThereACollision = false ,
shouldGroup = false ,
) : {
topRow : number ;
bottomRow : number ;
leftColumn : number ;
rightColumn : number ;
} {
2022-05-04 07:58:57 +00:00
if (
! shouldGroup &&
newPastingPositionMap &&
newPastingPositionMap [ widget . widgetId ]
) {
const newPastingPosition = newPastingPositionMap [ widget . widgetId ] ;
2024-09-18 16:35:28 +00:00
2022-05-04 07:58:57 +00:00
return {
topRow : newPastingPosition.top ,
bottomRow : newPastingPosition.bottom ,
leftColumn : newPastingPosition.left ,
rightColumn : newPastingPosition.right ,
} ;
}
2021-06-28 07:11:47 +00:00
const nextAvailableRow = parentBottomRow
? parentBottomRow
: nextAvailableRowInContainer ( parentId , canvasWidgets ) ;
2024-09-18 16:35:28 +00:00
2020-09-16 10:28:01 +00:00
return {
2021-08-25 05:00:31 +00:00
leftColumn : shouldPersistColumnPosition ? widget.leftColumn : 0 ,
rightColumn : shouldPersistColumnPosition
2021-06-28 07:11:47 +00:00
? widget . rightColumn
: widget . rightColumn - widget . leftColumn ,
2021-08-25 05:00:31 +00:00
topRow :
! isThereACollision && shouldGroup
? widget . topRow
: parentBottomRow
2024-04-22 09:17:28 +00:00
? nextAvailableRow + widget . topRow
: nextAvailableRow ,
2021-08-25 05:00:31 +00:00
bottomRow :
! isThereACollision && shouldGroup
? widget . bottomRow
: parentBottomRow
2024-04-22 09:17:28 +00:00
? nextAvailableRow + widget . bottomRow
: nextAvailableRow + ( widget . bottomRow - widget . topRow ) ,
2020-09-16 10:28:01 +00:00
} ;
}
2021-04-23 05:43:13 +00:00
/ * *
2024-04-29 10:13:39 +00:00
* This saga create a new widget from the copied one to store .
* It allows using both mouseLocation or gridPosition to locate where the copied widgets should be dropped .
* If gridPosition is available , use it , else , calculate gridPosition from mousePosition
2021-04-23 05:43:13 +00:00
* /
2024-04-29 10:13:39 +00:00
function * pasteWidgetSaga ( action : ReduxAction < PasteWidgetReduxAction > ) {
2023-04-07 13:51:35 +00:00
const {
flexLayers ,
widgets : copiedWidgets ,
} : {
widgets : CopiedWidgetGroup [ ] ;
flexLayers : FlexLayer [ ] ;
} = yield getCopiedWidgets ( ) ;
let copiedWidgetGroups = copiedWidgets ? [ . . . copiedWidgets ] : [ ] ;
2021-08-25 05:00:31 +00:00
const shouldGroup : boolean = action . payload . groupWidgets ;
2021-07-06 06:54:34 +00:00
2021-08-25 05:00:31 +00:00
const newlyCreatedWidgetIds : string [ ] = [ ] ;
const evalTree : DataTree = yield select ( getDataTree ) ;
const canvasWidgets : CanvasWidgetsReduxState = yield select ( getWidgets ) ;
let widgets : CanvasWidgetsReduxState = canvasWidgets ;
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
const selectedWidget : FlattenedWidgetProps < undefined > =
yield getSelectedWidgetWhenPasting ( ) ;
2021-07-08 06:30:19 +00:00
2023-04-07 13:51:35 +00:00
const isMobile : boolean = yield select ( getIsAutoLayoutMobileBreakPoint ) ;
const mainCanvasWidth : number = yield select ( getCanvasWidth ) ;
2023-03-04 07:25:54 +00:00
2023-02-21 04:13:25 +00:00
try {
let reflowedMovementMap ,
gridProps : GridProps | undefined ,
newPastingPositionMap : SpaceMap | undefined ,
canvasId ;
2022-05-20 13:57:12 +00:00
2023-02-21 04:13:25 +00:00
let pastingIntoWidgetId : string = yield getParentWidgetIdForPasting (
canvasWidgets ,
selectedWidget ,
) ;
2021-07-06 06:54:34 +00:00
2023-02-21 04:13:25 +00:00
let isThereACollision = false ;
2021-07-06 06:54:34 +00:00
2023-02-21 04:13:25 +00:00
// if this is true, selected widgets will be grouped in container
if ( shouldGroup ) {
copiedWidgetGroups = yield createSelectedWidgetsAsCopiedWidgets ( ) ;
pastingIntoWidgetId = yield getParentWidgetIdForGrouping (
widgets ,
copiedWidgetGroups ,
) ;
widgets = yield filterOutSelectedWidgets (
copiedWidgetGroups [ 0 ] . parentId ,
copiedWidgetGroups ,
) ;
isThereACollision = yield isSelectedWidgetsColliding (
widgets ,
copiedWidgetGroups ,
pastingIntoWidgetId ,
) ;
2020-09-16 10:28:01 +00:00
2023-02-21 04:13:25 +00:00
//while grouping, the container around the selected widgets will increase by 2 rows,
//hence if there are any widgets in that path then we reflow those widgets
// If there are already widgets inside the selection box even before grouping
//then we will have to move it down to the bottom most row
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
( { copiedWidgetGroups , gridProps , reflowedMovementMap } =
yield groupWidgetsIntoContainer (
copiedWidgetGroups ,
pastingIntoWidgetId ,
isThereACollision ,
) ) ;
2023-02-21 04:13:25 +00:00
}
2021-08-25 05:00:31 +00:00
2023-02-21 04:13:25 +00:00
if (
// to avoid invoking old way of copied widgets implementaion
! Array . isArray ( copiedWidgetGroups ) ||
! copiedWidgetGroups . length
)
return ;
2021-08-25 05:00:31 +00:00
2023-02-21 04:13:25 +00:00
const {
leftMostWidget ,
topMostWidget ,
totalWidth : copiedTotalWidth ,
} = getBoundaryWidgetsFromCopiedGroups ( copiedWidgetGroups ) ;
2021-08-25 05:00:31 +00:00
2023-02-21 04:13:25 +00:00
const nextAvailableRow : number = nextAvailableRowInContainer (
pastingIntoWidgetId ,
widgets ,
) ;
2022-05-04 07:58:57 +00:00
2023-02-21 04:13:25 +00:00
// skip new position calculation if grouping
if ( ! shouldGroup ) {
// new pasting positions, the variables are undefined if the positions cannot be calculated,
// then it pastes the regular way at the bottom of the canvas
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
( { canvasId , gridProps , newPastingPositionMap , reflowedMovementMap } =
yield call (
getNewPositions ,
copiedWidgetGroups ,
copiedTotalWidth ,
topMostWidget . topRow ,
leftMostWidget . leftColumn ,
2024-04-29 10:13:39 +00:00
action . payload ,
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
) ) ;
2023-02-21 04:13:25 +00:00
if ( canvasId ) pastingIntoWidgetId = canvasId ;
}
2022-05-04 07:58:57 +00:00
2023-02-21 04:13:25 +00:00
for ( const widgetGroup of copiedWidgetGroups ) {
//This is required when you cut the widget as CanvasWidgetState doesn't have the widget anymore
const widgetType = widgetGroup . list . find (
( widget ) = > widget . widgetId === widgetGroup . widgetId ,
) ? . type ;
2021-02-01 14:32:43 +00:00
2023-02-21 04:13:25 +00:00
if ( ! widgetType ) break ;
yield call (
executeWidgetBlueprintBeforeOperations ,
BlueprintOperationTypes . BEFORE_PASTE ,
{
parentId : pastingIntoWidgetId ,
widgetId : widgetGroup.widgetId ,
2021-08-25 05:00:31 +00:00
widgets ,
2023-02-21 04:13:25 +00:00
widgetType ,
} ,
) ;
}
2020-09-16 10:28:01 +00:00
2023-04-07 13:51:35 +00:00
const widgetIdMap : Record < string , string > = { } ;
2023-05-11 04:45:14 +00:00
const reverseWidgetIdMap : Record < string , string > = { } ;
2024-05-09 04:29:12 +00:00
const widgetNameMap : Record < string , string > = { } ;
2023-02-21 04:13:25 +00:00
yield all (
copiedWidgetGroups . map ( ( copiedWidgets ) = >
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
call ( function * ( ) {
2023-02-21 04:13:25 +00:00
// Don't try to paste if there is no copied widget
if ( ! copiedWidgets ) return ;
const copiedWidgetId = copiedWidgets . widgetId ;
const unUpdatedCopyOfWidget = copiedWidgets . list [ 0 ] ;
const newTopRow = shouldGroup
? isThereACollision
? topMostWidget . topRow
: 0
: topMostWidget . topRow ;
const copiedWidget = {
. . . unUpdatedCopyOfWidget ,
topRow : unUpdatedCopyOfWidget.topRow - newTopRow ,
bottomRow : unUpdatedCopyOfWidget.bottomRow - newTopRow ,
} ;
// Log the paste or group event.
if ( shouldGroup ) {
AnalyticsUtil . logEvent ( "WIDGET_GROUP" , {
widgetName : copiedWidget.widgetName ,
widgetType : copiedWidget.type ,
2021-08-25 05:00:31 +00:00
} ) ;
2023-02-21 04:13:25 +00:00
} else {
AnalyticsUtil . logEvent ( "WIDGET_PASTE" , {
widgetName : copiedWidget.widgetName ,
widgetType : copiedWidget.type ,
2021-08-25 05:00:31 +00:00
} ) ;
}
2023-02-21 04:13:25 +00:00
// Compute the new widget's positional properties
const newWidgetPosition = calculateNewWidgetPosition (
copiedWidget ,
pastingIntoWidgetId ,
widgets ,
nextAvailableRow ,
newPastingPositionMap ,
true ,
isThereACollision ,
shouldGroup ,
) ;
// Get a flat list of all the widgets to be updated
const widgetList = copiedWidgets . list ;
const newWidgetList : FlattenedWidgetProps [ ] = [ ] ;
// Generate new widgetIds for the flat list of all the widgets to be updated
widgetList . forEach ( ( widget ) = > {
// Create a copy of the widget properties
const newWidget = cloneDeep ( widget ) ;
2024-09-18 16:35:28 +00:00
2023-02-21 04:13:25 +00:00
newWidget . widgetId = generateReactKey ( ) ;
// Add the new widget id so that it maps the previous widget id
widgetIdMap [ widget . widgetId ] = newWidget . widgetId ;
2023-03-04 07:25:54 +00:00
reverseWidgetIdMap [ newWidget . widgetId ] = widget . widgetId ;
2023-02-21 04:13:25 +00:00
// Add the new widget to the list
newWidgetList . push ( newWidget ) ;
} ) ;
// For each of the new widgets generated
for ( let i = 0 ; i < newWidgetList . length ; i ++ ) {
const widget = newWidgetList [ i ] ;
const oldWidgetName = widget . widgetName ;
let newWidgetName = oldWidgetName ;
if ( ! shouldGroup ) {
newWidgetName = getNextWidgetName (
widgets ,
widget . type ,
evalTree ,
{
prefix : oldWidgetName ,
startWithoutIndex : true ,
} ,
) ;
}
// Update the children widgetIds if it has children
if ( widget . children && widget . children . length > 0 ) {
widget . children . forEach (
( childWidgetId : string , index : number ) = > {
if ( widget . children ) {
widget . children [ index ] = widgetIdMap [ childWidgetId ] ;
}
} ,
) ;
}
// Update the tabs for the tabs widget.
if ( widget . tabsObj && widget . type === "TABS_WIDGET" ) {
try {
const tabs = Object . values ( widget . tabsObj ) ;
2024-09-18 16:35:28 +00:00
2023-02-21 04:13:25 +00:00
if ( Array . isArray ( tabs ) ) {
2024-07-31 15:41:28 +00:00
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2024-05-09 04:29:12 +00:00
widget . tabsObj = tabs . reduce ( ( obj : any , tab : any ) = > {
2023-02-21 04:13:25 +00:00
tab . widgetId = widgetIdMap [ tab . widgetId ] ;
obj [ tab . id ] = tab ;
2024-09-18 16:35:28 +00:00
2023-02-21 04:13:25 +00:00
return obj ;
} , { } ) ;
}
} catch ( error ) {
log . debug ( "Error updating tabs" , error ) ;
2021-06-28 07:11:47 +00:00
}
}
2020-10-19 08:56:04 +00:00
2023-02-21 04:13:25 +00:00
// Update the table widget column properties
if (
widget . type === "TABLE_WIDGET_V2" ||
widget . type === "TABLE_WIDGET"
) {
try {
// If the primaryColumns of the table exist
if ( widget . primaryColumns ) {
// For each column
for ( const [ columnId , column ] of Object . entries (
widget . primaryColumns ,
2021-06-28 07:11:47 +00:00
) ) {
2023-02-21 04:13:25 +00:00
// For each property in the column
for ( const [ key , value ] of Object . entries (
column as ColumnProperties ,
) ) {
// Replace reference of previous widget with the new widgetName
// This handles binding scenarios like `{{Table2.tableData.map((currentRow) => (currentRow.id))}}`
widget . primaryColumns [ columnId ] [ key ] = isString ( value )
? value . replace (
` ${ oldWidgetName } . ` ,
` ${ newWidgetName } . ` ,
)
: value ;
}
2021-06-28 07:11:47 +00:00
}
}
2024-09-18 16:35:28 +00:00
2023-02-21 04:13:25 +00:00
// Use the new widget name we used to replace the column properties above.
widget . widgetName = newWidgetName ;
} catch ( error ) {
log . debug ( "Error updating table widget properties" , error ) ;
2021-06-28 07:11:47 +00:00
}
}
2020-10-19 08:56:04 +00:00
2023-02-21 04:13:25 +00:00
// TODO: here to move this to the widget definition
// Update the Select widget defaultValue properties
if (
widget . type === "MULTI_SELECT_WIDGET_V2" ||
widget . type === "SELECT_WIDGET"
) {
try {
// If the defaultOptionValue exist
if ( widget . defaultOptionValue ) {
const value = widget . defaultOptionValue ;
2024-09-18 16:35:28 +00:00
2023-02-21 04:13:25 +00:00
// replace All occurrence of old widget name
widget . defaultOptionValue = isString ( value )
? value . replaceAll ( ` ${ oldWidgetName } . ` , ` ${ newWidgetName } . ` )
: value ;
}
2024-09-18 16:35:28 +00:00
2023-02-21 04:13:25 +00:00
// Use the new widget name we used to replace the defaultValue properties above.
widget . widgetName = newWidgetName ;
} catch ( error ) {
log . debug ( "Error updating widget properties" , error ) ;
2022-08-18 07:10:58 +00:00
}
}
2024-09-18 16:35:28 +00:00
2023-02-21 04:13:25 +00:00
// If it is the copied widget, update position properties
if ( widget . widgetId === widgetIdMap [ copiedWidget . widgetId ] ) {
//when the widget is a modal widget, it has to paste on the main container
const pastingParentId =
widget . type === "MODAL_WIDGET"
? MAIN_CONTAINER_WIDGET_ID
: pastingIntoWidgetId ;
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
const { bottomRow , leftColumn , rightColumn , topRow } =
newWidgetPosition ;
2024-09-18 16:35:28 +00:00
2023-02-21 04:13:25 +00:00
widget . leftColumn = leftColumn ;
widget . topRow = topRow ;
widget . bottomRow = bottomRow ;
widget . rightColumn = rightColumn ;
widget . parentId = pastingParentId ;
// Also, update the parent widget in the canvas widgets
// to include this new copied widget's id in the parent's children
let parentChildren = [ widget . widgetId ] ;
const widgetChildren = widgets [ pastingParentId ] . children ;
2023-03-04 07:25:54 +00:00
2023-02-21 04:13:25 +00:00
if ( widgetChildren && Array . isArray ( widgetChildren ) ) {
2023-03-04 07:25:54 +00:00
// Add the new child to existing children after it's original siblings position.
const originalWidgetId : string = widgetList [ i ] . widgetId ;
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
const originalWidgetIndex : number =
widgetChildren . indexOf ( originalWidgetId ) ;
2024-09-18 16:35:28 +00:00
2023-03-04 07:25:54 +00:00
parentChildren = [
. . . widgetChildren . slice ( 0 , originalWidgetIndex + 1 ) ,
. . . parentChildren ,
. . . widgetChildren . slice ( originalWidgetIndex + 1 ) ,
] ;
2023-02-21 04:13:25 +00:00
}
2021-08-12 05:45:38 +00:00
2023-02-21 04:13:25 +00:00
widgets = {
. . . widgets ,
[ pastingParentId ] : {
. . . widgets [ pastingParentId ] ,
children : parentChildren ,
} ,
} ;
2024-09-18 16:35:28 +00:00
2023-02-21 04:13:25 +00:00
// If the copied widget's boundaries exceed the parent's
// Make the parent scrollable
2021-06-28 07:11:47 +00:00
if (
2023-02-21 04:13:25 +00:00
widgets [ pastingParentId ] . bottomRow *
widgets [ widget . parentId ] . parentRowSpace <=
widget . bottomRow * widget . parentRowSpace &&
! widget . detachFromLayout
2021-06-28 07:11:47 +00:00
) {
2023-02-21 04:13:25 +00:00
const parentOfPastingWidget = widgets [ pastingParentId ] . parentId ;
2024-09-18 16:35:28 +00:00
2023-02-21 04:13:25 +00:00
if (
parentOfPastingWidget &&
widget . parentId !== MAIN_CONTAINER_WIDGET_ID
) {
const parent = widgets [ parentOfPastingWidget ] ;
2024-09-18 16:35:28 +00:00
2023-02-21 04:13:25 +00:00
widgets [ parentOfPastingWidget ] = {
. . . parent ,
shouldScrollContents : true ,
} ;
}
2021-02-16 10:29:08 +00:00
}
2023-02-21 04:13:25 +00:00
} else {
// For all other widgets in the list
// (These widgets will be descendants of the copied widget)
// This means, that their parents will also be newly copied widgets
// Update widget's parent widget ids with the new parent widget ids
const newParentId = newWidgetList . find ( ( newWidget ) = >
widget . parentId
? newWidget . widgetId === widgetIdMap [ widget . parentId ]
: false ,
) ? . widgetId ;
2024-09-18 16:35:28 +00:00
2023-02-21 04:13:25 +00:00
if ( newParentId ) widget . parentId = newParentId ;
2021-02-16 10:29:08 +00:00
}
2024-09-18 16:35:28 +00:00
2023-02-21 04:13:25 +00:00
// Generate a new unique widget name
if ( ! shouldGroup ) {
widget . widgetName = newWidgetName ;
}
widgetNameMap [ oldWidgetName ] = widget . widgetName ;
// Add the new widget to the canvas widgets
widgets [ widget . widgetId ] = widget ;
2023-03-04 07:25:54 +00:00
/ * *
* If new parent is a vertical stack , then update flex layers .
* /
if ( widget . parentId ) {
const pastingIntoWidget = widgets [ widget . parentId ] ;
2024-09-18 16:35:28 +00:00
2023-04-07 13:51:35 +00:00
if (
pastingIntoWidget &&
isStack ( widgets , pastingIntoWidget ) &&
( pastingIntoWidgetId !== pastingIntoWidget . widgetId ||
! flexLayers ||
flexLayers . length <= 0 )
) {
2024-07-31 15:41:28 +00:00
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2023-10-11 07:14:38 +00:00
const metaProps : Record < string , any > =
yield select ( getWidgetsMeta ) ;
2024-09-18 16:35:28 +00:00
2023-03-04 07:25:54 +00:00
if ( widget . widgetId === widgetIdMap [ copiedWidget . widgetId ] )
widgets = pasteWidgetInFlexLayers (
widgets ,
widget . parentId ,
widget ,
reverseWidgetIdMap [ widget . widgetId ] ,
isMobile ,
2023-04-07 13:51:35 +00:00
mainCanvasWidth ,
2023-05-11 04:45:14 +00:00
metaProps ,
2023-03-04 07:25:54 +00:00
) ;
else if ( widget . type !== "CANVAS_WIDGET" )
widgets = addChildToPastedFlexLayers (
widgets ,
widget ,
widgetIdMap ,
isMobile ,
2023-04-07 13:51:35 +00:00
mainCanvasWidth ,
2023-05-11 04:45:14 +00:00
metaProps ,
2023-03-04 07:25:54 +00:00
) ;
}
}
2021-08-25 05:00:31 +00:00
}
2024-09-18 16:35:28 +00:00
2023-02-21 04:13:25 +00:00
newlyCreatedWidgetIds . push ( widgetIdMap [ copiedWidgetId ] ) ;
2024-09-18 16:35:28 +00:00
2023-02-21 04:13:25 +00:00
// 1. updating template in the copied widget and deleting old template associations
// 2. updating dynamicBindingPathList in the copied grid widget
for ( let i = 0 ; i < newWidgetList . length ; i ++ ) {
const widget = newWidgetList [ i ] ;
widgets = handleSpecificCasesWhilePasting (
widget ,
widgets ,
widgetNameMap ,
newWidgetList ,
) ;
2024-06-25 06:13:00 +00:00
// Moved handleIfParentIsListWidgetWhilePasting out of handleSpecificCasesWhilePasting as it is a compound case meaning it checks for parent of current widget rather than the current one itself(which are handled in handleSpecificCasesWhilePasting)
widgets = handleIfParentIsListWidgetWhilePasting ( widget , widgets ) ;
2021-02-16 10:29:08 +00:00
}
2023-02-21 04:13:25 +00:00
} ) ,
) ,
) ;
2024-05-09 04:29:12 +00:00
2023-02-21 04:13:25 +00:00
//calculate the new positions of the reflowed widgets
2023-05-11 04:45:14 +00:00
let reflowedWidgets = getReflowedPositions (
2023-02-21 04:13:25 +00:00
widgets ,
gridProps ,
reflowedMovementMap ,
) ;
2021-08-25 05:00:31 +00:00
2023-04-07 13:51:35 +00:00
if (
pastingIntoWidgetId &&
reflowedWidgets [ pastingIntoWidgetId ] &&
flexLayers &&
flexLayers . length > 0
) {
const newFlexLayers = getNewFlexLayers ( flexLayers , widgetIdMap ) ;
2024-09-18 16:35:28 +00:00
2023-04-07 13:51:35 +00:00
reflowedWidgets [ pastingIntoWidgetId ] = {
. . . reflowedWidgets [ pastingIntoWidgetId ] ,
flexLayers : [
. . . ( reflowedWidgets [ pastingIntoWidgetId ] ? . flexLayers || [ ] ) ,
. . . newFlexLayers ,
] ,
} ;
2024-07-31 15:41:28 +00:00
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2023-05-11 04:45:14 +00:00
const metaProps : Record < string , any > = yield select ( getWidgetsMeta ) ;
2024-09-18 16:35:28 +00:00
2023-05-11 04:45:14 +00:00
reflowedWidgets = updateWidgetPositions (
reflowedWidgets ,
pastingIntoWidgetId ,
isMobile ,
mainCanvasWidth ,
false ,
metaProps ,
) ;
2023-04-07 13:51:35 +00:00
}
2023-02-21 04:13:25 +00:00
// some widgets need to update property of parent if the parent have CHILD_OPERATIONS
// so here we are traversing up the tree till we get to MAIN_CONTAINER_WIDGET_ID
// while traversing, if we find any widget which has CHILD_OPERATION, we will call the fn in it
const updatedWidgets : CanvasWidgetsReduxState = yield call (
traverseTreeAndExecuteBlueprintChildOperations ,
reflowedWidgets [ pastingIntoWidgetId ] ,
newlyCreatedWidgetIds . filter (
( widgetId ) = > ! reflowedWidgets [ widgetId ] ? . detachFromLayout ,
) ,
reflowedWidgets ,
) ;
2024-09-18 16:35:28 +00:00
2023-12-29 02:41:49 +00:00
yield call ( updateAndSaveAnvilLayout , updatedWidgets ) ;
2022-05-04 07:58:57 +00:00
2024-07-31 02:54:51 +00:00
const basePageId : string = yield select ( getCurrentBasePageId ) ;
2021-08-25 05:00:31 +00:00
2023-02-21 04:13:25 +00:00
if ( copiedWidgetGroups && copiedWidgetGroups . length > 0 ) {
2024-07-31 02:54:51 +00:00
history . push ( builderURL ( { basePageId } ) ) ;
2023-02-21 04:13:25 +00:00
}
2022-07-11 04:06:29 +00:00
2023-02-21 04:13:25 +00:00
yield put ( {
type : ReduxActionTypes . RECORD_RECENTLY_ADDED_WIDGET ,
payload : newlyCreatedWidgetIds ,
} ) ;
yield put ( generateAutoHeightLayoutTreeAction ( true , true ) ) ;
2022-06-09 11:47:47 +00:00
2023-02-21 04:13:25 +00:00
//if pasting at the bottom of the canvas, then flash it.
if ( shouldGroup || ! newPastingPositionMap ) {
flashElementsById ( newlyCreatedWidgetIds , 100 ) ;
}
2022-05-25 10:05:53 +00:00
2023-02-21 04:13:25 +00:00
yield put (
selectWidgetInitAction (
SelectionRequestType . Multiple ,
newlyCreatedWidgetIds ,
) ,
) ;
} catch ( error ) {
yield put ( {
type : ReduxActionErrorTypes . WIDGET_OPERATION_ERROR ,
payload : {
action : ReduxActionTypes.PASTE_COPIED_WIDGET_INIT ,
error ,
2024-09-05 05:36:43 +00:00
logToDebugger : true ,
2023-02-21 04:13:25 +00:00
} ,
} ) ;
2022-05-04 07:58:57 +00:00
}
2020-09-16 10:28:01 +00:00
}
function * cutWidgetSaga() {
2023-10-11 07:14:38 +00:00
const allWidgets : { [ widgetId : string ] : FlattenedWidgetProps } =
yield select ( getWidgets ) ;
2021-06-28 07:11:47 +00:00
const selectedWidgets : string [ ] = yield select ( getSelectedWidgets ) ;
2024-09-18 16:35:28 +00:00
2021-06-28 07:11:47 +00:00
if ( ! selectedWidgets ) {
2023-05-19 18:37:06 +00:00
toast . show ( createMessage ( ERROR_WIDGET_CUT_NO_WIDGET_SELECTED ) , {
kind : "info" ,
2020-12-10 07:33:43 +00:00
} ) ;
2024-09-18 16:35:28 +00:00
2020-12-10 07:33:43 +00:00
return ;
}
2023-02-14 16:07:31 +00:00
const allAllowedToCut = selectedWidgets . some ( ( each ) = > {
2023-06-13 12:09:40 +00:00
//should not allow canvas widgets to be cut
return (
allWidgets [ each ] &&
! allWidgets [ each ] . disallowCopy &&
allWidgets [ each ] . type !== "CANVAS_WIDGET"
) ;
2023-02-14 16:07:31 +00:00
} ) ;
if ( ! allAllowedToCut ) {
2023-05-19 18:37:06 +00:00
toast . show ( createMessage ( ERROR_WIDGET_CUT_NOT_ALLOWED ) , {
kind : "info" ,
2023-02-14 16:07:31 +00:00
} ) ;
2024-09-18 16:35:28 +00:00
2023-02-14 16:07:31 +00:00
return ;
}
2021-06-28 07:11:47 +00:00
const selectedWidgetProps = selectedWidgets . map ( ( each ) = > allWidgets [ each ] ) ;
2023-04-07 13:51:35 +00:00
const canvasId = selectedWidgetProps ? . [ 0 ] ? . parentId || "" ;
const flexLayers : FlexLayer [ ] = getFlexLayersForSelectedWidgets (
selectedWidgets ,
canvasId ? allWidgets [ canvasId ] : undefined ,
) ;
2022-06-21 13:57:34 +00:00
const saveResult : boolean = yield createSelectedWidgetsCopy (
selectedWidgetProps ,
2023-04-07 13:51:35 +00:00
flexLayers ,
2022-06-21 13:57:34 +00:00
) ;
2020-12-10 07:33:43 +00:00
2021-06-28 07:11:47 +00:00
selectedWidgetProps . forEach ( ( each ) = > {
const eventName = "WIDGET_CUT_VIA_SHORTCUT" ; // cut only supported through a shortcut
2024-09-18 16:35:28 +00:00
2021-06-28 07:11:47 +00:00
AnalyticsUtil . logEvent ( eventName , {
widgetName : each.widgetName ,
widgetType : each.type ,
} ) ;
2020-09-16 10:28:01 +00:00
} ) ;
2020-12-10 07:33:43 +00:00
if ( saveResult ) {
2023-05-19 18:37:06 +00:00
toast . show (
createMessage (
2021-06-28 07:11:47 +00:00
WIDGET_CUT ,
selectedWidgetProps . length > 1
? ` ${ selectedWidgetProps . length } Widgets `
: selectedWidgetProps [ 0 ] . widgetName ,
) ,
2023-05-19 18:37:06 +00:00
{
kind : "success" ,
} ,
) ;
2020-12-10 07:33:43 +00:00
}
2020-09-16 10:28:01 +00:00
yield put ( {
2021-08-03 08:06:48 +00:00
type : WidgetReduxActionTypes . WIDGET_DELETE ,
2020-09-16 10:28:01 +00:00
payload : {
disallowUndo : true ,
2020-09-17 11:52:58 +00:00
isShortcut : true ,
2020-09-16 10:28:01 +00:00
} ,
} ) ;
}
2021-07-26 16:44:10 +00:00
function * addSuggestedWidget ( action : ReduxAction < Partial < WidgetProps > > ) {
2023-07-27 13:00:23 +00:00
const isSetWidgetIdForWalkthrough = ! ! (
action . payload . props . setWidgetIdForWalkthrough === "true"
) ;
2021-07-26 16:44:10 +00:00
const widgetConfig = action . payload ;
2024-09-18 16:35:28 +00:00
2023-07-27 13:00:23 +00:00
delete widgetConfig . props ? . setWidgetIdForWalkthrough ;
2020-10-15 07:21:00 +00:00
2021-07-26 16:44:10 +00:00
if ( ! widgetConfig . type ) return ;
2021-09-09 15:10:22 +00:00
const defaultConfig = WidgetFactory . widgetConfigMap . get ( widgetConfig . type ) ;
2022-06-21 13:57:34 +00:00
const evalTree : DataTree = yield select ( getDataTree ) ;
const widgets : CanvasWidgetsReduxState = yield select ( getWidgets ) ;
2021-07-26 16:44:10 +00:00
const widgetName = getNextWidgetName ( widgets , widgetConfig . type , evalTree ) ;
2023-11-15 12:31:54 +00:00
const layoutSystemType : LayoutSystemTypes = yield select ( getLayoutSystemType ) ;
2024-09-18 16:35:28 +00:00
2021-07-26 16:44:10 +00:00
try {
2020-10-15 07:21:00 +00:00
let newWidget = {
newWidgetId : generateReactKey ( ) ,
widgetId : "0" ,
2021-07-26 16:44:10 +00:00
parentId : "0" ,
2020-10-15 07:21:00 +00:00
renderMode : RenderModes.CANVAS ,
isLoading : false ,
2021-07-26 16:44:10 +00:00
. . . defaultConfig ,
widgetName ,
. . . widgetConfig ,
2020-10-15 07:21:00 +00:00
} ;
2021-07-26 16:44:10 +00:00
chore: upgrade to prettier v2 + enforce import types (#21013)Co-authored-by: Satish Gandham <hello@satishgandham.com> Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
## Description
This PR upgrades Prettier to v2 + enforces TypeScript’s [`import
type`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export)
syntax where applicable. It’s submitted as a separate PR so we can merge
it easily.
As a part of this PR, we reformat the codebase heavily:
- add `import type` everywhere where it’s required, and
- re-format the code to account for Prettier 2’s breaking changes:
https://prettier.io/blog/2020/03/21/2.0.0.html#breaking-changes
This PR is submitted against `release` to make sure all new code by team
members will adhere to new formatting standards, and we’ll have fewer
conflicts when merging `bundle-optimizations` into `release`. (I’ll
merge `release` back into `bundle-optimizations` once this PR is
merged.)
### Why is this needed?
This PR is needed because, for the Lodash optimization from
https://github.com/appsmithorg/appsmith/commit/7cbb12af886621256224be0c93e6a465dd710ad3,
we need to use `import type`. Otherwise, `babel-plugin-lodash` complains
that `LoDashStatic` is not a lodash function.
However, just using `import type` in the current codebase will give you
this:
<img width="962" alt="Screenshot 2023-03-08 at 17 45 59"
src="https://user-images.githubusercontent.com/2953267/223775744-407afa0c-e8b9-44a1-90f9-b879348da57f.png">
That’s because Prettier 1 can’t parse `import type` at all. To parse it,
we need to upgrade to Prettier 2.
### Why enforce `import type`?
Apart from just enabling `import type` support, this PR enforces
specifying `import type` everywhere it’s needed. (Developers will get
immediate TypeScript and ESLint errors when they forget to do so.)
I’m doing this because I believe `import type` improves DX and makes
refactorings easier.
Let’s say you had a few imports like below. Can you tell which of these
imports will increase the bundle size? (Tip: it’s not all of them!)
```ts
// app/client/src/workers/Linting/utils.ts
import { Position } from "codemirror";
import { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
It’s pretty hard, right?
What about now?
```ts
// app/client/src/workers/Linting/utils.ts
import type { Position } from "codemirror";
import type { LintError as JSHintError, LintOptions } from "jshint";
import { get, isEmpty, isNumber, keys, last, set } from "lodash";
```
Now, it’s clear that only `lodash` will be bundled.
This helps developers to see which imports are problematic, but it
_also_ helps with refactorings. Now, if you want to see where
`codemirror` is bundled, you can just grep for `import \{.*\} from
"codemirror"` – and you won’t get any type-only imports.
This also helps (some) bundlers. Upon transpiling, TypeScript erases
type-only imports completely. In some environment (not ours), this makes
the bundle smaller, as the bundler doesn’t need to bundle type-only
imports anymore.
## Type of change
- Chore (housekeeping or task changes that don't impact user perception)
## How Has This Been Tested?
This was tested to not break the build.
### Test Plan
> Add Testsmith test cases links that relate to this PR
### Issues raised during DP testing
> Link issues raised during DP testing for better visiblity and tracking
(copy link from comments dropped on this PR)
## Checklist:
### Dev activity
- [x] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] PR is being merged under a feature flag
### QA activity:
- [ ] Test plan has been approved by relevant developers
- [ ] Test plan has been peer reviewed by QA
- [ ] Cypress test cases have been added and approved by either SDET or
manual QA
- [ ] Organized project review call with relevant stakeholders after
Round 1/2 of QA
- [ ] Added Test Plan Approved label after reveiwing all Cypress test
---------
Co-authored-by: Satish Gandham <hello@satishgandham.com>
Co-authored-by: Satish Gandham <satish.iitg@gmail.com>
2023-03-16 11:41:47 +00:00
const { bottomRow , leftColumn , rightColumn , topRow } =
yield calculateNewWidgetPosition (
newWidget as WidgetProps ,
MAIN_CONTAINER_WIDGET_ID ,
widgets ,
) ;
2020-10-15 07:21:00 +00:00
newWidget = {
. . . newWidget ,
leftColumn ,
topRow ,
rightColumn ,
bottomRow ,
2021-08-12 05:45:38 +00:00
parentRowSpace : GridDefaults.DEFAULT_GRID_ROW_HEIGHT ,
2020-10-15 07:21:00 +00:00
} ;
2023-11-15 12:31:54 +00:00
switch ( layoutSystemType ) {
case LayoutSystemTypes . AUTO :
yield put ( {
type : ReduxActionTypes . AUTOLAYOUT_ADD_NEW_WIDGETS ,
payload : {
dropPayload : {
isNewLayer : true ,
alignment : FlexLayerAlignment.Start ,
} ,
newWidget ,
parentId : MAIN_CONTAINER_WIDGET_ID ,
direction : LayoutDirection.Vertical ,
addToBottom : true ,
2023-04-07 13:51:35 +00:00
} ,
2023-11-15 12:31:54 +00:00
} ) ;
break ;
case LayoutSystemTypes . ANVIL :
yield put (
addSuggestedWidgetAnvilAction ( {
newWidgetId : newWidget.newWidgetId ,
rows : newWidget.rows ,
columns : newWidget.columns ,
type : newWidget . type ,
. . . widgetConfig ,
} ) ,
) ;
break ;
default :
yield put ( {
type : WidgetReduxActionTypes . WIDGET_ADD_CHILD ,
payload : newWidget ,
} ) ;
break ;
2023-04-07 13:51:35 +00:00
}
2020-10-15 07:21:00 +00:00
2023-02-21 13:38:16 +00:00
yield take ( ReduxActionTypes . UPDATE_LAYOUT ) ;
2020-10-15 07:21:00 +00:00
2023-07-27 13:00:23 +00:00
if ( isSetWidgetIdForWalkthrough ) {
localStorage . setItem ( WIDGET_ID_SHOW_WALKTHROUGH , newWidget . newWidgetId ) ;
}
2023-02-21 13:38:16 +00:00
yield put (
selectWidgetInitAction ( SelectionRequestType . One , [ newWidget . newWidgetId ] ) ,
) ;
2020-10-15 07:21:00 +00:00
} catch ( error ) {
2021-08-20 04:13:16 +00:00
log . error ( error ) ;
2020-10-15 07:21:00 +00:00
}
}
2021-08-25 05:00:31 +00:00
/ * *
* saga to group selected widgets into a new container
*
* @param action
* /
export function * groupWidgetsSaga() {
const selectedWidgetIDs : string [ ] = yield select ( getSelectedWidgets ) ;
const isMultipleWidgetsSelected = selectedWidgetIDs . length > 1 ;
2023-06-09 08:52:27 +00:00
// Grouping functionality has been temporarily disabled for auto-layout canvas.
2023-04-28 19:31:29 +00:00
const isAutoLayout : boolean = yield select ( getIsAutoLayout ) ;
2024-09-18 16:35:28 +00:00
2023-04-28 19:31:29 +00:00
if ( isAutoLayout ) return ;
2024-09-18 16:35:28 +00:00
2021-08-25 05:00:31 +00:00
if ( isMultipleWidgetsSelected ) {
try {
yield put ( {
type : ReduxActionTypes . PASTE_COPIED_WIDGET_INIT ,
payload : {
groupWidgets : true ,
} ,
} ) ;
} catch ( error ) {
log . error ( error ) ;
}
}
}
2022-05-06 05:42:35 +00:00
function * widgetBatchUpdatePropertySaga() {
/ *
* BATCH_UPDATE_WIDGET_PROPERTY should be processed serially as
* it updates the state . We want the state updates from previous
* batch update to be flushed out to the store before processing
* the another batch update .
* /
2022-06-21 13:57:34 +00:00
const batchUpdateWidgetPropertyChannel : unknown = yield actionChannel (
2022-05-06 05:42:35 +00:00
ReduxActionTypes . BATCH_UPDATE_WIDGET_PROPERTY ,
) ;
while ( true ) {
2022-06-21 13:57:34 +00:00
// @ts-expect-error: Type mismatch
const action : unknown = yield take ( batchUpdateWidgetPropertyChannel ) ;
2024-09-18 16:35:28 +00:00
2022-06-21 13:57:34 +00:00
// @ts-expect-error: Type mismatch
2022-05-06 05:42:35 +00:00
yield call ( batchUpdateWidgetPropertySaga , action ) ;
}
}
2024-07-05 08:27:59 +00:00
/ * *
* This saga check if the paste operation is performed on a layout system compatible with the widgets being pasted
* If the widgets are not compatible , we show a toast warning to the user and prevent the paste operation
* If the widgets are compatible , we call the paste action
* @param action The page action payload and verify paste action type
* @returns void
* /
function * verifyPasteFeasibilitySaga (
action : ReduxAction < PasteWidgetReduxAction > ,
) {
try {
const {
layoutSystemType ,
} : {
layoutSystemType? : LayoutSystemTypes ;
} = yield getCopiedWidgets ( ) ;
const currentLayoutSystemType : LayoutSystemTypes =
yield select ( getLayoutSystemType ) ;
const isConflicting = isLayoutSystemConflictingForPaste (
currentLayoutSystemType ,
layoutSystemType ,
) ;
if ( isConflicting ) {
const message =
currentLayoutSystemType === LayoutSystemTypes . ANVIL
? ERROR_PASTE_ANVIL_LAYOUT_SYSTEM_CONFLICT
: ERROR_PASTE_FIXED_LAYOUT_SYSTEM_CONFLICT ;
2024-09-18 16:35:28 +00:00
2024-07-05 08:27:59 +00:00
toast . show ( createMessage ( message ) , {
kind : "warning" ,
} ) ;
2024-09-18 16:35:28 +00:00
2024-07-05 08:27:59 +00:00
return ;
}
yield put ( {
type : ReduxActionTypes . PASTE_COPIED_WIDGET_INIT ,
payload : action.payload ,
} ) ;
} finally {
if ( action . payload . existingWidgets ) {
yield call (
saveCopiedWidgets ,
JSON . stringify ( action . payload . existingWidgets ) ,
) ;
}
}
}
2024-07-31 15:41:28 +00:00
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2024-01-24 16:25:08 +00:00
function * shouldCallSaga ( saga : any , action : ReduxAction < unknown > ) {
2024-04-12 17:24:04 +00:00
const isAnvilLayout : boolean = yield select ( getIsAnvilLayout ) ;
2024-09-18 16:35:28 +00:00
2024-04-12 17:24:04 +00:00
if ( ! isAnvilLayout ) {
2024-01-24 16:25:08 +00:00
yield call ( saga , action ) ;
}
}
2019-09-19 22:25:37 +00:00
export default function * widgetOperationSagas() {
2021-09-21 07:55:56 +00:00
yield fork ( widgetAdditionSagas ) ;
yield fork ( widgetDeletionSagas ) ;
2021-06-17 13:26:54 +00:00
yield fork ( widgetSelectionSagas ) ;
2022-05-06 05:42:35 +00:00
yield fork ( widgetBatchUpdatePropertySaga ) ;
2019-09-19 22:25:37 +00:00
yield all ( [
2021-07-26 16:44:10 +00:00
takeEvery ( ReduxActionTypes . ADD_SUGGESTED_WIDGET , addSuggestedWidget ) ,
2021-08-03 08:06:48 +00:00
takeLatest ( WidgetReduxActionTypes . WIDGET_RESIZE , resizeSaga ) ,
2019-11-06 06:35:15 +00:00
takeEvery (
ReduxActionTypes . UPDATE_WIDGET_PROPERTY_REQUEST ,
updateWidgetPropertySaga ,
) ,
2021-04-27 07:16:54 +00:00
takeEvery (
2021-08-03 08:06:48 +00:00
WidgetReduxActionTypes . WIDGET_UPDATE_PROPERTY ,
2021-04-27 07:16:54 +00:00
updateWidgetPropertySaga ,
) ,
2020-02-26 12:44:56 +00:00
takeEvery (
ReduxActionTypes . SET_WIDGET_DYNAMIC_PROPERTY ,
setWidgetDynamicPropertySaga ,
) ,
2023-07-26 05:38:11 +00:00
takeEvery (
ReduxActionTypes . BATCH_SET_WIDGET_DYNAMIC_PROPERTY ,
batchUpdateWidgetDynamicPropertySaga ,
) ,
2020-03-06 09:45:21 +00:00
takeEvery (
ReduxActionTypes . RESET_CHILDREN_WIDGET_META ,
resetChildrenMetaSaga ,
) ,
2021-09-21 07:55:56 +00:00
takeEvery (
ReduxActionTypes . BATCH_UPDATE_MULTIPLE_WIDGETS_PROPERTY ,
batchUpdateMultipleWidgetsPropertiesSaga ,
) ,
2021-01-25 08:57:26 +00:00
takeEvery (
ReduxActionTypes . DELETE_WIDGET_PROPERTY ,
deleteWidgetPropertySaga ,
) ,
2020-03-27 09:02:11 +00:00
takeLatest ( ReduxActionTypes . UPDATE_CANVAS_SIZE , updateCanvasSize ) ,
2020-09-16 10:28:01 +00:00
takeLatest ( ReduxActionTypes . COPY_SELECTED_WIDGET_INIT , copyWidgetSaga ) ,
2024-01-24 16:25:08 +00:00
takeLeading (
ReduxActionTypes . PASTE_COPIED_WIDGET_INIT ,
shouldCallSaga ,
pasteWidgetSaga ,
) ,
2024-07-05 08:27:59 +00:00
// This was originally PASTE_COPIED_WIDGET_INIT, however, we now need to make sure
// that the paste happens between compatible widgets and layout systems.
// This saga is the intermidiary between the paste trigger and the actual paste operation
takeLeading (
ReduxActionTypes . VERIFY_LAYOUT_SYSTEM_AND_PASTE_WIDGETS ,
verifyPasteFeasibilitySaga ,
) ,
2020-09-16 10:28:01 +00:00
takeEvery ( ReduxActionTypes . CUT_SELECTED_WIDGET , cutWidgetSaga ) ,
2021-08-25 05:00:31 +00:00
takeEvery ( ReduxActionTypes . GROUP_WIDGETS_INIT , groupWidgetsSaga ) ,
2023-11-17 07:16:18 +00:00
takeEvery ( ReduxActionTypes . PARTIAL_IMPORT_INIT , partialImportSaga ) ,
takeEvery ( ReduxActionTypes . PARTIAL_EXPORT_INIT , partialExportSaga ) ,
2019-09-19 22:25:37 +00:00
] ) ;
}