Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2b626c1
feat: Add HTML column type and HTMLCell component to TableWidgetV2
rahulbarwal Dec 4, 2024
17eecc7
feat: Add unit tests for TableWidgetV2 cell components
rahulbarwal Dec 4, 2024
62267ea
feat: add HTML column type and update CSV transformation logic
rahulbarwal Dec 4, 2024
9fc0503
feat: Add support for HTML column type in TableWidgetV2
rahulbarwal Dec 5, 2024
5965306
- Changed the columnType from "text" to "html" in the derived test fo…
rahulbarwal Dec 5, 2024
19f7f3b
- Updated the filtering mechanism in derived.js to handle HTML column…
rahulbarwal Dec 5, 2024
a751320
Adds comments for readability.
rahulbarwal Dec 5, 2024
d31d2d8
Enhance HTML column handling in TableWidgetV2 tests
rahulbarwal Dec 5, 2024
21c9eee
Refactor comments in derived.js for clarity on HTML handling
rahulbarwal Dec 5, 2024
0954018
Updates imports
rahulbarwal Dec 6, 2024
e9e554f
Refactor TableWidgetV2 filter operators to use structured objects
rahulbarwal Dec 6, 2024
fff05a8
- Updated the getTextFromHTML function to return an empty string for …
rahulbarwal Dec 6, 2024
a432d7c
refactor
rahulbarwal Dec 6, 2024
7b7fe4b
Merge branch 'release' of https://github.com/appsmithorg/appsmith int…
rahulbarwal Dec 7, 2024
bdd9bc5
Adds feature flag for html column type.
rahulbarwal Dec 7, 2024
fe5be6b
Handles column type for value of feature flag for html col
rahulbarwal Dec 7, 2024
005e6fa
Implement safety net for HTML column type in TableWidgetV2; update HT…
rahulbarwal Dec 7, 2024
b193802
Adds event for HTML usage
rahulbarwal Dec 9, 2024
0063ed2
Implement HTMLCell component for TableWidgetV2 with analytics tracking
rahulbarwal Dec 9, 2024
e10781f
adds missing prop rendermode to tests
rahulbarwal Dec 9, 2024
46793ad
Add HTML column type support in TableWidgetV2 and implement related t…
rahulbarwal Dec 9, 2024
1ab1818
Enhance getColumnType utility to support HTML detection in TableWidgetV2
rahulbarwal Dec 9, 2024
3d017b8
Refactor HTMLCell tests in TableWidgetV2: update test cases and add s…
rahulbarwal Dec 9, 2024
29595a0
Refactor: updates variable name to a meaningful name
rahulbarwal Dec 9, 2024
e3aaf42
Refactor HTMLCell component in TableWidgetV2
rahulbarwal Dec 9, 2024
46c29c8
Merge branch 'release' of https://github.com/appsmithorg/appsmith int…
rahulbarwal Dec 9, 2024
aecd362
- Introduced tags for better categorization of test cases.
rahulbarwal Dec 9, 2024
c1ec9f3
* Wraps string case in a block.
rahulbarwal Dec 9, 2024
532bc70
Removes unnecessary comments.
rahulbarwal Dec 9, 2024
51cd403
- Enhanced logic for determining virtual row usage based on column pr…
rahulbarwal Dec 10, 2024
5f873fc
Refactor HTMLCell tests: move test data to a separate fixture file
rahulbarwal Dec 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { htmlTableData } from "../../../../../../fixtures/htmlCellInTableWidgetV2";
import { featureFlagIntercept } from "../../../../../../support/Objects/FeatureFlags";
import {
agHelper,
entityExplorer,
propPane,
table,
} from "../../../../../../support/Objects/ObjectsCore";

describe(
"Table Filter for HTML Cell",
{ tags: ["@tag.Widget", "@tag.Table"] },
function () {
before(() => {
featureFlagIntercept({
release_table_html_column_type_enabled: true,
});
entityExplorer.DragDropWidgetNVerify("tablewidgetv2", 650, 250);
propPane.EnterJSContext("Table data", JSON.stringify(htmlTableData));
});

it("1. Ensures HTML column type is available", function () {
table.ReadTableRowColumnData(1, 3, "v2").then(($cellData) => {
expect($cellData).to.include("Active");
});
});

it("2. Verify HTML columns are searchable", function () {
table.ReadTableRowColumnData(1, 3, "v2").then(($cellData) => {
expect($cellData).to.include("Active");
table.SearchTable($cellData);
table.ReadTableRowColumnData(0, 3, "v2").then((afterSearch) => {
expect(afterSearch).to.eq($cellData);
});
});
table.RemoveSearchTextNVerify("1", "v2");
});

it("3. Verify Table Filter for HTML columns", function () {
propPane.ExpandIfCollapsedSection("search\\&filters");
agHelper.AssertExistingToggleState("Allow filtering", "false");
propPane.TogglePropertyState("Allow filtering", "On");

table.OpenNFilterTable("status", "contains", "Active");
table.ReadTableRowColumnData(0, 3, "v2").then(($cellData) => {
expect($cellData).to.include("Active");
});
table.RemoveFilterNVerify("1", true, true, 0, "v2");

table.OpenNFilterTable("status", "contains", "Suspended");
table.ReadTableRowColumnData(0, 3, "v2").then(($cellData) => {
expect($cellData).to.include("Suspended");
});
table.RemoveFilterNVerify("1", true, true, 0, "v2");

table.OpenNFilterTable("status", "empty", "");
table.ReadTableRowColumnData(0, 0, "v2").then(($cellData) => {
expect($cellData).to.include("1");
});
table.RemoveFilterNVerify("1", true, true, 0, "v2");

table.OpenNFilterTable("status", "not empty", "");
table.ReadTableRowColumnData(0, 0, "v2").then(($cellData) => {
expect($cellData).to.include("2");
});
table.RemoveFilterNVerify("1", true, true, 0, "v2");
});

it("4. Verify Table sorting for HTML columns", function () {
table.SortColumn("status", "asc");
table.ReadTableRowColumnData(0, 3, "v2").then(($cellData) => {
expect($cellData).to.include("Active");
});
table.SortColumn("status", "desc");
table.ReadTableRowColumnData(0, 3, "v2").then(($cellData) => {
expect($cellData).to.include("Suspended");
});
});
},
);
56 changes: 56 additions & 0 deletions app/client/cypress/fixtures/htmlCellInTableWidgetV2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
export const htmlTableData = [
{
id: 1,
name: "John Smith",
email: "john.smith@email.com",
role: undefined,
status: null,
applicationDate: "2024-02-15",
lastUpdated: "2024-03-20",
department: "Engineering",
},
{
id: 2,
name: "Emma Wilson",
email: "emma.w@email.com",
role: "Designer",
status:
"<span style='background-color: #22c55e; color: white; padding: 4px 12px; border-radius: 20px; font-size: 14px;'><strong>Active</strong></span>",
applicationDate: "2024-03-01",
lastUpdated: "2024-03-19",
department: "Design",
},
{
id: 3,
name: "Michael Brown",
email: "m.brown@email.com",
role: "Manager",
status:
"<span style='background-color: #ef4444; color: white; padding: 4px 12px; border-radius: 20px; font-size: 14px;'><strong>Suspended</strong></span>",
applicationDate: "2024-01-10",
lastUpdated: "2024-03-18",
department: "Operations",
},
{
id: 4,
name: "Sarah Davis",
email: "sarah.d@email.com",
role: "Developer",
status:
"<span style='background-color: #22c55e; color: white; padding: 4px 12px; border-radius: 20px; font-size: 14px;'><strong>Active</strong></span>",
applicationDate: "2024-02-20",
lastUpdated: "2024-03-17",
department: "Engineering",
},
{
id: 5,
name: "James Wilson",
email: "j.wilson@email.com",
role: "Analyst",
status:
"<span style='background-color: #3b82f6; color: white; padding: 4px 12px; border-radius: 20px; font-size: 14px;'><strong>Reviewing</strong></span>",
applicationDate: "2024-03-05",
lastUpdated: "2024-03-16",
department: "Analytics",
},
];
3 changes: 2 additions & 1 deletion app/client/cypress/support/Pages/Table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ type columnTypeValues =
| "Button"
| "Menu button"
| "Icon button"
| "Select";
| "Select"
| "HTML";

export class Table {
private agHelper = ObjectsRegistry.AggregateHelper;
Expand Down
3 changes: 3 additions & 0 deletions app/client/src/ce/entities/FeatureFlag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ export const FEATURE_FLAG = {
"release_table_custom_loading_state_enabled",
release_custom_widget_ai_builder: "release_custom_widget_ai_builder",
ab_request_new_integration_enabled: "ab_request_new_integration_enabled",
release_table_html_column_type_enabled:
"release_table_html_column_type_enabled",
} as const;

export type FeatureFlag = keyof typeof FEATURE_FLAG;
Expand Down Expand Up @@ -81,6 +83,7 @@ export const DEFAULT_FEATURE_FLAG_VALUE: FeatureFlags = {
release_table_custom_loading_state_enabled: false,
release_custom_widget_ai_builder: false,
ab_request_new_integration_enabled: false,
release_table_html_column_type_enabled: false,
};

export const AB_TESTING_EVENT_KEYS = {
Expand Down
3 changes: 2 additions & 1 deletion app/client/src/ce/utils/analyticsUtilTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,8 @@ export type EventName =
| "CANVAS_HOVER"
| "MALFORMED_USAGE_PULSE"
| "REQUEST_INTEGRATION_CTA"
| "REQUEST_INTEGRATION_SUBMITTED";
| "REQUEST_INTEGRATION_SUBMITTED"
| "TABLE_WIDGET_V2_HTML_CELL_USAGE";

type HOMEPAGE_CREATE_APP_FROM_TEMPLATE_EVENTS =
| "TEMPLATE_DROPDOWN_CLICK"
Expand Down
17 changes: 15 additions & 2 deletions app/client/src/widgets/TableWidgetV2/component/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ import {
} from "./Constants";
import { Colors } from "constants/Colors";
import type { EventType } from "constants/AppsmithActionConstants/ActionConstants";
import type { EditableCell, TableVariant } from "../constants";
import {
ColumnTypes,
type EditableCell,
type TableVariant,
} from "../constants";
import SimpleBar from "simplebar-react";
import "simplebar-react/dist/simplebar.min.css";
import { createGlobalStyle } from "styled-components";
Expand Down Expand Up @@ -323,10 +327,19 @@ export function Table(props: TableProps) {
props.width,
]);

/**
* What this really translates is to fixed height rows:
* shouldUseVirtual: false -> fixed height row, irrespective of content small or big
* shouldUseVirtual: true -> height adjusts acc to content
* Right now all HTML content is dynamic height in nature hence
* for server paginated tables it needs this extra handling.
*/
const shouldUseVirtual =
props.serverSidePaginationEnabled &&
!props.columns.some(
(column) => !!column.columnProperties.allowCellWrapping,
(column) =>
!!column.columnProperties.allowCellWrapping ||
column.metaProperties?.type === ColumnTypes.HTML,
);

useEffect(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import type { RenderMode } from "constants/WidgetConstants";
import Interweave from "interweave";
import { isEqual } from "lodash";
import React, { useEffect, useMemo, useRef } from "react";
import styled from "styled-components";
import LinkFilter from "widgets/TextWidget/component/filters/LinkFilter";
import type { BaseCellComponentProps } from "../../Constants";
import { CellWrapper } from "../../TableStyledWrappers";
import { extractHTMLTags, sendHTMLCellAnalytics } from "./utils";

const HTMLContainer = styled.div`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI there is actually a name for such components that styles basic HTML elements. It's called "Prose". https://design-system.agriculture.gov.au/components/prose

If this was WDS, I would have created Prose component and use it here and in the text component instead of repeating.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, I am saying not to do this in this PR, this is just an FYI

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good to know! will keep this in mind. Thanks.

& {
height: 100%;
width: 100%;
position: relative;
}
ul {
list-style-type: disc;
list-style-position: inside;
}
ol {
list-style-type: decimal;
list-style-position: inside;
}
ul ul,
ol ul {
list-style-type: circle;
list-style-position: inside;
margin-left: 15px;
}
ol ol,
ul ol {
list-style-type: lower-latin;
list-style-position: inside;
margin-left: 15px;
}
h1 {
font-size: 2em;
margin: 0.67em 0;
}
h2 {
font-size: 1.5em;
margin: 0.75em 0;
}
h3 {
font-size: 1.17em;
margin: 0.83em 0;
}
h5 {
font-size: 0.83em;
margin: 1.5em 0;
}
h6 {
font-size: 0.75em;
margin: 1.67em 0;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: bold;
}
a {
color: #106ba3;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
`;

export interface HTMLCellProps extends BaseCellComponentProps {
value: string;
fontSize?: string;
renderMode: RenderMode;
}

const HTMLCell = (props: HTMLCellProps) => {
const {
allowCellWrapping,
cellBackground,
compactMode,
fontStyle,
horizontalAlignment,
isCellDisabled,
isCellVisible,
isHidden,
renderMode,
textColor,
textSize,
value,
verticalAlignment,
} = props;

const previousTagsRef = useRef<string[]>([]);

const interweaveCompatibleValue = useMemo(() => {
if (value === null || value === undefined) return "";

return String(value);
}, [value]);

/**
* For analytics, we want to know what tags are being used by users in HTMLCell?
* This will help us in knowing usage patterns and identifying if something is not working out.
*/
const extractedTags = useMemo(() => {
if (!interweaveCompatibleValue) return [];

return extractHTMLTags(interweaveCompatibleValue);
}, [interweaveCompatibleValue]);

useEffect(() => {
const areTagsChanged = !isEqual(
[...extractedTags].sort(),
[...previousTagsRef.current].sort(),
);

if (extractedTags.length > 0 && areTagsChanged) {
sendHTMLCellAnalytics(extractedTags);
previousTagsRef.current = extractedTags;
}
}, [extractedTags, renderMode]);

return (
<CellWrapper
allowCellWrapping={allowCellWrapping}
cellBackground={cellBackground}
className="cell-wrapper"
compactMode={compactMode}
fontStyle={fontStyle}
horizontalAlignment={horizontalAlignment}
isCellDisabled={isCellDisabled}
isCellVisible={isCellVisible}
isHidden={isHidden}
textColor={textColor}
textSize={textSize}
verticalAlignment={verticalAlignment}
>
<HTMLContainer data-testid="t--table-widget-v2-html-cell">
<Interweave
content={interweaveCompatibleValue}
filters={[new LinkFilter()]}
newWindow
/>
</HTMLContainer>
</CellWrapper>
);
};

export default HTMLCell;
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import AnalyticsUtil from "ee/utils/AnalyticsUtil";
import { debounce } from "lodash";

export const sendHTMLCellAnalytics = debounce(
(tags: string[]) => {
AnalyticsUtil.logEvent("TABLE_WIDGET_V2_HTML_CELL_USAGE", {
tags: tags,
});
},
1000,
{ leading: true, trailing: false, maxWait: 5000 },
);

export function extractHTMLTags(htmlString: string): string[] {
const div = document.createElement("div");

div.innerHTML = htmlString;
const elements = Array.from(div.getElementsByTagName("*"));
const uniqueTags = new Set(
elements.map((element) => element.tagName.toLowerCase()),
);

return Array.from(uniqueTags);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from "react";
import { fireEvent, render, screen } from "@testing-library/react";
import AutoToolTipComponent from "./AutoToolTipComponent";
import AutoToolTipComponent from "../AutoToolTipComponent";
import { ColumnTypes } from "widgets/TableWidgetV2/constants";
import "@testing-library/jest-dom";
import { isButtonTextTruncated } from "./AutoToolTipComponent";
import { isButtonTextTruncated } from "../AutoToolTipComponent";

jest.mock("react", () => {
const actualReact = jest.requireActual("react");
Expand Down
Loading