Skip to content

Commit 7104ede

Browse files
committed
Feat: Empty state for adv table
1 parent 0fa32c6 commit 7104ede

File tree

6 files changed

+201
-95
lines changed

6 files changed

+201
-95
lines changed

packages/components/src/components/hds/advanced-table/index.hbs

Lines changed: 82 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -112,79 +112,81 @@
112112
</div>
113113

114114
{{! Body }}
115-
<div class="hds-advanced-table__tbody" role="rowgroup">
116-
{{! ----------------------------------------------------------------------------------------
115+
{{#unless this.isEmpty}}
116+
<div class="hds-advanced-table__tbody" role="rowgroup">
117+
{{! ----------------------------------------------------------------------------------------
117118
IMPORTANT: we loop on the `model` array and for each record
118119
we yield the Tr/Td/Th elements _and_ the record itself as `data`
119120
this means the consumer will *have to* use the `data` key to access it in their template
120121
-------------------------------------------------------------------------------------------- }}
121-
{{#each this._tableModel.sortedRows key=this.identityKey as |record index|}}
122-
{{#if this._tableModel.hasRowsWithChildren}}
123-
<Hds::AdvancedTable::ExpandableTrGroup
124-
@record={{record}}
125-
@rowIndex={{index}}
126-
@onClickToggle={{record.onClickToggle}}
127-
as |T|
128-
>
122+
{{#each this._tableModel.sortedRows key=this.identityKey as |record index|}}
123+
{{#if this._tableModel.hasRowsWithChildren}}
124+
<Hds::AdvancedTable::ExpandableTrGroup
125+
@record={{record}}
126+
@rowIndex={{index}}
127+
@onClickToggle={{record.onClickToggle}}
128+
as |T|
129+
>
130+
{{yield
131+
(hash
132+
Tr=(component
133+
"hds/advanced-table/tr"
134+
isLastRow=(eq this._tableModel.lastVisibleRow.id T.data.id)
135+
isParentRow=T.isExpandable
136+
depth=T.depth
137+
displayRow=T.shouldDisplayChildRows
138+
data=T.data
139+
)
140+
Th=(component
141+
"hds/advanced-table/th"
142+
depth=T.depth
143+
isExpandable=T.isExpandable
144+
isExpanded=T.isExpanded
145+
newLabel=T.id
146+
parentId=T.parentId
147+
scope="row"
148+
onClickToggle=T.onClickToggle
149+
)
150+
Td=(component "hds/advanced-table/td" align=@align)
151+
data=T.data
152+
isOpen=T.isExpanded
153+
rowIndex=T.rowIndex
154+
)
155+
to="body"
156+
}}
157+
</Hds::AdvancedTable::ExpandableTrGroup>
158+
{{else}}
129159
{{yield
130160
(hash
131161
Tr=(component
132162
"hds/advanced-table/tr"
133-
isLastRow=(eq this._tableModel.lastVisibleRow.id T.data.id)
134-
isParentRow=T.isExpandable
135-
depth=T.depth
136-
displayRow=T.shouldDisplayChildRows
137-
data=T.data
163+
selectionScope="row"
164+
isLastRow=(eq this._tableModel.lastVisibleRow.id record.id)
165+
isSelectable=this.isSelectable
166+
onSelectionChange=this.onSelectionRowChange
167+
didInsert=this.didInsertRowCheckbox
168+
willDestroy=this.willDestroyRowCheckbox
169+
selectionAriaLabelSuffix=@selectionAriaLabelSuffix
170+
hasStickyColumn=this.hasStickyFirstColumn
171+
isStickyColumnPinned=this.isStickyColumnPinned
172+
data=record
138173
)
139174
Th=(component
140175
"hds/advanced-table/th"
141-
depth=T.depth
142-
isExpandable=T.isExpandable
143-
isExpanded=T.isExpanded
144-
newLabel=T.id
145-
parentId=T.parentId
146176
scope="row"
147-
onClickToggle=T.onClickToggle
177+
isStickyColumn=this.hasStickyFirstColumn
178+
isStickyColumnPinned=this.isStickyColumnPinned
148179
)
149180
Td=(component "hds/advanced-table/td" align=@align)
150-
data=T.data
151-
isOpen=T.isExpanded
152-
rowIndex=T.rowIndex
181+
data=record
182+
rowIndex=index
153183
)
154184
to="body"
155185
}}
156-
</Hds::AdvancedTable::ExpandableTrGroup>
157-
{{else}}
158-
{{yield
159-
(hash
160-
Tr=(component
161-
"hds/advanced-table/tr"
162-
selectionScope="row"
163-
isLastRow=(eq this._tableModel.lastVisibleRow.id record.id)
164-
isSelectable=this.isSelectable
165-
onSelectionChange=this.onSelectionRowChange
166-
didInsert=this.didInsertRowCheckbox
167-
willDestroy=this.willDestroyRowCheckbox
168-
selectionAriaLabelSuffix=@selectionAriaLabelSuffix
169-
hasStickyColumn=this.hasStickyFirstColumn
170-
isStickyColumnPinned=this.isStickyColumnPinned
171-
data=record
172-
)
173-
Th=(component
174-
"hds/advanced-table/th"
175-
scope="row"
176-
isStickyColumn=this.hasStickyFirstColumn
177-
isStickyColumnPinned=this.isStickyColumnPinned
178-
)
179-
Td=(component "hds/advanced-table/td" align=@align)
180-
data=record
181-
rowIndex=index
182-
)
183-
to="body"
184-
}}
185-
{{/if}}
186-
{{/each}}
187-
</div>
186+
{{/if}}
187+
{{/each}}
188+
</div>
189+
{{/unless}}
188190
</div>
189191

190192
{{#if this.showScrollIndicatorLeft}}
@@ -201,10 +203,29 @@
201203
/>
202204
{{/if}}
203205

204-
{{#if this.showScrollIndicatorBottom}}
205-
<div
206-
class="hds-advanced-table__scroll-indicator hds-advanced-table__scroll-indicator-bottom"
207-
{{style bottom=this.scrollIndicatorDimensions.bottom width=this.scrollIndicatorDimensions.width}}
208-
/>
206+
{{#unless this.isEmpty}}
207+
{{#if this.showScrollIndicatorBottom}}
208+
<div
209+
class="hds-advanced-table__scroll-indicator hds-advanced-table__scroll-indicator-bottom"
210+
{{style bottom=this.scrollIndicatorDimensions.bottom width=this.scrollIndicatorDimensions.width}}
211+
/>
212+
{{/if}}
213+
{{/unless}}
214+
215+
{{#if this.isEmpty}}
216+
<div class="hds-advanced-table__empty-state">
217+
<div class="hds-advanced-table__empty-state__content">
218+
{{#if (has-block "emptyState")}}
219+
{{yield to="emptyState"}}
220+
{{else}}
221+
<Hds::Layout::Flex @direction="column" @gap="8">
222+
<Hds::Text::Display @tag="h3" @size="300">No data to display</Hds::Text::Display>
223+
<Hds::Text::Body>
224+
No data is available in the table to display.
225+
</Hds::Text::Body>
226+
</Hds::Layout::Flex>
227+
{{/if}}
228+
</div>
229+
</div>
209230
{{/if}}
210231
</div>

packages/components/src/components/hds/advanced-table/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ export interface HdsAdvancedTableSignature {
151151
hasStickyFirstColumn?: boolean;
152152
childrenKey?: string;
153153
maxHeight?: string;
154+
isEmpty?: boolean;
154155
onColumnReorder?: HdsAdvancedTableColumnReorderCallback;
155156
onColumnResize?: (columnKey: string, newWidth?: string) => void;
156157
onSelectionChange?: (
@@ -199,6 +200,7 @@ export interface HdsAdvancedTableSignature {
199200
isOpen?: HdsAdvancedTableExpandState;
200201
},
201202
];
203+
emptyState?: [];
202204
};
203205
Element: HTMLDivElement;
204206
}
@@ -266,6 +268,14 @@ export default class HdsAdvancedTable extends Component<HdsAdvancedTableSignatur
266268
}
267269
}
268270

271+
get isEmpty(): boolean {
272+
const { isEmpty } = this.args;
273+
if (isEmpty !== undefined) {
274+
return isEmpty;
275+
}
276+
return false;
277+
}
278+
269279
get identityKey(): string | undefined {
270280
// we have to provide a way for the consumer to pass undefined because Ember tries to interpret undefined as missing an arg and therefore falls back to the default
271281
if (this.args.identityKey === 'none') {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
Dropdown=(component
3030
"hds/filter-bar/dropdown"
3131
onChange=this.onFilter
32-
filters=this.filters
32+
filters=@filters
3333
isLiveFilter=@isLiveFilter
3434
activeFilterableColumns=this.activeFilterableColumns
3535
)

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

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import HdsFilterBarFiltersDropdown from './filters-dropdown.ts';
2020
export interface HdsFilterBarSignature {
2121
Args: {
2222
filters: HdsFilterBarFilters;
23-
activeFilterableColumns?: string[];
23+
visibleFilterableColumns?: string[];
2424
isLiveFilter?: boolean;
2525
hasSearch?: boolean;
2626
showFilters?: boolean;
@@ -46,9 +46,7 @@ export interface HdsFilterBarSignature {
4646
}
4747

4848
export default class HdsFilterBar extends Component<HdsFilterBarSignature> {
49-
@tracked filters: HdsFilterBarFilters = {};
50-
@tracked hasActiveFilters: boolean = Object.keys(this.filters).length > 0;
51-
@tracked activeFilterableColumns: string[] = [];
49+
@tracked visibleFilterableColumns: string[] = [];
5250
@tracked showFilters: boolean = true;
5351

5452
private _element!: HTMLDivElement;
@@ -67,19 +65,38 @@ export default class HdsFilterBar extends Component<HdsFilterBarSignature> {
6765
constructor(owner: Owner, args: HdsFilterBarSignature['Args']) {
6866
super(owner, args);
6967

70-
const { filters, activeFilterableColumns, showFilters } = args;
68+
const { filters, visibleFilterableColumns, showFilters } = args;
7169

72-
if (filters) {
73-
this.filters = { ...filters };
74-
}
75-
76-
if (activeFilterableColumns) {
77-
this.activeFilterableColumns = [...activeFilterableColumns];
78-
}
70+
console.log('Initializing FilterBar with filters:', filters);
7971

8072
if (showFilters != null) {
8173
this.showFilters = showFilters;
8274
}
75+
76+
if (visibleFilterableColumns) {
77+
this.visibleFilterableColumns = [...visibleFilterableColumns];
78+
}
79+
80+
Object.keys(filters).forEach((k) => {
81+
if (!this.activeFilterableColumns.includes(k)) {
82+
this.activeFilterableColumns.push(k);
83+
}
84+
});
85+
}
86+
87+
get hasActiveFilters(): boolean {
88+
return Object.keys(this.args.filters).length > 0;
89+
}
90+
91+
get activeFilterableColumns(): string[] {
92+
const { filters } = this.args;
93+
const columns: string[] = [];
94+
95+
Object.keys(filters).forEach((k) => {
96+
columns.push(k);
97+
});
98+
99+
return columns.concat(this.visibleFilterableColumns);
83100
}
84101

85102
@action
@@ -88,22 +105,25 @@ export default class HdsFilterBar extends Component<HdsFilterBarSignature> {
88105
}
89106

90107
@action
91-
onFiltersChange(activeFilterableColumns: string[]): void {
92-
this.activeFilterableColumns = activeFilterableColumns;
108+
onFiltersChange(visibleFilterableColumns: string[]): void {
109+
const { filters } = this.args;
110+
111+
this.visibleFilterableColumns = visibleFilterableColumns;
93112

94-
Object.keys(this.filters).forEach((k) => {
113+
Object.keys(filters).forEach((k) => {
95114
if (!this.activeFilterableColumns.includes(k)) {
96115
this._triggerFilter(k);
97116
}
98117
});
99118

100119
let filterKeyToOpen: string | undefined = undefined;
101-
activeFilterableColumns.forEach((k) => {
102-
if (!this.filters[k]) {
120+
this.activeFilterableColumns.forEach((k) => {
121+
if (!filters[k]) {
103122
filterKeyToOpen = k;
104123
}
105124
});
106125

126+
console.log('Filter key to open:', filterKeyToOpen);
107127
if (filterKeyToOpen) {
108128
// eslint-disable-next-line ember/no-runloop
109129
schedule('afterRender', (): void => {
@@ -114,12 +134,10 @@ export default class HdsFilterBar extends Component<HdsFilterBarSignature> {
114134

115135
@action
116136
clearFilters(): void {
117-
this.filters = {};
118-
this.activeFilterableColumns = [];
119-
this.hasActiveFilters = false;
137+
this.visibleFilterableColumns = [];
120138
const { onFilter } = this.args;
121139
if (onFilter && typeof onFilter === 'function') {
122-
onFilter(this.filters);
140+
onFilter({});
123141
}
124142

125143
this._filtersDropdownToggleElement.focus();
@@ -139,43 +157,48 @@ export default class HdsFilterBar extends Component<HdsFilterBarSignature> {
139157
}
140158

141159
private _triggerFilter(key: string, keyFilter?: HdsFilterBarFilter): void {
142-
this._updateFilter(key, keyFilter);
143-
144-
this.hasActiveFilters = Object.keys(this.filters).length > 0;
160+
const newFilters = this._updateFilter(key, keyFilter);
145161

146162
const { onFilter } = this.args;
147163
if (onFilter && typeof onFilter === 'function') {
148-
onFilter(this.filters);
164+
onFilter(newFilters);
149165
}
150166
}
151167

152-
private _updateFilter(key: string, keyFilter?: HdsFilterBarFilter): void {
168+
private _updateFilter(
169+
key: string,
170+
keyFilter?: HdsFilterBarFilter
171+
): HdsFilterBarFilters {
172+
const { filters } = this.args;
153173
const newFilters = {} as HdsFilterBarFilters;
154-
Object.keys(this.filters).forEach((k) => {
174+
175+
Object.keys(filters).forEach((k) => {
155176
newFilters[k] = JSON.parse(
156-
JSON.stringify(this.filters[k])
177+
JSON.stringify(filters[k])
157178
) as HdsFilterBarFilter;
158179
});
159180
if (
160181
keyFilter === undefined ||
161182
(Array.isArray(keyFilter) && keyFilter.length === 0)
162183
) {
163184
delete newFilters[key];
164-
this.activeFilterableColumns = this.activeFilterableColumns.filter(
185+
this.visibleFilterableColumns = this.visibleFilterableColumns.filter(
165186
(colKey) => colKey !== key
166187
);
167188
// Focus back on the filters dropdown toggle after removing a filter
168189
this._filtersDropdownToggleElement.focus();
169190
} else {
170191
Object.assign(newFilters, { [key]: keyFilter });
171192
}
172-
this.filters = { ...newFilters };
193+
194+
return { ...newFilters };
173195
}
174196

175197
private _triggerDropdownOpen(key: string): void {
176198
const dropdownElement = this._element.querySelector(
177-
`.hds-dropdown[data-filter-key="${key}"]`
199+
`.hds-filter-bar__dropdown[data-filter-key="${key}"]`
178200
) as HTMLElement;
201+
console.log('Triggering dropdown open for key:', key, dropdownElement);
179202

180203
if (dropdownElement) {
181204
const toggleButton = dropdownElement.querySelector(

0 commit comments

Comments
 (0)