diff --git a/docs/pages/resources/changelog.md b/docs/pages/resources/changelog.md
index e4dab556..a6e70970 100644
--- a/docs/pages/resources/changelog.md
+++ b/docs/pages/resources/changelog.md
@@ -12,6 +12,10 @@ Components with the Experimental bad
New versions of Shoelace are released as-needed and generally occur when a critical mass of changes have accumulated. At any time, you can see what's coming in the next release by visiting [next.shoelace.style](https://next.shoelace.style).
+## next
+
+- Fixed a bug that prevented `` to be activated properly when rendered in another `` (#2367)
+
## 2.20.0
- Added the ability to set a custom snap function and use `repeat(n)` to `` [#2340]
diff --git a/src/components/tab-group/tab-group.component.ts b/src/components/tab-group/tab-group.component.ts
index 5e7064fd..8a9de2bc 100644
--- a/src/components/tab-group/tab-group.component.ts
+++ b/src/components/tab-group/tab-group.component.ts
@@ -93,17 +93,33 @@ export default class SlTabGroup extends ShoelaceElement {
});
this.mutationObserver = new MutationObserver(mutations => {
+ // Make sure to only observe the direct children of the tab group
+ // instead of other sub elements that might be slotted in.
+ // @see https://github.com/shoelace-style/shoelace/issues/2320
+ const instanceMutations = mutations.filter(({ target }) => {
+ if (target === this) return true; // Allow self updates
+ if ((target as HTMLElement).closest('sl-tab-group') !== this) return false; // We are not direct children
+
+ // We should only care about changes to the tab or tab panel
+ const tagName = (target as HTMLElement).tagName.toLowerCase();
+ return tagName === 'sl-tab' || tagName === 'sl-tab-panel';
+ });
+
+ if (instanceMutations.length === 0) {
+ return;
+ }
+
// Update aria labels when the DOM changes
- if (mutations.some(m => !['aria-labelledby', 'aria-controls'].includes(m.attributeName!))) {
+ if (instanceMutations.some(m => !['aria-labelledby', 'aria-controls'].includes(m.attributeName!))) {
setTimeout(() => this.setAriaLabels());
}
// Sync tabs when disabled states change
- if (mutations.some(m => m.attributeName === 'disabled')) {
+ if (instanceMutations.some(m => m.attributeName === 'disabled')) {
this.syncTabsAndPanels();
// sync tabs when active state on tab changes
- } else if (mutations.some(m => m.attributeName === 'active')) {
- const tabs = mutations
+ } else if (instanceMutations.some(m => m.attributeName === 'active')) {
+ const tabs = instanceMutations
.filter(m => m.attributeName === 'active' && (m.target as HTMLElement).tagName.toLowerCase() === 'sl-tab')
.map(m => m.target as SlTab);
const newActiveTab = tabs.find(tab => tab.active);
@@ -117,7 +133,14 @@ export default class SlTabGroup extends ShoelaceElement {
// After the first update...
this.updateComplete.then(() => {
this.syncTabsAndPanels();
- this.mutationObserver.observe(this, { attributes: true, childList: true, subtree: true });
+
+ this.mutationObserver.observe(this, {
+ attributes: true,
+ attributeFilter: ['active', 'disabled', 'name', 'panel'],
+ childList: true,
+ subtree: true
+ });
+
this.resizeObserver.observe(this.nav);
// Wait for tabs and tab panels to be registered