Skip to content

Commit

Permalink
refactor(cc-block)!: rework component and introduce slot stack
Browse files Browse the repository at this point in the history
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
HeleneAmouzou committed Oct 16, 2024
1 parent b69b5b8 commit 7bb3425
Show file tree
Hide file tree
Showing 9 changed files with 709 additions and 204 deletions.
420 changes: 286 additions & 134 deletions src/components/cc-block/cc-block.js

Large diffs are not rendered by default.

283 changes: 225 additions & 58 deletions src/components/cc-block/cc-block.stories.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/components/cc-block/cc-block.types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type BlockToggleState = 'off' | 'open' | 'close';
2 changes: 0 additions & 2 deletions src/components/common.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,6 @@ interface Zone {
lon: number; // Longitude
}

type ToggleStateType = 'off' | 'open' | 'close';

type AppStatus =
| 'restart-failed'
| 'restarting'
Expand Down
76 changes: 76 additions & 0 deletions src/directives/hasSlottedChildren.js
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);
108 changes: 108 additions & 0 deletions src/directives/hasSlottedChildren.test.js
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);
});
});
15 changes: 13 additions & 2 deletions src/lib/events.types.d.ts
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;
};
4 changes: 0 additions & 4 deletions src/translations/translations.en.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,6 @@ export const translations = {
//#region cc-beta
'cc-beta.label': `beta`,
//#endregion
//#region cc-block
'cc-block.toggle.close': `Close`,
'cc-block.toggle.open': `Open`,
//#endregion
//#region cc-button
'cc-button.cancel': `Click to cancel`,
//#endregion
Expand Down
4 changes: 0 additions & 4 deletions src/translations/translations.fr.js
Original file line number Diff line number Diff line change
Expand Up @@ -249,10 +249,6 @@ export const translations = {
//#region cc-beta
'cc-beta.label': `bêta`,
//#endregion
//#region cc-block
'cc-block.toggle.close': `Fermer`,
'cc-block.toggle.open': `Ouvrir`,
//#endregion
//#region cc-button
'cc-button.cancel': `Cliquez pour annuler`,
//#endregion
Expand Down

0 comments on commit 7bb3425

Please sign in to comment.