2022-07-14 07:02:35 +00:00
import log from "loglevel" ;
2023-03-30 04:54:29 +00:00
import memoizeOne from "memoize-one" ;
2025-02-10 09:20:25 +00:00
import React , { lazy , Suspense } from "react" ;
2023-03-30 04:54:29 +00:00
2022-07-14 07:02:35 +00:00
import _ , {
2023-10-03 08:10:51 +00:00
filter ,
2022-07-14 07:02:35 +00:00
isArray ,
isEmpty ,
2023-10-03 08:10:51 +00:00
isNil ,
isNumber ,
2022-11-18 10:54:35 +00:00
isObject ,
2023-10-03 08:10:51 +00:00
isString ,
2023-06-01 17:26:05 +00:00
merge ,
2023-10-03 08:10:51 +00:00
orderBy ,
pickBy ,
union ,
without ,
xor ,
xorWith ,
2022-07-14 07:02:35 +00:00
} from "lodash" ;
2025-02-10 09:20:25 +00:00
import type { IconName } from "@blueprintjs/icons" ;
import { IconNames } from "@blueprintjs/icons" ;
import type { BatchPropertyUpdatePayload } from "actions/controlActions" ;
import Skeleton from "components/utils/Skeleton" ;
import { EventType } from "constants/AppsmithActionConstants/ActionConstants" ;
import { Colors } from "constants/Colors" ;
import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants" ;
2023-10-03 08:10:51 +00:00
import {
RenderModes ,
WIDGET_PADDING ,
WIDGET_TAGS ,
} from "constants/WidgetConstants" ;
2025-02-10 09:20:25 +00:00
import type { SetterConfig , Stylesheet } from "entities/AppTheming" ;
import equal from "fast-deep-equal/es6" ;
import {
FlexVerticalAlignment ,
ResponsiveBehavior ,
} from "layoutSystems/common/utils/constants" ;
2022-07-14 07:02:35 +00:00
import { noop , retryPromise } from "utils/AppsmithUtils" ;
2025-02-10 09:20:25 +00:00
import type { ExtraDef } from "utils/autocomplete/defCreatorUtils" ;
import { generateTypeDef } from "utils/autocomplete/defCreatorUtils" ;
import type { DynamicPath } from "utils/DynamicBindingUtils" ;
import { klonaRegularWithTelemetry } from "utils/helpers" ;
import localStorage from "utils/localStorage" ;
import type {
AnvilConfig ,
AutocompletionDefinitions ,
PropertyUpdates ,
SnipingModeProperty ,
} from "WidgetProvider/constants" ;
import type {
WidgetQueryConfig ,
WidgetQueryGenerationFormConfig ,
} from "WidgetQueryGenerators/types" ;
import type { WidgetProps , WidgetState } from "widgets/BaseWidget" ;
import BaseWidget from "widgets/BaseWidget" ;
import { TimePrecision } from "widgets/DatePickerWidget2/constants" ;
import type { MenuItem } from "widgets/MenuButtonWidget/constants" ;
import { MenuItemsSource } from "widgets/MenuButtonWidget/constants" ;
import {
DefaultAutocompleteDefinitions ,
sanitizeKey ,
} from "widgets/WidgetUtils" ;
import { ButtonCell } from "../component/cellComponents/ButtonCell" ;
import { CheckboxCell } from "../component/cellComponents/CheckboxCell" ;
import { DateCell } from "../component/cellComponents/DateCell" ;
import { EditActionCell } from "../component/cellComponents/EditActionsCell" ;
import HTMLCell from "../component/cellComponents/HTMLCell" ;
import { IconButtonCell } from "../component/cellComponents/IconButtonCell" ;
import { ImageCell } from "../component/cellComponents/ImageCell" ;
import { MenuButtonCell } from "../component/cellComponents/MenuButtonCell" ;
import PlainTextCell from "../component/cellComponents/PlainTextCell" ;
import { SelectCell } from "../component/cellComponents/SelectCell" ;
import { SwitchCell } from "../component/cellComponents/SwitchCell" ;
import { VideoCell } from "../component/cellComponents/VideoCell" ;
2023-10-03 08:10:51 +00:00
import type {
ColumnProperties ,
ReactTableColumnProps ,
ReactTableFilter ,
} from "../component/Constants" ;
import {
AddNewRowActions ,
CompactModeTypes ,
DEFAULT_FILTER ,
SORT_ORDER ,
SortOrderTypes ,
StickyType ,
} from "../component/Constants" ;
2025-02-10 09:20:25 +00:00
import { CellWrapper } from "../component/TableStyledWrappers" ;
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" ;
2022-11-05 09:54:20 +00:00
import {
ActionColumnTypes ,
2023-10-12 08:24:03 +00:00
ALLOW_TABLE_WIDGET_SERVER_SIDE_FILTERING ,
2022-07-14 07:02:35 +00:00
ColumnTypes ,
DEFAULT_BUTTON_LABEL ,
DEFAULT_COLUMN_WIDTH ,
DEFAULT_MENU_BUTTON_LABEL ,
DEFAULT_MENU_VARIANT ,
2023-10-03 08:10:51 +00:00
defaultEditableCell ,
2022-07-14 07:02:35 +00:00
EditableCellActions ,
InlineEditingSaveOptions ,
ORIGINAL_INDEX_KEY ,
2023-04-13 10:08:46 +00:00
PaginationDirection ,
2023-10-03 08:10:51 +00:00
TABLE_COLUMN_ORDER_KEY ,
2022-07-14 07:02:35 +00:00
} from "../constants" ;
2025-02-10 09:20:25 +00:00
import IconSVG from "../icon.svg" ;
import ThumbnailSVG from "../thumbnail.svg" ;
2022-07-14 07:02:35 +00:00
import derivedProperties from "./parseDerivedProperties" ;
2025-02-10 09:20:25 +00:00
import contentConfig from "./propertyConfig/contentConfig" ;
import styleConfig from "./propertyConfig/styleConfig" ;
import type { getColumns } from "./reactTableUtils/getColumnsPureFn" ;
import { getMemoiseGetColumnsWithLocalStorageFn } from "./reactTableUtils/getColumnsPureFn" ;
import type {
tableData ,
transformDataWithEditableCell ,
} from "./reactTableUtils/transformDataPureFn" ;
import { getMemoiseTransformDataWithEditableCell } from "./reactTableUtils/transformDataPureFn" ;
2022-07-14 07:02:35 +00:00
import {
2023-10-03 08:10:51 +00:00
createEditActionColumn ,
deleteLocalTableColumnOrderByWidgetId ,
generateLocalNewColumnOrderFromStickyValue ,
generateNewColumnOrderFromStickyValue ,
getAllStickyColumnsCount ,
2022-07-14 07:02:35 +00:00
getAllTableColumnKeys ,
2023-10-03 08:10:51 +00:00
getBooleanPropertyValue ,
getCellProperties ,
getColumnOrderByWidgetIdFromLS ,
getColumnType ,
2022-07-14 07:02:35 +00:00
getDefaultColumnProperties ,
getDerivedColumns ,
getSelectRowIndex ,
getSelectRowIndices ,
2023-10-03 08:10:51 +00:00
getTableStyles ,
2022-07-14 07:02:35 +00:00
isColumnTypeEditable ,
2023-02-15 11:42:46 +00:00
updateAndSyncTableLocalColumnOrders ,
2022-07-14 07:02:35 +00:00
} from "./utilities" ;
2023-10-09 13:54:06 +00:00
const ReactTableComponent = lazy ( async ( ) = >
retryPromise ( async ( ) = > import ( "../component" ) ) ,
2022-07-14 07:02:35 +00:00
) ;
2024-07-31 15:41:28 +00:00
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2023-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 ] ;
}
2024-09-18 16:35:28 +00:00
2023-03-30 04:54:29 +00:00
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 ;
2024-07-31 15:41:28 +00:00
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2023-03-30 04:54:29 +00:00
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 getConfig() {
2023-06-01 17:26:05 +00:00
return {
2023-09-06 12:15:04 +00:00
name : "Table" ,
iconSVG : IconSVG ,
2024-04-09 01:16:46 +00:00
thumbnailSVG : ThumbnailSVG ,
2023-09-06 12:15:04 +00:00
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 {
2023-10-05 12:57:39 +00:00
flexVerticalAlignment : FlexVerticalAlignment.Top ,
2023-09-06 12:15:04 +00:00
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 ,
2024-07-05 07:46:11 +00:00
isVisibleFilters : false ,
2023-09-06 12:15:04 +00:00
isVisibleDownload : true ,
isVisiblePagination : true ,
isSortable : true ,
delimiter : "," ,
version : 2 ,
inlineEditingSaveOption : InlineEditingSaveOptions.ROW_LEVEL ,
2023-10-12 08:24:03 +00:00
enableServerSideFiltering : TableWidgetV2.getFeatureFlag (
ALLOW_TABLE_WIDGET_SERVER_SIDE_FILTERING ,
)
? false
: undefined ,
feat: Update TableWidgetV2 to include customIsLoading property (#36857)
## Description
<ins>Problem</ins>
There are many problems with table loader logic, for which many users
try to implement a modal for loader. These problems stem from dependency
and delay on eval, discussed comprehensively in #12308
<ins>Solution</ins>
This PR updates the TableWidgetV2 component to include a new property
called `customIsLoading`. This property controls the loading state of
the widget and is added to the TableWidgetProps interface. Additionally,
the component's state is updated to include the `customIsLoading`
property.
The `contentConfig` file for the TableWidgetV2 is also modified to
include the `customIsLoading` property with its corresponding label,
control type, help text, and validation.
These changes improve the flexibility and customization options of the
TableWidgetV2 component.
Fixes #12308
_or_
Fixes `Issue URL`
> [!WARNING]
> _If no issue exists, please create an issue first, and check with the
maintainers if the issue is valid._
## Automation
/ok-to-test tags="@tag.Table"
### :mag: Cypress test results
<!-- This is an auto-generated comment: Cypress test results -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/11456273525>
> Commit: 1c6f4f9caabc3aa45ec3916e5ccb465d946ab0a1
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=11456273525&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Table`
> Spec:
> <hr>Tue, 22 Oct 2024 09:17:37 UTC
<!-- end of auto-generated comment: Cypress test results -->
## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [x] No
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced a new feature flag for custom loading states in the table
widget.
- Added properties for managing custom loading behavior in the
`TableWidgetV2`.
- **Bug Fixes**
- Enhanced loading state management to ensure accurate representation
based on new properties.
- **Tests**
- Added unit tests for loading behavior in the `TableWidgetV2`
component, covering default and custom loading scenarios.
- **Documentation**
- Updated help text for properties related to loading states to improve
clarity.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-10-22 09:21:45 +00:00
customIsLoading : false ,
customIsLoadingValue : "" ,
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 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 ,
} ) ;
2024-02-26 08:33:36 +00:00
chore: removed old flags for airgap instances (#36609)
## Description
Removed all the occurrences of listed flags in the codebase:
1. ab_ds_binding_enabled
2. ab_ds_schema_enabled
3. ab_gsheet_schema_enabled
4. ab_learnability_discoverability_collapse_all_except_data_enabled
5. ab_learnability_ease_of_initial_use_enabled
6. ab_mock_mongo_schema_enabled
7. ab_start_with_data_default_enabled
8. rollout_js_enabled_one_click_binding_enabled
Fixes #36256
_or_
Fixes `Issue URL`
> [!WARNING]
> _If no issue exists, please create an issue first, and check with the
maintainers if the issue is valid._
## Automation
/ok-to-test tags="@tag.All"
### :mag: Cypress test results
<!-- This is an auto-generated comment: Cypress test results -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/11177173738>
> Commit: bfbf6bbe77b963c5d257c29cf5bac35139417a07
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=11177173738&attempt=2"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.All`
> Spec:
> <hr>Fri, 04 Oct 2024 10:31:10 UTC
<!-- end of auto-generated comment: Cypress test results -->
## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Summary by CodeRabbit
- **New Features**
- Enhanced test coverage for the Community Issues page, focusing on
pagination, search, filtering, and issue management.
- Improved functionality for adding new rows to table widgets, including
visibility controls and state validations.
- **Bug Fixes**
- Resolved issues related to the visibility of UI elements when adding
new rows and ensured accurate data reflection in the table.
- **Tests**
- Expanded tests for pagination, row selection, search functionality,
and filtering logic in table widgets.
- Added comprehensive assertions for client-side search and filtering
scenarios, including checks for modal visibility during issue
management.
- **Chores**
- Removed obsolete feature flags and streamlined logic for managing
feature flags across components.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-10-07 09:56:25 +00:00
dynamicPropertyPathList . push ( { key : "tableData" } ) ;
2023-09-06 12:15:04 +00:00
}
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-10-03 08:10:51 +00:00
getOneClickBindingConnectableWidgetConfig : ( widget : WidgetProps ) = > {
return {
widgetBindPath : ` ${ widget . widgetName } .selectedRow ` ,
message : ` Make sure ${ widget . widgetName } is bound to the same data source ` ,
} ;
} ,
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
}
2023-10-19 20:27:40 +00:00
static getAnvilConfig ( ) : AnvilConfig | null {
return {
2023-11-14 05:25:48 +00:00
isLargeWidget : false ,
2023-10-19 20:27:40 +00:00
widgetSize : {
maxHeight : { } ,
maxWidth : { } ,
minHeight : { base : "300px" } ,
minWidth : { base : "280px" } ,
} ,
} ;
}
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
2024-07-31 15:41:28 +00:00
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
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-10-12 08:24:03 +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" ,
2024-02-07 11:06:01 +00:00
type : "array" ,
2023-07-08 14:07:26 +00:00
} ,
} ,
} ;
}
2022-07-14 07:02:35 +00:00
/ *
* Function to get the table columns with appropriate render functions
* based on columnType
* /
getTableColumns = ( ) = > {
2024-01-22 04:45:50 +00:00
const {
columnWidthMap ,
isPreviewMode ,
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 ) ;
2024-09-18 16:35:28 +00:00
2023-03-30 04:54:29 +00:00
return memoisdGetColumnsWithLocalStorage (
this . renderCell ,
columnWidthMap ,
orderedTableColumns ,
componentWidth ,
renderMode ,
2024-01-22 04:45:50 +00:00
isPreviewMode ,
2023-03-30 04:54:29 +00:00
) ;
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 = > {
2023-10-11 07:14:38 +00:00
const { primaryColumns = { } , tableData = [ ] } = this . props ;
2022-07-14 07:02:35 +00:00
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 ) ;
2024-09-18 16:35:28 +00:00
2022-09-01 06:10:19 +00:00
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 ,
) ;
2024-09-18 16:35:28 +00:00
2023-06-27 05:15:41 +00:00
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 ,
} ;
2024-09-18 16:35:28 +00:00
2023-06-27 05:15:41 +00:00
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
2024-03-21 09:46:00 +00:00
const isTableDataModified = 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 ;
2024-09-18 16:35:28 +00:00
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 {
2023-10-02 19:41:05 +00:00
if (
! isNil ( defaultSelectedRowIndex ) &&
parseInt ( defaultSelectedRowIndex ? . toString ( ) , 10 ) > - 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 ,
) ;
2024-09-18 16:35:28 +00:00
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 ,
) ;
2024-09-18 16:35:28 +00:00
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
}
2024-09-18 16:35:28 +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
2023-09-11 15:55:11 +00:00
let { componentHeight , componentWidth } = this . props ;
2024-09-18 16:35:28 +00:00
2022-09-22 11:15:00 +00:00
// (2 * WIDGET_PADDING) gives the total horizontal padding (i.e. paddingLeft + paddingRight)
componentWidth = componentWidth - 2 * WIDGET_PADDING ;
2024-09-18 16:35:28 +00:00
2022-09-22 11:15:00 +00:00
return { componentHeight , componentWidth } ;
} ;
2023-09-11 15:55:11 +00:00
getWidgetView() {
2022-07-14 07:02:35 +00:00
const {
feat: Update TableWidgetV2 to include customIsLoading property (#36857)
## Description
<ins>Problem</ins>
There are many problems with table loader logic, for which many users
try to implement a modal for loader. These problems stem from dependency
and delay on eval, discussed comprehensively in #12308
<ins>Solution</ins>
This PR updates the TableWidgetV2 component to include a new property
called `customIsLoading`. This property controls the loading state of
the widget and is added to the TableWidgetProps interface. Additionally,
the component's state is updated to include the `customIsLoading`
property.
The `contentConfig` file for the TableWidgetV2 is also modified to
include the `customIsLoading` property with its corresponding label,
control type, help text, and validation.
These changes improve the flexibility and customization options of the
TableWidgetV2 component.
Fixes #12308
_or_
Fixes `Issue URL`
> [!WARNING]
> _If no issue exists, please create an issue first, and check with the
maintainers if the issue is valid._
## Automation
/ok-to-test tags="@tag.Table"
### :mag: Cypress test results
<!-- This is an auto-generated comment: Cypress test results -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/11456273525>
> Commit: 1c6f4f9caabc3aa45ec3916e5ccb465d946ab0a1
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=11456273525&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Table`
> Spec:
> <hr>Tue, 22 Oct 2024 09:17:37 UTC
<!-- end of auto-generated comment: Cypress test results -->
## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [x] No
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced a new feature flag for custom loading states in the table
widget.
- Added properties for managing custom loading behavior in the
`TableWidgetV2`.
- **Bug Fixes**
- Enhanced loading state management to ensure accurate representation
based on new properties.
- **Tests**
- Added unit tests for loading behavior in the `TableWidgetV2`
component, covering default and custom loading scenarios.
- **Documentation**
- Updated help text for properties related to loading states to improve
clarity.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-10-22 09:21:45 +00:00
customIsLoading ,
customIsLoadingValue ,
2022-07-14 07:02:35 +00:00
delimiter ,
filteredTableData = [ ] ,
isVisibleDownload ,
isVisibleFilters ,
isVisiblePagination ,
isVisibleSearch ,
2023-10-11 07:14:38 +00:00
pageSize ,
2023-06-01 17:26:05 +00:00
primaryColumns ,
2023-10-11 07:14:38 +00:00
totalRecordsCount ,
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 }
feat: Update TableWidgetV2 to include customIsLoading property (#36857)
## Description
<ins>Problem</ins>
There are many problems with table loader logic, for which many users
try to implement a modal for loader. These problems stem from dependency
and delay on eval, discussed comprehensively in #12308
<ins>Solution</ins>
This PR updates the TableWidgetV2 component to include a new property
called `customIsLoading`. This property controls the loading state of
the widget and is added to the TableWidgetProps interface. Additionally,
the component's state is updated to include the `customIsLoading`
property.
The `contentConfig` file for the TableWidgetV2 is also modified to
include the `customIsLoading` property with its corresponding label,
control type, help text, and validation.
These changes improve the flexibility and customization options of the
TableWidgetV2 component.
Fixes #12308
_or_
Fixes `Issue URL`
> [!WARNING]
> _If no issue exists, please create an issue first, and check with the
maintainers if the issue is valid._
## Automation
/ok-to-test tags="@tag.Table"
### :mag: Cypress test results
<!-- This is an auto-generated comment: Cypress test results -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/11456273525>
> Commit: 1c6f4f9caabc3aa45ec3916e5ccb465d946ab0a1
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=11456273525&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Table`
> Spec:
> <hr>Tue, 22 Oct 2024 09:17:37 UTC
<!-- end of auto-generated comment: Cypress test results -->
## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [x] No
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced a new feature flag for custom loading states in the table
widget.
- Added properties for managing custom loading behavior in the
`TableWidgetV2`.
- **Bug Fixes**
- Enhanced loading state management to ensure accurate representation
based on new properties.
- **Tests**
- Added unit tests for loading behavior in the `TableWidgetV2`
component, covering default and custom loading scenarios.
- **Documentation**
- Updated help text for properties related to loading states to improve
clarity.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-10-22 09:21:45 +00:00
isLoading = {
customIsLoading
? customIsLoadingValue || this . props . isLoading
: this . props . isLoading
}
2022-07-14 07:02:35 +00:00
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 ,
} ,
} ;
2024-09-18 16:35:28 +00:00
2023-02-15 11:42:46 +00:00
newTableColumnOrder = tableWidgetColumnOrder ;
}
2024-09-18 16:35:28 +00:00
2023-02-15 11:42:46 +00:00
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 ,
) ;
2024-09-18 16:35:28 +00:00
2023-02-15 11:42:46 +00:00
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 ;
2024-09-18 16:35:28 +00:00
2023-02-15 11:42:46 +00:00
newColumnOrder = generateLocalNewColumnOrderFromStickyValue (
localTableColumnOrder . columnOrder ,
columnName ,
sticky ,
leftOrder ,
rightOrder ,
) ;
const updatedOrders = updateAndSyncTableLocalColumnOrders (
columnName ,
leftOrder ,
rightOrder ,
sticky ,
) ;
2024-09-18 16:35:28 +00:00
2023-02-15 11:42:46 +00:00
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 ,
) ;
2024-09-18 16:35:28 +00:00
2023-06-27 05:15:41 +00:00
if ( localTableColumnOrder ) {
const { leftOrder , rightOrder } = localTableColumnOrder ;
2024-09-18 16:35:28 +00:00
2023-06-27 05:15:41 +00:00
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 ) ;
}
} ;
2024-07-31 15:41:28 +00:00
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2022-07-14 07:02:35 +00:00
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 = ( {
action ,
2023-10-11 07:14:38 +00:00
additionalData = { } ,
2023-03-30 04:54:29 +00:00
eventType ,
2023-10-11 07:14:38 +00:00
onComplete = noop ,
2023-03-30 04:54:29 +00:00
row ,
2023-10-11 07:14:38 +00:00
rowIndex ,
triggerPropertyName ,
2023-03-30 04:54:29 +00:00
} : OnColumnEventArgs ) = > {
const { filteredTableData = [ ] , pushBatchMetaUpdates } = this . props ;
const currentRow = row || filteredTableData [ rowIndex ] ;
2024-09-18 16:35:28 +00:00
2023-03-30 04:54:29 +00:00
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 = ( {
action ,
2023-10-11 07:14:38 +00:00
additionalData = { } ,
2022-07-14 07:02:35 +00:00
eventType ,
2023-10-11 07:14:38 +00:00
onComplete = noop ,
2022-07-14 07:02:35 +00:00
row ,
2023-10-11 07:14:38 +00:00
rowIndex ,
triggerPropertyName ,
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 ,
) ;
2024-09-18 16:35:28 +00:00
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 ;
2024-09-18 16:35:28 +00:00
2023-04-13 10:08:46 +00:00
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
}
2024-09-18 16:35:28 +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
}
2024-09-18 16:35:28 +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
}
}
2024-09-18 16:35:28 +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 ) = > {
2024-08-29 07:05:48 +00:00
const newTransientTableData = klonaRegularWithTelemetry (
this . props . transientTableData ,
"TableWidgetV2.removeRowFromTransientTableData" ,
) ;
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
}
2024-09-18 16:35:28 +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 ,
} ,
} ,
) ;
} ;
2024-07-31 15:41:28 +00:00
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
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 {
2023-10-11 07:14:38 +00:00
compactMode = CompactModeTypes . DEFAULT ,
2022-07-14 07:02:35 +00:00
filteredTableData = [ ] ,
multiRowSelection ,
selectedRowIndex ,
selectedRowIndices ,
} = 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 }
fix: isRequired validation property for table select column (#36375)
## Description
**Problem**
The select column of the table widget does not have a validation
property within its property pane to allow users add an isRequired
validation to the table select column.
**Solution**
Added a Validation section to the table select column's property pane,
which includes an isRequired toggle. When enabled, this feature will
trigger a visual indication (error border colour) around the select
widget if a required field is left unselected during "Add new row" or
inline editing.
Fixes #30091
## Automation
/ok-to-test tags="@tag.Widget, @tag.Table, @tag.Binding, @tag.Sanity,
@tag.Select"
### :mag: Cypress test results
<!-- This is an auto-generated comment: Cypress test results -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/10957896180>
> Commit: d2597e6a26938f2b99f2f997fca7bc110e5c2091
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=10957896180&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Widget, @tag.Table, @tag.Binding, @tag.Sanity,
@tag.Select`
> Spec:
> <hr>Fri, 20 Sep 2024 12:23:29 UTC
<!-- end of auto-generated comment: Cypress test results -->
## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [x] No
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced end-to-end tests for Select column validation in Table
widgets.
- Enhanced validation logic to support Select column types in the Table
widget.
- Added visual feedback for required Select fields during row addition
and inline editing.
- Improved locator for single-select widget button control to enhance UI
interaction.
- **Bug Fixes**
- Improved error handling and visual representation for invalid editable
Select cells.
- **Documentation**
- Updated validation configuration to include Select column types for
better usability.
- **Refactor**
- Enhanced code clarity for styled components related to Select fields.
- Modified method to improve versatility in handling table interactions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Sai Charan <saicharan.chetpelly@zemosolabs.com>
Co-authored-by: Pawan Kumar <pawankumar@Pawans-MacBook-Pro-2.local>
2024-09-23 07:44:46 +00:00
isEditableCellValid = { this . isColumnCellValid ( alias ) }
2022-09-30 04:03:53 +00:00
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 }
2023-09-11 15:55:11 +00:00
tableWidth = { this . props . componentWidth }
2022-09-30 04:03:53 +00:00
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 }
2023-09-11 15:55:11 +00:00
tableWidth = { this . props . componentWidth }
2023-01-16 09:23:56 +00:00
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 }
/ >
) ;
2024-12-11 07:37:30 +00:00
case ColumnTypes . HTML :
return (
< HTMLCell
allowCellWrapping = { cellProperties . allowCellWrapping }
cellBackground = { cellProperties . cellBackground }
compactMode = { compactMode }
fontStyle = { cellProperties . fontStyle }
horizontalAlignment = { cellProperties . horizontalAlignment }
isCellDisabled = { cellProperties . isCellDisabled }
isCellVisible = { cellProperties . isCellVisible ? ? true }
isHidden = { isHidden }
renderMode = { this . props . renderMode }
textColor = { cellProperties . textColor }
textSize = { cellProperties . textSize }
value = { props . cell . value }
verticalAlignment = { cellProperties . verticalAlignment }
/ >
) ;
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 }
2023-11-06 05:35:26 +00:00
currencyCode = { cellProperties . currencyCode }
decimals = { cellProperties . decimals }
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 }
2023-11-06 05:35:26 +00:00
notation = { cellProperties . notation }
2022-11-05 09:54:20 +00:00
onCellTextChange = { this . onCellTextChange }
2022-07-14 07:02:35 +00:00
onSubmitString = { props . cell . column . columnProperties . onSubmit }
rowIndex = { rowIndex }
2023-09-11 15:55:11 +00:00
tableWidth = { this . props . componentWidth }
2022-07-14 07:02:35 +00:00
textColor = { cellProperties . textColor }
textSize = { cellProperties . textSize }
2023-11-06 05:35:26 +00:00
thousandSeparator = { cellProperties . thousandSeparator }
2022-07-14 07:02:35 +00:00
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 ,
} ) ;
}
2024-09-18 16:35:28 +00:00
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
}
2024-09-18 16:35:28 +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 ,
fix: Fixes currentRow calculation logic in table(property pane) (#35390)
## Description
<ins>Problem</ins>
`currentRow` variable which is availabe in property pane of col settings
- is not getting correct value during runtime if the table is sorted or
filtered.
<ins>Root cause</ins>
* We are considering `processedTableData` to get the `currentRow`.
* This property is not updated during filtering and sorting, another
property `filteredTableData` is updated instead.
* We CANNOT use `filteredTableData` as it depends on `primaryColumns`
property which we intend to update as well - this is leading to cyclic
dependency during evaluations.
<ins>Solution</ins>
Since the problem is related to edit cases and given the constraints
around using `filteredTableData` directly, we fixed the problem by
adding a new property to `editableCellValue` object called
`__originalIndex__`.
* This property stores the index of the row being edited in
`processedTableData`
* On top of this change, the PR adds a migration for updating the
current row binding in TableWidgetV2, ensuring accurate current row
calculation and improving the functionality of the widget.
* We also added unit test for migration changes.
* Additionally, This pull request refactors the DSLs for TableWidgetV2
migration test cases to update the DSLs to separate folder, drastically
reducing the file size to its core logic, improving the readability of
the code.
* We also updated relevant test cases to account for this change.
[Testing
plan](https://www.notion.so/appsmith/Issue-34346-currentRow-doesn-t-work-correctly-when-the-table-is-filtered-449225ae822c485493036599c2b19487)
[Counter part EE
pr](https://github.com/appsmithorg/appsmith-ee/pull/4879)
Fixes #34346
_or_
Fixes `Issue URL`
> [!WARNING]
> _If no issue exists, please create an issue first, and check with the
maintainers if the issue is valid._
## Automation
/ok-to-test tags="@tag.All"
### :mag: Cypress test results
<!-- This is an auto-generated comment: Cypress test results -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/10451549845>
> Commit: d1d65c6898c223bf3f6dfbfe93b8e8de214fcc7d
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=10451549845&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.All`
> Spec:
> <hr>Mon, 19 Aug 2024 11:15:04 UTC
<!-- end of auto-generated comment: Cypress test results -->
## Communication
Should the DevRel and Marketing teams inform users about this change?
- [x] Yes
- [ ] No
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Summary by CodeRabbit
- **New Features**
- Updated DSL migration process to support version 90, enhancing
compatibility and robustness.
- Introduced new migration logic for table widget data bindings,
improving inline editing capabilities.
- Enhanced validation logic for editable cells in table widgets,
allowing for more dynamic data handling.
- Added a method to discard edits in specific table cells, improving
user interaction.
- Introduced a new message constant for required fields, enhancing user
feedback.
- **Bug Fixes**
- Improved validation checks for table cells based on updated indices
and values.
- **Tests**
- Added comprehensive tests to validate migration functions related to
Table Widget, ensuring all aspects function correctly post-update.
- Enhanced test specifications for improved validation logic and
coverage in table widget functionalities.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: sneha122 <sneha@appsmith.com>
2024-08-20 08:01:45 +00:00
__originalIndex__ : this.getRowOriginalIndex ( rowIndex ) ,
2022-09-13 05:41:59 +00:00
} ) ;
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
}
}
2024-09-18 16:35:28 +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
} ,
} ) ;
}
2024-09-18 16:35:28 +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 ,
) = > {
fix: mandatory date column enforcement (#35613)
## Description
**Problem:**
When the `isRequired` property is set for date columns in the table
widget, the validation doesn't work as expected. Users can add new rows
without filling in the date field, even though it is marked as required.
This results in rows being added with missing date values, which can
lead to incomplete or invalid data entries.
**Root Cause:**
The validation logic for the date column is currently handled within the
DateCell component. However, the isRequired validation functionality was
not implemented within this component. Additionally, the general
validation logic in the `getEditableCellValidity` function, located in
the derived.js file, does not account date cells in its isRequired
validation.
**Solution:**
To fix this issue:
**Enhance the `getEditableCellValidity` function:** Extend the existing
validation logic in getEditableCellValidity to include the date cell in
its validation, specifically checking for the isRequired property.
**Integrate with DateCell validation:** Ensure that the isRequired
validation is properly executed in conjunction with the existing date
validations inside the DateCell component. This will enforce the
requirement and prevent new rows from being added if the date field is
left empty.
**Test Plan**
https://www.notion.so/appsmith/Test-Plan-Date-Column-Marked-as-Required-Doesn-t-Enforce-Mandatory-Entry-When-Adding-New-Table-Row-c73b764af60842a188cba056bdda6d79?pvs=4
Fixes #34258
## Automation
/ok-to-test tags="@tag.All"
### :mag: Cypress test results
<!-- This is an auto-generated comment: Cypress test results -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/10453174231>
> Commit: 40fe2eaf7d45024bef00f8031e971a5bac2a4e3e
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=10453174231&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.All`
> Spec:
> <hr>Mon, 19 Aug 2024 13:17:23 UTC
<!-- end of auto-generated comment: Cypress test results -->
## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Summary by CodeRabbit
- **New Features**
- Introduced validation for "Date" column types, ensuring that required
fields are correctly enforced.
- **Enhancements**
- Improved validation logic for more accurate user feedback in date
cells.
- Expanded support for validating "date" columns in the table widget.
- **Bug Fixes**
- Enhanced error handling to ensure proper indication of cell validity
based on new validation criteria.
- Updated visual feedback for cell editor state in the UI.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-08-20 06:09:51 +00:00
const { commitBatchMetaUpdates } = this . props ;
2023-01-16 09:23:56 +00:00
fix: mandatory date column enforcement (#35613)
## Description
**Problem:**
When the `isRequired` property is set for date columns in the table
widget, the validation doesn't work as expected. Users can add new rows
without filling in the date field, even though it is marked as required.
This results in rows being added with missing date values, which can
lead to incomplete or invalid data entries.
**Root Cause:**
The validation logic for the date column is currently handled within the
DateCell component. However, the isRequired validation functionality was
not implemented within this component. Additionally, the general
validation logic in the `getEditableCellValidity` function, located in
the derived.js file, does not account date cells in its isRequired
validation.
**Solution:**
To fix this issue:
**Enhance the `getEditableCellValidity` function:** Extend the existing
validation logic in getEditableCellValidity to include the date cell in
its validation, specifically checking for the isRequired property.
**Integrate with DateCell validation:** Ensure that the isRequired
validation is properly executed in conjunction with the existing date
validations inside the DateCell component. This will enforce the
requirement and prevent new rows from being added if the date field is
left empty.
**Test Plan**
https://www.notion.so/appsmith/Test-Plan-Date-Column-Marked-as-Required-Doesn-t-Enforce-Mandatory-Entry-When-Adding-New-Table-Row-c73b764af60842a188cba056bdda6d79?pvs=4
Fixes #34258
## Automation
/ok-to-test tags="@tag.All"
### :mag: Cypress test results
<!-- This is an auto-generated comment: Cypress test results -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/10453174231>
> Commit: 40fe2eaf7d45024bef00f8031e971a5bac2a4e3e
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=10453174231&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.All`
> Spec:
> <hr>Mon, 19 Aug 2024 13:17:23 UTC
<!-- end of auto-generated comment: Cypress test results -->
## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Summary by CodeRabbit
- **New Features**
- Introduced validation for "Date" column types, ensuring that required
fields are correctly enforced.
- **Enhancements**
- Improved validation logic for more accurate user feedback in date
cells.
- Expanded support for validating "date" columns in the table widget.
- **Bug Fixes**
- Enhanced error handling to ensure proper indication of cell validity
based on new validation criteria.
- Updated visual feedback for cell editor state in the UI.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-08-20 06:09:51 +00:00
this . pushTransientTableDataActionsUpdates ( {
[ ORIGINAL_INDEX_KEY ] : this . getRowOriginalIndex ( rowIndex ) ,
[ alias ] : value ,
} ) ;
2023-01-16 09:23:56 +00:00
fix: mandatory date column enforcement (#35613)
## Description
**Problem:**
When the `isRequired` property is set for date columns in the table
widget, the validation doesn't work as expected. Users can add new rows
without filling in the date field, even though it is marked as required.
This results in rows being added with missing date values, which can
lead to incomplete or invalid data entries.
**Root Cause:**
The validation logic for the date column is currently handled within the
DateCell component. However, the isRequired validation functionality was
not implemented within this component. Additionally, the general
validation logic in the `getEditableCellValidity` function, located in
the derived.js file, does not account date cells in its isRequired
validation.
**Solution:**
To fix this issue:
**Enhance the `getEditableCellValidity` function:** Extend the existing
validation logic in getEditableCellValidity to include the date cell in
its validation, specifically checking for the isRequired property.
**Integrate with DateCell validation:** Ensure that the isRequired
validation is properly executed in conjunction with the existing date
validations inside the DateCell component. This will enforce the
requirement and prevent new rows from being added if the date field is
left empty.
**Test Plan**
https://www.notion.so/appsmith/Test-Plan-Date-Column-Marked-as-Required-Doesn-t-Enforce-Mandatory-Entry-When-Adding-New-Table-Row-c73b764af60842a188cba056bdda6d79?pvs=4
Fixes #34258
## Automation
/ok-to-test tags="@tag.All"
### :mag: Cypress test results
<!-- This is an auto-generated comment: Cypress test results -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/10453174231>
> Commit: 40fe2eaf7d45024bef00f8031e971a5bac2a4e3e
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=10453174231&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.All`
> Spec:
> <hr>Mon, 19 Aug 2024 13:17:23 UTC
<!-- end of auto-generated comment: Cypress test results -->
## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Summary by CodeRabbit
- **New Features**
- Introduced validation for "Date" column types, ensuring that required
fields are correctly enforced.
- **Enhancements**
- Improved validation logic for more accurate user feedback in date
cells.
- Expanded support for validating "date" columns in the table widget.
- **Bug Fixes**
- Enhanced error handling to ensure proper indication of cell validity
based on new validation criteria.
- Updated visual feedback for cell editor state in the UI.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-08-20 06:09:51 +00:00
if ( onSubmit && this . props . editableCell ? . column ) {
//since onSubmit is truthy this makes action truthy as well, so we can push this event
this . pushOnColumnEvent ( {
rowIndex : rowIndex ,
action : onSubmit ,
triggerPropertyName : "onSubmit" ,
eventType : EventType.ON_SUBMIT ,
row : {
. . . this . props . filteredTableData [ rowIndex ] ,
[ this . props . editableCell . column ] : value ,
} ,
} ) ;
2023-01-16 09:23:56 +00:00
}
fix: mandatory date column enforcement (#35613)
## Description
**Problem:**
When the `isRequired` property is set for date columns in the table
widget, the validation doesn't work as expected. Users can add new rows
without filling in the date field, even though it is marked as required.
This results in rows being added with missing date values, which can
lead to incomplete or invalid data entries.
**Root Cause:**
The validation logic for the date column is currently handled within the
DateCell component. However, the isRequired validation functionality was
not implemented within this component. Additionally, the general
validation logic in the `getEditableCellValidity` function, located in
the derived.js file, does not account date cells in its isRequired
validation.
**Solution:**
To fix this issue:
**Enhance the `getEditableCellValidity` function:** Extend the existing
validation logic in getEditableCellValidity to include the date cell in
its validation, specifically checking for the isRequired property.
**Integrate with DateCell validation:** Ensure that the isRequired
validation is properly executed in conjunction with the existing date
validations inside the DateCell component. This will enforce the
requirement and prevent new rows from being added if the date field is
left empty.
**Test Plan**
https://www.notion.so/appsmith/Test-Plan-Date-Column-Marked-as-Required-Doesn-t-Enforce-Mandatory-Entry-When-Adding-New-Table-Row-c73b764af60842a188cba056bdda6d79?pvs=4
Fixes #34258
## Automation
/ok-to-test tags="@tag.All"
### :mag: Cypress test results
<!-- This is an auto-generated comment: Cypress test results -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/10453174231>
> Commit: 40fe2eaf7d45024bef00f8031e971a5bac2a4e3e
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=10453174231&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.All`
> Spec:
> <hr>Mon, 19 Aug 2024 13:17:23 UTC
<!-- end of auto-generated comment: Cypress test results -->
## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Summary by CodeRabbit
- **New Features**
- Introduced validation for "Date" column types, ensuring that required
fields are correctly enforced.
- **Enhancements**
- Improved validation logic for more accurate user feedback in date
cells.
- Expanded support for validating "date" columns in the table widget.
- **Bug Fixes**
- Enhanced error handling to ensure proper indication of cell validity
based on new validation criteria.
- Updated visual feedback for cell editor state in the UI.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2024-08-20 06:09:51 +00:00
commitBatchMetaUpdates ( ) ;
this . clearEditableCell ( ) ;
2023-01-16 09:23:56 +00:00
} ;
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 ,
} ,
} ) ;
}
2024-09-18 16:35:28 +00:00
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 ,
} ,
} ) ;
}
2024-09-18 16:35:28 +00:00
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 = (
2024-07-31 15:41:28 +00:00
// TODO: Fix this the next time the file is edited
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2022-11-05 09:54:20 +00:00
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 ;