Skip to content

improvement(knowledge): polish tag filter dropdowns#4816

Open
andresdjasso wants to merge 3 commits into
improvement/platformfrom
improvement/kb-tag-filter-dropdown
Open

improvement(knowledge): polish tag filter dropdowns#4816
andresdjasso wants to merge 3 commits into
improvement/platformfrom
improvement/kb-tag-filter-dropdown

Conversation

@andresdjasso
Copy link
Copy Markdown

Summary

  • Replace the knowledge base document filter controls with chip-style dropdowns for status and tag filters.
  • Redesign tag filtering into compact inline rows with tag, operator, and value controls, including date picker support and between-range inputs.
  • Add reusable EMCN chip controls: ChipDatePicker, ChipMultiSelect, and Calendar.
  • Align and animate the resource filter/sort dropdown surfaces to match the workspace dropdown behavior.

Type of Change

  • Improvement

Testing

  • Tested manually on the knowledge base page with a local test base.
  • Verified all tag definitions appear in the tag dropdown.
  • Verified filter and sort dropdown positioning inside the resource section.
  • bunx biome check passed for touched files.
  • git diff --check passed.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Screenshots/Videos

@vercel
Copy link
Copy Markdown

vercel Bot commented May 31, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped May 31, 2026 12:28am

Request Review

@waleedlatif1 waleedlatif1 marked this pull request as ready for review May 31, 2026 01:53
@cursor
Copy link
Copy Markdown

cursor Bot commented May 31, 2026

PR Summary

Low Risk
UI-only filter and component changes; status filter moves from multi-select to single choice, which may narrow edge-case filtering behavior.

Overview
Knowledge list and document views now use chip-style filter controls (ChipDropdown, ChipMultiSelect, ChipDatePicker) instead of Combobox, with per-section Clear actions and a redesigned tag filter UI (inline tag/operator/value rows, date ranges, and scroll-on-add).

ResourceOptionsBar treats Filter and Sort as one menu bar: shared open state so switching between them is a single click, with end-aligned popovers and matching open/close animation.

New shared EMCN pieces: Calendar (local YYYY-MM-DD parsing), ChipDatePicker, ChipMultiSelect, plus Input chip variant and flush on ChipDropdown for dense filter rows.

Reviewed by Cursor Bugbot for commit ab9fb09. Configure here.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit ab9fb09. Configure here.

const [view, setView] = useState(() => {
const base = selected ?? today
return { month: base.getMonth(), year: base.getFullYear() }
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Calendar month ignores value

Medium Severity

The calendar keeps its visible month in local state initialized only on mount. If the selected value is set or changed later, or the user navigates to another month and closes the picker without picking a day, reopening can show a month that does not contain the current selection, so the chosen date may not appear selected.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ab9fb09. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 31, 2026

Greptile Summary

This PR replaces the knowledge base filter controls with chip-style dropdowns (ChipDropdown, ChipMultiSelect, ChipDatePicker) and adds a custom Calendar component. It also redesigns the tag filter rows into compact inline controls and aligns the resource filter/sort popover animation and positioning with the workspace dropdown.

  • Three new EMCN components are introduced (Calendar, ChipDatePicker, ChipMultiSelect) and exported from the component index, along with a new chip input variant for use in tag-filter value rows.
  • resource-options-bar.tsx gains a shared openMenu state to coordinate Filter and Sort as a single menu bar, preventing the need for a double-click when switching between them.
  • base.tsx and knowledge.tsx migrate from Combobox-backed filter panels to the new chip controls, simplifying state (e.g. enabledFilter changes from string[] to a typed union) and removing intermediate display-label memos.

Confidence Score: 4/5

Safe to merge; all three filter pages work correctly with the new chip controls and the openMenu coordination logic handles all prop combinations without duplicating UI elements.

The Calendar component's view state is lazy-initialized from the value prop but never re-synchronizes while mounted. In every current call site the calendar is inside a Popover that unmounts on close, so users never see a stale month, but the exported Calendar API has a subtle footgun for future direct-embedding use cases. The remaining findings are minor duplication and a string cast. No data loss or broken filtering paths are introduced by this PR.

apps/sim/components/emcn/components/calendar/calendar.tsx — the view-state initialization pattern warrants a second look before Calendar is used outside a popover context.

Important Files Changed

Filename Overview
apps/sim/components/emcn/components/calendar/calendar.tsx New public Calendar component; view state is lazy-initialized once and never re-syncs with external value prop changes — a footgun for direct (non-popover) embedding.
apps/sim/components/emcn/components/chip-date-picker/chip-date-picker.tsx New ChipDatePicker component; well-structured, duplicates the POPOVER_ANIMATION_CLASSES constant that also appears in resource-options-bar.tsx.
apps/sim/components/emcn/components/chip-multi-select/chip-multi-select.tsx New ChipMultiSelect component; clean implementation with correct search-clear-on-close and prevent-default select behavior.
apps/sim/app/workspace/[workspaceId]/components/resource/components/resource-options-bar/resource-options-bar.tsx Adds openMenu state for single-click switching between Filter and Sort; conditional sort rendering logic correctly handles all prop combinations without duplicate renders.
apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx Migrates status filter to typed union, refactors tag filter rows to inline chips with TagFilterValueControl; includes an unsafe string cast in the ChipDropdown onChange handler.
apps/sim/app/workspace/[workspaceId]/knowledge/knowledge.tsx Replaces Combobox filter panels with ChipDropdown/ChipMultiSelect; connector and content filters correctly simplified from string[] to single-select with all-as-empty semantics.
apps/sim/components/emcn/components/chip-dropdown/chip-dropdown.tsx Minor addition: forwards the new flush prop to chipVariants, no logic changes.
apps/sim/components/emcn/components/index.ts Exports three new components and their types; alphabetically ordered, follows existing patterns.
apps/sim/components/emcn/components/input/input.tsx Adds chip variant to inputVariants CVA; well-documented, no unintended side effects on the default variant.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[ResourceOptionsBar render] --> B{onFilterToggle?}
    B -- yes --> C[Render toggle Button\nuncontrolled]
    B -- no --> D{filter ReactNode?}
    D -- yes --> E[Render Popover.Root\nopen = openMenu === 'filter'\nAnchor wraps Filter + Sort]
    D -- no --> F[Render null]
    C --> G{sort?}
    E --> H{sort?}
    F --> I{sort?}
    G -- yes --> J[Standalone SortDropdown\nuncontrolled]
    H -- yes --> K[Controlled SortDropdown\ninside Anchor\nopen = openMenu === 'sort']
    I -- yes --> L[Standalone SortDropdown\nuncontrolled]
    E --> M[PopoverContent\nalign=end, sideOffset=6\nanimated]
    M --> N[filter children\ne.g. AutoWidthPanel with\nChipDropdown + TagFilterSection]
    style E fill:#d4edda
    style K fill:#d4edda
    style J fill:#fff3cd
    style L fill:#fff3cd
Loading

Reviews (1): Last reviewed commit: "improvement(knowledge): soften list filt..." | Re-trigger Greptile

Comment on lines +99 to +102
const [view, setView] = useState(() => {
const base = selected ?? today
return { month: base.getMonth(), year: base.getFullYear() }
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 The view state is lazy-initialized once from value and never re-synchronizes when value changes externally while Calendar is mounted. In the current usage (inside a Popover that unmounts on close) this is safe, but Calendar is exported as a public component and embedding it directly — e.g. in a form where a parent resets the date — would leave the calendar stuck showing the wrong month. Adding a useEffect that resets the view whenever value changes keeps the public API correct.

Suggested change
const [view, setView] = useState(() => {
const base = selected ?? today
return { month: base.getMonth(), year: base.getFullYear() }
})
const [view, setView] = useState(() => {
const base = selected ?? today
return { month: base.getMonth(), year: base.getFullYear() }
})
useEffect(() => {
if (selected) {
setView({ month: selected.getMonth(), year: selected.getFullYear() })
}
}, [selected])

Comment on lines +16 to +17
const POPOVER_ANIMATION_CLASSES =
'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=open]:animate-in motion-reduce:animate-none'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 The POPOVER_ANIMATION_CLASSES string here is byte-for-byte identical to FILTER_POPOVER_ANIMATION_CLASSES in resource-options-bar.tsx. Extracting it to a shared constant removes the duplication and ensures animation tweaks are applied consistently.

Suggested change
const POPOVER_ANIMATION_CLASSES =
'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=open]:animate-in motion-reduce:animate-none'
/**
* Shared Radix popover enter/exit animation classes keeps chip-family popovers
* visually consistent. Also used in resource-options-bar.tsx.
*/
export const POPOVER_ANIMATION_CLASSES =
'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=open]:animate-in motion-reduce:animate-none'

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +901 to +902
onChange={(value) => {
setEnabledFilter(value as 'all' | 'enabled' | 'disabled')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 The as cast widens the type without a runtime guard. If STATUS_FILTER_OPTIONS is ever extended without updating this handler, the cast silently accepts the new value and setEnabledFilter receives a string outside the union, breaking the filter logic.

Suggested change
onChange={(value) => {
setEnabledFilter(value as 'all' | 'enabled' | 'disabled')
onChange={(value) => {
if (value !== 'all' && value !== 'enabled' && value !== 'disabled') return
setEnabledFilter(value)

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.

1 participant