PromucFlow_constructor/app/client/src/widgets/TableWidgetV2/widget/derived.js

868 lines
25 KiB
JavaScript
Raw Normal View History

/* eslint-disable @typescript-eslint/no-unused-vars*/
export default {
getSelectedRow: (props, moment, _) => {
let index = -1;
/*
* If multiRowSelection is turned on, use the last index to
* populate the selectedRowIndex
*/
if (props.multiRowSelection) {
if (
_.isArray(props.selectedRowIndices) &&
props.selectedRowIndices.length &&
props.selectedRowIndices.every((i) => _.isNumber(i))
) {
index = props.selectedRowIndices[props.selectedRowIndices.length - 1];
} else if (_.isNumber(props.selectedRowIndices)) {
index = props.selectedRowIndices;
}
} else if (
!_.isNil(props.selectedRowIndex) &&
!_.isNaN(parseInt(props.selectedRowIndex))
) {
index = parseInt(props.selectedRowIndex);
}
const rows = props.filteredTableData || props.processedTableData || [];
const primaryColumns = props.primaryColumns;
const nonDataColumnTypes = [
"editActions",
"button",
"iconButton",
"menuButton",
];
const nonDataColumnAliases = primaryColumns
? Object.values(primaryColumns)
.filter((column) => nonDataColumnTypes.includes(column.columnType))
.map((column) => column.alias)
: [];
let selectedRow;
/*
* Note(Balaji): Need to include customColumn values in the selectedRow (select, rating)
* It should have updated values.
*/
if (index > -1) {
selectedRow = { ...rows[index] };
} else {
/*
* If index is not a valid, selectedRow should have
* proper row structure with empty string values
*/
selectedRow = {};
Object.keys(rows[0]).forEach((key) => {
selectedRow[key] = "";
});
}
const keysToBeOmitted = [
"__originalIndex__",
"__primaryKey__",
...nonDataColumnAliases,
];
return _.omit(selectedRow, keysToBeOmitted);
},
//
getTriggeredRow: (props, moment, _) => {
let index = -1;
const parsedTriggeredRowIndex = parseInt(props.triggeredRowIndex);
if (!_.isNaN(parsedTriggeredRowIndex)) {
index = parsedTriggeredRowIndex;
}
const rows = props.filteredTableData || props.processedTableData || [];
const primaryColumns = props.primaryColumns;
const nonDataColumnTypes = [
"editActions",
"button",
"iconButton",
"menuButton",
];
const nonDataColumnAliases = primaryColumns
? Object.values(primaryColumns)
.filter((column) => nonDataColumnTypes.includes(column.columnType))
.map((column) => column.alias)
: [];
let triggeredRow;
/*
* Note(Balaji): Need to include customColumn values in the triggeredRow (select, rating)
* It should have updated values.
*/
if (index > -1) {
const row = rows.find((row) => row.__originalIndex__ === index);
triggeredRow = { ...row };
} else {
/*
* If triggeredRowIndex is not a valid index, triggeredRow should
* have proper row structure with empty string values
*/
triggeredRow = {};
Object.keys(rows[0]).forEach((key) => {
triggeredRow[key] = "";
});
}
const keysToBeOmitted = [
"__originalIndex__",
"__primaryKey__",
...nonDataColumnAliases,
];
return _.omit(triggeredRow, keysToBeOmitted);
},
//
getSelectedRows: (props, moment, _) => {
if (!props.multiRowSelection) {
return [];
}
let indices = [];
if (
_.isArray(props.selectedRowIndices) &&
props.selectedRowIndices.every((i) => _.isNumber(i))
) {
indices = props.selectedRowIndices;
}
const rows = props.filteredTableData || props.processedTableData || [];
const primaryColumns = props.primaryColumns;
const nonDataColumnTypes = [
"editActions",
"button",
"iconButton",
"menuButton",
];
const nonDataColumnAliases = primaryColumns
? Object.values(primaryColumns)
.filter((column) => nonDataColumnTypes.includes(column.columnType))
.map((column) => column.alias)
: [];
const keysToBeOmitted = [
"__originalIndex__",
"__primaryKey__",
...nonDataColumnAliases,
];
return indices.map((index) => _.omit(rows[index], keysToBeOmitted));
},
//
getPageSize: (props, moment, _) => {
const TABLE_SIZES = {
DEFAULT: {
COLUMN_HEADER_HEIGHT: 32,
TABLE_HEADER_HEIGHT: 38,
ROW_HEIGHT: 40,
ROW_FONT_SIZE: 14,
VERTICAL_PADDING: 6,
EDIT_ICON_TOP: 10,
},
SHORT: {
COLUMN_HEADER_HEIGHT: 32,
TABLE_HEADER_HEIGHT: 38,
ROW_HEIGHT: 30,
ROW_FONT_SIZE: 12,
VERTICAL_PADDING: 0,
EDIT_ICON_TOP: 5,
},
TALL: {
COLUMN_HEADER_HEIGHT: 32,
TABLE_HEADER_HEIGHT: 38,
ROW_HEIGHT: 60,
ROW_FONT_SIZE: 18,
VERTICAL_PADDING: 16,
EDIT_ICON_TOP: 21,
},
};
const compactMode = props.compactMode || "DEFAULT";
const componentHeight =
fix: Makes use of mobile positioning properties in Table Widget (#24729) ## Description Table widget's pageSize property was not taking account of mobile position properties (`mobileTopRow` and `mobileBottomRow`) in Auto Layout mode. This caused the issues mentioned in this PR. Since this is a derived property, properties such as `isMobile` and `appPositioningType` were not directly available. So, we added these into the DataTree as well. #### PR fixes following issue(s) Fixes #22907 Fixes #22911 #### Media > A video or a GIF is preferred. when using Loom, don’t embed because it looks like it’s a GIF. instead, just link to the video > > #### Type of change - Bug fix (non-breaking change which fixes an issue) > > > ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [ ] Jest - [ ] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] 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 - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#areas-of-interest-) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --------- Co-authored-by: rahulramesha <rahul@appsmith.com>
2023-07-17 05:42:52 +00:00
(props.appPositioningType === "AUTO" && props.isMobile
? props.mobileBottomRow - props.mobileTopRow
: props.bottomRow - props.topRow) *
props.parentRowSpace -
10;
const tableSizes = TABLE_SIZES[compactMode];
let pageSize =
(componentHeight -
tableSizes.TABLE_HEADER_HEIGHT -
tableSizes.COLUMN_HEADER_HEIGHT) /
tableSizes.ROW_HEIGHT;
return pageSize % 1 > 0.3 ? Math.ceil(pageSize) : Math.floor(pageSize);
},
//
getProcessedTableData: (props, moment, _) => {
let data;
if (_.isArray(props.tableData)) {
/* Populate meta keys (__originalIndex__, __primaryKey__) and transient values */
data = props.tableData.map((row, index) => ({
...row,
__originalIndex__: index,
__primaryKey__: props.primaryColumnId
? row[props.primaryColumnId]
: undefined,
...props.transientTableData[index],
}));
} else {
data = [];
}
return data;
},
//
getOrderedTableColumns: (props, moment, _) => {
let columns = [];
let existingColumns = props.primaryColumns || {};
/*
* Assign index based on the columnOrder
*/
if (
_.isArray(props.columnOrder) &&
props.columnOrder.length > 0 &&
Object.keys(existingColumns).length > 0
) {
const newColumnsInOrder = {};
let index = 0;
_.uniq(props.columnOrder).forEach((columnId) => {
if (existingColumns[columnId]) {
newColumnsInOrder[columnId] = Object.assign(
{},
existingColumns[columnId],
{
index,
},
);
index++;
}
});
existingColumns = newColumnsInOrder;
}
const sortByColumn = props.sortOrder && props.sortOrder.column;
const isAscOrder = props.sortOrder && props.sortOrder.order === "asc";
/* set sorting flags and convert the existing columns into an array */
Object.values(existingColumns).forEach((column) => {
/* guard to not allow columns without id */
if (column.id) {
column.isAscOrder = column.id === sortByColumn ? isAscOrder : undefined;
columns.push(column);
}
});
return columns;
},
//
getFilteredTableData: (props, moment, _) => {
/* Make a shallow copy */
const primaryColumns = props.primaryColumns || {};
let processedTableData = [...props.processedTableData];
const derivedColumns = {};
Object.keys(primaryColumns).forEach((id) => {
if (primaryColumns[id] && primaryColumns[id].isDerived) {
derivedColumns[id] = primaryColumns[id];
}
});
if (!processedTableData || !processedTableData.length) {
return [];
}
/* extend processedTableData with values from
* - computedValues, in case of normal column
* - empty values, in case of derived column
*/
if (primaryColumns && _.isPlainObject(primaryColumns)) {
Object.entries(primaryColumns).forEach(([id, column]) => {
let computedValues = [];
if (column && column.computedValue) {
if (_.isString(column.computedValue)) {
try {
computedValues = JSON.parse(column.computedValue);
} catch (e) {
/* do nothing */
}
} else if (_.isArray(column.computedValue)) {
computedValues = column.computedValue;
}
}
/* for derived columns inject empty strings */
if (
computedValues.length === 0 &&
derivedColumns &&
derivedColumns[id]
) {
computedValues = Array(processedTableData.length).fill("");
}
computedValues.forEach((computedValue, index) => {
processedTableData[index] = {
...processedTableData[index],
[column.alias]: computedValue,
};
});
});
}
const columns = props.orderedTableColumns;
const sortByColumnId = props.sortOrder.column;
let sortedTableData;
if (sortByColumnId) {
const sortBycolumn = columns.find(
(column) => column.id === sortByColumnId,
);
const sortByColumnOriginalId = sortBycolumn.alias;
const columnType =
sortBycolumn && sortBycolumn.columnType
? sortBycolumn.columnType
: "text";
const inputFormat = sortBycolumn.inputFormat;
const isEmptyOrNil = (value) => {
return _.isNil(value) || value === "";
};
const isAscOrder = props.sortOrder.order === "asc";
const sortByOrder = (isAGreaterThanB) => {
if (isAGreaterThanB) {
return isAscOrder ? 1 : -1;
} else {
return isAscOrder ? -1 : 1;
}
};
sortedTableData = processedTableData.sort((a, b) => {
if (_.isPlainObject(a) && _.isPlainObject(b)) {
if (
isEmptyOrNil(a[sortByColumnOriginalId]) ||
isEmptyOrNil(b[sortByColumnOriginalId])
) {
/* push null, undefined and "" values to the bottom. */
return isEmptyOrNil(a[sortByColumnOriginalId]) ? 1 : -1;
} else {
switch (columnType) {
case "number":
return sortByOrder(
Number(a[sortByColumnOriginalId]) >
Number(b[sortByColumnOriginalId]),
);
case "date":
try {
return sortByOrder(
moment(a[sortByColumnOriginalId], inputFormat).isAfter(
moment(b[sortByColumnOriginalId], inputFormat),
),
);
} catch (e) {
return -1;
}
fix: table search is not working properly for url colums (#24244) ## Description - If we have a column type of `url` in table, the search considers the computed value and filters data based on that. But the search should take the display text as the search value to filter data. #### PR fixes following issue(s) Fixes #23636 #### Media #### Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [x] Jest - [x] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] 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 - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Test-plan-implementation#speedbreaker-features-to-consider-for-every-change) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans/_edit#areas-of-interest) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
2023-06-21 12:01:06 +00:00
case "url":
const column = primaryColumns[sortByColumnOriginalId];
if (column && column.displayText) {
if (_.isString(column.displayText)) {
return sortByOrder(false);
} else if (_.isArray(column.displayText)) {
return sortByOrder(
column.displayText[a.__originalIndex__]
.toString()
.toLowerCase() >
column.displayText[b.__originalIndex__]
.toString()
.toLowerCase(),
);
}
}
default:
return sortByOrder(
a[sortByColumnOriginalId].toString().toLowerCase() >
b[sortByColumnOriginalId].toString().toLowerCase(),
);
}
}
} else {
return isAscOrder ? 1 : 0;
}
});
} else {
sortedTableData = [...processedTableData];
}
const ConditionFunctions = {
isExactly: (a, b) => {
return a.toString() === b.toString();
},
empty: (a) => {
return _.isNil(a) || _.isEmpty(a.toString());
},
notEmpty: (a) => {
return !_.isNil(a) && !_.isEmpty(a.toString());
},
notEqualTo: (a, b) => {
return a.toString() !== b.toString();
},
/* Note: Duplicate of isExactly */
isEqualTo: (a, b) => {
return a.toString() === b.toString();
},
lessThan: (a, b) => {
return Number(a) < Number(b);
},
lessThanEqualTo: (a, b) => {
return Number(a) <= Number(b);
},
greaterThan: (a, b) => {
return Number(a) > Number(b);
},
greaterThanEqualTo: (a, b) => {
return Number(a) >= Number(b);
},
contains: (a, b) => {
try {
return a
.toString()
.toLowerCase()
.includes(b.toString().toLowerCase());
} catch (e) {
return false;
}
},
doesNotContain: (a, b) => {
try {
return !a
.toString()
.toLowerCase()
.includes(b.toString().toLowerCase());
} catch (e) {
return false;
}
},
startsWith: (a, b) => {
try {
return (
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
a.toString().toLowerCase().indexOf(b.toString().toLowerCase()) === 0
);
} catch (e) {
return false;
}
},
endsWith: (a, b) => {
try {
const _a = a.toString().toLowerCase();
const _b = b.toString().toLowerCase();
return (
_a.lastIndexOf(_b) >= 0 &&
_a.length === _a.lastIndexOf(_b) + _b.length
);
} catch (e) {
return false;
}
},
is: (a, b) => {
return moment(a).isSame(moment(b), "minute");
},
isNot: (a, b) => {
return !moment(a).isSame(moment(b), "minute");
},
isAfter: (a, b) => {
return moment(a).isAfter(moment(b), "minute");
},
isBefore: (a, b) => {
return moment(a).isBefore(moment(b), "minute");
},
isChecked: (a) => {
return a === true;
},
isUnChecked: (a) => {
return a === false;
},
};
let searchKey;
/* skipping search when client side search is turned off */
if (
props.searchText &&
(!props.onSearchTextChanged || props.enableClientSideSearch)
) {
searchKey = props.searchText.toLowerCase();
} else {
searchKey = "";
}
/*
* We need to omit hidden column values from being included
* in the search
*/
const hiddenColumns = Object.values(props.primaryColumns)
.filter((column) => !column.isVisible)
.map((column) => column.alias);
const finalTableData = sortedTableData.filter((row) => {
let isSearchKeyFound = true;
fix: table search is not working properly for url colums (#24244) ## Description - If we have a column type of `url` in table, the search considers the computed value and filters data based on that. But the search should take the display text as the search value to filter data. #### PR fixes following issue(s) Fixes #23636 #### Media #### Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [x] Jest - [x] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] 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 - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Test-plan-implementation#speedbreaker-features-to-consider-for-every-change) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans/_edit#areas-of-interest) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
2023-06-21 12:01:06 +00:00
const columnWithDisplayText = Object.values(props.primaryColumns).filter(
(column) => column.columnType === "url" && column.displayText,
);
const displayedRow = {
...row,
...columnWithDisplayText.reduce((acc, column) => {
let displayText;
if (_.isArray(column.displayText)) {
displayText = column.displayText[row.__originalIndex__];
} else {
displayText = column.displayText;
}
acc[column.alias] = displayText;
return acc;
}, {}),
};
if (searchKey) {
fix: table search is not working properly for url colums (#24244) ## Description - If we have a column type of `url` in table, the search considers the computed value and filters data based on that. But the search should take the display text as the search value to filter data. #### PR fixes following issue(s) Fixes #23636 #### Media #### Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [x] Jest - [x] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] 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 - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Test-plan-implementation#speedbreaker-features-to-consider-for-every-change) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans/_edit#areas-of-interest) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
2023-06-21 12:01:06 +00:00
isSearchKeyFound = Object.values(_.omit(displayedRow, hiddenColumns))
.join(", ")
.toLowerCase()
.includes(searchKey);
}
if (!isSearchKeyFound) {
return false;
}
/* when there is no filter defined */
if (!props.filters || props.filters.length === 0) {
return true;
}
const filterOperator =
props.filters.length >= 2 ? props.filters[1].operator : "OR";
let isSatisfyingFilters = filterOperator === "AND";
for (let i = 0; i < props.filters.length; i++) {
let filterResult = true;
try {
const conditionFunction =
ConditionFunctions[props.filters[i].condition];
if (conditionFunction) {
filterResult = conditionFunction(
fix: table search is not working properly for url colums (#24244) ## Description - If we have a column type of `url` in table, the search considers the computed value and filters data based on that. But the search should take the display text as the search value to filter data. #### PR fixes following issue(s) Fixes #23636 #### Media #### Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [x] Jest - [x] Cypress > > #### Test Plan > Add Testsmith test cases links that relate to this PR > > #### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) > > > ## Checklist: #### Dev activity - [x] My code follows the style guidelines of this project - [x] 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 - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Test-plan-implementation#speedbreaker-features-to-consider-for-every-change) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans/_edit#areas-of-interest) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
2023-06-21 12:01:06 +00:00
displayedRow[props.filters[i].column],
props.filters[i].value,
);
}
} catch (e) {
filterResult = false;
}
/* if one filter condition is not satisfied and filter operator is AND, bailout early */
if (!filterResult && filterOperator === "AND") {
isSatisfyingFilters = false;
break;
} else if (filterResult && filterOperator === "OR") {
/* if one filter condition is satisfied and filter operator is OR, bailout early */
isSatisfyingFilters = true;
break;
}
isSatisfyingFilters =
filterOperator === "AND"
? isSatisfyingFilters && filterResult
: isSatisfyingFilters || filterResult;
}
return isSatisfyingFilters;
});
return finalTableData;
},
//
getUpdatedRow: (props, moment, _) => {
let index = -1;
const parsedUpdatedRowIndex = parseInt(props.updatedRowIndex);
if (!_.isNaN(parsedUpdatedRowIndex)) {
index = parsedUpdatedRowIndex;
}
const rows = props.filteredTableData || props.processedTableData || [];
const primaryColumns = props.primaryColumns;
let updatedRow;
if (index > -1) {
const row = rows.find((row) => row.__originalIndex__ === index);
updatedRow = { ...row };
} else {
/*
* If updatedRowIndex is not a valid index, updatedRow should
* have proper row structure with empty string values
*/
updatedRow = {};
if (rows && rows[0]) {
Object.keys(rows[0]).forEach((key) => {
updatedRow[key] = "";
});
}
}
const nonDataColumnTypes = [
"editActions",
"button",
"iconButton",
"menuButton",
];
const nonDataColumnAliases = primaryColumns
? Object.values(primaryColumns)
.filter((column) => nonDataColumnTypes.includes(column.columnType))
.map((column) => column.alias)
: [];
const keysToBeOmitted = [
"__originalIndex__",
"__primaryKey__",
...nonDataColumnAliases,
];
return _.omit(updatedRow, keysToBeOmitted);
},
//
getUpdatedRows: (props, moment, _) => {
const primaryColumns = props.primaryColumns;
const nonDataColumnTypes = [
"editActions",
"button",
"iconButton",
"menuButton",
];
const nonDataColumnAliases = primaryColumns
? Object.values(primaryColumns)
.filter((column) => nonDataColumnTypes.includes(column.columnType))
.map((column) => column.alias)
: [];
const keysToBeOmitted = [
"__originalIndex__",
"__primaryKey__",
...nonDataColumnAliases,
];
/*
* case 1. If transientTableData is not empty, return aray of updated row.
* case 2. If transientTableData is empty, return empty array
*
* updated row structure
* {
* index: {{original index of the row}},
* {{primary_column}}: {{primary_column_value}} // only if primary has been set
* updatedFields: {
* {{updated_column_1}}: {{updated_column_1_value}}
* },
* allFields: {
* {{updated_column_1}}: {{updated_column_1_value}}
* {{rest of the fields from the row}}
* }
* }
*/
/* case 1 */
if (
props.transientTableData &&
!!Object.keys(props.transientTableData).length
) {
const updatedRows = [];
const tableData = props.processedTableData || props.tableData;
/* updatedRows is not sorted by index */
Object.entries(props.transientTableData)
.filter((entry) => {
return (
!_.isNil(entry[0]) && !!entry[0] && _.isFinite(Number(entry[0]))
);
})
.forEach((entry) => {
const key = entry[0];
const value = entry[1];
const row = tableData.find(
(row) => row.__originalIndex__ === Number(key),
);
updatedRows.push({
index: Number(key),
[props.primaryColumnId]: row[props.primaryColumnId],
updatedFields: value,
allFields: _.omit(row, keysToBeOmitted) || {},
});
});
return updatedRows;
} else {
/* case 2 */
return [];
}
},
//
getUpdatedRowIndices: (props, moment, _) => {
/* should return the keys of the transientTableData */
if (props.transientTableData) {
return Object.keys(props.transientTableData).map((index) =>
Number(index),
);
} else {
return [];
}
},
//
getPageOffset: (props, moment, _) => {
const pageSize =
props.serverSidePaginationEnabled && props.tableData
? props.tableData?.length
: props.pageSize;
if (
Number.isFinite(props.pageNo) &&
Number.isFinite(pageSize) &&
props.pageNo >= 0 &&
pageSize >= 0
) {
/* Math.max fixes the value of (pageNo - 1) to a minimum of 0 as negative values are not valid */
return Math.max(props.pageNo - 1, 0) * pageSize;
}
return 0;
},
//
getEditableCellValidity: (props, moment, _) => {
if (
fix: null check before accessing editableCell property in table widget (#22500) ## Description This PR adds optional chaining while accessing the properties of `editableCell` object in the table widget. Fixes #20789 ## Type of change - Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? Please refer to the [comment.](https://github.com/appsmithorg/appsmith/issues/20789#issuecomment-1515845278) ### 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 - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag ### QA activity: - [ ] 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
2023-04-20 13:12:31 +00:00
(!props.editableCell?.column && !props.isAddRowInProgress) ||
!props.primaryColumns
) {
return {};
}
const createRegex = (regex) => {
if (!regex) {
return new RegExp("//");
}
/*
* break up the regexp pattern into 4 parts: given regex, regex prefix , regex pattern, regex flags
* Example /test/i will be split into ["/test/gi", "/", "test", "gi"]
*/
const regexParts = regex.match(/(\/?)(.+)\\1([a-z]*)/i);
let parsedRegex;
if (!regexParts) {
parsedRegex = new RegExp(regex);
} else {
/*
* if we don't have a regex flags (gmisuy), convert provided string into regexp directly
*/
if (
regexParts[3] &&
!/^(?!.*?(.).*?\\1)[gmisuy]+$/.test(regexParts[3])
) {
parsedRegex = RegExp(regex);
} else {
/*
* if we have a regex flags, use it to form regexp
*/
parsedRegex = new RegExp(regexParts[2], regexParts[3]);
}
}
return parsedRegex;
};
let editableColumns = [];
const validatableColumns = ["text", "number"];
if (props.isAddRowInProgress) {
Object.values(props.primaryColumns)
.filter(
(column) =>
column.isEditable && validatableColumns.includes(column.columnType),
)
.forEach((column) => {
editableColumns.push([column, props.newRow[column.alias]]);
});
} else {
const editedColumn = Object.values(props.primaryColumns).find(
fix: null check before accessing editableCell property in table widget (#22500) ## Description This PR adds optional chaining while accessing the properties of `editableCell` object in the table widget. Fixes #20789 ## Type of change - Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? Please refer to the [comment.](https://github.com/appsmithorg/appsmith/issues/20789#issuecomment-1515845278) ### 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 - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag ### QA activity: - [ ] 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
2023-04-20 13:12:31 +00:00
(column) => column.alias === props.editableCell?.column,
);
if (validatableColumns.includes(editedColumn.columnType)) {
fix: null check before accessing editableCell property in table widget (#22500) ## Description This PR adds optional chaining while accessing the properties of `editableCell` object in the table widget. Fixes #20789 ## Type of change - Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? Please refer to the [comment.](https://github.com/appsmithorg/appsmith/issues/20789#issuecomment-1515845278) ### 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 - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag ### QA activity: - [ ] 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
2023-04-20 13:12:31 +00:00
editableColumns.push([editedColumn, props.editableCell?.value]);
}
}
const validationMap = {};
editableColumns.forEach((validationObj) => {
const editedColumn = validationObj[0];
const value = validationObj[1];
if (editedColumn && editedColumn.validation) {
const validation = editedColumn.validation;
/* General validations */
if (
!validation.isColumnEditableCellRequired &&
(value === "" || _.isNil(value))
) {
validationMap[editedColumn.alias] = true;
return;
} else if (
(!_.isNil(validation.isColumnEditableCellValid) &&
!validation.isColumnEditableCellValid) ||
(validation.regex && !createRegex(validation.regex).test(value)) ||
(validation.isColumnEditableCellRequired &&
(value === "" || _.isNil(value)))
) {
validationMap[editedColumn.alias] = false;
return;
}
/* Column type related validations */
switch (editedColumn.columnType) {
case "number":
if (
!_.isNil(validation.min) &&
validation.min !== "" &&
validation.min > value
) {
validationMap[editedColumn.alias] = false;
return;
}
if (
!_.isNil(validation.max) &&
validation.max !== "" &&
validation.max < value
) {
validationMap[editedColumn.alias] = false;
return;
}
break;
}
}
validationMap[editedColumn.alias] = true;
});
return validationMap;
},
//
getTableHeaders: (props, moment, _) => {
const columns = props.primaryColumns
? Object.values(props.primaryColumns)
: [];
feat: Table one click binding for MongoDB and Postgres (#23629) > Pull Request Template > > Use this template to quickly create a well written pull request. Delete all quotes before creating the pull request. > ## Description > Add a TL;DR when description is extra long (helps content team) > > Please include a summary of the changes and which issue has been fixed. Please also include relevant motivation > and context. List any dependencies that are required for this change > > Links to Notion, Figma or any other documents that might be relevant to the PR > > #### PR fixes following issue(s) Fixes # (issue number) > if no issue exists, please create an issue and ask the maintainers about this first > > #### Media > A video or a GIF is preferred. when using Loom, don’t embed because it looks like it’s a GIF. instead, just link to the video > > #### Type of change > Please delete options that are not relevant. - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) - Breaking change (fix or feature that would cause existing functionality to not work as expected) - Chore (housekeeping or task changes that don't impact user perception) - This change requires a documentation update > > > ## Testing > #### How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Also list any relevant details for your test configuration. > Delete anything that is not relevant - [x] Manual - [x] Jest - [x] Cypress > > #### Test Plan > One Click Binding - https://github.com/appsmithorg/TestSmith/issues/2390 > #### 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 - [x] I have performed a self-review of my own code - [x] 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 - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag #### QA activity: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Test-plan-implementation#speedbreaker-features-to-consider-for-every-change) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans/_edit#areas-of-interest) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed --------- Co-authored-by: Vemparala Surya Vamsi <vamsi@appsmith.com>
2023-06-01 17:26:05 +00:00
return columns
.sort((a, b) => a.index - b.index)
.map((column) => ({
id: column?.id,
label: column?.label,
isVisible: column?.isVisible,
}));
},
//
};