From 383efc8025ad55a02c7d8113ddf5c71b8a3812f4 Mon Sep 17 00:00:00 2001 From: Dylan Hyun Date: Fri, 5 Sep 2025 10:42:13 -0400 Subject: [PATCH 01/19] SPIKE Adv table filter bar filtering --- packages/components/package.json | 6 + .../advanced-table/filter-bar/checkbox.hbs | 5 + .../hds/advanced-table/filter-bar/checkbox.ts | 47 +++ .../advanced-table/filter-bar/dropdown.hbs | 36 ++ .../hds/advanced-table/filter-bar/dropdown.ts | 183 +++++++++ .../filter-bar/filters-checkbox.hbs | 5 + .../filter-bar/filters-checkbox.ts | 40 ++ .../filter-bar/filters-dropdown.hbs | 20 + .../filter-bar/filters-dropdown.ts | 88 +++++ .../hds/advanced-table/filter-bar/index.hbs | 37 ++ .../hds/advanced-table/filter-bar/index.ts | 118 ++++++ .../hds/advanced-table/filter-bar/radio.hbs | 5 + .../hds/advanced-table/filter-bar/radio.ts | 47 +++ .../components/hds/advanced-table/index.hbs | 6 + .../components/hds/advanced-table/index.ts | 60 +++ .../hds/advanced-table/models/column.ts | 6 + .../components/hds/advanced-table/types.ts | 23 ++ .../src/styles/components/advanced-table.scss | 84 ++++- packages/components/src/template-registry.ts | 18 + .../mock/app/main/generic-advanced-table.gts | 350 ++++++++++++------ 20 files changed, 1064 insertions(+), 120 deletions(-) create mode 100644 packages/components/src/components/hds/advanced-table/filter-bar/checkbox.hbs create mode 100644 packages/components/src/components/hds/advanced-table/filter-bar/checkbox.ts create mode 100644 packages/components/src/components/hds/advanced-table/filter-bar/dropdown.hbs create mode 100644 packages/components/src/components/hds/advanced-table/filter-bar/dropdown.ts create mode 100644 packages/components/src/components/hds/advanced-table/filter-bar/filters-checkbox.hbs create mode 100644 packages/components/src/components/hds/advanced-table/filter-bar/filters-checkbox.ts create mode 100644 packages/components/src/components/hds/advanced-table/filter-bar/filters-dropdown.hbs create mode 100644 packages/components/src/components/hds/advanced-table/filter-bar/filters-dropdown.ts create mode 100644 packages/components/src/components/hds/advanced-table/filter-bar/index.hbs create mode 100644 packages/components/src/components/hds/advanced-table/filter-bar/index.ts create mode 100644 packages/components/src/components/hds/advanced-table/filter-bar/radio.hbs create mode 100644 packages/components/src/components/hds/advanced-table/filter-bar/radio.ts diff --git a/packages/components/package.json b/packages/components/package.json index 6081e4dacc7..db2d112c939 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -144,6 +144,12 @@ "./components/hds/accordion/item/button.js": "./dist/_app_/components/hds/accordion/item/button.js", "./components/hds/accordion/item.js": "./dist/_app_/components/hds/accordion/item.js", "./components/hds/advanced-table/expandable-tr-group.js": "./dist/_app_/components/hds/advanced-table/expandable-tr-group.js", + "./components/hds/advanced-table/filter-bar/checkbox.js": "./dist/_app_/components/hds/advanced-table/filter-bar/checkbox.js", + "./components/hds/advanced-table/filter-bar/dropdown.js": "./dist/_app_/components/hds/advanced-table/filter-bar/dropdown.js", + "./components/hds/advanced-table/filter-bar/filters-checkbox.js": "./dist/_app_/components/hds/advanced-table/filter-bar/filters-checkbox.js", + "./components/hds/advanced-table/filter-bar/filters-dropdown.js": "./dist/_app_/components/hds/advanced-table/filter-bar/filters-dropdown.js", + "./components/hds/advanced-table/filter-bar.js": "./dist/_app_/components/hds/advanced-table/filter-bar.js", + "./components/hds/advanced-table/filter-bar/radio.js": "./dist/_app_/components/hds/advanced-table/filter-bar/radio.js", "./components/hds/advanced-table.js": "./dist/_app_/components/hds/advanced-table.js", "./components/hds/advanced-table/models/column.js": "./dist/_app_/components/hds/advanced-table/models/column.js", "./components/hds/advanced-table/models/row.js": "./dist/_app_/components/hds/advanced-table/models/row.js", diff --git a/packages/components/src/components/hds/advanced-table/filter-bar/checkbox.hbs b/packages/components/src/components/hds/advanced-table/filter-bar/checkbox.hbs new file mode 100644 index 00000000000..6d2e3ecd56f --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/filter-bar/checkbox.hbs @@ -0,0 +1,5 @@ +{{#let @checkbox as |Checkbox|}} + + {{yield}} + +{{/let}} \ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/filter-bar/checkbox.ts b/packages/components/src/components/hds/advanced-table/filter-bar/checkbox.ts new file mode 100644 index 00000000000..dae7c7fbdd3 --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/filter-bar/checkbox.ts @@ -0,0 +1,47 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import type { WithBoundArgs } from '@glint/template'; + +import type { HdsAdvancedTableFilter } from '../types.ts'; + +import HdsDropdownListItemCheckbox from '../../dropdown/list-item/checkbox.ts'; + +import type { HdsDropdownSignature } from '../../dropdown/index.ts'; + +export interface HdsAdvancedTableFilterBarCheckboxSignature { + Args: HdsDropdownSignature['Args'] & { + checkbox?: WithBoundArgs; + value?: string; + keyFilter: HdsAdvancedTableFilter[] | HdsAdvancedTableFilter | undefined; + onChange?: (event: Event) => void; + }; + Blocks: { + default: []; + }; + Element: HTMLDivElement; +} + +export default class HdsAdvancedTableFilterBarCheckbox extends Component { + @action + onChange(event: Event): void { + const { onChange } = this.args; + if (onChange && typeof onChange === 'function') { + onChange(event); + } + } + + get isChecked(): boolean { + const { keyFilter, value } = this.args; + if (Array.isArray(keyFilter)) { + return keyFilter.some((filter) => filter.value === value); + } else if (keyFilter && value) { + return keyFilter.value === value; + } + return false; + } +} diff --git a/packages/components/src/components/hds/advanced-table/filter-bar/dropdown.hbs b/packages/components/src/components/hds/advanced-table/filter-bar/dropdown.hbs new file mode 100644 index 00000000000..f173fe8fe8c --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/filter-bar/dropdown.hbs @@ -0,0 +1,36 @@ +{{! @glint-nocheck }} + + {{yield + (hash + ToggleButton=(component D.ToggleButton count=this.numFilters text=@text color="secondary" size="small") + Checkbox=(component + "hds/advanced-table/filter-bar/checkbox" + checkbox=D.Checkbox + keyFilter=this.internalFilters + onChange=this.onChange + ) + Radio=(component + "hds/advanced-table/filter-bar/radio" radio=D.Radio keyFilter=this.internalFilters onChange=this.onChange + ) + ) + }} + {{#unless @isLiveFilter}} + + + + + + + {{/unless}} + \ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/filter-bar/dropdown.ts b/packages/components/src/components/hds/advanced-table/filter-bar/dropdown.ts new file mode 100644 index 00000000000..e208e23c045 --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/filter-bar/dropdown.ts @@ -0,0 +1,183 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; +import { modifier } from 'ember-modifier'; +import type { WithBoundArgs } from '@glint/template'; + +import HdsDropdown from '../../dropdown/index.ts'; +import HdsDropdownToggleButton from '../../dropdown/toggle/button.ts'; + +import HdsAdvancedTableFilterBarCheckbox from './checkbox.ts'; +import HdsAdvancedTableFilterBarRadio from './radio.ts'; + +import type { + HdsAdvancedTableFilter, + HdsAdvancedTableFilters, +} from '../types.ts'; +import type { HdsDropdownSignature } from '../../dropdown/index.ts'; + +export interface HdsAdvancedTableFilterBarDropdownSignature { + Args: HdsDropdownSignature['Args'] & { + dropdown?: WithBoundArgs; + key: string; + filters: HdsAdvancedTableFilters; + isMultiSelect?: boolean; + isLiveFilter?: boolean; + activeFilterableColumns?: string[]; + onChange: (key: string, keyFilter?: HdsAdvancedTableFilter[]) => void; + }; + Blocks: { + default: [ + { + ToggleButton?: WithBoundArgs< + typeof HdsDropdownToggleButton, + 'color' | 'text' | 'size' + >; + Checkbox?: WithBoundArgs< + typeof HdsAdvancedTableFilterBarCheckbox, + 'checkbox' | 'keyFilter' | 'onChange' + >; + Radio?: WithBoundArgs< + typeof HdsAdvancedTableFilterBarRadio, + 'radio' | 'keyFilter' | 'onChange' + >; + }, + ]; + }; + Element: HTMLDivElement; +} + +export default class HdsAdvancedTableFilterBarDropdown extends Component< + HdsDropdownSignature & HdsAdvancedTableFilterBarDropdownSignature +> { + @tracked internalFilters: HdsAdvancedTableFilter[] | undefined = []; + + private _updateInternalFilters = modifier(() => { + if (this.keyFilter) { + this.internalFilters = JSON.parse( + JSON.stringify(this.keyFilter) + ) as HdsAdvancedTableFilter[]; + } else { + this.internalFilters = []; + } + }); + + get keyFilter(): HdsAdvancedTableFilter[] | undefined { + const { filters, key } = this.args; + + if (!filters) { + return undefined; + } + return filters[key]; + } + + get numFilters(): number { + if (Array.isArray(this.keyFilter)) { + return this.keyFilter.length; + } + return 0; + } + + @action + onChange(event: Event): void { + const addFilter = (value: unknown): HdsAdvancedTableFilter[] => { + const newFilter = { + text: value as string, + value: value, + }; + if ( + Array.isArray(this.internalFilters) && + input.classList.contains('hds-form-checkbox') + ) { + this.internalFilters.push(newFilter); + return this.internalFilters; + } else { + return [newFilter]; + } + }; + + const removeFilter = (value: string): HdsAdvancedTableFilter[] => { + const newFilter = [] as HdsAdvancedTableFilter[]; + if (Array.isArray(this.internalFilters)) { + this.internalFilters.forEach((filter) => { + if (filter.value != value) { + newFilter.push(filter); + } + }); + } + return newFilter; + }; + + const input = event.target as HTMLInputElement; + + let newFilter = [] as HdsAdvancedTableFilter[]; + + if (input.checked) { + newFilter = addFilter(input.value); + } else { + newFilter = removeFilter(input.value); + } + + this.internalFilters = newFilter; + + if (this.args.isLiveFilter) { + const { onChange } = this.args; + if (onChange && typeof onChange === 'function') { + if (newFilter.length === 0) { + onChange(this.args.key, undefined); + } else { + onChange(this.args.key, newFilter); + } + } + } + } + + @action + onApply(closeDropdown?: () => void): void { + const { onChange } = this.args; + if (onChange && typeof onChange === 'function') { + onChange(this.args.key, this.internalFilters); + } + + if (closeDropdown && typeof closeDropdown === 'function') { + closeDropdown(); + } + } + + @action + onClear(closeDropdown?: () => void): void { + this.internalFilters = []; + + const { onChange } = this.args; + if (onChange && typeof onChange === 'function') { + onChange(this.args.key, this.internalFilters); + } + + if (closeDropdown && typeof closeDropdown === 'function') { + closeDropdown(); + } + } + + get classNames(): string { + const classes = ['hds-advanced-table__filter-bar__dropdown']; + + // add a class based on the @align argument + if (!this._isActiveFilterableColumn()) { + classes.push('hds-advanced-table__filter-bar__dropdown--hidden'); + } + + return classes.join(' '); + } + + private _isActiveFilterableColumn(): boolean { + if (this.args.activeFilterableColumns) { + return this.args.activeFilterableColumns.includes(this.args.key); + } + return false; + } +} diff --git a/packages/components/src/components/hds/advanced-table/filter-bar/filters-checkbox.hbs b/packages/components/src/components/hds/advanced-table/filter-bar/filters-checkbox.hbs new file mode 100644 index 00000000000..6d2e3ecd56f --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/filter-bar/filters-checkbox.hbs @@ -0,0 +1,5 @@ +{{#let @checkbox as |Checkbox|}} + + {{yield}} + +{{/let}} \ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/filter-bar/filters-checkbox.ts b/packages/components/src/components/hds/advanced-table/filter-bar/filters-checkbox.ts new file mode 100644 index 00000000000..99d254d9021 --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/filter-bar/filters-checkbox.ts @@ -0,0 +1,40 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import type { WithBoundArgs } from '@glint/template'; + +import HdsDropdownListItemCheckbox from '../../dropdown/list-item/checkbox.ts'; + +import type { HdsDropdownSignature } from '../../dropdown/index.ts'; + +export interface HdsAdvancedTableFilterBarFiltersCheckboxSignature { + Args: HdsDropdownSignature['Args'] & { + checkbox?: WithBoundArgs; + value?: string; + activeFilterableColumns?: string[]; + onChange?: (event: Event) => void; + }; + Blocks: { + default: []; + }; + Element: HTMLDivElement; +} + +export default class HdsAdvancedTableFilterBarFiltersCheckbox extends Component { + @action + onChange(event: Event): void { + const { onChange } = this.args; + if (onChange && typeof onChange === 'function') { + onChange(event); + } + } + + get isChecked(): boolean { + const { value, activeFilterableColumns } = this.args; + return activeFilterableColumns?.includes(value || '') || false; + } +} diff --git a/packages/components/src/components/hds/advanced-table/filter-bar/filters-dropdown.hbs b/packages/components/src/components/hds/advanced-table/filter-bar/filters-dropdown.hbs new file mode 100644 index 00000000000..24daba4278c --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/filter-bar/filters-dropdown.hbs @@ -0,0 +1,20 @@ +{{! @glint-nocheck }} + + + {{yield + (hash + Checkbox=(component + "hds/advanced-table/filter-bar/filters-checkbox" + checkbox=D.Checkbox + activeFilterableColumns=this.activeFilterableColumns + onChange=this.onChange + ) + ) + }} + + + + + + + \ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/filter-bar/filters-dropdown.ts b/packages/components/src/components/hds/advanced-table/filter-bar/filters-dropdown.ts new file mode 100644 index 00000000000..62fcac7e583 --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/filter-bar/filters-dropdown.ts @@ -0,0 +1,88 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; +import type { WithBoundArgs } from '@glint/template'; + +import HdsDropdown from '../../dropdown/index.ts'; +import HdsAdvancedTableFilterBarFiltersCheckbox from './filters-checkbox.ts'; + +import type { HdsDropdownSignature } from '../../dropdown/index.ts'; + +export interface HdsAdvancedTableFilterBarFiltersDropdownSignature { + Args: HdsDropdownSignature['Args'] & { + dropdown?: WithBoundArgs; + activeFilterableColumns?: string[]; + onChange: (filterableColumns: string[]) => void; + }; + Blocks: { + default: [ + { + Checkbox?: WithBoundArgs< + typeof HdsAdvancedTableFilterBarFiltersCheckbox, + 'checkbox' | 'onChange' | 'activeFilterableColumns' + >; + }, + ]; + }; + Element: HTMLDivElement; +} + +export default class HdsAdvancedTableFilterBarFiltersDropdown extends Component< + HdsDropdownSignature & HdsAdvancedTableFilterBarFiltersDropdownSignature +> { + @tracked activeFilterableColumns: string[] = + this.args.activeFilterableColumns ?? []; + + @action + onChange(event: Event): void { + const input = event.target as HTMLInputElement; + + if (input.checked) { + this.activeFilterableColumns = [ + ...this.activeFilterableColumns, + input.value, + ]; + } else { + this.activeFilterableColumns = this.activeFilterableColumns?.filter( + (col) => col !== input.value + ); + } + } + + @action + onApply(closeDropdown?: () => void): void { + const { onChange } = this.args; + if (onChange && typeof onChange === 'function') { + onChange(this.activeFilterableColumns); + } + + if (closeDropdown && typeof closeDropdown === 'function') { + closeDropdown(); + } + } + + @action + onClear(closeDropdown?: () => void): void { + this.activeFilterableColumns = []; + + const { onChange } = this.args; + if (onChange && typeof onChange === 'function') { + onChange(this.activeFilterableColumns); + } + + if (closeDropdown && typeof closeDropdown === 'function') { + closeDropdown(); + } + } + + get classNames(): string { + const classes = ['hds-advanced-table__filter-bar__filters-dropdown']; + + return classes.join(' '); + } +} diff --git a/packages/components/src/components/hds/advanced-table/filter-bar/index.hbs b/packages/components/src/components/hds/advanced-table/filter-bar/index.hbs new file mode 100644 index 00000000000..418c812c45d --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/filter-bar/index.hbs @@ -0,0 +1,37 @@ +
+
+ {{yield + (hash + FiltersDropdown=(component "hds/advanced-table/filter-bar/filters-dropdown" onChange=this.onFiltersChange) + Dropdown=(component + "hds/advanced-table/filter-bar/dropdown" + onChange=this.onFilter + filters=this.filters + isLiveFilter=@isLiveFilter + activeFilterableColumns=this.activeFilterableColumns + ) + ) + }} +
+ {{#if this.hasActiveFilters}} +
+
+ {{#each-in this.filters as |key filter|}} + {{#if filter}} + {{#each filter as |f|}} + + {{/each}} + {{/if}} + {{/each-in}} +
+ +
+ {{/if}} +
\ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/filter-bar/index.ts b/packages/components/src/components/hds/advanced-table/filter-bar/index.ts new file mode 100644 index 00000000000..e64634c6d91 --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/filter-bar/index.ts @@ -0,0 +1,118 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; + +import type { WithBoundArgs } from '@glint/template'; +import type Owner from '@ember/owner'; + +import type { + HdsAdvancedTableFilter, + HdsAdvancedTableFilters, +} from '../types.ts'; +import HdsAdvancedTableFilterBarDropdown from './dropdown.ts'; +import HdsAdvancedTableFilterBarFiltersDropdown from './filters-dropdown.ts'; + +export interface HdsAdvancedTableFilterBarSignature { + Args: { + filters: HdsAdvancedTableFilters; + activeFilterableColumns?: string[]; + isLiveFilter?: boolean; + onFilter?: (filters: HdsAdvancedTableFilters) => void; + }; + Blocks: { + default?: [ + { + FiltersDropdown?: WithBoundArgs< + typeof HdsAdvancedTableFilterBarFiltersDropdown, + 'onChange' + >; + Dropdown?: WithBoundArgs< + typeof HdsAdvancedTableFilterBarDropdown, + 'onChange' | 'filters' | 'isLiveFilter' + >; + }, + ]; + }; + Element: HTMLDivElement; +} +export default class HdsAdvancedTableFilterBar extends Component { + @tracked filters: HdsAdvancedTableFilters = {}; + @tracked hasActiveFilters: boolean = Object.keys(this.filters).length > 0; + @tracked activeFilterableColumns: string[] = []; + + constructor(owner: Owner, args: HdsAdvancedTableFilterBarSignature['Args']) { + super(owner, args); + + const { filters, activeFilterableColumns } = args; + + if (filters) { + this.filters = { ...filters }; + } + + if (activeFilterableColumns) { + this.activeFilterableColumns = [...activeFilterableColumns]; + } + } + + @action + onFilter(key: string, keyFilter?: HdsAdvancedTableFilter[]): void { + this._updateFilter(key, keyFilter); + + this.hasActiveFilters = Object.keys(this.filters).length > 0; + + const { onFilter } = this.args; + if (onFilter && typeof onFilter === 'function') { + onFilter(this.filters); + } + } + + @action + onFiltersChange(activeFilterableColumns: string[]): void { + this.activeFilterableColumns = activeFilterableColumns; + } + + @action + clearFilters(): void { + this.filters = {}; + this.hasActiveFilters = false; + const { onFilter } = this.args; + if (onFilter && typeof onFilter === 'function') { + onFilter(this.filters); + } + } + + private _updateFilter( + key: string, + keyFilter?: HdsAdvancedTableFilter[] + ): void { + const newFilters = {} as HdsAdvancedTableFilters; + Object.keys(this.filters).forEach((k) => { + newFilters[k] = JSON.parse( + JSON.stringify(this.filters[k]) + ) as HdsAdvancedTableFilter[]; + }); + if ( + keyFilter === undefined || + (Array.isArray(keyFilter) && keyFilter.length === 0) + ) { + delete newFilters[key]; + } else { + Object.assign(newFilters, { [key]: keyFilter }); + } + this.filters = { ...newFilters }; + } + + private onFilterDismiss = (key: string, filterValue: unknown): void => { + const oldFilter = this.filters[key]; + let newFilter: HdsAdvancedTableFilter[] = []; + if (Array.isArray(oldFilter)) { + newFilter = oldFilter.filter((filter) => filter.value !== filterValue); + } + this.onFilter(key, newFilter); + }; +} diff --git a/packages/components/src/components/hds/advanced-table/filter-bar/radio.hbs b/packages/components/src/components/hds/advanced-table/filter-bar/radio.hbs new file mode 100644 index 00000000000..3cadd013836 --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/filter-bar/radio.hbs @@ -0,0 +1,5 @@ +{{#let @radio as |Radio|}} + + {{yield}} + +{{/let}} \ No newline at end of file diff --git a/packages/components/src/components/hds/advanced-table/filter-bar/radio.ts b/packages/components/src/components/hds/advanced-table/filter-bar/radio.ts new file mode 100644 index 00000000000..af8d02ec2ba --- /dev/null +++ b/packages/components/src/components/hds/advanced-table/filter-bar/radio.ts @@ -0,0 +1,47 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import type { WithBoundArgs } from '@glint/template'; + +import type { HdsAdvancedTableFilter } from '../types.ts'; + +import HdsDropdownListItemRadio from '../../dropdown/list-item/radio.ts'; + +import type { HdsDropdownSignature } from '../../dropdown/index.ts'; + +export interface HdsAdvancedTableFilterBarRadioSignature { + Args: HdsDropdownSignature['Args'] & { + radio?: WithBoundArgs; + value?: string; + keyFilter: HdsAdvancedTableFilter[] | HdsAdvancedTableFilter | undefined; + onChange?: (event: Event) => void; + }; + Blocks: { + default: []; + }; + Element: HTMLDivElement; +} + +export default class HdsAdvancedTableFilterBarRadio extends Component { + @action + onChange(event: Event): void { + const { onChange } = this.args; + if (onChange && typeof onChange === 'function') { + onChange(event); + } + } + + get isChecked(): boolean { + const { keyFilter, value } = this.args; + if (Array.isArray(keyFilter)) { + return keyFilter.some((filter) => filter.value === value); + } else if (keyFilter && value) { + return keyFilter.value === value; + } + return false; + } +} diff --git a/packages/components/src/components/hds/advanced-table/index.hbs b/packages/components/src/components/hds/advanced-table/index.hbs index 45eb3875f77..4e49cfb74ee 100644 --- a/packages/components/src/components/hds/advanced-table/index.hbs +++ b/packages/components/src/components/hds/advanced-table/index.hbs @@ -3,6 +3,12 @@ SPDX-License-Identifier: MPL-2.0 }} +{{yield + (hash + FilterBar=(component "hds/advanced-table/filter-bar" filters=@filters onFilter=@onFilter isLiveFilter=@isLiveFilter) + ) + to="actions" +}}
void; onSelectionChange?: ( selection: HdsAdvancedTableOnSelectionChangeSignature ) => void; onSort?: (sortBy: string, sortOrder: HdsAdvancedTableThSortOrder) => void; + onFilter?: (filters: HdsAdvancedTableFilters) => void; }; Blocks: { + actions?: [ + { + FilterBar?: WithBoundArgs< + typeof HdsAdvancedTableFilterBar, + 'filters' | 'isLiveFilter' | 'onFilter' + >; + }, + ]; body?: [ { Td?: WithBoundArgs; @@ -222,6 +236,8 @@ export default class HdsAdvancedTable extends Component 0; } get identityKey(): string | undefined { @@ -700,6 +722,27 @@ export default class HdsAdvancedTable extends Component 0; + } } diff --git a/packages/components/src/components/hds/advanced-table/models/column.ts b/packages/components/src/components/hds/advanced-table/models/column.ts index c5c038874aa..48cf3ad2ecb 100644 --- a/packages/components/src/components/hds/advanced-table/models/column.ts +++ b/packages/components/src/components/hds/advanced-table/models/column.ts @@ -13,7 +13,9 @@ import type { HdsDropdownToggleButtonSignature } from '../../dropdown/toggle/but import type { HdsAdvancedTableCell, HdsAdvancedTableHorizontalAlignment, + HdsAdvancedTableFilterOption, HdsAdvancedTableColumn as HdsAdvancedTableColumnType, + HdsAdvancedTableFilterType, } from '../types'; export const DEFAULT_WIDTH = '1fr'; // default to '1fr' to allow flexible width @@ -51,6 +53,8 @@ export default class HdsAdvancedTableColumn { @tracked thContextMenuToggleElement?: HdsDropdownToggleButtonSignature['Element'] = undefined; + @tracked filterOptions?: HdsAdvancedTableFilterOption[] = undefined; + @tracked filterType?: HdsAdvancedTableFilterType = undefined; // width properties @tracked transientWidth?: `${number}px` = undefined; // used for transient width changes @@ -165,6 +169,8 @@ export default class HdsAdvancedTableColumn { this.tooltip = column.tooltip; this._setWidthValues(column); this.sortingFunction = column.sortingFunction; + this.filterOptions = column.filterOptions; + this.filterType = column.filterType; } // main collection function diff --git a/packages/components/src/components/hds/advanced-table/types.ts b/packages/components/src/components/hds/advanced-table/types.ts index 9eee584519b..342cb819064 100644 --- a/packages/components/src/components/hds/advanced-table/types.ts +++ b/packages/components/src/components/hds/advanced-table/types.ts @@ -74,6 +74,27 @@ export type HdsAdvancedTableSelectableRow = { export type HdsAdvancedTableExpandState = boolean; +export type HdsAdvancedTableFilterOption = { + value: string; + label: string; +}; + +export interface HdsAdvancedTableFilter { + text: string; + value: unknown; +} + +export interface HdsAdvancedTableFilters { + [name: string]: HdsAdvancedTableFilter[] | undefined; +} + +export enum HdsAdvancedTableFilterTypeValues { + Checkbox = 'checkbox', + Radio = 'radio', +} + +export type HdsAdvancedTableFilterType = `${HdsAdvancedTableFilterTypeValues}`; + interface BaseHdsAdvancedTableColumn { align?: HdsAdvancedTableHorizontalAlignment; isVisuallyHidden?: boolean; @@ -83,6 +104,8 @@ interface BaseHdsAdvancedTableColumn { width?: string; minWidth?: `${number}px`; maxWidth?: `${number}px`; + filterOptions?: HdsAdvancedTableFilterOption[]; + filterType?: HdsAdvancedTableFilterType; } interface SortableHdsAdvancedTableColumn extends BaseHdsAdvancedTableColumn { diff --git a/packages/components/src/styles/components/advanced-table.scss b/packages/components/src/styles/components/advanced-table.scss index 75936fadddd..f8622b2b71b 100644 --- a/packages/components/src/styles/components/advanced-table.scss +++ b/packages/components/src/styles/components/advanced-table.scss @@ -415,7 +415,8 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); } } -.hds-advanced-table__th-context-menu .hds-dropdown-toggle-icon { +.hds-advanced-table__th-context-menu .hds-dropdown-toggle-icon, +.hds-advanced-table__th-filter-menu .hds-dropdown-toggle-icon { width: $hds-advanced-table-button-size; height: $hds-advanced-table-button-size; margin: -2px 0; @@ -504,6 +505,25 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); align-self: flex-start; } +.hds-advanced-table__th-filter-menu--active { + position: relative; + + &::before { + position: absolute; + top: -4px; + right: -4px; + width: 6px; + height: 6px; + background-color: var(--token-color-foreground-action); + border-radius: 50%; + content: ""; + } +} + +.hds-advanced-table__clear-filters-button { + margin-bottom: 16px; +} + // ---------------------------------------------------------------- // TABLE BODY @@ -745,3 +765,65 @@ $hds-advanced-table-drag-preview-background-color: rgba(204, 227, 254, 30%); border-radius: var(--token-border-radius-medium); box-shadow: var(--token-elevation-mid-box-shadow); } + +// ---------------------------------------------------------------- + +// FILTER BAR +.hds-advanced-table__filter-bar { + display: grid; + gap: 8px; + padding: 8px; + background-color: var(--token-color-surface-faint); + border: 1px solid var(--token-color-border-primary); + border-bottom: none; + border-radius: $hds-advanced-table-border-radius $hds-advanced-table-border-radius 0 0; + + .hds-advanced-table__filter-bar__filters { + display: flex; + gap: 12px 12px; + align-items: end; + + .hds-form-text-input { + width: auto; + } + } + + .hds-advanced-table__filter-bar__actions { + display: flex; + flex-direction: row; + gap: 8px; + align-items: end; + justify-content: space-between; + } + + .hds-advanced-table__filter-bar__active-filters { + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 8px; + align-items: center; + } +} + +.hds-advanced-table__filter-bar + .hds-advanced-table__container { + border-top-left-radius: 0; + border-top-right-radius: 0; + + .hds-advanced-table__thead .hds-advanced-table__tr:first-of-type .hds-advanced-table__th { + &:first-child { + border-top-left-radius: 0; + } + + &:last-child { + border-top-right-radius: 0; + } + } +} + +.hds-advanced-table__filter-bar__dropdown { + display: block; + + &--hidden { + display: none; + } +} diff --git a/packages/components/src/template-registry.ts b/packages/components/src/template-registry.ts index 4dee43a90a6..c6b0e5ebba3 100644 --- a/packages/components/src/template-registry.ts +++ b/packages/components/src/template-registry.ts @@ -21,6 +21,12 @@ import type HdsAdvancedTableThSortComponent from './components/hds/advanced-tabl import type HdsAdvancedTableThSelectableComponent from './components/hds/advanced-table/th-selectable'; import type HdsAdvancedTableTrComponent from './components/hds/advanced-table/tr'; import type HdsAdvancedTableExpandableTrGroupComponent from './components/hds/advanced-table/expandable-tr-group.ts'; +import type HdsAdvancedTableFilterBarComponent from './components/hds/advanced-table/filter-bar'; +import type HdsAdvancedTableFilterBarDropdownComponent from './components/hds/advanced-table/filter-bar/dropdown'; +import type HdsAdvancedTableFilterBarCheckboxComponent from './components/hds/advanced-table/filter-bar/checkbox'; +import type HdsAdvancedTableFilterBarRadioComponent from './components/hds/advanced-table/filter-bar/radio'; +import type HdsAdvancedTableFilterBarFiltersDropdownComponent from './components/hds/advanced-table/filter-bar/filters-dropdown'; +import type HdsAdvancedTableFilterBarFiltersCheckboxComponent from './components/hds/advanced-table/filter-bar/filters-checkbox'; import type HdsAlertComponent from './components/hds/alert'; import type HdsAlertDescriptionComponent from './components/hds/alert/description'; import type HdsAlertTitleComponent from './components/hds/alert/title'; @@ -285,6 +291,18 @@ export default interface HdsComponentsRegistry { 'hds/advanced-table/th-selectable': typeof HdsAdvancedTableThSelectableComponent; 'Hds::AdvancedTable::ExpandableTrGroup': typeof HdsAdvancedTableExpandableTrGroupComponent; 'hds/advanced-table/expandable-tr-group': typeof HdsAdvancedTableExpandableTrGroupComponent; + 'Hds::AdvancedTable::FilterBar': typeof HdsAdvancedTableFilterBarComponent; + 'hds/advanced-table/filter-bar': typeof HdsAdvancedTableFilterBarComponent; + 'Hds::AdvancedTable::FilterBar::Dropdown': typeof HdsAdvancedTableFilterBarDropdownComponent; + 'hds/advanced-table/filter-bar/dropdown': typeof HdsAdvancedTableFilterBarDropdownComponent; + 'Hds::AdvancedTable::FilterBar::Checkbox': typeof HdsAdvancedTableFilterBarCheckboxComponent; + 'hds/advanced-table/filter-bar/checkbox': typeof HdsAdvancedTableFilterBarCheckboxComponent; + 'Hds::AdvancedTable::FilterBar::Radio': typeof HdsAdvancedTableFilterBarRadioComponent; + 'hds/advanced-table/filter-bar/radio': typeof HdsAdvancedTableFilterBarRadioComponent; + 'Hds::AdvancedTable::FilterBar::FiltersDropdown': typeof HdsAdvancedTableFilterBarFiltersDropdownComponent; + 'hds/advanced-table/filter-bar/filters-dropdown': typeof HdsAdvancedTableFilterBarFiltersDropdownComponent; + 'Hds::AdvancedTable::FilterBar::FiltersCheckbox': typeof HdsAdvancedTableFilterBarFiltersCheckboxComponent; + 'hds/advanced-table/filter-bar/filters-checkbox': typeof HdsAdvancedTableFilterBarFiltersCheckboxComponent; // Alert 'Hds::Alert': typeof HdsAlertComponent; diff --git a/showcase/app/components/mock/app/main/generic-advanced-table.gts b/showcase/app/components/mock/app/main/generic-advanced-table.gts index c238ae7606e..401c534ea75 100644 --- a/showcase/app/components/mock/app/main/generic-advanced-table.gts +++ b/showcase/app/components/mock/app/main/generic-advanced-table.gts @@ -4,8 +4,11 @@ */ import Component from '@glimmer/component'; import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; import { deepTracked } from 'ember-deep-tracked'; import { get } from '@ember/helper'; +import { on } from '@ember/modifier'; +import style from 'ember-style-modifier/modifiers/style'; // HDS components import { @@ -13,6 +16,7 @@ import { HdsLinkInline, HdsBadge, HdsBadgeColorValues, + HdsFormToggleField, type HdsAdvancedTableOnSelectionChangeSignature, } from '@hashicorp/design-system-components/components'; @@ -22,93 +26,6 @@ export interface MockAppMainGenericAdvancedTableSignature { Element: HTMLDivElement; } -const SAMPLE_COLUMNS = [ - { - isSortable: true, - label: 'Name', - key: 'name', - width: 'max-content', - }, - { - label: 'Project name', - key: 'project-name', - isSortable: true, - width: 'max-content', - }, - { - label: 'Current run ID', - key: 'current-run-id', - isSortable: true, - width: 'max-content', - }, - { - label: 'Run status', - key: 'run-status', - isSortable: true, - width: 'max-content', - }, - { - label: 'Current run applied', - key: 'current-run-applied', - isSortable: true, - width: 'max-content', - }, - { - label: 'VCS repo', - key: 'vcs-repo', - isSortable: true, - width: 'max-content', - }, - { - label: 'Module count', - key: 'module-count', - isSortable: true, - width: 'max-content', - }, - { - label: 'Modules', - key: 'modules', - isSortable: true, - width: 'max-content', - }, - { - label: 'Provider count', - key: 'provider-count', - isSortable: true, - width: 'max-content', - }, - { - label: 'Providers', - key: 'providers', - isSortable: true, - width: 'max-content', - }, - { - label: 'Terraform version', - key: 'terraform-version', - isSortable: true, - width: 'max-content', - }, - { - label: 'State terraform version', - key: 'state-terraform-version', - isSortable: true, - width: 'max-content', - }, - { - label: 'Created', - key: 'created', - isSortable: true, - width: 'max-content', - }, - { - label: 'Updated', - key: 'updated', - isSortable: true, - width: 'max-content', - }, -]; - const SAMPLE_MODEL = [ { name: 'zoguve-guw-mannaz', @@ -151,7 +68,7 @@ const SAMPLE_MODEL = [ 'run-status': 'applied', 'run-status-color': HdsBadgeColorValues.Success, 'current-run-applied': 'Mar 06, 2025 09:08:14 am', - 'vcs-repo': 'example/sClKKTBbyCIzf@d8NxH2', + 'vcs-repo': 'example/a))!hzfpKcBl0', 'module-count': 31, modules: 'wad-bedzeaje-rogmejca', 'provider-count': 42, @@ -168,7 +85,7 @@ const SAMPLE_MODEL = [ 'run-status': 'planned', 'run-status-color': HdsBadgeColorValues.Warning, 'current-run-applied': 'Mar 06, 2025 09:07:14 am', - 'vcs-repo': 'example/y0^(Nm*63', + 'vcs-repo': 'example/a))!hzfpKcBl0', 'module-count': 58, modules: 'wad-bedzeaje-rogmejca', 'provider-count': 140, @@ -185,7 +102,7 @@ const SAMPLE_MODEL = [ 'run-status': 'applied', 'run-status-color': HdsBadgeColorValues.Success, 'current-run-applied': 'Mar 06, 2025 09:06:14 am', - 'vcs-repo': 'example/ljPWe[4', + 'vcs-repo': 'example/a))!hzfpKcBl0', 'module-count': 32, modules: 'wad-bedzeaje-rogmejca', 'provider-count': 50, @@ -202,7 +119,7 @@ const SAMPLE_MODEL = [ 'run-status': 'errored', 'run-status-color': HdsBadgeColorValues.Critical, 'current-run-applied': 'Mar 06, 2025 09:05:14 am', - 'vcs-repo': 'example/E*fcS4mn@BoDgZu0O5', + 'vcs-repo': 'example/a))!hzfpKcBl0', 'module-count': 94, modules: 'wad-bedzeaje-rogmejca', 'provider-count': 113, @@ -236,13 +153,13 @@ const SAMPLE_MODEL = [ 'run-status': 'errored', 'run-status-color': HdsBadgeColorValues.Critical, 'current-run-applied': 'Mar 06, 2025 09:03:14 am', - 'vcs-repo': 'example/(DCFjSEKcBuU44J8AB87', + 'vcs-repo': 'example/&j[RmmtjpQX6', 'module-count': 114, modules: 'wad-bedzeaje-rogmejca', 'provider-count': 107, providers: 'susnup-da-zuw', 'terraform-version': '0.14.0', - 'state-terraform-version': '0.15.0', + 'state-terraform-version': '0.16.0', created: 'Feb 27 2025', updated: 'Feb 27 2025', }, @@ -253,13 +170,13 @@ const SAMPLE_MODEL = [ 'run-status': 'planned', 'run-status-color': HdsBadgeColorValues.Warning, 'current-run-applied': 'Mar 06, 2025 09:02:14 am', - 'vcs-repo': 'example/9YURY8', + 'vcs-repo': 'example/&j[RmmtjpQX6', 'module-count': 106, modules: 'wad-bedzeaje-rogmejca', 'provider-count': 185, providers: 'susnup-da-zuw', - 'terraform-version': '0.14.0', - 'state-terraform-version': '0.15.0', + 'terraform-version': '0.14.5', + 'state-terraform-version': '0.16.0', created: 'Feb 26 2025', updated: 'Feb 26 2025', }, @@ -270,13 +187,13 @@ const SAMPLE_MODEL = [ 'run-status': 'errored', 'run-status-color': HdsBadgeColorValues.Critical, 'current-run-applied': 'Mar 06, 2025 09:01:14 am', - 'vcs-repo': 'example/9YURY8', + 'vcs-repo': 'example/&j[RmmtjpQX6', 'module-count': 124, modules: 'wad-bedzeaje-rogmejca', 'provider-count': 175, providers: 'susnup-da-zuw', - 'terraform-version': '0.14.0', - 'state-terraform-version': '0.15.0', + 'terraform-version': '0.14.5', + 'state-terraform-version': '0.16.0', created: 'Feb 25 2025', updated: 'Feb 25 2025', }, @@ -287,13 +204,13 @@ const SAMPLE_MODEL = [ 'run-status': 'applied', 'run-status-color': HdsBadgeColorValues.Success, 'current-run-applied': 'Mar 06, 2025 09:00:14 am', - 'vcs-repo': 'example/d2s3B46I10', + 'vcs-repo': 'example/&j[RmmtjpQX6', 'module-count': 70, modules: 'wad-bedzeaje-rogmejca', 'provider-count': 168, providers: 'susnup-da-zuw', - 'terraform-version': '0.14.0', - 'state-terraform-version': '0.15.0', + 'terraform-version': '0.14.5', + 'state-terraform-version': '0.16.0', created: 'Feb 24 2025', updated: 'Feb 24 2025', }, @@ -309,8 +226,8 @@ const SAMPLE_MODEL = [ modules: 'wad-bedzeaje-rogmejca', 'provider-count': 168, providers: 'susnup-da-zuw', - 'terraform-version': '0.14.0', - 'state-terraform-version': '0.15.0', + 'terraform-version': '0.14.5', + 'state-terraform-version': '0.16.0', created: 'Feb 23 2025', updated: 'Feb 23 2025', }, @@ -321,13 +238,13 @@ const SAMPLE_MODEL = [ 'run-status': 'errored', 'run-status-color': HdsBadgeColorValues.Critical, 'current-run-applied': 'Mar 06, 2025 08:59:14 am', - 'vcs-repo': 'example/v@C6&hBTou11', + 'vcs-repo': 'example/d2s3B46I10', 'module-count': 106, modules: 'wad-bedzeaje-rogmejca', 'provider-count': 61, providers: 'susnup-da-zuw', - 'terraform-version': '0.14.0', - 'state-terraform-version': '0.15.0', + 'terraform-version': '0.14.5', + 'state-terraform-version': '0.16.0', created: 'Feb 22 2025', updated: 'Feb 22 2025', }, @@ -338,12 +255,12 @@ const SAMPLE_MODEL = [ 'run-status': 'applied', 'run-status-color': HdsBadgeColorValues.Success, 'current-run-applied': 'Mar 06, 2025 08:58:14 am', - 'vcs-repo': 'example/@t23^12', + 'vcs-repo': 'example/d2s3B46I10', 'module-count': 14, modules: 'wad-bedzeaje-rogmejca', 'provider-count': 143, providers: 'susnup-da-zuw', - 'terraform-version': '0.14.0', + 'terraform-version': '0.14.5', 'state-terraform-version': '0.15.0', created: 'Feb 21 2025', updated: 'Feb 21 2025', @@ -355,12 +272,12 @@ const SAMPLE_MODEL = [ 'run-status': 'planned', 'run-status-color': HdsBadgeColorValues.Warning, 'current-run-applied': 'Mar 06, 2025 08:58:14 am', - 'vcs-repo': 'example/@t23^12', + 'vcs-repo': 'example/d2s3B46I10', 'module-count': 14, modules: 'wad-bedzeaje-rogmejca', 'provider-count': 143, providers: 'susnup-da-zuw', - 'terraform-version': '0.14.0', + 'terraform-version': '0.14.5', 'state-terraform-version': '0.15.0', created: 'Feb 20 2025', updated: 'Feb 20 2025', @@ -377,7 +294,7 @@ const SAMPLE_MODEL = [ modules: 'wad-bedzeaje-rogmejca', 'provider-count': 98, providers: 'susnup-da-zuw', - 'terraform-version': '0.14.0', + 'terraform-version': '0.14.5', 'state-terraform-version': '0.15.0', created: 'Feb 19 2025', updated: 'Feb 19 2025', @@ -394,7 +311,7 @@ const SAMPLE_MODEL = [ modules: 'wad-bedzeaje-rogmejca', 'provider-count': 170, providers: 'susnup-da-zuw', - 'terraform-version': '0.14.0', + 'terraform-version': '0.14.5', 'state-terraform-version': '0.15.0', created: 'Feb 18 2025', updated: 'Feb 18 2025', @@ -406,12 +323,12 @@ const SAMPLE_MODEL = [ 'run-status': 'planned', 'run-status-color': HdsBadgeColorValues.Warning, 'current-run-applied': 'Mar 06, 2025 08:57:14 am', - 'vcs-repo': 'example/t*vN3@*BxJnG116', + 'vcs-repo': 'example/d2s3B46I10', 'module-count': 139, modules: 'wad-bedzeaje-rogmejca', 'provider-count': 170, providers: 'susnup-da-zuw', - 'terraform-version': '0.14.0', + 'terraform-version': '0.14.5', 'state-terraform-version': '0.15.0', created: 'Feb 17 2025', updated: 'Feb 17 2025', @@ -428,7 +345,7 @@ const SAMPLE_MODEL = [ modules: 'wad-bedzeaje-rogmejca', 'provider-count': 83, providers: 'susnup-da-zuw', - 'terraform-version': '0.14.0', + 'terraform-version': '0.14.5', 'state-terraform-version': '0.15.0', created: 'Feb 16 2025', updated: 'Feb 16 2025', @@ -445,7 +362,7 @@ const SAMPLE_MODEL = [ modules: 'wad-bedzeaje-rogmejca', 'provider-count': 152, providers: 'susnup-da-zuw', - 'terraform-version': '0.14.0', + 'terraform-version': '0.14.5', 'state-terraform-version': '0.15.0', created: 'Feb 15 2025', updated: 'Feb 15 2025', @@ -462,13 +379,124 @@ const SAMPLE_MODEL = [ modules: 'wad-bedzeaje-rogmejca', 'provider-count': 11, providers: 'susnup-da-zuw', - 'terraform-version': '0.14.0', + 'terraform-version': '0.14.5', 'state-terraform-version': '0.15.0', created: 'Feb 14 2025', updated: 'Feb 14 2025', }, ]; + +const SAMPLE_MODEL_VALUES = { + 'project-name': Array.from( + new Set(SAMPLE_MODEL.map((item) => item['project-name'])), + ).map((value) => ({ value, label: value })), + 'run-status': Array.from( + new Set(SAMPLE_MODEL.map((item) => item['run-status'])), + ).map((value) => ({ value, label: value })), + 'vcs-repo': Array.from( + new Set(SAMPLE_MODEL.map((item) => item['vcs-repo'])), + ).map((value) => ({ value, label: value })), + 'terraform-version': Array.from( + new Set(SAMPLE_MODEL.map((item) => item['terraform-version'])), + ).map((value) => ({ value, label: value })), + 'state-terraform-version': Array.from( + new Set(SAMPLE_MODEL.map((item) => item['state-terraform-version'])), + ).map((value) => ({ value, label: value })), +}; + +const SAMPLE_COLUMNS = [ + { + isSortable: true, + label: 'Name', + key: 'name', + width: 'max-content', + }, + { + label: 'Project name', + key: 'project-name', + isSortable: true, + width: 'max-content', + filterType: 'checkbox', + }, + { + label: 'Current run ID', + key: 'current-run-id', + isSortable: true, + width: 'max-content', + }, + { + label: 'Run status', + key: 'run-status', + isSortable: true, + width: 'max-content', + filterType: 'checkbox', + }, + { + label: 'Current run applied', + key: 'current-run-applied', + isSortable: true, + width: 'max-content', + }, + { + label: 'VCS repo', + key: 'vcs-repo', + isSortable: true, + width: 'max-content', + filterType: 'checkbox', + }, + { + label: 'Module count', + key: 'module-count', + isSortable: true, + width: 'max-content', + }, + { + label: 'Modules', + key: 'modules', + isSortable: true, + width: 'max-content', + }, + { + label: 'Provider count', + key: 'provider-count', + isSortable: true, + width: 'max-content', + }, + { + label: 'Providers', + key: 'providers', + isSortable: true, + width: 'max-content', + }, + { + label: 'Terraform version', + key: 'terraform-version', + isSortable: true, + width: 'max-content', + filterType: 'radio', + }, + { + label: 'State terraform version', + key: 'state-terraform-version', + isSortable: true, + width: 'max-content', + filterType: 'radio', + }, + { + label: 'Created', + key: 'created', + isSortable: true, + width: 'max-content', + }, + { + label: 'Updated', + key: 'updated', + isSortable: true, + width: 'max-content', + }, +]; + const updateModelWithSelectAllState = ( modelData: HdsAdvancedTableSignature['Args']['model'], selectAllState: boolean, @@ -499,6 +527,8 @@ export default class MockAppMainGenericAdvancedTable extends Component { + const filter = filters[name]; + if (!filter) return; + + if (Array.isArray(filter)) { + if (filter.length === 1) return filter[0]?.value; + return filter.map((f: HdsAdvancedTableSignature['Args']['filters'][]) => f.value); + } + return filter.value; + }; + + @action + onFilter(filters: HdsAdvancedTableSignature['Args']['filters']) { + this.filters = filters; + } + + get demoModelFilteredData() { + const filterItem = (item: HdsAdvancedTableSignature['Args']['filters']): boolean => { + if (Object.keys(this.filters).length === 0) return true; + let match = true; + Object.keys(this.filters).forEach((key) => { + const keyFilters = this.valuesFromFilter(this.filters, key); + if (Array.isArray(keyFilters)) { + if (!keyFilters.includes(item[key])) { + match = false; + } + } else if (item[key] !== keyFilters) { + match = false; + } + }); + return match; + }; + + return this.demoModel.filter(filterItem); + } + + @action + onLiveFilterToggle(event: Event) { + const target = event.target as HTMLInputElement; + this.isLiveFilter = target.checked; + } +