Skip to content

Commit 0f1e82e

Browse files
committed
Feat: dropdown range filter, fix sync of filters in dropdown
1 parent 8964f40 commit 0f1e82e

File tree

10 files changed

+158
-130
lines changed

10 files changed

+158
-130
lines changed

packages/components/src/components/hds/filter-bar/checkbox.hbs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,8 @@
22
Copyright (c) HashiCorp, Inc.
33
SPDX-License-Identifier: MPL-2.0
44
}}
5-
<Hds::Form::Checkbox::Field
6-
class="hds-filter-bar__filters-dropdown__filter-option"
7-
checked={{this.isChecked}}
8-
@value={{@value}}
9-
{{on "change" this.onChange}}
10-
as |F|
11-
>
12-
<F.Label>{{yield}}</F.Label>
13-
</Hds::Form::Checkbox::Field>
5+
<li class="hds-filter-bar__filters-dropdown__filter-option">
6+
<Hds::Form::Checkbox::Field checked={{this.isChecked}} @value={{@value}} {{on "change" this.onChange}} as |F|>
7+
<F.Label>{{yield}}</F.Label>
8+
</Hds::Form::Checkbox::Field>
9+
</li>

packages/components/src/components/hds/filter-bar/filter-options.hbs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,25 @@
22
{{#let @panel as |Panel|}}
33
<Panel class="hds-filter-bar__filters-dropdown__filter-options" {{this._setUpFilterOptions}}>
44
{{#if @searchEnabled}}
5-
<span>Search goes here</span>
5+
<div class="hds-filter-bar__filters-dropdown__filter-options__search">
6+
<Hds::Form::TextInput::Base
7+
@type="search"
8+
placeholder={{if @searchPlaceholder @searchPlaceholder "Search"}}
9+
{{on "input" this.onSearch}}
10+
/>
11+
</div>
612
{{/if}}
713
{{#if (eq @type "range")}}
814
<Hds::FilterBar::Range @keyFilter={{this.keyFilter}} @onChange={{this.onRangeChange}} />
915
{{else}}
10-
{{yield
11-
(hash
12-
Checkbox=(component "hds/filter-bar/checkbox" keyFilter=this.keyFilter onChange=this.onSelectionChange)
13-
Radio=(component "hds/filter-bar/radio" keyFilter=this.keyFilter onChange=this.onSelectionChange)
14-
)
15-
}}
16+
<ul class="hds-filter-bar__filters-dropdown__filter-options__list">
17+
{{yield
18+
(hash
19+
Checkbox=(component "hds/filter-bar/checkbox" keyFilter=this.keyFilter onChange=this.onSelectionChange)
20+
Radio=(component "hds/filter-bar/radio" keyFilter=this.keyFilter onChange=this.onSelectionChange)
21+
)
22+
}}
23+
</ul>
1624
{{/if}}
1725
</Panel>
1826
{{/let}}

packages/components/src/components/hds/filter-bar/filter-options.ts

Lines changed: 23 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ export interface HdsFilterBarFilterOptionsSignature {
3030
key: string;
3131
type?: HdsFilterBarFilterType;
3232
filters: HdsFilterBarFilters;
33-
isMultiSelect?: boolean;
34-
activeFilterableColumns?: string[];
3533
searchEnabled?: boolean;
3634
onChange: (key: string, keyFilter?: HdsFilterBarFilter) => void;
3735
};
@@ -55,7 +53,11 @@ export interface HdsFilterBarFilterOptionsSignature {
5553
export default class HdsFilterBarFilterOptions extends Component<HdsFilterBarFilterOptionsSignature> {
5654
@tracked internalFilters: HdsFilterBarData | undefined = [];
5755

58-
private _setUpFilterOptions = modifier(() => {
56+
private _element!: HTMLDivElement;
57+
58+
private _setUpFilterOptions = modifier((element: HTMLDivElement) => {
59+
this._element = element;
60+
5961
if (this.keyFilter) {
6062
this.internalFilters = JSON.parse(
6163
JSON.stringify(this.keyFilter)
@@ -169,71 +171,29 @@ export default class HdsFilterBarFilterOptions extends Component<HdsFilterBarFil
169171
} as HdsFilterBarFilter;
170172
}
171173

172-
// get toggleButtonText(): string {
173-
// const { key, filters, text } = this.args;
174-
175-
// let displayText = key;
176-
// if (text && text.length > 0) {
177-
// displayText = text;
178-
// }
179-
180-
// const keyFilter = filters[key];
181-
182-
// if (!filters || !keyFilter || !keyFilter.data) {
183-
// return displayText;
184-
// } else if (this.args.type === 'range') {
185-
// return `${displayText} ${this._rangeFilterText(keyFilter.data)}`;
186-
// } else if (this.args.type === 'single-select') {
187-
// return `${displayText}: ${this._singleSelectFilterText(keyFilter.data)}`;
188-
// } else {
189-
// return `${displayText}: ${this._multiSelectFilterText(keyFilter.data)}`;
190-
// }
191-
// }
192-
193-
// private _rangeFilterText(filterData: HdsFilterBarData): string {
194-
// if ('selector' in filterData && 'value' in filterData) {
195-
// return `${SELECTORS_DISPLAY_SYMBOL[filterData.selector]} ${filterData.value}`;
196-
// } else {
197-
// return '';
198-
// }
199-
// }
200-
201-
// private _singleSelectFilterText(filterData: HdsFilterBarData): string {
202-
// if ('value' in filterData) {
203-
// return filterData.value as string;
204-
// } else {
205-
// return '';
206-
// }
207-
// }
208-
209-
// private _multiSelectFilterText(filterData: HdsFilterBarData): string {
210-
// if (Array.isArray(filterData) && filterData.length > 0) {
211-
// const charMax = 10;
212-
// let filtersString = '';
213-
214-
// filtersString = filterData
215-
// .map((filter) => {
216-
// if ('text' in filter && typeof filter.text === 'string') {
217-
// if (filter.text.length > charMax) {
218-
// return filter.text.slice(0, charMax) + '...';
219-
// }
220-
// return filter.text;
221-
// }
222-
// return '';
223-
// })
224-
// .join(', ');
225-
226-
// return filtersString;
227-
// } else {
228-
// return '';
229-
// }
230-
// }
231-
232174
get classNames(): string {
233175
const classes = ['hds-filter-bar__filter-options'];
234176

235177
classes.push(`hds-filter-bar__dropdown--type-${this.type}`);
236178

237179
return classes.join(' ');
238180
}
181+
182+
private onSearch = (event: Event) => {
183+
const listItems = this._element.querySelectorAll(
184+
'.hds-filter-bar__filters-dropdown__filter-option'
185+
);
186+
const input = event.target as HTMLInputElement;
187+
listItems.forEach((item) => {
188+
if (item.textContent) {
189+
const text = item.textContent.toLowerCase();
190+
const searchText = input.value.toLowerCase();
191+
if (text.includes(searchText)) {
192+
item.style.display = '';
193+
} else {
194+
item.style.display = 'none';
195+
}
196+
}
197+
});
198+
};
239199
}

packages/components/src/components/hds/filter-bar/filters-dropdown.hbs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
{{! @glint-nocheck }}
2-
<Hds::Dropdown @listPosition="bottom-left" @height="500px" class={{this.classNames}} as |D|>
2+
<Hds::Dropdown
3+
@listPosition="bottom-left"
4+
@height="500px"
5+
@width="500px"
6+
class={{this.classNames}}
7+
@onClose={{this._onClose}}
8+
{{this._syncFilters @filters}}
9+
as |D|
10+
>
311
<D.ToggleButton @icon="filter" @text="Filter" @color="secondary" @size="small" />
412
<D.Generic>
513
<Hds::Tabs as |T|>
@@ -14,9 +22,20 @@
1422
</Hds::Tabs>
1523
</D.Generic>
1624
<D.Footer @hasDivider={{true}}>
17-
<Hds::ButtonSet>
18-
<Hds::Button @text="Apply" @isFullWidth={{true}} @size="small" {{on "click" (fn this.onApply D.close)}} />
19-
<Hds::Button @text="Clear" @color="secondary" @size="small" {{on "click" (fn this.onClear D.close)}} />
20-
</Hds::ButtonSet>
25+
<Hds::Layout::Flex @direction="row" @justify="space-between" @align="center" as |LF|>
26+
<Hds::ButtonSet>
27+
<Hds::Button @text="Apply filters" @size="small" {{on "click" (fn this.onApply D.close)}} />
28+
<Hds::Button
29+
@text="Clear all filters"
30+
@color="secondary"
31+
@size="small"
32+
{{on "click" (fn this.onClear D.close)}}
33+
/>
34+
</Hds::ButtonSet>
35+
<LF.Item>
36+
<Hds::Text::Body>Filters selected:</Hds::Text::Body>
37+
<Hds::BadgeCount @text={{this.numFilters}} @type="outlined" />
38+
</LF.Item>
39+
</Hds::Layout::Flex>
2140
</D.Footer>
2241
</Hds::Dropdown>

packages/components/src/components/hds/filter-bar/filters-dropdown.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import Component from '@glimmer/component';
77
import { action } from '@ember/object';
88
import { tracked } from '@glimmer/tracking';
9+
import { modifier } from 'ember-modifier';
910
import type Owner from '@ember/owner';
1011
import type { WithBoundArgs } from '@glint/template';
1112

@@ -54,6 +55,14 @@ export default class HdsFilterBarFiltersDropdown extends Component<
5455
}
5556
}
5657

58+
private _syncFilters = modifier(
59+
(_element, [_filters]: [HdsFilterBarFilters | undefined]) => {
60+
if (_filters) {
61+
this.internalFilters = _filters;
62+
}
63+
}
64+
);
65+
5766
@action
5867
onFilter(key: string, keyFilter?: HdsFilterBarFilter): void {
5968
this.internalFilters = this._updateFilter(key, keyFilter);
@@ -85,6 +94,21 @@ export default class HdsFilterBarFiltersDropdown extends Component<
8594
}
8695
}
8796

97+
get numFilters(): number {
98+
let numFilters = 0;
99+
Object.keys(this.internalFilters).forEach((key) => {
100+
const filter = this.internalFilters[key];
101+
if (filter) {
102+
if (Array.isArray(filter.data)) {
103+
numFilters += filter.data.length;
104+
} else {
105+
numFilters += 1;
106+
}
107+
}
108+
});
109+
return numFilters;
110+
}
111+
88112
get classNames(): string {
89113
const classes = ['hds-filter-bar__filters-dropdown'];
90114

@@ -95,12 +119,11 @@ export default class HdsFilterBarFiltersDropdown extends Component<
95119
key: string,
96120
keyFilter?: HdsFilterBarFilter
97121
): HdsFilterBarFilters {
98-
const { filters } = this.args;
99122
const newFilters = {} as HdsFilterBarFilters;
100123

101-
Object.keys(filters).forEach((k) => {
124+
Object.keys(this.internalFilters).forEach((k) => {
102125
newFilters[k] = JSON.parse(
103-
JSON.stringify(filters[k])
126+
JSON.stringify(this.internalFilters[k])
104127
) as HdsFilterBarFilter;
105128
});
106129
if (
@@ -114,4 +137,13 @@ export default class HdsFilterBarFiltersDropdown extends Component<
114137

115138
return { ...newFilters };
116139
}
140+
141+
private _onClose = (): void => {
142+
const { filters } = this.args;
143+
if (filters) {
144+
this.internalFilters = { ...filters };
145+
} else {
146+
this.internalFilters = {};
147+
}
148+
};
117149
}
Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
<Hds::Form::Radio::Field
2-
class="hds-filter-bar__filters-dropdown__filter-option"
3-
checked={{this.isChecked}}
4-
@value={{@value}}
5-
{{on "change" this.onChange}}
6-
as |F|
7-
>
8-
<F.Label>{{yield}}</F.Label>
9-
</Hds::Form::Radio::Field>
1+
<li class="hds-filter-bar__filters-dropdown__filter-option">
2+
<Hds::Form::Radio::Field checked={{this.isChecked}} @value={{@value}} {{on "change" this.onChange}} as |F|>
3+
<F.Label>{{yield}}</F.Label>
4+
</Hds::Form::Radio::Field>
5+
</li>
Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,24 @@
1-
{{#let @generic as |Generic|}}
2-
<Generic>
3-
<Hds::Form::Label>
4-
Number is
5-
</Hds::Form::Label>
6-
<Hds::Layout::Flex @gap="8">
7-
<Hds::Form::Select::Base @id={{this._selectorInputId}} {{on "change" this.onSelectorChange}} as |F|>
8-
<F.Options>
9-
<option value="">Pick a selector</option>
10-
{{#each this._selectorValues as |selectorValue|}}
11-
<option value={{selectorValue}} selected={{eq selectorValue this._selector}}>{{this.selectorText
12-
selectorValue
13-
}}</option>
14-
{{/each}}
15-
</F.Options>
16-
</Hds::Form::Select::Base>
17-
<Hds::Form::TextInput::Base
18-
@id={{this._valueInputId}}
19-
@type="text"
20-
@value={{this.stringValue}}
21-
placeholder="Enter a number"
22-
{{on "change" this.onValueChange}}
23-
/>
24-
</Hds::Layout::Flex>
25-
</Generic>
26-
{{/let}}
1+
<div class="hds-filter-bar__filters-dropdown__filter-range">
2+
<Hds::Form::Label>
3+
Number is
4+
</Hds::Form::Label>
5+
<Hds::Layout::Flex @direction="column" @gap="16">
6+
<Hds::Form::Select::Base @id={{this._selectorInputId}} {{on "change" this.onSelectorChange}} as |F|>
7+
<F.Options>
8+
<option value="">Pick a selector</option>
9+
{{#each this._selectorValues as |selectorValue|}}
10+
<option value={{selectorValue}} selected={{eq selectorValue this._selector}}>{{this.selectorText
11+
selectorValue
12+
}}</option>
13+
{{/each}}
14+
</F.Options>
15+
</Hds::Form::Select::Base>
16+
<Hds::Form::TextInput::Base
17+
@id={{this._valueInputId}}
18+
@type="text"
19+
@value={{this.stringValue}}
20+
placeholder="Enter a number"
21+
{{on "change" this.onValueChange}}
22+
/>
23+
</Hds::Layout::Flex>
24+
</div>

packages/components/src/components/hds/filter-bar/range.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import Component from '@glimmer/component';
77
import { action } from '@ember/object';
88
import { tracked } from '@glimmer/tracking';
99
import type Owner from '@ember/owner';
10-
import type { WithBoundArgs } from '@glint/template';
1110
import { guidFor } from '@ember/object/internals';
1211

1312
import type {
@@ -16,8 +15,6 @@ import type {
1615
} from './types.ts';
1716
import { HdsFilterBarRangeFilterSelectorValues } from './types.ts';
1817

19-
import HdsDropdownListItemGeneric from '../dropdown/list-item/generic.ts';
20-
2118
import type { HdsDropdownSignature } from '../dropdown/index.ts';
2219

2320
export const SELECTORS: HdsFilterBarRangeFilterSelector[] = Object.values(
@@ -50,7 +47,6 @@ export const SELECTORS_DISPLAY_SYMBOL: Record<
5047

5148
export interface HdsFilterBarRangeSignature {
5249
Args: HdsDropdownSignature['Args'] & {
53-
generic?: WithBoundArgs<typeof HdsDropdownListItemGeneric, never>;
5450
keyFilter: HdsFilterBarRangeFilter | undefined;
5551
onChange?: (
5652
selector?: HdsFilterBarRangeFilterSelector,

packages/components/src/styles/components/filter-bar.scss

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,20 @@
5353
.hds-filter-bar__filters-dropdown__filter-options .hds-form-field--layout-flag {
5454
padding: 8px 12px;
5555
}
56+
57+
.hds-filter-bar__filters-dropdown .hds-layout-flex {
58+
margin: 8px 0;
59+
}
60+
61+
.hds-filter-bar__filters-dropdown .hds-button-set {
62+
gap: 8px;
63+
}
64+
65+
.hds-filter-bar__filters-dropdown__filter-options__list {
66+
flex: 1 1 auto;
67+
margin: 0;
68+
padding: 4px 0;
69+
overflow-y: auto;
70+
list-style: none;
71+
overscroll-behavior: contain;
72+
}

0 commit comments

Comments
 (0)