From 31f99648884a294ba808e1ae9b0da6c2dcaed505 Mon Sep 17 00:00:00 2001 From: Adam Kudrna Date: Tue, 30 Jan 2024 18:52:30 +0100 Subject: [PATCH] Feat(web): Introduce option to disable scrolling inside `Modal` #DS-732 Scrolling inside `ModalDialog` can now be turned off by adding the `ModalDialog--nonScrolling` modifier class. --- .../web/src/scss/components/Modal/README.md | 28 ++- .../web/src/scss/components/Modal/_Modal.scss | 43 ++-- .../scss/components/Modal/_ModalDialog.scss | 53 ++++- .../web/src/scss/components/Modal/_theme.scss | 15 +- .../web/src/scss/components/Modal/index.html | 214 +++++++++++------- 5 files changed, 226 insertions(+), 127 deletions(-) diff --git a/packages/web/src/scss/components/Modal/README.md b/packages/web/src/scss/components/Modal/README.md index 8c81822afb..60a1b960a5 100644 --- a/packages/web/src/scss/components/Modal/README.md +++ b/packages/web/src/scss/components/Modal/README.md @@ -60,6 +60,9 @@ This is useful for Modals with dynamic content, e.g. a list of items that can be ``` +👉 Please note the preferred height options are ignored when scrolling inside ModalDialog is +[turned off](#disable-scrolling-inside-modaldialog). + 👉 Please note the custom height values are considered **preferred:** Modal will not expand beyond the viewport height. ### Custom Max Height @@ -82,6 +85,8 @@ You can use the custom property `--modal-max-height-tablet` to override the max ``` +👉 Please note the max height is ignored when scrolling inside ModalDialog is [turned off](#disable-scrolling-inside-modaldialog). + 👉 Please note the max height on mobile screens is currently not customizable. Let us know if you need this feature! 🙏 ## ModalDialog @@ -246,7 +251,7 @@ Use our JavaScript plugin to open your Modal, e.g.: ``` -## Disable Modal closing on backdrop click +## Disable Modal Closing on Backdrop Click Disable modal close when clicking on the backdrop. You can still close modal with close buttons or ESC key. @@ -280,6 +285,27 @@ scrolling, e.g.: ``` +### Disable Scrolling Inside ModalDialog + +Scrolling inside ModalDialog can be turned off by adding the `ModalDialog--nonScrollable` modifier class: + +```html +
+ +
+``` + +This way, the ModalBody will expand to fit the height of its content and the whole ModalDialog will scroll in case the +content is longer than user's viewport. + +👉 Please note that this modifier class can produce unexpected results when used in combination with ScrollView. + +#### ⚠️ DEPRECATION NOTICE + +The `.ModalDialog--nonScrollable` modifier will be removed in the next major release and the ModalDialog will be made +non-scrollable by default. It will be possible to re-enable the inside scrolling by adding the +`.ModalDialog--scrollable` modifier class (which will remain the recommended default usage). + ## Stacking Modals Multiple Modals can be open at the same time. That means, you can open a Modal from another Modal, and they will display diff --git a/packages/web/src/scss/components/Modal/_Modal.scss b/packages/web/src/scss/components/Modal/_Modal.scss index 4e0c3d2d48..a2af9d2200 100644 --- a/packages/web/src/scss/components/Modal/_Modal.scss +++ b/packages/web/src/scss/components/Modal/_Modal.scss @@ -1,10 +1,11 @@ // 1. In order to be transitioned, the visibility of the dialog is controlled through `opacity` and `visibility` // properties instead of the default `display`. -// 2. Use a `::before` pseudo-element instead of `::backdrop` to make fading possible. -// 3. Allow scrolling through the backdrop. -// 4. Restore text selection after `all: unset`. -// 5. For alignments other than `top`, prefer the `bottom` CSS property to enable smooth transitions between the uniform -// and docked variants (see `_ModalDialog.scss`). +// 2. Simply use Modal background color instead of `::backdrop` to make fading possible. (A pseudo-element cannot be +// used because it covers the scrollbar of the ModalDialog with the inside scrolling turned off.) +// 3. Use background gradient to prevent background from flickering in Safari. +// 4. Clip overflow during transition of the docked variant on mobile screens. +// 5. Allow scrolling through the modal. Has no effect unless the modal is taller than the viewport. +// 6. Restore text selection after `all: unset`. @use 'sass:map'; @use '../../tools/breakpoint'; @@ -19,6 +20,8 @@ z-index: 1; display: flex; // 1. padding-block: theme.$padding-y; + overflow: hidden; // 4. + background: linear-gradient(#{theme.$backdrop-background-color}, #{theme.$backdrop-background-color}); // 8. visibility: hidden; // 1. opacity: 0; // 1. pointer-events: none; // 1. @@ -29,29 +32,16 @@ background-color: transparent; } - // 2. - &::before { - content: ''; - position: fixed; - inset: 0; - z-index: -2; - background-color: theme.$backdrop-background-color; - visibility: hidden; - opacity: 0; - } - @media (prefers-reduced-motion: no-preference) { transition-property: visibility, opacity; transition-duration: theme.$transition-duration; - - // 2. - &::before { - transition: inherit; - } } } -// 5. +.Modal:has(.ModalDialog--nonScrollable) { + overflow-y: auto; // 5. +} + .Modal--center, .Modal:not(.Modal--top, .Modal--bottom) { --modal-top: auto; @@ -80,13 +70,6 @@ visibility: visible; opacity: 1; - user-select: text; // 4. + user-select: text; // 6. pointer-events: auto; - - // 2. - &::before { - visibility: visible; - opacity: 1; - pointer-events: none; // 3. - } } diff --git a/packages/web/src/scss/components/Modal/_ModalDialog.scss b/packages/web/src/scss/components/Modal/_ModalDialog.scss index ae011ef526..cbbf30c771 100644 --- a/packages/web/src/scss/components/Modal/_ModalDialog.scss +++ b/packages/web/src/scss/components/Modal/_ModalDialog.scss @@ -6,6 +6,7 @@ // 5. Unfortunately, the open state cannot be part of the parent `-docked-modal-dialog()` mixin because it's not // possible to generate selector for the feature class scenario. // 6. Override bottom padding of parent `.Modal` in the docked variant. +// 7. Override the min-height of the expanded docked variant. @use 'sass:map'; @use '../../settings/feature-flags'; @@ -53,13 +54,12 @@ width: theme.$dialog-uniform-width; max-width: calc(100% - #{theme.$padding-x}); height: theme.$dialog-uniform-height; - max-height: theme.$dialog-uniform-max-height; + min-height: unset; // 7. @deprecated Can be removed once the uniform modal dialog has been made default. border-radius: theme.$dialog-border-radius; } @include breakpoint.up(map.get(theme.$breakpoints, tablet)) { - height: theme.$dialog-height-tablet; - max-height: theme.$dialog-uniform-max-height-tablet; + height: theme.$dialog-uniform-height-tablet; } @include breakpoint.up(map.get(theme.$breakpoints, desktop)) { @@ -67,6 +67,16 @@ } } +@mixin -scrollable-uniform-modal-dialog($from-breakpoint) { + @if $from-breakpoint == 0 { + max-height: theme.$dialog-uniform-max-height; + } + + @include breakpoint.up(map.get(theme.$breakpoints, tablet)) { + max-height: theme.$dialog-uniform-max-height-tablet; + } +} + @mixin -docked-modal-dialog() { @include breakpoint.down(map.get(theme.$breakpoints, tablet)) { --modal-top: auto; @@ -76,8 +86,7 @@ width: theme.$dialog-width; max-width: none; - height: theme.$dialog-height; - max-height: theme.$dialog-max-height; + height: theme.$dialog-docked-height; border-radius: theme.$dialog-border-radius theme.$dialog-border-radius 0 0; } } @@ -91,10 +100,23 @@ @mixin -expand-docked-modal-dialog() { @include breakpoint.down(map.get(theme.$breakpoints, tablet)) { - height: theme.$dialog-max-height; + min-height: theme.$dialog-docked-expanded-height; } } +@mixin -scrollable-docked-modal-dialog() { + @include breakpoint.down(map.get(theme.$breakpoints, tablet)) { + max-height: theme.$dialog-docked-expanded-height; + } +} + +// @deprecated The non-scrollable modal dialog is deprecated and will be removed in the next major release. +// Migration: In CSS, make the modal dialog non-scrollable by default so scrolling inside is turned on by a modifier class. +@mixin -non-scrollable-modal-dialog() { + height: auto; + max-height: none; +} + .ModalDialog { @include -modal-dialog(); @@ -102,9 +124,12 @@ // Migration: Remove the feature flag and make the uniform dialog the default. @if feature-flags.$modal-enable-uniform-dialog { @include -uniform-modal-dialog($from-breakpoint: map.get(theme.$breakpoints, mobile)); + @include -scrollable-uniform-modal-dialog($from-breakpoint: map.get(theme.$breakpoints, mobile)); } @else { @include -docked-modal-dialog(); + @include -scrollable-docked-modal-dialog(); @include -uniform-modal-dialog($from-breakpoint: map.get(theme.$breakpoints, tablet)); + @include -scrollable-uniform-modal-dialog($from-breakpoint: map.get(theme.$breakpoints, tablet)); [open] > & { @include -open-docked-modal-dialog(); @@ -126,22 +151,36 @@ .ModalDialog--dockOnMobile.ModalDialog--expandOnMobile { @include -expand-docked-modal-dialog(); } + + .ModalDialog--nonScrollable { + @include -non-scrollable-modal-dialog(); + } } @else { .spirit-feature-modal-enable-uniform-dialog .ModalDialog { @include -uniform-modal-dialog($from-breakpoint: map.get(theme.$breakpoints, mobile)); + @include -scrollable-uniform-modal-dialog($from-breakpoint: map.get(theme.$breakpoints, mobile)); } .spirit-feature-modal-enable-uniform-dialog .ModalDialog--dockOnMobile { @include -docked-modal-dialog(); + @include -scrollable-docked-modal-dialog(); } .spirit-feature-modal-enable-uniform-dialog [open] > .ModalDialog--dockOnMobile { @include -open-docked-modal-dialog(); } - // stylelint-disable-next-line selector-max-class -- Only target the docked variant. + // stylelint-disable selector-max-class, no-descending-specificity -- Only target the docked variant. .ModalDialog--expandOnMobile, .spirit-feature-modal-enable-uniform-dialog .ModalDialog--dockOnMobile.ModalDialog--expandOnMobile { @include -expand-docked-modal-dialog(); } + // stylelint-enable selector-max-class + + // @deprecated The non-scrollable modal dialog is deprecated and will be removed in the next major release. + // Migration: In CSS, make the modal dialog non-scrollable by default so scrolling inside is turned on by a modifier class. + .ModalDialog--nonScrollable, + .spirit-feature-modal-enable-uniform-dialog .ModalDialog--nonScrollable { + @include -non-scrollable-modal-dialog(); + } } diff --git a/packages/web/src/scss/components/Modal/_theme.scss b/packages/web/src/scss/components/Modal/_theme.scss index 3f24094c78..929519ff58 100644 --- a/packages/web/src/scss/components/Modal/_theme.scss +++ b/packages/web/src/scss/components/Modal/_theme.scss @@ -19,21 +19,22 @@ $common-padding-x-tablet: tokens.$space-800; $dialog-width: 100%; $dialog-width-tablet: 640px; $dialog-width-desktop: 680px; -$dialog-height: var(--modal-preferred-height-mobile, min-content); -$dialog-height-tablet: var(--modal-preferred-height-tablet, min-content); -$dialog-max-height: calc(100dvh - #{tokens.$space-1100}); -$dialog-max-height-tablet: min(var(--modal-max-height-tablet, 600px), calc(100dvh - #{2 * tokens.$space-600})); $dialog-text-color: tokens.$text-primary-default; $dialog-border-radius: tokens.$radius-200; $dialog-background-color: tokens.$background-basic; $dialog-shadow: tokens.$shadow-300; -// @deprecated The "uniform" Header padding is deprecated and will be removed in the next major release. +// @deprecated The "uniform" dialog dimensions are deprecated and will be removed in the next major release. // Migration: Rename the variables containing the `uniform` keyword to remove it. $dialog-uniform-width: 640px; $dialog-uniform-height: var(--modal-preferred-height-mobile, min-content); -$dialog-uniform-max-height: min(var(--modal-max-height-mobile, 600px), calc(100dvh - #{2 * tokens.$space-600})); -$dialog-uniform-max-height-tablet: min(var(--modal-max-height-tablet, 600px), calc(100dvh - #{2 * tokens.$space-600})); +$dialog-uniform-height-tablet: var(--modal-preferred-height-tablet, min-content); +$dialog-uniform-max-height: min(var(--modal-max-height-mobile, 600px), calc(100dvh - #{2 * $padding-y})); +$dialog-uniform-max-height-tablet: min(var(--modal-max-height-tablet, 600px), calc(100dvh - #{2 * $padding-y})); + +$dialog-docked-height: var(--modal-preferred-height-mobile, min-content); +$dialog-docked-margin-top: tokens.$space-1100; +$dialog-docked-expanded-height: calc(100dvh - #{$dialog-docked-margin-top}); // ModalHeader $header-gap: tokens.$space-400; diff --git a/packages/web/src/scss/components/Modal/index.html b/packages/web/src/scss/components/Modal/index.html index 74a62e3f03..54617ff63e 100644 --- a/packages/web/src/scss/components/Modal/index.html +++ b/packages/web/src/scss/components/Modal/index.html @@ -15,9 +15,22 @@ footerElement.classList.add(`ModalFooter--${event.target.value}`); }; + const toggleDockOnMobile = (selector, dependingInputSelector) => { + const dependingInputElement = document.querySelector(dependingInputSelector); + const dependingLabelElement = dependingInputElement.closest('label'); + + document.querySelector(selector).classList.toggle('ModalDialog--dockOnMobile'); + dependingInputElement.disabled = !dependingInputElement.disabled; + dependingLabelElement.classList.toggle('Checkbox--disabled'); + } + const toggleExpandOnMobile = (selector) => { document.querySelector(selector).classList.toggle('ModalDialog--expandOnMobile'); } + + const toggleScrolling = (selector) => { + document.querySelector(selector).classList.toggle('ModalDialog--nonScrollable'); + }
@@ -559,6 +572,100 @@

+ + + + + + +
+ + +
+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquam at excepturi laudantium magnam mollitia perferendis reprehenderit, voluptate. Cum delectus dicta +

+ +
+ + + +
+ +

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquam at excepturi laudantium magnam mollitia + perferendis reprehenderit, voluptate. Cum delectus dicta ducimus eligendi excepturi natus perferendis + provident unde. Eveniet, iste, molestiae? +

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquam at excepturi laudantium magnam mollitia + perferendis reprehenderit, voluptate. Cum delectus dicta ducimus eligendi excepturi natus perferendis + provident unde. Eveniet, iste, molestiae? +

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquam at excepturi laudantium magnam mollitia + perferendis reprehenderit, voluptate. Cum delectus dicta ducimus eligendi excepturi natus perferendis + provident unde. Eveniet, iste, molestiae? +

+

+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquam at excepturi laudantium magnam mollitia + perferendis reprehenderit, voluptate. Cum delectus dicta ducimus eligendi excepturi natus perferendis + provident unde. Eveniet, iste, molestiae? +

+ +
+ + + +
+
+ + +
+
+ + +
+ + +
+ +

@@ -802,17 +909,6 @@

Feature Flag: Uniform Modal on Mobile

Open Modal - - @@ -847,7 +943,7 @@

Modal Title

provident unde. Eveniet, iste, molestiae?

-
+
Modal alignment:
- - - - -
- - -
-

Modal Title

- -
- - - -
- -

- Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aliquam at excepturi laudantium magnam mollitia - perferendis reprehenderit, voluptate. Cum delectus dicta ducimus eligendi excepturi natus perferendis - provident unde. Eveniet, iste, molestiae? -

- -
- - - -
-
- Optional description -
-
- - -
-
- - -
- - -
- -