Skip to content

SRVKP-11167: Add custom DataViewFilterToolbar and useDataViewFilter hook#1003

Merged
openshift-merge-bot[bot] merged 1 commit intoopenshift-pipelines:masterfrom
anwesha-palit-redhat:feat/SRVKP-11167
Mar 23, 2026
Merged

SRVKP-11167: Add custom DataViewFilterToolbar and useDataViewFilter hook#1003
openshift-merge-bot[bot] merged 1 commit intoopenshift-pipelines:masterfrom
anwesha-palit-redhat:feat/SRVKP-11167

Conversation

@anwesha-palit-redhat
Copy link
Copy Markdown
Contributor

Summary

  • Introduces DataViewFilterToolbar, a reusable filter toolbar component built with PatternFly 6 core components (Toolbar, MenuToggle, Popper, SearchInput, ToolbarFilter) that mirrors the console's DataViewToolbar + DataViewFilters pattern. This is needed because @patternfly/react-data-view filter components (DataViewCheckboxFilter, etc.) cannot be used in dynamic plugins due to a React context mismatch — the host console's ConsoleDataView renders DataViewFilters from its own module instance, and the plugin's bundled copy creates a separate context, causing runtime crashes.

  • Introduces useDataViewFilter, a generic hook that manages filter state (name, label, and configurable checkbox filters) and returns pre-filtered data for use with ConsoleDataView.

Features

  • Category selector dropdown: When multiple filter types exist, a dropdown lets users switch between filter categories (Name, Label, Status, etc.), showing only the active filter's input — matching the DataViewFilters UX pattern.
  • Name filter: Text search with chip display.
  • Label filter: Enter key=value or key label selectors, displayed as removable chips. Supports AND matching across multiple labels.
  • Checkbox filters: Configurable dropdown filters (e.g., Status, Data source) with per-option count badges and optional totalCount for server-side pagination.
  • Ref-based width syncing: Dropdown menus and their toggles auto-sync width on first open to prevent layout shift.
  • Clear all: "Clear filters" button resets all filter state.

Why not use @patternfly/react-data-view directly?

ConsoleDataView internally renders DataViewFilters from the host's copy of @patternfly/react-data-view. Filter components passed via additionalFilterNodes from the plugin come from a different module instance, creating separate React contexts. Since @patternfly/react-data-view is not in the console's Webpack Module Federation shared modules list, there is no way to share a single instance. This custom toolbar uses only @patternfly/react-core components (which are shared) and pre-filters data before passing it to ConsoleDataView, avoiding the context issue entirely.

Sample Usage

import { DataViewFilterToolbar } from '../common/DataViewFilterToolbar';
import type { CheckboxFilterConfig } from '../common/DataViewFilterToolbar';
import { useDataViewFilter } from '../hooks/useDataViewFilter';

const MyList: FC = () => {
  const [data, loaded, loadError] = useK8sWatchResource<MyKind[]>({ ... });

  // Define checkbox filters with per-option counts
  const checkboxFilters = useMemo<CheckboxFilterConfig[]>(() => {
    const statusCounts: Record<string, number> = {};
    (data || []).forEach((obj) => {
      const status = getStatus(obj);
      statusCounts[status] = (statusCounts[status] || 0) + 1;
    });
    return [{
      id: 'status',
      title: t('Status'),
      placeholder: t('Filter by status'),
      options: [
        { value: 'Succeeded', label: t('Succeeded'), count: statusCounts['Succeeded'] || 0 },
        { value: 'Failed', label: t('Failed'), count: statusCounts['Failed'] || 0 },
      ],
    }];
  }, [data, t]);

  const getName = useCallback((obj: MyKind) => obj.metadata?.name || '', []);
  const getLabels = useCallback((obj: MyKind) => obj.metadata?.labels, []);

  const matchesCheckboxFilter = useCallback(
    (obj: MyKind, filterId: string, selectedValues: string[]) => {
      if (filterId === 'status') {
        return selectedValues.includes(getStatus(obj));
      }
      return true;
    },
    [],
  );

  const { filterValues, onFilterChange, onClearAll, filteredData } =
    useDataViewFilter<MyKind>({
      data: data || [],
      getName,
      getLabels,
      checkboxFilters,
      matchesCheckboxFilter,
    });

  return (
    <ListPageBody>
      <DataViewFilterToolbar
        filterValues={filterValues}
        onFilterChange={onFilterChange}
        onClearAll={onClearAll}
        checkboxFilters={checkboxFilters}
      />
      <ConsoleDataView
        data={filteredData}
        loaded={loaded}
        loadError={loadError}
        columns={columns}
        getDataViewRows={getDataViewRows}
        hideNameLabelFilters
      />
    </ListPageBody>
  );
};

Key points:

  • Pass hideNameLabelFilters to ConsoleDataView to avoid duplicate filter UI
  • Use hideLabelFilter or hideNameFilter on DataViewFilterToolbar to selectively hide filters
  • FilterOption.count is mandatory; totalCount is optional (for server-side pagination, renders as count/totalCount)

Test plan

  • Verify DataViewFilterToolbar renders the category selector dropdown when multiple filters are configured
  • Verify checkbox filters show options with count badges, toggle selection, and filter data
  • Verify "Clear filters" resets all filter state
  • Verify no layout shift when opening/closing filter dropdowns
  • Verify the toolbar integrates correctly with ConsoleDataView (data is pre-filtered, hideNameLabelFilters is set)

Screenshots / Screen Recordings

custom_filter.mov

Co-authored and Assisted By

  • Claude

@openshift-ci-robot
Copy link
Copy Markdown
Collaborator

openshift-ci-robot commented Mar 20, 2026

@anwesha-palit-redhat: This pull request references SRVKP-11167 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "4.22.0" version, but no target version was set.

Details

In response to this:

Summary

  • Introduces DataViewFilterToolbar, a reusable filter toolbar component built with PatternFly 6 core components (Toolbar, MenuToggle, Popper, SearchInput, ToolbarFilter) that mirrors the console's DataViewToolbar + DataViewFilters pattern. This is needed because @patternfly/react-data-view filter components (DataViewCheckboxFilter, etc.) cannot be used in dynamic plugins due to a React context mismatch — the host console's ConsoleDataView renders DataViewFilters from its own module instance, and the plugin's bundled copy creates a separate context, causing runtime crashes.

  • Introduces useDataViewFilter, a generic hook that manages filter state (name, label, and configurable checkbox filters) and returns pre-filtered data for use with ConsoleDataView.

Features

  • Category selector dropdown: When multiple filter types exist, a dropdown lets users switch between filter categories (Name, Label, Status, etc.), showing only the active filter's input — matching the DataViewFilters UX pattern.
  • Name filter: Text search with chip display.
  • Label filter: Enter key=value or key label selectors, displayed as removable chips. Supports AND matching across multiple labels.
  • Checkbox filters: Configurable dropdown filters (e.g., Status, Data source) with per-option count badges and optional totalCount for server-side pagination.
  • Ref-based width syncing: Dropdown menus and their toggles auto-sync width on first open to prevent layout shift.
  • Clear all: "Clear filters" button resets all filter state.

Why not use @patternfly/react-data-view directly?

ConsoleDataView internally renders DataViewFilters from the host's copy of @patternfly/react-data-view. Filter components passed via additionalFilterNodes from the plugin come from a different module instance, creating separate React contexts. Since @patternfly/react-data-view is not in the console's Webpack Module Federation shared modules list, there is no way to share a single instance. This custom toolbar uses only @patternfly/react-core components (which are shared) and pre-filters data before passing it to ConsoleDataView, avoiding the context issue entirely.

Sample Usage

import { DataViewFilterToolbar } from '../common/DataViewFilterToolbar';
import type { CheckboxFilterConfig } from '../common/DataViewFilterToolbar';
import { useDataViewFilter } from '../hooks/useDataViewFilter';

const MyList: FC = () => {
 const [data, loaded, loadError] = useK8sWatchResource<MyKind[]>({ ... });

 // Define checkbox filters with per-option counts
 const checkboxFilters = useMemo<CheckboxFilterConfig[]>(() => {
   const statusCounts: Record<string, number> = {};
   (data || []).forEach((obj) => {
     const status = getStatus(obj);
     statusCounts[status] = (statusCounts[status] || 0) + 1;
   });
   return [{
     id: 'status',
     title: t('Status'),
     placeholder: t('Filter by status'),
     options: [
       { value: 'Succeeded', label: t('Succeeded'), count: statusCounts['Succeeded'] || 0 },
       { value: 'Failed', label: t('Failed'), count: statusCounts['Failed'] || 0 },
     ],
   }];
 }, [data, t]);

 const getName = useCallback((obj: MyKind) => obj.metadata?.name || '', []);
 const getLabels = useCallback((obj: MyKind) => obj.metadata?.labels, []);

 const matchesCheckboxFilter = useCallback(
   (obj: MyKind, filterId: string, selectedValues: string[]) => {
     if (filterId === 'status') {
       return selectedValues.includes(getStatus(obj));
     }
     return true;
   },
   [],
 );

 const { filterValues, onFilterChange, onClearAll, filteredData } =
   useDataViewFilter<MyKind>({
     data: data || [],
     getName,
     getLabels,
     checkboxFilters,
     matchesCheckboxFilter,
   });

 return (
   <ListPageBody>
     <DataViewFilterToolbar
       filterValues={filterValues}
       onFilterChange={onFilterChange}
       onClearAll={onClearAll}
       checkboxFilters={checkboxFilters}
     />
     <ConsoleDataView
       data={filteredData}
       loaded={loaded}
       loadError={loadError}
       columns={columns}
       getDataViewRows={getDataViewRows}
       hideNameLabelFilters
     />
   </ListPageBody>
 );
};

Key points:

  • Pass hideNameLabelFilters to ConsoleDataView to avoid duplicate filter UI
  • Use hideLabelFilter or hideNameFilter on DataViewFilterToolbar to selectively hide filters
  • FilterOption.count is mandatory; totalCount is optional (for server-side pagination, renders as count/totalCount)

Test plan

  • Verify DataViewFilterToolbar renders the category selector dropdown when multiple filters are configured
  • Verify checkbox filters show options with count badges, toggle selection, and filter data
  • Verify "Clear filters" resets all filter state
  • Verify no layout shift when opening/closing filter dropdowns
  • Verify the toolbar integrates correctly with ConsoleDataView (data is pre-filtered, hideNameLabelFilters is set)

Screenshots / Screen Recordings

custom_filter.mov

Co-authored and Assisted By

  • Claude

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci openshift-ci bot added the approved Label for Approved PRs label Mar 20, 2026
@arvindk-softwaredev
Copy link
Copy Markdown
Contributor

ci/prow/frontend job is failing, please update the translation files for i18n

@anwesha-palit-redhat
Copy link
Copy Markdown
Contributor Author

ci/prow/frontend job is failing, please update the translation files for i18n

updated and passing now

Copy link
Copy Markdown
Contributor

@arvindk-softwaredev arvindk-softwaredev left a comment

Choose a reason for hiding this comment

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

/lgtm

@openshift-ci openshift-ci bot added the lgtm Looks Good to Me Label label Mar 23, 2026
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci bot commented Mar 23, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: anwesha-palit-redhat, arvindk-softwaredev

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:
  • OWNERS [anwesha-palit-redhat]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-merge-bot openshift-merge-bot bot merged commit c64fba5 into openshift-pipelines:master Mar 23, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Label for Approved PRs jira/valid-reference lgtm Looks Good to Me Label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants