Skip to content

Commit

Permalink
fix(tabs): handle tab close events that remove the associated tab-tit…
Browse files Browse the repository at this point in the history
…le and tab elements from the DOM (#9768)

**Related Issue:** #7155

## Summary

This issue resolves some issues that happen when corresponding
`calcite-tab` and `calcite-tab-title` elements are removed from the DOM
when they are closed.
  • Loading branch information
eriklharper authored and github-actions[bot] committed Jul 30, 2024
1 parent 85fbd9c commit bda619c
Show file tree
Hide file tree
Showing 8 changed files with 288 additions and 216 deletions.
66 changes: 26 additions & 40 deletions packages/calcite-components/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -983,7 +983,7 @@ export namespace Components {
"status": Status;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity": MutableValidityState;
Expand Down Expand Up @@ -1347,7 +1347,7 @@ export namespace Components {
"validationMessage": string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity": MutableValidityState;
Expand Down Expand Up @@ -2282,7 +2282,7 @@ export namespace Components {
"validationMessage": string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity": MutableValidityState;
Expand Down Expand Up @@ -2405,7 +2405,7 @@ export namespace Components {
"validationMessage": string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity": MutableValidityState;
Expand Down Expand Up @@ -2600,7 +2600,7 @@ export namespace Components {
"validationMessage": string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity": MutableValidityState;
Expand Down Expand Up @@ -2741,7 +2741,7 @@ export namespace Components {
"validationMessage": string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity": MutableValidityState;
Expand Down Expand Up @@ -2841,7 +2841,7 @@ export namespace Components {
"validationMessage": string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity": MutableValidityState;
Expand Down Expand Up @@ -2929,7 +2929,7 @@ export namespace Components {
"validationMessage": string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity": MutableValidityState;
Expand Down Expand Up @@ -4225,7 +4225,7 @@ export namespace Components {
"validationMessage": string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity": MutableValidityState;
Expand Down Expand Up @@ -4320,7 +4320,7 @@ export namespace Components {
"validationMessage": string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity": MutableValidityState;
Expand Down Expand Up @@ -5230,7 +5230,7 @@ export namespace Components {
"placeholder": string;
/**
* When `true`, the component's `value` can be read, but cannot be modified.
* @readonly
* @readonly
* @mdn [readOnly](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly)
*/
"readOnly": boolean;
Expand Down Expand Up @@ -5274,7 +5274,7 @@ export namespace Components {
"validationMessage": string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity": MutableValidityState;
Expand Down Expand Up @@ -6056,10 +6056,6 @@ export interface CalciteSwitchCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLCalciteSwitchElement;
}
export interface CalciteTabCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLCalciteTabElement;
}
export interface CalciteTabNavCustomEvent<T> extends CustomEvent<T> {
detail: T;
target: HTMLCalciteTabNavElement;
Expand Down Expand Up @@ -7496,25 +7492,15 @@ declare global {
prototype: HTMLCalciteSwitchElement;
new (): HTMLCalciteSwitchElement;
};
interface HTMLCalciteTabElementEventMap {
"calciteInternalTabRegister": void;
}
interface HTMLCalciteTabElement extends Components.CalciteTab, HTMLStencilElement {
addEventListener<K extends keyof HTMLCalciteTabElementEventMap>(type: K, listener: (this: HTMLCalciteTabElement, ev: CalciteTabCustomEvent<HTMLCalciteTabElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
removeEventListener<K extends keyof HTMLCalciteTabElementEventMap>(type: K, listener: (this: HTMLCalciteTabElement, ev: CalciteTabCustomEvent<HTMLCalciteTabElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
}
var HTMLCalciteTabElement: {
prototype: HTMLCalciteTabElement;
new (): HTMLCalciteTabElement;
};
interface HTMLCalciteTabNavElementEventMap {
"calciteTabChange": void;
"calciteInternalTabNavSlotChange": Element[];
"calciteInternalTabChange": TabChangeEventDetail;
}
interface HTMLCalciteTabNavElement extends Components.CalciteTabNav, HTMLStencilElement {
Expand Down Expand Up @@ -8809,7 +8795,7 @@ declare namespace LocalJSX {
"status"?: Status;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity"?: MutableValidityState;
Expand Down Expand Up @@ -9202,7 +9188,7 @@ declare namespace LocalJSX {
"validationMessage"?: string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity"?: MutableValidityState;
Expand Down Expand Up @@ -10178,7 +10164,7 @@ declare namespace LocalJSX {
"validationMessage"?: string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity"?: MutableValidityState;
Expand Down Expand Up @@ -10312,7 +10298,7 @@ declare namespace LocalJSX {
"validationMessage"?: string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity"?: MutableValidityState;
Expand Down Expand Up @@ -10509,7 +10495,7 @@ declare namespace LocalJSX {
"validationMessage"?: string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity"?: MutableValidityState;
Expand Down Expand Up @@ -10655,7 +10641,7 @@ declare namespace LocalJSX {
"validationMessage"?: string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity"?: MutableValidityState;
Expand Down Expand Up @@ -10766,7 +10752,7 @@ declare namespace LocalJSX {
"validationMessage"?: string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity"?: MutableValidityState;
Expand Down Expand Up @@ -10873,7 +10859,7 @@ declare namespace LocalJSX {
"validationMessage"?: string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity"?: MutableValidityState;
Expand Down Expand Up @@ -12232,7 +12218,7 @@ declare namespace LocalJSX {
"validationMessage"?: string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity"?: MutableValidityState;
Expand Down Expand Up @@ -12331,7 +12317,7 @@ declare namespace LocalJSX {
"validationMessage"?: string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity"?: MutableValidityState;
Expand Down Expand Up @@ -12875,7 +12861,6 @@ declare namespace LocalJSX {
"value"?: any;
}
interface CalciteTab {
"onCalciteInternalTabRegister"?: (event: CalciteTabCustomEvent<void>) => void;
/**
* Specifies the size of the component inherited from the parent `calcite-tabs`, defaults to `m`.
*/
Expand Down Expand Up @@ -12903,6 +12888,7 @@ declare namespace LocalJSX {
*/
"messages"?: TabNavMessages;
"onCalciteInternalTabChange"?: (event: CalciteTabNavCustomEvent<TabChangeEventDetail>) => void;
"onCalciteInternalTabNavSlotChange"?: (event: CalciteTabNavCustomEvent<Element[]>) => void;
/**
* Emits when the selected `calcite-tab` changes.
*/
Expand Down Expand Up @@ -13278,7 +13264,7 @@ declare namespace LocalJSX {
"placeholder"?: string;
/**
* When `true`, the component's `value` can be read, but cannot be modified.
* @readonly
* @readonly
* @mdn [readOnly](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly)
*/
"readOnly"?: boolean;
Expand Down Expand Up @@ -13314,7 +13300,7 @@ declare namespace LocalJSX {
"validationMessage"?: string;
/**
* The current validation state of the component.
* @readonly
* @readonly
* @mdn [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState)
*/
"validity"?: MutableValidityState;
Expand Down
62 changes: 36 additions & 26 deletions packages/calcite-components/src/components/tab-nav/tab-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
focusElementInGroup,
FocusElementInGroupDestination,
getElementDir,
slotChangeGetAssignedElements,
} from "../../utils/dom";
import { createObserver } from "../../utils/observers";
import { Scale } from "../interfaces";
Expand Down Expand Up @@ -73,6 +74,14 @@ export class TabNav implements LocalizedComponent, T9nComponent {
*/
@Prop({ mutable: true }) selectedTitle: HTMLCalciteTabTitleElement = null;

@Watch("selectedTitle")
selectedTitleChanged(): void {
this.calciteInternalTabChange.emit({
tab: this.selectedTabId,
});
this.updateActiveIndicator();
}

/**
* Specifies the size of the component inherited from the parent `calcite-tabs`, defaults to `m`.
*
Expand Down Expand Up @@ -126,29 +135,6 @@ export class TabNav implements LocalizedComponent, T9nComponent {
/* wired up by t9n util */
}

@Watch("selectedTabId")
async selectedTabIdChanged(): Promise<void> {
if (
localStorage &&
this.storageId &&
this.selectedTabId !== undefined &&
this.selectedTabId !== null
) {
localStorage.setItem(`calcite-tab-nav-${this.storageId}`, JSON.stringify(this.selectedTabId));
}

this.calciteInternalTabChange.emit({
tab: this.selectedTabId,
});

this.selectedTitle = await this.getTabTitleById(this.selectedTabId);
}

@Watch("selectedTitle")
selectedTitleChanged(): void {
this.updateActiveIndicator();
}

//--------------------------------------------------------------------------
//
// Lifecycle
Expand Down Expand Up @@ -297,6 +283,7 @@ export class TabNav implements LocalizedComponent, T9nComponent {
: this.getIndexOfTabTitle(activatedTabTitle);
event.stopPropagation();

this.selectedTitle = activatedTabTitle;
this.scrollTabTitleIntoView(activatedTabTitle);
}

Expand Down Expand Up @@ -360,9 +347,10 @@ export class TabNav implements LocalizedComponent, T9nComponent {
* @param event
*/
@Listen("calciteInternalTabTitleRegister")
updateTabTitles(event: CustomEvent<TabID>): void {
async updateTabTitles(event: CustomEvent<TabID>): Promise<void> {
if ((event.target as HTMLCalciteTabTitleElement).selected) {
this.selectedTabId = event.detail;
this.selectedTitle = await this.getTabTitleById(this.selectedTabId);
}
}

Expand Down Expand Up @@ -395,6 +383,11 @@ export class TabNav implements LocalizedComponent, T9nComponent {
*/
@Event({ cancelable: false }) calciteTabChange: EventEmitter<void>;

/**
* @internal
*/
@Event() calciteInternalTabNavSlotChange: EventEmitter<Element[]>;

/**
* @internal
*/
Expand Down Expand Up @@ -423,6 +416,22 @@ export class TabNav implements LocalizedComponent, T9nComponent {

@State() private selectedTabId: TabID;

@Watch("selectedTabId")
async selectedTabIdChanged(): Promise<void> {
if (
localStorage &&
this.storageId &&
this.selectedTabId !== undefined &&
this.selectedTabId !== null
) {
localStorage.setItem(`calcite-tab-nav-${this.storageId}`, JSON.stringify(this.selectedTabId));
}

this.calciteInternalTabChange.emit({
tab: this.selectedTabId,
});
}

private activeIndicatorEl: HTMLElement;

private activeIndicatorContainerEl: HTMLDivElement;
Expand Down Expand Up @@ -501,10 +510,11 @@ export class TabNav implements LocalizedComponent, T9nComponent {
private onSlotChange = (event: Event): void => {
this.intersectionObserver?.disconnect();

const slottedChildren = (event.target as HTMLSlotElement).assignedElements();
slottedChildren.forEach((child) => {
const slottedElements = slotChangeGetAssignedElements(event, "calcite-tab-title");
slottedElements.forEach((child) => {
this.intersectionObserver?.observe(child);
});
this.calciteInternalTabNavSlotChange.emit(slottedElements);
};

private storeContainerRef = (el: HTMLDivElement) => (this.containerEl = el);
Expand Down
Loading

0 comments on commit bda619c

Please sign in to comment.