Skip to content
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

SPIKE: DataGrid #2432

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,17 @@
"./components/hds/accordion/index.js": "./dist/_app_/components/hds/accordion/index.js",
"./components/hds/accordion/item/button.js": "./dist/_app_/components/hds/accordion/item/button.js",
"./components/hds/accordion/item/index.js": "./dist/_app_/components/hds/accordion/item/index.js",
"./components/hds/advanced-table/helpers.js": "./dist/_app_/components/hds/advanced-table/helpers.js",
"./components/hds/advanced-table/index.js": "./dist/_app_/components/hds/advanced-table/index.js",
"./components/hds/advanced-table/td.js": "./dist/_app_/components/hds/advanced-table/td.js",
"./components/hds/advanced-table/th-button-expand.js": "./dist/_app_/components/hds/advanced-table/th-button-expand.js",
"./components/hds/advanced-table/th-button-sort.js": "./dist/_app_/components/hds/advanced-table/th-button-sort.js",
"./components/hds/advanced-table/th-button-tooltip.js": "./dist/_app_/components/hds/advanced-table/th-button-tooltip.js",
"./components/hds/advanced-table/th-selectable.js": "./dist/_app_/components/hds/advanced-table/th-selectable.js",
"./components/hds/advanced-table/th-sort.js": "./dist/_app_/components/hds/advanced-table/th-sort.js",
"./components/hds/advanced-table/th.js": "./dist/_app_/components/hds/advanced-table/th.js",
"./components/hds/advanced-table/tr-expandable-group.js": "./dist/_app_/components/hds/advanced-table/tr-expandable-group.js",
"./components/hds/advanced-table/tr.js": "./dist/_app_/components/hds/advanced-table/tr.js",
"./components/hds/alert/description.js": "./dist/_app_/components/hds/alert/description.js",
"./components/hds/alert/index.js": "./dist/_app_/components/hds/alert/index.js",
"./components/hds/alert/title.js": "./dist/_app_/components/hds/alert/title.js",
Expand Down Expand Up @@ -270,6 +281,7 @@
"./components/hds/side-nav/toggle-button.js": "./dist/_app_/components/hds/side-nav/toggle-button.js",
"./components/hds/stepper/step/indicator.js": "./dist/_app_/components/hds/stepper/step/indicator.js",
"./components/hds/stepper/task/indicator.js": "./dist/_app_/components/hds/stepper/task/indicator.js",
"./components/hds/table/helpers.js": "./dist/_app_/components/hds/table/helpers.js",
"./components/hds/table/index.js": "./dist/_app_/components/hds/table/index.js",
"./components/hds/table/td.js": "./dist/_app_/components/hds/table/td.js",
"./components/hds/table/th-button-sort.js": "./dist/_app_/components/hds/table/th-button-sort.js",
Expand Down
13 changes: 13 additions & 0 deletions packages/components/src/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@ export { default as HdsAccordion } from './components/hds/accordion/index.ts';
export { default as HdsAccordionItem } from './components/hds/accordion/item/index.ts';
export * from './components/hds/accordion/types.ts';

// Advanced Table
export { default as HdsAdvancedTable } from './components/hds/advanced-table/index.ts';
export { default as HdsAdvancedTableTd } from './components/hds/advanced-table/td.ts';
export { default as HdsAdvancedTableTh } from './components/hds/advanced-table/th.ts';
export { default as HdsAdvancedTableThButtonExpand } from './components/hds/advanced-table/th-button-expand.ts';
export { default as HdsAdvancedTableThButtonSort } from './components/hds/advanced-table/th-button-sort.ts';
export { default as HdsAdvancedTableThButtonTooltip } from './components/hds/advanced-table/th-button-tooltip.ts';
export { default as HdsAdvancedTableThSelectable } from './components/hds/advanced-table/th-selectable.ts';
export { default as HdsAdvancedTableThSort } from './components/hds/advanced-table/th-sort.ts';
export { default as HdsAdvancedTableTrExpandableGroup } from './components/hds/advanced-table/tr-expandable-group.ts';
export { default as HdsAdvancedTableTr } from './components/hds/advanced-table/tr.ts';
export * from './components/hds/advanced-table/types.ts';

// Alert
export { default as HdsAlert } from './components/hds/alert/index.ts';
export { default as HdsAlertDescription } from './components/hds/alert/description.ts';
Expand Down
122 changes: 122 additions & 0 deletions packages/components/src/components/hds/advanced-table/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// TODO
// did insert
// add escape listener to focusable elements inside cell
// Page Down: Moves focus down an author-determined number of rows, typically scrolling so the bottom row in the currently visible set of rows becomes one of the first visible rows. If focus is in the last row of the grid, focus does not move.
// Page Up: Moves focus up an author-determined number of rows, typically scrolling so the top row in the currently visible set of rows becomes one of the last visible rows. If focus is in the first row of the grid, focus does not move.
// Home: moves focus to the first cell in the row that contains focus.
// End: moves focus to the last cell in the row that contains focus.
// Control + Home: moves focus to the first cell in the first row.
// Control + End: moves focus to the last cell in the last row.

import type { HdsAdvancedTableTdSignature } from './td';
import type { HdsAdvancedTableThSignature } from './th';

export const didInsertGridCell = (
cell:
| HdsAdvancedTableThSignature['Element']
| HdsAdvancedTableTdSignature['Element']
): void => {
const currentRow = cell.parentElement;

if (
currentRow?.parentElement?.classList.contains('hds-advanced-table__thead')
) {
const thead = currentRow.parentElement;
if (
thead.children.item(0) === currentRow &&
currentRow.children.item(0) === cell
) {
cell.setAttribute('tabindex', '0');
} else {
cell.setAttribute('tabindex', '-1');
}
} else if (
currentRow?.parentElement?.classList.contains('hds-advanced-table__tbody')
) {
const table = currentRow.parentElement?.closest('[role="grid"]');
const thead = table?.querySelector(
'[role="rowgroup"].hds-advanced-table__thead'
);
const tbody = table?.querySelector(
'[role="rowgroup"].hds-advanced-table__tbody'
);

if (thead === null) {
if (
tbody?.children.item(0) === currentRow &&
currentRow.children.item(0) === cell
) {
cell.setAttribute('tabindex', '0');
}
} else {
cell.setAttribute('tabindex', '-1');
}
}
};

export const handleGridCellKeyPress = (event: KeyboardEvent): void => {
const { key, target } = event;

const changeActiveCell = (oldCell: HTMLElement, newCell: HTMLElement) => {
oldCell.setAttribute('tabindex', '-1');
newCell.setAttribute('tabindex', '0');
newCell.focus();
};

const findNewRow = (
currentRow: HTMLElement,
direction: 'ArrowDown' | 'ArrowUp'
) => {
const table = currentRow.parentElement?.closest('[role="grid"]');
const allRows = table?.querySelectorAll('[role="row"]');

if (allRows) {
const currentRowIndex = Array.from(allRows).indexOf(currentRow);
if (direction === 'ArrowDown') return allRows[currentRowIndex + 1];
else if (direction === 'ArrowUp') return allRows[currentRowIndex - 1];
}
};

if (target instanceof HTMLElement) {
if (key === 'Enter') {
const focusableElements = target.querySelectorAll(
'button, a[href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);

// if (focusableElements.length === 1) {
// const element = focusableElements[0] as HTMLElement;
// element.click();
// } else

if (focusableElements.length > 0) {
const element = focusableElements[0] as HTMLElement;
element.focus();
}
} else if (event.key === 'ArrowRight' || event.key === 'ArrowLeft') {
const nextElement =
key === 'ArrowRight'
? target.nextElementSibling
: target.previousElementSibling;

if (nextElement !== null && nextElement instanceof HTMLElement) {
changeActiveCell(target, nextElement);
}
} else if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
const currentRow = target.parentElement;

if (currentRow instanceof HTMLElement) {
const currentCellIndex = Array.from(currentRow.children).indexOf(
target
);
const nextRow = findNewRow(currentRow, event.key);

if (nextRow !== null && nextRow instanceof HTMLElement) {
const nextCell = nextRow.children[currentCellIndex];
if (nextCell instanceof HTMLElement) {
changeActiveCell(target, nextCell);
}
}
}
}
}
};
89 changes: 89 additions & 0 deletions packages/components/src/components/hds/advanced-table/index.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}

<div class={{this.classNames}} ...attributes role="grid" {{style grid-template-columns=this.gridTemplateColumns}}>
<div class="hds-advanced-table__thead {{if @hasStickyHeader 'hds-advanced-table__thead--sticky'}}" role="rowgroup">
<Hds::AdvancedTable::Tr
@selectionScope="col"
@onClickSortBySelected={{if @selectableColumnKey (fn this.setSortBy @selectableColumnKey)}}
@sortBySelectedOrder={{if (eq this.sortBy @selectableColumnKey) this.sortOrder}}
@isSelectable={{@isSelectable}}
@onSelectionChange={{this.onSelectionAllChange}}
@didInsertCheckbox={{this.didInsertSelectAllCheckbox}}
@willDestroy={{this.willDestroySelectAllCheckbox}}
@selectionAriaLabelSuffix="all rows"
>
{{#each @columns as |column|}}
{{#if column.isSortable}}
<Hds::AdvancedTable::ThSort
@sortOrder={{if (eq column.key this.sortBy) this.sortOrder}}
@onClickSort={{fn this.setSortBy column.key}}
@align={{column.align}}
@tooltip={{column.tooltip}}
>
{{column.label}}
</Hds::AdvancedTable::ThSort>
{{else}}
<Hds::AdvancedTable::Th
@align={{column.align}}
@tooltip={{column.tooltip}}
@isVisuallyHidden={{column.isVisuallyHidden}}
>{{column.label}}</Hds::AdvancedTable::Th>
{{/if}}
{{/each}}
</Hds::AdvancedTable::Tr>
</div>

<div class="hds-advanced-table__tbody" role="rowgroup">
{{! ----------------------------------------------------------------------------------------
IMPORTANT: we loop on the `model` array and for each record
we yield the Tr/Td/Th elements _and_ the record itself as `data`
this means the consumer will *have to* use the `data` key to access it in their template
-------------------------------------------------------------------------------------------- }}
{{#each (sort-by this.getSortCriteria @model) key=this.identityKey as |record|}}
{{#if @hasNestedRows}}
<Hds::AdvancedTable::TrExpandableGroup @record={{record}} @childrenKey={{@childrenKey}} as |T|>
{{yield
(hash
Tr=(component "hds/advanced-table/tr" depth=T.depth)
Th=(component
"hds/advanced-table/th"
scope="row"
isExpandable=T.isExpandable
newLabel=T.id
parentId=T.parentId
onClickToggle=T.onClickToggle
isExpanded=T.isExpanded
depth=T.depth
)
Td=(component "hds/advanced-table/td" align=@align)
data=T.data
isExpanded=T.isExpanded
)
to="body"
}}
</Hds::AdvancedTable::TrExpandableGroup>
{{else}}
{{yield
(hash
Tr=(component
"hds/advanced-table/tr"
selectionScope="row"
isSelectable=@isSelectable
onSelectionChange=this.onSelectionRowChange
didInsertCheckbox=this.didInsertRowCheckbox
willDestroy=this.willDestroyRowCheckbox
selectionAriaLabelSuffix=@selectionAriaLabelSuffix
)
Th=(component "hds/advanced-table/th" scope="row")
Td=(component "hds/advanced-table/td" align=@align)
data=record
)
to="body"
}}
{{/if}}
{{/each}}
</div>
</div>
Loading
Loading