From b13e0f46ad5869d9e8cf623a1b69158440e8d031 Mon Sep 17 00:00:00 2001 From: Eunjae Lee Date: Fri, 16 Jan 2026 11:19:25 +0100 Subject: [PATCH] feat: clear all filters when deselecting a segment (#26920) ## What does this PR do? When a filter segment is selected and the user clicks it again to deselect, this PR now clears all active filters in addition to deselecting the segment. Previously, deselecting a segment only called `setSegmentId(null)`, which removed the segment selection but left the filters in place. Now it calls `clearAll()` which clears both the segment selection and all active filters. ## Mandatory Tasks (DO NOT REMOVE) - [x] I have self-reviewed the code (A decent size PR without self-review might be rejected). - [x] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). N/A - no documentation changes needed. - [x] I confirm automated tests are in place that prove my fix is effective or that my feature works. ## How should this be tested? 1. Go to a page with DataTable and filter segments (e.g., Bookings page or Organization Members page) 2. Select a filter segment (e.g., "My Bookings" system segment or a user-created segment) 3. Verify filters are applied (URL contains `activeFilters`) 4. Click the same segment again to deselect it 5. Verify the segment is deselected AND all filters are cleared (URL should no longer contain `activeFilters`) An e2e test has been added to `filter-segment.e2e.ts`: - `Deselecting a segment clears all active filters` ## Checklist - [x] My code follows the style guidelines of this project - [x] I have checked if my changes generate no new warnings ## Human Review Checklist - [ ] Verify that calling `clearAll()` instead of `setSegmentId(null)` is the intended behavior (clears both segment selection and all active filters) - [ ] Consider if there are use cases where users might want to keep filters when deselecting a segment --- > **Link to Devin run**: https://app.devin.ai/sessions/44f22b80d9c442bdb334d04dac2b476d > **Requested by**: @eunjae-lee --- .../segment/FilterSegmentSelect.tsx | 32 ++++++------- apps/web/playwright/filter-segment.e2e.ts | 47 +++++++++++++++---- apps/web/playwright/system-segments.e2e.ts | 4 +- 3 files changed, 55 insertions(+), 28 deletions(-) diff --git a/apps/web/modules/data-table/components/segment/FilterSegmentSelect.tsx b/apps/web/modules/data-table/components/segment/FilterSegmentSelect.tsx index 8a54814ec0..71563d447a 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/filter-segment.e2e.ts b/apps/web/playwright/filter-segment.e2e.ts index 2511540de2..b98f223bf9 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, - createFilterSegment, - selectSegment, - deleteSegment, - listSegments, clearFilters, - openSegmentSubmenu, - locateSelectedSegmentName, + createFilterSegment, + deleteSegment, + expectSegmentCleared, + expectSegmentSelected, getByTableColumnText, + listSegments, + locateSelectedSegmentName, + 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 11ac507727..e4602e8f7e 100644 --- a/apps/web/playwright/system-segments.e2e.ts +++ b/apps/web/playwright/system-segments.e2e.ts @@ -1,7 +1,7 @@ -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, expectSegmentCleared, expectSegmentSelected, selectSegment } from "./filter-helpers"; import { test } from "./lib/fixtures"; test.describe.configure({ mode: "parallel" });