diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 27290dc609..9d2bfebaf7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -4,6 +4,29 @@ ## Component changes +- Dialog + + - if no invoker or close btn JS fails gracefully, providing helpful feedback in the console instead of throwing Error + - CSS support for old syntax now checks it is not picked up also by the new syntax (make sure not dialog.dialog) + - new dialog can have multiple invokers + +- Buttons + + - loading spinner of buttons keep their aspect-ratio even if the buttons is squashed, when they're in overflow state + +- Cards + + - add support for div.illustration & picture elements in lieu of the img element as illustrations of used in the cards component (changes in documentation will come in a future release) + - default cards wide with no img and no text content -> align-items: center (better default when title wraps) + - Dropdown - dropdown.init() method now accept `id` parameter to initialize specific dropdowns, instead of always all dropdowns found in the DOM + +- Utility classes + + - Add utility classes for display grid and inline grid + +## Technical changes + +- chore deps (update deps packages minor versions) diff --git a/src/App/components/Cards/index.js b/src/App/components/Cards/index.js index 84643f21d2..c04535e6ac 100644 --- a/src/App/components/Cards/index.js +++ b/src/App/components/Cards/index.js @@ -36,6 +36,18 @@ const Cards = ({ src={imgSrc} alt="alt text to be set" /> + + {/* TODO: in documentation show use with div.illustration */} + {/*
*/} + + {/* TODO: in documentation show use with picture element */} + {/* + + + */} )} {/* no icon displayed if an image used as illustration */} diff --git a/src/less/components/button.less b/src/less/components/button.less index 865eb4bce8..81690f9476 100644 --- a/src/less/components/button.less +++ b/src/less/components/button.less @@ -217,6 +217,7 @@ border: 0.15rem solid; width: 1rem; height: 1rem; + aspect-ratio: 1; animation: loader-spin 0.8s linear infinite; } diff --git a/src/less/components/card.less b/src/less/components/card.less index f4ad02cec0..a54b1bbb4b 100644 --- a/src/less/components/card.less +++ b/src/less/components/card.less @@ -44,7 +44,7 @@ padding-inline-end: var(--padding-outer); } - &:not(.cards-wide):has(> img:first-child) .h4 { + &:not(.cards-wide):has(> :is(img, picture, .illustration):first-child) .h4 { padding-block-start: 0; } @@ -69,7 +69,7 @@ padding-block-end: var(--padding-outer); } - &.cards-wide:has(> img:first-child) + &.cards-wide:has(> :is(img, picture, .illustration):first-child) :is(.h4, .cards-icon, .cards-content, .cards-cta) { padding-inline-start: 0; @@ -95,7 +95,7 @@ margin-inline-end: 0.5rem; } - &:has(> img:first-child) { + &:has(> :is(img, picture, .illustration):first-child) { grid-template-areas: "image" "title" @@ -103,7 +103,7 @@ "cta"; grid-template-columns: 1fr; - &:is(button) img { + &:is(button) :is(img, picture, .illustration) { max-width: 100%; } @@ -115,15 +115,15 @@ grid-area: image; } - &:has(> img:first-child.ratio-1-1):before { + &:has(> :is(img, picture, .illustration):first-child.ratio-1-1):before { aspect-ratio: 1 / 1; } - &:has(> img:first-child.ratio-4-3):before { + &:has(> :is(img, picture, .illustration):first-child.ratio-4-3):before { aspect-ratio: 4 / 3; } - & > img:first-child { + & > :is(img, picture, .illustration):first-child { width: 100%; aspect-ratio: 16 / 9; grid-area: image; @@ -131,6 +131,12 @@ margin: 0; position: absolute; + &:is(picture) > img { + width: 100%; + height: 100%; + object-fit: cover; + } + &.ratio-1-1 { aspect-ratio: 1 / 1; } @@ -194,7 +200,7 @@ } } - &:not(:has(> img:first-child)) { + &:not(:has(> :is(img, picture, .illustration):first-child)) { grid-template-rows: auto auto; & .cards-cta { @@ -202,7 +208,7 @@ } } - &:not(:has(> img:first-child)):not(:has(.h4)) { + &:not(:has(> :is(img, picture, .illustration):first-child)):not(:has(.h4)) { grid-template-areas: "icon text-content cta"; grid-template-columns: auto 1fr min-content; grid-template-rows: auto; @@ -213,20 +219,23 @@ } } - &:not(:has(> img:first-child)):not(:has(.cards-content)) { + &:not(:has(> :is(img, picture, .illustration):first-child)):not( + :has(.cards-content) + ) { grid-template-areas: "icon title cta"; grid-template-columns: auto 1fr min-content; grid-template-rows: auto; padding: var(--padding-outer); + align-items: center; & :is(.cards-icon, .h4, .cards-cta) { padding: 0; } } - &:not(:has(> img:first-child)):not(:has(.cards-icon)):not( - :has(.cards-content) - ) { + &:not(:has(> :is(img, picture, .illustration):first-child)):not( + :has(.cards-icon) + ):not(:has(.cards-content)) { grid-template-areas: "title cta"; grid-template-columns: 1fr min-content; grid-template-rows: auto; @@ -237,7 +246,7 @@ } } - &:has(> img:first-child) { + &:has(> :is(img, picture, .illustration):first-child) { grid-template-areas: "image title" "image text-content" @@ -266,7 +275,7 @@ aspect-ratio: 1; } - & > img:first-child { + & > :is(img, picture, .illustration):first-child { max-width: 100%; object-fit: cover; width: auto; diff --git a/src/less/components/dialog.less b/src/less/components/dialog.less index f9f60e8254..9ba67c508b 100644 --- a/src/less/components/dialog.less +++ b/src/less/components/dialog.less @@ -15,7 +15,7 @@ dialog { } // TODO: can be removed in next major release when silent support for div.dialog is over -.dialog { +.dialog:not(dialog) { position: fixed; top: 0; right: 0; @@ -138,7 +138,7 @@ dialog { body.dialog-open { overflow: hidden; - .dialog { + .dialog:not(dialog) { overflow-x: hidden; overflow-y: auto; display: flex; @@ -326,7 +326,7 @@ dialog { // MARK: A11y reduced motion @media (forced-colors: active) { - .dialog { + .dialog:not(dialog) { border: 10px solid; > section { diff --git a/src/less/utilities/display.less b/src/less/utilities/display.less index 6e48fd276e..49d56706a2 100644 --- a/src/less/utilities/display.less +++ b/src/less/utilities/display.less @@ -14,6 +14,8 @@ each(@grid-breakpoints, .(@size, @abbr) { .d@{infix}-table-cell { display: table-cell !important; } .d@{infix}-flex { display: flex !important; } .d@{infix}-inline-flex { display: inline-flex !important; } + .d@{infix}-grid { display: grid !important; } + .d@{infix}-inline-grid { display: inline-grid !important; } } .media-breakpoint-up(@size, @abbr, @ruleset); diff --git a/src/scripts/main/dialog/dialog.e2e.spec.js b/src/scripts/main/dialog/dialog.e2e.spec.js index f2151862d5..851a3104eb 100644 --- a/src/scripts/main/dialog/dialog.e2e.spec.js +++ b/src/scripts/main/dialog/dialog.e2e.spec.js @@ -1,6 +1,8 @@ // @ts-check const { test, expect } = require("@playwright/test"); +// TODO: add tests dialogs for situations where multiple invokers are present, and for no invoker or no close button (no script but JS should not throw entirely, just gracefully fail) + test.beforeEach(async ({ page }) => { await page.goto("http://localhost:3000/components/dialog"); }); diff --git a/src/scripts/main/dialog/index.js b/src/scripts/main/dialog/index.js index d6fdc3cbab..0e06513959 100644 --- a/src/scripts/main/dialog/index.js +++ b/src/scripts/main/dialog/index.js @@ -124,21 +124,39 @@ const _createDialog = (dialogQuery) => { // MARK: script for element const _activateDialogElement = (dialog) => { - const dialogInvoker = document.querySelector( + const dialogInvokers = document.querySelectorAll( `button[data-dialog-open="${dialog.id}"]`, ); const closeDialogButtons = dialog.querySelectorAll( "button[data-dialog-close]", ); - // add event listener on dialogInvoker, it should call dialog.showModal() - dialogInvoker.addEventListener("click", () => { - dialog.showModal(); - }); + if (!dialogInvokers.length) { + console.error( + "There was no open button implemented for the dialog. Please make sure you add at least 1 button with the correct attributes to your HTML for the script to work (or do not call this script and use the JS methods on your side)", + ); + + return; + } + + if (!closeDialogButtons.length) { + console.error( + "There was no close button implemented for the dialog. Please make sure you add at least 1 button with the correct attributes to your HTML for the script to work (or do not call this script and use the JS methods on your side)", + ); + + return; + } + + // add event listener on dialogInvokers, it should call dialog.showModal() + [...dialogInvokers]?.map((invokerBtn) => + invokerBtn?.addEventListener("click", () => { + dialog.showModal(); + }), + ); // add event listener on dialogs close button, it should call dialog.close() - [...closeDialogButtons].map((closeBtn) => - closeBtn.addEventListener("click", () => { + [...closeDialogButtons]?.map((closeBtn) => + closeBtn?.addEventListener("click", () => { dialog.close(); }), );