From 1cb495a5894c53f57fa02adf6c72fc63bc5cac3d Mon Sep 17 00:00:00 2001 From: Snorre Kim Date: Tue, 24 Sep 2024 15:08:42 +0200 Subject: [PATCH] feat(SASS mixins): media queries mixins now uses 0.1px instead of 1px to avoid overlap (#3985) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `1px` overlap-avoidance between media queries created a `0.999...px` gap between mixin media queries. That is visible if user zooms in. This PR changes that to `0.1px` so you would have to zoom in more than 1000% in order to get a gap. ### Example: browser with 200% zoom and 640.5px with 1. `'small'` breakpoint `40em = 640px 3. `allBelow('small')` `0px to 640px` is narrower than `640.5px` 4. `allAbove('small')` `641px to infinity` is wider than `640.5px` 5. Changing `allAbove('small')` to `640.1px to infinity` includes `640.5px` ### Changes: * changed overlap-avoidance from `1px` to `0.1px` in both SASS mixins and react hooks. * updated documentation for "SASS and Mixins" and "Media Queries and Breakpints" to make it clearer that we do not include the minimum breakpoint in our intervals. ### other fixes: * fixed Portal Home button breakpoint off by 1 px * fixed a bug where you could not use any other unit than `em` for `allAbove` and `allBetween` mixin. * fixed a bug where `$breakpoint-offset`. was not used in `allBetween` mixin. * fixed a bug with parsing `'and'` in array sizes for `MediaQueryUtils.ts` * added a comment that `handheld` is a [deprecated](https://developer.mozilla.org/en-US/docs/Web/CSS/@media#sect2) media-type in 'MediaQueryUtils.ts' (should we remove it?) --------- Co-authored-by: Tobias Høegh --- .../src/docs/uilib/helpers/sass.mdx | 11 +++++ .../src/docs/uilib/layout/media-queries.mdx | 16 +++---- .../src/shared/menu/StickyMenuBar.module.scss | 2 +- .../__snapshots__/Accordion.test.tsx.snap | 6 +-- .../__snapshots__/Autocomplete.test.tsx.snap | 4 +- .../__snapshots__/Breadcrumb.test.tsx.snap | 6 +-- .../__snapshots__/DatePicker.test.tsx.snap | 2 +- .../__snapshots__/Drawer.test.tsx.snap | 6 +-- .../__snapshots__/Dropdown.test.tsx.snap | 2 +- .../__snapshots__/FormRow.test.tsx.snap | 2 +- .../__snapshots__/GlobalStatus.test.tsx.snap | 4 +- .../__snapshots__/Section.test.tsx.snap | 4 +- .../__snapshots__/SkipContent.test.tsx.snap | 4 +- .../__snapshots__/Space.test.tsx.snap | 4 +- .../__snapshots__/ToggleButton.test.tsx.snap | 2 +- .../__snapshots__/DrawerList.test.tsx.snap | 6 +-- .../dnb-eufemia/src/shared/MediaQueryUtils.ts | 34 ++++++------- .../src/shared/__tests__/MediaQuery.test.tsx | 8 ++-- .../shared/__tests__/MediaQueryUtils.test.tsx | 48 +++++++++++++++---- .../src/shared/__tests__/useMedia.test.tsx | 4 +- .../shared/__tests__/useMediaQuery.test.tsx | 4 +- .../dnb-eufemia/src/style/core/utilities.scss | 15 ++++-- .../__snapshots__/Elements.test.js.snap | 2 +- 23 files changed, 122 insertions(+), 74 deletions(-) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/helpers/sass.mdx b/packages/dnb-design-system-portal/src/docs/uilib/helpers/sass.mdx index 6ff27b60888..c4be8ecdc87 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/helpers/sass.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/helpers/sass.mdx @@ -78,16 +78,27 @@ You can import Eufemia _mixins_ directly into your SCSS styles: Use the `allAbove`, `allBelow` and `allBetween` mixins to add media queries to your css. +To prevent overlapping media queries, `0.00625em` gets added to the minimum width. This results in an increment of approximately `0.1px` when using `em` units. If you're using a unit other than `em`, you may need to adjust this value accordingly, as `0.00625px` is typically too small to effectively prevent overlaps. + +| mixin | actual interval (em) | actual interval (px) | +| ----------------------- | ---------------------- | -------------------- | +| `allBelow(40em)` | 0 to 40em | 0 to 640px | +| `allBetween(40em,60em)` | 40.00625em to 60em | 640.1px to 960px | +| `allAbove(60em)` | 60.00625em to infinity | 960.1px to infinity | + ```scss @import '@dnb/eufemia/style/core/utilities.scss'; @include allBelow(small) { + // from 0px to 'small' (640px) } @include allBetween(small, medium) { + // from 640.1px ('small' + 0.1px) to 960px ('medium') } @include allAbove(medium) { + // from 960.1px ('medium' + 0.1px) and wider } ``` diff --git a/packages/dnb-design-system-portal/src/docs/uilib/layout/media-queries.mdx b/packages/dnb-design-system-portal/src/docs/uilib/layout/media-queries.mdx index 5bbb8468bfe..3d113e4e8e5 100644 --- a/packages/dnb-design-system-portal/src/docs/uilib/layout/media-queries.mdx +++ b/packages/dnb-design-system-portal/src/docs/uilib/layout/media-queries.mdx @@ -31,13 +31,13 @@ UX designers are using a 12 column system during their design processes. Application in DNB do actually break only twice (`small` and `medium`). But have a HTML body max-width of `large`. -| Name | Range | Mixin | Columns | -| ---------- | ----------------- | --------------------------- | ------- | -| `isSmall` | from 0 to 40em | `allBelow(small)` | 4 | -| `isMedium` | from 40em to 60em | `allBetween(small, medium)` | 6 | -| `isLarge` | from 60em | `allAbove(medium)` | 12 | +| React hook | Range | SASS mixin | Columns | +| ---------- | --------------------------------- | --------------------------- | ------- | +| `isSmall` | from 0 to 40em | `allBelow(small)` | 4 | +| `isMedium` | from (not including) 40em to 60em | `allBetween(small, medium)` | 6 | +| `isLarge` | from (not including) 60em | `allAbove(medium)` | 12 | -
+Note: if you've set [custom sass breakpoints](/uilib/helpers/sass/#custom-offset) using `$breakpoints` or `$breakpoint-offset`, the sass mixins will be different. So when dealing with naming of breakpoint ranges (between breakpoints), we actually use the term "large" when a media query exceeds `medium`: @@ -53,8 +53,8 @@ So when dealing with naming of breakpoint ranges (between breakpoints), we actua Here is how ranges breaks down in pixels: - The small range goes from 0 to 640px -- The medium range goes from 641px to 960px -- The large range goes from 961px to infinity +- The medium range goes from 640.1px to 960px +- The large range goes from 960.1px to infinity ### UX Design and Breakpoints diff --git a/packages/dnb-design-system-portal/src/shared/menu/StickyMenuBar.module.scss b/packages/dnb-design-system-portal/src/shared/menu/StickyMenuBar.module.scss index b70031a3757..5b2aca97189 100644 --- a/packages/dnb-design-system-portal/src/shared/menu/StickyMenuBar.module.scss +++ b/packages/dnb-design-system-portal/src/shared/menu/StickyMenuBar.module.scss @@ -63,7 +63,7 @@ display: none; } } - @include allAbove(small, math.div(1, 16)) { + @include allAbove(small) { #toggle-main-menu-small-screen { display: none; } diff --git a/packages/dnb-eufemia/src/components/accordion/__tests__/__snapshots__/Accordion.test.tsx.snap b/packages/dnb-eufemia/src/components/accordion/__tests__/__snapshots__/Accordion.test.tsx.snap index 5787346ae01..8699fd43314 100644 --- a/packages/dnb-eufemia/src/components/accordion/__tests__/__snapshots__/Accordion.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/accordion/__tests__/__snapshots__/Accordion.test.tsx.snap @@ -228,7 +228,7 @@ p > .dnb-icon { position: static; max-width: 60rem; } -@media screen and (min-width: 40.0625em) { +@media screen and (min-width: 40.00625em) { .dnb-accordion-group--single-container .dnb-accordion__header { width: 40%; } @@ -240,7 +240,7 @@ p > .dnb-icon { margin-top: 0; } } -@media screen and (min-width: 40.0625em) { +@media screen and (min-width: 40.00625em) { .dnb-accordion-group--single-container .dnb-accordion > .dnb-accordion__header .dnb-accordion__header__icon { transform: rotate(-90deg); } @@ -248,7 +248,7 @@ p > .dnb-icon { .dnb-accordion-group--single-container .dnb-accordion-group__children { max-width: 60rem; } -@media screen and (min-width: 40.0625em) { +@media screen and (min-width: 40.00625em) { .dnb-accordion-group--single-container .dnb-accordion-group__children { position: relative; display: flex; diff --git a/packages/dnb-eufemia/src/components/autocomplete/__tests__/__snapshots__/Autocomplete.test.tsx.snap b/packages/dnb-eufemia/src/components/autocomplete/__tests__/__snapshots__/Autocomplete.test.tsx.snap index c72de206322..85cdfea5033 100644 --- a/packages/dnb-eufemia/src/components/autocomplete/__tests__/__snapshots__/Autocomplete.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/autocomplete/__tests__/__snapshots__/Autocomplete.test.tsx.snap @@ -2241,7 +2241,7 @@ html:not([data-whatintent=touch]) .dnb-dropdown__status--error:not(.dnb-dropdown border-radius: 50%; } } -@media screen and (min-width: 40.0625em) { +@media screen and (min-width: 40.00625em) { .dnb-dropdown--action-menu .dnb-dropdown__shell .dnb-dropdown__text { padding: 0 0.5rem; } @@ -2764,7 +2764,7 @@ html[data-visual-test] .dnb-autocomplete .dnb-input__submit-button__button .dnb- z-index: 3; cursor: text; } -@media screen and (min-width: 40.0625em) { +@media screen and (min-width: 40.00625em) { .dnb-autocomplete .dnb-input--has-inner-element .dnb-input__placeholder, .dnb-autocomplete .dnb-input--has-inner-element .dnb-input__input { padding-right: 0 !important; } diff --git a/packages/dnb-eufemia/src/components/breadcrumb/__tests__/__snapshots__/Breadcrumb.test.tsx.snap b/packages/dnb-eufemia/src/components/breadcrumb/__tests__/__snapshots__/Breadcrumb.test.tsx.snap index f26defe6a6f..e91825894cb 100644 --- a/packages/dnb-eufemia/src/components/breadcrumb/__tests__/__snapshots__/Breadcrumb.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/breadcrumb/__tests__/__snapshots__/Breadcrumb.test.tsx.snap @@ -637,7 +637,7 @@ button.dnb-button::-moz-focus-inner { ); } } -@media screen and (max-width: 60em) and (min-width: 40.0625em) { +@media screen and (max-width: 60em) and (min-width: 40.00625em) { .dnb-section { --breakout: var(--breakout--medium, var(--breakout--fallback)); --background-color--value: var(--background-color--medium); @@ -650,7 +650,7 @@ button.dnb-button::-moz-focus-inner { ); } } -@media screen and (min-width: 60.0625em) { +@media screen and (min-width: 60.00625em) { .dnb-section { --breakout: var(--breakout--large, var(--breakout--fallback)); --background-color--value: var(--background-color--large); @@ -791,7 +791,7 @@ html[data-visual-test] .dnb-breadcrumb__multiple .dnb-breadcrumb__item { display: none; } } -@media screen and (min-width: 60.0625em) { +@media screen and (min-width: 60.00625em) { .dnb-breadcrumb--variant-responsive .dnb-breadcrumb__toggle { display: none; } diff --git a/packages/dnb-eufemia/src/components/date-picker/__tests__/__snapshots__/DatePicker.test.tsx.snap b/packages/dnb-eufemia/src/components/date-picker/__tests__/__snapshots__/DatePicker.test.tsx.snap index cf5c95eb754..0a21ae3141d 100644 --- a/packages/dnb-eufemia/src/components/date-picker/__tests__/__snapshots__/DatePicker.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/date-picker/__tests__/__snapshots__/DatePicker.test.tsx.snap @@ -3650,7 +3650,7 @@ button .dnb-form-status__text { .dnb-toggle-button-group__suffix { font-size: var(--font-size-basis); } -@media screen and (min-width: 40.0625em) { +@media screen and (min-width: 40.00625em) { .dnb-form-label + .dnb-toggle-button { transform: translateY(-0.5rem); } diff --git a/packages/dnb-eufemia/src/components/drawer/__tests__/__snapshots__/Drawer.test.tsx.snap b/packages/dnb-eufemia/src/components/drawer/__tests__/__snapshots__/Drawer.test.tsx.snap index 70f71535997..10641a58d69 100644 --- a/packages/dnb-eufemia/src/components/drawer/__tests__/__snapshots__/Drawer.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/drawer/__tests__/__snapshots__/Drawer.test.tsx.snap @@ -946,7 +946,7 @@ html[data-visual-test] .dnb-modal__overlay, .dnb-modal__overlay--no-animation { .dnb-drawer--spacing .dnb-drawer__content { padding: 0 var(--drawer-spacing); } -@media screen and (min-width: 80.0625em) { +@media screen and (min-width: 80.00625em) { .dnb-drawer--spacing .dnb-drawer__content { padding: 0 calc(var(--drawer-spacing) * 1.75); } @@ -1054,7 +1054,7 @@ html:not([data-visual-test]) .dnb-drawer--hide.dnb-drawer--bottom { .dnb-drawer--spacing .dnb-drawer__header { padding: 0 var(--drawer-spacing); } -@media screen and (min-width: 80.0625em) { +@media screen and (min-width: 80.00625em) { .dnb-drawer--spacing .dnb-drawer__header { padding: 0 calc(var(--drawer-spacing) * 1.75); } @@ -1094,7 +1094,7 @@ html:not([data-visual-test]) .dnb-drawer--hide.dnb-drawer--bottom { margin: var(--drawer-spacing) 0; padding: 0 var(--drawer-spacing); } -@media screen and (min-width: 80.0625em) { +@media screen and (min-width: 80.00625em) { .dnb-drawer--spacing .dnb-drawer__navigation.dnb-section { padding: 0 calc(var(--drawer-spacing) * 1.75); } diff --git a/packages/dnb-eufemia/src/components/dropdown/__tests__/__snapshots__/Dropdown.test.tsx.snap b/packages/dnb-eufemia/src/components/dropdown/__tests__/__snapshots__/Dropdown.test.tsx.snap index efe6d1b6ab1..af26dc0b783 100644 --- a/packages/dnb-eufemia/src/components/dropdown/__tests__/__snapshots__/Dropdown.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/dropdown/__tests__/__snapshots__/Dropdown.test.tsx.snap @@ -918,7 +918,7 @@ html:not([data-whatintent=touch]) .dnb-dropdown__status--error:not(.dnb-dropdown border-radius: 50%; } } -@media screen and (min-width: 40.0625em) { +@media screen and (min-width: 40.00625em) { .dnb-dropdown--action-menu .dnb-dropdown__shell .dnb-dropdown__text { padding: 0 0.5rem; } diff --git a/packages/dnb-eufemia/src/components/form-row/__tests__/__snapshots__/FormRow.test.tsx.snap b/packages/dnb-eufemia/src/components/form-row/__tests__/__snapshots__/FormRow.test.tsx.snap index 49c1f195551..7383cea9f08 100644 --- a/packages/dnb-eufemia/src/components/form-row/__tests__/__snapshots__/FormRow.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/form-row/__tests__/__snapshots__/FormRow.test.tsx.snap @@ -50,7 +50,7 @@ exports[`FormRow scss has to match style dependencies css 1`] = ` margin-top: 1rem; } } -@media screen and (min-width: 40.0625em) { +@media screen and (min-width: 40.00625em) { .dnb-form-row--centered.dnb-form-row:not(.dnb-form-row--vertical) .dnb-form-row__content { align-items: center; } diff --git a/packages/dnb-eufemia/src/components/global-status/__tests__/__snapshots__/GlobalStatus.test.tsx.snap b/packages/dnb-eufemia/src/components/global-status/__tests__/__snapshots__/GlobalStatus.test.tsx.snap index cf6a2a98f79..da959a36d6b 100644 --- a/packages/dnb-eufemia/src/components/global-status/__tests__/__snapshots__/GlobalStatus.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/global-status/__tests__/__snapshots__/GlobalStatus.test.tsx.snap @@ -637,7 +637,7 @@ button.dnb-button::-moz-focus-inner { ); } } -@media screen and (max-width: 60em) and (min-width: 40.0625em) { +@media screen and (max-width: 60em) and (min-width: 40.00625em) { .dnb-section { --breakout: var(--breakout--medium, var(--breakout--fallback)); --background-color--value: var(--background-color--medium); @@ -650,7 +650,7 @@ button.dnb-button::-moz-focus-inner { ); } } -@media screen and (min-width: 60.0625em) { +@media screen and (min-width: 60.00625em) { .dnb-section { --breakout: var(--breakout--large, var(--breakout--fallback)); --background-color--value: var(--background-color--large); diff --git a/packages/dnb-eufemia/src/components/section/__tests__/__snapshots__/Section.test.tsx.snap b/packages/dnb-eufemia/src/components/section/__tests__/__snapshots__/Section.test.tsx.snap index ae2b3416a3e..19b393353c0 100644 --- a/packages/dnb-eufemia/src/components/section/__tests__/__snapshots__/Section.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/section/__tests__/__snapshots__/Section.test.tsx.snap @@ -74,7 +74,7 @@ exports[`Section scss has to match style dependencies css 1`] = ` ); } } -@media screen and (max-width: 60em) and (min-width: 40.0625em) { +@media screen and (max-width: 60em) and (min-width: 40.00625em) { .dnb-section { --breakout: var(--breakout--medium, var(--breakout--fallback)); --background-color--value: var(--background-color--medium); @@ -87,7 +87,7 @@ exports[`Section scss has to match style dependencies css 1`] = ` ); } } -@media screen and (min-width: 60.0625em) { +@media screen and (min-width: 60.00625em) { .dnb-section { --breakout: var(--breakout--large, var(--breakout--fallback)); --background-color--value: var(--background-color--large); diff --git a/packages/dnb-eufemia/src/components/skip-content/__tests__/__snapshots__/SkipContent.test.tsx.snap b/packages/dnb-eufemia/src/components/skip-content/__tests__/__snapshots__/SkipContent.test.tsx.snap index 823f20bc06c..df8d760e814 100644 --- a/packages/dnb-eufemia/src/components/skip-content/__tests__/__snapshots__/SkipContent.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/skip-content/__tests__/__snapshots__/SkipContent.test.tsx.snap @@ -637,7 +637,7 @@ button.dnb-button::-moz-focus-inner { ); } } -@media screen and (max-width: 60em) and (min-width: 40.0625em) { +@media screen and (max-width: 60em) and (min-width: 40.00625em) { .dnb-section { --breakout: var(--breakout--medium, var(--breakout--fallback)); --background-color--value: var(--background-color--medium); @@ -650,7 +650,7 @@ button.dnb-button::-moz-focus-inner { ); } } -@media screen and (min-width: 60.0625em) { +@media screen and (min-width: 60.00625em) { .dnb-section { --breakout: var(--breakout--large, var(--breakout--fallback)); --background-color--value: var(--background-color--large); diff --git a/packages/dnb-eufemia/src/components/space/__tests__/__snapshots__/Space.test.tsx.snap b/packages/dnb-eufemia/src/components/space/__tests__/__snapshots__/Space.test.tsx.snap index 9edb00fe3e3..87f80887cfb 100644 --- a/packages/dnb-eufemia/src/components/space/__tests__/__snapshots__/Space.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/space/__tests__/__snapshots__/Space.test.tsx.snap @@ -591,7 +591,7 @@ exports[`Space scss has to match style dependencies css 1`] = ` --padding-bottom: var(--space-b-s); } } -@media screen and (max-width: 60em) and (min-width: 40.0625em) { +@media screen and (max-width: 60em) and (min-width: 40.00625em) { .dnb-space { --padding-right: var(--space-r-m); --padding-left: var(--space-l-m); @@ -599,7 +599,7 @@ exports[`Space scss has to match style dependencies css 1`] = ` --padding-bottom: var(--space-b-m); } } -@media screen and (min-width: 60.0625em) { +@media screen and (min-width: 60.00625em) { .dnb-space { --padding-right: var(--space-r-l); --padding-left: var(--space-l-l); diff --git a/packages/dnb-eufemia/src/components/toggle-button/__tests__/__snapshots__/ToggleButton.test.tsx.snap b/packages/dnb-eufemia/src/components/toggle-button/__tests__/__snapshots__/ToggleButton.test.tsx.snap index bf2882e4b57..3aa7134d415 100644 --- a/packages/dnb-eufemia/src/components/toggle-button/__tests__/__snapshots__/ToggleButton.test.tsx.snap +++ b/packages/dnb-eufemia/src/components/toggle-button/__tests__/__snapshots__/ToggleButton.test.tsx.snap @@ -1771,7 +1771,7 @@ button .dnb-form-status__text { .dnb-toggle-button-group__suffix { font-size: var(--font-size-basis); } -@media screen and (min-width: 40.0625em) { +@media screen and (min-width: 40.00625em) { .dnb-form-label + .dnb-toggle-button { transform: translateY(-0.5rem); } diff --git a/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__snapshots__/DrawerList.test.tsx.snap b/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__snapshots__/DrawerList.test.tsx.snap index 54b153e1574..fe3fea65d45 100644 --- a/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__snapshots__/DrawerList.test.tsx.snap +++ b/packages/dnb-eufemia/src/fragments/drawer-list/__tests__/__snapshots__/DrawerList.test.tsx.snap @@ -174,7 +174,7 @@ html[data-visual-test] .dnb-drawer-list--scroll .dnb-drawer-list__options, .dnb- word-break: break-word; font-size: inherit; } -@media screen and (min-width: 40.0625em) { +@media screen and (min-width: 40.00625em) { .dnb-drawer-list__option__suffix { grid-column: 2; grid-row: 1/-1; @@ -322,7 +322,7 @@ html[data-visual-test] .dnb-drawer-list:not(.dnb-drawer-list--opened) .dnb-drawe justify-content: flex-start; align-items: center; } -@media screen and (min-width: 40.0625em) { +@media screen and (min-width: 40.00625em) { .dnb-drawer-list--action-menu.dnb-drawer-list--is-popup.dnb-drawer-list--left .dnb-drawer-list__list { left: 0; } @@ -528,7 +528,7 @@ html:not([data-whatintent=touch]) .dnb-drawer-list__option--selected .dnb-drawer .dnb-drawer-list__option--selected .dnb-drawer-list__option__inner::before { visibility: hidden; } -@media screen and (min-width: 40.0625em) { +@media screen and (min-width: 40.00625em) { .dnb-drawer-list__option--selected .dnb-drawer-list__option__suffix { z-index: 2; background-color: inherit; diff --git a/packages/dnb-eufemia/src/shared/MediaQueryUtils.ts b/packages/dnb-eufemia/src/shared/MediaQueryUtils.ts index 54f54d1c6c2..11d8ad002fe 100644 --- a/packages/dnb-eufemia/src/shared/MediaQueryUtils.ts +++ b/packages/dnb-eufemia/src/shared/MediaQueryUtils.ts @@ -27,7 +27,6 @@ export type MediaQueryCondition = minWidth?: number | string | MediaQuerySizes maxWidth?: number | string | MediaQuerySizes orientation?: string - handheld?: boolean not?: boolean all?: boolean monochrome?: boolean @@ -196,10 +195,10 @@ export function buildQuery( listOfQueries = listOfQueries.concat( combineQueries(when, breakpoints, options) ) - } else if (when && typeof when === 'object') { - const query = convertToMediaQuery(when, breakpoints, options) - if (query) { - listOfQueries.push(query) + } else if (typeof when === 'object') { + const queryItem = convertToMediaQuery(when, breakpoints, options) + if (queryItem) { + listOfQueries.push(queryItem) } } @@ -249,15 +248,8 @@ function combineQueries( const query = convertToMediaQuery(when, breakpoints, options) if (query) { - switch (arr[i - 1]) { - case 'and': - listOfQueries.push('and') - break - - case 'or': - default: - listOfQueries.push(', ') - break + if (query !== 'and' && arr[i - 1] !== 'and') { + listOfQueries.push(', ') } listOfQueries.push(query) @@ -303,9 +295,11 @@ export function convertToMediaQuery( return query.reduce((acc, q, index) => { acc += objToMediaQuery(q, breakpoints, options) if (index < query.length - 1) { - acc += ', ' + if (q !== 'and' && query[index + 1] !== 'and') { + acc += ',' + } + acc += ' ' } - return acc }, '') as string } @@ -322,6 +316,9 @@ function objToMediaQuery( breakpoints: MediaQueryBreakpoints = null, options?: MediaQueryOptions ): string { + if (typeof obj === 'string') { + return obj + } let hasNot = false let query: string | Array = Object.keys(obj).reduce( (acc, feature) => { @@ -332,6 +329,9 @@ function objToMediaQuery( hasNot = true return acc } + if (feature === 'monochrome') { + feature = `(${feature})` + } if (feature === 'min' || feature === 'max') { feature = `${feature}-width` @@ -370,7 +370,7 @@ function objToMediaQuery( const size = parseFloat(query.match(/\(min-width: ([0-9]+)em\)/)[1]) || 0 if (size > 0) { - const correctedSize = (size * 16 + 1) / 16 + const correctedSize = (size * 16 + 0.1) / 16 // add 0.1px to the minimum to avoid overlap with and equivalent maximum query = query.replace( /(min-width: [0-9]+em)/, `min-width: ${correctedSize}em` diff --git a/packages/dnb-eufemia/src/shared/__tests__/MediaQuery.test.tsx b/packages/dnb-eufemia/src/shared/__tests__/MediaQuery.test.tsx index 6ef48f95da8..29afb72cff4 100644 --- a/packages/dnb-eufemia/src/shared/__tests__/MediaQuery.test.tsx +++ b/packages/dnb-eufemia/src/shared/__tests__/MediaQuery.test.tsx @@ -42,7 +42,7 @@ describe('MediaQuery', () => { it('renders with props as an object', () => { matchMedia.useMediaQuery( - '(min-width: 60.0625em) and (max-width: 72em)' + '(min-width: 60.00625em) and (max-width: 72em)' ) const props: MediaQueryProps = { @@ -56,7 +56,7 @@ describe('MediaQuery', () => { it('should match for query with medium width', () => { matchMedia.useMediaQuery( - '(min-width: 60.0625em) and (max-width: 72em)' + '(min-width: 60.00625em) and (max-width: 72em)' ) render( @@ -69,7 +69,7 @@ describe('MediaQuery', () => { it('should match for query when different breakpoints are given', () => { matchMedia.useMediaQuery( - '(min-width: 40.0625em) and (max-width: 80em), (min-width: 0) and (max-width: 30rem), (max-width: 90em)' + '(min-width: 40.00625em) and (max-width: 80em), (min-width: 0) and (max-width: 30rem), (max-width: 90em)' ) render( @@ -162,7 +162,7 @@ describe('MediaQuery', () => { it('should match for query with medium and large width', () => { matchMedia.useMediaQuery( - '(min-width: 60.0625em) and (max-width: 72em), (min-width: 72.0625em) and (max-width: 80em)' + '(min-width: 60.00625em) and (max-width: 72em), (min-width: 72.00625em) and (max-width: 80em)' ) render( diff --git a/packages/dnb-eufemia/src/shared/__tests__/MediaQueryUtils.test.tsx b/packages/dnb-eufemia/src/shared/__tests__/MediaQueryUtils.test.tsx index a45c2f5cc6d..df2b5edb4ba 100644 --- a/packages/dnb-eufemia/src/shared/__tests__/MediaQueryUtils.test.tsx +++ b/packages/dnb-eufemia/src/shared/__tests__/MediaQueryUtils.test.tsx @@ -87,7 +87,9 @@ describe('buildQuery', () => { }) it('should return query string for media type with not', () => { - expect(buildQuery({ when: { handheld: false } })).toBe('not handheld') + expect(buildQuery({ when: { monochrome: false } })).toBe( + 'not (monochrome)' + ) }) it('should return query string for media type with orientation', () => { @@ -228,15 +230,30 @@ describe('buildQuery', () => { buildQuery({ when: [ { minWidth: 10 }, - { handheld: true, orientation: 'landscape' }, + { monochrome: true, orientation: 'landscape' }, + ], + }) + ).toBe('(min-width: 10em), (monochrome) and (orientation: landscape)') + }) + + it('should combine multiple media queries seperated by "and"', () => { + expect( + buildQuery({ + when: [ + { minWidth: 10 }, + 'and', + { monochrome: true, orientation: 'landscape' }, + { maxWidth: 50 }, ], }) - ).toBe('(min-width: 10em), handheld and (orientation: landscape)') + ).toBe( + '(min-width: 10em) and (monochrome) and (orientation: landscape), (max-width: 50em)' + ) }) it('should only return feature if its value is true', () => { expect(buildQuery({ when: { all: true, monochrome: true } })).toBe( - 'all and monochrome' + 'all and (monochrome)' ) }) }) @@ -278,7 +295,9 @@ describe('convertToMediaQuery', () => { }) it('should return query string for media type with not', () => { - expect(convertToMediaQuery({ handheld: false })).toBe('not handheld') + expect(convertToMediaQuery({ monochrome: false })).toBe( + 'not (monochrome)' + ) }) it('should return query string for media type with orientation', () => { @@ -353,14 +372,27 @@ describe('convertToMediaQuery', () => { expect( convertToMediaQuery([ { minWidth: 10 }, - { handheld: true, orientation: 'landscape' }, + { monochrome: true, orientation: 'landscape' }, ]) - ).toBe('(min-width: 10em), handheld and (orientation: landscape)') + ).toBe('(min-width: 10em), (monochrome) and (orientation: landscape)') + }) + + it('should combine multiple media queries seperated by "and"', () => { + expect( + convertToMediaQuery([ + { minWidth: 10 }, + 'and', + { monochrome: true, orientation: 'landscape' }, + { maxWidth: 50 }, + ]) + ).toBe( + '(min-width: 10em) and (monochrome) and (orientation: landscape), (max-width: 50em)' + ) }) it('should only return feature if its value is true', () => { expect(convertToMediaQuery({ all: true, monochrome: true })).toBe( - 'all and monochrome' + 'all and (monochrome)' ) }) }) diff --git a/packages/dnb-eufemia/src/shared/__tests__/useMedia.test.tsx b/packages/dnb-eufemia/src/shared/__tests__/useMedia.test.tsx index a4f84981385..a72eac6e455 100644 --- a/packages/dnb-eufemia/src/shared/__tests__/useMedia.test.tsx +++ b/packages/dnb-eufemia/src/shared/__tests__/useMedia.test.tsx @@ -842,7 +842,7 @@ describe('useMedia', () => { }) it('will return positive isMedium', () => { - const query = `(min-width: 40.0625em) and (max-width: 60em)` + const query = `(min-width: 40.00625em) and (max-width: 60em)` matchMedia.useMediaQuery(query) const { result } = renderHook(useMedia, { wrapper }) @@ -858,7 +858,7 @@ describe('useMedia', () => { }) it('will return positive isLarge', () => { - const query = `(min-width: 60.0625em)` + const query = `(min-width: 60.00625em)` matchMedia.useMediaQuery(query) const { result } = renderHook(useMedia, { wrapper }) diff --git a/packages/dnb-eufemia/src/shared/__tests__/useMediaQuery.test.tsx b/packages/dnb-eufemia/src/shared/__tests__/useMediaQuery.test.tsx index 48188252943..960ef08afea 100644 --- a/packages/dnb-eufemia/src/shared/__tests__/useMediaQuery.test.tsx +++ b/packages/dnb-eufemia/src/shared/__tests__/useMediaQuery.test.tsx @@ -55,7 +55,7 @@ describe('useMediaQuery', () => { it('should have valid strings inside render and rerender', () => { matchMedia.useMediaQuery( - '(min-width: 60.0625em) and (max-width: 72em)' + '(min-width: 60.00625em) and (max-width: 72em)' ) const { rerender } = render( @@ -110,7 +110,7 @@ describe('useMediaQuery', () => { it('should handle media query changes', () => { matchMedia.useMediaQuery( - 'not screen and (min-width: 40.0625em) and (max-width: 72em)' + 'not screen and (min-width: 40.00625em) and (max-width: 72em)' ) const match1Handler = jest.fn() diff --git a/packages/dnb-eufemia/src/style/core/utilities.scss b/packages/dnb-eufemia/src/style/core/utilities.scss index 58d244fcfab..e9ffe1696cb 100644 --- a/packages/dnb-eufemia/src/style/core/utilities.scss +++ b/packages/dnb-eufemia/src/style/core/utilities.scss @@ -291,18 +291,23 @@ $breakpoint-offset: 0; // @include allAbove(medium){ styles go here.. } // $offset and $list are needed to provide global customization options @mixin allAbove($size, $offset: $breakpoint-offset, $list: $breakpoints) { - @media screen and (min-width: (if(map-has-key($list, $size), map-get($list, $size), #{$size}) + $offset + 0.0625em)) { + @media screen and (min-width: (if(map-has-key($list, $size), map-get($list, $size), $size) + $offset + 0.00625)) { @content; } } @mixin allBelow($size, $offset: $breakpoint-offset, $list: $breakpoints) { - @media screen and (max-width: (if(map-has-key($list, $size), map-get($list, $size), #{$size}) + $offset)) { + @media screen and (max-width: (if(map-has-key($list, $size), map-get($list, $size), $size) + $offset)) { @content; } } -@mixin allBetween($fromSize, $toSize, $fromOffset: null, $toOffset: null) { - @include allBelow($toSize, if($toOffset == null, 0, $toOffset)) { - @include allAbove($fromSize, if($fromOffset == null, 0, $fromOffset)) { +@mixin allBetween( + $fromSize, + $toSize, + $fromOffset: $breakpoint-offset, + $toOffset: $breakpoint-offset +) { + @include allBelow($toSize, $toOffset) { + @include allAbove($fromSize, $fromOffset) { @content; } } diff --git a/packages/dnb-eufemia/src/style/elements/__tests__/__snapshots__/Elements.test.js.snap b/packages/dnb-eufemia/src/style/elements/__tests__/__snapshots__/Elements.test.js.snap index 0d6d3c820c9..c96e523debd 100644 --- a/packages/dnb-eufemia/src/style/elements/__tests__/__snapshots__/Elements.test.js.snap +++ b/packages/dnb-eufemia/src/style/elements/__tests__/__snapshots__/Elements.test.js.snap @@ -483,7 +483,7 @@ del .dnb-code { margin-top: calc(var(--row-gap) + 0.5rem); margin-left: 2rem; } -@media screen and (min-width: 40.0625em) { +@media screen and (min-width: 40.00625em) { .dnb-dl__layout--horizontal { --dt-max-width: 40%; --dd-max-width: calc(60% - 1rem);