2022-07-14 07:02:35 +00:00
import React , { lazy , Suspense } from "react" ;
import log from "loglevel" ;
2023-03-30 04:54:29 +00:00
import memoizeOne from "memoize-one" ;
2022-07-14 07:02:35 +00:00
import _ , {
isNumber ,
isString ,
isNil ,
xor ,
without ,
isArray ,
xorWith ,
isEmpty ,
union ,
2022-11-18 10:54:35 +00:00
isObject ,
2023-02-15 11:42:46 +00:00
pickBy ,
2022-12-30 10:52:11 +00:00
orderBy ,
2023-02-15 11:42:46 +00:00
filter ,
2023-06-01 17:26:05 +00:00
merge ,
2022-07-14 07:02:35 +00:00
} from "lodash" ;
2023-06-01 17:26:05 +00:00
import type { WidgetProps , WidgetState } from "widgets/BaseWidget" ;
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 BaseWidget from "widgets/BaseWidget" ;
import { RenderModes , WIDGET_PADDING } from "constants/WidgetConstants" ;
2022-07-14 07:02:35 +00:00
import { EventType } from "constants/AppsmithActionConstants/ActionConstants" ;
import Skeleton from "components/utils/Skeleton" ;
import { noop , retryPromise } from "utils/AppsmithUtils" ;
2023-05-03 06:26:08 +00:00
import { SORT_ORDER } from "../component/Constants" ;
2023-06-27 05:15:41 +00:00
import { StickyType } from "../component/Constants" ;
import type { ReactTableFilter } from "../component/Constants" ;
2023-03-30 04:54:29 +00:00
import { AddNewRowActions , DEFAULT_FILTER } from "../component/Constants" ;
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 {
EditableCell ,
OnColumnEventArgs ,
TableWidgetProps ,
TransientDataPayload ,
} from "../constants" ;
2023-08-22 11:27:02 +00:00
import { ALLOW_TABLE_WIDGET_SERVER_SIDE_FILTERING } from "../constants" ;
2022-11-05 09:54:20 +00:00
import {
ActionColumnTypes ,
2022-07-14 07:02:35 +00:00
ColumnTypes ,
2022-09-13 05:41:59 +00:00
defaultEditableCell ,
2022-07-14 07:02:35 +00:00
DEFAULT_BUTTON_LABEL ,
DEFAULT_COLUMN_WIDTH ,
DEFAULT_MENU_BUTTON_LABEL ,
DEFAULT_MENU_VARIANT ,
EditableCellActions ,
InlineEditingSaveOptions ,
ORIGINAL_INDEX_KEY ,
2023-02-15 11:42:46 +00:00
TABLE_COLUMN_ORDER_KEY ,
2023-04-13 10:08:46 +00:00
PaginationDirection ,
2022-07-14 07:02:35 +00:00
} from "../constants" ;
import derivedProperties from "./parseDerivedProperties" ;
import {
getAllTableColumnKeys ,
getDefaultColumnProperties ,
getDerivedColumns ,
getTableStyles ,
getSelectRowIndex ,
getSelectRowIndices ,
getCellProperties ,
isColumnTypeEditable ,
2022-09-29 05:26:08 +00:00
getColumnType ,
2022-12-30 10:52:11 +00:00
getBooleanPropertyValue ,
2023-02-15 11:42:46 +00:00
deleteLocalTableColumnOrderByWidgetId ,
getColumnOrderByWidgetIdFromLS ,
generateLocalNewColumnOrderFromStickyValue ,
updateAndSyncTableLocalColumnOrders ,
getAllStickyColumnsCount ,
2023-06-01 17:26:05 +00:00
createEditActionColumn ,
2022-07-14 07:02:35 +00:00
} from "./utilities" ;
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 {
2022-07-14 07:02:35 +00:00
ColumnProperties ,
ReactTableColumnProps ,
} from "../component/Constants" ;
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 { CompactModeTypes , SortOrderTypes } from "../component/Constants" ;
2022-08-17 18:23:53 +00:00
import contentConfig from "./propertyConfig/contentConfig" ;
import styleConfig from "./propertyConfig/styleConfig" ;
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 { BatchPropertyUpdatePayload } from "actions/controlActions" ;
import type { IconName } from "@blueprintjs/icons" ;
import { IconNames } from "@blueprintjs/icons" ;
2022-07-14 07:02:35 +00:00
import { Colors } from "constants/Colors" ;
import equal from "fast-deep-equal/es6" ;
2023-04-14 06:27:49 +00:00
import {
sanitizeKey ,
DefaultAutocompleteDefinitions ,
} from "widgets/WidgetUtils" ;
2022-09-30 04:03:53 +00:00
import PlainTextCell from "../component/cellComponents/PlainTextCell" ;
2022-07-14 07:02:35 +00:00
import { ButtonCell } from "../component/cellComponents/ButtonCell" ;
import { MenuButtonCell } from "../component/cellComponents/MenuButtonCell" ;
import { ImageCell } from "../component/cellComponents/ImageCell" ;
import { VideoCell } from "../component/cellComponents/VideoCell" ;
import { IconButtonCell } from "../component/cellComponents/IconButtonCell" ;
import { EditActionCell } from "../component/cellComponents/EditActionsCell" ;
import { klona as clone } from "klona" ;
2022-09-08 11:05:59 +00:00
import { CheckboxCell } from "../component/cellComponents/CheckboxCell" ;
2022-09-16 04:34:11 +00:00
import { SwitchCell } from "../component/cellComponents/SwitchCell" ;
2022-09-30 04:03:53 +00:00
import { SelectCell } from "../component/cellComponents/SelectCell" ;
2022-11-05 09:54:20 +00:00
import { CellWrapper } from "../component/TableStyledWrappers" ;
2023-02-15 11:42:46 +00:00
import localStorage from "utils/localStorage" ;
import { generateNewColumnOrderFromStickyValue } from "./utilities" ;
2023-07-08 14:07:26 +00:00
import type { SetterConfig , Stylesheet } from "entities/AppTheming" ;
2023-01-16 09:23:56 +00:00
import { DateCell } from "../component/cellComponents/DateCell" ;
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 { MenuItem } from "widgets/MenuButtonWidget/constants" ;
import { MenuItemsSource } from "widgets/MenuButtonWidget/constants" ;
2023-01-16 09:23:56 +00:00
import { TimePrecision } from "widgets/DatePickerWidget2/constants" ;
2023-03-30 04:54:29 +00:00
import type { getColumns } from "./reactTableUtils/getColumnsPureFn" ;
import { getMemoiseGetColumnsWithLocalStorageFn } from "./reactTableUtils/getColumnsPureFn" ;
import type {
tableData ,
transformDataWithEditableCell ,
} from "./reactTableUtils/transformDataPureFn" ;
import { getMemoiseTransformDataWithEditableCell } from "./reactTableUtils/transformDataPureFn" ;
2023-04-14 06:27:49 +00:00
import type { ExtraDef } from "utils/autocomplete/dataTreeTypeDefCreator" ;
import { generateTypeDef } from "utils/autocomplete/dataTreeTypeDefCreator" ;
2023-09-06 12:15:04 +00:00
import type { AutocompletionDefinitions } from "WidgetProvider/constants" ;
2023-06-01 17:26:05 +00:00
import type {
WidgetQueryConfig ,
WidgetQueryGenerationFormConfig ,
} from "WidgetQueryGenerators/types" ;
2023-07-20 06:22:20 +00:00
import type { DynamicPath } from "utils/DynamicBindingUtils" ;
2023-09-06 12:15:04 +00:00
import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants" ;
import { ResponsiveBehavior } from "utils/autoLayout/constants" ;
import IconSVG from "../icon.svg" ;
import type {
PropertyUpdates ,
SnipingModeProperty ,
} from "WidgetProvider/constants" ;
import { WIDGET_TAGS } from "constants/WidgetConstants" ;
2022-07-14 07:02:35 +00:00
const ReactTableComponent = lazy ( ( ) = >
retryPromise ( ( ) = > import ( "../component" ) ) ,
) ;
2023-03-30 04:54:29 +00:00
const emptyArr : any = [ ] ;
type addNewRowToTable = (
tableData : tableData ,
isAddRowInProgress : boolean ,
newRowContent : Record < string , unknown > ,
) = > tableData ;
const getMemoisedAddNewRow = ( ) : addNewRowToTable = >
memoizeOne ( ( tableData , isAddRowInProgress , newRowContent ) = > {
if ( isAddRowInProgress ) {
return [ newRowContent , . . . tableData ] ;
}
return tableData ;
} ) ;
2022-07-14 07:02:35 +00:00
class TableWidgetV2 extends BaseWidget < TableWidgetProps , WidgetState > {
2022-09-13 05:41:59 +00:00
inlineEditTimer : number | null = null ;
2023-03-30 04:54:29 +00:00
memoisedAddNewRow : addNewRowToTable ;
memoiseGetColumnsWithLocalStorage : ( localStorage : any ) = > getColumns ;
memoiseTransformDataWithEditableCell : transformDataWithEditableCell ;
2022-07-14 07:02:35 +00:00
2023-09-06 12:15:04 +00:00
static type = "TABLE_WIDGET_V2" ;
static preloadConfig = true ;
static getConfig() {
2023-06-01 17:26:05 +00:00
return {
2023-09-06 12:15:04 +00:00
name : "Table" ,
iconSVG : IconSVG ,
tags : [ WIDGET_TAGS . SUGGESTED_WIDGETS , WIDGET_TAGS . DISPLAY ] ,
needsMeta : true ,
needsHeightForContent : true ,
2023-06-01 17:26:05 +00:00
} ;
}
2023-09-06 12:15:04 +00:00
static getDefaults() {
return {
responsiveBehavior : ResponsiveBehavior.Fill ,
minWidth : FILL_WIDGET_MIN_WIDTH ,
rows : 28 ,
canFreezeColumn : true ,
columnUpdatedAt : Date.now ( ) ,
columns : 34 ,
animateLoading : true ,
defaultSelectedRowIndex : 0 ,
defaultSelectedRowIndices : [ 0 ] ,
label : "Data" ,
widgetName : "Table" ,
searchKey : "" ,
textSize : "0.875rem" ,
horizontalAlignment : "LEFT" ,
verticalAlignment : "CENTER" ,
totalRecordsCount : 0 ,
defaultPageSize : 0 ,
dynamicPropertyPathList : [ ] ,
borderColor : Colors.GREY_5 ,
borderWidth : "1" ,
dynamicBindingPathList : [ ] ,
primaryColumns : { } ,
tableData : "" ,
columnWidthMap : { } ,
columnOrder : [ ] ,
enableClientSideSearch : true ,
isVisibleSearch : true ,
isVisibleFilters : true ,
isVisibleDownload : true ,
isVisiblePagination : true ,
isSortable : true ,
delimiter : "," ,
version : 2 ,
inlineEditingSaveOption : InlineEditingSaveOptions.ROW_LEVEL ,
enableServerSideFiltering : TableWidgetV2.getFeatureFlag (
ALLOW_TABLE_WIDGET_SERVER_SIDE_FILTERING ,
)
? false
: undefined ,
} ;
}
2023-06-01 17:26:05 +00:00
2023-09-06 12:15:04 +00:00
static getMethods() {
return {
getQueryGenerationConfig : ( widget : WidgetProps ) = > {
return {
select : {
limit : ` ${ widget . widgetName } .pageSize ` ,
where : ` ${ widget . widgetName } .searchText ` ,
offset : ` ${ widget . widgetName } .pageOffset ` ,
orderBy : ` ${ widget . widgetName } .sortOrder.column ` ,
sortOrder : ` ${ widget . widgetName } .sortOrder.order !== "desc" ` ,
2023-06-01 17:26:05 +00:00
} ,
2023-09-06 12:15:04 +00:00
create : {
value : ` ( ${ widget . widgetName } .newRow || {}) ` ,
} ,
update : {
value : ` ${ widget . widgetName } .updatedRow ` ,
where : ` ${ widget . widgetName } .updatedRow ` ,
} ,
totalRecord : true ,
} ;
} ,
getPropertyUpdatesForQueryBinding : (
queryConfig : WidgetQueryConfig ,
_widget : WidgetProps ,
formConfig : WidgetQueryGenerationFormConfig ,
) = > {
const widget = _widget as TableWidgetProps ;
let modify = { } ;
const dynamicPropertyPathList : DynamicPath [ ] = [ ] ;
if ( queryConfig . select ) {
modify = merge ( modify , {
tableData : queryConfig.select.data ,
onPageChange : queryConfig.select.run ,
serverSidePaginationEnabled : true ,
onSearchTextChanged : formConfig.searchableColumn
? queryConfig . select . run
: undefined ,
onSort : queryConfig.select.run ,
enableClientSideSearch : ! formConfig . searchableColumn ,
primaryColumnId : formConfig.primaryColumn ,
isVisibleDownload : false ,
} ) ;
}
2023-06-01 17:26:05 +00:00
2023-09-06 12:15:04 +00:00
if ( queryConfig . create ) {
modify = merge ( modify , {
onAddNewRowSave : queryConfig.create.run ,
allowAddNewRow : true ,
. . . Object . keys ( widget . primaryColumns ) . reduce (
( prev : Record < string , boolean > , curr ) = > {
if ( formConfig . primaryColumn !== curr ) {
prev [ ` primaryColumns. ${ curr } .isEditable ` ] = true ;
prev [ ` primaryColumns. ${ curr } .isCellEditable ` ] = true ;
}
prev [ ` showInlineEditingOptionDropdown ` ] = true ;
return prev ;
} ,
{ } ,
) ,
} ) ;
}
2023-06-01 17:26:05 +00:00
2023-09-06 12:15:04 +00:00
if ( queryConfig . update ) {
let editAction = { } ;
2023-06-01 17:26:05 +00:00
2023-09-06 12:15:04 +00:00
if (
! Object . values ( widget . primaryColumns ) . some (
( column ) = > column . columnType === ColumnTypes . EDIT_ACTIONS ,
)
) {
editAction = Object . values ( createEditActionColumn ( widget ) ) . reduce (
(
prev : Record < string , unknown > ,
curr : {
propertyPath : string ;
propertyValue : unknown ;
isDynamicPropertyPath? : boolean ;
} ,
) = > {
prev [ curr . propertyPath ] = curr . propertyValue ;
if ( curr . isDynamicPropertyPath ) {
dynamicPropertyPathList . push ( { key : curr.propertyPath } ) ;
}
return prev ;
} ,
{ } ,
) ;
}
2023-07-20 06:22:20 +00:00
2023-09-06 12:15:04 +00:00
modify = merge ( modify , {
. . . editAction ,
[ ` primaryColumns.EditActions1.onSave ` ] : queryConfig . update . run ,
} ) ;
}
2023-06-01 17:26:05 +00:00
2023-09-06 12:15:04 +00:00
if ( queryConfig . total_record ) {
modify = merge ( modify , {
totalRecordsCount : queryConfig.total_record.data ,
} ) ;
}
2023-06-01 17:26:05 +00:00
2023-09-06 12:15:04 +00:00
return {
modify ,
dynamicUpdates : {
dynamicPropertyPathList ,
} ,
} ;
} ,
getSnipingModeUpdates : (
propValueMap : SnipingModeProperty ,
) : PropertyUpdates [ ] = > {
return [
{
propertyPath : "tableData" ,
propertyValue : propValueMap.data ,
2023-09-08 07:42:48 +00:00
isDynamicPropertyPath : ! ! propValueMap . isDynamicPropertyPath ,
2023-09-06 12:15:04 +00:00
} ,
] ;
} ,
} ;
}
2023-06-01 17:26:05 +00:00
2023-09-06 12:15:04 +00:00
static getAutoLayoutConfig() {
2023-07-20 06:22:20 +00:00
return {
2023-09-06 12:15:04 +00:00
widgetSize : [
{
viewportMinWidth : 0 ,
configuration : ( ) = > {
return {
minWidth : "280px" ,
minHeight : "300px" ,
} ;
} ,
} ,
] ,
2023-07-20 06:22:20 +00:00
} ;
2023-06-01 17:26:05 +00:00
}
2022-08-17 18:23:53 +00:00
static getPropertyPaneContentConfig() {
return contentConfig ;
}
static getPropertyPaneStyleConfig() {
return styleConfig ;
}
2023-06-01 17:26:05 +00:00
2023-03-30 04:54:29 +00:00
constructor ( props : TableWidgetProps ) {
super ( props ) ;
// generate new cache instances so that each table widget instance has its own respective cache instance
this . memoisedAddNewRow = getMemoisedAddNewRow ( ) ;
this . memoiseGetColumnsWithLocalStorage =
getMemoiseGetColumnsWithLocalStorageFn ( ) ;
this . memoiseTransformDataWithEditableCell =
getMemoiseTransformDataWithEditableCell ( ) ;
}
2022-08-17 18:23:53 +00:00
2022-07-14 07:02:35 +00:00
static getMetaPropertiesMap ( ) : Record < string , any > {
return {
pageNo : 1 ,
selectedRowIndex : undefined ,
selectedRowIndices : undefined ,
searchText : undefined ,
triggeredRowIndex : undefined ,
filters : [ ] ,
sortOrder : {
column : "" ,
order : null ,
} ,
transientTableData : { } ,
2022-11-25 04:39:59 +00:00
updatedRowIndex : - 1 ,
2022-09-13 05:41:59 +00:00
editableCell : defaultEditableCell ,
columnEditableCellValue : { } ,
2022-09-30 04:03:53 +00:00
selectColumnFilterText : { } ,
2022-11-05 09:54:20 +00:00
isAddRowInProgress : false ,
newRowContent : undefined ,
newRow : undefined ,
2023-04-13 10:08:46 +00:00
previousPageVisited : false ,
nextPageVisited : false ,
2022-07-14 07:02:35 +00:00
} ;
}
2023-04-14 06:27:49 +00:00
static getAutocompleteDefinitions ( ) : AutocompletionDefinitions {
return ( widget : TableWidgetProps , extraDefsToDefine? : ExtraDef ) = > {
2023-08-22 11:27:02 +00:00
const config : AutocompletionDefinitions = {
2023-04-14 06:27:49 +00:00
"!doc" :
"The Table is the hero widget of Appsmith. You can display data from an API in a table, trigger an action when a user selects a row and even work with large paginated data sets" ,
"!url" : "https://docs.appsmith.com/widget-reference/table" ,
selectedRow : generateTypeDef ( widget . selectedRow , extraDefsToDefine ) ,
selectedRows : generateTypeDef ( widget . selectedRows , extraDefsToDefine ) ,
selectedRowIndices : generateTypeDef ( widget . selectedRowIndices ) ,
triggeredRow : generateTypeDef ( widget . triggeredRow ) ,
updatedRow : generateTypeDef ( widget . updatedRow ) ,
selectedRowIndex : "number" ,
tableData : generateTypeDef ( widget . tableData , extraDefsToDefine ) ,
pageNo : "number" ,
pageSize : "number" ,
isVisible : DefaultAutocompleteDefinitions.isVisible ,
searchText : "string" ,
totalRecordsCount : "number" ,
sortOrder : {
column : "string" ,
order : [ "asc" , "desc" ] ,
} ,
updatedRows : generateTypeDef ( widget . updatedRows , extraDefsToDefine ) ,
updatedRowIndices : generateTypeDef ( widget . updatedRowIndices ) ,
triggeredRowIndex : generateTypeDef ( widget . triggeredRowIndex ) ,
pageOffset : generateTypeDef ( widget . pageOffset ) ,
tableHeaders : generateTypeDef ( widget . tableHeaders ) ,
newRow : generateTypeDef ( widget . newRow ) ,
isAddRowInProgress : "bool" ,
previousPageVisited : generateTypeDef ( widget . previousPageVisited ) ,
nextPageVisited : generateTypeDef ( widget . nextPageButtonClicked ) ,
} ;
2023-07-08 14:07:26 +00:00
2023-08-22 11:27:02 +00:00
if ( this . getFeatureFlag ( ALLOW_TABLE_WIDGET_SERVER_SIDE_FILTERING ) ) {
config [ "filters" ] = generateTypeDef ( widget . filters ) ;
}
2023-04-14 06:27:49 +00:00
return config ;
} ;
}
2022-07-14 07:02:35 +00:00
static getDerivedPropertiesMap() {
return {
selectedRow : ` {{(()=>{ ${ derivedProperties . getSelectedRow } })()}} ` ,
triggeredRow : ` {{(()=>{ ${ derivedProperties . getTriggeredRow } })()}} ` ,
selectedRows : ` {{(()=>{ ${ derivedProperties . getSelectedRows } })()}} ` ,
pageSize : ` {{(()=>{ ${ derivedProperties . getPageSize } })()}} ` ,
triggerRowSelection : "{{!!this.onRowSelected}}" ,
processedTableData : ` {{(()=>{ ${ derivedProperties . getProcessedTableData } })()}} ` ,
orderedTableColumns : ` {{(()=>{ ${ derivedProperties . getOrderedTableColumns } })()}} ` ,
filteredTableData : ` {{(()=>{ ${ derivedProperties . getFilteredTableData } })()}} ` ,
updatedRows : ` {{(()=>{ ${ derivedProperties . getUpdatedRows } })()}} ` ,
updatedRowIndices : ` {{(()=>{ ${ derivedProperties . getUpdatedRowIndices } })()}} ` ,
2022-11-25 04:39:59 +00:00
updatedRow : ` {{(()=>{ ${ derivedProperties . getUpdatedRow } })()}} ` ,
2022-09-01 10:01:02 +00:00
pageOffset : ` {{(()=>{ ${ derivedProperties . getPageOffset } })()}} ` ,
2022-11-05 09:54:20 +00:00
isEditableCellsValid : ` {{(()=>{ ${ derivedProperties . getEditableCellValidity } })()}} ` ,
2022-11-25 06:12:23 +00:00
tableHeaders : ` {{(()=>{ ${ derivedProperties . getTableHeaders } })()}} ` ,
2022-07-14 07:02:35 +00:00
} ;
}
static getDefaultPropertiesMap ( ) : Record < string , string > {
return {
searchText : "defaultSearchText" ,
selectedRowIndex : "defaultSelectedRowIndex" ,
selectedRowIndices : "defaultSelectedRowIndices" ,
} ;
}
2022-09-13 05:40:08 +00:00
static getLoadingProperties ( ) : Array < RegExp > | undefined {
return [ /\.tableData$/ ] ;
}
2022-11-28 04:44:31 +00:00
static getStylesheetConfig ( ) : Stylesheet {
return {
accentColor : "{{appsmith.theme.colors.primaryColor}}" ,
borderRadius : "{{appsmith.theme.borderRadius.appBorderRadius}}" ,
boxShadow : "{{appsmith.theme.boxShadow.appBoxShadow}}" ,
childStylesheet : {
button : {
buttonColor : "{{appsmith.theme.colors.primaryColor}}" ,
borderRadius : "{{appsmith.theme.borderRadius.appBorderRadius}}" ,
boxShadow : "none" ,
} ,
menuButton : {
menuColor : "{{appsmith.theme.colors.primaryColor}}" ,
borderRadius : "{{appsmith.theme.borderRadius.appBorderRadius}}" ,
boxShadow : "none" ,
} ,
iconButton : {
buttonColor : "{{appsmith.theme.colors.primaryColor}}" ,
borderRadius : "{{appsmith.theme.borderRadius.appBorderRadius}}" ,
boxShadow : "none" ,
} ,
editActions : {
saveButtonColor : "{{appsmith.theme.colors.primaryColor}}" ,
saveBorderRadius : "{{appsmith.theme.borderRadius.appBorderRadius}}" ,
discardButtonColor : "{{appsmith.theme.colors.primaryColor}}" ,
discardBorderRadius :
"{{appsmith.theme.borderRadius.appBorderRadius}}" ,
} ,
} ,
} ;
}
2023-07-08 14:07:26 +00:00
static getSetterConfig ( ) : SetterConfig {
return {
__setters : {
setVisibility : {
path : "isVisible" ,
type : "string" ,
} ,
setSelectedRowIndex : {
path : "defaultSelectedRowIndex" ,
type : "number" ,
disabled : "return options.entity.multiRowSelection" ,
} ,
setSelectedRowIndices : {
path : "defaultSelectedRowIndices" ,
type : "array" ,
disabled : "return !options.entity.multiRowSelection" ,
} ,
setData : {
path : "tableData" ,
type : "object" ,
} ,
} ,
} ;
}
2022-07-14 07:02:35 +00:00
/ *
* Function to get the table columns with appropriate render functions
* based on columnType
* /
getTableColumns = ( ) = > {
2023-06-27 05:15:41 +00:00
const { columnWidthMap , orderedTableColumns , renderMode , widgetId } =
this . props ;
2022-09-22 11:15:00 +00:00
const { componentWidth } = this . getPaddingAdjustedDimensions ( ) ;
2023-03-30 04:54:29 +00:00
const widgetLocalStorageState = getColumnOrderByWidgetIdFromLS ( widgetId ) ;
const memoisdGetColumnsWithLocalStorage =
this . memoiseGetColumnsWithLocalStorage ( widgetLocalStorageState ) ;
return memoisdGetColumnsWithLocalStorage (
this . renderCell ,
columnWidthMap ,
orderedTableColumns ,
componentWidth ,
renderMode ,
) ;
2022-07-14 07:02:35 +00:00
} ;
transformData = (
tableData : Array < Record < string , unknown > > ,
columns : ReactTableColumnProps [ ] ,
) = > {
2023-03-30 04:54:29 +00:00
return this . memoiseTransformDataWithEditableCell (
this . props . editableCell ,
tableData ,
columns ,
) ;
2022-07-14 07:02:35 +00:00
} ;
updateDerivedColumnsIndex = (
derivedColumns : Record < string , ColumnProperties > ,
tableColumnCount : number ,
) = > {
if ( ! derivedColumns ) {
return [ ] ;
}
//update index property of all columns in new derived columns
return Object . values ( derivedColumns ) . map (
( column : ColumnProperties , index : number ) = > {
return {
. . . column ,
index : index + tableColumnCount ,
} ;
} ,
) ;
} ;
/ *
* Function to create new primary Columns from the tableData
* gets called on component mount and on component update
* /
createTablePrimaryColumns = ( ) :
| Record < string , ColumnProperties >
| undefined = > {
const { tableData = [ ] , primaryColumns = { } } = this . props ;
if ( ! _ . isArray ( tableData ) || tableData . length === 0 ) {
return ;
}
const existingColumnIds = Object . keys ( primaryColumns ) ;
const newTableColumns : Record < string , ColumnProperties > = { } ;
const tableStyles = getTableStyles ( this . props ) ;
const columnKeys : string [ ] = getAllTableColumnKeys ( tableData ) ;
/ *
* Generate default column properties for all columns
* But do not replace existing columns with the same id
* /
columnKeys . forEach ( ( columnKey , index ) = > {
const existingColumn = this . getColumnByOriginalId ( columnKey ) ;
if ( ! ! existingColumn ) {
// Use the existing column properties
newTableColumns [ existingColumn . id ] = existingColumn ;
} else {
const hashedColumnKey = sanitizeKey ( columnKey , {
existingKeys : union ( existingColumnIds , Object . keys ( newTableColumns ) ) ,
} ) ;
// Create column properties for the new column
2022-09-29 05:26:08 +00:00
const columnType = getColumnType ( tableData , columnKey ) ;
2022-07-14 07:02:35 +00:00
const columnProperties = getDefaultColumnProperties (
columnKey ,
hashedColumnKey ,
index ,
this . props . widgetName ,
2022-09-29 05:26:08 +00:00
false ,
columnType ,
2022-07-14 07:02:35 +00:00
) ;
newTableColumns [ columnProperties . id ] = {
. . . columnProperties ,
. . . tableStyles ,
} ;
}
} ) ;
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 derivedColumns : Record < string , ColumnProperties > =
getDerivedColumns ( primaryColumns ) ;
2022-07-14 07:02:35 +00:00
const updatedDerivedColumns = this . updateDerivedColumnsIndex (
derivedColumns ,
Object . keys ( newTableColumns ) . length ,
) ;
//add derived columns to new Table columns
updatedDerivedColumns . forEach ( ( derivedColumn : ColumnProperties ) = > {
newTableColumns [ derivedColumn . id ] = derivedColumn ;
} ) ;
const newColumnIds = Object . keys ( newTableColumns ) ;
// check if the columns ids differ
if ( _ . xor ( existingColumnIds , newColumnIds ) . length > 0 ) {
return newTableColumns ;
} else {
return ;
}
} ;
/ *
* Function to update primaryColumns when the tablData schema changes
* /
updateColumnProperties = (
tableColumns? : Record < string , ColumnProperties > ,
2023-06-27 05:15:41 +00:00
shouldPersistLocalOrderWhenTableDataChanges = false ,
2022-07-14 07:02:35 +00:00
) = > {
const { columnOrder = [ ] , primaryColumns = { } } = this . props ;
const derivedColumns = getDerivedColumns ( primaryColumns ) ;
if ( tableColumns ) {
const existingColumnIds = Object . keys ( primaryColumns ) ;
const existingDerivedColumnIds = Object . keys ( derivedColumns ) ;
const newColumnIds = Object . keys ( tableColumns ) ;
//Check if there is any difference in the existing and new columns ids
if ( _ . xor ( existingColumnIds , newColumnIds ) . length > 0 ) {
const newColumnIdsToAdd = _ . without ( newColumnIds , . . . existingColumnIds ) ;
const propertiesToAdd : Record < string , unknown > = { } ;
newColumnIdsToAdd . forEach ( ( columnId : string ) = > {
// id could be an empty string
if ( ! ! columnId ) {
Object . entries ( tableColumns [ columnId ] ) . forEach ( ( [ key , value ] ) = > {
propertiesToAdd [ ` primaryColumns. ${ columnId } . ${ key } ` ] = value ;
} ) ;
}
} ) ;
/ *
* If new columnOrders have different values from the original columnOrders
* Only update when there are new Columns ( Derived or Primary )
* /
if (
! ! newColumnIds . length &&
! ! _ . xor ( newColumnIds , columnOrder ) . length &&
2022-09-02 09:16:30 +00:00
! equal ( _ . sortBy ( newColumnIds ) , _ . sortBy ( existingDerivedColumnIds ) )
2022-07-14 07:02:35 +00:00
) {
2022-09-01 06:10:19 +00:00
// Maintain original columnOrder and keep new columns at the end
let newColumnOrder = _ . intersection ( columnOrder , newColumnIds ) ;
newColumnOrder = _ . union ( newColumnOrder , newColumnIds ) ;
2023-05-03 06:26:08 +00:00
const compareColumns = ( a : string , b : string ) = > {
const aSticky = tableColumns [ a ] . sticky || "none" ;
const bSticky = tableColumns [ b ] . sticky || "none" ;
if ( aSticky === bSticky ) {
return 0 ;
}
return SORT_ORDER [ aSticky ] - SORT_ORDER [ bSticky ] ;
} ;
// Sort the column order to retain the position of frozen columns
newColumnOrder . sort ( compareColumns ) ;
2022-09-01 06:10:19 +00:00
propertiesToAdd [ "columnOrder" ] = newColumnOrder ;
2023-06-27 05:15:41 +00:00
/ * *
* As the table data changes in Deployed app , we also update the local storage .
*
* this . updateColumnProperties gets executed on mount and on update of the component .
* On mount we get new tableColumns that may not have any sticky columns .
* This will lead to loss of sticky column that were frozen by the user .
* To avoid this and to maintain user ' s sticky columns we use shouldPersistLocalOrderWhenTableDataChanges below
* so as to avoid updating the local storage on mount .
* * /
if (
this . props . renderMode === RenderModes . PAGE &&
shouldPersistLocalOrderWhenTableDataChanges
) {
const leftOrder = newColumnOrder . filter (
( col : string ) = > tableColumns [ col ] . sticky === StickyType . LEFT ,
) ;
const rightOrder = newColumnOrder . filter (
( col : string ) = > tableColumns [ col ] . sticky === StickyType . RIGHT ,
) ;
this . persistColumnOrder ( newColumnOrder , leftOrder , rightOrder ) ;
}
2022-07-14 07:02:35 +00:00
}
const propertiesToUpdate : BatchPropertyUpdatePayload = {
modify : propertiesToAdd ,
} ;
const pathsToDelete : string [ ] = [ ] ;
const columnsIdsToDelete = without ( existingColumnIds , . . . newColumnIds ) ;
if ( ! ! columnsIdsToDelete . length ) {
columnsIdsToDelete . forEach ( ( id : string ) = > {
if ( ! primaryColumns [ id ] . isDerived ) {
pathsToDelete . push ( ` primaryColumns. ${ id } ` ) ;
}
} ) ;
propertiesToUpdate . remove = pathsToDelete ;
}
super . batchUpdateWidgetProperty ( propertiesToUpdate , false ) ;
}
}
} ;
2023-03-30 04:54:29 +00:00
//no need to batch meta updates
2023-02-15 11:42:46 +00:00
hydrateStickyColumns = ( ) = > {
const localTableColumnOrder = getColumnOrderByWidgetIdFromLS (
this . props . widgetId ,
) ;
const leftLen : number = Object . keys (
pickBy ( this . props . primaryColumns , ( col ) = > col . sticky === "left" ) ,
) . length ;
const leftOrder = [ . . . ( this . props . columnOrder || [ ] ) ] . slice ( 0 , leftLen ) ;
const rightLen : number = Object . keys (
pickBy ( this . props . primaryColumns , ( col ) = > col . sticky !== "right" ) ,
) . length ;
const rightOrder : string [ ] = [ . . . ( this . props . columnOrder || [ ] ) ] . slice (
rightLen ,
) ;
if ( localTableColumnOrder ) {
2023-06-27 05:15:41 +00:00
const {
columnOrder ,
columnUpdatedAt ,
leftOrder : localLeftOrder ,
rightOrder : localRightOrder ,
} = localTableColumnOrder ;
2023-02-15 11:42:46 +00:00
if ( this . props . columnUpdatedAt !== columnUpdatedAt ) {
// Delete and set the column orders defined by the developer
deleteLocalTableColumnOrderByWidgetId ( this . props . widgetId ) ;
this . persistColumnOrder (
this . props . columnOrder ? ? [ ] ,
leftOrder ,
rightOrder ,
) ;
} else {
2023-06-27 05:15:41 +00:00
const propertiesToAdd : Record < string , string > = { } ;
propertiesToAdd [ "columnOrder" ] = columnOrder ;
/ * *
* We reset the sticky values of the columns that were frozen by the developer .
* /
if ( Object . keys ( this . props . primaryColumns ) . length > 0 ) {
columnOrder . forEach ( ( colName : string ) = > {
if (
this . props . primaryColumns [ colName ] ? . sticky !== StickyType . NONE
) {
propertiesToAdd [ ` primaryColumns. ${ colName } .sticky ` ] =
StickyType . NONE ;
}
} ) ;
}
/ * *
* We pickup the left and the right frozen columns from the localstorage
* and update the sticky value of these columns respectively .
* /
if ( localLeftOrder . length > 0 ) {
localLeftOrder . forEach ( ( colName : string ) = > {
propertiesToAdd [ ` primaryColumns. ${ colName } .sticky ` ] =
StickyType . LEFT ;
} ) ;
}
if ( localRightOrder . length > 0 ) {
localRightOrder . forEach ( ( colName : string ) = > {
propertiesToAdd [ ` primaryColumns. ${ colName } .sticky ` ] =
StickyType . RIGHT ;
} ) ;
}
const propertiesToUpdate = {
modify : propertiesToAdd ,
} ;
super . batchUpdateWidgetProperty ( propertiesToUpdate ) ;
2023-02-15 11:42:46 +00:00
}
} else {
// If user deletes local storage or no column orders for the given table widget exists hydrate it with the developer changes.
this . persistColumnOrder (
this . props . columnOrder ? ? [ ] ,
leftOrder ,
rightOrder ,
) ;
}
} ;
2022-07-14 07:02:35 +00:00
componentDidMount() {
2023-02-15 11:42:46 +00:00
const { canFreezeColumn , renderMode , tableData } = this . props ;
2022-07-14 07:02:35 +00:00
if ( _ . isArray ( tableData ) && ! ! tableData . length ) {
const newPrimaryColumns = this . createTablePrimaryColumns ( ) ;
// When the Table data schema changes
if ( newPrimaryColumns && ! ! Object . keys ( newPrimaryColumns ) . length ) {
this . updateColumnProperties ( newPrimaryColumns ) ;
}
}
2023-06-27 05:15:41 +00:00
if ( canFreezeColumn && renderMode === RenderModes . PAGE ) {
//dont neet to batch this since single action
this . hydrateStickyColumns ( ) ;
}
2022-07-14 07:02:35 +00:00
}
componentDidUpdate ( prevProps : TableWidgetProps ) {
const {
defaultSelectedRowIndex ,
defaultSelectedRowIndices ,
pageNo ,
pageSize ,
primaryColumns = { } ,
serverSidePaginationEnabled ,
totalRecordsCount ,
} = this . props ;
// Bail out if tableData is a string. This signifies an error in evaluations
if ( isString ( this . props . tableData ) ) {
return ;
}
2023-02-15 11:42:46 +00:00
if (
this . props . primaryColumns &&
( ! equal ( prevProps . columnOrder , this . props . columnOrder ) ||
filter ( prevProps . orderedTableColumns , { isVisible : false } ) . length !==
filter ( this . props . orderedTableColumns , { isVisible : false } ) . length ||
getAllStickyColumnsCount ( prevProps . orderedTableColumns ) !==
getAllStickyColumnsCount ( this . props . orderedTableColumns ) )
) {
if ( this . props . renderMode === RenderModes . CANVAS ) {
super . batchUpdateWidgetProperty (
{
modify : {
columnUpdatedAt : Date.now ( ) ,
} ,
} ,
false ,
) ;
}
}
2023-03-30 04:54:29 +00:00
//check if necessary we are batching now updates
2022-07-14 07:02:35 +00:00
// Check if tableData is modifed
const isTableDataModified = ! equal (
this . props . tableData ,
prevProps . tableData ,
) ;
2023-06-01 17:26:05 +00:00
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates , pushBatchMetaUpdates } = this . props ;
2022-07-14 07:02:35 +00:00
// If the user has changed the tableData OR
// The binding has returned a new value
if ( isTableDataModified ) {
2023-03-30 04:54:29 +00:00
this . pushMetaRowDataUpdates (
2022-07-14 07:02:35 +00:00
prevProps . filteredTableData ,
this . props . filteredTableData ,
) ;
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "triggeredRowIndex" , - 1 ) ;
2022-07-14 07:02:35 +00:00
const newColumnIds : string [ ] = getAllTableColumnKeys (
this . props . tableData ,
) ;
const primaryColumnIds = Object . keys ( primaryColumns ) . filter (
( id : string ) = > ! primaryColumns [ id ] . isDerived ,
) ;
if ( xor ( newColumnIds , primaryColumnIds ) . length > 0 ) {
const newTableColumns = this . createTablePrimaryColumns ( ) ;
if ( newTableColumns ) {
2023-06-27 05:15:41 +00:00
this . updateColumnProperties ( newTableColumns , isTableDataModified ) ;
2022-07-14 07:02:35 +00:00
}
2022-12-01 05:24:48 +00:00
2023-08-22 11:27:02 +00:00
pushBatchMetaUpdates ( "filters" , [ ] ) ;
2022-07-14 07:02:35 +00:00
}
}
/ *
* Clear transient table data and editablecell when tableData changes
* /
if ( isTableDataModified ) {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "transientTableData" , { } ) ;
2022-11-25 04:39:59 +00:00
// reset updatedRowIndex whenever transientTableData is flushed.
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "updatedRowIndex" , - 1 ) ;
2022-11-25 04:39:59 +00:00
2023-03-30 04:54:29 +00:00
this . pushClearEditableCellsUpdates ( ) ;
pushBatchMetaUpdates ( "selectColumnFilterText" , { } ) ;
2022-07-14 07:02:35 +00:00
}
if ( ! pageNo ) {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "pageNo" , 1 ) ;
2023-04-13 10:08:46 +00:00
this . updatePaginationDirectionFlags ( PaginationDirection . INITIAL ) ;
2022-07-14 07:02:35 +00:00
}
//check if pageNo does not excede the max Page no, due to change of totalRecordsCount
2023-04-13 10:08:46 +00:00
if ( serverSidePaginationEnabled !== prevProps . serverSidePaginationEnabled ) {
2022-07-14 07:02:35 +00:00
//reset pageNo when serverSidePaginationEnabled is toggled
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "pageNo" , 1 ) ;
2023-04-13 10:08:46 +00:00
this . updatePaginationDirectionFlags ( PaginationDirection . INITIAL ) ;
} else {
//check if pageNo does not excede the max Page no, due to change of totalRecordsCount or change of pageSize
if ( serverSidePaginationEnabled && totalRecordsCount ) {
const maxAllowedPageNumber = Math . ceil ( totalRecordsCount / pageSize ) ;
if ( pageNo > maxAllowedPageNumber ) {
pushBatchMetaUpdates ( "pageNo" , maxAllowedPageNumber ) ;
this . updatePaginationDirectionFlags ( PaginationDirection . NEXT_PAGE ) ;
}
}
2022-07-14 07:02:35 +00:00
}
/ *
* When defaultSelectedRowIndex or defaultSelectedRowIndices
* is changed from property pane
* /
if (
2022-09-02 09:16:30 +00:00
! equal ( defaultSelectedRowIndex , prevProps . defaultSelectedRowIndex ) ||
! equal ( defaultSelectedRowIndices , prevProps . defaultSelectedRowIndices )
2022-07-14 07:02:35 +00:00
) {
2023-03-30 04:54:29 +00:00
this . pushUpdateSelectedRowIndexUpdates ( ) ;
2022-07-14 07:02:35 +00:00
}
2023-03-30 04:54:29 +00:00
this . pushResetPageNoUpdates ( prevProps ) ;
2022-07-14 07:02:35 +00:00
2023-03-30 04:54:29 +00:00
this . pushResetRowSelectionPropertiesUpdates ( prevProps ) ;
commitBatchMetaUpdates ( ) ;
2022-07-14 07:02:35 +00:00
}
2023-03-30 04:54:29 +00:00
pushResetPageNoUpdates = ( prevProps : TableWidgetProps ) = > {
const { onPageSizeChange , pageSize , pushBatchMetaUpdates } = this . props ;
2022-07-14 07:02:35 +00:00
if ( pageSize !== prevProps . pageSize ) {
if ( onPageSizeChange ) {
2023-04-13 10:08:46 +00:00
this . updatePaginationDirectionFlags ( PaginationDirection . INITIAL ) ;
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "pageNo" , 1 , {
2022-07-14 07:02:35 +00:00
triggerPropertyName : "onPageSizeChange" ,
dynamicString : onPageSizeChange ,
event : {
type : EventType . ON_PAGE_SIZE_CHANGE ,
} ,
} ) ;
} else {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "pageNo" , 1 ) ;
2023-04-13 10:08:46 +00:00
this . updatePaginationDirectionFlags ( PaginationDirection . INITIAL ) ;
2022-07-14 07:02:35 +00:00
}
}
} ;
2023-03-30 04:54:29 +00:00
pushResetRowSelectionPropertiesUpdates = ( prevProps : TableWidgetProps ) = > {
2022-07-14 07:02:35 +00:00
const {
defaultSelectedRowIndex ,
defaultSelectedRowIndices ,
multiRowSelection ,
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ,
2022-07-14 07:02:35 +00:00
} = this . props ;
// reset selectedRowIndices and selectedRowIndex to defaults
if ( multiRowSelection !== prevProps . multiRowSelection ) {
if ( multiRowSelection ) {
if (
defaultSelectedRowIndices &&
_ . isArray ( defaultSelectedRowIndices ) &&
defaultSelectedRowIndices . every ( ( i ) = > _ . isFinite ( i ) )
) {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "selectedRowIndices" , defaultSelectedRowIndices ) ;
2022-07-14 07:02:35 +00:00
}
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "selectedRowIndex" , - 1 ) ;
2022-07-14 07:02:35 +00:00
} else {
if ( ! isNil ( defaultSelectedRowIndex ) && defaultSelectedRowIndex > - 1 ) {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "selectedRowIndex" , defaultSelectedRowIndex ) ;
2022-07-14 07:02:35 +00:00
}
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "selectedRowIndices" , [ ] ) ;
2022-07-14 07:02:35 +00:00
}
}
} ;
/ *
* Function to update selectedRowIndices & selectedRowIndex from
* defaultSelectedRowIndices & defaultSelectedRowIndex respectively
* /
2023-03-30 04:54:29 +00:00
pushUpdateSelectedRowIndexUpdates = ( ) = > {
2022-07-14 07:02:35 +00:00
const {
defaultSelectedRowIndex ,
defaultSelectedRowIndices ,
multiRowSelection ,
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ,
2022-07-14 07:02:35 +00:00
} = this . props ;
if ( multiRowSelection ) {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "selectedRowIndices" , defaultSelectedRowIndices ) ;
2022-07-14 07:02:35 +00:00
} else {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "selectedRowIndex" , defaultSelectedRowIndex ) ;
2022-07-14 07:02:35 +00:00
}
} ;
/ *
* Function to update selectedRow details when order of tableData changes
* /
2023-03-30 04:54:29 +00:00
pushMetaRowDataUpdates = (
2022-07-14 07:02:35 +00:00
oldTableData : Array < Record < string , unknown > > ,
newTableData : Array < Record < string , unknown > > ,
) = > {
const {
defaultSelectedRowIndex ,
defaultSelectedRowIndices ,
multiRowSelection ,
primaryColumnId ,
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ,
2022-07-14 07:02:35 +00:00
selectedRowIndex ,
selectedRowIndices ,
} = this . props ;
if ( multiRowSelection ) {
const indices = getSelectRowIndices (
oldTableData ,
newTableData ,
defaultSelectedRowIndices ,
selectedRowIndices ,
primaryColumnId ,
) ;
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "selectedRowIndices" , indices ) ;
2022-07-14 07:02:35 +00:00
} else {
const index = getSelectRowIndex (
oldTableData ,
newTableData ,
defaultSelectedRowIndex ,
selectedRowIndex ,
primaryColumnId ,
) ;
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "selectedRowIndex" , index ) ;
2022-07-14 07:02:35 +00:00
}
} ;
getSelectedRowIndices = ( ) = > {
const { multiRowSelection , selectedRowIndices } = this . props ;
let indices : number [ ] | undefined ;
if ( multiRowSelection ) {
if ( _ . isArray ( selectedRowIndices ) ) {
indices = selectedRowIndices ;
} else if ( _ . isNumber ( selectedRowIndices ) ) {
indices = [ selectedRowIndices ] ;
} else {
indices = [ ] ;
}
} else {
indices = undefined ;
}
return indices ;
} ;
updateFilters = ( filters : ReactTableFilter [ ] ) = > {
2023-08-22 11:27:02 +00:00
const {
commitBatchMetaUpdates ,
enableServerSideFiltering ,
onTableFilterUpdate ,
pushBatchMetaUpdates ,
} = this . props ;
2023-03-30 04:54:29 +00:00
this . pushResetSelectedRowIndexUpdates ( ) ;
2023-08-22 11:27:02 +00:00
if ( enableServerSideFiltering ) {
pushBatchMetaUpdates ( "filters" , filters , {
triggerPropertyName : "onTableFilterUpdate" ,
dynamicString : onTableFilterUpdate ,
event : {
type : EventType . ON_FILTER_UPDATE ,
} ,
} ) ;
} else {
pushBatchMetaUpdates ( "filters" , filters ) ;
}
2022-07-14 07:02:35 +00:00
// Reset Page only when a filter is added
2023-02-09 11:29:06 +00:00
if ( ! isEmpty ( xorWith ( filters , [ DEFAULT_FILTER ] , equal ) ) ) {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "pageNo" , 1 ) ;
2023-04-13 10:08:46 +00:00
this . updatePaginationDirectionFlags ( PaginationDirection . INITIAL ) ;
2022-07-14 07:02:35 +00:00
}
2023-03-30 04:54:29 +00:00
commitBatchMetaUpdates ( ) ;
2022-07-14 07:02:35 +00:00
} ;
toggleDrag = ( disable : boolean ) = > {
this . disableDrag ( disable ) ;
} ;
2022-09-22 11:15:00 +00:00
getPaddingAdjustedDimensions = ( ) = > {
// eslint-disable-next-line prefer-const
let { componentHeight , componentWidth } = this . getComponentDimensions ( ) ;
// (2 * WIDGET_PADDING) gives the total horizontal padding (i.e. paddingLeft + paddingRight)
componentWidth = componentWidth - 2 * WIDGET_PADDING ;
return { componentHeight , componentWidth } ;
} ;
2022-07-14 07:02:35 +00:00
getPageView() {
const {
totalRecordsCount ,
delimiter ,
pageSize ,
filteredTableData = [ ] ,
isVisibleDownload ,
isVisibleFilters ,
isVisiblePagination ,
isVisibleSearch ,
2023-06-01 17:26:05 +00:00
primaryColumns ,
2022-07-14 07:02:35 +00:00
} = this . props ;
2023-06-01 17:26:05 +00:00
2023-03-30 04:54:29 +00:00
const tableColumns = this . getTableColumns ( ) || emptyArr ;
2022-07-14 07:02:35 +00:00
const transformedData = this . transformData ( filteredTableData , tableColumns ) ;
const isVisibleHeaderOptions =
isVisibleDownload ||
isVisibleFilters ||
isVisiblePagination ||
isVisibleSearch ;
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 { componentHeight , componentWidth } =
this . getPaddingAdjustedDimensions ( ) ;
2023-03-30 04:54:29 +00:00
const finalTableData = this . memoisedAddNewRow (
transformedData ,
this . props . isAddRowInProgress ,
this . props . newRowContent ,
) ;
2022-11-05 09:54:20 +00:00
2022-07-14 07:02:35 +00:00
return (
< Suspense fallback = { < Skeleton / > } >
< ReactTableComponent
accentColor = { this . props . accentColor }
2022-11-05 09:54:20 +00:00
allowAddNewRow = { this . props . allowAddNewRow }
allowRowSelection = { ! this . props . isAddRowInProgress }
allowSorting = { ! this . props . isAddRowInProgress }
2022-07-14 07:02:35 +00:00
applyFilter = { this . updateFilters }
2022-10-14 04:53:31 +00:00
borderColor = { this . props . borderColor }
2022-07-14 07:02:35 +00:00
borderRadius = { this . props . borderRadius }
2022-10-14 04:53:31 +00:00
borderWidth = { this . props . borderWidth }
2022-07-14 07:02:35 +00:00
boxShadow = { this . props . boxShadow }
2023-02-15 11:42:46 +00:00
canFreezeColumn = { this . props . canFreezeColumn }
2022-07-14 07:02:35 +00:00
columnWidthMap = { this . props . columnWidthMap }
columns = { tableColumns }
compactMode = { this . props . compactMode || CompactModeTypes . DEFAULT }
delimiter = { delimiter }
disableDrag = { this . toggleDrag }
2022-11-05 09:54:20 +00:00
disabledAddNewRowSave = { this . hasInvalidColumnCell ( ) }
2022-07-14 07:02:35 +00:00
editMode = { this . props . renderMode === RenderModes . CANVAS }
editableCell = { this . props . editableCell }
filters = { this . props . filters }
2023-02-15 11:42:46 +00:00
handleColumnFreeze = { this . handleColumnFreeze }
2022-07-14 07:02:35 +00:00
handleReorderColumn = { this . handleReorderColumn }
handleResizeColumn = { this . handleResizeColumn }
height = { componentHeight }
2022-11-05 09:54:20 +00:00
isAddRowInProgress = { this . props . isAddRowInProgress }
isEditableCellsValid = { this . props . isEditableCellsValid }
2022-07-14 07:02:35 +00:00
isLoading = { this . props . isLoading }
isSortable = { this . props . isSortable ? ? true }
isVisibleDownload = { isVisibleDownload }
isVisibleFilters = { isVisibleFilters }
isVisiblePagination = { isVisiblePagination }
isVisibleSearch = { isVisibleSearch }
2022-11-05 09:54:20 +00:00
multiRowSelection = {
this . props . multiRowSelection && ! this . props . isAddRowInProgress
}
2022-07-14 07:02:35 +00:00
nextPageClick = { this . handleNextPageClick }
2022-11-05 09:54:20 +00:00
onAddNewRow = { this . handleAddNewRowClick }
onAddNewRowAction = { this . handleAddNewRowAction }
2022-07-14 07:02:35 +00:00
onBulkEditDiscard = { this . onBulkEditDiscard }
onBulkEditSave = { this . onBulkEditSave }
2023-06-01 17:26:05 +00:00
onConnectData = { this . onConnectData }
2022-07-14 07:02:35 +00:00
onRowClick = { this . handleRowClick }
pageNo = { this . props . pageNo }
pageSize = {
isVisibleHeaderOptions ? Math . max ( 1 , pageSize ) : pageSize + 1
}
prevPageClick = { this . handlePrevPageClick }
2022-10-06 09:32:09 +00:00
primaryColumnId = { this . props . primaryColumnId }
2022-07-14 07:02:35 +00:00
searchKey = { this . props . searchText }
searchTableData = { this . handleSearchTable }
selectAllRow = { this . handleAllRowSelect }
selectedRowIndex = {
this . props . selectedRowIndex === undefined
? - 1
: this . props . selectedRowIndex
}
selectedRowIndices = { this . getSelectedRowIndices ( ) }
serverSidePaginationEnabled = { ! ! this . props . serverSidePaginationEnabled }
2023-06-01 17:26:05 +00:00
showConnectDataOverlay = {
primaryColumns &&
! Object . keys ( primaryColumns ) . length &&
this . props . renderMode === RenderModes . CANVAS
}
2022-07-14 07:02:35 +00:00
sortTableColumn = { this . handleColumnSorting }
2023-03-30 04:54:29 +00:00
tableData = { finalTableData }
2022-07-14 07:02:35 +00:00
totalRecordsCount = { totalRecordsCount }
triggerRowSelection = { this . props . triggerRowSelection }
unSelectAllRow = { this . unSelectAllRow }
updatePageNo = { this . updatePageNumber }
2022-10-14 04:53:31 +00:00
variant = { this . props . variant }
2022-07-14 07:02:35 +00:00
widgetId = { this . props . widgetId }
widgetName = { this . props . widgetName }
width = { componentWidth }
/ >
< / Suspense >
) ;
}
2023-02-15 11:42:46 +00:00
/ * *
* Function to update or add the tableWidgetColumnOrder key in the local storage
* tableWidgetColumnOrder = {
* < widget - id > : {
* columnOrder : [ ] ,
* leftOrder : [ ] ,
* rightOrder : [ ] ,
* }
* }
* /
persistColumnOrder = (
newColumnOrder : string [ ] ,
leftOrder : string [ ] ,
rightOrder : string [ ] ,
) = > {
const widgetId = this . props . widgetId ;
const localTableWidgetColumnOrder = localStorage . getItem (
TABLE_COLUMN_ORDER_KEY ,
) ;
let newTableColumnOrder ;
if ( localTableWidgetColumnOrder ) {
try {
let parsedTableWidgetColumnOrder = JSON . parse (
localTableWidgetColumnOrder ,
) ;
let columnOrder ;
if ( newColumnOrder ) {
columnOrder = newColumnOrder ;
} else if ( parsedTableWidgetColumnOrder [ widgetId ] ) {
columnOrder = parsedTableWidgetColumnOrder [ widgetId ] ;
} else {
columnOrder = this . props . columnOrder ;
}
parsedTableWidgetColumnOrder = {
. . . parsedTableWidgetColumnOrder ,
[ widgetId ] : {
columnOrder ,
columnUpdatedAt : this.props.columnUpdatedAt ,
leftOrder ,
rightOrder ,
} ,
} ;
newTableColumnOrder = parsedTableWidgetColumnOrder ;
} catch ( e ) {
log . debug ( "Unable to parse local column order:" , { e } ) ;
}
} else {
const tableWidgetColumnOrder = {
[ widgetId ] : {
columnOrder : newColumnOrder ,
columnUpdatedAt : this.props.columnUpdatedAt ,
leftOrder ,
rightOrder ,
} ,
} ;
newTableColumnOrder = tableWidgetColumnOrder ;
}
localStorage . setItem (
TABLE_COLUMN_ORDER_KEY ,
JSON . stringify ( newTableColumnOrder ) ,
) ;
} ;
handleColumnFreeze = ( columnName : string , sticky? : StickyType ) = > {
if ( this . props . columnOrder ) {
let newColumnOrder ;
const localTableColumnOrder = getColumnOrderByWidgetIdFromLS (
this . props . widgetId ,
) ;
if ( this . props . renderMode === RenderModes . CANVAS ) {
newColumnOrder = generateNewColumnOrderFromStickyValue (
this . props . primaryColumns ,
this . props . columnOrder ,
columnName ,
sticky ,
) ;
// Updating these properties in batch so that undo/redo gets executed in a combined way.
super . batchUpdateWidgetProperty (
{
modify : {
[ ` primaryColumns. ${ columnName } .sticky ` ] : sticky ,
columnOrder : newColumnOrder ,
} ,
} ,
true ,
) ;
} else if (
localTableColumnOrder &&
this . props . renderMode === RenderModes . PAGE
) {
const { leftOrder , rightOrder } = localTableColumnOrder ;
newColumnOrder = generateLocalNewColumnOrderFromStickyValue (
localTableColumnOrder . columnOrder ,
columnName ,
sticky ,
leftOrder ,
rightOrder ,
) ;
const updatedOrders = updateAndSyncTableLocalColumnOrders (
columnName ,
leftOrder ,
rightOrder ,
sticky ,
) ;
this . persistColumnOrder (
newColumnOrder ,
updatedOrders . leftOrder ,
updatedOrders . rightOrder ,
) ;
2023-06-27 05:15:41 +00:00
super . batchUpdateWidgetProperty (
{
modify : {
[ ` primaryColumns. ${ columnName } .sticky ` ] : sticky ,
columnOrder : newColumnOrder ,
} ,
} ,
true ,
) ;
2023-02-15 11:42:46 +00:00
}
}
} ;
2022-07-14 07:02:35 +00:00
handleReorderColumn = ( columnOrder : string [ ] ) = > {
columnOrder = columnOrder . map ( ( alias ) = > this . getColumnIdByAlias ( alias ) ) ;
2023-06-27 05:15:41 +00:00
if (
this . props . canFreezeColumn &&
this . props . renderMode === RenderModes . PAGE
) {
const localTableColumnOrder = getColumnOrderByWidgetIdFromLS (
this . props . widgetId ,
) ;
if ( localTableColumnOrder ) {
const { leftOrder , rightOrder } = localTableColumnOrder ;
this . persistColumnOrder ( columnOrder , leftOrder , rightOrder ) ;
} else {
this . persistColumnOrder ( columnOrder , [ ] , [ ] ) ;
2023-02-15 11:42:46 +00:00
}
2022-07-14 07:02:35 +00:00
}
2023-06-27 05:15:41 +00:00
super . updateWidgetProperty ( "columnOrder" , columnOrder ) ;
2022-07-14 07:02:35 +00:00
} ;
handleColumnSorting = ( columnAccessor : string , isAsc : boolean ) = > {
const columnId = this . getColumnIdByAlias ( columnAccessor ) ;
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates , pushBatchMetaUpdates } = this . props ;
this . pushResetSelectedRowIndexUpdates ( false ) ;
2022-07-14 07:02:35 +00:00
let sortOrderProps ;
if ( columnId ) {
sortOrderProps = {
column : columnId ,
order : isAsc ? SortOrderTypes.asc : SortOrderTypes.desc ,
} ;
} else {
sortOrderProps = {
column : "" ,
order : null ,
} ;
}
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "sortOrder" , sortOrderProps , {
2022-07-14 07:02:35 +00:00
triggerPropertyName : "onSort" ,
dynamicString : this.props.onSort ,
event : {
type : EventType . ON_SORT ,
} ,
} ) ;
2023-03-30 04:54:29 +00:00
commitBatchMetaUpdates ( ) ;
2022-07-14 07:02:35 +00:00
} ;
handleResizeColumn = ( columnWidthMap : { [ key : string ] : number } ) = > {
if ( this . props . renderMode === RenderModes . CANVAS ) {
super . updateWidgetProperty ( "columnWidthMap" , columnWidthMap ) ;
} else {
2023-03-30 04:54:29 +00:00
//single action no need to batch
2022-07-14 07:02:35 +00:00
this . props . updateWidgetMetaProperty ( "columnWidthMap" , columnWidthMap ) ;
}
} ;
handleSearchTable = ( searchKey : any ) = > {
2023-03-30 04:54:29 +00:00
const {
commitBatchMetaUpdates ,
multiRowSelection ,
onSearchTextChanged ,
pushBatchMetaUpdates ,
} = this . props ;
2022-07-14 07:02:35 +00:00
/ *
* Clear rowSelection to avoid selecting filtered rows
* based on stale selection indices
* /
if ( multiRowSelection ) {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "selectedRowIndices" , [ ] ) ;
2022-07-14 07:02:35 +00:00
} else {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "selectedRowIndex" , - 1 ) ;
2022-07-14 07:02:35 +00:00
}
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "pageNo" , 1 ) ;
2023-04-13 10:08:46 +00:00
this . updatePaginationDirectionFlags ( PaginationDirection . INITIAL ) ;
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "searchText" , searchKey , {
2022-07-14 07:02:35 +00:00
triggerPropertyName : "onSearchTextChanged" ,
dynamicString : onSearchTextChanged ,
event : {
type : EventType . ON_SEARCH ,
} ,
} ) ;
2023-03-30 04:54:29 +00:00
commitBatchMetaUpdates ( ) ;
2022-07-14 07:02:35 +00:00
} ;
2023-03-30 04:54:29 +00:00
/ * *
* This function just pushes the meta update
* /
pushOnColumnEvent = ( {
rowIndex ,
action ,
onComplete = noop ,
triggerPropertyName ,
eventType ,
row ,
additionalData = { } ,
} : OnColumnEventArgs ) = > {
const { filteredTableData = [ ] , pushBatchMetaUpdates } = this . props ;
const currentRow = row || filteredTableData [ rowIndex ] ;
pushBatchMetaUpdates (
"triggeredRowIndex" ,
currentRow ? . [ ORIGINAL_INDEX_KEY ] ,
{
triggerPropertyName : triggerPropertyName ,
dynamicString : action ,
event : {
type : eventType ,
callback : onComplete ,
} ,
globalContext : { currentRow , . . . additionalData } ,
} ,
) ;
} ;
2022-07-14 07:02:35 +00:00
/ *
* Function to handle customColumn button type click interactions
* /
onColumnEvent = ( {
rowIndex ,
action ,
onComplete = noop ,
triggerPropertyName ,
eventType ,
row ,
2022-09-30 04:03:53 +00:00
additionalData = { } ,
2022-07-14 07:02:35 +00:00
} : OnColumnEventArgs ) = > {
2023-03-30 04:54:29 +00:00
if ( action ) {
const { commitBatchMetaUpdates } = this . props ;
2022-07-14 07:02:35 +00:00
2023-03-30 04:54:29 +00:00
this . pushOnColumnEvent ( {
rowIndex ,
action ,
onComplete ,
triggerPropertyName ,
eventType ,
row ,
additionalData ,
} ) ;
commitBatchMetaUpdates ( ) ;
} else {
onComplete ( ) ;
2022-07-14 07:02:35 +00:00
}
} ;
onDropdownOptionSelect = ( action : string ) = > {
super . executeAction ( {
dynamicString : action ,
event : {
type : EventType . ON_OPTION_CHANGE ,
} ,
} ) ;
} ;
handleAllRowSelect = ( pageData : Record < string , unknown > [ ] ) = > {
if ( this . props . multiRowSelection ) {
const selectedRowIndices = pageData . map (
( row : Record < string , unknown > ) = > row . index ,
) ;
2023-03-30 04:54:29 +00:00
//single action no need to batch
2022-07-14 07:02:35 +00:00
this . props . updateWidgetMetaProperty (
"selectedRowIndices" ,
selectedRowIndices ,
) ;
}
} ;
handleRowClick = ( row : Record < string , unknown > , selectedIndex : number ) = > {
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 { multiRowSelection , selectedRowIndex , selectedRowIndices } =
this . props ;
2023-03-30 04:54:29 +00:00
// no need to batch actions here because it a time only one will execute
2022-07-14 07:02:35 +00:00
if ( multiRowSelection ) {
let indices : Array < number > ;
if ( _ . isArray ( selectedRowIndices ) ) {
indices = [ . . . selectedRowIndices ] ;
} else {
indices = [ ] ;
}
/ *
* Deselect if the index is already present
* /
if ( indices . includes ( selectedIndex ) ) {
indices . splice ( indices . indexOf ( selectedIndex ) , 1 ) ;
this . props . updateWidgetMetaProperty ( "selectedRowIndices" , indices ) ;
} else {
/ *
* select if the index is not present already
* /
indices . push ( selectedIndex ) ;
this . props . updateWidgetMetaProperty ( "selectedRowIndices" , indices , {
triggerPropertyName : "onRowSelected" ,
dynamicString : this.props.onRowSelected ,
event : {
type : EventType . ON_ROW_SELECTED ,
} ,
} ) ;
}
} else {
let index ;
if ( isNumber ( selectedRowIndex ) ) {
index = selectedRowIndex ;
} else {
index = - 1 ;
}
if ( index !== selectedIndex ) {
this . props . updateWidgetMetaProperty ( "selectedRowIndex" , selectedIndex , {
triggerPropertyName : "onRowSelected" ,
dynamicString : this.props.onRowSelected ,
event : {
type : EventType . ON_ROW_SELECTED ,
} ,
} ) ;
} else {
this . props . updateWidgetMetaProperty ( "selectedRowIndex" , - 1 ) ;
}
}
} ;
updatePageNumber = ( pageNo : number , event? : EventType ) = > {
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates , pushBatchMetaUpdates } = this . props ;
2023-04-13 10:08:46 +00:00
const paginationDirection =
event == EventType . ON_NEXT_PAGE
? PaginationDirection . NEXT_PAGE
: PaginationDirection . PREVIOUS_PAGE ;
this . updatePaginationDirectionFlags ( paginationDirection ) ;
2022-07-14 07:02:35 +00:00
if ( event ) {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "pageNo" , pageNo , {
2022-07-14 07:02:35 +00:00
triggerPropertyName : "onPageChange" ,
dynamicString : this.props.onPageChange ,
event : {
type : event ,
} ,
} ) ;
} else {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "pageNo" , pageNo ) ;
2022-07-14 07:02:35 +00:00
}
if ( this . props . onPageChange ) {
2023-03-30 04:54:29 +00:00
this . pushResetSelectedRowIndexUpdates ( ) ;
2022-07-14 07:02:35 +00:00
}
2023-03-30 04:54:29 +00:00
commitBatchMetaUpdates ( ) ;
2022-07-14 07:02:35 +00:00
} ;
2023-04-13 10:08:46 +00:00
updatePaginationDirectionFlags = ( direction? : PaginationDirection ) = > {
const { pushBatchMetaUpdates } = this . props ;
let previousButtonFlag = false ;
let nextButtonFlag = false ;
if ( direction ) {
switch ( direction ) {
case PaginationDirection . INITIAL : {
previousButtonFlag = false ;
nextButtonFlag = false ;
break ;
}
case PaginationDirection . NEXT_PAGE : {
nextButtonFlag = true ;
break ;
}
case PaginationDirection . PREVIOUS_PAGE : {
previousButtonFlag = true ;
break ;
}
}
}
pushBatchMetaUpdates ( "previousPageVisited" , previousButtonFlag ) ;
pushBatchMetaUpdates ( "nextPageVisited" , nextButtonFlag ) ;
} ;
2022-07-14 07:02:35 +00:00
handleNextPageClick = ( ) = > {
const pageNo = ( this . props . pageNo || 1 ) + 1 ;
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates , pushBatchMetaUpdates } = this . props ;
2022-07-14 07:02:35 +00:00
2023-04-13 10:08:46 +00:00
this . updatePaginationDirectionFlags ( PaginationDirection . NEXT_PAGE ) ;
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "pageNo" , pageNo , {
2022-07-14 07:02:35 +00:00
triggerPropertyName : "onPageChange" ,
dynamicString : this.props.onPageChange ,
event : {
type : EventType . ON_NEXT_PAGE ,
} ,
} ) ;
if ( this . props . onPageChange ) {
2023-03-30 04:54:29 +00:00
this . pushResetSelectedRowIndexUpdates ( ) ;
2022-07-14 07:02:35 +00:00
}
2023-03-30 04:54:29 +00:00
commitBatchMetaUpdates ( ) ;
2022-07-14 07:02:35 +00:00
} ;
2023-03-30 04:54:29 +00:00
pushResetSelectedRowIndexUpdates = ( skipDefault? : boolean ) = > {
const { pushBatchMetaUpdates } = this . props ;
2022-07-14 07:02:35 +00:00
const {
defaultSelectedRowIndex ,
defaultSelectedRowIndices ,
multiRowSelection ,
} = this . props ;
if ( multiRowSelection ) {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates (
2022-07-14 07:02:35 +00:00
"selectedRowIndices" ,
skipDefault ? [ ] : defaultSelectedRowIndices ,
) ;
} else {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates (
2022-07-14 07:02:35 +00:00
"selectedRowIndex" ,
skipDefault ? - 1 : defaultSelectedRowIndex ,
) ;
}
} ;
unSelectAllRow = ( ) = > {
this . props . updateWidgetMetaProperty ( "selectedRowIndices" , [ ] ) ;
} ;
handlePrevPageClick = ( ) = > {
const pageNo = ( this . props . pageNo || 1 ) - 1 ;
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates , pushBatchMetaUpdates } = this . props ;
2022-07-14 07:02:35 +00:00
if ( pageNo >= 1 ) {
2023-04-13 10:08:46 +00:00
this . updatePaginationDirectionFlags ( PaginationDirection . PREVIOUS_PAGE ) ;
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "pageNo" , pageNo , {
2022-07-14 07:02:35 +00:00
triggerPropertyName : "onPageChange" ,
dynamicString : this.props.onPageChange ,
event : {
type : EventType . ON_PREV_PAGE ,
} ,
} ) ;
if ( this . props . onPageChange ) {
2023-03-30 04:54:29 +00:00
this . pushResetSelectedRowIndexUpdates ( ) ;
2022-07-14 07:02:35 +00:00
}
}
2023-03-30 04:54:29 +00:00
commitBatchMetaUpdates ( ) ;
2022-07-14 07:02:35 +00:00
} ;
getColumnIdByAlias ( alias : string ) {
const { primaryColumns } = this . props ;
if ( primaryColumns ) {
const column = Object . values ( primaryColumns ) . find (
( column ) = > column . alias === alias ,
) ;
if ( column ) {
return column . id ;
}
}
return alias ;
}
getColumnByOriginalId ( originalId : string ) {
return Object . values ( this . props . primaryColumns ) . find ( ( column ) = > {
return column . originalId === originalId ;
} ) ;
}
2023-03-30 04:54:29 +00:00
pushTransientTableDataActionsUpdates = ( data : TransientDataPayload ) = > {
2022-11-05 09:54:20 +00:00
const { __originalIndex__ , . . . transientData } = data ;
2023-03-30 04:54:29 +00:00
const { pushBatchMetaUpdates } = this . props ;
2022-07-14 07:02:35 +00:00
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "transientTableData" , {
2022-07-14 07:02:35 +00:00
. . . this . props . transientTableData ,
2022-11-05 09:54:20 +00:00
[ __originalIndex__ ] : {
. . . this . props . transientTableData [ __originalIndex__ ] ,
2022-07-14 07:02:35 +00:00
. . . transientData ,
} ,
} ) ;
2022-11-25 04:39:59 +00:00
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "updatedRowIndex" , __originalIndex__ ) ;
2022-07-14 07:02:35 +00:00
} ;
2022-11-05 09:54:20 +00:00
removeRowFromTransientTableData = ( index : number ) = > {
2022-07-14 07:02:35 +00:00
const newTransientTableData = clone ( this . props . transientTableData ) ;
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates , pushBatchMetaUpdates } = this . props ;
2022-07-14 07:02:35 +00:00
if ( newTransientTableData ) {
2022-11-05 09:54:20 +00:00
delete newTransientTableData [ index ] ;
2022-07-14 07:02:35 +00:00
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "transientTableData" , newTransientTableData ) ;
2022-07-14 07:02:35 +00:00
}
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "updatedRowIndex" , - 1 ) ;
commitBatchMetaUpdates ( ) ;
2022-07-14 07:02:35 +00:00
} ;
getRowOriginalIndex = ( index : number ) = > {
const { filteredTableData } = this . props ;
if ( filteredTableData ) {
const row = filteredTableData [ index ] ;
if ( row ) {
return row [ ORIGINAL_INDEX_KEY ] ;
}
}
return - 1 ;
} ;
onBulkEditSave = ( ) = > {
this . props . updateWidgetMetaProperty (
"transientTableData" ,
this . props . transientTableData ,
{
triggerPropertyName : "onBulkSave" ,
dynamicString : this.props.onBulkSave ,
event : {
type : EventType . ON_BULK_SAVE ,
} ,
} ,
) ;
} ;
onBulkEditDiscard = ( ) = > {
this . props . updateWidgetMetaProperty (
"transientTableData" ,
{ } ,
{
triggerPropertyName : "onBulkDiscard" ,
dynamicString : this.props.onBulkDiscard ,
event : {
type : EventType . ON_BULK_DISCARD ,
} ,
} ,
) ;
} ;
2022-09-30 04:03:53 +00:00
renderCell = ( props : any ) = > {
const column =
this . getColumnByOriginalId (
props . cell . column . columnProperties . originalId ,
) || props . cell . column . columnProperties ;
2022-11-05 09:54:20 +00:00
const rowIndex = props . cell . row . index ;
/ *
* We don 't need to render cells that don' t display data ( button , iconButton , etc )
* /
if (
this . props . isAddRowInProgress &&
rowIndex === 0 &&
ActionColumnTypes . includes ( column . columnType )
) {
return < CellWrapper / > ;
}
2022-07-14 07:02:35 +00:00
const isHidden = ! column . isVisible ;
const {
filteredTableData = [ ] ,
multiRowSelection ,
selectedRowIndex ,
selectedRowIndices ,
compactMode = CompactModeTypes . DEFAULT ,
} = this . props ;
2022-11-05 09:54:20 +00:00
let row ;
let originalIndex : number ;
2022-07-14 07:02:35 +00:00
2022-11-05 09:54:20 +00:00
/ *
* In add new row flow , a temporary row is injected at the top of the tableData , which doesn ' t
* have original row index value . so we are using - 1 as the value
* /
if ( this . props . isAddRowInProgress ) {
row = filteredTableData [ rowIndex - 1 ] ;
originalIndex = rowIndex === 0 ? - 1 : row [ ORIGINAL_INDEX_KEY ] ? ? rowIndex ;
} else {
row = filteredTableData [ rowIndex ] ;
originalIndex = row [ ORIGINAL_INDEX_KEY ] ? ? rowIndex ;
}
2022-07-14 07:02:35 +00:00
2023-04-06 18:28:24 +00:00
const isNewRow = this . props . isAddRowInProgress && rowIndex === 0 ;
2022-11-05 09:54:20 +00:00
/ *
* cellProperties order or size does not change when filter / sorting / grouping is applied
* on the data thus original index is needed to identify the column ' s cell property .
* /
2023-04-06 18:28:24 +00:00
const cellProperties = getCellProperties ( column , originalIndex , isNewRow ) ;
2022-07-14 07:02:35 +00:00
let isSelected = false ;
if ( this . props . transientTableData ) {
2022-09-30 04:03:53 +00:00
cellProperties . hasUnsavedChanges =
2022-07-14 07:02:35 +00:00
this . props . transientTableData . hasOwnProperty ( originalIndex ) &&
this . props . transientTableData [ originalIndex ] . hasOwnProperty (
props . cell . column . columnProperties . alias ,
) ;
}
if ( multiRowSelection ) {
isSelected =
_ . isArray ( selectedRowIndices ) && selectedRowIndices . includes ( rowIndex ) ;
} else {
isSelected = selectedRowIndex === rowIndex ;
}
const isColumnEditable =
column . isEditable && isColumnTypeEditable ( column . columnType ) ;
2022-09-16 04:34:11 +00:00
const alias = props . cell . column . columnProperties . alias ;
2022-11-05 09:54:20 +00:00
const isCellEditable = isColumnEditable && cellProperties . isCellEditable ;
2022-09-08 11:05:59 +00:00
const isCellEditMode =
2022-11-05 09:54:20 +00:00
( props . cell . column . alias === this . props . editableCell ? . column &&
rowIndex === this . props . editableCell ? . index ) ||
( isNewRow && isColumnEditable ) ;
const shouldDisableEdit =
( this . props . inlineEditingSaveOption ===
InlineEditingSaveOptions . ROW_LEVEL &&
this . props . updatedRowIndices . length &&
this . props . updatedRowIndices . indexOf ( originalIndex ) === - 1 ) ||
( this . hasInvalidColumnCell ( ) && ! isNewRow ) ;
const disabledEditMessage = ` Save or discard the ${
this . props . isAddRowInProgress ? "newly added" : "unsaved"
} row to start editing here ` ;
if ( this . props . isAddRowInProgress ) {
cellProperties . isCellDisabled = rowIndex !== 0 ;
if ( rowIndex === 0 ) {
cellProperties . cellBackground = "" ;
}
}
2022-07-14 07:02:35 +00:00
switch ( column . columnType ) {
case ColumnTypes . BUTTON :
return (
< ButtonCell
allowCellWrapping = { cellProperties . allowCellWrapping }
cellBackground = { cellProperties . cellBackground }
columnActions = { [
{
backgroundColor :
cellProperties . buttonColor || this . props . accentColor ,
eventType : EventType.ON_CLICK ,
id : column.id ,
isVisible : true ,
label : cellProperties.buttonLabel || DEFAULT_BUTTON_LABEL ,
dynamicTrigger : column.onClick || "" ,
variant : cellProperties.buttonVariant ,
borderRadius :
cellProperties . borderRadius || this . props . borderRadius ,
boxShadow : cellProperties.boxShadow ,
} ,
] }
compactMode = { compactMode }
fontStyle = { cellProperties . fontStyle }
horizontalAlignment = { cellProperties . horizontalAlignment }
2022-11-05 09:54:20 +00:00
isCellDisabled = { cellProperties . isCellDisabled }
2022-07-14 07:02:35 +00:00
isCellVisible = { cellProperties . isCellVisible ? ? true }
isDisabled = { ! ! cellProperties . isDisabled }
isHidden = { isHidden }
isSelected = { isSelected }
onCommandClick = { ( action : string , onComplete : ( ) = > void ) = >
this . onColumnEvent ( {
rowIndex ,
action ,
onComplete ,
triggerPropertyName : "onClick" ,
eventType : EventType.ON_CLICK ,
} )
}
textColor = { cellProperties . textColor }
textSize = { cellProperties . textSize }
verticalAlignment = { cellProperties . verticalAlignment }
/ >
) ;
case ColumnTypes . EDIT_ACTIONS :
return (
< EditActionCell
allowCellWrapping = { cellProperties . allowCellWrapping }
cellBackground = { cellProperties . cellBackground }
columnActions = { [
{
id : EditableCellActions.SAVE ,
label : cellProperties.saveActionLabel ,
dynamicTrigger : column.onSave || "" ,
eventType : EventType.ON_ROW_SAVE ,
iconName : cellProperties.saveActionIconName ,
variant : cellProperties.saveButtonVariant ,
backgroundColor :
cellProperties . saveButtonColor || this . props . accentColor ,
iconAlign : cellProperties.saveIconAlign ,
borderRadius :
cellProperties . saveBorderRadius || this . props . borderRadius ,
isVisible : cellProperties.isSaveVisible ,
2022-09-13 05:41:59 +00:00
isDisabled :
2022-11-05 09:54:20 +00:00
cellProperties . isSaveDisabled || this . hasInvalidColumnCell ( ) ,
2022-07-14 07:02:35 +00:00
boxShadow : cellProperties.boxShadow ,
} ,
{
id : EditableCellActions.DISCARD ,
label : cellProperties.discardActionLabel ,
dynamicTrigger : column.onDiscard || "" ,
eventType : EventType.ON_ROW_DISCARD ,
iconName : cellProperties.discardActionIconName ,
variant : cellProperties.discardButtonVariant ,
backgroundColor :
cellProperties . discardButtonColor || this . props . accentColor ,
iconAlign : cellProperties.discardIconAlign ,
borderRadius :
cellProperties . discardBorderRadius || this . props . borderRadius ,
isVisible : cellProperties.isDiscardVisible ,
2022-09-13 05:41:59 +00:00
isDisabled :
cellProperties . isDiscardDisabled ||
2022-11-05 09:54:20 +00:00
this . hasInvalidColumnCell ( ) ,
2022-07-14 07:02:35 +00:00
boxShadow : cellProperties.boxShadow ,
} ,
] }
compactMode = { compactMode }
fontStyle = { cellProperties . fontStyle }
horizontalAlignment = { cellProperties . horizontalAlignment }
2022-11-05 09:54:20 +00:00
isCellDisabled = { cellProperties . isCellDisabled }
isCellVisible = { cellProperties . isCellVisible }
2022-07-14 07:02:35 +00:00
isHidden = { isHidden }
isSelected = { isSelected }
onCommandClick = { (
action : string ,
onComplete : ( ) = > void ,
eventType : EventType ,
) = >
this . onColumnEvent ( {
rowIndex ,
action ,
onComplete ,
triggerPropertyName : "onClick" ,
eventType : eventType ,
} )
}
onDiscard = { ( ) = >
this . removeRowFromTransientTableData ( originalIndex )
}
textColor = { cellProperties . textColor }
textSize = { cellProperties . textSize }
verticalAlignment = { cellProperties . verticalAlignment }
/ >
) ;
2022-09-30 04:03:53 +00:00
case ColumnTypes . SELECT :
return (
< SelectCell
accentColor = { this . props . accentColor }
alias = { props . cell . column . columnProperties . alias }
allowCellWrapping = { cellProperties . allowCellWrapping }
2022-11-05 09:54:20 +00:00
autoOpen = { ! this . props . isAddRowInProgress }
2022-09-30 04:03:53 +00:00
borderRadius = { cellProperties . borderRadius }
cellBackground = { cellProperties . cellBackground }
columnType = { column . columnType }
compactMode = { compactMode }
disabledEditIcon = {
2022-11-05 09:54:20 +00:00
shouldDisableEdit || this . props . isAddRowInProgress
2022-09-30 04:03:53 +00:00
}
2022-11-05 09:54:20 +00:00
disabledEditIconMessage = { disabledEditMessage }
2022-09-30 04:03:53 +00:00
filterText = {
this . props . selectColumnFilterText ? . [
this . props . editableCell ? . column || column . alias
]
}
fontStyle = { cellProperties . fontStyle }
hasUnsavedChanges = { cellProperties . hasUnsavedChanges }
horizontalAlignment = { cellProperties . horizontalAlignment }
2022-11-05 09:54:20 +00:00
isCellDisabled = { cellProperties . isCellDisabled }
2022-09-30 04:03:53 +00:00
isCellEditMode = { isCellEditMode }
2022-11-05 09:54:20 +00:00
isCellEditable = { isCellEditable }
2022-09-30 04:03:53 +00:00
isCellVisible = { cellProperties . isCellVisible ? ? true }
isEditable = { isColumnEditable }
isFilterable = { cellProperties . isFilterable }
isHidden = { isHidden }
2022-11-05 09:54:20 +00:00
isNewRow = { isNewRow }
2022-09-30 04:03:53 +00:00
key = { props . key }
onFilterChange = { this . onSelectFilterChange }
onFilterChangeActionString = { column . onFilterUpdate }
onItemSelect = { this . onOptionSelect }
onOptionSelectActionString = { column . onOptionChange }
2022-12-16 04:35:51 +00:00
options = { cellProperties . selectOptions }
2022-09-30 04:03:53 +00:00
placeholderText = { cellProperties . placeholderText }
resetFilterTextOnClose = { cellProperties . resetFilterTextOnClose }
rowIndex = { rowIndex }
serverSideFiltering = { cellProperties . serverSideFiltering }
tableWidth = { this . getComponentDimensions ( ) . componentWidth }
textColor = { cellProperties . textColor }
textSize = { cellProperties . textSize }
toggleCellEditMode = { this . toggleCellEditMode }
value = { props . cell . value }
verticalAlignment = { cellProperties . verticalAlignment }
width = {
this . props . columnWidthMap ? . [ column . id ] || DEFAULT_COLUMN_WIDTH
}
/ >
) ;
2022-07-14 07:02:35 +00:00
case ColumnTypes . IMAGE :
const onClick = column . onClick
? ( ) = >
this . onColumnEvent ( {
rowIndex ,
action : column.onClick ,
triggerPropertyName : "onClick" ,
eventType : EventType.ON_CLICK ,
} )
: noop ;
return (
< ImageCell
allowCellWrapping = { cellProperties . allowCellWrapping }
cellBackground = { cellProperties . cellBackground }
compactMode = { compactMode }
fontStyle = { cellProperties . fontStyle }
horizontalAlignment = { cellProperties . horizontalAlignment }
2022-10-14 12:21:54 +00:00
imageSize = { cellProperties . imageSize }
2022-11-05 09:54:20 +00:00
isCellDisabled = { cellProperties . isCellDisabled }
2022-07-14 07:02:35 +00:00
isCellVisible = { cellProperties . isCellVisible ? ? true }
isHidden = { isHidden }
isSelected = { isSelected }
onClick = { onClick }
textColor = { cellProperties . textColor }
textSize = { cellProperties . textSize }
value = { props . cell . value }
verticalAlignment = { cellProperties . verticalAlignment }
/ >
) ;
case ColumnTypes . MENU_BUTTON :
2022-12-30 10:52:11 +00:00
const getVisibleItems = ( rowIndex : number ) = > {
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 { configureMenuItems , menuItems , menuItemsSource , sourceData } =
cellProperties ;
2022-12-30 10:52:11 +00:00
if ( menuItemsSource === MenuItemsSource . STATIC && menuItems ) {
const visibleItems = Object . values ( menuItems ) ? . filter ( ( item ) = >
getBooleanPropertyValue ( item . isVisible , rowIndex ) ,
) ;
return visibleItems ? . length
? orderBy ( visibleItems , [ "index" ] , [ "asc" ] )
: [ ] ;
} else if (
menuItemsSource === MenuItemsSource . DYNAMIC &&
isArray ( sourceData ) &&
sourceData ? . length &&
configureMenuItems ? . config
) {
const { config } = configureMenuItems ;
const getValue = (
propertyName : keyof MenuItem ,
index : number ,
rowIndex : number ,
) = > {
const value = config [ propertyName ] ;
if ( isArray ( value ) && isArray ( value [ rowIndex ] ) ) {
return value [ rowIndex ] [ index ] ;
} else if ( isArray ( value ) ) {
return value [ index ] ;
}
return value ? ? null ;
} ;
const visibleItems = sourceData
. map ( ( item , index ) = > ( {
. . . item ,
id : index.toString ( ) ,
isVisible : getValue ( "isVisible" , index , rowIndex ) ,
isDisabled : getValue ( "isDisabled" , index , rowIndex ) ,
index : index ,
widgetId : "" ,
label : getValue ( "label" , index , rowIndex ) ,
onClick : config?.onClick ,
textColor : getValue ( "textColor" , index , rowIndex ) ,
backgroundColor : getValue ( "backgroundColor" , index , rowIndex ) ,
iconAlign : getValue ( "iconAlign" , index , rowIndex ) ,
iconColor : getValue ( "iconColor" , index , rowIndex ) ,
iconName : getValue ( "iconName" , index , rowIndex ) ,
} ) )
. filter ( ( item ) = > item . isVisible === true ) ;
return visibleItems ;
}
return [ ] ;
} ;
2022-07-14 07:02:35 +00:00
return (
< MenuButtonCell
allowCellWrapping = { cellProperties . allowCellWrapping }
borderRadius = {
cellProperties . borderRadius || this . props . borderRadius
}
boxShadow = { cellProperties . boxShadow }
cellBackground = { cellProperties . cellBackground }
compactMode = { compactMode }
2022-12-30 10:52:11 +00:00
configureMenuItems = { cellProperties . configureMenuItems }
2022-07-14 07:02:35 +00:00
fontStyle = { cellProperties . fontStyle }
2022-12-30 10:52:11 +00:00
getVisibleItems = { getVisibleItems }
2022-07-14 07:02:35 +00:00
horizontalAlignment = { cellProperties . horizontalAlignment }
iconAlign = { cellProperties . iconAlign }
iconName = { cellProperties . menuButtoniconName || undefined }
2022-11-05 09:54:20 +00:00
isCellDisabled = { cellProperties . isCellDisabled }
2022-07-14 07:02:35 +00:00
isCellVisible = { cellProperties . isCellVisible ? ? true }
isCompact = { ! ! cellProperties . isCompact }
isDisabled = { ! ! cellProperties . isDisabled }
isHidden = { isHidden }
isSelected = { isSelected }
label = { cellProperties . menuButtonLabel ? ? DEFAULT_MENU_BUTTON_LABEL }
menuColor = {
cellProperties . menuColor || this . props . accentColor || Colors . GREEN
}
menuItems = { cellProperties . menuItems }
2022-12-30 10:52:11 +00:00
menuItemsSource = { cellProperties . menuItemsSource }
2022-07-14 07:02:35 +00:00
menuVariant = { cellProperties . menuVariant ? ? DEFAULT_MENU_VARIANT }
2022-12-30 10:52:11 +00:00
onCommandClick = { (
action : string ,
index? : number ,
onComplete ? : ( ) = > void ,
) = > {
const additionalData : Record <
string ,
string | number | Record < string , unknown >
> = { } ;
if ( cellProperties ? . sourceData && _ . isNumber ( index ) ) {
additionalData . currentItem = cellProperties . sourceData [ index ] ;
additionalData . currentIndex = index ;
}
return this . onColumnEvent ( {
2022-07-14 07:02:35 +00:00
rowIndex ,
action ,
onComplete ,
triggerPropertyName : "onClick" ,
eventType : EventType.ON_CLICK ,
2022-12-30 10:52:11 +00:00
additionalData ,
} ) ;
} }
2022-08-01 05:01:34 +00:00
rowIndex = { originalIndex }
2022-12-30 10:52:11 +00:00
sourceData = { cellProperties . sourceData }
2022-07-14 07:02:35 +00:00
textColor = { cellProperties . textColor }
textSize = { cellProperties . textSize }
verticalAlignment = { cellProperties . verticalAlignment }
/ >
) ;
case ColumnTypes . ICON_BUTTON :
return (
< IconButtonCell
allowCellWrapping = { cellProperties . allowCellWrapping }
borderRadius = {
cellProperties . borderRadius || this . props . borderRadius
}
boxShadow = { cellProperties . boxShadow || "NONE" }
buttonColor = {
cellProperties . buttonColor ||
this . props . accentColor ||
Colors . GREEN
}
buttonVariant = { cellProperties . buttonVariant || "PRIMARY" }
cellBackground = { cellProperties . cellBackground }
columnActions = { [
{
id : column.id ,
dynamicTrigger : column.onClick || "" ,
} ,
] }
compactMode = { compactMode }
disabled = { ! ! cellProperties . isDisabled }
fontStyle = { cellProperties . fontStyle }
horizontalAlignment = { cellProperties . horizontalAlignment }
iconName = { ( cellProperties . iconName || IconNames . ADD ) as IconName }
2022-11-05 09:54:20 +00:00
isCellDisabled = { cellProperties . isCellDisabled }
2022-07-14 07:02:35 +00:00
isCellVisible = { cellProperties . isCellVisible ? ? true }
isHidden = { isHidden }
isSelected = { isSelected }
onCommandClick = { ( action : string , onComplete : ( ) = > void ) = >
this . onColumnEvent ( {
rowIndex ,
action ,
onComplete ,
triggerPropertyName : "onClick" ,
eventType : EventType.ON_CLICK ,
} )
}
textColor = { cellProperties . textColor }
textSize = { cellProperties . textSize }
verticalAlignment = { cellProperties . verticalAlignment }
/ >
) ;
case ColumnTypes . VIDEO :
return (
< VideoCell
allowCellWrapping = { cellProperties . allowCellWrapping }
cellBackground = { cellProperties . cellBackground }
compactMode = { compactMode }
fontStyle = { cellProperties . fontStyle }
horizontalAlignment = { cellProperties . horizontalAlignment }
2022-11-05 09:54:20 +00:00
isCellDisabled = { cellProperties . isCellDisabled }
2022-07-14 07:02:35 +00:00
isCellVisible = { cellProperties . isCellVisible ? ? true }
isHidden = { isHidden }
textColor = { cellProperties . textColor }
textSize = { cellProperties . textSize }
value = { props . cell . value }
verticalAlignment = { cellProperties . verticalAlignment }
/ >
) ;
2022-09-08 11:05:59 +00:00
case ColumnTypes . CHECKBOX :
return (
< CheckboxCell
accentColor = { this . props . accentColor }
borderRadius = {
cellProperties . borderRadius || this . props . borderRadius
}
cellBackground = { cellProperties . cellBackground }
compactMode = { compactMode }
disabledCheckbox = {
2022-11-05 09:54:20 +00:00
shouldDisableEdit || ( this . props . isAddRowInProgress && ! isNewRow )
2022-09-08 11:05:59 +00:00
}
2022-11-05 09:54:20 +00:00
disabledCheckboxMessage = { disabledEditMessage }
2022-09-30 04:03:53 +00:00
hasUnSavedChanges = { cellProperties . hasUnsavedChanges }
2022-09-08 11:05:59 +00:00
horizontalAlignment = { cellProperties . horizontalAlignment }
2022-11-05 09:54:20 +00:00
isCellDisabled = { cellProperties . isCellDisabled }
isCellEditable = { isCellEditable }
2022-09-08 11:05:59 +00:00
isCellVisible = { cellProperties . isCellVisible ? ? true }
isHidden = { isHidden }
2022-11-05 09:54:20 +00:00
onChange = { ( ) = >
this . onCheckChange (
column ,
props . cell . row . values ,
! props . cell . value ,
alias ,
originalIndex ,
2022-09-16 04:34:11 +00:00
rowIndex ,
2022-11-05 09:54:20 +00:00
)
}
2022-09-16 04:34:11 +00:00
value = { props . cell . value }
verticalAlignment = { cellProperties . verticalAlignment }
/ >
) ;
case ColumnTypes . SWITCH :
return (
< SwitchCell
accentColor = { this . props . accentColor }
cellBackground = { cellProperties . cellBackground }
compactMode = { compactMode }
disabledSwitch = {
2022-11-05 09:54:20 +00:00
shouldDisableEdit || ( this . props . isAddRowInProgress && ! isNewRow )
2022-09-16 04:34:11 +00:00
}
2022-11-05 09:54:20 +00:00
disabledSwitchMessage = { disabledEditMessage }
2022-09-30 04:03:53 +00:00
hasUnSavedChanges = { cellProperties . hasUnsavedChanges }
2022-09-16 04:34:11 +00:00
horizontalAlignment = { cellProperties . horizontalAlignment }
2022-11-05 09:54:20 +00:00
isCellDisabled = { cellProperties . isCellDisabled }
isCellEditable = { isCellEditable }
2022-09-16 04:34:11 +00:00
isCellVisible = { cellProperties . isCellVisible ? ? true }
isHidden = { isHidden }
2022-11-05 09:54:20 +00:00
onChange = { ( ) = >
this . onCheckChange (
column ,
props . cell . row . values ,
! props . cell . value ,
alias ,
originalIndex ,
2022-09-08 11:05:59 +00:00
rowIndex ,
2022-11-05 09:54:20 +00:00
)
}
2022-09-08 11:05:59 +00:00
value = { props . cell . value }
verticalAlignment = { cellProperties . verticalAlignment }
/ >
) ;
2023-01-16 09:23:56 +00:00
case ColumnTypes . DATE :
return (
< DateCell
accentColor = { this . props . accentColor }
alias = { props . cell . column . columnProperties . alias }
borderRadius = { this . props . borderRadius }
cellBackground = { cellProperties . cellBackground }
closeOnSelection
columnType = { column . columnType }
compactMode = { compactMode }
disabledEditIcon = {
shouldDisableEdit || this . props . isAddRowInProgress
}
disabledEditIconMessage = { disabledEditMessage }
firstDayOfWeek = { props . cell . column . columnProperties . firstDayOfWeek }
fontStyle = { cellProperties . fontStyle }
hasUnsavedChanges = { cellProperties . hasUnsavedChanges }
horizontalAlignment = { cellProperties . horizontalAlignment }
inputFormat = { cellProperties . inputFormat }
isCellDisabled = { cellProperties . isCellDisabled }
isCellEditMode = { isCellEditMode }
isCellEditable = { isCellEditable }
isCellVisible = { cellProperties . isCellVisible ? ? true }
isEditableCellValid = { this . isColumnCellValid ( alias ) }
isHidden = { isHidden }
isNewRow = { isNewRow }
isRequired = {
props . cell . column . columnProperties . validation
. isColumnEditableCellRequired
}
maxDate = { props . cell . column . columnProperties . validation . maxDate }
minDate = { props . cell . column . columnProperties . validation . minDate }
onCellTextChange = { this . onCellTextChange }
onDateSave = { this . onDateSave }
onDateSelectedString = {
props . cell . column . columnProperties . onDateSelected
}
outputFormat = { cellProperties . outputFormat }
rowIndex = { rowIndex }
shortcuts = { cellProperties . shortcuts }
tableWidth = { this . getComponentDimensions ( ) . componentWidth }
textColor = { cellProperties . textColor }
textSize = { cellProperties . textSize }
timePrecision = { cellProperties . timePrecision || TimePrecision . NONE }
toggleCellEditMode = { this . toggleCellEditMode }
updateNewRowValues = { this . updateNewRowValues }
validationErrorMessage = "This field is required"
value = { props . cell . value }
verticalAlignment = { cellProperties . verticalAlignment }
widgetId = { this . props . widgetId }
/ >
) ;
2022-07-14 07:02:35 +00:00
default :
2022-09-13 05:41:59 +00:00
let validationErrorMessage ;
if ( isCellEditMode ) {
validationErrorMessage =
column . validation . isColumnEditableCellRequired &&
2022-11-05 09:54:20 +00:00
( isNil ( props . cell . value ) || props . cell . value === "" )
2022-09-13 05:41:59 +00:00
? "This field is required"
: column . validation ? . errorMessage ;
}
2022-07-14 07:02:35 +00:00
return (
2022-09-30 04:03:53 +00:00
< PlainTextCell
2022-07-14 07:02:35 +00:00
accentColor = { this . props . accentColor }
alias = { props . cell . column . columnProperties . alias }
allowCellWrapping = { cellProperties . allowCellWrapping }
cellBackground = { cellProperties . cellBackground }
columnType = { column . columnType }
compactMode = { compactMode }
2022-11-05 09:54:20 +00:00
disabledEditIcon = {
shouldDisableEdit || this . props . isAddRowInProgress
}
disabledEditIconMessage = { disabledEditMessage }
2022-07-14 07:02:35 +00:00
displayText = { cellProperties . displayText }
fontStyle = { cellProperties . fontStyle }
2022-09-30 04:03:53 +00:00
hasUnsavedChanges = { cellProperties . hasUnsavedChanges }
2022-07-14 07:02:35 +00:00
horizontalAlignment = { cellProperties . horizontalAlignment }
2022-11-05 09:54:20 +00:00
isCellDisabled = { cellProperties . isCellDisabled }
2022-07-14 07:02:35 +00:00
isCellEditMode = { isCellEditMode }
2022-11-05 09:54:20 +00:00
isCellEditable = { isCellEditable }
2022-07-14 07:02:35 +00:00
isCellVisible = { cellProperties . isCellVisible ? ? true }
2022-11-05 09:54:20 +00:00
isEditableCellValid = { this . isColumnCellValid ( alias ) }
2022-07-14 07:02:35 +00:00
isHidden = { isHidden }
2022-11-05 09:54:20 +00:00
isNewRow = { isNewRow }
onCellTextChange = { this . onCellTextChange }
2022-07-14 07:02:35 +00:00
onSubmitString = { props . cell . column . columnProperties . onSubmit }
rowIndex = { rowIndex }
2022-09-30 04:03:53 +00:00
tableWidth = { this . getComponentDimensions ( ) . componentWidth }
2022-07-14 07:02:35 +00:00
textColor = { cellProperties . textColor }
textSize = { cellProperties . textSize }
toggleCellEditMode = { this . toggleCellEditMode }
2022-09-13 05:41:59 +00:00
validationErrorMessage = { validationErrorMessage }
2022-07-14 07:02:35 +00:00
value = { props . cell . value }
verticalAlignment = { cellProperties . verticalAlignment }
2022-09-13 05:41:59 +00:00
widgetId = { this . props . widgetId }
2022-07-14 07:02:35 +00:00
/ >
) ;
}
} ;
2022-11-05 09:54:20 +00:00
onCellTextChange = (
2022-09-13 05:41:59 +00:00
value : EditableCell [ "value" ] ,
inputValue : string ,
2022-11-05 09:54:20 +00:00
alias : string ,
2022-09-13 05:41:59 +00:00
) = > {
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates , pushBatchMetaUpdates } = this . props ;
2022-11-05 09:54:20 +00:00
if ( this . props . isAddRowInProgress ) {
this . updateNewRowValues ( alias , inputValue , value ) ;
} else {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "editableCell" , {
2022-11-05 09:54:20 +00:00
. . . this . props . editableCell ,
value : value ,
inputValue ,
2022-09-30 04:03:53 +00:00
} ) ;
2022-11-05 09:54:20 +00:00
if ( this . props . editableCell ? . column ) {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "columnEditableCellValue" , {
2022-11-05 09:54:20 +00:00
. . . this . props . columnEditableCellValue ,
[ this . props . editableCell ? . column ] : value ,
} ) ;
}
2023-03-30 04:54:29 +00:00
commitBatchMetaUpdates ( ) ;
2022-09-30 04:03:53 +00:00
}
2022-07-14 07:02:35 +00:00
} ;
toggleCellEditMode = (
enable : boolean ,
rowIndex : number ,
alias : string ,
value : string | number ,
2022-09-30 04:03:53 +00:00
onSubmit? : string ,
action? : EditableCellActions ,
2022-07-14 07:02:35 +00:00
) = > {
2022-11-05 09:54:20 +00:00
if ( this . props . isAddRowInProgress ) {
return ;
}
2022-07-14 07:02:35 +00:00
if ( enable ) {
2022-09-13 05:41:59 +00:00
if ( this . inlineEditTimer ) {
clearTimeout ( this . inlineEditTimer ) ;
2022-07-14 07:02:35 +00:00
}
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates , pushBatchMetaUpdates } = this . props ;
2022-07-14 07:02:35 +00:00
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "editableCell" , {
2022-07-14 07:02:35 +00:00
column : alias ,
index : rowIndex ,
value : value ,
// To revert back to previous on discard
initialValue : value ,
2022-09-13 05:41:59 +00:00
inputValue : value ,
} ) ;
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "columnEditableCellValue" , {
2022-09-13 05:41:59 +00:00
. . . this . props . columnEditableCellValue ,
[ alias ] : value ,
2022-07-14 07:02:35 +00:00
} ) ;
/ *
* We need to clear the selectedRowIndex and selectedRowIndices
* if the rows are sorted , to avoid selectedRow jumping to
* different page .
* /
if ( this . props . sortOrder . column ) {
if ( this . props . multiRowSelection ) {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "selectedRowIndices" , [ ] ) ;
2022-07-14 07:02:35 +00:00
} else {
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "selectedRowIndex" , - 1 ) ;
2022-07-14 07:02:35 +00:00
}
}
2023-03-30 04:54:29 +00:00
commitBatchMetaUpdates ( ) ;
2022-07-14 07:02:35 +00:00
} else {
if (
2022-11-05 09:54:20 +00:00
this . isColumnCellValid ( alias ) &&
2022-07-14 07:02:35 +00:00
action === EditableCellActions . SAVE &&
2022-09-15 07:47:15 +00:00
value !== this . props . editableCell ? . initialValue
2022-07-14 07:02:35 +00:00
) {
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates } = this . props ;
this . pushTransientTableDataActionsUpdates ( {
2022-11-05 09:54:20 +00:00
[ ORIGINAL_INDEX_KEY ] : this . getRowOriginalIndex ( rowIndex ) ,
2022-09-15 07:47:15 +00:00
[ alias ] : this . props . editableCell ? . value ,
2022-07-14 07:02:35 +00:00
} ) ;
2022-09-30 04:03:53 +00:00
if ( onSubmit && this . props . editableCell ? . column ) {
2023-03-30 04:54:29 +00:00
//since onSubmit is truthy that makes action truthy as well, so we can push this event
this . pushOnColumnEvent ( {
2022-07-14 07:02:35 +00:00
rowIndex : rowIndex ,
action : onSubmit ,
triggerPropertyName : "onSubmit" ,
eventType : EventType.ON_SUBMIT ,
row : {
. . . this . props . filteredTableData [ rowIndex ] ,
2022-09-30 04:03:53 +00:00
[ this . props . editableCell . column ] : this . props . editableCell . value ,
2022-07-14 07:02:35 +00:00
} ,
} ) ;
}
2023-03-30 04:54:29 +00:00
commitBatchMetaUpdates ( ) ;
2022-09-13 05:41:59 +00:00
this . clearEditableCell ( ) ;
} else if (
action === EditableCellActions . DISCARD ||
2022-09-15 07:47:15 +00:00
value === this . props . editableCell ? . initialValue
2022-09-13 05:41:59 +00:00
) {
this . clearEditableCell ( ) ;
2022-07-14 07:02:35 +00:00
}
2022-09-13 05:41:59 +00:00
}
} ;
2023-01-16 09:23:56 +00:00
onDateSave = (
rowIndex : number ,
alias : string ,
value : string ,
onSubmit? : string ,
) = > {
if ( this . isColumnCellValid ( alias ) ) {
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates } = this . props ;
this . pushTransientTableDataActionsUpdates ( {
2023-01-16 09:23:56 +00:00
[ ORIGINAL_INDEX_KEY ] : this . getRowOriginalIndex ( rowIndex ) ,
[ alias ] : value ,
} ) ;
if ( onSubmit && this . props . editableCell ? . column ) {
2023-03-30 04:54:29 +00:00
//since onSubmit is truthy this makes action truthy as well, so we can push this event
this . pushOnColumnEvent ( {
2023-01-16 09:23:56 +00:00
rowIndex : rowIndex ,
action : onSubmit ,
triggerPropertyName : "onSubmit" ,
eventType : EventType.ON_SUBMIT ,
row : {
. . . this . props . filteredTableData [ rowIndex ] ,
[ this . props . editableCell . column ] : value ,
} ,
} ) ;
}
2023-03-30 04:54:29 +00:00
commitBatchMetaUpdates ( ) ;
2023-01-16 09:23:56 +00:00
this . clearEditableCell ( ) ;
}
} ;
2023-03-30 04:54:29 +00:00
pushClearEditableCellsUpdates = ( ) = > {
const { pushBatchMetaUpdates } = this . props ;
pushBatchMetaUpdates ( "editableCell" , defaultEditableCell ) ;
pushBatchMetaUpdates ( "columnEditableCellValue" , { } ) ;
} ;
2023-01-16 09:23:56 +00:00
2022-09-13 05:41:59 +00:00
clearEditableCell = ( skipTimeout? : boolean ) = > {
const clear = ( ) = > {
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates } = this . props ;
this . pushClearEditableCellsUpdates ( ) ;
commitBatchMetaUpdates ( ) ;
2022-09-13 05:41:59 +00:00
} ;
2022-07-14 07:02:35 +00:00
2022-09-13 05:41:59 +00:00
if ( skipTimeout ) {
clear ( ) ;
} else {
2022-07-14 07:02:35 +00:00
/ *
* We need to let the evaulations compute derived property ( filteredTableData )
* before we clear the editableCell to avoid the text flickering
* /
2022-09-13 05:41:59 +00:00
this . inlineEditTimer = setTimeout ( clear , 100 ) ;
2022-07-14 07:02:35 +00:00
}
} ;
2022-09-13 05:41:59 +00:00
isColumnCellEditable = ( column : ColumnProperties , rowIndex : number ) = > {
return (
2022-09-15 07:47:15 +00:00
column . alias === this . props . editableCell ? . column &&
rowIndex === this . props . editableCell ? . index
2022-09-13 05:41:59 +00:00
) ;
} ;
2022-09-30 04:03:53 +00:00
onOptionSelect = (
2022-10-04 10:45:33 +00:00
value : string | number ,
2022-09-30 04:03:53 +00:00
rowIndex : number ,
column : string ,
action? : string ,
) = > {
2022-11-05 09:54:20 +00:00
if ( this . props . isAddRowInProgress ) {
this . updateNewRowValues ( column , value , value ) ;
} else {
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates , pushBatchMetaUpdates } = this . props ;
this . pushTransientTableDataActionsUpdates ( {
2022-11-05 09:54:20 +00:00
[ ORIGINAL_INDEX_KEY ] : this . getRowOriginalIndex ( rowIndex ) ,
[ column ] : value ,
} ) ;
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "editableCell" , defaultEditableCell ) ;
2022-09-30 04:03:53 +00:00
2022-11-05 09:54:20 +00:00
if ( action && this . props . editableCell ? . column ) {
2023-03-30 04:54:29 +00:00
//since action is truthy we can push this event
this . pushOnColumnEvent ( {
2022-11-05 09:54:20 +00:00
rowIndex ,
2023-03-30 04:54:29 +00:00
action ,
2022-11-05 09:54:20 +00:00
triggerPropertyName : "onOptionChange" ,
eventType : EventType.ON_OPTION_CHANGE ,
row : {
. . . this . props . filteredTableData [ rowIndex ] ,
[ this . props . editableCell . column ] : value ,
} ,
} ) ;
}
2023-03-30 04:54:29 +00:00
commitBatchMetaUpdates ( ) ;
2022-09-30 04:03:53 +00:00
}
} ;
onSelectFilterChange = (
text : string ,
rowIndex : number ,
serverSideFiltering : boolean ,
alias : string ,
action? : string ,
) = > {
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates , pushBatchMetaUpdates } = this . props ;
pushBatchMetaUpdates ( "selectColumnFilterText" , {
2022-09-30 04:03:53 +00:00
. . . this . props . selectColumnFilterText ,
[ alias ] : text ,
} ) ;
if ( action && serverSideFiltering ) {
2023-03-30 04:54:29 +00:00
//since action is truthy we can push this event
this . pushOnColumnEvent ( {
2022-09-30 04:03:53 +00:00
rowIndex ,
2023-03-30 04:54:29 +00:00
action ,
2022-09-30 04:03:53 +00:00
triggerPropertyName : "onFilterUpdate" ,
eventType : EventType.ON_FILTER_UPDATE ,
row : {
. . . this . props . filteredTableData [ rowIndex ] ,
} ,
additionalData : {
filterText : text ,
} ,
} ) ;
}
2023-03-30 04:54:29 +00:00
commitBatchMetaUpdates ( ) ;
2022-09-30 04:03:53 +00:00
} ;
2022-11-05 09:54:20 +00:00
onCheckChange = (
column : any ,
row : Record < string , unknown > ,
value : boolean ,
alias : string ,
originalIndex : number ,
rowIndex : number ,
) = > {
if ( this . props . isAddRowInProgress ) {
this . updateNewRowValues ( alias , value , value ) ;
} else {
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates } = this . props ;
this . pushTransientTableDataActionsUpdates ( {
2022-11-05 09:54:20 +00:00
[ ORIGINAL_INDEX_KEY ] : originalIndex ,
[ alias ] : value ,
} ) ;
2023-03-30 04:54:29 +00:00
commitBatchMetaUpdates ( ) ;
//cannot batch this update because we are not sure if it action is truthy or not
2022-11-05 09:54:20 +00:00
this . onColumnEvent ( {
rowIndex ,
action : column.onCheckChange ,
triggerPropertyName : "onCheckChange" ,
eventType : EventType.ON_CHECK_CHANGE ,
row : {
. . . row ,
[ alias ] : value ,
} ,
} ) ;
}
} ;
handleAddNewRowClick = ( ) = > {
const defaultNewRow = this . props . defaultNewRow || { } ;
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates , pushBatchMetaUpdates } = this . props ;
pushBatchMetaUpdates ( "isAddRowInProgress" , true ) ;
pushBatchMetaUpdates ( "newRowContent" , defaultNewRow ) ;
pushBatchMetaUpdates ( "newRow" , defaultNewRow ) ;
2022-11-05 09:54:20 +00:00
// New row gets added at the top of page 1 when client side pagination enabled
if ( ! this . props . serverSidePaginationEnabled ) {
2023-04-13 10:08:46 +00:00
this . updatePaginationDirectionFlags ( PaginationDirection . INITIAL ) ;
2022-11-05 09:54:20 +00:00
}
//Since we're adding a newRowContent thats not part of tableData, the index changes
// so we're resetting the row selection
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "selectedRowIndex" , - 1 ) ;
pushBatchMetaUpdates ( "selectedRowIndices" , [ ] ) ;
commitBatchMetaUpdates ( ) ;
2022-11-05 09:54:20 +00:00
} ;
handleAddNewRowAction = (
type : AddNewRowActions ,
onActionComplete : ( ) = > void ,
) = > {
let triggerPropertyName , action , eventType ;
const onComplete = ( ) = > {
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates , pushBatchMetaUpdates } = this . props ;
pushBatchMetaUpdates ( "isAddRowInProgress" , false ) ;
pushBatchMetaUpdates ( "newRowContent" , undefined ) ;
pushBatchMetaUpdates ( "newRow" , undefined ) ;
commitBatchMetaUpdates ( ) ;
2022-11-05 09:54:20 +00:00
onActionComplete ( ) ;
} ;
if ( type === AddNewRowActions . SAVE ) {
triggerPropertyName = "onAddNewRowSave" ;
action = this . props . onAddNewRowSave ;
eventType = EventType . ON_ADD_NEW_ROW_SAVE ;
} else {
triggerPropertyName = "onAddNewRowDiscard" ;
action = this . props . onAddNewRowDiscard ;
eventType = EventType . ON_ADD_NEW_ROW_DISCARD ;
}
if ( action ) {
super . executeAction ( {
triggerPropertyName : triggerPropertyName ,
dynamicString : action ,
event : {
type : eventType ,
callback : onComplete ,
} ,
} ) ;
} else {
onComplete ( ) ;
}
} ;
isColumnCellValid = ( columnsAlias : string ) = > {
if ( this . props . isEditableCellsValid ? . hasOwnProperty ( columnsAlias ) ) {
return this . props . isEditableCellsValid [ columnsAlias ] ;
}
return true ;
} ;
hasInvalidColumnCell = ( ) = > {
2022-11-18 10:54:35 +00:00
if ( isObject ( this . props . isEditableCellsValid ) ) {
return Object . values ( this . props . isEditableCellsValid ) . some ( ( d ) = > ! d ) ;
} else {
return false ;
}
2022-11-05 09:54:20 +00:00
} ;
updateNewRowValues = (
alias : string ,
value : unknown ,
parsedValue : unknown ,
) = > {
2023-03-30 04:54:29 +00:00
const { commitBatchMetaUpdates , pushBatchMetaUpdates } = this . props ;
2022-11-05 09:54:20 +00:00
/ *
* newRowContent holds whatever the user types while newRow holds the parsed value
* newRowContent is being used to populate the cell while newRow is being used
* for validations .
* /
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "newRowContent" , {
2022-11-05 09:54:20 +00:00
. . . this . props . newRowContent ,
[ alias ] : value ,
} ) ;
2023-03-30 04:54:29 +00:00
pushBatchMetaUpdates ( "newRow" , {
2022-11-05 09:54:20 +00:00
. . . this . props . newRow ,
[ alias ] : parsedValue ,
} ) ;
2023-03-30 04:54:29 +00:00
commitBatchMetaUpdates ( ) ;
2022-11-05 09:54:20 +00:00
} ;
2023-06-01 17:26:05 +00:00
onConnectData = ( ) = > {
if ( this . props . renderMode === RenderModes . CANVAS ) {
super . updateOneClickBindingOptionsVisibility ( true ) ;
}
} ;
2022-07-14 07:02:35 +00:00
}
export default TableWidgetV2 ;