Skip to content

Commit

Permalink
feat: POC-4 HTML Column Integration with table features (#37654)
Browse files Browse the repository at this point in the history
## Description
> [!TIP]  
> _Add a TL;DR when the description is longer than 500 words or
extremely technical (helps the content, marketing, and DevRel team)._
>
> _Please also include relevant motivation and context. List any
dependencies that are required for this change. Add links to Notion,
Figma or any other documents that might be relevant to the PR._


Fixes #`Issue Number`  
_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=""

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!CAUTION]  
> If you modify the content in this section, you are likely to disrupt
the CI result for your PR.

<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No
  • Loading branch information
rahulbarwal authored Nov 29, 2024
1 parent 14ce4cb commit 0026e28
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 70 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ColumnTypes } from "widgets/TableWidgetV2/constants";
import type { TableColumnProps } from "../../Constants";
import { isString } from "lodash";

Expand Down Expand Up @@ -35,7 +36,11 @@ export const transformTableDataIntoCsv = (props: {
? value.replace("\n", " ")
: value;

if (isString(value) && value.includes(",")) {
const shouldQuote =
(isString(value) && value.includes(",")) ||
column.metaProperties.type === ColumnTypes.HTML;

if (shouldQuote) {
csvDataRow.push(`"${value}"`);
} else {
csvDataRow.push(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ import { RenderOptionWrapper } from "../../../TableStyledWrappers";
//TODO(abhinav): Fix this cross import between widgets
import DatePickerComponent from "widgets/DatePickerWidget2/component";
import { TimePrecision } from "widgets/DatePickerWidget2/constants";
import { ColumnTypes, ReadOnlyColumnTypes } from "../../../../constants";
import {
ColumnTypes,
FILTER_OPERATORS,
ReadOnlyColumnTypes,
} from "../../../../constants";
import { importRemixIcon } from "@appsmith/ads-old";

const CloseIcon = importRemixIcon(
Expand Down Expand Up @@ -112,73 +116,65 @@ const AutoToolTipComponentWrapper = styled(AutoToolTipComponent)`

const typeOperatorsMap: Record<ReadOnlyColumnTypes, DropdownOption[]> = {
[ColumnTypes.TEXT]: [
{ label: "contains", value: "contains", type: "input" },
{ label: "does not contain", value: "doesNotContain", type: "input" },
{ label: "starts with", value: "startsWith", type: "input" },
{ label: "ends with", value: "endsWith", type: "input" },
{ label: "is exactly", value: "isExactly", type: "input" },
{ label: "empty", value: "empty", type: "" },
{ label: "not empty", value: "notEmpty", type: "" },
FILTER_OPERATORS.CONTAINS,
FILTER_OPERATORS.DOES_NOT_CONTAIN,
FILTER_OPERATORS.STARTS_WITH,
FILTER_OPERATORS.ENDS_WITH,
FILTER_OPERATORS.IS_EXACTLY,
FILTER_OPERATORS.EMPTY,
FILTER_OPERATORS.NOT_EMPTY,
],
[ColumnTypes.URL]: [
{ label: "contains", value: "contains", type: "input" },
{ label: "does not contain", value: "doesNotContain", type: "input" },
{ label: "starts with", value: "startsWith", type: "input" },
{ label: "ends with", value: "endsWith", type: "input" },
{ label: "is exactly", value: "isExactly", type: "input" },
{ label: "empty", value: "empty", type: "" },
{ label: "not empty", value: "notEmpty", type: "" },
FILTER_OPERATORS.CONTAINS,
FILTER_OPERATORS.DOES_NOT_CONTAIN,
FILTER_OPERATORS.STARTS_WITH,
FILTER_OPERATORS.ENDS_WITH,
FILTER_OPERATORS.IS_EXACTLY,
FILTER_OPERATORS.EMPTY,
FILTER_OPERATORS.NOT_EMPTY,
],
[ColumnTypes.DATE]: [
{ label: "is", value: "is", type: "date" },
{ label: "is before", value: "isBefore", type: "date" },
{ label: "is after", value: "isAfter", type: "date" },
{ label: "is not", value: "isNot", type: "date" },
{ label: "empty", value: "empty", type: "" },
{ label: "not empty", value: "notEmpty", type: "" },
],
[ColumnTypes.IMAGE]: [
{ label: "empty", value: "empty", type: "" },
{ label: "not empty", value: "notEmpty", type: "" },
],
[ColumnTypes.VIDEO]: [
{ label: "empty", value: "empty", type: "" },
{ label: "not empty", value: "notEmpty", type: "" },
FILTER_OPERATORS.IS,
FILTER_OPERATORS.IS_BEFORE,
FILTER_OPERATORS.IS_AFTER,
FILTER_OPERATORS.IS_NOT,
FILTER_OPERATORS.EMPTY,
FILTER_OPERATORS.NOT_EMPTY,
],
[ColumnTypes.IMAGE]: [FILTER_OPERATORS.EMPTY, FILTER_OPERATORS.NOT_EMPTY],
[ColumnTypes.VIDEO]: [FILTER_OPERATORS.EMPTY, FILTER_OPERATORS.NOT_EMPTY],
[ColumnTypes.NUMBER]: [
{ label: "is equal to", value: "isEqualTo", type: "input" },
{ label: "not equal to", value: "notEqualTo", type: "input" },
{ label: "greater than", value: "greaterThan", type: "input" },
{
label: "greater than or equal to",
value: "greaterThanEqualTo",
type: "input",
},
{ label: "less than", value: "lessThan", type: "input" },
{
label: "less than or equal to",
value: "lessThanEqualTo",
type: "input",
},
{ label: "empty", value: "empty", type: "" },
{ label: "not empty", value: "notEmpty", type: "" },
FILTER_OPERATORS.IS_EQUAL_TO,
FILTER_OPERATORS.NOT_EQUAL_TO,
FILTER_OPERATORS.GREATER_THAN,
FILTER_OPERATORS.GREATER_THAN_EQUAL_TO,
FILTER_OPERATORS.LESS_THAN,
FILTER_OPERATORS.LESS_THAN_EQUAL_TO,
FILTER_OPERATORS.EMPTY,
FILTER_OPERATORS.NOT_EMPTY,
],
[ColumnTypes.CHECKBOX]: [
{ label: "is checked", value: "isChecked", type: "" },
{ label: "is unchecked", value: "isUnChecked", type: "" },
FILTER_OPERATORS.IS_CHECKED,
FILTER_OPERATORS.IS_UNCHECKED,
],
[ColumnTypes.SWITCH]: [
{ label: "is checked", value: "isChecked", type: "" },
{ label: "is unchecked", value: "isUnChecked", type: "" },
FILTER_OPERATORS.IS_CHECKED,
FILTER_OPERATORS.IS_UNCHECKED,
],
[ColumnTypes.SELECT]: [
{ label: "contains", value: "contains", type: "input" },
{ label: "does not contain", value: "doesNotContain", type: "input" },
{ label: "starts with", value: "startsWith", type: "input" },
{ label: "ends with", value: "endsWith", type: "input" },
{ label: "is exactly", value: "isExactly", type: "input" },
{ label: "empty", value: "empty", type: "" },
{ label: "not empty", value: "notEmpty", type: "" },
FILTER_OPERATORS.CONTAINS,
FILTER_OPERATORS.DOES_NOT_CONTAIN,
FILTER_OPERATORS.STARTS_WITH,
FILTER_OPERATORS.ENDS_WITH,
FILTER_OPERATORS.IS_EXACTLY,
FILTER_OPERATORS.EMPTY,
FILTER_OPERATORS.NOT_EMPTY,
],
[ColumnTypes.HTML]: [
FILTER_OPERATORS.CONTAINS,
FILTER_OPERATORS.DOES_NOT_CONTAIN,
FILTER_OPERATORS.EMPTY,
FILTER_OPERATORS.NOT_EMPTY,
],
};

Expand All @@ -197,6 +193,7 @@ const columnTypeNameMap: Record<ReadOnlyColumnTypes, string> = {
[ReadOnlyColumnTypes.CHECKBOX]: "Check",
[ReadOnlyColumnTypes.SWITCH]: "Check",
[ReadOnlyColumnTypes.SELECT]: "Text",
[ReadOnlyColumnTypes.HTML]: "HTML",
};

function RenderOption(props: { type: string; title: string; active: boolean }) {
Expand Down
44 changes: 44 additions & 0 deletions app/client/src/widgets/TableWidgetV2/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ export enum ReadOnlyColumnTypes {
CHECKBOX = "checkbox",
SWITCH = "switch",
SELECT = "select",
HTML = "html",
}

export const ActionColumnTypes = [
Expand All @@ -176,6 +177,7 @@ export const FilterableColumnTypes = [
ColumnTypes.SELECT,
ColumnTypes.CHECKBOX,
ColumnTypes.SWITCH,
ColumnTypes.HTML,
];

export const DEFAULT_BUTTON_COLOR = "rgb(3, 179, 101)";
Expand Down Expand Up @@ -243,3 +245,45 @@ export const ALLOW_TABLE_WIDGET_SERVER_SIDE_FILTERING =

export const CUSTOM_LOADING_STATE_ENABLED =
FEATURE_FLAG["release_table_custom_loading_state_enabled"];

export const FILTER_OPERATORS = {
CONTAINS: { label: "contains", value: "contains", type: "input" },
DOES_NOT_CONTAIN: {
label: "does not contain",
value: "doesNotContain",
type: "input",
},
STARTS_WITH: {
label: "starts with",
value: "startsWith",
type: "input",
},
ENDS_WITH: { label: "ends with", value: "endsWith", type: "input" },
EMPTY: { label: "empty", value: "empty", type: "" },
NOT_EMPTY: { label: "not empty", value: "notEmpty", type: "" },
IS_EXACTLY: { label: "is exactly", value: "isExactly", type: "input" },
IS_CHECKED: { label: "is checked", value: "isChecked", type: "" },
IS_UNCHECKED: { label: "is unchecked", value: "isUnChecked", type: "" },
IS: { label: "is", value: "is", type: "date" },
IS_BEFORE: { label: "is before", value: "isBefore", type: "date" },
IS_AFTER: { label: "is after", value: "isAfter", type: "date" },
IS_NOT: { label: "is not", value: "isNot", type: "date" },
IS_EQUAL_TO: { label: "is equal to", value: "isEqualTo", type: "input" },
NOT_EQUAL_TO: { label: "not equal to", value: "notEqualTo", type: "input" },
GREATER_THAN: {
label: "greater than",
value: "greaterThan",
type: "input",
},
GREATER_THAN_EQUAL_TO: {
label: "greater than or equal to",
value: "greaterThanEqualTo",
type: "input",
},
LESS_THAN: { label: "less than", value: "lessThan", type: "input" },
LESS_THAN_EQUAL_TO: {
label: "less than or equal to",
value: "lessThanEqualTo",
type: "input",
},
};
57 changes: 44 additions & 13 deletions app/client/src/widgets/TableWidgetV2/widget/derived.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,16 @@ export default {
return [];
}

const getTextFromHTML = (html) => {
if (!html) return "";

const div = document.createElement("div");

div.innerHTML = html;

return div.textContent || div.innerText || "";
};

/* extend processedTableData with values from
* - computedValues, in case of normal column
* - empty values, in case of derived column
Expand Down Expand Up @@ -504,6 +514,15 @@ export default {
);
}
}
case "html":
return sortByOrder(
getTextFromHTML(
processedA[sortByColumnOriginalId],
).toLowerCase() >
getTextFromHTML(
processedB[sortByColumnOriginalId],
).toLowerCase(),
);
default:
return sortByOrder(
processedA[sortByColumnOriginalId].toString().toLowerCase() >
Expand Down Expand Up @@ -699,6 +718,9 @@ export default {
const columnWithDisplayText = Object.values(props.primaryColumns).filter(
(column) => column.columnType === "url" && column.displayText,
);
const columnWithHTML = Object.values(props.primaryColumns).filter(
(column) => column.columnType === "html",
);

/*
* For select columns with label and values, we need to include the label value
Expand Down Expand Up @@ -781,22 +803,31 @@ export default {
});
}

const displayedRow = {
...row,
...labelValuesForSelectCell,
...columnWithDisplayText.reduce((acc, column) => {
let displayText;
const HTMLValues = columnWithHTML.reduce((acc, column) => {
acc[column.alias] = getTextFromHTML(row[column.alias]);

if (_.isArray(column.displayText)) {
displayText = column.displayText[row.__originalIndex__];
} else {
displayText = column.displayText;
}
return acc;
}, {});

const URLValues = columnWithDisplayText.reduce((acc, column) => {
let displayText;

if (_.isArray(column.displayText)) {
displayText = column.displayText[row.__originalIndex__];
} else {
displayText = column.displayText;
}

acc[column.alias] = displayText;

acc[column.alias] = displayText;
return acc;
}, {});

return acc;
}, {}),
const displayedRow = {
...row,
...labelValuesForSelectCell,
...URLValues,
...HTMLValues,
};

if (searchKey) {
Expand Down

0 comments on commit 0026e28

Please sign in to comment.