From 7bb34257a205d5d3de4cecdfb4acfda2b9f2fdf9 Mon Sep 17 00:00:00 2001 From: Helene Amouzou Date: Tue, 15 Oct 2024 15:32:22 +0200 Subject: [PATCH] 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 --- src/components/cc-block/cc-block.js | 420 +++++++++++++------- src/components/cc-block/cc-block.stories.js | 283 ++++++++++--- src/components/cc-block/cc-block.types.d.ts | 1 + src/components/common.types.d.ts | 2 - src/directives/hasSlottedChildren.js | 76 ++++ src/directives/hasSlottedChildren.test.js | 108 +++++ src/lib/events.types.d.ts | 15 +- src/translations/translations.en.js | 4 - src/translations/translations.fr.js | 4 - 9 files changed, 709 insertions(+), 204 deletions(-) create mode 100644 src/components/cc-block/cc-block.types.d.ts create mode 100644 src/directives/hasSlottedChildren.js create mode 100644 src/directives/hasSlottedChildren.test.js diff --git a/src/components/cc-block/cc-block.js b/src/components/cc-block/cc-block.js index 35f51b13a..1b5330915 100644 --- a/src/components/cc-block/cc-block.js +++ b/src/components/cc-block/cc-block.js @@ -1,7 +1,10 @@ import { css, html, LitElement } from 'lit'; import { classMap } from 'lit/directives/class-map.js'; -import { iconRemixArrowDownSFill as iconDown, iconRemixArrowUpSFill as iconUp } from '../../assets/cc-remix.icons.js'; -import { i18n } from '../../translations/translation.js'; +import { iconRemixArrowRightSLine as iconArrowRight } from '../../assets/cc-remix.icons.js'; +import { hasSlottedChildren } from '../../directives/hasSlottedChildren.js'; +import { dispatchCustomEvent } from '../../lib/events.js'; +import { isStringEmpty } from '../../lib/utils.js'; +import { linkStyles } from '../../templates/cc-link/cc-link.js'; import '../cc-button/cc-button.js'; import '../cc-expand/cc-expand.js'; import '../cc-icon/cc-icon.js'; @@ -9,32 +12,40 @@ import '../cc-img/cc-img.js'; /** * @typedef {import('../common.types.js').IconModel} IconModel - * @typedef {import('../common.types.js').ToggleStateType} ToggleStateType + * @typedef {import('./cc-block.types.js').BlockToggleState} BlockToggleState */ /** - * A display component with mostly HTML+CSS and a open/close toggle feature. + * A display component with mostly HTML+CSS and an open/close toggle feature organized with slots. * * ## Details * - * * The main section is wrapped in a `` so variation of this section height will be animated. + * * The content-body section is wrapped in a `` so variation of this section height will be animated. * - * @cssdisplay grid + * @cssdisplay block * - * @slot - The main content of the block. The direct children of this will be spaced in a 1 column CSS grid. - * @slot button - A zone dedicated for a button/toggle in the to right corner. - * @slot overlay - The content to display on top of the main content. - * @slot title - The title of the block. Try to only use text. Use the `icon` property/attribute. + * @fires {CustomEvent<'open'|'close'>} cc-block:toggle-change - Fires toggle state whenever it changes. + * + * @slot ribbon - The ribbon in the top left corner. + * @slot header - A zone dedicated to header content. + * @slot header-icon - The icon in the header. + * @slot header-title - The title of the header. Try to only use text. Use the `icon` property/attribute. + * @slot header-right - A zone dedicated to header content on the top right side. + * @slot content - A zone dedicated to main content. + * @slot content-header - A zone dedicated to content header content. + * @slot content-body - A zone dedicated to content body content. + * @slot content-footer - A zone dedicated to content footer content. + * @slot footer - A zone dedicated to footer content. + * @slot footer-left - A zone dedicated to footer left side content. + * @slot footer-right - A zone dedicated to footer right side content. */ export class CcBlock extends LitElement { static get properties() { return { icon: { type: Object }, image: { type: String }, - noHead: { type: Boolean, attribute: 'no-head', reflect: true }, ribbon: { type: String, reflect: true }, - state: { type: String, reflect: true }, - _overlay: { type: Boolean, state: true }, + toggle: { type: String, reflect: true }, }; } @@ -47,196 +58,337 @@ export class CcBlock extends LitElement { /** @type {string|null} Sets the icon before the title using a ``. Icon is hidden if nullish. Property will be ignored if `icon` property is already set. */ this.image = null; - /** @type {boolean} Hides the head section. */ - this.noHead = false; - - /** @type {string|null} Adds a ribbon on the top left corner if it is not empty. */ + /** @type {string|null} Adds a ribbon in the top left corner if it is not empty. */ this.ribbon = null; - /** @type {ToggleStateType} Sets the state of the toggle behaviour. */ - this.state = 'off'; - - /** @type {boolean} */ - this._overlay = false; + /** @type {BlockToggleState} Sets the state of the toggle behaviour. */ + this.toggle = 'off'; } - _clickToggle() { - if (this.state === 'close') { - this.state = 'open'; - } else if (this.state === 'open') { - this.state = 'close'; + _onClickToggle() { + if (this.toggle === 'close') { + this.toggle = 'open'; + } else if (this.toggle === 'open') { + this.toggle = 'close'; } + dispatchCustomEvent(this, 'toggle-change', this.toggle); } render() { - const isToggleEnabled = this.state === 'open' || this.state === 'close'; - const isOpen = this.state !== 'close'; + const isToggleEnabled = this.toggle === 'open' || this.toggle === 'close'; + const isToggleOpen = this.toggle === 'open'; + const areContentAndFooterHidden = isToggleEnabled && !isToggleOpen; - /* TODO when reworking the component, check this a11y issue https://github.com/CleverCloud/clever-components/issues/225#issuecomment-1239462826 */ - /* eslint-disable lit-a11y/click-events-have-key-events */ return html` - ${this.ribbon != null && this.ribbon !== '' ? html`
${this.ribbon}
` : ''} - ${!this.noHead - ? html` -
- ${this.image != null && this.icon == null ? html` ` : ''} - ${this.icon != null ? html` ` : ''} - - ${isToggleEnabled - ? html` - ${isOpen ? i18n('cc-block.toggle.close') : i18n('cc-block.toggle.open')} - - ` - : ''} - -
- ` - : ''} - - - ${!isToggleEnabled || isOpen +
+
+ + ${!isStringEmpty(this.ribbon) ? html`
${this.ribbon}
` : ''} +
+
+ + ${isToggleEnabled ? html` -
- -
+ ` - : ''} - + : this._renderHeader()} + +
`; } - firstUpdated() { - /** @type {HTMLSlotElement|undefined} */ - const $overlay = this.shadowRoot.querySelector('slot[name="overlay"]'); - $overlay.addEventListener('slotchange', () => { - const oldVal = this._overlay; - this._overlay = $overlay.assignedNodes().length > 0; - this.requestUpdate('_overlay', oldVal); - }); + _renderHeader() { + const hasImageOrIcon = this.image != null || this.icon != null; + return html` + +
+
+ ${this.image == null && this.icon == null ? html` ` : ''} + ${this.image != null && this.icon == null + ? html` ` + : ''} + ${this.icon != null ? html` ` : ''} +
+
+ +
+
+ +
+
+
+ `; } static get styles() { return [ // language=CSS + linkStyles, css` :host { background-color: var(--cc-color-bg-default, #fff); border: 1px solid var(--cc-color-border-neutral, #aaa); border-radius: var(--cc-border-radius-default, 0.25em); box-sizing: border-box; - display: grid; + display: block; + margin-bottom: 1em; overflow: hidden; position: relative; } - .head { + .container { + display: grid; + gap: 0.5em; + padding-block: 1em; + + --left-space: 1em; + } + + .container[ribbon-is-slotted], + :host([ribbon]:not([ribbon=''])) .container { + --left-space: 3.5em; + } + + .ribbon { + display: none; + } + + .container[ribbon-is-slotted] .ribbon, + :host([ribbon]:not([ribbon=''])) .ribbon { + background: var(--cc-color-bg-strong); + color: white; + display: inherit; + font-size: 0.9em; + font-weight: bold; + height: var(--height); + left: calc(var(--width) / -2); + line-height: var(--height); + position: absolute; + text-align: center; + top: calc(var(--height) / -2); + transform: rotate(var(--r)) translateY(var(--translate)); + width: var(--width); + z-index: 2; + + --height: 1.5em; + --width: 8em; + --r: -45deg; + --translate: 1.6em; + } + + /* region header */ + + .header { align-items: center; color: var(--cc-color-text-primary-strongest); + display: none; + padding: 1em 1em 1em var(--left-space); + } + + .container[header-is-slotted] .header, + .container[header-title-is-slotted] .header, + .container[header-right-is-slotted] .header, + .container[header-icon-is-slotted] .header { display: flex; - padding: 1em; + flex-wrap: wrap; + gap: 0.5em; } - :host([ribbon]) .head { - padding-left: 3.5em; + .container[header-is-slotted], + .container[header-title-is-slotted], + .container[header-right-is-slotted] { + padding-top: 0; } - :host([state='open']) .head:hover, - :host([state='close']) .head:hover { - background-color: var(--cc-color-bg-neutral-hovered); - cursor: pointer; + ::slotted([slot='header']) { + padding: 1em 1em 1em var(--left-space); } - cc-img { - align-self: flex-start; - border-radius: var(--cc-border-radius-default, 0.25em); - height: 1.5em; - margin-right: 1em; - width: 1.5em; + .header-icon { + display: none; } - cc-icon { - align-self: flex-start; - margin-right: 1em; + .container[header-icon-is-slotted] .header-icon, + .has-image-or-icon.header-icon { + display: flex; } - .toggle_button { - --cc-icon-size: 1.5em; + .header-img, + ::slotted([slot='header-icon']) { + border-radius: var(--cc-border-radius-default, 0.25em); + height: 1.5em; + width: 1.5em; } - ::slotted([slot='title']) { + .header-title { flex: 1 1 0; + } + + ::slotted([slot='header-title']) { + color: var(--cc-color-text-primary-strongest); font-size: 1.2em; font-weight: bold; } - .info-ribbon { - --height: 1.5em; - --width: 8em; - --r: -45deg; - --translate: 1.6em; + .container[header-right-is-slotted] .header-right { + display: inline-block; + } - background: var(--cc-color-bg-strong); - color: white; - font-size: 0.9em; - font-weight: bold; - height: var(--height); - left: calc(var(--width) / -2); - line-height: var(--height); - position: absolute; - text-align: center; - top: calc(var(--height) / -2); - transform: rotate(var(--r)) translateY(var(--translate)); - width: var(--width); - z-index: 2; + .toggle-button { + --cc-icon-color: var(--cc-color-text-primary-strongest); + + align-items: center; + background-color: transparent; + border: 0; + display: flex; + font-family: inherit; + font-size: 1em; + justify-content: space-between; + padding: 0 1em 0 0; + text-align: start; } - .main { - display: grid; - grid-gap: 1em; - padding: 0.5em 1em 1em; + .toggle-icon { + transition: transform 0.3s ease-out; + } + + .toggle-button[aria-expanded='true'] .toggle-icon { + transform: rotate(0.25turn); + } + + .toggle-button:hover { + background-color: var(--cc-color-bg-neutral-hovered); } - :host([no-head]) .main { - padding: 1em; + /* endregion */ + + /* region content */ + + .content { + display: none; + gap: 1em; } - .main-wrapper--overlay { - filter: blur(0.3em); - opacity: 0.35; + ::slotted([slot='content']) { + padding-left: var(--left-space); + padding-right: 1em; + } + + .container[content-is-slotted] .content, + .container[content-header-is-slotted] .content, + .container[content-body-is-slotted] .content, + .container[content-footer-is-slotted] .content { + display: grid; + gap: 1em; } - /* superpose main and overlay */ + .content-header, + .content-body, + .content-footer { + display: none; + padding: 0 1em 0 var(--left-space); + } - .main-wrapper, - ::slotted([slot='overlay']) { - grid-area: 2 / 1 / auto / auto; + .container[content-header-is-slotted] .content-header, + .container[content-body-is-slotted] .content-body, + .container[content-footer-is-slotted] .content-footer { + display: block; } - :host([ribbon]) .main-wrapper { - padding-left: 2.5em; + .content-body { + margin-block: -0.5em; + padding-block: 0.5em; } - ::slotted([slot='overlay']) { - align-content: center; + #content-and-footer { display: grid; - justify-items: center; - /* we have a few z-index:2 on atoms */ - z-index: 10; + gap: 1em; } - ::slotted(.cc-block_empty-msg) { - color: var(--cc-color-text-weak); - font-style: italic; + #content-and-footer.hidden { + display: none; } + + /* endregion */ + + /* region footer */ + + .footer { + background-color: var(--cc-color-bg-neutral); + box-shadow: inset 0 6px 6px -6px rgb(0 0 0 / 40%); + display: none; + flex-wrap: wrap; + gap: 1em; + /* TODO doc */ + justify-content: flex-end; + padding: 0.5em 1em; + } + + .footer-left { + flex: 1 1 0; + } + + .footer-left, + .footer-right { + display: none; + } + + .container[footer-is-slotted], + .container[footer-left-is-slotted], + .container[footer-right-is-slotted], + :host([toggle='close']) .container { + padding-bottom: 0; + } + + .container[footer-is-slotted] .footer, + .container[footer-left-is-slotted] .footer, + .container[footer-right-is-slotted] .footer { + display: flex; + } + + .container[footer-left-is-slotted] .footer-left, + .container[footer-right-is-slotted] .footer-right { + display: block; + } + + ::slotted([slot='footer']) { + padding: 0.5em 1em; + } + + /* endregion */ `, ]; } diff --git a/src/components/cc-block/cc-block.stories.js b/src/components/cc-block/cc-block.stories.js index ba6f29ce2..c3a9533b4 100644 --- a/src/components/cc-block/cc-block.stories.js +++ b/src/components/cc-block/cc-block.stories.js @@ -11,10 +11,14 @@ export default { component: 'cc-block', }; +/** + * @typedef {import('./cc-block.js').CcBlock} CcBlock + */ + const htmlExample = ` -
This is my block
-
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
-
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+
This is my block
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
`; const conf = { @@ -22,6 +26,7 @@ const conf = { }; export const defaultStory = makeStory(conf, { + /** @type {Array>} */ items: [ { innerHTML: htmlExample, @@ -29,30 +34,32 @@ export const defaultStory = makeStory(conf, { ], }); -export const overlayWithLoader = makeStory(conf, { +export const image = makeStory(conf, { + /** @type {Array>} */ items: [ { - // language=HTML - innerHTML: ` -
This is my block
-
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
-
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
- - `, + image: 'https://assets.clever-cloud.com/logos/nodejs.svg', + innerHTML: htmlExample, }, ], }); -export const image = makeStory(conf, { +export const imageSlotted = makeStory(conf, { + /** @type {Array>} */ items: [ { - image: 'https://assets.clever-cloud.com/logos/nodejs.svg', - innerHTML: htmlExample, + innerHTML: ` + Nodejs logo +
This is my block
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+ `, }, ], }); export const icon = makeStory(conf, { + /** @type {Array>} */ items: [ { icon: iconInfo, @@ -61,10 +68,25 @@ export const icon = makeStory(conf, { ], }); +export const iconSlotted = makeStory(conf, { + /** @type {Array>} */ + items: [ + { + innerHTML: ` + +
This is my block
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+ `, + }, + ], +}); + export const imageAndIcon = makeStory(conf, { docs: ` If you set both \`image\` and \`icon\` properties, the \`image\` property will be ignored: `, + /** @type {Array>} */ items: [ { image: 'https://assets.clever-cloud.com/logos/nodejs.svg', @@ -74,118 +96,263 @@ If you set both \`image\` and \`icon\` properties, the \`image\` property will b ], }); -export const button = makeStory(conf, { +export const ribbon = makeStory(conf, { + /** @type {Array>} */ items: [ { + ribbon: 'info', innerHTML: ` -
This is my block
- A button -
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
-
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+
This is my block
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
`, }, ], }); -export const noHead = makeStory(conf, { +export const ribbonSlotted = makeStory(conf, { + /** @type {Array>} */ items: [ { - noHead: true, innerHTML: ` -
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
-
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+
info
+
This is my block
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
`, }, ], }); -export const ribbon = makeStory(conf, { +export const ribbonWithToggle = makeStory(conf, { + /** @type {Array>} */ items: [ { - ribbon: 'info', + toggle: 'open', innerHTML: ` -
This is my block
-
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
-
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+
info
+
This is my block
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
`, }, ], }); -export const ribbonWithState = makeStory(conf, { +export const toggleOpen = makeStory(conf, { + /** @type {Array>} */ items: [ { - ribbon: 'info', - state: 'open', innerHTML: ` -
This is my block
-
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
-
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
- `, +
This is my block
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+
Some content in the right footer side.
+ `, + toggle: 'open', }, ], }); -export const ribbonWithNoHead = makeStory(conf, { +export const toggleClose = makeStory(conf, { + /** @type {Array>} */ items: [ { - ribbon: 'info', - noHead: true, innerHTML: ` -
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
-
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
- `, +
This is my block
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+
Some content in the right footer side.
+ `, + toggle: 'close', }, ], }); -export const stateWithOpen = makeStory(conf, { +export const imageAndOpen = makeStory(conf, { + /** @type {Array>} */ items: [ { + image: 'https://assets.clever-cloud.com/logos/nodejs.svg', innerHTML: htmlExample, - state: 'open', + toggle: 'open', }, ], }); -export const stateWithClose = makeStory(conf, { +export const iconAndOpen = makeStory(conf, { + /** @type {Array>} */ items: [ { + icon: iconInfo, innerHTML: htmlExample, - state: 'close', + toggle: 'open', }, ], }); -export const stateWithOverflow = makeStory(conf, { +export const headerRightContent = makeStory(conf, { + /** @type {Array>} */ items: [ { innerHTML: ` -
This is my block
-
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
-
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+
This is my block
+ A button +
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
`, - state: 'open', }, ], }); -export const imageAndOpen = makeStory(conf, { +export const header = makeStory(conf, { + /** @type {Array>} */ items: [ { - image: 'https://assets.clever-cloud.com/logos/nodejs.svg', - innerHTML: htmlExample, - state: 'open', + innerHTML: ` +
Some custom content in the header.
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+ `, }, ], }); -export const iconAndOpen = makeStory(conf, { +export const contentHeader = makeStory(conf, { + /** @type {Array>} */ items: [ { - icon: iconInfo, - innerHTML: htmlExample, - state: 'open', + innerHTML: ` +
This is my block
+
Some content in my content header.
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+`, + }, + ], +}); + +export const contentBodyWithOverflow = makeStory(conf, { + /** @type {Array>} */ + items: [ + { + innerHTML: ` +
This is my block
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+ `, + }, + ], +}); + +export const contentFooterAndHeader = makeStory(conf, { + /** @type {Array>} */ + items: [ + { + innerHTML: ` +
This is my block
+
Some content in my content header.
+
+ +

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi beatae culpa eaque eum ex explicabo iusto laudantium magni odio possimus quaerat, qui sit tenetur totam velit veritatis vitae. Magni, nemo!

+

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi beatae culpa eaque eum ex explicabo iusto laudantium magni odio possimus quaerat, qui sit tenetur totam velit veritatis vitae. Magni, nemo!

+

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Animi beatae culpa eaque eum ex explicabo iusto laudantium magni odio possimus quaerat, qui sit tenetur totam velit veritatis vitae. Magni, nemo!

+
+
Some content in my content footer.
+ +`, + }, + ], + css: ` + p.text { + display: none; + } + + input:checked ~ p.text { + display: block; + } + `, +}); + +export const contentFooter = makeStory(conf, { + /** @type {Array>} */ + items: [ + { + innerHTML: ` +
This is my block
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+
Some content in my content footer.
+`, + }, + ], +}); + +export const content = makeStory(conf, { + /** @type {Array>} */ + items: [ + { + innerHTML: ` +
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+ `, + }, + ], +}); + +export const footerLeft = makeStory(conf, { + /** @type {Array>} */ + items: [ + { + innerHTML: ` +
This is my block
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+
Some content in the left footer side.
+`, + }, + ], +}); + +export const footerRight = makeStory(conf, { + /** @type {Array>} */ + items: [ + { + innerHTML: ` +
This is my block
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+
Some content in the right footer side.
+`, + }, + ], +}); + +export const footerLeftAndRight = makeStory(conf, { + /** @type {Array>} */ + items: [ + { + innerHTML: ` +
This is my block
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+
Some content in the left footer side.
+
Some content in the right footer side.
+`, + }, + ], +}); + +export const footer = makeStory(conf, { + /** @type {Array>} */ + items: [ + { + innerHTML: ` +
This is my block
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque feugiat dui at leo porta dignissim. Etiam ut purus ultrices, pulvinar tellus quis, cursus massa. Mauris dignissim accumsan ex, at vestibulum lectus fermentum id. Quisque nec magna arcu. Quisque in metus sed erat sodales euismod eget id purus. Sed sagittis rhoncus mauris. Ut sit amet urna ac nunc semper porta. Nam ut felis eu velit luctus rutrum. Nam leo nisl, molestie a varius non, ullamcorper sit amet tortor. Donec in convallis ex. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent hendrerit venenatis erat, eu malesuada nulla viverra eu. Curabitur porta risus augue, non rutrum lectus hendrerit a.
+
Sed volutpat dolor nec rutrum vulputate. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer rhoncus turpis orci, at tempor tortor scelerisque varius. Integer nec fermentum dui. Integer vitae dolor sit amet erat ullamcorper elementum. Donec blandit lacinia erat, vitae blandit libero ornare id. In luctus odio a lacus dignissim, id posuere tortor lacinia. Pellentesque sed massa ac tellus tincidunt rutrum. Praesent commodo enim nibh, ut consectetur tortor consequat non. Aliquam mi enim, mattis eu velit quis, sollicitudin fringilla ex. Donec at augue ultrices, porta justo in, mattis tortor. Nunc sollicitudin nisi eget urna condimentum semper. Pellentesque sagittis quam eu mollis viverra. Proin tincidunt auctor nibh quis suscipit.
+
Some custom content in the footer.
+`, }, ], }); diff --git a/src/components/cc-block/cc-block.types.d.ts b/src/components/cc-block/cc-block.types.d.ts new file mode 100644 index 000000000..5abf3dd16 --- /dev/null +++ b/src/components/cc-block/cc-block.types.d.ts @@ -0,0 +1 @@ +export type BlockToggleState = 'off' | 'open' | 'close'; diff --git a/src/components/common.types.d.ts b/src/components/common.types.d.ts index 982b63b6b..44e39c249 100644 --- a/src/components/common.types.d.ts +++ b/src/components/common.types.d.ts @@ -188,8 +188,6 @@ interface Zone { lon: number; // Longitude } -type ToggleStateType = 'off' | 'open' | 'close'; - type AppStatus = | 'restart-failed' | 'restarting' diff --git a/src/directives/hasSlottedChildren.js b/src/directives/hasSlottedChildren.js new file mode 100644 index 000000000..d01ab9878 --- /dev/null +++ b/src/directives/hasSlottedChildren.js @@ -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} 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); diff --git a/src/directives/hasSlottedChildren.test.js b/src/directives/hasSlottedChildren.test.js new file mode 100644 index 000000000..15292bccf --- /dev/null +++ b/src/directives/hasSlottedChildren.test.js @@ -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`
`; + } + }, + ); + + const element = await getElement(`<${slot}>
toto
`); + + 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`
`; + } + }, + ); + + const element = await getElement(`<${slot}>
toto
`); + + 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`
+ +
`; + } + }, + ); + + const element = await getElement(`<${slot}>
toto
`); + + 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`
+ +
`; + } + }, + ); + + const element = await getElement(`<${slot}>
toto
`); + + 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`
`; + } + }, + ); + + const element = await getElement(`<${slot}>
toto
`); + + expect(element.hasIsSlottedAttribute('slotName')).equal(true); + + element.innerHTML = 'toto'; + await elementUpdated(element); + + expect(element.hasIsSlottedAttribute('slotName')).equal(false); + }); +}); diff --git a/src/lib/events.types.d.ts b/src/lib/events.types.d.ts index f2494edac..8a72d6e6d 100644 --- a/src/lib/events.types.d.ts +++ b/src/lib/events.types.d.ts @@ -1,3 +1,14 @@ -export type EventWithTarget = GenericEventWithTarget; +export type EventWithTarget = GenericEventWithTarget< + Event, + T, + U +>; -export type GenericEventWithTarget = E & { target: T }; +export type GenericEventWithTarget< + E extends Event, + T extends EventTarget, + U extends EventTarget = null | Element, +> = E & { + target: T; + currentTarget: U; +}; diff --git a/src/translations/translations.en.js b/src/translations/translations.en.js index ecb554d2a..ec40d3abe 100644 --- a/src/translations/translations.en.js +++ b/src/translations/translations.en.js @@ -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 diff --git a/src/translations/translations.fr.js b/src/translations/translations.fr.js index 41b38eee9..acddf7f49 100644 --- a/src/translations/translations.fr.js +++ b/src/translations/translations.fr.js @@ -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