kopia lustrzana https://github.com/shoelace-style/shoelace
feat(details): use details and summary html tag to enable in browser searching (#1470)
rodzic
a067ccb9e0
commit
89f0f4a02c
|
@ -47,11 +47,13 @@ export default class SlDetails extends ShoelaceElement {
|
|||
|
||||
private readonly localize = new LocalizeController(this);
|
||||
|
||||
@query('.details') details: HTMLElement;
|
||||
@query('.details') details: HTMLDetailsElement;
|
||||
@query('.details__header') header: HTMLElement;
|
||||
@query('.details__body') body: HTMLElement;
|
||||
@query('.details__expand-icon-slot') expandIconSlot: HTMLSlotElement;
|
||||
|
||||
detailsObserver: MutationObserver;
|
||||
|
||||
/**
|
||||
* Indicates whether or not the details is open. You can toggle this attribute to show and hide the details, or you
|
||||
* can use the `show()` and `hide()` methods and this attribute will reflect the details' open state.
|
||||
|
@ -65,11 +67,31 @@ export default class SlDetails extends ShoelaceElement {
|
|||
@property({ type: Boolean, reflect: true }) disabled = false;
|
||||
|
||||
firstUpdated() {
|
||||
this.body.hidden = !this.open;
|
||||
this.body.style.height = this.open ? 'auto' : '0';
|
||||
if (this.open) {
|
||||
this.details.open = true;
|
||||
}
|
||||
|
||||
this.detailsObserver = new MutationObserver(changes => {
|
||||
for (const change of changes) {
|
||||
if (change.type === 'attributes' && change.attributeName === 'open') {
|
||||
if (this.details.open) {
|
||||
this.show();
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
this.detailsObserver.observe(this.details, { attributes: true });
|
||||
}
|
||||
|
||||
private handleSummaryClick() {
|
||||
disconnectedCallback() {
|
||||
this.detailsObserver.disconnect();
|
||||
}
|
||||
|
||||
private handleSummaryClick(ev: MouseEvent) {
|
||||
ev.preventDefault();
|
||||
if (!this.disabled) {
|
||||
if (this.open) {
|
||||
this.hide();
|
||||
|
@ -106,15 +128,16 @@ export default class SlDetails extends ShoelaceElement {
|
|||
@watch('open', { waitUntilFirstUpdate: true })
|
||||
async handleOpenChange() {
|
||||
if (this.open) {
|
||||
this.details.open = true;
|
||||
// Show
|
||||
const slShow = this.emit('sl-show', { cancelable: true });
|
||||
if (slShow.defaultPrevented) {
|
||||
this.open = false;
|
||||
this.details.open = false;
|
||||
return;
|
||||
}
|
||||
|
||||
await stopAnimations(this.body);
|
||||
this.body.hidden = false;
|
||||
|
||||
const { keyframes, options } = getAnimation(this, 'details.show', { dir: this.localize.dir() });
|
||||
await animateTo(this.body, shimKeyframesHeightAuto(keyframes, this.body.scrollHeight), options);
|
||||
|
@ -125,6 +148,7 @@ export default class SlDetails extends ShoelaceElement {
|
|||
// Hide
|
||||
const slHide = this.emit('sl-hide', { cancelable: true });
|
||||
if (slHide.defaultPrevented) {
|
||||
this.details.open = true;
|
||||
this.open = true;
|
||||
return;
|
||||
}
|
||||
|
@ -133,9 +157,9 @@ export default class SlDetails extends ShoelaceElement {
|
|||
|
||||
const { keyframes, options } = getAnimation(this, 'details.hide', { dir: this.localize.dir() });
|
||||
await animateTo(this.body, shimKeyframesHeightAuto(keyframes, this.body.scrollHeight), options);
|
||||
this.body.hidden = true;
|
||||
this.body.style.height = 'auto';
|
||||
|
||||
this.details.open = false;
|
||||
this.emit('sl-after-hide');
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +188,7 @@ export default class SlDetails extends ShoelaceElement {
|
|||
const isRtl = this.localize.dir() === 'rtl';
|
||||
|
||||
return html`
|
||||
<div
|
||||
<details
|
||||
part="base"
|
||||
class=${classMap({
|
||||
details: true,
|
||||
|
@ -173,7 +197,7 @@ export default class SlDetails extends ShoelaceElement {
|
|||
'details--rtl': isRtl
|
||||
})}
|
||||
>
|
||||
<div
|
||||
<summary
|
||||
part="header"
|
||||
id="header"
|
||||
class="details__header"
|
||||
|
@ -195,12 +219,12 @@ export default class SlDetails extends ShoelaceElement {
|
|||
<sl-icon library="system" name=${isRtl ? 'chevron-left' : 'chevron-right'}></sl-icon>
|
||||
</slot>
|
||||
</span>
|
||||
</div>
|
||||
</summary>
|
||||
|
||||
<div class="details__body" role="region" aria-labelledby="header">
|
||||
<slot part="content" id="content" class="details__content"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,10 @@ export default css`
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.details__header::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.details__header:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
|
|
@ -31,20 +31,19 @@ describe('<sl-details>', () => {
|
|||
`);
|
||||
const body = el.shadowRoot!.querySelector<HTMLElement>('.details__body')!;
|
||||
|
||||
expect(body.hidden).to.be.false;
|
||||
expect(parseInt(getComputedStyle(body).height)).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('should not be visible without the open attribute', async () => {
|
||||
const el = await fixture<SlDetails>(html`
|
||||
<sl-details>
|
||||
<sl-details summary="click me">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
|
||||
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
||||
consequat.
|
||||
</sl-details>
|
||||
`);
|
||||
const body = el.shadowRoot!.querySelector<HTMLElement>('.details__body')!;
|
||||
|
||||
expect(body.hidden).to.be.true;
|
||||
expect(parseInt(getComputedStyle(body).height)).to.equal(0);
|
||||
});
|
||||
|
||||
it('should emit sl-show and sl-after-show when calling show()', async () => {
|
||||
|
@ -55,7 +54,6 @@ describe('<sl-details>', () => {
|
|||
consequat.
|
||||
</sl-details>
|
||||
`);
|
||||
const body = el.shadowRoot!.querySelector<HTMLElement>('.details__body')!;
|
||||
const showHandler = sinon.spy();
|
||||
const afterShowHandler = sinon.spy();
|
||||
|
||||
|
@ -68,7 +66,6 @@ describe('<sl-details>', () => {
|
|||
|
||||
expect(showHandler).to.have.been.calledOnce;
|
||||
expect(afterShowHandler).to.have.been.calledOnce;
|
||||
expect(body.hidden).to.be.false;
|
||||
});
|
||||
|
||||
it('should emit sl-hide and sl-after-hide when calling hide()', async () => {
|
||||
|
@ -79,7 +76,6 @@ describe('<sl-details>', () => {
|
|||
consequat.
|
||||
</sl-details>
|
||||
`);
|
||||
const body = el.shadowRoot!.querySelector<HTMLElement>('.details__body')!;
|
||||
const hideHandler = sinon.spy();
|
||||
const afterHideHandler = sinon.spy();
|
||||
|
||||
|
@ -92,7 +88,6 @@ describe('<sl-details>', () => {
|
|||
|
||||
expect(hideHandler).to.have.been.calledOnce;
|
||||
expect(afterHideHandler).to.have.been.calledOnce;
|
||||
expect(body.hidden).to.be.true;
|
||||
});
|
||||
|
||||
it('should emit sl-show and sl-after-show when setting open = true', async () => {
|
||||
|
@ -127,7 +122,6 @@ describe('<sl-details>', () => {
|
|||
consequat.
|
||||
</sl-details>
|
||||
`);
|
||||
const body = el.shadowRoot!.querySelector<HTMLElement>('.details__body')!;
|
||||
const hideHandler = sinon.spy();
|
||||
const afterHideHandler = sinon.spy();
|
||||
|
||||
|
@ -140,7 +134,6 @@ describe('<sl-details>', () => {
|
|||
|
||||
expect(hideHandler).to.have.been.calledOnce;
|
||||
expect(afterHideHandler).to.have.been.calledOnce;
|
||||
expect(body.hidden).to.be.true;
|
||||
});
|
||||
|
||||
it('should not open when preventing sl-show', async () => {
|
||||
|
|
Ładowanie…
Reference in New Issue