From 326872a86b305b0c7eb6c44ac1ca9eb97df37c2a Mon Sep 17 00:00:00 2001 From: Dhruwang Jariwala <67850763+Dhruwang@users.noreply.github.com> Date: Fri, 5 Sep 2025 16:11:39 +0530 Subject: [PATCH] fix: response data table settings modal breaking (#6501) --- .../components/ResponseTableColumns.test.tsx | 85 +++++---- .../components/ResponseTableColumns.tsx | 26 +-- .../data-table-settings-modal-item.test.tsx | 180 ++++++++++-------- .../data-table-settings-modal-item.tsx | 107 +---------- .../components/data-table-settings-modal.tsx | 4 +- 5 files changed, 173 insertions(+), 229 deletions(-) diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponseTableColumns.test.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponseTableColumns.test.tsx index e4f85ce7ea..8677cfb0bd 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponseTableColumns.test.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponseTableColumns.test.tsx @@ -1,5 +1,4 @@ import { extractChoiceIdsFromResponse } from "@/lib/response/utils"; -import { processResponseData } from "@/lib/responses"; import { getContactIdentifier } from "@/lib/utils/contact"; import { getFormattedDateTimeString } from "@/lib/utils/datetime"; import { getSelectionColumn } from "@/modules/ui/components/data-table"; @@ -31,10 +30,6 @@ vi.mock("@/lib/i18n/utils", () => ({ getLocalizedValue: vi.fn((localizedString, locale) => localizedString[locale] || localizedString.default), })); -vi.mock("@/lib/responses", () => ({ - processResponseData: vi.fn((data) => (Array.isArray(data) ? data.join(", ") : String(data))), -})); - vi.mock("@/lib/utils/contact", () => ({ getContactIdentifier: vi.fn((person) => person?.attributes?.email || person?.id || "Anonymous"), })); @@ -139,13 +134,13 @@ const mockSurvey = { questions: [ { id: "q1open", - type: TSurveyQuestionTypeEnum.OpenText, + type: "openText", headline: { default: "Open Text Question" }, required: true, } as unknown as TSurveyQuestion, { id: "q2matrix", - type: TSurveyQuestionTypeEnum.Matrix, + type: "matrix", headline: { default: "Matrix Question" }, rows: [ { id: "row-1", label: { default: "Row1" } }, @@ -159,19 +154,19 @@ const mockSurvey = { } as unknown as TSurveyQuestion, { id: "q3address", - type: TSurveyQuestionTypeEnum.Address, + type: "address", headline: { default: "Address Question" }, required: false, } as unknown as TSurveyQuestion, { id: "q4contact", - type: TSurveyQuestionTypeEnum.ContactInfo, + type: "contactInfo", headline: { default: "Contact Info Question" }, required: false, } as unknown as TSurveyQuestion, { id: "q5single", - type: TSurveyQuestionTypeEnum.MultipleChoiceSingle, + type: "multipleChoiceSingle", headline: { default: "Single Choice Question" }, required: false, choices: [ @@ -182,7 +177,7 @@ const mockSurvey = { } as unknown as TSurveyQuestion, { id: "q6multi", - type: TSurveyQuestionTypeEnum.MultipleChoiceMulti, + type: "multipleChoiceMulti", headline: { default: "Multi Choice Question" }, required: false, choices: [ @@ -275,12 +270,12 @@ describe("generateResponseTableColumns", () => { test("should generate columns for variables", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); - const var1Col = columns.find((col) => (col as any).accessorKey === "var1"); + const var1Col = columns.find((col) => (col as any).accessorKey === "VARIABLE_var1"); expect(var1Col).toBeDefined(); const var1Cell = (var1Col?.cell as any)?.({ row: { original: mockResponseData } } as any); expect(var1Cell.props.children).toBe("Segment A"); - const var2Col = columns.find((col) => (col as any).accessorKey === "var2"); + const var2Col = columns.find((col) => (col as any).accessorKey === "VARIABLE_var2"); expect(var2Col).toBeDefined(); const var2Cell = (var2Col?.cell as any)?.({ row: { original: mockResponseData } } as any); expect(var2Cell.props.children).toBe(100); @@ -288,7 +283,7 @@ describe("generateResponseTableColumns", () => { test("should generate columns for hidden fields if fieldIds exist", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); - const hf1Col = columns.find((col) => (col as any).accessorKey === "hf1"); + const hf1Col = columns.find((col) => (col as any).accessorKey === "HIDDEN_FIELD_hf1"); expect(hf1Col).toBeDefined(); const hf1Cell = (hf1Col?.cell as any)?.({ row: { original: mockResponseData } } as any); expect(hf1Cell.props.children).toBe("Hidden Field 1 Value"); @@ -443,7 +438,7 @@ describe("ResponseTableColumns - Column Implementations", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); // Find the variable column for var1 - const var1Column: any = columns.find((col) => (col as any).accessorKey === "var1"); + const var1Column: any = columns.find((col) => (col as any).accessorKey === "VARIABLE_var1"); expect(var1Column).toBeDefined(); // Test the header @@ -460,7 +455,7 @@ describe("ResponseTableColumns - Column Implementations", () => { expect(cellResult?.props.children).toBe("Test Value"); // Test with a number variable - const var2Column: any = columns.find((col) => (col as any).accessorKey === "var2"); + const var2Column: any = columns.find((col) => (col as any).accessorKey === "VARIABLE_var2"); expect(var2Column).toBeDefined(); const mockRowNumber = { @@ -475,7 +470,7 @@ describe("ResponseTableColumns - Column Implementations", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); // Find the hidden field column - const hfColumn: any = columns.find((col) => (col as any).accessorKey === "hf1"); + const hfColumn: any = columns.find((col) => (col as any).accessorKey === "HIDDEN_FIELD_hf1"); expect(hfColumn).toBeDefined(); // Test the header @@ -502,7 +497,7 @@ describe("ResponseTableColumns - Column Implementations", () => { const columns = generateResponseTableColumns(surveyWithNoHiddenFields, false, true, t as any); // Check that no hidden field columns were created - const hfColumn = columns.find((col) => (col as any).accessorKey === "hf1"); + const hfColumn = columns.find((col) => (col as any).accessorKey === "HIDDEN_FIELD_hf1"); expect(hfColumn).toBeUndefined(); }); }); @@ -520,11 +515,11 @@ describe("ResponseTableColumns - Multiple Choice Questions", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); // Should have main response column - const mainColumn = columns.find((col) => (col as any).accessorKey === "q5single"); + const mainColumn = columns.find((col) => (col as any).accessorKey === "QUESTION_q5single"); expect(mainColumn).toBeDefined(); // Should have option IDs column - const optionIdsColumn = columns.find((col) => (col as any).accessorKey === "q5singleoptionIds"); + const optionIdsColumn = columns.find((col) => (col as any).accessorKey === "QUESTION_q5singleoptionIds"); expect(optionIdsColumn).toBeDefined(); }); @@ -532,17 +527,17 @@ describe("ResponseTableColumns - Multiple Choice Questions", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); // Should have main response column - const mainColumn = columns.find((col) => (col as any).accessorKey === "q6multi"); + const mainColumn = columns.find((col) => (col as any).accessorKey === "QUESTION_q6multi"); expect(mainColumn).toBeDefined(); // Should have option IDs column - const optionIdsColumn = columns.find((col) => (col as any).accessorKey === "q6multioptionIds"); + const optionIdsColumn = columns.find((col) => (col as any).accessorKey === "QUESTION_q6multioptionIds"); expect(optionIdsColumn).toBeDefined(); }); test("multipleChoiceSingle main column renders RenderResponse component", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); - const mainColumn: any = columns.find((col) => (col as any).accessorKey === "q5single"); + const mainColumn: any = columns.find((col) => (col as any).accessorKey === "QUESTION_q5single"); const mockRow = { original: { @@ -558,7 +553,7 @@ describe("ResponseTableColumns - Multiple Choice Questions", () => { test("multipleChoiceMulti main column renders RenderResponse component", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); - const mainColumn: any = columns.find((col) => (col as any).accessorKey === "q6multi"); + const mainColumn: any = columns.find((col) => (col as any).accessorKey === "QUESTION_q6multi"); const mockRow = { original: { @@ -584,7 +579,9 @@ describe("ResponseTableColumns - Choice ID Columns", () => { test("option IDs column calls extractChoiceIdsFromResponse for string response", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); - const optionIdsColumn: any = columns.find((col) => (col as any).accessorKey === "q5singleoptionIds"); + const optionIdsColumn: any = columns.find( + (col) => (col as any).accessorKey === "QUESTION_q5singleoptionIds" + ); const mockRow = { original: { @@ -604,7 +601,9 @@ describe("ResponseTableColumns - Choice ID Columns", () => { test("option IDs column calls extractChoiceIdsFromResponse for array response", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); - const optionIdsColumn: any = columns.find((col) => (col as any).accessorKey === "q6multioptionIds"); + const optionIdsColumn: any = columns.find( + (col) => (col as any).accessorKey === "QUESTION_q6multioptionIds" + ); const mockRow = { original: { @@ -624,7 +623,9 @@ describe("ResponseTableColumns - Choice ID Columns", () => { test("option IDs column renders IdBadge components for choice IDs", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); - const optionIdsColumn: any = columns.find((col) => (col as any).accessorKey === "q6multioptionIds"); + const optionIdsColumn: any = columns.find( + (col) => (col as any).accessorKey === "QUESTION_q6multioptionIds" + ); const mockRow = { original: { @@ -646,7 +647,9 @@ describe("ResponseTableColumns - Choice ID Columns", () => { test("option IDs column returns null for non-string/array response values", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); - const optionIdsColumn: any = columns.find((col) => (col as any).accessorKey === "q5singleoptionIds"); + const optionIdsColumn: any = columns.find( + (col) => (col as any).accessorKey === "QUESTION_q5singleoptionIds" + ); const mockRow = { original: { @@ -663,7 +666,9 @@ describe("ResponseTableColumns - Choice ID Columns", () => { test("option IDs column returns null when no choice IDs found", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); - const optionIdsColumn: any = columns.find((col) => (col as any).accessorKey === "q5singleoptionIds"); + const optionIdsColumn: any = columns.find( + (col) => (col as any).accessorKey === "QUESTION_q5singleoptionIds" + ); const mockRow = { original: { @@ -682,7 +687,9 @@ describe("ResponseTableColumns - Choice ID Columns", () => { test("option IDs column handles missing language gracefully", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); - const optionIdsColumn: any = columns.find((col) => (col as any).accessorKey === "q5singleoptionIds"); + const optionIdsColumn: any = columns.find( + (col) => (col as any).accessorKey === "QUESTION_q5singleoptionIds" + ); const mockRow = { original: { @@ -712,8 +719,10 @@ describe("ResponseTableColumns - Helper Functions", () => { test("question headers are properly created for multiple choice questions", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); - const mainColumn: any = columns.find((col) => (col as any).accessorKey === "q5single"); - const optionIdsColumn: any = columns.find((col) => (col as any).accessorKey === "q5singleoptionIds"); + const mainColumn: any = columns.find((col) => (col as any).accessorKey === "QUESTION_q5single"); + const optionIdsColumn: any = columns.find( + (col) => (col as any).accessorKey === "QUESTION_q5singleoptionIds" + ); // Test main column header const mainHeader = mainColumn?.header?.(); @@ -728,8 +737,8 @@ describe("ResponseTableColumns - Helper Functions", () => { test("question headers include proper icons for multiple choice questions", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); - const singleChoiceColumn: any = columns.find((col) => (col as any).accessorKey === "q5single"); - const multiChoiceColumn: any = columns.find((col) => (col as any).accessorKey === "q6multi"); + const singleChoiceColumn: any = columns.find((col) => (col as any).accessorKey === "QUESTION_q5single"); + const multiChoiceColumn: any = columns.find((col) => (col as any).accessorKey === "QUESTION_q6multi"); // Headers should be functions that return JSX expect(typeof singleChoiceColumn?.header).toBe("function"); @@ -754,10 +763,10 @@ describe("ResponseTableColumns - Integration Tests", () => { const columns = generateResponseTableColumns(mockSurvey, false, true, t as any); // Find all multiple choice related columns - const singleMainCol = columns.find((col) => (col as any).accessorKey === "q5single"); - const singleIdsCol = columns.find((col) => (col as any).accessorKey === "q5singleoptionIds"); - const multiMainCol = columns.find((col) => (col as any).accessorKey === "q6multi"); - const multiIdsCol = columns.find((col) => (col as any).accessorKey === "q6multioptionIds"); + const singleMainCol = columns.find((col) => (col as any).accessorKey === "QUESTION_q5single"); + const singleIdsCol = columns.find((col) => (col as any).accessorKey === "QUESTION_q5singleoptionIds"); + const multiMainCol = columns.find((col) => (col as any).accessorKey === "QUESTION_q6multi"); + const multiIdsCol = columns.find((col) => (col as any).accessorKey === "QUESTION_q6multioptionIds"); expect(singleMainCol).toBeDefined(); expect(singleIdsCol).toBeDefined(); diff --git a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponseTableColumns.tsx b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponseTableColumns.tsx index c62ac3c7fa..2ecacb1a21 100644 --- a/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponseTableColumns.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/components/ResponseTableColumns.tsx @@ -34,6 +34,8 @@ const getQuestionColumnsData = ( t: TFnType ): ColumnDef[] => { const QUESTIONS_ICON_MAP = getQuestionIconMap(t); + const addressFields = ["addressLine1", "addressLine2", "city", "state", "zip", "country"]; + const contactInfoFields = ["firstName", "lastName", "email", "phone", "company"]; // Helper function to create consistent column headers const createQuestionHeader = (questionType: string, headline: string, suffix?: string) => { @@ -74,7 +76,7 @@ const getQuestionColumnsData = ( case "matrix": return question.rows.map((matrixRow) => { return { - accessorKey: matrixRow.label.default, + accessorKey: "QUESTION_" + question.id + "_" + matrixRow.label.default, header: () => { return (
@@ -99,10 +101,9 @@ const getQuestionColumnsData = ( }); case "address": - const addressFields = ["addressLine1", "addressLine2", "city", "state", "zip", "country"]; return addressFields.map((addressField) => { return { - accessorKey: addressField, + accessorKey: "QUESTION_" + question.id + "_" + addressField, header: () => { return (
@@ -123,10 +124,9 @@ const getQuestionColumnsData = ( }); case "contactInfo": - const contactInfoFields = ["firstName", "lastName", "email", "phone", "company"]; return contactInfoFields.map((contactInfoField) => { return { - accessorKey: contactInfoField, + accessorKey: "QUESTION_" + question.id + "_" + contactInfoField, header: () => { return (
@@ -153,7 +153,7 @@ const getQuestionColumnsData = ( const questionHeadline = getQuestionHeadline(question, survey); return [ { - accessorKey: question.id, + accessorKey: "QUESTION_" + question.id, header: createQuestionHeader(question.type, questionHeadline), cell: ({ row }) => { const responseValue = row.original.responseData[question.id]; @@ -171,7 +171,7 @@ const getQuestionColumnsData = ( }, }, { - accessorKey: question.id + "optionIds", + accessorKey: "QUESTION_" + question.id + "optionIds", header: createQuestionHeader(question.type, questionHeadline, t("common.option_id")), cell: ({ row }) => { const responseValue = row.original.responseData[question.id]; @@ -193,7 +193,7 @@ const getQuestionColumnsData = ( default: return [ { - accessorKey: question.id, + accessorKey: "QUESTION_" + question.id, header: () => (
@@ -233,7 +233,7 @@ const getMetadataColumnsData = (t: TFnType): ColumnDef[] => const IconComponent = COLUMNS_ICON_MAP[label]; metadataColumns.push({ - accessorKey: label, + accessorKey: "METADATA_" + label, header: () => (
{IconComponent && } @@ -309,7 +309,7 @@ export const generateResponseTableColumns = ( const statusColumn: ColumnDef = { accessorKey: "status", size: 200, - header: t("common.status"), + header: () =>
{t("common.status")}
, cell: ({ row }) => { const status = row.original.status; return ; @@ -318,7 +318,7 @@ export const generateResponseTableColumns = ( const tagsColumn: ColumnDef = { accessorKey: "tags", - header: t("common.tags"), + header: () =>
{t("common.tags")}
, cell: ({ row }) => { const tags = row.original.tags; if (Array.isArray(tags)) { @@ -337,7 +337,7 @@ export const generateResponseTableColumns = ( const variableColumns: ColumnDef[] = survey.variables.map((variable) => { return { - accessorKey: variable.id, + accessorKey: "VARIABLE_" + variable.id, header: () => (
{VARIABLES_ICON_MAP[variable.type]} @@ -356,7 +356,7 @@ export const generateResponseTableColumns = ( const hiddenFieldColumns: ColumnDef[] = survey.hiddenFields.fieldIds ? survey.hiddenFields.fieldIds.map((hiddenFieldId) => { return { - accessorKey: hiddenFieldId, + accessorKey: "HIDDEN_FIELD_" + hiddenFieldId, header: () => (
diff --git a/apps/web/modules/ui/components/data-table/components/data-table-settings-modal-item.test.tsx b/apps/web/modules/ui/components/data-table/components/data-table-settings-modal-item.test.tsx index 21bba7a746..f150f1c4d1 100644 --- a/apps/web/modules/ui/components/data-table/components/data-table-settings-modal-item.test.tsx +++ b/apps/web/modules/ui/components/data-table/components/data-table-settings-modal-item.test.tsx @@ -3,113 +3,135 @@ import userEvent from "@testing-library/user-event"; import { afterEach, describe, expect, test, vi } from "vitest"; import { DataTableSettingsModalItem } from "./data-table-settings-modal-item"; -// Mock the dnd-kit hooks -vi.mock("@dnd-kit/sortable", async () => { - const actual = await vi.importActual("@dnd-kit/sortable"); - return { - ...actual, - useSortable: () => ({ - attributes: {}, - listeners: {}, - setNodeRef: vi.fn(), - transform: { x: 0, y: 0, scaleX: 1, scaleY: 1 }, - transition: "transform 100ms ease", - isDragging: false, - }), - }; -}); +// Mock the Switch component +vi.mock("@/modules/ui/components/switch", () => ({ + Switch: ({ id, checked, onCheckedChange, disabled }: any) => ( + onCheckedChange(e.target.checked)} + disabled={disabled} + /> + ), +})); + +// Mock lucide-react +vi.mock("lucide-react", () => ({ + GripVertical: () =>
, +})); + +// Mock dnd-kit hooks +vi.mock("@dnd-kit/sortable", () => ({ + useSortable: vi.fn(() => ({ + attributes: { "data-testid": "sortable-attributes" }, + listeners: { "data-testid": "sortable-listeners" }, + setNodeRef: vi.fn(), + transform: null, + transition: null, + isDragging: false, + })), +})); + +vi.mock("@dnd-kit/utilities", () => ({ + CSS: { + Translate: { + toString: vi.fn(() => "translate3d(0px, 0px, 0px)"), + }, + }, +})); + +// Mock @tanstack/react-table +vi.mock("@tanstack/react-table", () => ({ + flexRender: vi.fn((_, context) => `Header: ${context.column.id}`), +})); describe("DataTableSettingsModalItem", () => { afterEach(() => { cleanup(); }); - test("renders standard column name correctly", () => { - const mockColumn = { - id: "firstName", - getIsVisible: vi.fn().mockReturnValue(true), - toggleVisibility: vi.fn(), - }; - - render(); - - expect(screen.getByText("environments.contacts.first_name")).toBeInTheDocument(); - const switchElement = screen.getByRole("switch"); - expect(switchElement).toBeInTheDocument(); - expect(switchElement).toHaveAttribute("aria-checked", "true"); + const createMockColumn = (id: string, isVisible: boolean = true) => ({ + id, + getIsVisible: vi.fn(() => isVisible), + toggleVisibility: vi.fn(), + columnDef: { + header: `${id} Header`, + }, }); - test("renders createdAt column with correct label", () => { - const mockColumn = { - id: "createdAt", - getIsVisible: vi.fn().mockReturnValue(true), - toggleVisibility: vi.fn(), + const createMockTable = (columns: any[]) => { + const headers = columns.map((column) => ({ + column, + getContext: vi.fn(() => ({ column })), + })); + + return { + getHeaderGroups: vi.fn(() => [{ headers }]), }; + }; - render(); + test("renders column item with grip icon and switch", () => { + const mockColumn = createMockColumn("firstName"); + const mockTable = createMockTable([mockColumn]); - expect(screen.getByText("common.date")).toBeInTheDocument(); + render(); + + expect(screen.getByTestId("grip-vertical")).toBeInTheDocument(); + expect(screen.getByTestId("switch-firstName")).toBeInTheDocument(); + expect(screen.getByText("Header: firstName")).toBeInTheDocument(); }); - test("renders verifiedEmail column with correct label", () => { - const mockColumn = { - id: "verifiedEmail", - getIsVisible: vi.fn().mockReturnValue(true), - toggleVisibility: vi.fn(), - }; + test("switch reflects column visibility state", () => { + const mockColumn = createMockColumn("firstName", true); + const mockTable = createMockTable([mockColumn]); - render(); + render(); - expect(screen.getByText("common.verified_email")).toBeInTheDocument(); + const switchElement = screen.getByTestId("switch-firstName") as HTMLInputElement; + expect(switchElement.checked).toBe(true); }); - test("renders userId column with correct label", () => { - const mockColumn = { - id: "userId", - getIsVisible: vi.fn().mockReturnValue(true), - toggleVisibility: vi.fn(), - }; + test("switch shows unchecked when column is hidden", () => { + const mockColumn = createMockColumn("firstName", false); + const mockTable = createMockTable([mockColumn]); - render(); + render(); - expect(screen.getByText("common.user_id")).toBeInTheDocument(); + const switchElement = screen.getByTestId("switch-firstName") as HTMLInputElement; + expect(switchElement.checked).toBe(false); }); - test("renders question from survey with localized headline", () => { - const mockColumn = { - id: "question1", - getIsVisible: vi.fn().mockReturnValue(true), - toggleVisibility: vi.fn(), - }; + test("calls toggleVisibility when switch is clicked", async () => { + const user = userEvent.setup(); + const mockColumn = createMockColumn("firstName", true); + const mockTable = createMockTable([mockColumn]); - const mockSurvey = { - questions: [ - { - id: "question1", - type: "open", - headline: { default: "Test Question" }, - }, - ], - }; + render(); - render(); + const switchElement = screen.getByTestId("switch-firstName"); + await user.click(switchElement); - expect(screen.getByText("Test Question")).toBeInTheDocument(); + expect(mockColumn.toggleVisibility).toHaveBeenCalledWith(false); }); - test("toggles visibility when switch is clicked", async () => { - const toggleVisibilityMock = vi.fn(); - const mockColumn = { - id: "lastName", - getIsVisible: vi.fn().mockReturnValue(true), - toggleVisibility: toggleVisibilityMock, - }; + test("renders with correct column id as element id", () => { + const mockColumn = createMockColumn("lastName"); + const mockTable = createMockTable([mockColumn]); - render(); + render(); - const switchElement = screen.getByRole("switch"); - await userEvent.click(switchElement); + const elementWithId = screen.getByText("Header: lastName").closest("[id='lastName']"); + expect(elementWithId).toBeInTheDocument(); + }); - expect(toggleVisibilityMock).toHaveBeenCalledWith(false); + test("renders reorder button with correct aria-label", () => { + const mockColumn = createMockColumn("firstName"); + const mockTable = createMockTable([mockColumn]); + + render(); + + const reorderButton = screen.getByRole("button", { name: "Reorder column" }); + expect(reorderButton).toBeInTheDocument(); }); }); diff --git a/apps/web/modules/ui/components/data-table/components/data-table-settings-modal-item.tsx b/apps/web/modules/ui/components/data-table/components/data-table-settings-modal-item.tsx index df1c5d1b30..bd23e3dcd8 100644 --- a/apps/web/modules/ui/components/data-table/components/data-table-settings-modal-item.tsx +++ b/apps/web/modules/ui/components/data-table/components/data-table-settings-modal-item.tsx @@ -1,97 +1,22 @@ "use client"; -import { COLUMNS_ICON_MAP } from "@/app/(app)/environments/[environmentId]/surveys/[surveyId]/(analysis)/responses/lib/utils"; -import { getLocalizedValue } from "@/lib/i18n/utils"; -import { getQuestionIconMap } from "@/modules/survey/lib/questions"; import { Switch } from "@/modules/ui/components/switch"; import { useSortable } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; -import { Column } from "@tanstack/react-table"; -import { useTranslate } from "@tolgee/react"; -import { capitalize } from "lodash"; +import { Column, Table, flexRender } from "@tanstack/react-table"; import { GripVertical } from "lucide-react"; -import { useMemo } from "react"; import { TSurvey } from "@formbricks/types/surveys/types"; interface DataTableSettingsModalItemProps { column: Column; + table: Table; survey?: TSurvey; } -const getColumnIcon = (columnId: string) => { - const Icon = COLUMNS_ICON_MAP[columnId]; - if (Icon) { - return ( - - ); - } - return null; -}; - -export const DataTableSettingsModalItem = ({ column, survey }: DataTableSettingsModalItemProps) => { - const { t } = useTranslate(); - const QUESTIONS_ICON_MAP = getQuestionIconMap(t); +export const DataTableSettingsModalItem = ({ column, table }: DataTableSettingsModalItemProps) => { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: column.id, }); - const isOptionIdColumn = column.id.endsWith("optionIds"); - - const getOptionIdColumnLabel = () => { - const questionId = column.id.split("optionIds")[0]; - const question = survey?.questions.find((q) => q.id === questionId); - if (question) { - return `${getLocalizedValue(question.headline, "default")} - ${t("common.option_id")}`; - } - return null; - }; - - const getLabelFromColumnId = () => { - switch (column.id) { - case "createdAt": - return t("common.date"); - case "addressLine1": - return t("environments.surveys.edit.address_line_1"); - case "addressLine2": - return t("environments.surveys.edit.address_line_2"); - case "city": - return t("environments.surveys.edit.city"); - case "state": - return t("environments.surveys.edit.state"); - case "zip": - return t("environments.surveys.edit.zip"); - case "verifiedEmail": - return t("common.verified_email"); - case "userId": - return t("common.user_id"); - case "contactsTableUser": - return "ID"; - case "firstName": - return t("environments.contacts.first_name"); - case "lastName": - return t("environments.contacts.last_name"); - case "action": - return t("common.action"); - case "country": - return t("environments.surveys.responses.country"); - case "os": - return t("environments.surveys.responses.os"); - case "device": - return t("environments.surveys.responses.device"); - case "browser": - return t("environments.surveys.responses.browser"); - case "url": - return t("common.url"); - case "source": - return t("environments.surveys.responses.source"); - - default: - return capitalize(column.id); - } - }; - - const question = survey?.questions.find((question) => question.id === column.id); const style = { transition: transition ?? "transform 100ms ease", @@ -99,9 +24,11 @@ export const DataTableSettingsModalItem = ({ column, survey }: DataTableSett zIndex: isDragging ? 10 : 1, }; - const iconMemo = useMemo(() => { - return getColumnIcon(column.id); - }, [column.id]); + // Find the header for this column from the table's header groups + const header = table + .getHeaderGroups() + .flatMap((headerGroup) => headerGroup.headers) + .find((h) => h.column.id === column.id); return (
@@ -113,23 +40,7 @@ export const DataTableSettingsModalItem = ({ column, survey }: DataTableSett -
- {question ? ( - <> - - {getLocalizedValue(question.headline, "default")} - - ) : ( - <> - {iconMemo} - - {isOptionIdColumn ? getOptionIdColumnLabel() : getLabelFromColumnId()} - - - )} -
+ {flexRender(column.columnDef.header, header?.getContext() ?? { column })}
({ if (columnId === "select" || columnId === "createdAt") return; const column = tableColumns.find((column) => column.id === columnId); if (!column) return null; - return ; + return ( + + ); })}