feat(tabs): add tabs property to retrieve tab elements

PiperOrigin-RevId: 563784477
This commit is contained in:
Elizabeth Mitchell 2023-09-08 10:12:58 -07:00 committed by Copybara-Service
parent 23b291b2dd
commit bf48fc307e
2 changed files with 21 additions and 18 deletions

View File

@ -53,9 +53,8 @@ export class TabsHarness extends Harness<Tabs> {
get harnessedItems() { get harnessedItems() {
// Test access to protected property // Test access to protected property
// tslint:disable-next-line:no-dict-access-on-struct-type // tslint:disable-next-line:no-dict-access-on-struct-type
return (this.element['items'] as Array<ElementWithHarness<Tab>>) return (this.element.tabs as Array<ElementWithHarness<Tab>>).map(item => {
.map(item => { return (item.harness ?? new TabHarness(item)) as TabHarness;
return (item.harness ?? new TabHarness(item)) as TabHarness; });
});
} }
} }

View File

@ -47,6 +47,13 @@ export class Tabs extends LitElement {
setupHostAria(Tabs, {focusable: false}); setupHostAria(Tabs, {focusable: false});
} }
/**
* The tabs of this tab bar.
*/
get tabs() {
return this.maybeTabItems.filter(isTab);
}
/** /**
* Index of the selected item. * Index of the selected item.
*/ */
@ -63,9 +70,6 @@ export class Tabs extends LitElement {
@queryAssignedElements({flatten: true}) @queryAssignedElements({flatten: true})
private readonly maybeTabItems!: HTMLElement[]; private readonly maybeTabItems!: HTMLElement[];
private get items(): Tab[] {
return this.maybeTabItems.filter(isTab);
}
// this tracks if items have changed, which triggers rendering so they can // this tracks if items have changed, which triggers rendering so they can
// be kept in sync // be kept in sync
@ -75,21 +79,21 @@ export class Tabs extends LitElement {
* The item currently selected. * The item currently selected.
*/ */
get selectedItem() { get selectedItem() {
return this.items[this.selected]; return this.tabs[this.selected];
} }
/** /**
* The item previously selected. * The item previously selected.
*/ */
get previousSelectedItem() { get previousSelectedItem() {
return this.items[this.previousSelected]; return this.tabs[this.previousSelected];
} }
/** /**
* The item currently focused. * The item currently focused.
*/ */
private get focusedItem() { private get focusedItem() {
return this.items.find((el: HTMLElement) => el.matches(':focus-within')); return this.tabs.find((el: HTMLElement) => el.matches(':focus-within'));
} }
private readonly internals = polyfillElementInternalsAria( private readonly internals = polyfillElementInternalsAria(
@ -116,19 +120,19 @@ export class Tabs extends LitElement {
} }
let indexToFocus = -1; let indexToFocus = -1;
const focused = this.focusedItem ?? this.selectedItem; const focused = this.focusedItem ?? this.selectedItem;
const itemCount = this.items.length; const itemCount = this.tabs.length;
const isPrevKey = key === 'ArrowLeft' || key === 'ArrowUp'; const isPrevKey = key === 'ArrowLeft' || key === 'ArrowUp';
if (key === 'Home') { if (key === 'Home') {
indexToFocus = 0; indexToFocus = 0;
} else if (key === 'End') { } else if (key === 'End') {
indexToFocus = itemCount - 1; indexToFocus = itemCount - 1;
} else { } else {
const focusedIndex = this.items.indexOf(focused) || 0; const focusedIndex = this.tabs.indexOf(focused) || 0;
indexToFocus = focusedIndex + (isPrevKey ? -1 : 1); indexToFocus = focusedIndex + (isPrevKey ? -1 : 1);
indexToFocus = indexToFocus =
indexToFocus < 0 ? itemCount - 1 : indexToFocus % itemCount; indexToFocus < 0 ? itemCount - 1 : indexToFocus % itemCount;
} }
const itemToFocus = this.items[indexToFocus]; const itemToFocus = this.tabs[indexToFocus];
if (itemToFocus !== null && itemToFocus !== focused) { if (itemToFocus !== null && itemToFocus !== focused) {
this.updateFocusableItem(itemToFocus); this.updateFocusableItem(itemToFocus);
itemToFocus.focus(); itemToFocus.focus();
@ -150,7 +154,7 @@ export class Tabs extends LitElement {
const nowFocused = const nowFocused =
(this.getRootNode() as unknown as DocumentOrShadowRoot).activeElement as (this.getRootNode() as unknown as DocumentOrShadowRoot).activeElement as
Tab; Tab;
if (this.items.indexOf(nowFocused) === -1) { if (this.tabs.indexOf(nowFocused) === -1) {
this.updateFocusableItem(this.selectedItem); this.updateFocusableItem(this.selectedItem);
} }
}; };
@ -209,7 +213,7 @@ export class Tabs extends LitElement {
const itemsChanged = changed.has('itemsDirty'); const itemsChanged = changed.has('itemsDirty');
// sync state with items. // sync state with items.
if (itemsChanged) { if (itemsChanged) {
this.items.forEach((item, i) => { this.tabs.forEach((item, i) => {
item.active = this.selected === i; item.active = this.selected === i;
}); });
} }
@ -227,7 +231,7 @@ export class Tabs extends LitElement {
} }
private updateFocusableItem(focusableItem: HTMLElement|null) { private updateFocusableItem(focusableItem: HTMLElement|null) {
for (const item of this.items) { for (const item of this.tabs) {
item.tabIndex = item === focusableItem ? 0 : -1; item.tabIndex = item === focusableItem ? 0 : -1;
} }
} }
@ -248,7 +252,7 @@ export class Tabs extends LitElement {
return; return;
} }
const item = (target as Element).closest(`${this.localName} > *`) as Tab; const item = (target as Element).closest(`${this.localName} > *`) as Tab;
const i = this.items.indexOf(item); const i = this.tabs.indexOf(item);
if (i > -1 && this.selected !== i) { if (i > -1 && this.selected !== i) {
this.selected = i; this.selected = i;
this.updateFocusableItem(this.selectedItem); this.updateFocusableItem(this.selectedItem);
@ -264,7 +268,7 @@ export class Tabs extends LitElement {
} }
private async itemsUpdateComplete() { private async itemsUpdateComplete() {
for (const item of this.items) { for (const item of this.tabs) {
await item.updateComplete; await item.updateComplete;
} }
return true; return true;