diff --git a/src/layouts/EditHomepage/HomepagePreview.tsx b/src/layouts/EditHomepage/HomepagePreview.tsx index aaf401745..bb86e43de 100644 --- a/src/layouts/EditHomepage/HomepagePreview.tsx +++ b/src/layouts/EditHomepage/HomepagePreview.tsx @@ -5,6 +5,7 @@ import { useParams } from "react-router-dom" import editorStyles from "styles/isomer-cms/pages/Editor.module.scss" +import { TemplateAnnouncementsSection } from "templates/homepage/AnnouncementsSection" import TemplateHeroSection from "templates/homepage/HeroSection" import TemplateInfobarSection from "templates/homepage/InfobarSection" import TemplateInfopicLeftSection from "templates/homepage/InfopicLeftSection" @@ -166,6 +167,19 @@ export const HomepagePreview = ({ )} )} + {/* Announcements section */} + {EditorHomepageFrontmatterSection.isAnnouncements(section) && ( + <> + } + /> + + )} ))} diff --git a/src/styles/isomer-template/helpers.scss b/src/styles/isomer-template/helpers.scss index 766b0c78b..d8614c0c6 100644 --- a/src/styles/isomer-template/helpers.scss +++ b/src/styles/isomer-template/helpers.scss @@ -8,6 +8,7 @@ */ $spaceamounts: ( + "auto", 0, 1, 2, @@ -39,46 +40,70 @@ $spaceamounts: ( 80, 96 ); -$sides: (top, bottom, left, right); - -@each $space in $spaceamounts { - $remSpace: calc(#{$space}rem / 4); - - @each $side in $sides { - .m#{str-slice($side, 0, 1)}-#{$space} { - margin-#{$side}: $remSpace; - } - - .p#{str-slice($side, 0, 1)}-#{$space} { - padding-#{$side}: $remSpace; - } - } +$sides: ( + "t": "top", + "b": "bottom", + "l": "left", + "r": "right", + "x": ( + "left", + "right", + ), + "y": ( + "top", + "bottom", + ), + "": ( + "top", + "bottom", + "left", + "right", + ), +); - .mx-#{$space} { - margin-left: $remSpace; - margin-right: $remSpace; - } +@each $breakpoint, $minwidth in $breakpoints { + @if $minwidth != 0px { + @media (min-width: $minwidth) { + @each $space in $spaceamounts { + $remSpace: if($space == "auto", auto, calc(#{$space}rem / 4)); - .px-#{$space} { - padding-left: $remSpace; - padding-right: $remSpace; - } + @each $prefix, $positions in $sides { + $suffix: #{$prefix}; + @if $minwidth != 0px { + $suffix: #{$prefix}-#{$breakpoint}; + } - .my-#{$space} { - margin-top: $remSpace; - margin-bottom: $remSpace; - } + .m#{$suffix}-#{$space} { + @each $position in $positions { + margin-#{$position}: $remSpace !important; + } + } - .py-#{$space} { - padding-top: $remSpace; - padding-bottom: $remSpace; - } + .p#{$suffix}-#{$space} { + @each $position in $positions { + padding-#{$position}: $remSpace !important; + } + } + } + } + } + } @else { + @each $space in $spaceamounts { + $remSpace: if($space == "auto", auto, calc(#{$space}rem / 4)); - .m-#{$space} { - margin: $remSpace; - } + @each $prefix, $positions in $sides { + .m#{$prefix}-#{$space} { + @each $position in $positions { + margin-#{$position}: $remSpace !important; + } + } - .p-#{$space} { - padding: $remSpace; + .p#{$prefix}-#{$space} { + @each $position in $positions { + padding-#{$position}: $remSpace !important; + } + } + } + } } } diff --git a/src/styles/isomer-template/homepage/announcements.scss b/src/styles/isomer-template/homepage/announcements.scss new file mode 100644 index 000000000..5494a51ce --- /dev/null +++ b/src/styles/isomer-template/homepage/announcements.scss @@ -0,0 +1,35 @@ +.announcements-divider { + height: 1px; + color: #f9f9f9; +} + +.announcements-announcement-title { + font-size: 1.625rem; + font-weight: 700; + line-height: 2rem; +} + +.announcements-announcement-subtitle { + font-size: 1rem; + font-weight: 400; + line-height: 1.375rem; +} + +.announcements-announcement-link { + color: var(--site-secondary-color); + font-size: 1.125rem; + font-weight: 600; + line-height: 1.5rem; + letter-spacing: 0.27px; + text-decoration-line: underline; + text-transform: capitalize; + text-underline-offset: 0.25rem; +} + +.announcements-announcement-link:hover { + color: var(--site-secondary-color-hover); +} + +a[target="_blank"].announcements-announcement-link::after { + content: unset; +} diff --git a/src/styles/isomer-template/styles.scss b/src/styles/isomer-template/styles.scss index 7b6b4708d..801cb90c8 100644 --- a/src/styles/isomer-template/styles.scss +++ b/src/styles/isomer-template/styles.scss @@ -1,3 +1,13 @@ -@charset 'UTF-8'; +// Minimum width before a media query is triggered +$breakpoints: ( + "": 0px, + sm: 431px, + md: 770px, + lg: 1024px, + xl: 1280px, + xxl: 1408px, +); -@use "helpers"; +@import "helpers"; + +@import "homepage/announcements"; diff --git a/src/templates/homepage/AnnouncementsSection.tsx b/src/templates/homepage/AnnouncementsSection.tsx new file mode 100644 index 000000000..9ccc1e335 --- /dev/null +++ b/src/templates/homepage/AnnouncementsSection.tsx @@ -0,0 +1,168 @@ +import { forwardRef } from "react" + +import editorStyles from "styles/isomer-cms/pages/Editor.module.scss" + +import { getClassNames } from "templates/utils/stylingUtils" + +import { AnnouncementsSection } from "types/homepage" + +type TemplateAnnouncementsSectionProps = Omit< + AnnouncementsSection, + "announcement_items" +> & { + announcementItems: AnnouncementsSection["announcement_items"] + sectionIndex: number +} + +export const TemplateAnnouncementsSection = forwardRef< + HTMLDivElement, + TemplateAnnouncementsSectionProps +>( + ( + { + title, + subtitle, + announcementItems, + sectionIndex, + }: TemplateAnnouncementsSectionProps, + ref + ) => { + return ( +
+
+
+
+
+ <> + {subtitle && ( +

+ {subtitle} +

+ )} + {title && ( +

+ {title} +

+ )} + +
+ + {announcementItems && + announcementItems.map((announcement, index) => { + return ( + <> +
+
+

+ {announcement.title} +

+

+ {announcement.date} +

+
+
+

{announcement.announcement}

+ {announcement.link_text && announcement.link_url && ( +
+
+ {announcement.link_text} +
+
+ )} +
+
+ {announcementItems && + announcementItems.length === index + 1 ? ( +
+ ) : ( +
+ )} + + ) + })} + +
+
+
+
+
+ ) + } +) diff --git a/src/types/homepage.ts b/src/types/homepage.ts index 77c79f93a..e4d23dfb8 100644 --- a/src/types/homepage.ts +++ b/src/types/homepage.ts @@ -49,7 +49,7 @@ export interface InfopicSection { description?: string button?: string url?: string - images?: string + image?: string alt?: string } @@ -59,6 +59,18 @@ export interface ResourcesSection { button?: string } +export interface AnnouncementsSection { + title?: string + subtitle?: string + announcement_items?: Array<{ + title?: string + date?: string + announcement?: string + link_text?: string + link_url?: string + }> +} + export interface HomepageDto { content: { frontMatter: { @@ -73,6 +85,7 @@ export interface HomepageDto { | InfobarSection | InfopicSection | ResourcesSection + | AnnouncementsSection )[] } pageBody?: string @@ -91,20 +104,18 @@ export type HomepageEditorHeroSection = | EditorHeroHighlightsSection export type HeroFrontmatterSection = { hero: HomepageEditorHeroSection } -// TODO: add properties here instead of typing as `Record` -// we can find them in `HomepagePreview` -export type ResourcesFrontmatterSection = { resources: Record } +export type ResourcesFrontmatterSection = { resources: ResourcesSection } -// TODO: add properties here instead of typing as `Record` -// we can find them in `HomepagePreview` export type InfopicFrontmatterSection = { - infopic: Record + infopic: InfopicSection } -// TODO: add properties here instead of typing as `Record` -// we can find them in `HomepagePreview` export type InfobarFrontmatterSection = { - infobar: Record + infobar: InfobarSection +} + +export type AnnouncementsFrontmatterSection = { + announcements: AnnouncementsSection } export type EditorHomepageFrontmatterSection = @@ -112,6 +123,7 @@ export type EditorHomepageFrontmatterSection = | ResourcesFrontmatterSection | InfopicFrontmatterSection | InfobarFrontmatterSection + | AnnouncementsFrontmatterSection export const EditorHomepageFrontmatterSection = { isHero: ( @@ -130,6 +142,10 @@ export const EditorHomepageFrontmatterSection = { section: EditorHomepageFrontmatterSection ): section is InfobarFrontmatterSection => !!(section as InfobarFrontmatterSection).infobar, + isAnnouncements: ( + section: EditorHomepageFrontmatterSection + ): section is AnnouncementsFrontmatterSection => + !!(section as AnnouncementsFrontmatterSection).announcements, } export interface EditorHomepageState {