diff --git a/docs/pages/components/breadcrumb.md b/docs/pages/components/breadcrumb.md index 9f707158..6be8c96f 100644 --- a/docs/pages/components/breadcrumb.md +++ b/docs/pages/components/breadcrumb.md @@ -199,7 +199,63 @@ const App = () => ( ### With Dropdowns -Dropdown menus can be placed in a prefix or suffix slot to provide additional options. +Dropdown menus can be placed in the default slot to provide additional options. + +```html:preview + + Homepage + + + + + + + Web Design + Web Development + Marketing + + + + Our Services + Digital Media + +``` + +```jsx:react +import { + SlBreadcrumb, + SlBreadcrumbItem, + SlButton, + SlDropdown, + SlIcon, + SlMenu, + SlMenuItem +} from '@shoelace-style/shoelace/dist/react'; + +const App = () => ( + + Homepage + + + + + + + + Web Design + + Web Development + Marketing + + + + Our Services + Digital Media + +); +``` + +Alternatively, you can place dropdown menus in a prefix or suffix slot. ```html:preview diff --git a/docs/pages/resources/changelog.md b/docs/pages/resources/changelog.md index acdab320..4326d210 100644 --- a/docs/pages/resources/changelog.md +++ b/docs/pages/resources/changelog.md @@ -15,6 +15,8 @@ New versions of Shoelace are released as-needed and generally occur when a criti ## Next - Added ability to auto hide scroll buttons for `` when scroll button is not clickable by adding the `auto-hide-scroll-buttons` attribute. [#2128] +- Added support for using `` in `` default slot [#2015] +- Fixed a bug that caused errors to show in the console when components disconnect before before `firstUpdated()` executes [#2127] ## 2.16.0 diff --git a/src/components/breadcrumb-item/breadcrumb-item.component.ts b/src/components/breadcrumb-item/breadcrumb-item.component.ts index 5c81c15b..2afbac1d 100644 --- a/src/components/breadcrumb-item/breadcrumb-item.component.ts +++ b/src/components/breadcrumb-item/breadcrumb-item.component.ts @@ -2,7 +2,8 @@ import { classMap } from 'lit/directives/class-map.js'; import { HasSlotController } from '../../internal/slot.js'; import { html } from 'lit'; import { ifDefined } from 'lit/directives/if-defined.js'; -import { property } from 'lit/decorators.js'; +import { property, query, state } from 'lit/decorators.js'; +import { watch } from '../../internal/watch.js'; import componentStyles from '../../styles/component.styles.js'; import ShoelaceElement from '../../internal/shoelace-element.js'; import styles from './breadcrumb-item.styles.js'; @@ -31,6 +32,10 @@ export default class SlBreadcrumbItem extends ShoelaceElement { private readonly hasSlotController = new HasSlotController(this, 'prefix', 'suffix'); + @query('slot:not([name])') defaultSlot: HTMLSlotElement; + + @state() private renderType: 'button' | 'link' | 'dropdown' = 'button'; + /** * Optional URL to direct the user to when the breadcrumb item is activated. When set, a link will be rendered * internally. When unset, a button will be rendered instead. @@ -43,9 +48,34 @@ export default class SlBreadcrumbItem extends ShoelaceElement { /** The `rel` attribute to use on the link. Only used when `href` is set. */ @property() rel = 'noreferrer noopener'; - render() { - const isLink = this.href ? true : false; + private setRenderType() { + const hasDropdown = + this.defaultSlot.assignedElements({ flatten: true }).filter(i => i.tagName.toLowerCase() === 'sl-dropdown') + .length > 0; + if (this.href) { + this.renderType = 'link'; + return; + } + + if (hasDropdown) { + this.renderType = 'dropdown'; + return; + } + + this.renderType = 'button'; + } + + @watch('href', { waitUntilFirstUpdate: true }) + hrefChanged() { + this.setRenderType(); + } + + handleSlotChange() { + this.setRenderType(); + } + + render() { return html`
- ${isLink + ${this.renderType === 'link' ? html` - + ` - : html` + : ''} + ${this.renderType === 'button' + ? html` - `} + ` + : ''} + ${this.renderType === 'dropdown' + ? html` + + ` + : ''} diff --git a/src/components/breadcrumb-item/breadcrumb-item.test.ts b/src/components/breadcrumb-item/breadcrumb-item.test.ts index b89964f6..2ec94f3a 100644 --- a/src/components/breadcrumb-item/breadcrumb-item.test.ts +++ b/src/components/breadcrumb-item/breadcrumb-item.test.ts @@ -103,6 +103,30 @@ describe('', () => { }); }); + describe('when rendering a sl-dropdown in the default slot', () => { + it('should not render a link or button tag, but a div wrapper', async () => { + el = await fixture(html` + + + + + + + Web Design + Web Development + Marketing + + + + `); + + await expect(el).to.be.accessible(); + expect(el.shadowRoot!.querySelector('a')).to.be.null; + expect(el.shadowRoot!.querySelector('button')).to.be.null; + expect(el.shadowRoot!.querySelector('div.breadcrumb-item__label--drop-down')).not.to.be.null; + }); + }); + describe('when provided an element in the slot "prefix" to support prefix icons', () => { before(async () => { el = await fixture(html` diff --git a/src/components/carousel/carousel.component.ts b/src/components/carousel/carousel.component.ts index e1204736..0d3d7f94 100644 --- a/src/components/carousel/carousel.component.ts +++ b/src/components/carousel/carousel.component.ts @@ -103,7 +103,7 @@ export default class SlCarousel extends ShoelaceElement { disconnectedCallback(): void { super.disconnectedCallback(); - this.mutationObserver.disconnect(); + this.mutationObserver?.disconnect(); } protected firstUpdated(): void { diff --git a/src/components/details/details.component.ts b/src/components/details/details.component.ts index 2a38fd41..ad379b99 100644 --- a/src/components/details/details.component.ts +++ b/src/components/details/details.component.ts @@ -89,7 +89,7 @@ export default class SlDetails extends ShoelaceElement { disconnectedCallback() { super.disconnectedCallback(); - this.detailsObserver.disconnect(); + this.detailsObserver?.disconnect(); } private handleSummaryClick(event: MouseEvent) { diff --git a/src/components/range/range.component.ts b/src/components/range/range.component.ts index b3b38532..853679b7 100644 --- a/src/components/range/range.component.ts +++ b/src/components/range/range.component.ts @@ -132,7 +132,7 @@ export default class SlRange extends ShoelaceElement implements ShoelaceFormCont disconnectedCallback() { super.disconnectedCallback(); - this.resizeObserver.unobserve(this.input); + this.resizeObserver?.unobserve(this.input); } private handleChange() { diff --git a/src/components/split-panel/split-panel.component.ts b/src/components/split-panel/split-panel.component.ts index c61d014a..d2d2411a 100644 --- a/src/components/split-panel/split-panel.component.ts +++ b/src/components/split-panel/split-panel.component.ts @@ -85,7 +85,7 @@ export default class SlSplitPanel extends ShoelaceElement { disconnectedCallback() { super.disconnectedCallback(); - this.resizeObserver.unobserve(this); + this.resizeObserver?.unobserve(this); } private detectSize() { diff --git a/src/components/tab-group/tab-group.component.ts b/src/components/tab-group/tab-group.component.ts index fee2708d..1827baf7 100644 --- a/src/components/tab-group/tab-group.component.ts +++ b/src/components/tab-group/tab-group.component.ts @@ -127,8 +127,8 @@ export default class SlTabGroup extends ShoelaceElement { disconnectedCallback() { super.disconnectedCallback(); - this.mutationObserver.disconnect(); - this.resizeObserver.unobserve(this.nav); + this.mutationObserver?.disconnect(); + this.resizeObserver?.unobserve(this.nav); } private getAllTabs() { diff --git a/src/components/textarea/textarea.component.ts b/src/components/textarea/textarea.component.ts index d0dd8d5d..9a176df2 100644 --- a/src/components/textarea/textarea.component.ts +++ b/src/components/textarea/textarea.component.ts @@ -164,7 +164,7 @@ export default class SlTextarea extends ShoelaceElement implements ShoelaceFormC disconnectedCallback() { super.disconnectedCallback(); if (this.input) { - this.resizeObserver.unobserve(this.input); + this.resizeObserver?.unobserve(this.input); } } diff --git a/src/components/tree/tree.component.ts b/src/components/tree/tree.component.ts index 23ac4663..a9bb1a1c 100644 --- a/src/components/tree/tree.component.ts +++ b/src/components/tree/tree.component.ts @@ -111,8 +111,7 @@ export default class SlTree extends ShoelaceElement { disconnectedCallback() { super.disconnectedCallback(); - - this.mutationObserver.disconnect(); + this.mutationObserver?.disconnect(); } // Generates a clone of the expand icon element to use for each tree item