Skip to content
Merged
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
59 changes: 35 additions & 24 deletions tabs/tab.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,25 +93,34 @@ export class Tab extends LitElement {

render() {
const indicator = html`<div class="indicator"></div>`
const content = html`
<md-focus-ring part="focus-ring" inward .control=${this}></md-focus-ring>
<md-elevation part="elevation"></md-elevation>
<md-ripple .control=${this}></md-ripple>
<div class="content ${classMap(this.getContentClasses())}" role="presentation">
<slot name="icon" @slotchange=${this.handleIconSlotChange}></slot>
<slot @slotchange=${this.handleSlotChange}></slot>
${this.fullWidthIndicator ? nothing : indicator}
</div>
${this.fullWidthIndicator ? indicator : nothing}
`
return html` <div class="wrapper ${this.type}">
${
this.href
? html`<a class="button" href=${this.href} target=${this.target || nothing} tabindex="-1">${content}</a>`
: html`<div class="button" role="presentation" @click=${this.handleContentClick}>${content}</div>`
}
<div class="button" role="presentation" @click=${this.handleContentClick}>
<md-focus-ring part="focus-ring" inward .control=${this}></md-focus-ring>
<md-elevation part="elevation"></md-elevation>
<md-ripple .control=${this}></md-ripple>
<div class="content ${classMap(this.getContentClasses())}" role="presentation">
<slot name="icon" @slotchange=${this.handleIconSlotChange}></slot>
<slot @slotchange=${this.handleSlotChange}></slot>
${this.fullWidthIndicator ? nothing : indicator}
</div>
${this.fullWidthIndicator ? indicator : nothing}
${this.href ? this.renderLink() : nothing}
</div>
</div>`
}

renderLink() {
const { ariaLabel } = this
return html`
<a
class="link"
id="link"
href=${this.href}
target=${this.target || nothing}
tabindex="-1"
aria-label=${ariaLabel || nothing}></a>
`
}
getContentClasses() {
let cc = {
'has-icon': this.hasIcon,
Expand Down Expand Up @@ -145,14 +154,17 @@ export class Tab extends LitElement {
// Prevent default behavior such as scrolling when pressing spacebar.
event.preventDefault()
if (this.href) {
const link = this.renderRoot.querySelector('a.button')
const link = this.renderRoot.querySelector('a.link')
link?.click()
} else {
this.click()
}
}
}
handleContentClick(event) {
if (this.href) {
return
}
Comment on lines +165 to +167

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 When this.href is present, returning early from handleContentClick means clicking the tab content will not dispatch a click event on the host element (this.click()), nor will activateTab in <md-tabs> be triggered (which listens to @click on the slot). Consequently, clicking an anchor-based tab will navigate to the URL but fail to update activeTabIndex or active states in <md-tabs> when SPA routing or intra-page navigation is used without a full page refresh. Consider allowing the host click or ensuring md-tabs can capture the tab activation.

// Ensure the "click" target is always the tab, and not content, by stopping
// propagation of content clicks and re-clicking the host.
event.stopPropagation()
Expand Down Expand Up @@ -247,14 +259,13 @@ export class Tab extends LitElement {
:host([active]) md-focus-ring {
margin-bottom: calc(var(--_active-indicator-height) + 1px);
}
.button {
display: inline-flex;
position: relative;
align-items: center;
justify-content: center;
text-decoration: none;
color: inherit;
.link {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
outline: none;
z-index: 1;
}
.button::before {
background: var(--_container-color);
Expand Down
Loading