From a0f44b356518ba1ce2dd199a1de46afe82585f3b Mon Sep 17 00:00:00 2001 From: Kasper Birch Date: Sat, 1 Jun 2024 14:34:51 +0200 Subject: [PATCH 1/6] Add new `banner` component This component contains a required link and title. It can be used with a description and a background image to enhance visual appeal. The title field is a `ReactNode` where the `` (underlined) tag has a custom underline SVG placed beneath it. --- base.scss | 1 + public/icons/basic/icon-underlined.svg | 3 + src/stories/Library/banner/Banner.stories.tsx | 53 +++++++++++++++ src/stories/Library/banner/Banner.tsx | 40 ++++++++++++ src/stories/Library/banner/banner.scss | 65 +++++++++++++++++++ src/styles/scss/tools/mixins.tools.scss | 24 +++++++ 6 files changed, 186 insertions(+) create mode 100644 public/icons/basic/icon-underlined.svg create mode 100644 src/stories/Library/banner/Banner.stories.tsx create mode 100644 src/stories/Library/banner/Banner.tsx create mode 100644 src/stories/Library/banner/banner.scss diff --git a/base.scss b/base.scss index 1c94a6f29..5e584d9d1 100644 --- a/base.scss +++ b/base.scss @@ -144,6 +144,7 @@ @import "./src/stories/Library/filtered-event-list/filtered-event-list"; @import "./src/stories/Library/event-list-stacked/event-list-stacked"; @import "./src/stories/Library/material-search/material-search"; +@import "./src/stories/Library/banner/banner"; // Autosuggest block styling needs to be loaded before the rest of the scss for autosuggest @import "./src/stories/Blocks/autosuggest/autosuggest"; diff --git a/public/icons/basic/icon-underlined.svg b/public/icons/basic/icon-underlined.svg new file mode 100644 index 000000000..8352ab299 --- /dev/null +++ b/public/icons/basic/icon-underlined.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/stories/Library/banner/Banner.stories.tsx b/src/stories/Library/banner/Banner.stories.tsx new file mode 100644 index 000000000..f8862fcd0 --- /dev/null +++ b/src/stories/Library/banner/Banner.stories.tsx @@ -0,0 +1,53 @@ +import { ComponentStory, ComponentMeta } from "@storybook/react"; +import { withDesign } from "storybook-addon-designs"; +import Banner from "./Banner"; +import ImageCredited from "../image-credited/ImageCredited"; + +export default { + title: "Library / Banner", + component: Banner, + decorators: [withDesign], + parameters: { + design: { + type: "figma", + url: "https://www.figma.com/design/Zx9GrkFA3l4ISvyZD2q0Qi/Designsystem?node-id=446-6957&t=IWAOniAbcjV2y2Hf-4", + }, + }, + argTypes: { + image: { + defaultValue: ( + + ), + }, + title: { + name: "Title", + defaultValue: "Hvad skal jeg høre?", + control: { type: "text" }, + }, + description: { + name: "Description", + defaultValue: + "Om du er dedikeret musiknørd eller moderat musikinteresseret, så er dette siden til dig. Her kan du finde anbefalinger, digitale musikmagasiner, nyheder, musiklitteratur og meget mere.", + control: { type: "text" }, + }, + }, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ; + +export const Default = Template.bind({}); + +export const NoImage = Template.bind({}); +NoImage.args = { + title: "Title uden billede", + image: undefined, +}; + +export const NoDescription = Template.bind({}); +NoDescription.args = { + title: "Banner uden beskrivelse", + description: undefined, +}; diff --git a/src/stories/Library/banner/Banner.tsx b/src/stories/Library/banner/Banner.tsx new file mode 100644 index 000000000..e9bf05354 --- /dev/null +++ b/src/stories/Library/banner/Banner.tsx @@ -0,0 +1,40 @@ +import { FC, ReactNode } from "react"; +import clsx from "clsx"; +import MediaContainer from "../media-container/MediaContainer"; +import { ReactComponent as ArrowLargeRight } from "../Arrows/icon-arrow-ui/icon-arrow-ui-large-right.svg"; + +type BannerType = { + title: string; + image?: ReactNode; + description?: string; +}; + +const Banner: FC = ({ image, title, description }) => { + return ( + + {image && ( +
+ +
+ )} +
+

) words. + // eslint-disable-next-line react/no-danger + dangerouslySetInnerHTML={{ __html: title }} + /> + {description && ( +

{description}

+ )} + +

+
+ ); +}; + +export default Banner; diff --git a/src/stories/Library/banner/banner.scss b/src/stories/Library/banner/banner.scss new file mode 100644 index 000000000..7d11d3b99 --- /dev/null +++ b/src/stories/Library/banner/banner.scss @@ -0,0 +1,65 @@ +@mixin banner-height { + height: 500px; + @include media-query__medium { + height: 810px; + } +} + +@mixin banner-text-margin { + margin-bottom: $s-md; + @include media-query__medium { + margin-bottom: $s-xl; + } +} + +.banner { + @include layout-container($layout__max-width--large, 0); + @include banner-height; + display: grid; + grid-template-rows: 1fr auto 1fr; + text-decoration: none; +} + +.banner-content { + padding: $s-xl; + max-width: 375px; + background-color: white; + grid-row: 2; + grid-column: 1; + + @include media-query__medium { + padding: $s-2xl; + max-width: 575px; + margin-left: 250px; + } + + &--no-image { + max-width: unset; + background-color: unset; + margin-left: unset; + text-align: center; + } +} + +.banner-content__title { + @include typography($typo__h1); + @include underlined-title; + @include banner-text-margin; +} + +.banner-content__description { + @include typography($typo__body-medium); + @include banner-text-margin; + color: $color__global-grey; +} + +.banner-visual { + grid-row: 1 / -1; + grid-column: 1; + + img { + @include banner-height; + object-fit: cover; + object-position: center; + } +} diff --git a/src/styles/scss/tools/mixins.tools.scss b/src/styles/scss/tools/mixins.tools.scss index a78af14f2..6a9803566 100644 --- a/src/styles/scss/tools/mixins.tools.scss +++ b/src/styles/scss/tools/mixins.tools.scss @@ -70,3 +70,27 @@ text-overflow: ellipsis; text-align: left; } + +/** + * Mixin `underlined-title` applies a custom underline effect to `` elements. + * - Uses a pseudo-element (::after) with a positioned SVG image as the underline. + * - Removes default text underline. + */ +@mixin underlined-title { + u { + position: relative; + text-decoration: none; + + &::after { + content: ""; + position: absolute; + bottom: -5px; + left: 0; + width: 100%; + height: 10px; + background-size: 100% 100%; + // Underlined icon from public/icons/basic/icon-underlined.svg + background-image: url("data:image/svg+xml,%3Csvg width='205' height='8' viewBox='0 0 205 8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1.11133 6.87425C73.7223 0.129386 128.695 1.4981 203.889 1.8943' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A"); + } + } +} From e2ea0db622516ec1bf5c07a89b8bf10e36420c5c Mon Sep 17 00:00:00 2001 From: Kasper Birch Date: Thu, 13 Jun 2024 15:31:07 +0200 Subject: [PATCH 2/6] Enhance `banner` layout with dynamic spacing Replaced the fixed `margin-left` pixels in `banner-content` with a new layout wrapper `banner__spacing` for dynamic spacing. --- src/stories/Library/banner/Banner.tsx | 32 ++++++++++++++------------ src/stories/Library/banner/banner.scss | 18 ++++++++------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/stories/Library/banner/Banner.tsx b/src/stories/Library/banner/Banner.tsx index e9bf05354..cd0ad0c7b 100644 --- a/src/stories/Library/banner/Banner.tsx +++ b/src/stories/Library/banner/Banner.tsx @@ -17,21 +17,23 @@ const Banner: FC = ({ image, title, description }) => { )} -
-

) words. - // eslint-disable-next-line react/no-danger - dangerouslySetInnerHTML={{ __html: title }} - /> - {description && ( -

{description}

- )} - +
+
+

) words. + // eslint-disable-next-line react/no-danger + dangerouslySetInnerHTML={{ __html: title }} + /> + {description && ( +

{description}

+ )} + +

); diff --git a/src/stories/Library/banner/banner.scss b/src/stories/Library/banner/banner.scss index 7d11d3b99..d5f065683 100644 --- a/src/stories/Library/banner/banner.scss +++ b/src/stories/Library/banner/banner.scss @@ -1,6 +1,6 @@ @mixin banner-height { height: 500px; - @include media-query__medium { + @include media-query__small { height: 810px; } } @@ -20,23 +20,25 @@ text-decoration: none; } +.banner__spacing { + @include layout-container($layout__max-width--medium, 0); + grid-row: 2; + grid-column: 1; +} + .banner-content { padding: $s-xl; - max-width: 375px; + max-width: 90%; background-color: white; - grid-row: 2; - grid-column: 1; - @include media-query__medium { + @include media-query__small { padding: $s-2xl; - max-width: 575px; - margin-left: 250px; + max-width: 60%; } &--no-image { max-width: unset; background-color: unset; - margin-left: unset; text-align: center; } } From d925093a871ff42a99bd3bd83929ae5a0132e8a2 Mon Sep 17 00:00:00 2001 From: Kasper Birch Date: Thu, 13 Jun 2024 17:13:54 +0200 Subject: [PATCH 3/6] Add `link` prop to `banner` --- src/stories/Library/banner/Banner.stories.tsx | 5 +++++ src/stories/Library/banner/Banner.tsx | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/stories/Library/banner/Banner.stories.tsx b/src/stories/Library/banner/Banner.stories.tsx index f8862fcd0..cffbbb34f 100644 --- a/src/stories/Library/banner/Banner.stories.tsx +++ b/src/stories/Library/banner/Banner.stories.tsx @@ -33,6 +33,11 @@ export default { "Om du er dedikeret musiknørd eller moderat musikinteresseret, så er dette siden til dig. Her kan du finde anbefalinger, digitale musikmagasiner, nyheder, musiklitteratur og meget mere.", control: { type: "text" }, }, + link: { + name: "Link", + defaultValue: "#", + control: { type: "text" }, + }, }, } as ComponentMeta; diff --git a/src/stories/Library/banner/Banner.tsx b/src/stories/Library/banner/Banner.tsx index cd0ad0c7b..2be85daa3 100644 --- a/src/stories/Library/banner/Banner.tsx +++ b/src/stories/Library/banner/Banner.tsx @@ -4,14 +4,15 @@ import MediaContainer from "../media-container/MediaContainer"; import { ReactComponent as ArrowLargeRight } from "../Arrows/icon-arrow-ui/icon-arrow-ui-large-right.svg"; type BannerType = { + link: string; title: string; image?: ReactNode; description?: string; }; -const Banner: FC = ({ image, title, description }) => { +const Banner: FC = ({ link, image, title, description }) => { return ( - + {image && (
From 5d24bbebb75bda318994f307aee7620201ce0810 Mon Sep 17 00:00:00 2001 From: LasseStaus Date: Wed, 19 Jun 2024 09:13:39 +0200 Subject: [PATCH 4/6] Refactor html/css for banner Rename mixin to be more generic Enhance responsiveness of component simplify grid stacking DDFFORM-540 --- src/stories/Library/banner/Banner.tsx | 28 ++++--- src/stories/Library/banner/banner.scss | 98 ++++++++++++++----------- src/styles/scss/tools/mixins.tools.scss | 4 +- 3 files changed, 72 insertions(+), 58 deletions(-) diff --git a/src/stories/Library/banner/Banner.tsx b/src/stories/Library/banner/Banner.tsx index 2be85daa3..9c7675a17 100644 --- a/src/stories/Library/banner/Banner.tsx +++ b/src/stories/Library/banner/Banner.tsx @@ -14,25 +14,29 @@ const Banner: FC = ({ link, image, title, description }) => { return ( {image && ( -
+
)} -
+
-

) words. - // eslint-disable-next-line react/no-danger - dangerouslySetInnerHTML={{ __html: title }} - /> - {description && ( -

{description}

+ {title && ( +

) words. + // eslint-disable-next-line react/no-danger + dangerouslySetInnerHTML={{ __html: title }} + /> )} + {description &&

{description}

}

diff --git a/src/stories/Library/banner/banner.scss b/src/stories/Library/banner/banner.scss index d5f065683..75c103bae 100644 --- a/src/stories/Library/banner/banner.scss +++ b/src/stories/Library/banner/banner.scss @@ -1,67 +1,77 @@ -@mixin banner-height { - height: 500px; - @include media-query__small { - height: 810px; - } -} - -@mixin banner-text-margin { - margin-bottom: $s-md; - @include media-query__medium { - margin-bottom: $s-xl; - } -} +$_banner-content-wrapper-width: 944px; +$_banner-content-max-width-small: 300px; +$_banner-content-max-width-medium: 569px; +$_banner-content-no-image-max-width-small: $layout__max-width--small; +$_banner-height-small: 500px; +$_banner-height-medium: 810px; .banner { @include layout-container($layout__max-width--large, 0); - @include banner-height; + display: grid; - grid-template-rows: 1fr auto 1fr; + align-items: center; text-decoration: none; + box-sizing: border-box; + background-color: $color__global-secondary; + height: $_banner-height-small; + + @include media-query__small { + height: $_banner-height-medium; + } } -.banner__spacing { - @include layout-container($layout__max-width--medium, 0); - grid-row: 2; +.banner__media-wrapper { + grid-row: 1; grid-column: 1; + height: $_banner-height-small; + @include media-query__small { + height: $_banner-height-medium; + } } -.banner-content { - padding: $s-xl; - max-width: 90%; - background-color: white; - +.banner__content-wrapper { + @include layout-container($_banner-content-wrapper-width, 0); + width: 100%; + grid-row: 1; + grid-column: 1; + padding: $s-2xl $s-2xl $s-2xl 0; @include media-query__small { - padding: $s-2xl; - max-width: 60%; + padding-right: unset; + } + &--no-image { + padding: unset; } +} + +.banner__content { + padding: $s-xl; + max-width: $_banner-content-max-width-small; + background-color: $color__global-white; + display: flex; + flex-direction: column; + gap: $s-md; &--no-image { - max-width: unset; + max-width: $_banner-content-max-width-small; + margin: 0 auto; background-color: unset; text-align: center; + align-items: center; + } + @include media-query__small { + gap: $s-lg; + padding: calc($s-2xl + 6px); + max-width: $_banner-content-max-width-medium; } } -.banner-content__title { - @include typography($typo__h1); - @include underlined-title; - @include banner-text-margin; +.banner__title { + @include typography($typo__h2); + @include u-extending-underline-style; } -.banner-content__description { - @include typography($typo__body-medium); - @include banner-text-margin; +.banner__description { + @include typography($typo__body-placeholder); + line-height: 160%; color: $color__global-grey; } - -.banner-visual { - grid-row: 1 / -1; - grid-column: 1; - - img { - @include banner-height; - object-fit: cover; - object-position: center; - } -} diff --git a/src/styles/scss/tools/mixins.tools.scss b/src/styles/scss/tools/mixins.tools.scss index 6a9803566..d789392c9 100644 --- a/src/styles/scss/tools/mixins.tools.scss +++ b/src/styles/scss/tools/mixins.tools.scss @@ -72,11 +72,11 @@ } /** - * Mixin `underlined-title` applies a custom underline effect to `` elements. + * Mixin `u-extending-underline-style` applies a custom underline effect to `` elements. * - Uses a pseudo-element (::after) with a positioned SVG image as the underline. * - Removes default text underline. */ -@mixin underlined-title { +@mixin u-extending-underline-style { u { position: relative; text-decoration: none; From 02fd75d90c95490036f4fde85e6447cfbb09a676 Mon Sep 17 00:00:00 2001 From: LasseStaus Date: Wed, 19 Jun 2024 09:14:00 +0200 Subject: [PATCH 5/6] Extend banner stories DDFFORM-540 --- src/stories/Library/banner/Banner.stories.tsx | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/stories/Library/banner/Banner.stories.tsx b/src/stories/Library/banner/Banner.stories.tsx index cffbbb34f..a01564384 100644 --- a/src/stories/Library/banner/Banner.stories.tsx +++ b/src/stories/Library/banner/Banner.stories.tsx @@ -47,12 +47,31 @@ export const Default = Template.bind({}); export const NoImage = Template.bind({}); NoImage.args = { + image: undefined, +}; + +export const NoImageOnlyTitle = Template.bind({}); +NoImageOnlyTitle.args = { title: "Title uden billede", image: undefined, + description: undefined, +}; + +export const NoImageOnlyDescription = Template.bind({}); +NoImageOnlyDescription.args = { + title: undefined, + image: undefined, + description: + "Om du er dedikeret musiknørd eller moderat musikinteresseret, så er dette siden til dig. Her kan du finde anbefalinger, digitale musikmagasiner, nyheder, musiklitteratur og meget mere.", }; -export const NoDescription = Template.bind({}); -NoDescription.args = { +export const WithImageOnlyTitle = Template.bind({}); +WithImageOnlyTitle.args = { title: "Banner uden beskrivelse", description: undefined, }; +export const WithImageOnlyDescription = Template.bind({}); +WithImageOnlyDescription.args = { + description: + "Om du er dedikeret musiknørd eller moderat musikinteresseret, så er dette siden til dig. Her kan du finde anbefalinger, digitale musikmagasiner, nyheder, musiklitteratur og meget mere.", +}; From 9a9e7c202d57c598d9aa10fdef2a5e1e4ebb9d44 Mon Sep 17 00:00:00 2001 From: LasseStaus Date: Thu, 20 Jun 2024 14:02:46 +0200 Subject: [PATCH 6/6] Rewrite to use backgroundImage MediaContainer / image credited is not being correctly used here. To avoid redundant markup we use backgroundImage inline instead. DDFFORM-540 --- src/stories/Library/banner/Banner.stories.tsx | 19 +++---- src/stories/Library/banner/Banner.tsx | 33 ++++++------ src/stories/Library/banner/banner.scss | 52 ++++++++++--------- src/styles/scss/tools/mixins.tools.scss | 6 ++- 4 files changed, 54 insertions(+), 56 deletions(-) diff --git a/src/stories/Library/banner/Banner.stories.tsx b/src/stories/Library/banner/Banner.stories.tsx index a01564384..4f5ac8a6b 100644 --- a/src/stories/Library/banner/Banner.stories.tsx +++ b/src/stories/Library/banner/Banner.stories.tsx @@ -1,7 +1,6 @@ -import { ComponentStory, ComponentMeta } from "@storybook/react"; +import { ComponentMeta, ComponentStory } from "@storybook/react"; import { withDesign } from "storybook-addon-designs"; import Banner from "./Banner"; -import ImageCredited from "../image-credited/ImageCredited"; export default { title: "Library / Banner", @@ -14,13 +13,9 @@ export default { }, }, argTypes: { - image: { - defaultValue: ( - - ), + imageSrc: { + defaultValue: "images/campaign_cover.jpg", + control: { type: "text" }, }, title: { name: "Title", @@ -47,20 +42,20 @@ export const Default = Template.bind({}); export const NoImage = Template.bind({}); NoImage.args = { - image: undefined, + imageSrc: undefined, }; export const NoImageOnlyTitle = Template.bind({}); NoImageOnlyTitle.args = { title: "Title uden billede", - image: undefined, + imageSrc: undefined, description: undefined, }; export const NoImageOnlyDescription = Template.bind({}); NoImageOnlyDescription.args = { title: undefined, - image: undefined, + imageSrc: undefined, description: "Om du er dedikeret musiknørd eller moderat musikinteresseret, så er dette siden til dig. Her kan du finde anbefalinger, digitale musikmagasiner, nyheder, musiklitteratur og meget mere.", }; diff --git a/src/stories/Library/banner/Banner.tsx b/src/stories/Library/banner/Banner.tsx index 9c7675a17..9bdb2787e 100644 --- a/src/stories/Library/banner/Banner.tsx +++ b/src/stories/Library/banner/Banner.tsx @@ -1,31 +1,30 @@ -import { FC, ReactNode } from "react"; import clsx from "clsx"; -import MediaContainer from "../media-container/MediaContainer"; +import { FC, ReactNode } from "react"; import { ReactComponent as ArrowLargeRight } from "../Arrows/icon-arrow-ui/icon-arrow-ui-large-right.svg"; type BannerType = { link: string; title: string; - image?: ReactNode; + imageSrc?: ReactNode; description?: string; }; -const Banner: FC = ({ link, image, title, description }) => { +const Banner: FC = ({ link, imageSrc, title, description }) => { + const backgroundImageStyle = imageSrc + ? { backgroundImage: `url(${imageSrc})` } + : {}; return ( -
- {image && ( -
- -
- )} -
+ +
{title && ( diff --git a/src/stories/Library/banner/banner.scss b/src/stories/Library/banner/banner.scss index 75c103bae..7a6bd4e47 100644 --- a/src/stories/Library/banner/banner.scss +++ b/src/stories/Library/banner/banner.scss @@ -1,5 +1,5 @@ $_banner-content-wrapper-width: 944px; -$_banner-content-max-width-small: 300px; +$_banner-content-max-width-small: 330px; $_banner-content-max-width-medium: 569px; $_banner-content-no-image-max-width-small: $layout__max-width--small; $_banner-height-small: 500px; @@ -13,51 +13,54 @@ $_banner-height-medium: 810px; text-decoration: none; box-sizing: border-box; background-color: $color__global-secondary; - height: $_banner-height-small; + min-height: $_banner-height-small; - @include media-query__small { - height: $_banner-height-medium; + &--has-image { + background-repeat: no-repeat; + background-position: center; + background-size: cover; + height: 100%; + width: 100%; } -} -.banner__media-wrapper { - grid-row: 1; - grid-column: 1; - height: $_banner-height-small; @include media-query__small { - height: $_banner-height-medium; + min-height: $_banner-height-medium; } } .banner__content-wrapper { @include layout-container($_banner-content-wrapper-width, 0); width: 100%; - grid-row: 1; - grid-column: 1; - padding: $s-2xl $s-2xl $s-2xl 0; + padding: $s-2xl 0; + + &--has-image { + padding-right: $s-2xl; + } + @include media-query__small { padding-right: unset; } - &--no-image { - padding: unset; - } } .banner__content { - padding: $s-xl; - max-width: $_banner-content-max-width-small; - background-color: $color__global-white; display: flex; flex-direction: column; + align-items: center; gap: $s-md; + text-align: center; + padding: $s-xl; + margin: 0 auto; + max-width: $_banner-content-max-width-medium; - &--no-image { + &--has-image { + padding: $s-xl; + margin: unset; + text-align: unset; + align-items: unset; + background-color: $color__global-white; max-width: $_banner-content-max-width-small; - margin: 0 auto; - background-color: unset; - text-align: center; - align-items: center; } + @include media-query__small { gap: $s-lg; padding: calc($s-2xl + 6px); @@ -73,5 +76,4 @@ $_banner-height-medium: 810px; .banner__description { @include typography($typo__body-placeholder); line-height: 160%; - color: $color__global-grey; } diff --git a/src/styles/scss/tools/mixins.tools.scss b/src/styles/scss/tools/mixins.tools.scss index d789392c9..62a722ab6 100644 --- a/src/styles/scss/tools/mixins.tools.scss +++ b/src/styles/scss/tools/mixins.tools.scss @@ -87,8 +87,10 @@ bottom: -5px; left: 0; width: 100%; - height: 10px; - background-size: 100% 100%; + height: 12px; + background-repeat: no-repeat; + background-position: left; + background-size: cover; // Underlined icon from public/icons/basic/icon-underlined.svg background-image: url("data:image/svg+xml,%3Csvg width='205' height='8' viewBox='0 0 205 8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1.11133 6.87425C73.7223 0.129386 128.695 1.4981 203.889 1.8943' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E%0A"); }