Skip to content

Commit

Permalink
fix table arrow nav
Browse files Browse the repository at this point in the history
  • Loading branch information
shleewhite committed Oct 1, 2024
1 parent 7eb2811 commit 572a72a
Show file tree
Hide file tree
Showing 11 changed files with 139 additions and 270 deletions.
120 changes: 120 additions & 0 deletions packages/components/src/components/hds/table/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// 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 { HdsTableTdSignature } from './td';
import type { HdsTableThSignature } from './th';

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

if (currentRow?.parentElement?.tagName === '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?.tagName === 'TBODY') {
const table = currentRow.parentElement.parentElement;
const thead = table?.querySelector('thead');
const tbody = table?.querySelector('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) => {
newCell.setAttribute('tabindex', '0');
newCell.classList.add('hds-table__td--gridcell-active');

oldCell.setAttribute('tabindex', '-1');
oldCell.classList.remove('hds-table__td--gridcell-active');

newCell.focus();
};

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 > 1) {
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 =
key === 'ArrowDown'
? currentRow.nextElementSibling
: currentRow.previousElementSibling;

if (nextRow !== null && nextRow instanceof HTMLElement) {
const nextCell = nextRow.children[currentCellIndex];
if (nextCell instanceof HTMLElement) {
changeActiveCell(target, nextCell);
}
} else {
if (currentRow?.parentElement?.tagName === 'TBODY') {
const thead = currentRow?.parentElement?.previousElementSibling;
const lastTheadRow = thead?.querySelector('tr:last-child');
const theadCell = lastTheadRow?.children[currentCellIndex];
if (key === 'ArrowUp' && theadCell instanceof HTMLElement) {
changeActiveCell(target, theadCell);
}
} else if (currentRow?.parentElement?.tagName === 'THEAD') {
const tbody = currentRow?.parentElement?.nextElementSibling;
const firstTbodyRow = tbody?.querySelector('tr:first-child');
const tbodyCell = firstTbodyRow?.children[currentCellIndex];
if (key === 'ArrowDown' && tbodyCell instanceof HTMLElement) {
changeActiveCell(target, tbodyCell);
}
}
}
}
}
}
};
58 changes: 6 additions & 52 deletions packages/components/src/components/hds/table/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
@didInsertCheckbox={{this.didInsertSelectAllCheckbox}}
@willDestroy={{this.willDestroySelectAllCheckbox}}
@selectionAriaLabelSuffix="all rows"
@didInsertGridcell={{this.didInsertGridCell}}
@onKeyPress={{if @isGrid this.handleGridCellKeyPress}}
>
{{#each @columns as |column|}}
{{#if column.isSortable}}
Expand All @@ -33,8 +31,6 @@
@width={{column.width}}
@tooltip={{column.tooltip}}
@isGrid={{@isGrid}}
@didInsert={{this.didInsertGridCell}}
@onKeyPress={{if @isGrid this.handleGridCellKeyPress}}
>
{{column.label}}
</Hds::Table::ThSort>
Expand All @@ -45,8 +41,6 @@
@tooltip={{column.tooltip}}
@isVisuallyHidden={{column.isVisuallyHidden}}
@isGrid={{@isGrid}}
@didInsert={{this.didInsertGridCell}}
@onKeyPress={{if @isGrid this.handleGridCellKeyPress}}
>{{column.label}}</Hds::Table::Th>
{{/if}}
{{/each}}
Expand All @@ -62,23 +56,11 @@
didInsertCheckbox=this.didInsertSelectAllCheckbox
willDestroy=this.willDestroySelectAllCheckbox
selectionAriaLabelSuffix="all rows"
didInsertGridcell=this.didInsertGridCell
onKeyPress=(if @isGrid this.handleGridCellKeyPress)
onClickSortBySelected=(if @selectableColumnKey (fn this.setSortBy @selectableColumnKey))
sortBySelectedOrder=(if (eq this.sortBy @selectableColumnKey) this.sortOrder)
)
Th=(component
"hds/table/th"
isGrid=@isGrid
didInsert=this.didInsertGridCell
onKeyPress=(if @isGrid this.handleGridCellKeyPress)
)
ThSort=(component
"hds/table/th-sort"
isGrid=@isGrid
didInsert=this.didInsertGridCell
onKeyPress=(if @isGrid this.handleGridCellKeyPress)
)
Th=(component "hds/table/th" isGrid=@isGrid)
ThSort=(component "hds/table/th-sort" isGrid=@isGrid)
sortBy=this.sortBy
sortOrder=this.sortOrder
setSortBy=this.setSortBy
Expand Down Expand Up @@ -106,23 +88,9 @@
didInsertCheckbox=this.didInsertRowCheckbox
willDestroy=this.willDestroyRowCheckbox
selectionAriaLabelSuffix=@selectionAriaLabelSuffix
didInsertGridcell=this.didInsertGridCell
onKeyPress=(if @isGrid this.handleGridCellKeyPress)
)
Th=(component
"hds/table/th"
scope="row"
isGrid=@isGrid
didInsert=this.didInsertGridCell
onKeyPress=(if @isGrid this.handleGridCellKeyPress)
)
Td=(component
"hds/table/td"
align=@align
isGrid=@isGrid
didInsert=this.didInsertGridCell
onKeyPress=(if @isGrid this.handleGridCellKeyPress)
)
Th=(component "hds/table/th" scope="row" isGrid=@isGrid)
Td=(component "hds/table/td" align=@align isGrid=@isGrid)
data=record
)
to="body"
Expand All @@ -139,23 +107,9 @@
didInsertCheckbox=this.didInsertRowCheckbox
willDestroy=this.willDestroyRowCheckbox
selectionAriaLabelSuffix=@selectionAriaLabelSuffix
didInsertGridcell=this.didInsertGridCell
onKeyPress=(if @isGrid this.handleGridCellKeyPress)
)
Th=(component
"hds/table/th"
scope="row"
isGrid=@isGrid
didInsert=this.didInsertGridCell
onKeyPress=(if @isGrid this.handleGridCellKeyPress)
)
Td=(component
"hds/table/td"
align=@align
isGrid=@isGrid
didInsert=this.didInsertGridCell
onKeyPress=(if @isGrid this.handleGridCellKeyPress)
)
Th=(component "hds/table/th" scope="row" isGrid=@isGrid)
Td=(component "hds/table/td" align=@align isGrid=@isGrid)
sortBy=this.sortBy
sortOrder=this.sortOrder
)
Expand Down
151 changes: 0 additions & 151 deletions packages/components/src/components/hds/table/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,157 +192,6 @@ export default class HdsTable extends Component<HdsTableSignature> {
return classes.join(' ');
}

// TODO
// did insert
// add escape listener to focusable elements inside cell
// check if it is the first cell and add tabindex 0
// 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.

didInsertGridCell(
cell: HdsTableThSignature['Element'] | HdsTableTdSignature['Element'],
isGrid: boolean
): void {
if (isGrid) {
const currentRow = cell.parentElement;

if (currentRow?.parentElement?.tagName === '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?.tagName === 'TBODY') {
const table = currentRow.parentElement.parentElement;
const thead = table?.querySelector('thead');
const tbody = table?.querySelector('tbody');

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

@action
handleGridCellKeyPress(event: KeyboardEvent): void {
const { key, target } = event;

const changeActiveCell = (oldCell: HTMLElement, newCell: HTMLElement) => {
newCell.setAttribute('tabindex', '0');
newCell.classList.add('hds-table__td--gridcell-active');

oldCell.setAttribute('tabindex', '-1');
oldCell.classList.remove('hds-table__td--gridcell-active');

newCell.focus();
};

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 > 1) {
const element = focusableElements[0] as HTMLElement;
element.focus();
}
} else if (event.key === 'ArrowRight' || event.key === 'ArrowLeft') {
const nextElement =
key === 'ArrowRight'
? target.nextElementSibling
: target.previousElementSibling;

console.log('hello');

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 =
key === 'ArrowDown'
? currentRow.nextElementSibling
: currentRow.previousElementSibling;
if (nextRow !== null && nextRow instanceof HTMLElement) {
const nextCell = nextRow.children[currentCellIndex];
if (nextCell instanceof HTMLElement) {
changeActiveCell(target, nextCell);
}
} else {
if (currentRow?.parentElement?.tagName === 'TBODY') {
const thead = currentRow?.parentElement?.previousElementSibling;
const lastTheadRow = thead?.querySelector('tr:last-child');
const theadCell = lastTheadRow?.children[currentCellIndex];
if (theadCell instanceof HTMLElement) {
changeActiveCell(target, theadCell);
}
} else if (currentRow?.parentElement?.tagName === 'THEAD') {
const tbody = currentRow?.parentElement?.nextElementSibling;
const firstTbodyRow = tbody?.querySelector('tr:first-child');
const tbodyCell = firstTbodyRow?.children[currentCellIndex];
if (tbodyCell instanceof HTMLElement) {
changeActiveCell(target, tbodyCell);
}
}
}
}
}
}
}

// onMoveHorizontal(event: KeyboardEvent): void {
// event.preventDefault();
// console.log('hello');

// const { target, key } = event;

// }

// onMoveVertical(event: KeyboardEvent): void {
// event.preventDefault();

// const { target, key } = event;
// const changeActiveCell = (oldCell: HTMLElement, newCell: HTMLElement) => {
// newCell.setAttribute('tabindex', '0');
// newCell.classList.add('hds-table__td--gridcell-active');

// oldCell.setAttribute('tabindex', '-1');
// oldCell.classList.remove('hds-table__td--gridcell-active');

// newCell.focus();
// };

// if (target instanceof HTMLElement) {

// }
// }

@action
setSortBy(column: string): void {
if (this.sortBy === column) {
Expand Down
7 changes: 1 addition & 6 deletions packages/components/src/components/hds/table/td.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: MPL-2.0
}}
<td
class={{this.classNames}}
...attributes
role={{if @isGrid "gridcell"}}
tabindex={{if @isGrid (if @isActiveGridCell 0 -1)}}
>
<td class={{this.classNames}} ...attributes role={{if @isGrid "gridcell"}} {{did-insert this.didInsert}}>
{{yield}}
</td>
Loading

0 comments on commit 572a72a

Please sign in to comment.