Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
c953cd2
adds custom sort function feature flag for table widget
rahulbarwal Mar 10, 2025
e972096
updates custom sort function visibility condition for table widget
rahulbarwal Mar 10, 2025
3e19295
updates InputTextControl and TableWidgetV2 with custom sort function …
rahulbarwal Mar 11, 2025
06bafb2
adds TableCustomSortControl component and tests for custom sorting fu…
rahulbarwal Mar 12, 2025
fa71ef2
updates TableWidgetV2 to support custom sort function data in data tr…
rahulbarwal Mar 12, 2025
eb6f6de
updates TableCustomSortControl to enhance custom sorting logic and im…
rahulbarwal Mar 14, 2025
1ec2edc
updates TableCustomSortControl tests to enhance computation expressio…
rahulbarwal Mar 14, 2025
11fb75a
updates TableWidgetV2 to improve custom sorting logic by conditionall…
rahulbarwal Mar 14, 2025
60065ae
fix: correct custom sort function visibility condition in TableWidgetV2
rahulbarwal Mar 14, 2025
99ad424
Reverts unnecessary changes
rahulbarwal Mar 14, 2025
bad817b
Merge branch 'release' of https://github.com/appsmithorg/appsmith int…
rahulbarwal Mar 14, 2025
3f91704
Merge branch 'release' of https://github.com/appsmithorg/appsmith int…
rahulbarwal Mar 18, 2025
9509824
fix: update custom sort function arguments in TableWidgetV2 configura…
rahulbarwal Mar 18, 2025
c9d12a2
refactor: Update VirtualList to improve load more button styling and …
rahulbarwal Mar 20, 2025
95dc092
test: add unit test for handling original data in computed value
rahulbarwal Mar 20, 2025
ade1c38
fix: handle empty sort order in TableCustomSortControl
rahulbarwal Mar 20, 2025
b64c291
removes unnecessary release_fn_calling_enabled feature flag from Feat…
rahulbarwal Mar 21, 2025
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
5 changes: 5 additions & 0 deletions app/client/src/ce/entities/FeatureFlag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ export const FEATURE_FLAG = {
"release_external_saas_plugins_enabled",
release_tablev2_infinitescroll_enabled:
"release_tablev2_infinitescroll_enabled",
release_fn_calling_enabled: "release_fn_calling_enabled",
release_table_custom_sort_function_enabled:
"release_table_custom_sort_function_enabled",
} as const;

export type FeatureFlag = keyof typeof FEATURE_FLAG;
Expand Down Expand Up @@ -97,6 +100,8 @@ export const DEFAULT_FEATURE_FLAG_VALUE: FeatureFlags = {
release_ads_entity_item_enabled: false,
release_external_saas_plugins_enabled: false,
release_tablev2_infinitescroll_enabled: false,
release_fn_calling_enabled: false,
release_table_custom_sort_function_enabled: false,
};

export const AB_TESTING_EVENT_KEYS = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { EditorTheme } from "components/editorComponents/CodeEditor/EditorConfig";
import TableCustomSortControl from "./TableCustomSortControl";

const requiredParams = {
evaluatedValue: "",
widgetProperties: {
widgetName: "Table1",
primaryColumns: {
id: {
alias: "id",
originalId: "id",
},
name: {
alias: "name",
originalId: "name",
},
},
},
parentPropertyName: "",
parentPropertyValue: "",
additionalDynamicData: {},
label: "Custom Sort",
propertyName: "customSort",
controlType: "TABLE_CUSTOM_SORT",
isBindProperty: true,
isTriggerProperty: false,
onPropertyChange: jest.fn(),
openNextPanel: jest.fn(),
deleteProperties: jest.fn(),
hideEvaluatedValue: false,
placeholderText: "Enter custom sort logic",
};

describe("TableCustomSortControl.getInputComputedValue", () => {
// Create an instance to use for generating test data
const testInstance = new TableCustomSortControl({
...requiredParams,
theme: EditorTheme.LIGHT,
});

it("should extract computation expression correctly from binding string with arrow function", () => {
const sortExpression = "tableData.sort((a, b) => a.id - b.id)";

const bindingString = testInstance.getComputedValue(
`{{${sortExpression}}}`,
"Table1",
);

const result = TableCustomSortControl.getInputComputedValue(bindingString);

expect(result).toBe(`{{${sortExpression}}}`);
});

it("should extract computation expression correctly from binding string with function body", () => {
const sortExpression = `{
return tableData.filter(row => row.original_status === "active");
}`;

const bindingString = testInstance.getComputedValue(
`{{${sortExpression}}}`,
"Table1",
);

const result = TableCustomSortControl.getInputComputedValue(bindingString);

expect(result).toBe(`{{${sortExpression}}}`);
});

it("should return original value when binding string doesn't match expected format", () => {
const nonMatchingString = "{{Table1.tableData[0].id}}";
const result =
TableCustomSortControl.getInputComputedValue(nonMatchingString);

expect(result).toBe(nonMatchingString);
});

it("should handle empty string input", () => {
const emptyString = "";
const result = TableCustomSortControl.getInputComputedValue(emptyString);

expect(result).toBe(emptyString);
});

it("should support round-trip conversion (getComputedValue -> getInputComputedValue)", () => {
// Test with arrow function
const arrowExpression =
"tableData.sort((a, b) => a.name.localeCompare(b.name))";
const arrowBindingString = testInstance.getComputedValue(
arrowExpression,
"Table1",
);
const arrowResult =
TableCustomSortControl.getInputComputedValue(arrowBindingString);

expect(arrowResult).toBe(arrowExpression);

// Test with function body
const bodyExpression = `{
const direction = order === "asc" ? 1 : -1;
return tableData.sort((a, b) => direction * (a[column] - b[column]));
}`;
const bodyBindingString = testInstance.getComputedValue(
`{{${bodyExpression}}}`,
"Table1",
);
const bodyResult =
TableCustomSortControl.getInputComputedValue(bodyBindingString);
// We need to normalize the whitespace for this comparison
const normalizeWhitespace = (str: string) =>
str.replace(/\s+/g, " ").trim();

expect(normalizeWhitespace(bodyResult)).toBe(
normalizeWhitespace(`{{${bodyExpression}}}`),
);
});
});

describe("TableCustomSortControl instance methods", () => {
const testInstance = new TableCustomSortControl({
...requiredParams,
theme: EditorTheme.LIGHT,
});

it("generates correct binding with getComputedValue", () => {
const inputValue = "{{tableData.sort((a, b) => a.id - b.id)}}";
const result = testInstance.getComputedValue(inputValue, "Table1");

// Check that the result contains key parts of our expected binding
expect(result).toContain(
"const originalTableData = Table1.tableData || []",
);
expect(result).toContain(
"const filteredTableData = Table1.filteredTableData || []",
);
expect(result).toContain("const primaryColumnId = Table1.primaryColumnId");
expect(result).toContain(
"const getMergedTableData = (originalData, filteredData, primaryId)",
);
expect(result).toContain(
"const mergedTableData = primaryColumnId ? getMergedTableData(originalTableData, filteredTableData, primaryColumnId) : filteredTableData",
);
expect(result).toContain("tableData.sort((a, b) => a.id - b.id)");
expect(result).toContain(
"if (Array.isArray(sortedTableData) && primaryColumnId)",
);
expect(result).toContain('console.error("Error in table custom sort:", e)');
});

it("handles non-binding values correctly in getComputedValue", () => {
const plainValue = "plain text";
const result = testInstance.getComputedValue(plainValue, "Table1");

expect(result).toBe(plainValue);
});

it("handles empty string in getComputedValue", () => {
const emptyValue = "";
const result = testInstance.getComputedValue(emptyValue, "Table1");

expect(result).toBe(emptyValue);
});

it("correctly handles original data in computed value", () => {
const inputValue =
"{{tableData.map(row => ({...row, age: row.__original__.age}))}}";
const result = testInstance.getComputedValue(inputValue, "Table1");

expect(result).toContain(
"const originalTableData = Table1.tableData || []",
);
expect(result).toContain(
"const filteredTableData = Table1.filteredTableData || []",
);
expect(result).toContain("const primaryColumnId = Table1.primaryColumnId");
expect(result).toContain(
"const getMergedTableData = (originalData, filteredData, primaryId)",
);
expect(result).toContain(
"const mergedTableData = primaryColumnId ? getMergedTableData(originalTableData, filteredTableData, primaryColumnId) : filteredTableData",
);
expect(result).toContain(
"tableData.map(row => ({...row, age: row.__original__.age}))",
);
});
});

describe("TableCustomSortControl.getControlType", () => {
it("returns the correct control type", () => {
expect(TableCustomSortControl.getControlType()).toBe("TABLE_CUSTOM_SORT");
});
});
Loading