From a3e716823c1b90ef59a66a7e60414db4db7a02d2 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 16 Jan 2026 09:35:51 +0000 Subject: [PATCH 1/2] feat: clear all filters when deselecting a segment When a segment is selected and clicked again to deselect, now calls clearAll() instead of just setSegmentId(null) to also clear all active filters. Added e2e tests to verify the behavior for both system and user segments. Co-Authored-By: eunjae@cal.com --- .../segment/FilterSegmentSelect.tsx | 32 ++++----- apps/web/playwright/system-segments.e2e.ts | 72 ++++++++++++++++++- 2 files changed, 84 insertions(+), 20 deletions(-) diff --git a/apps/web/modules/data-table/components/segment/FilterSegmentSelect.tsx b/apps/web/modules/data-table/components/segment/FilterSegmentSelect.tsx index 8a54814ec02592..71563d447a081e 100644 --- a/apps/web/modules/data-table/components/segment/FilterSegmentSelect.tsx +++ b/apps/web/modules/data-table/components/segment/FilterSegmentSelect.tsx @@ -1,27 +1,25 @@ -import { useSession } from "next-auth/react"; -import { useState, useMemo } from "react"; - import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner"; +import { useDataTable } from "@calcom/features/data-table/hooks"; +import type { + CombinedFilterSegment, + FilterSegmentOutput, + SystemFilterSegmentInternal, + UserFilterSegment, +} from "@calcom/features/data-table/lib/types"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Button } from "@calcom/ui/components/button"; import { Dropdown, DropdownItem, - DropdownMenuPortal, DropdownMenuContent, DropdownMenuItem, - DropdownMenuTrigger, DropdownMenuLabel, + DropdownMenuPortal, + DropdownMenuTrigger, } from "@calcom/ui/components/dropdown"; import { Icon, type IconName } from "@calcom/ui/components/icon"; - -import { useDataTable } from "@calcom/features/data-table/hooks"; -import type { - FilterSegmentOutput, - CombinedFilterSegment, - SystemFilterSegmentInternal, - UserFilterSegment, -} from "@calcom/features/data-table/lib/types"; +import { useSession } from "next-auth/react"; +import { useMemo, useState } from "react"; import { DeleteSegmentDialog } from "./DeleteSegmentDialog"; import { DuplicateSegmentDialog } from "./DuplicateSegmentDialog"; import { RenameSegmentDialog } from "./RenameSegmentDialog"; @@ -43,7 +41,7 @@ export function FilterSegmentSelect({ shortLabel }: Props = {}) { const { t } = useLocale(); const session = useSession(); const isAdminOrOwner = checkAdminOrOwner(session.data?.user?.org?.role); - const { segments, selectedSegment, segmentId, setSegmentId, isSegmentEnabled } = useDataTable(); + const { segments, selectedSegment, segmentId, setSegmentId, isSegmentEnabled, clearAll } = useDataTable(); const [segmentToRename, setSegmentToRename] = useState(); const [segmentToDuplicate, setSegmentToDuplicate] = useState(); const [segmentToDelete, setSegmentToDelete] = useState(); @@ -192,7 +190,7 @@ export function FilterSegmentSelect({ shortLabel }: Props = {}) { segment={segment} onSelect={() => { if (segmentId && segmentId.type === segment.type && segmentId.id === segment.id) { - setSegmentId(null); + clearAll(); } else { if (segment.type === "system") { setSegmentId({ id: segment.id, type: "system" }); @@ -247,9 +245,7 @@ function DropdownItemWithSubmenu({ const [isOpen, setIsOpen] = useState(false); return ( - +
{children}
diff --git a/apps/web/playwright/system-segments.e2e.ts b/apps/web/playwright/system-segments.e2e.ts index 11ac507727a3c5..54300aa9c01aae 100644 --- a/apps/web/playwright/system-segments.e2e.ts +++ b/apps/web/playwright/system-segments.e2e.ts @@ -1,7 +1,15 @@ -import { expect } from "@playwright/test"; import type { Page } from "@playwright/test"; +import { expect } from "@playwright/test"; -import { addFilter, selectSegment, expectSegmentCleared, expectSegmentSelected } from "./filter-helpers"; +import { + addFilter, + applySelectFilter, + createFilterSegment, + deleteSegment, + expectSegmentCleared, + expectSegmentSelected, + selectSegment, +} from "./filter-helpers"; import { test } from "./lib/fixtures"; test.describe.configure({ mode: "parallel" }); @@ -117,4 +125,64 @@ test.describe("System Segments", () => { await expect(page.getByTestId("filter-popover-trigger-eventTypeId")).toBeVisible(); }); }); + + test.describe("Deselect Clears Filters", () => { + test("Deselecting a system segment clears all active filters", async ({ page, users }) => { + const user = await users.create(undefined, { hasTeam: true }); + await user.apiLogin(); + + await navigateToBookings(page); + + // Select system segment (which applies filters) + await selectSegment(page, "My Bookings"); + await expectSegmentSelected(page, "My Bookings"); + + // Verify filters are applied (URL should contain activeFilters) + const urlBeforeDeselect = page.url(); + expect(urlBeforeDeselect).toContain("activeFilters"); + + // Deselect the segment by clicking it again + await selectSegment(page, "My Bookings"); + await expectSegmentCleared(page); + + // Verify filters are cleared (URL should not contain activeFilters) + const urlAfterDeselect = page.url(); + expect(urlAfterDeselect).not.toContain("activeFilters"); + }); + + test("Deselecting a user segment clears all active filters", async ({ page, users }) => { + const orgOwner = await users.create(undefined, { + hasTeam: true, + isOrg: true, + }); + const { team: org } = await orgOwner.getOrgMembership(); + + await orgOwner.apiLogin(); + await page.goto(`/settings/organizations/${org.slug}/members`); + + const dataTable = page.getByTestId("user-list-data-table").first(); + await expect(dataTable).toBeVisible(); + + // Create a filter segment + await applySelectFilter(page, "role", "admin"); + const segmentName = "Test Deselect Segment"; + await createFilterSegment(page, segmentName); + + // Verify segment is selected and filters are applied + await expectSegmentSelected(page, segmentName); + const urlWithSegment = page.url(); + expect(urlWithSegment).toContain("activeFilters"); + + // Deselect the segment by clicking it again + await selectSegment(page, segmentName); + await expectSegmentCleared(page); + + // Verify filters are cleared + const urlAfterDeselect = page.url(); + expect(urlAfterDeselect).not.toContain("activeFilters"); + + // Clean up - delete the segment + await deleteSegment(page, segmentName); + }); + }); }); From b1bdd4adb305cee15cdbd220bc8bae5c6368f680 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 16 Jan 2026 09:45:51 +0000 Subject: [PATCH 2/2] refactor: move deselect filter test to filter-segment.e2e.ts Moved the 'Deselecting a segment clears all active filters' test from system-segments.e2e.ts to filter-segment.e2e.ts since the behavior applies to all segments, not just system segments. Co-Authored-By: eunjae@cal.com --- apps/web/playwright/filter-segment.e2e.ts | 43 +++++++++++-- apps/web/playwright/system-segments.e2e.ts | 70 +--------------------- 2 files changed, 38 insertions(+), 75 deletions(-) diff --git a/apps/web/playwright/filter-segment.e2e.ts b/apps/web/playwright/filter-segment.e2e.ts index 2511540de2bd85..b98f223bf93373 100644 --- a/apps/web/playwright/filter-segment.e2e.ts +++ b/apps/web/playwright/filter-segment.e2e.ts @@ -1,17 +1,18 @@ -import { expect } from "@playwright/test"; - import { MembershipRole } from "@calcom/prisma/enums"; +import { expect } from "@playwright/test"; import { applySelectFilter, + clearFilters, createFilterSegment, - selectSegment, deleteSegment, + expectSegmentCleared, + expectSegmentSelected, + getByTableColumnText, listSegments, - clearFilters, - openSegmentSubmenu, locateSelectedSegmentName, - getByTableColumnText, + openSegmentSubmenu, + selectSegment, } from "./filter-helpers"; import { test } from "./lib/fixtures"; @@ -362,4 +363,34 @@ test.describe("Filter Segment Functionality", () => { ).toBeHidden(); }); }); + + test("Deselecting a segment clears all active filters", async ({ page, users }) => { + const orgOwner = await users.create(undefined, { + hasTeam: true, + isOrg: true, + }); + const { team: org } = await orgOwner.getOrgMembership(); + + await orgOwner.apiLogin(); + await page.goto(`/settings/organizations/${org.slug}/members`); + + const dataTable = page.getByTestId("user-list-data-table").first(); + await expect(dataTable).toBeVisible(); + + await applySelectFilter(page, "role", "admin"); + const segmentName = "Test Deselect Segment"; + await createFilterSegment(page, segmentName); + + await expectSegmentSelected(page, segmentName); + const urlWithSegment = page.url(); + expect(urlWithSegment).toContain("activeFilters"); + + await selectSegment(page, segmentName); + await expectSegmentCleared(page); + + const urlAfterDeselect = page.url(); + expect(urlAfterDeselect).not.toContain("activeFilters"); + + await deleteSegment(page, segmentName); + }); }); diff --git a/apps/web/playwright/system-segments.e2e.ts b/apps/web/playwright/system-segments.e2e.ts index 54300aa9c01aae..e4602e8f7e66ab 100644 --- a/apps/web/playwright/system-segments.e2e.ts +++ b/apps/web/playwright/system-segments.e2e.ts @@ -1,15 +1,7 @@ import type { Page } from "@playwright/test"; import { expect } from "@playwright/test"; -import { - addFilter, - applySelectFilter, - createFilterSegment, - deleteSegment, - expectSegmentCleared, - expectSegmentSelected, - selectSegment, -} from "./filter-helpers"; +import { addFilter, expectSegmentCleared, expectSegmentSelected, selectSegment } from "./filter-helpers"; import { test } from "./lib/fixtures"; test.describe.configure({ mode: "parallel" }); @@ -125,64 +117,4 @@ test.describe("System Segments", () => { await expect(page.getByTestId("filter-popover-trigger-eventTypeId")).toBeVisible(); }); }); - - test.describe("Deselect Clears Filters", () => { - test("Deselecting a system segment clears all active filters", async ({ page, users }) => { - const user = await users.create(undefined, { hasTeam: true }); - await user.apiLogin(); - - await navigateToBookings(page); - - // Select system segment (which applies filters) - await selectSegment(page, "My Bookings"); - await expectSegmentSelected(page, "My Bookings"); - - // Verify filters are applied (URL should contain activeFilters) - const urlBeforeDeselect = page.url(); - expect(urlBeforeDeselect).toContain("activeFilters"); - - // Deselect the segment by clicking it again - await selectSegment(page, "My Bookings"); - await expectSegmentCleared(page); - - // Verify filters are cleared (URL should not contain activeFilters) - const urlAfterDeselect = page.url(); - expect(urlAfterDeselect).not.toContain("activeFilters"); - }); - - test("Deselecting a user segment clears all active filters", async ({ page, users }) => { - const orgOwner = await users.create(undefined, { - hasTeam: true, - isOrg: true, - }); - const { team: org } = await orgOwner.getOrgMembership(); - - await orgOwner.apiLogin(); - await page.goto(`/settings/organizations/${org.slug}/members`); - - const dataTable = page.getByTestId("user-list-data-table").first(); - await expect(dataTable).toBeVisible(); - - // Create a filter segment - await applySelectFilter(page, "role", "admin"); - const segmentName = "Test Deselect Segment"; - await createFilterSegment(page, segmentName); - - // Verify segment is selected and filters are applied - await expectSegmentSelected(page, segmentName); - const urlWithSegment = page.url(); - expect(urlWithSegment).toContain("activeFilters"); - - // Deselect the segment by clicking it again - await selectSegment(page, segmentName); - await expectSegmentCleared(page); - - // Verify filters are cleared - const urlAfterDeselect = page.url(); - expect(urlAfterDeselect).not.toContain("activeFilters"); - - // Clean up - delete the segment - await deleteSegment(page, segmentName); - }); - }); });