From f6df41708f9f1b77b86c50f4b58c30db325ccf1d Mon Sep 17 00:00:00 2001 From: Mathias Oterhals Myklebust Date: Wed, 30 Oct 2024 14:39:53 +0100 Subject: [PATCH 1/2] feat(CustomLink): color prop --- src/components/link/CustomLink.tsx | 6 +++++- src/components/link/link.module.css | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/components/link/CustomLink.tsx b/src/components/link/CustomLink.tsx index 143805384..73ad4d033 100644 --- a/src/components/link/CustomLink.tsx +++ b/src/components/link/CustomLink.tsx @@ -13,6 +13,7 @@ interface ICustomLink { link: ILink; isSelected?: boolean; size?: "normal" | "small"; + color?: "dark" | "light"; } const CustomLink = ({ @@ -20,6 +21,7 @@ const CustomLink = ({ link, isSelected, size = "normal", + color = "dark", }: ICustomLink) => { const href = getHref(link); const newTab = link.newTab; @@ -35,7 +37,9 @@ const CustomLink = ({ (type === "link" ? (
Date: Thu, 31 Oct 2024 07:37:12 +0100 Subject: [PATCH 2/2] feat(Header): announcement banner --- .../navigation/header/Header.stories.tsx | 2 ++ src/components/navigation/header/Header.tsx | 25 +++++++++++++ .../navigation/header/HeaderPreview.tsx | 17 +++++++-- .../navigation/header/PageHeader.tsx | 30 ++++++++++------ .../navigation/header/header.module.css | 24 +++++++++++++ src/components/navigation/mockData.ts | 9 +++++ src/styles/global.css | 1 + studio/deskStructure.ts | 11 ++++++ studio/lib/interfaces/announcement.ts | 8 +++++ studio/lib/queries/siteSettings.ts | 13 +++++++ studio/schema.ts | 2 ++ .../documents/siteSettings/announcement.ts | 36 +++++++++++++++++++ 12 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 studio/lib/interfaces/announcement.ts create mode 100644 studio/schemas/documents/siteSettings/announcement.ts diff --git a/src/components/navigation/header/Header.stories.tsx b/src/components/navigation/header/Header.stories.tsx index 469f0ba7f..ccd89bc20 100644 --- a/src/components/navigation/header/Header.stories.tsx +++ b/src/components/navigation/header/Header.stories.tsx @@ -2,6 +2,7 @@ import { Meta, StoryObj } from "@storybook/react"; import { defaultLanguage } from "i18n/supportedLanguages"; import { + mockAnnouncement, mockLogo, mockNavigation, mockPathTranslations, @@ -30,6 +31,7 @@ export const Default: Story = { args: { navigation: mockNavigation, assets: mockLogo, + announcement: mockAnnouncement, currentLanguage: defaultLanguage?.id ?? "en", pathTranslations: mockPathTranslations, }, diff --git a/src/components/navigation/header/Header.tsx b/src/components/navigation/header/Header.tsx index 696dab389..31dd6e0f8 100644 --- a/src/components/navigation/header/Header.tsx +++ b/src/components/navigation/header/Header.tsx @@ -11,7 +11,9 @@ import LanguageSwitcher from "src/components/languageSwitcher/LanguageSwitcher"; import CustomLink from "src/components/link/CustomLink"; import LinkButton from "src/components/linkButton/LinkButton"; import { BreadCrumbMenu } from "src/components/navigation/breadCrumbMenu/BreadCrumbMenu"; +import Text from "src/components/text/Text"; import { getHref } from "src/utils/link"; +import { Announcement } from "studio/lib/interfaces/announcement"; import { BrandAssets } from "studio/lib/interfaces/brandAssets"; import { InternationalizedString } from "studio/lib/interfaces/global"; import { ILink, Navigation } from "studio/lib/interfaces/navigation"; @@ -23,6 +25,7 @@ import styles from "./header.module.css"; export interface IHeader { navigation: Navigation; assets: BrandAssets; + announcement: Announcement | null; currentLanguage: string; pathTitles: string[]; pathTranslations: InternationalizedString; @@ -35,6 +38,7 @@ const filterLinks = (data: ILink[], type: string) => export const Header = ({ navigation, assets, + announcement, currentLanguage, pathTitles, pathTranslations, @@ -72,6 +76,11 @@ export const Header = ({ }; }, []); + const showAnnouncement = + announcement !== null && + announcement.text.length > 0 && + (!announcement.hideAfter || new Date(announcement.hideAfter) > new Date()); + return ( <> )} + {showAnnouncement && ( +
+
+ {announcement.text} + {announcement.link && announcement.link.linkTitle && ( +
+ +
+ )} +
+
+ )}
{showBreadcrumbs && ( diff --git a/src/components/navigation/header/HeaderPreview.tsx b/src/components/navigation/header/HeaderPreview.tsx index f7d525f32..3fe13023d 100644 --- a/src/components/navigation/header/HeaderPreview.tsx +++ b/src/components/navigation/header/HeaderPreview.tsx @@ -1,16 +1,22 @@ "use client"; import { QueryResponseInitial, useQuery } from "@sanity/react-loader"; +import { Announcement } from "studio/lib/interfaces/announcement"; import { BrandAssets } from "studio/lib/interfaces/brandAssets"; import { InternationalizedString } from "studio/lib/interfaces/global"; import { Navigation } from "studio/lib/interfaces/navigation"; -import { BRAND_ASSETS_QUERY, NAV_QUERY } from "studio/lib/queries/siteSettings"; +import { + ANNOUNCEMENT_QUERY, + BRAND_ASSETS_QUERY, + NAV_QUERY, +} from "studio/lib/queries/siteSettings"; import { Header } from "./Header"; export default function HeaderPreview({ initialNav, initialBrandAssets, + initialAnnouncement, currentLanguage, pathTitles, pathTranslations, @@ -18,6 +24,7 @@ export default function HeaderPreview({ }: { initialNav: QueryResponseInitial; initialBrandAssets: QueryResponseInitial; + initialAnnouncement: QueryResponseInitial; currentLanguage: string; pathTitles: string[]; pathTranslations: InternationalizedString; @@ -25,7 +32,7 @@ export default function HeaderPreview({ }) { const { data: newNav } = useQuery( NAV_QUERY, - { language: initialNav.data.language }, + { language: currentLanguage }, { initial: initialNav }, ); const { data: newBrandAssets } = useQuery( @@ -33,6 +40,11 @@ export default function HeaderPreview({ {}, { initial: initialBrandAssets }, ); + const { data: newAnnouncement } = useQuery( + ANNOUNCEMENT_QUERY, + { language: currentLanguage }, + { initial: initialAnnouncement }, + ); return ( newNav && @@ -40,6 +52,7 @@ export default function HeaderPreview({
( + ANNOUNCEMENT_QUERY, + { language }, + { perspective }, + ); + return ( - initialBrandAssets.data !== null && - initialNav.data !== null && + isNonNullQueryResponse(initialBrandAssets) && + isNonNullQueryResponse(initialNav) && (isDraftMode ? ( .documentId(defaultSeoID) .title("Default SEO"), ), + S.listItem() + .title("Announcement") + .icon(ConfettiIcon) + .child( + S.document() + .schemaType(announcementID) + .documentId(announcementID) + .title("Announcement"), + ), ]), ); diff --git a/studio/lib/interfaces/announcement.ts b/studio/lib/interfaces/announcement.ts new file mode 100644 index 000000000..0e11476aa --- /dev/null +++ b/studio/lib/interfaces/announcement.ts @@ -0,0 +1,8 @@ +import { ILink } from "./navigation"; + +export interface Announcement { + language: string; + text: string; + link?: ILink; + hideAfter?: string; +} diff --git a/studio/lib/queries/siteSettings.ts b/studio/lib/queries/siteSettings.ts index 9fb95f846..e8e99ef5f 100644 --- a/studio/lib/queries/siteSettings.ts +++ b/studio/lib/queries/siteSettings.ts @@ -87,3 +87,16 @@ export const DEFAULT_SEO_QUERY = groq` ${SEO_FRAGMENT} } `; + +// Announcement +export const ANNOUNCEMENT_QUERY = groq` + *[_type == "announcement"][0]{ + ${LANGUAGE_FIELD_FRAGMENT}, + "text": ${translatedFieldFragment("text")}, + "link": link { + ..., + ${TRANSLATED_LINK_FRAGMENT} + }, + hideAfter + } +`; diff --git a/studio/schema.ts b/studio/schema.ts index 099a9e4f6..324b8c11c 100644 --- a/studio/schema.ts +++ b/studio/schema.ts @@ -7,6 +7,7 @@ import legalDocument from "./schemas/documents/admin/legalDocuments"; import compensations from "./schemas/documents/compensations"; import languageSettings from "./schemas/documents/languageSettings"; import pageBuilder from "./schemas/documents/pageBuilder"; +import announcement from "./schemas/documents/siteSettings/announcement"; import brandAssets from "./schemas/documents/siteSettings/brandAssets"; import locale from "./schemas/documents/siteSettings/locale"; import navigationManager from "./schemas/documents/siteSettings/navigationManager"; @@ -41,5 +42,6 @@ export const schema: { types: SchemaTypeDefinition[] } = { locale, richText, seo, + announcement, ], }; diff --git a/studio/schemas/documents/siteSettings/announcement.ts b/studio/schemas/documents/siteSettings/announcement.ts new file mode 100644 index 000000000..1a3e9f236 --- /dev/null +++ b/studio/schemas/documents/siteSettings/announcement.ts @@ -0,0 +1,36 @@ +import { defineField, defineType } from "sanity"; + +import { link } from "studio/schemas/objects/link"; + +export const announcementID = "announcement"; + +const announcement = defineType({ + name: announcementID, + type: "document", + title: "Announcement", + description: "Message displayed in a banner at the top of each page.", + fields: [ + defineField({ + name: "text", + type: "internationalizedArrayString", + title: "Text", + }), + link, + defineField({ + name: "hideAfter", + type: "datetime", + title: "Hide After", + description: + "The announcement will be hidden after the specified date and time.", + }), + ], + preview: { + prepare() { + return { + title: "Announcement", + }; + }, + }, +}); + +export default announcement;