-
Notifications
You must be signed in to change notification settings - Fork 49
Filter Bar - new component #3386
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
a13589e to
2216998
Compare
📦 RC Packages PublishedLatest commit: aaa30cc Published 1 packages@hashicorp/design-system-components@5.1.0-rc-20251125211530 |
6078967 to
3b7d553
Compare
fcce535 to
64c48b9
Compare
There was a problem hiding this 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 introduces a new FilterBar component to the design system library and integrates it as a contextual component within the AdvancedTable. The FilterBar supports multiple filter types (single-select, multi-select, numerical, date, datetime, time, and generic) and provides both immediate and deferred filtering modes. The AdvancedTable gains new filtering capabilities through an :actions named block and an :emptyState named block for displaying content when no data is available.
Key Changes
- Added new
FilterBarcomponent with support for multiple filter types and live filtering - Integrated
FilterBaras a contextual component inAdvancedTablevia new:actionsnamed block - Added
isEmptyargument and:emptyStatenamed block toAdvancedTablefor empty state handling
Reviewed changes
Copilot reviewed 69 out of 69 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/components/src/components/hds/filter-bar/index.ts | Main FilterBar component logic with filter management and state handling |
| packages/components/src/components/hds/filter-bar/types.ts | TypeScript type definitions for all filter types and data structures |
| packages/components/src/styles/components/filter-bar.scss | SCSS styles for FilterBar component and subcomponents |
| packages/components/src/components/hds/filter-bar/tabs/index.ts | Tabs component for organizing filter groups |
| packages/components/src/components/hds/filter-bar/filter-group/index.ts | FilterGroup component managing individual filter selections |
| packages/components/src/components/hds/filter-bar/dropdown.ts | Dropdown wrapper for filter selection UI |
| packages/components/src/components/hds/advanced-table/index.ts | Updated AdvancedTable to support FilterBar integration and empty state |
| packages/components/src/styles/components/advanced-table.scss | Styles for FilterBar integration and empty state in AdvancedTable |
| showcase/app/mocks/run-data.ts | Mock data for FilterBar demos and testing |
| showcase/app/components/page-components/filter-bar/index.gts | Showcase page structure for FilterBar component |
| showcase/app/components/mock/app/main/generic-advanced-table.gts | Updated demo with FilterBar integration and filtering logic |
| packages/components/translations/hds/components/filter-bar/en-us.yaml | Translation strings for FilterBar component |
| packages/components/src/components.ts | Export statements for new FilterBar components |
| packages/components/src/template-registry.ts | Template registry entries for FilterBar components |
| private _dropdownToggleElemenet!: HTMLDivElement; | ||
| private _appliedFiltersButtonId = 'applied-filters-button-' + guidFor(this); | ||
| private _appliedFiltersContentId = 'applied-filters-content-' + guidFor(this); | ||
|
|
||
| private _setUpFilterBar = modifier((element: HTMLDivElement) => { | ||
| this._dropdownToggleElemenet = element.querySelector( |
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Corrected spelling of 'Elemenet' to 'Element'.
| private _dropdownToggleElemenet!: HTMLDivElement; | |
| private _appliedFiltersButtonId = 'applied-filters-button-' + guidFor(this); | |
| private _appliedFiltersContentId = 'applied-filters-content-' + guidFor(this); | |
| private _setUpFilterBar = modifier((element: HTMLDivElement) => { | |
| this._dropdownToggleElemenet = element.querySelector( | |
| private _dropdownToggleElement!: HTMLDivElement; | |
| private _appliedFiltersButtonId = 'applied-filters-button-' + guidFor(this); | |
| private _appliedFiltersContentId = 'applied-filters-content-' + guidFor(this); | |
| private _setUpFilterBar = modifier((element: HTMLDivElement) => { | |
| this._dropdownToggleElement = element.querySelector( |
| <div class="hds-filter-bar__filter-group__search"> | ||
| <Hds::Form::TextInput::Base | ||
| @type="search" | ||
| placeholder={{hds-t "components.filter-bar.filter-group.search-input-placeholder" default="Search"}} |
Copilot
AI
Dec 8, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The translation key is inconsistent with other translation keys in the FilterBar component. It should use the 'hds.components' prefix like other keys (e.g., 'hds.components.filter-bar.filter-group.search-input-placeholder').
| placeholder={{hds-t "components.filter-bar.filter-group.search-input-placeholder" default="Search"}} | |
| placeholder={{hds-t "hds.components.filter-bar.filter-group.search-input-placeholder" default="Search"}} |
zamoore
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a bunch of early feedback on the components. Still need to review the docs, styles, and do some QA testing.
Looking really nice!
| <div class="hds-advanced-table__actions-container-wrapper"> | ||
| {{#if (has-block "actions")}} | ||
| <div class="hds-advanced-table__actions"> | ||
| {{yield (hash FilterBar=(component "hds/filter-bar")) to="actions"}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[question] Is this actions block used in case we plan to add more yielded components or are we letting users add whatever they'd like here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was mainly used for organization since the rest of the component uses named blocks for other sections. It could also open up using more things inside there in the future, but they would probably be other contextual components we control. No need to allow people to add anything in.
Originally I did just have the FilterBar as a default contextual component, but because of the other named blocks a template like this doesn't work.
<HdsAdvancedTable as |T|>
<T.FilterBar />
<:body>
...
</:body>
<HdsAdvancedTable>
The consumer has to use a <:default> named block, which isn't a pattern we've used in any other components.
<HdsAdvancedTable>
<:default as |D|>
<D.FilterBar />
<:/default>
<:body>
...
</:body>
<HdsAdvancedTable>
I'm good with using a :default block if we want to go that way. This was more so just an organization and consistency choice.
| {{/if}} | ||
| {{/unless}} | ||
|
|
||
| {{#if this.isEmpty}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[question] Could this be an if/else with the condition above it?
| if (isEmpty !== undefined) { | ||
| return isEmpty; | ||
| } | ||
| return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit]
I think return isEmpty ?? false my be a bit faster to read, but that's just a style thing.
| HdsFilterBarData, | ||
| HdsFilterBarGenericFilterData, | ||
| } from './types.ts'; | ||
| // import HdsDropdown from '../dropdown/index.ts'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dead code?
|
|
||
| @tracked _isExpanded: boolean = this.hasActiveFilters; | ||
|
|
||
| private _dropdownToggleElemenet!: HTMLDivElement; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| private _dropdownToggleElemenet!: HTMLDivElement; | |
| private _dropdownToggleElement!: HTMLDivElement; |
| if (this.nodeIndex !== undefined && typeof onClick === 'function') { | ||
| onClick(event, this.nodeIndex); | ||
| } else { | ||
| return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we could remove the else block unless you need to return false here.
|
|
||
| export default class HdsFilterBarTabsPanel extends Component<HdsFilterBarTabsPanelSignature> { | ||
| private _panelId = 'panel-' + guidFor(this); | ||
| private _elementId?: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[question] Is this used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No think it can be removed
| this.internalFilters = newFilter; | ||
| } else { | ||
| if (Array.isArray(this.internalFilters)) { | ||
| this.internalFilters.push(newFilter); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] Prefer non-mutative vhanges
| this.internalFilters.push(newFilter); | |
| this.internalFilters = [...this.internalFilters, newFilter]; |
| } | ||
|
|
||
| private onSearch = (event: Event) => { | ||
| const listItems = this._panelElement.querySelectorAll( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[question] - Can we use the modifier registration pattern here rather than query for elements?
| const text = item.textContent.toLowerCase(); | ||
| const searchText = input.value.toLowerCase(); | ||
| if (text.includes(searchText)) { | ||
| item.classList.remove( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way we can do this idiomatically in the template?
📌 Summary
If merged, this PR would add a new
FilterBarcomponent to the library, and aFilterBarcontextual component to theAdvancedTable.Related spike PR: #3336
Preview links
🛠️ Detailed description
This PR adds support for filtering data through the new
FilterBarcomponent, and adds this as a contextual component to theAdvancedTable.FilterBar
The
FilterBaris a new component to the component library, which supports providing both a set of filter options through a dropdown with various contextual components, and tracking applied filters through thefiltersargument.Filter types
The
filtersargument is used to track the various filters currently applied to a data set. A consumer will pass an object with various filters to this argument, and all filters will be displayed as dismissible tags.The format of the object for the
filtersargument also matches the object that is bubbled up to the user when any filters are applied via theonFilterargument.The following filter types are supported:
The format of the object for the applied filters is as follows:
Filter contextual components
The options available for a user to select in the filters dropdown are provided via contextual
DropdownandFilterGroupcomponents.AdvancedTable
In the
AdvancedTablefiltering support has been added through a new named block:actionsand contextual componentFilterBarused inside that block, which uses the newFilterBarcomponent.Also a new named argument
isEmptyand named block:emptyStatehave been added to support showing a block of content when no data is available. WhenisEmptyistrue, the table body is not rendered, and the content inside the:emptyStateis shown.📸 Screenshots
Filter Bar in AdvancedTable

AdvancedTable empty state

🔗 External links
Jira ticket: HDS-4591
Figma file: File
👀 Component checklist
💬 Please consider using conventional comments when reviewing this PR.
📋 PCI review checklist
Examples of changes to controls include access controls, encryption, logging, etc.
Examples include changes to operating systems, ports, protocols, services, cryptography-related components, PII processing code, etc.