-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(cc-block)!: rework component and introduce slot stack
BREAKING CHANGE: the properties have changed - `noHead`: property has been deleted - `state`: property has been renamed to `toggle` - `overlay`: property has been deleted Fixes #225
- Loading branch information
1 parent
b69b5b8
commit 7bb3425
Showing
9 changed files
with
709 additions
and
204 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export type BlockToggleState = 'off' | 'open' | 'close'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import { nothing } from 'lit'; | ||
import { AsyncDirective, directive, PartType } from 'lit/async-directive.js'; | ||
import { EventHandler } from '../lib/events.js'; | ||
|
||
/** | ||
* @typedef {import('lit/directive.js').Part} Part | ||
* @typedef {import('lit/directive.js').PartInfo} PartInfo | ||
* @typedef {import('../lib/events.types.js').EventWithTarget<HTMLSlotElement>} SlotEventWithTarget | ||
*/ | ||
|
||
/** | ||
* A Lit directive that checks if an element has slotted children. | ||
* If it is the case, the directive adds an `${changedSlot.name}-is-slotted` attribute to the element. | ||
* If it is the default slot, the directive adds 'is-slotted' attribute to the element. | ||
* If not, the `${changedSlot.name}-is-slotted` (or 'is-slotted' in case of default slot) attribute is removed. | ||
*/ | ||
|
||
class HasSlottedChildrenDirective extends AsyncDirective { | ||
/** | ||
* @param {PartInfo} partInfo | ||
*/ | ||
constructor(partInfo) { | ||
super(partInfo); | ||
/** @type {HTMLElement} element */ | ||
this.element = null; | ||
this.eventHandler = null; | ||
} | ||
|
||
disconnected() { | ||
super.disconnected(); | ||
this.eventHandler?.disconnect(); | ||
} | ||
|
||
/** | ||
* @param {Part} part | ||
* @returns {unknown} | ||
*/ | ||
update(part) { | ||
if (part.type !== PartType.ELEMENT) { | ||
throw new Error('This directive must be set as on Element.'); | ||
} | ||
if (!(part.element instanceof HTMLElement)) { | ||
throw new Error('This directive must be set on a HTMLElement.'); | ||
} | ||
if (part.element !== this.element) { | ||
this.eventHandler?.disconnect(); | ||
this.element = part.element; | ||
this.eventHandler = new EventHandler( | ||
this.element, | ||
'slotchange', | ||
/** @param {SlotEventWithTarget} e */ | ||
(e) => { | ||
const container = e.currentTarget; | ||
const changedSlot = e.target; | ||
const slotName = changedSlot.name; | ||
const attributeName = slotName === '' ? 'is-slotted' : `${slotName}-is-slotted`; | ||
|
||
if (changedSlot.assignedNodes().length > 0) { | ||
container.setAttribute(attributeName, ''); | ||
} else { | ||
container.removeAttribute(attributeName); | ||
} | ||
}, | ||
); | ||
this.eventHandler.connect(); | ||
} | ||
|
||
return this.render(); | ||
} | ||
|
||
render() { | ||
return nothing; | ||
} | ||
} | ||
|
||
export const hasSlottedChildren = directive(HasSlottedChildrenDirective); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* eslint-env node, mocha */ | ||
|
||
import { defineCE, elementUpdated, expect } from '@open-wc/testing'; | ||
import { LitElement, html } from 'lit'; | ||
import { getElement } from '../../test/helpers/element-helper.js'; | ||
import { hasSlottedChildren } from './hasSlottedChildren.js'; | ||
|
||
describe('hasSlottedChildrenDirective', () => { | ||
it('Should set the right attribute when slot is used', async () => { | ||
const slot = defineCE( | ||
class extends LitElement { | ||
hasIsSlottedAttribute(name) { | ||
return this.shadowRoot.querySelector('.main').hasAttribute(`${name}-is-slotted`); | ||
} | ||
|
||
render() { | ||
return html`<div class="main" ${hasSlottedChildren()}><slot name="slotName"></slot></div>`; | ||
} | ||
}, | ||
); | ||
|
||
const element = await getElement(`<${slot}><div slot="slotName">toto</div></${slot}>`); | ||
|
||
expect(element.hasIsSlottedAttribute('slotName')).equal(true); | ||
}); | ||
|
||
it('Should not set the attribute when slot is not used', async () => { | ||
const slot = defineCE( | ||
class extends LitElement { | ||
hasIsSlottedAttribute(name) { | ||
return this.shadowRoot.querySelector('.main').hasAttribute(`${name}-is-slotted`); | ||
} | ||
|
||
render() { | ||
return html`<div class="main" ${hasSlottedChildren()}><slot name="slotName"></slot></div>`; | ||
} | ||
}, | ||
); | ||
|
||
const element = await getElement(`<${slot}><div>toto</div></${slot}>`); | ||
|
||
expect(element.hasIsSlottedAttribute('slotName')).equal(false); | ||
}); | ||
|
||
it('Should set the right attribute when stack slot is used', async () => { | ||
const slot = defineCE( | ||
class extends LitElement { | ||
hasIsSlottedAttribute(name) { | ||
return this.shadowRoot.querySelector('.main').hasAttribute(`${name}-is-slotted`); | ||
} | ||
|
||
render() { | ||
return html`<div class="main" ${hasSlottedChildren()}> | ||
<slot name="slotName"><slot name="innerSlotName"></slot></slot> | ||
</div>`; | ||
} | ||
}, | ||
); | ||
|
||
const element = await getElement(`<${slot}><div slot="innerSlotName">toto</div></${slot}>`); | ||
|
||
expect(element.hasIsSlottedAttribute('slotName')).equal(false); | ||
expect(element.hasIsSlottedAttribute('innerSlotName')).equal(true); | ||
}); | ||
|
||
it('Should set the default attribute when default slot is used', async () => { | ||
const slot = defineCE( | ||
class extends LitElement { | ||
hasDefaultIsSlottedAttribute() { | ||
return this.shadowRoot.querySelector('.main').hasAttribute(`is-slotted`); | ||
} | ||
|
||
render() { | ||
return html`<div class="main" ${hasSlottedChildren()}> | ||
<slot></slot> | ||
</div>`; | ||
} | ||
}, | ||
); | ||
|
||
const element = await getElement(`<${slot}><div>toto</div></${slot}>`); | ||
|
||
expect(element.hasDefaultIsSlottedAttribute()).equal(true); | ||
}); | ||
|
||
it('Should remove the attribute when slot is not used anymore', async () => { | ||
const slot = defineCE( | ||
class extends LitElement { | ||
hasIsSlottedAttribute(name) { | ||
return this.shadowRoot.querySelector('.main').hasAttribute(`${name}-is-slotted`); | ||
} | ||
|
||
render() { | ||
return html`<div class="main" ${hasSlottedChildren()}><slot name="slotName"></slot></div>`; | ||
} | ||
}, | ||
); | ||
|
||
const element = await getElement(`<${slot}><div slot="slotName">toto</div></${slot}>`); | ||
|
||
expect(element.hasIsSlottedAttribute('slotName')).equal(true); | ||
|
||
element.innerHTML = 'toto'; | ||
await elementUpdated(element); | ||
|
||
expect(element.hasIsSlottedAttribute('slotName')).equal(false); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,14 @@ | ||
export type EventWithTarget<T extends EventTarget> = GenericEventWithTarget<Event, T>; | ||
export type EventWithTarget<T extends EventTarget, U extends EventTarget = null | Element> = GenericEventWithTarget< | ||
Event, | ||
T, | ||
U | ||
>; | ||
|
||
export type GenericEventWithTarget<E extends Event, T extends EventTarget> = E & { target: T }; | ||
export type GenericEventWithTarget< | ||
E extends Event, | ||
T extends EventTarget, | ||
U extends EventTarget = null | Element, | ||
> = E & { | ||
target: T; | ||
currentTarget: U; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters