fix(tabs): remove previously selected tab property

PiperOrigin-RevId: 563797711
This commit is contained in:
Elizabeth Mitchell 2023-09-08 10:58:13 -07:00 committed by Copybara-Service
parent 2468241157
commit 70ce0d2779
3 changed files with 17 additions and 25 deletions

View File

@ -8,25 +8,25 @@ import '../../elevation/elevation.js';
import '../../focus/md-focus-ring.js'; import '../../focus/md-focus-ring.js';
import '../../ripple/ripple.js'; import '../../ripple/ripple.js';
import {html, isServer, LitElement, nothing, PropertyValues} from 'lit'; import {html, isServer, LitElement, nothing} from 'lit';
import {property, query, queryAssignedElements, queryAssignedNodes, state} from 'lit/decorators.js'; import {property, query, queryAssignedElements, queryAssignedNodes, state} from 'lit/decorators.js';
import {classMap} from 'lit/directives/class-map.js'; import {classMap} from 'lit/directives/class-map.js';
import {polyfillElementInternalsAria, setupHostAria} from '../../internal/aria/aria.js'; import {polyfillElementInternalsAria, setupHostAria} from '../../internal/aria/aria.js';
import {EASING} from '../../internal/motion/animation.js'; import {EASING} from '../../internal/motion/animation.js';
interface Tabs extends HTMLElement {
selected?: number;
selectedItem?: Tab;
previousSelectedItem?: Tab;
}
/** /**
* Symbol for tabs to use to animate their indicators based off another tab's * Symbol for tabs to use to animate their indicators based off another tab's
* indicator. * indicator.
*/ */
const INDICATOR = Symbol('indicator'); const INDICATOR = Symbol('indicator');
/**
* Symbol used by the tab bar to request a tab to animate its indicator from a
* previously selected tab.
*/
export const ANIMATE_INDICATOR = Symbol('animateIndicator');
/** /**
* Tab component. * Tab component.
*/ */
@ -104,11 +104,8 @@ export class Tab extends LitElement {
}; };
} }
protected override updated(changed: PropertyValues) { protected override updated() {
if (changed.has('active')) { this.internals.ariaSelected = String(this.active);
this.internals.ariaSelected = String(this.active);
this.animateSelected();
}
} }
private async handleKeydown(event: KeyboardEvent) { private async handleKeydown(event: KeyboardEvent) {
@ -125,7 +122,7 @@ export class Tab extends LitElement {
} }
} }
private animateSelected() { [ANIMATE_INDICATOR](previousTab: Tab) {
if (!this[INDICATOR]) { if (!this[INDICATOR]) {
return; return;
} }
@ -133,25 +130,22 @@ export class Tab extends LitElement {
this[INDICATOR].getAnimations().forEach(a => { this[INDICATOR].getAnimations().forEach(a => {
a.cancel(); a.cancel();
}); });
const frames = this.getKeyframes(); const frames = this.getKeyframes(previousTab);
if (frames !== null) { if (frames !== null) {
this[INDICATOR].animate( this[INDICATOR].animate(
frames, {duration: 250, easing: EASING.EMPHASIZED}); frames, {duration: 250, easing: EASING.EMPHASIZED});
} }
} }
private getKeyframes() { private getKeyframes(previousTab: Tab) {
const reduceMotion = shouldReduceMotion(); const reduceMotion = shouldReduceMotion();
if (!this.active) { if (!this.active) {
return reduceMotion ? [{'opacity': 1}, {'transform': 'none'}] : null; return reduceMotion ? [{'opacity': 1}, {'transform': 'none'}] : null;
} }
// TODO(b/298105040): avoid hardcoding selector
const tabs = this.closest<Tabs>('md-tabs');
const from: Keyframe = {}; const from: Keyframe = {};
const fromRect = const fromRect =
(tabs?.previousSelectedItem?.[INDICATOR]?.getBoundingClientRect() ?? previousTab[INDICATOR]?.getBoundingClientRect() ?? ({} as DOMRect);
({} as DOMRect));
const fromPos = fromRect.left; const fromPos = fromRect.left;
const fromExtent = fromRect.width; const fromExtent = fromRect.width;
const toRect = this[INDICATOR]!.getBoundingClientRect(); const toRect = this[INDICATOR]!.getBoundingClientRect();

View File

@ -11,7 +11,7 @@ import {property, queryAssignedElements, state} from 'lit/decorators.js';
import {polyfillElementInternalsAria, setupHostAria} from '../../internal/aria/aria.js'; import {polyfillElementInternalsAria, setupHostAria} from '../../internal/aria/aria.js';
import {Tab} from './tab.js'; import {ANIMATE_INDICATOR, Tab} from './tab.js';
const NAVIGATION_KEYS = new Map([ const NAVIGATION_KEYS = new Map([
['default', new Set(['Home', 'End'])], ['default', new Set(['Home', 'End'])],
@ -85,7 +85,7 @@ export class Tabs extends LitElement {
/** /**
* The item previously selected. * The item previously selected.
*/ */
get previousSelectedItem() { private get previousSelectedItem() {
return this.tabs[this.previousSelected]; return this.tabs[this.previousSelected];
} }
@ -142,6 +142,7 @@ export class Tabs extends LitElement {
this.previousSelectedItem !== this.selectedItem) { this.previousSelectedItem !== this.selectedItem) {
this.previousSelectedItem.active = false; this.previousSelectedItem.active = false;
this.selectedItem.active = true; this.selectedItem.active = true;
this.selectedItem[ANIMATE_INDICATOR](this.previousSelectedItem);
} }
if (this.selectedItem !== this.focusedItem) { if (this.selectedItem !== this.focusedItem) {
this.updateFocusableItem(this.selectedItem); this.updateFocusableItem(this.selectedItem);

View File

@ -70,17 +70,14 @@ describe('<md-tabs>', () => {
}); });
}); });
it('updates selectedItem/previousSelectedItem', async () => { it('updates selectedItem', async () => {
const {harness} = await setupTest({selected: 1}); const {harness} = await setupTest({selected: 1});
expect(harness.element.selectedItem) expect(harness.element.selectedItem)
.toBe(harness.harnessedItems[1].element); .toBe(harness.harnessedItems[1].element);
expect(harness.element.previousSelectedItem).toBeUndefined();
harness.element.selected = 0; harness.element.selected = 0;
await harness.element.updateComplete; await harness.element.updateComplete;
expect(harness.element.selectedItem) expect(harness.element.selectedItem)
.toBe(harness.harnessedItems[0].element); .toBe(harness.harnessedItems[0].element);
expect(harness.element.previousSelectedItem)
.toBe(harness.harnessedItems[1].element);
}); });
it('maintains selection when tabs are mutated', async () => { it('maintains selection when tabs are mutated', async () => {