Skip to content

Conversation

@tomaszpacior
Copy link
Collaborator

@tomaszpacior tomaszpacior commented Jan 8, 2026

What does this PR do?

  • Feature - Add expandable inline filters variant with active filter badges

Related Ticket(s)

  • Notion Ticket

Key Changes

  • Implemented new inline filters variant with "Show more filters" / "Hide more filters" functionality
    • Leading filters (marked with isLeading: true) are always visible
    • Non-leading filters can be expanded/collapsed with smooth staggered animations
    • Active filters are displayed as removable badges below the filter controls
    • "Clear all filters" button appears when multiple filters are active
  • Enhanced Filters component in @o2s/ui package
    • Added InlineFiltersContent component for managing inline filter state and animations
    • Added helper functions: separateLeadingItems, getActiveFilterBadges, getFilterResetValue
    • Implemented fade-in/fade-out animations with CSS keyframes and staggered timing
  • Updated FilterItem component to support inline variant behavior
    • Added isInlineVariant prop for auto-submit behavior
    • Created ToggleGroupWithLabel wrapper component for labeled toggle groups
    • Increased text filter debounce from 500ms to 1000ms
  • Extended framework models and request types
    • Added search and priority fields to ticket request types
    • Added new filter labels: showMoreFilters, hideMoreFilters, noActiveFilters
    • Added isLabelHidden option for FilterToggleGroup
  • Updated mocked CMS data with new filter configurations (EN, DE, PL)
    • Added new priority filter
    • Changed some filters from FilterSelect to FilterToggleGroup with allowMultiple: true

Summary by CodeRabbit

  • New Features

    • Inline filters variant with expandable sections, active badges, and a clear-all action
    • Search and priority filters for ticket lists
    • Labeled toggle-group UI (optional hidden labels) and expanded filter label keys (show more / hide / no active)
    • Debounced text filtering and smooth enter/exit animations for filter items
  • Chores

    • Minor version bumps across packages

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 8, 2026

Walkthrough

Adds an inline filters variant with expandable sections, debounced text filtering, ToggleGroupWithLabel, new filter label keys, search and priority filters, model/request surface updates, mocked mapper changes, and related animations and styles.

Changes

Cohort / File(s) Summary
Changeset
\.changeset/dull-dancers-hide.md
Declares minor version bumps and documents feature: "add inline filters variant with expandable sections"
Ticket List Block Models
packages/blocks/ticket-list/src/api-harmonization/ticket-list.model.ts, packages/framework/src/modules/cms/models/blocks/ticket-list.model.ts
Adds label keys showMoreFilters?, hideMoreFilters?, noActiveFilters?; extends public filters with search? and priority?
Ticket List Request Types
packages/blocks/ticket-list/src/api-harmonization/ticket-list.request.ts, packages/framework/src/modules/tickets/tickets.request.ts
Adds optional query fields search?: string and priority?: string to ticket list request/query types
Filter Framework Models
packages/framework/src/utils/models/filters.ts
Adds isLabelHidden?: boolean to FilterToggleGroup model
Filter UI Types
packages/ui/src/components/Filters/Filters.types.ts
Adds exported FilterLabels type; replaces prior inline label shape; adds isInlineVariant?: boolean to FilterItemProps
Filter UI Implementation
packages/ui/src/components/Filters/FilterItem.tsx, packages/ui/src/components/Filters/Filters.tsx, packages/ui/src/components/Filters/Filters.types.ts
Implements inline variant with expandable sections, debounced text input, active filter badges, separation of leading items, unified toggle/select rendering paths; wires new label keys
Toggle Group Element
packages/ui/src/elements/toggle-group.tsx
Adds ToggleGroupWithLabel component and isLabelHidden prop; adjusts styling variants and exports
Mocked Integration Mapper
packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.ticket-list.mapper.ts
Adds priority filter, reworks status/type/topic filter configs across locales, and expands localized filter label keys (showMoreFilters, hideMoreFilters, noActiveFilters)
Global Styles
packages/ui/src/globals.css
Adds filterFadeIn and filterFadeOut keyframe animations
Client wiring
packages/blocks/ticket-list/src/frontend/TicketList.client.tsx
Initializes new search and priority filter fields and passes variant="inline" to FiltersSection

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant Client as TicketList.client.tsx
  participant FiltersUI as Filters / FilterItem / ToggleGroup
  participant Framework as framework (GetTicketListQuery)
  participant MockMapper as integrations/mocked mapper
  participant API as Backend/API

  User->>Client: open list / interact with filters
  Client->>FiltersUI: render inline filters (variant="inline")
  User->>FiltersUI: type search / set priority / toggle filters
  FiltersUI->>Client: emit filter change (text debounced)
  Client->>Framework: send GetTicketListQuery (includes search, priority)
  Framework->>MockMapper: map request/block (local/dev)
  MockMapper->>API: fetch filtered tickets
  API-->>Client: return tickets
  Client-->>User: render updated list
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • marcinkrasowski

Poem

🐇
I hop through filters, tidy and spry,
Inline sections fold with a blink of my eye,
Badges gleam bright as toggles agree,
Search and priority dance along with me,
A cheerful rabbit sings: "Filters, set free!"

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main feature: implementing an inline filters variant with expandable sections, which is the primary objective.
Description check ✅ Passed The PR description follows the template structure and provides comprehensive details on the feature, key changes, and implementation across multiple packages.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @packages/ui/src/components/Filters/FilterItem.tsx:
- Line 28: debouncedSubmit created via useMemo
(debounce(TEXT_FILTER_DEBOUNCE_MS, () => submitForm())) can fire after unmount
because it isn’t cancelled; change to create/retain the debounced function in
useMemo or a ref and add a useEffect that returns a cleanup which calls
debouncedSubmit.cancel() (or the debounce lib’s equivalent) whenever
debouncedSubmit or submitForm changes and on unmount; ensure you verify debounce
returns a cancel method (or switch to lodash.debounce) and reference the
debouncedSubmit, submitForm, TEXT_FILTER_DEBOUNCE_MS, useMemo and useEffect
symbols when applying the fix.
🧹 Nitpick comments (1)
packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.ticket-list.mapper.ts (1)

541-549: Redundant detailsUrl assignments in mapTicketListBlock.

The detailsUrl property is already defined in each mock object (lines 178, 357, 538) with the same values being assigned in the spread. This redundancy doesn't cause bugs but could be simplified.

♻️ Optional cleanup
 export const mapTicketListBlock = (locale: string): CMS.Model.TicketListBlock.TicketListBlock => {
     switch (locale) {
         case 'de':
-            return { ...MOCK_TICKET_LIST_BLOCK_DE, detailsUrl: '/faelle/{id}' };
+            return MOCK_TICKET_LIST_BLOCK_DE;
         case 'pl':
-            return { ...MOCK_TICKET_LIST_BLOCK_PL, detailsUrl: '/zgloszenia/{id}' };
+            return MOCK_TICKET_LIST_BLOCK_PL;
         default:
-            return { ...MOCK_TICKET_LIST_BLOCK_EN, detailsUrl: '/cases/{id}' };
+            return MOCK_TICKET_LIST_BLOCK_EN;
     }
 };
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9e0d3ba and cd42e40.

📒 Files selected for processing (15)
  • .changeset/dull-dancers-hide.md
  • diff-feature.txt
  • diff-name-status.txt
  • packages/blocks/ticket-list/src/api-harmonization/ticket-list.model.ts
  • packages/blocks/ticket-list/src/api-harmonization/ticket-list.request.ts
  • packages/blocks/ticket-list/src/frontend/TicketList.client.tsx
  • packages/framework/src/modules/cms/models/blocks/ticket-list.model.ts
  • packages/framework/src/modules/tickets/tickets.request.ts
  • packages/framework/src/utils/models/filters.ts
  • packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.ticket-list.mapper.ts
  • packages/ui/src/components/Filters/FilterItem.tsx
  • packages/ui/src/components/Filters/Filters.tsx
  • packages/ui/src/components/Filters/Filters.types.ts
  • packages/ui/src/elements/toggle-group.tsx
  • packages/ui/src/globals.css
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-26T11:57:00.632Z
Learnt from: marcinkrasowski
Repo: o2sdev/openselfservice PR: 411
File: packages/framework/src/modules/cms/models/blocks/product-list.model.ts:29-46
Timestamp: 2025-11-26T11:57:00.632Z
Learning: In the framework layer (packages/framework/src/modules/cms/models/blocks/*.model.ts), block classes like ProductListBlock should NOT include explicit __typename discriminators. The __typename field is added at the API harmonization layer (packages/blocks/*/src/api-harmonization/*.model.ts) where it's needed for discriminated unions. The framework maintains normalized data models without these discriminators.

Applied to files:

  • packages/blocks/ticket-list/src/api-harmonization/ticket-list.model.ts
  • packages/framework/src/modules/cms/models/blocks/ticket-list.model.ts
🧬 Code graph analysis (4)
packages/ui/src/elements/toggle-group.tsx (2)
packages/ui/src/elements/label.tsx (1)
  • Label (17-17)
packages/ui/src/lib/utils.ts (1)
  • cn (5-7)
packages/ui/src/components/Filters/FilterItem.tsx (4)
packages/framework/src/utils/models/filters.ts (1)
  • FilterItem (12-17)
packages/ui/src/components/Filters/Filters.types.ts (1)
  • FilterItemProps (28-38)
packages/ui/src/elements/toggle-group.tsx (3)
  • ToggleGroupItem (124-124)
  • ToggleGroupWithLabel (124-124)
  • ToggleGroup (124-124)
packages/ui/src/lib/utils.ts (1)
  • cn (5-7)
packages/framework/src/modules/cms/models/blocks/ticket-list.model.ts (3)
packages/framework/src/utils/models/filters.ts (1)
  • Filters (1-10)
packages/ui/src/components/Filters/Filters.tsx (1)
  • Filters (329-478)
packages/blocks/ticket-list/src/api-harmonization/ticket-list.model.ts (1)
  • Ticket (40-57)
packages/ui/src/components/Filters/Filters.types.ts (2)
packages/framework/src/utils/models/filters.ts (1)
  • Filters (1-10)
packages/ui/src/components/Filters/Filters.tsx (1)
  • Filters (329-478)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: deploy-preview
🔇 Additional comments (23)
diff-feature.txt (1)

1-70: Incomplete review context: This file lists only documentation and configuration changes, not the feature implementation.

The provided diff-feature.txt is a high-level manifest of repo-wide file operations (adds, modifications, deletions). It shows extensive documentation restructuring (Contentful, Strapi, Algolia, Zendesk, Redis, SurveyJS, theming) and package manifest updates, but does not contain the actual implementation code for the inline filters variant feature described in the PR objectives.

According to the PR summary, the following implementation files should exist:

  • @o2s/ui package Filters component with InlineFiltersContent subcomponent
  • Helper functions: separateLeadingItems, getActiveFilterBadges, getFilterResetValue
  • CSS keyframe animations with staggered timing
  • Updates to FilterItem component with isInlineVariant prop
  • New ToggleGroupWithLabel wrapper component
  • Type updates for search, priority, isLabelHidden fields
  • Mocked CMS data updates (EN, DE, PL)

To conduct a comprehensive review of this feature, please provide the actual implementation files containing the above components and logic, not just the file manifest.

packages/framework/src/utils/models/filters.ts (1)

37-37: LGTM! Consistent pattern for label visibility control.

The addition of isLabelHidden to FilterToggleGroup aligns with the existing pattern used in FilterText (line 58) and supports the new inline filter variants.

packages/framework/src/modules/tickets/tickets.request.ts (1)

21-22: LGTM! Backward-compatible query extensions.

The new search and priority parameters are appropriately marked as optional, maintaining backward compatibility while enabling the new filtering capabilities described in the PR.

packages/blocks/ticket-list/src/api-harmonization/ticket-list.request.ts (1)

10-11: LGTM! Proper interface alignment.

The addition of search and priority fields correctly aligns GetTicketListBlockQuery with the extended Tickets.Request.GetTicketListQuery interface from the framework.

packages/ui/src/globals.css (1)

141-161: LGTM! Clean animation definitions for filter transitions.

The filterFadeIn and filterFadeOut keyframes provide smooth transitions with appropriate opacity and translation values. The 8px vertical offset creates a subtle, polished effect for the inline filter UI.

.changeset/dull-dancers-hide.md (1)

1-8: LGTM!

The changeset correctly documents the minor version bumps for all affected packages and provides a clear description of the feature addition.

packages/blocks/ticket-list/src/frontend/TicketList.client.tsx (2)

41-42: LGTM!

The new search and priority filter fields are correctly initialized as empty strings in the initial filter state, matching the expected request query structure.


208-214: LGTM!

The inline variant configuration is properly set up with the necessary labels for expandable filter sections. The new label keys (showMoreFilters, hideMoreFilters, noActiveFilters) align with the inline filter UI requirements.

packages/blocks/ticket-list/src/api-harmonization/ticket-list.model.ts (1)

26-28: LGTM!

The new label fields are correctly added as optional properties to support the inline filters UI. The type extensions align with the corresponding framework model changes.

packages/framework/src/modules/cms/models/blocks/ticket-list.model.ts (2)

12-14: LGTM!

The Filters type is correctly extended to include the new search and priority fields alongside the existing sort and viewMode options. All fields are appropriately optional.


24-26: LGTM!

The new label fields are consistently added to both the runtime labels and the CMS metadata Meta.labels, ensuring proper support for the inline filters UI across the data model.

Also applies to: 56-58

packages/ui/src/components/Filters/Filters.types.ts (2)

4-11: LGTM!

The new FilterLabels interface consolidates all filter-related labels into a single, reusable type. All fields are appropriately optional, maintaining flexibility and backward compatibility. The interface includes comprehensive labeling for the inline filter variant, including accessibility labels.


20-20: LGTM!

The type updates correctly replace inline label types with the shared FilterLabels interface, improving type consistency. The new isInlineVariant flag in FilterItemProps enables proper conditional behavior for the inline filter variant.

Also applies to: 36-37

packages/ui/src/elements/toggle-group.tsx (1)

100-122: LGTM! New ToggleGroupWithLabel component is well-structured.

The component correctly wraps ToggleGroup with accessibility support via sr-only for hidden labels. The props are properly spread to the underlying ToggleGroup.

One minor note: the className order in the cn() call at line 116 places labelClassName before the conditional sr-only. This works correctly since sr-only will override visibility-related styles when isLabelHidden is true.

packages/integrations/mocked/src/modules/cms/mappers/blocks/cms.ticket-list.mapper.ts (1)

135-146: LGTM! Priority filter added consistently across all locales.

The new priority filter with HIGH/MEDIUM/LOW options is properly localized and maintains consistent structure across EN, DE, and PL locales.

Also applies to: 314-325, 495-506

packages/ui/src/components/Filters/Filters.tsx (5)

68-73: Consider using toSorted() or documenting mutation safety.

The arraysEqual function correctly creates copies before sorting, avoiding mutation of input arrays. This is good practice.


186-207: Animation effect handles cleanup correctly.

The effect properly tracks previous expansion state via ref, manages animation timing, and returns cleanup functions for timeouts. The staggered timing based on otherItems.length provides smooth visual transitions.


316-320: "Clear all filters" button only appears with 2+ active filters.

This is intentional UX - when only one filter is active, users can remove it via the badge's X button. The "Clear all filters" button adds value only with multiple active filters.


306-310: Good accessibility: aria-label for filter removal button.

The remove button correctly includes an accessible label that falls back to a sensible default when labels?.removeFilterAriaLabel is not provided.


214-220: FilterDateRange reset value should be handled explicitly or excluded from badges.

getFilterResetValue has no case for FilterDateRange and defaults to returning an empty string. While FilterDateRange is not in SUPPORTED_FILTER_TYPES and filters are excluded from the rendered UI, getActiveFilterBadges receives the unfiltered items array. If a date range filter with changed values appears in the data, users could remove it via the badge, resetting it to an empty string—which may not be the correct reset value for dates. Either add explicit handling for FilterDateRange in getFilterResetValue, or ensure date range filters are excluded from badge generation.

packages/ui/src/components/Filters/FilterItem.tsx (3)

126-147: Single-selection toggle group rendering logic doesn't consider isInlineVariant.

For allowMultiple: true (lines 82-103), the code checks isInlineVariant to decide between ToggleGroupWithLabel and ToggleGroup. However, for single selection (allowMultiple: false), the rendering is based solely on isLeading:

  • isLeading: trueToggleGroup (no label)
  • isLeading: falseToggleGroupWithLabel (with label)

This asymmetry may be intentional (leading items never need labels), but verify this aligns with the expected behavior for single-selection inline filters.


41-64: Dynamic corner rounding for adjacent selected items is a nice UX touch.

The logic correctly rounds corners only on the outer edges of contiguous selected items, creating a visual grouping effect.


66-80: ALL option handling in multi-select is correct.

When "ALL" is clicked, the selection is cleared (empty array). When other options are selected, "ALL" is filtered out. The ref-based tracking of whether "ALL" was clicked ensures correct behavior in the async flow.

@vercel
Copy link

vercel bot commented Jan 9, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Review Updated (UTC)
o2s-docs Skipped Skipped Jan 9, 2026 1:50pm

@vercel vercel bot temporarily deployed to Preview – o2s-docs January 9, 2026 13:06 Inactive
@vercel vercel bot temporarily deployed to Preview – o2s-docs January 9, 2026 13:26 Inactive
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/components/Filters/FilterItem.tsx (1)

93-137: Inconsistent wrapping: leading single-select lacks ScrollContainer.

For allowMultiple: true, the toggle items are wrapped in ScrollContainer (line 86-88), but for single-select with isLeading: true (lines 114-121), there's no ScrollContainer. This could cause overflow issues if there are many options.

🔧 Proposed fix for consistency
                         return isLeading ? (
                             <ToggleGroup
                                 type="single"
                                 variant="solid"
                                 value={currentValue}
                                 onValueChange={handleValueChange}
                             >
-                                {toggleGroupItems}
+                                <ScrollContainer className="scroll-container flex whitespace-nowrap w-full">
+                                    {toggleGroupItems}
+                                </ScrollContainer>
                             </ToggleGroup>
                         ) : (
🧹 Nitpick comments (3)
packages/ui/src/elements/toggle-group.tsx (2)

103-107: Consider adding ref forwarding support to ToggleGroupWithLabelProps.

The ToggleGroupWithLabelProps extends ToggleGroupProps which includes a ref property, but the ToggleGroupWithLabel component doesn't forward a ref to the outer div wrapper. If consumers need to reference the container element, this could be limiting.

♻️ Proposed enhancement
-type ToggleGroupWithLabelProps = ToggleGroupProps & {
+type ToggleGroupWithLabelProps = Omit<ToggleGroupProps, 'ref'> & {
     label: string | React.ReactNode;
     labelClassName?: string;
     isLabelHidden?: boolean;
+    ref?: React.Ref<HTMLDivElement>;
 };

109-125: Missing htmlFor association between Label and ToggleGroup.

The Label component is rendered without an htmlFor attribute linking it to the toggle group. While toggle groups don't have a single input to reference, for accessibility it's common to use aria-labelledby on the group referencing the label's id.

♻️ Proposed fix for accessibility
+import { useId } from 'react';
+
 const ToggleGroupWithLabel = ({
     className,
     label,
     labelClassName,
     isLabelHidden,
     children,
     ...props
 }: ToggleGroupWithLabelProps) => {
+    const labelId = useId();
     return (
         <div className="grid gap-2">
-            <Label className={cn(labelClassName, isLabelHidden && 'sr-only')}>{label}</Label>
-            <ToggleGroup {...props} className={className}>
+            <Label id={labelId} className={cn(labelClassName, isLabelHidden && 'sr-only')}>{label}</Label>
+            <ToggleGroup {...props} className={className} aria-labelledby={labelId}>
                 {children}
             </ToggleGroup>
         </div>
     );
 };
packages/ui/src/components/Filters/FilterItem.tsx (1)

38-92: Potential stale closure issue with allWasClickedRef in onClick handler.

The allWasClickedRef pattern works but relies on the onClick firing before onValueChange. This ordering is implementation-dependent on the underlying Radix toggle group. Additionally, if multiple items are clicked rapidly, the ref state could become inconsistent.

Consider using the value directly in handleValueChange to determine if ALL was selected:

♻️ Alternative approach without ref timing dependency
                         const handleValueChange = async (value: string[]) => {
                             let newValue: string[];
+                            const wasAllClicked = value.includes('ALL') && !currentValue.includes('ALL');

-                            if (allWasClickedRef.current) {
+                            if (wasAllClicked) {
                                 newValue = [];
-                                allWasClickedRef.current = false;
                             } else {
                                 newValue = value.filter((v) => v !== 'ALL');
                             }

                             await setFieldValue(field.name, newValue);
                             if (isLeading || isInlineVariant) {
                                 await submitForm();
                             }
                         };

This approach determines intent by comparing the new value array with the current value, eliminating the timing dependency.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 81cef8f and 8431052.

📒 Files selected for processing (2)
  • packages/ui/src/components/Filters/FilterItem.tsx
  • packages/ui/src/elements/toggle-group.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
packages/ui/src/elements/toggle-group.tsx (3)
packages/ui/src/elements/checkbox.tsx (1)
  • Label (99-99)
packages/ui/src/elements/label.tsx (1)
  • Label (17-17)
packages/ui/src/lib/utils.ts (1)
  • cn (5-7)
packages/ui/src/components/Filters/FilterItem.tsx (3)
packages/framework/src/utils/models/filters.ts (1)
  • FilterItem (12-17)
packages/ui/src/components/Filters/Filters.types.ts (1)
  • FilterItemProps (28-38)
packages/ui/src/elements/toggle-group.tsx (3)
  • ToggleGroupItem (127-127)
  • ToggleGroupWithLabel (127-127)
  • ToggleGroup (127-127)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: deploy-preview
🔇 Additional comments (7)
packages/ui/src/elements/toggle-group.tsx (2)

7-7: LGTM!

Import of Label component is correctly placed and necessary for the new ToggleGroupWithLabel component.


18-32: LGTM!

The toggleGroupVariants configuration is well-structured with clear variant definitions. The CSS classes for handling adjacent [data-state=on] elements to remove border-radius appropriately creates a visually connected group.

packages/ui/src/components/Filters/FilterItem.tsx (5)

16-17: LGTM!

The debounce constant is clearly named and the 1000ms value is reasonable for text input filtering to reduce unnecessary submissions while still feeling responsive.


28-34: LGTM!

Good use of useMemo for the debounced function with proper cleanup in useEffect to cancel pending debounced calls on unmount, preventing potential state updates on unmounted components.


193-196: LGTM!

The text filter correctly uses the debounced submit, updating the field value immediately for responsive UI while debouncing the actual form submission.


150-152: LGTM!

The isInlineVariant condition is correctly added to trigger auto-submit for FilterSelect, consistent with the FilterToggleGroup behavior.


78-89: If FilterToggleGroup type includes isLabelHidden property, pass it to ToggleGroupWithLabel.

The ToggleGroupWithLabel component supports isLabelHidden (as shown by InputWithLabel pattern for FilterText at line 188). However, it is not currently being passed in the FilterToggleGroup cases. If the FilterToggleGroup type definition includes this property, it should be passed similarly: isLabelHidden={item.isLabelHidden}.

@vercel vercel bot temporarily deployed to Preview – o2s-docs January 9, 2026 13:36 Inactive
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/components/Filters/FilterItem.tsx (1)

41-76: Multi ToggleGroup: “ALL” reset depends on onClick (pointer-only) — fix to support keyboard/a11y.
With keyboard interaction, allWasClickedRef won’t flip, so selecting “ALL” while other values are selected may fail to clear them. Consider deriving intent from previous selection instead of onClick.

Proposed fix (remove pointer-only dependency)
-                        const toggleGroupItems = item.options.map((option) => {
+                        const toggleGroupItems = item.options.map((option) => {
                             return (
                                 <ToggleGroupItem
                                     key={option.value}
                                     value={option.value}
                                     className="min-w-[98px] rounded-sm h-9"
-                                    onClick={() => {
-                                        allWasClickedRef.current = option.value === 'ALL';
-                                    }}
                                 >
                                     {option.label}
                                 </ToggleGroupItem>
                             );
                         });

                         const handleValueChange = async (value: string[]) => {
                             let newValue: string[];

-                            if (allWasClickedRef.current) {
-                                newValue = [];
-                                allWasClickedRef.current = false;
-                            } else {
-                                newValue = value.filter((v) => v !== 'ALL');
-                            }
+                            const hadSelections = (field.value?.length ?? 0) > 0;
+                            const includesAll = value.includes('ALL');
+
+                            if (includesAll && hadSelections) {
+                                // user chose ALL to clear existing selections
+                                newValue = [];
+                            } else {
+                                // coming from ALL-state (empty) or normal multi-select updates
+                                newValue = value.filter((v) => v !== 'ALL');
+                            }

                             await setFieldValue(field.name, newValue);
                             if (isLeading || isInlineVariant) {
                                 await submitForm();
                             }
                         };

Follow-up: if you adopt this, you can delete allWasClickedRef (Line 26) and drop useRef from the React import.

</details>

</blockquote></details>

</blockquote></details>
🧹 Nitpick comments (2)
packages/ui/src/components/Filters/FilterItem.tsx (2)

3-16: Hook/constant additions look fine; debounce delay change should be intentional.
TEXT_FILTER_DEBOUNCE_MS=1000 is a noticeable UX shift—worth double-checking it matches product expectations.


96-125: Single ToggleGroup path is clear; consider extracting shared rendering to reduce duplication.
The toggleGroupItems + ScrollContainer + ToggleGroupWithLabel pattern is now duplicated across single/multiple.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8431052 and 92f8c1c.

📒 Files selected for processing (1)
  • packages/ui/src/components/Filters/FilterItem.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
packages/ui/src/components/Filters/FilterItem.tsx (3)
packages/framework/src/utils/models/filters.ts (1)
  • FilterItem (12-17)
packages/ui/src/components/Filters/Filters.types.ts (1)
  • FilterItemProps (28-38)
packages/ui/src/elements/toggle-group.tsx (2)
  • ToggleGroupItem (127-127)
  • ToggleGroupWithLabel (127-127)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: deploy-preview
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (3)
packages/ui/src/components/Filters/FilterItem.tsx (3)

141-144: Good: Select now respects isInlineVariant for auto-submit.
This matches the new inline behavior without forcing submits in other contexts.


184-187: Verify intended behavior: text filter now always auto-submits (debounced), even when not leading/inline.
Toggle/select submission is gated by (isLeading || isInlineVariant), but text isn’t—if that’s unintended, gate debouncedSubmit() similarly.

Possible adjustment (if you want parity with other filter types)
                                     onChange={async (e) => {
                                         await setFieldValue(field.name, e.target.value);
-                                        debouncedSubmit();
+                                        if (isLeading || isInlineVariant) {
+                                            debouncedSubmit();
+                                        }
                                     }}

28-35: Debounced submit lifecycle and API contract verified.
The [email protected] library correctly returns a callable with cancel(options?: { upcomingOnly?: boolean }): void. The TypeScript types match the runtime behavior, and your cleanup pattern in the useEffect is properly implemented.

@vercel vercel bot temporarily deployed to Preview – o2s-docs January 9, 2026 13:50 Inactive
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @packages/ui/src/components/Filters/FilterItem.tsx:
- Line 16: The debounce constant TEXT_FILTER_DEBOUNCE_MS in FilterItem.tsx is
set to 1000ms which can feel sluggish; change its value to a lower, more
responsive default (e.g., 300–500ms — set to 500ms for a conservative
improvement) and optionally expose it as a prop or configurable value on the
FilterItem component so tests and callers can override the debounce when needed;
update any references to TEXT_FILTER_DEBOUNCE_MS and relevant tests/usage to
reflect the new default.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 92f8c1c and bdc6d62.

📒 Files selected for processing (1)
  • packages/ui/src/components/Filters/FilterItem.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
packages/ui/src/components/Filters/FilterItem.tsx (3)
packages/framework/src/utils/models/filters.ts (1)
  • FilterItem (12-17)
packages/ui/src/components/Filters/Filters.types.ts (1)
  • FilterItemProps (28-38)
packages/ui/src/elements/toggle-group.tsx (2)
  • ToggleGroupItem (127-127)
  • ToggleGroupWithLabel (127-127)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: deploy-preview
🔇 Additional comments (5)
packages/ui/src/components/Filters/FilterItem.tsx (5)

26-32: LGTM! Proper debounce lifecycle management.

The debounced submit is correctly memoized and cleaned up on unmount, preventing potential memory leaks and stale submissions.


57-73: ALL selection logic is correct but complex.

The handleValueChange function properly manages the "ALL" toggle behavior: clicking ALL when filters are active clears them, and any other selection automatically excludes ALL. The state cycle (empty → ALL displayed → specific selections → ALL clears → empty) works as intended for a multi-select toggle with an "ALL" option.


93-121: LGTM! Single selection toggle refactor is clean.

The single-selection path correctly handles ALL as an empty filter state, defaults appropriately, and consistently applies auto-submit behavior for inline variants.


138-140: LGTM! Consistent inline variant behavior.

The auto-submit logic for FilterSelect now correctly supports the inline variant, matching the pattern used in FilterToggleGroup.


181-184: LGTM! Text filter debounce refactored correctly.

The field value updates immediately for responsive UI feedback, while form submission is properly debounced. The cleanup in useEffect ensures cancelled submissions if the component unmounts.

@tomaszpacior tomaszpacior merged commit 79b7c87 into main Jan 12, 2026
9 checks passed
@tomaszpacior tomaszpacior deleted the feature/show-more-filters branch January 12, 2026 10:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants