Skip to content

Comments

feat: filters per visualization#3455

Open
xavikp wants to merge 5 commits intomainfrom
add-multifilter-datacollection
Open

feat: filters per visualization#3455
xavikp wants to merge 5 commits intomainfrom
add-multifilter-datacollection

Conversation

@xavikp
Copy link
Contributor

@xavikp xavikp commented Feb 18, 2026

Description

Add per-visualization filter and preset overrides to OneDataCollection. Each visualization can declare its own filters and/or presets, letting different views show only the relevant filter controls while maintaining independent filter state across view switches.

Problem and constraints

OneDataCollection supports multiple visualization types (table, card, list, kanban) but shares a single filter/preset set across all of them. Some views need only a subset of filters, and switching views should not lose the user's applied filters.

Key constraints:

  • Switching visualization must save the outgoing view's filter state and restore the incoming view's state
  • During the React render between setCurrentFilters and the parent re-rendering with new props, child components must see the target view's filters synchronously (not stale filters from the previous view)
  • A never-visited view should initialize with its first preset (per-viz > source > empty)
  • Storage restoration must be idempotentsetAllVisualizationFilters only takes effect once
  • Backward compatible: when no visualization declares overrides, behavior is identical to before

Behavior rules

Scenario effectiveFilters effectivePresets
No viz has overrides source.filters source.presets
Active viz has filters viz's filters source presets filtered to matching keys
Active viz has presets (same as above) viz's presets (replaces source, not merged)
Active viz has both viz's filters viz's presets
Active viz has neither, but others do source.filters source.presets

State transition guarantees

  1. Save → Restore: useLayoutEffect on currentVisualization change saves outgoing state to filterStatesRef, restores incoming state via sourceSetCurrentFilters
  2. Synchronous bridge: pendingFiltersRef holds the target state between the layout effect call and the next render, so currentFilters returns the correct value immediately
  3. Init idempotency: appliedInitRef ensures setAllVisualizationFilters (called by storage) only applies once
  4. Default for unvisited views: Falls back to first preset of (viz.presets ?? sourcePresets ?? []), then {}

Backward compatibility

  • hasPerVisualizationFilters is false when no visualization declares filters or presets → all new code paths are skipped
  • effectiveSource passes through source unchanged when the feature is off
  • Storage shape: allVisualizationFilters is {} when off, so existing storage keys are unaffected

Reviewer guide

Suggested reading order:

  1. visualizations/collection/types.ts — new filters and presets fields on visualization config
  2. usePerVisualizationFilters.ts — core hook (filter resolution, state save/restore, synchronous bridge)
  3. OneDatacollection.tsx — integration point (effectiveSource patching)
  4. useDataCollectionStorage.ts — persistence of allVisualizationFilters
  5. usePerVisualizationFilters.test.ts — 16 test cases covering all behavior rules
  6. per-visualization-filters.stories.tsx — 3 Storybook stories demonstrating usage
multiPresetsFilters.mp4

@github-actions github-actions bot added feat react Changes affect packages/react labels Feb 18, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 18, 2026

✅ No New Circular Dependencies

No new circular dependencies detected. Current count: 0

@github-actions
Copy link
Contributor

github-actions bot commented Feb 18, 2026

📦 Alpha Package Version Published

Use pnpm i github:factorialco/f0#npm/alpha-pr-3455 to install the package

Use pnpm i github:factorialco/f0#9b124c314463bc5af22ae50718b641643f194427 to install this specific commit

@github-actions
Copy link
Contributor

github-actions bot commented Feb 18, 2026

🔍 Visual review for your branch is published 🔍

Here are the links to:

@github-actions
Copy link
Contributor

github-actions bot commented Feb 18, 2026

Coverage Report for packages/react

Status Category Percentage Covered / Total
🔵 Lines 39.03% 7182 / 18398
🔵 Statements 38.45% 7352 / 19117
🔵 Functions 29.5% 1530 / 5186
🔵 Branches 28.83% 4184 / 14508
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/react/src/components/OneFilterPicker/components/FiltersControls.tsx 81.81% 53.06% 80.95% 82.43% 76, 113-117, 122, 128, 190-286
packages/react/src/experimental/OneDataCollection/OneDatacollection.tsx 69.5% 42.1% 67.5% 68.84% 229-233, 330-346, 355-363, 386-389, 406, 429, 445-454, 461-467, 485-491, 523-526, 672, 679, 753-857
packages/react/src/experimental/OneDataCollection/exports.ts 100% 100% 100% 100%
packages/react/src/experimental/OneDataCollection/useEventEmitter.ts 77.77% 57.69% 100% 93.33% 46, 52, 56, 81-86, 93, 99, 103
packages/react/src/experimental/OneDataCollection/hooks/usePerVisualizationFilters.ts 97.87% 95% 93.75% 97.59% 163-164
packages/react/src/experimental/OneDataCollection/hooks/useDataColectionStorage/types.ts 100% 100% 100% 100%
packages/react/src/experimental/OneDataCollection/hooks/useDataColectionStorage/useDataCollectionStorage.ts 55.26% 35% 66.66% 55.26% 59-63, 85-105, 138-153
packages/react/src/experimental/OneDataCollection/hooks/useDataCollectionData/useDataCollectionLanesData.tsx 97.36% 71.42% 100% 97.14% 160
packages/react/src/experimental/OneDataCollection/visualizations/collection/types.ts 100% 100% 100% 100%
Generated in workflow #11119 for commit 6985ec9 by the Vitest Coverage Report Action

…ider deps

- FiltersControls: skip localFiltersValue keys absent from filter definitions,
  preventing crashes when switching between visualizations with different schemas
- useDataCollectionStorage: move setStorageReady(true) inside the async .then()
  callback so storage reports readiness only after values are actually restored
- LaneProvider: destructure hook return into stable fields for useEffect deps,
  breaking an infinite render loop caused by unstable object identity
Define optional per-visualization filter and preset overrides on
visualization configs. Each visualization type in the union now
intersects with VisualizationFilterOverrides<Filters>, allowing
views to declare their own `filters` (subset of source filters)
and/or `presets` (replacing source presets for that view).

Export the new type from the public barrel for consumer use.
Core hook that manages independent filter state per visualization.
When no visualization declares overrides it is a pure pass-through.
Otherwise each view gets its own currentFilters with save/restore
on switch, synchronous transition bridging via pendingFiltersRef,
and idempotent storage initialization.

Includes 16 test cases covering: pass-through mode, effective
filters/presets resolution, save-restore on switch, storage
restore, init idempotency, synchronous transitions, and default
preset fallback for never-visited views.
Wire usePerVisualizationFilters into OneDatacollection.tsx:
- Patch source with effectiveSource so children see correct filters
- Use effectiveFilters/effectivePresets for OneFilterPicker rendering
- Use activeCurrentFilters/activeSetCurrentFilters for state management
- Persist allVisualizationFilters through useDataCollectionStorage
- Include currentVisualization in filter/preset change event emissions

Storage types extended with visualizationFilters feature for
persisting per-view filter state across page reloads.
Three stories demonstrating usage patterns:
- FiltersPerVisualization: table (department only) vs card (salary+search)
- MixedGlobalAndPerViewFilters: table overrides, card inherits global
- PerViewPresetsOnly: table with custom presets, list with global
@xavikp xavikp force-pushed the add-multifilter-datacollection branch from ffb452c to 6985ec9 Compare February 23, 2026 09:32
@xavikp xavikp marked this pull request as ready for review February 23, 2026 10:57
@xavikp xavikp requested a review from a team as a code owner February 23, 2026 10:57
@xavikp xavikp requested a review from Copilot February 23, 2026 16:47
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds per-visualization filter and preset overrides to OneDataCollection, enabling each visualization (table, card, list, kanban) to declare its own independent filter configuration while maintaining separate filter states across view switches.

Changes:

  • Added VisualizationFilterOverrides type allowing visualizations to declare optional filters and presets properties
  • Implemented usePerVisualizationFilters hook managing independent filter state per visualization with synchronous transitions via pendingFiltersRef
  • Extended storage system to persist per-visualization filter states under visualizationFilters key

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
visualizations/collection/types.ts Adds VisualizationFilterOverrides type and applies it to all visualization union variants
hooks/usePerVisualizationFilters.ts Core hook implementing filter resolution, state save/restore, and synchronous transition logic
OneDatacollection.tsx Integrates per-viz filters hook, patches source with effective filters, passes visualization index to event emitter
useEventEmitter.ts Adds optional currentVisualization parameter to include viz index in filter/preset events
hooks/useDataColectionStorage/types.ts Extends storage types with visualizationFilters feature
hooks/useDataColectionStorage/useDataCollectionStorage.ts Fixes storage ready timing - sets flag after restoration completes
hooks/useDataCollectionData/useDataCollectionLanesData.tsx Improves dependency tracking by destructuring hook values for useEffect
components/OneFilterPicker/components/FiltersControls.tsx Adds defensive check for missing filter keys when switching visualizations
hooks/__tests__/usePerVisualizationFilters.test.ts Comprehensive test suite with 16 test cases covering all behavior scenarios
__stories__/filters/per-visualization-filters.stories.tsx Three Storybook examples demonstrating usage patterns
exports.ts Exports VisualizationFilterOverrides type for public API

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +38 to +39
/** Additional presets shown only when this visualization is active.
* These are displayed alongside the global source presets. */
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

The documentation states "These are displayed alongside the global source presets" but the actual implementation at lines 211-212 shows that visualization presets completely replace source presets, not merge with them. The documentation should be corrected to say "These replace the global source presets for this visualization" to match the actual behavior.

Suggested change
/** Additional presets shown only when this visualization is active.
* These are displayed alongside the global source presets. */
/** Preset configuration used only when this visualization is active.
* These replace the global source presets for this visualization. */

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat react Changes affect packages/react

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant