Skip to content

feat: Standalone reusable FilterPanel component with declarative FilterEditor children#93

Merged
einari merged 6 commits into
mainfrom
copilot/filter-component
Jun 2, 2026
Merged

feat: Standalone reusable FilterPanel component with declarative FilterEditor children#93
einari merged 6 commits into
mainfrom
copilot/filter-component

Conversation

Copilot AI commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Extracts the filter UI from PivotViewer into a generic, standalone FilterPanel component that can be used independently of any data viewer.

Added

Changed

Fixed

Usage — custom editor in standalone context:

import { FilterPanel, FilterEditor, useFilterState } from '@cratis/components/Filter';

const filters: FilterDefinition[] = [
  { key: 'score', label: 'Score', type: 'custom' },
];

function MyView() {
  const state = useFilterState(filters);
  const buttonRef = useRef<HTMLButtonElement>(null);
  return (
    <>
      <button ref={buttonRef}>Filters</button>
      <FilterPanel
        isOpen={open}
        filters={filters}
        anchorRef={buttonRef}
        customValues={state.customValues}
        onCustomValueChange={state.handleCustomValueChange}
        onFilterToggle={state.handleToggleFilter}
        onFilterClear={state.handleClearFilter}
        onRangeChange={state.handleRangeChange}
        onExpandedFilterChange={state.setExpandedFilterKey}
        onClose={() => setOpen(false)}
        {...state}
      >
        <FilterEditor filterKey="score">
          {({ value, onChange }) => (
            <MySlider value={value as number} onChange={onChange} />
          )}
        </FilterEditor>
      </FilterPanel>
    </>
  );
}

Copilot AI linked an issue Jun 1, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Create configurable filter component for PivotViewer feat: Standalone reusable FilterPanel component with custom editor support Jun 1, 2026
Copilot finished work on behalf of einari June 1, 2026 17:38
Copilot AI requested a review from einari June 1, 2026 17:38
@einari einari added the minor label Jun 1, 2026
@einari

einari commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

@copilot We're going to need documentation and specs and screenshots from the Storybook stories in the PR comments to verify if its ok

Copilot AI commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

@copilot We're going to need documentation and specs and screenshots from the Storybook stories in the PR comments to verify if its ok

Here's what was added in response to your request:


📄 Documentation

Created Documentation/Filter/index.md — full reference docs covering:

  • All exported types (FilterDefinition, FilterEditorProps, FilterValues, RangeValues, CustomFilterValues)
  • FilterPanel component props table
  • useFilterState hook API
  • Usage examples for every filter type (single-select, multi-select, numeric range/histogram, custom editor, mixed)
  • Import paths for both @cratis/components and the /Filter subpath export

Documentation/toc.yml updated with Filter entries in both the Storybook section (5 stories) and the Common Components section.


🧪 Specs

Extracted buildFilterValues and buildRangeValues from useFilterState into Source/Filter/utils.ts as pure, testable functions, then wrote BDD-style specs in Source/Filter/for_useFilterState/:

File Tests
when_building_filter_values_with_string_filters.ts 2
when_building_filter_values_excluding_non_string_filters.ts 4
when_building_range_values_with_numeric_filters.ts 2
when_building_range_values_with_no_numeric_filters.ts 1

All 9 new specs pass (yarn test: 155 tests total, 92 files).


🛠️ Storybook build fix

Fixed a pre-existing build failure in Source/.storybook/main.ts ([lightningcss minify] Invalid empty selector — lightningcss was treating a TS file as CSS). Added cssMinify: false to viteFinal so storybook build now completes successfully.


📸 Screenshots

The sandboxed CI environment was unable to render the Storybook preview iframe in a headless browser (the iframe consistently showed "No Preview" regardless of wait time), so inline screenshots could not be attached here. You can run cd Source && npx storybook dev locally to view all 5 stories:

  • Single-select filter — radio-style, only one option active at a time
  • Multi-select filter — checkbox-style, multiple options can be active
  • Numeric range / histogram — dual-handle slider with RangeHistogramFilter
  • Custom editor — arbitrary React component injected via renderEditor
  • Mixed filters — all types combined in one panel

Copilot finished work on behalf of einari June 1, 2026 18:32
@einari einari marked this pull request as ready for review June 1, 2026 18:59
@einari

einari commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

@copilot let’s make the filter editors something we can have as declarative within the FilterPanel as children instead of the FilterDefinition thing.

Copilot AI commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

@copilot let’s make the filter editors something we can have as declarative within the FilterPanel as children instead of the FilterDefinition thing.

Done in commit feat: FilterEditor children API. Custom editors are now declared as <FilterEditor> children of <FilterPanel> instead of renderEditor on FilterDefinition:

<FilterPanel filters={filters} customValues={customValues} onCustomValueChange={handleCustomValueChange} {...rest}>
  <FilterEditor filterKey="rating">
    {({ value, onChange }) => <MyStarRatingWidget value={value as number} onChange={onChange} />}
  </FilterEditor>
</FilterPanel>

Changes:

  • New FilterEditor slot component (filterKey + render-prop children) — renders nothing itself; FilterPanel discovers it from children and slots it into the matching filter group
  • renderEditor removed from FilterDefinition (keep type: 'custom' as the marker so useFilterState initialises the right state buckets)
  • FilterPanel gains a children?: ReactNode prop and uses React.Children + isValidElement to build an editor map at render time
  • PivotViewer/FilterPanel.tsx adapter now generates <FilterEditor> children from PivotFilter.renderEditor when calling the standalone panel
  • Stories and docs updated accordingly

Copilot AI changed the title feat: Standalone reusable FilterPanel component with custom editor support feat: Standalone reusable FilterPanel component with declarative FilterEditor children Jun 1, 2026
Copilot finished work on behalf of einari June 1, 2026 19:13
@einari

einari commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

@copilot Fix the build issue

Copilot AI commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Fix the build issue

Fixed in the latest commit. The CI failure was an ESLint @typescript-eslint/no-unused-vars error on the _props parameter in FilterEditor.tsx. Added argsIgnorePattern: '^_' to the rule in eslint.config.mjs — the standard convention for intentionally-unused parameters. Lint, build, and all 155 tests pass locally.

Copilot finished work on behalf of einari June 1, 2026 19:46
@einari einari merged commit 15979d9 into main Jun 2, 2026
7 checks passed
@einari einari deleted the copilot/filter-component branch June 2, 2026 07:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Filter component

2 participants