Skip to content

Commit

Permalink
feat(Header): announcement banner
Browse files Browse the repository at this point in the history
  • Loading branch information
mathiazom committed Oct 31, 2024
1 parent 38901c5 commit b5f27c3
Show file tree
Hide file tree
Showing 12 changed files with 165 additions and 13 deletions.
2 changes: 2 additions & 0 deletions src/components/navigation/header/Header.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Meta, StoryObj } from "@storybook/react";

import { defaultLanguage } from "i18n/supportedLanguages";
import {
mockAnnouncement,
mockLogo,
mockNavigation,
mockPathTranslations,
Expand Down Expand Up @@ -30,6 +31,7 @@ export const Default: Story = {
args: {
navigation: mockNavigation,
assets: mockLogo,
announcement: mockAnnouncement,
currentLanguage: defaultLanguage?.id ?? "en",
pathTranslations: mockPathTranslations,
},
Expand Down
25 changes: 25 additions & 0 deletions src/components/navigation/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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;
Expand All @@ -35,6 +38,7 @@ const filterLinks = (data: ILink[], type: string) =>
export const Header = ({
navigation,
assets,
announcement,
currentLanguage,
pathTitles,
pathTranslations,
Expand Down Expand Up @@ -72,6 +76,11 @@ export const Header = ({
};
}, []);

const showAnnouncement =
announcement !== null &&
announcement.text.length > 0 &&
(!announcement.hideAfter || new Date(announcement.hideAfter) > new Date());

return (
<>
<FocusOn
Expand Down Expand Up @@ -126,6 +135,22 @@ export const Header = ({
</div>
)}
</nav>
{showAnnouncement && (
<div className={styles.announcementWrapper}>
<div className={styles.announcementContent}>
<Text type={"bodySmall"}>{announcement.text}</Text>
{announcement.link && announcement.link.linkTitle && (
<div>
<CustomLink
link={announcement.link}
size={"small"}
color={"light"}
/>
</div>
)}
</div>
</div>
)}
</header>
</FocusOn>
{showBreadcrumbs && (
Expand Down
17 changes: 15 additions & 2 deletions src/components/navigation/header/HeaderPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,58 @@
"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,
showBreadcrumbs,
}: {
initialNav: QueryResponseInitial<Navigation>;
initialBrandAssets: QueryResponseInitial<BrandAssets>;
initialAnnouncement: QueryResponseInitial<Announcement | null>;
currentLanguage: string;
pathTitles: string[];
pathTranslations: InternationalizedString;
showBreadcrumbs: boolean;
}) {
const { data: newNav } = useQuery<Navigation | null>(
NAV_QUERY,
{ language: initialNav.data.language },
{ language: currentLanguage },
{ initial: initialNav },
);
const { data: newBrandAssets } = useQuery<BrandAssets | null>(
BRAND_ASSETS_QUERY,
{},
{ initial: initialBrandAssets },
);
const { data: newAnnouncement } = useQuery<Announcement | null>(
ANNOUNCEMENT_QUERY,
{ language: currentLanguage },
{ initial: initialAnnouncement },
);

return (
newNav &&
newBrandAssets && (
<Header
navigation={newNav}
assets={newBrandAssets}
announcement={newAnnouncement}
currentLanguage={currentLanguage}
pathTitles={pathTitles}
pathTranslations={pathTranslations}
Expand Down
30 changes: 19 additions & 11 deletions src/components/navigation/header/PageHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { getDraftModeInfo } from "src/utils/draftmode";
import { isNonNullQueryResponse } from "src/utils/queryResponse";
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 { loadStudioQuery } from "studio/lib/store";

import { Header } from "./Header";
Expand Down Expand Up @@ -35,19 +41,20 @@ export default async function PageHeader({
{ perspective },
);

const initialAnnouncement = await loadStudioQuery<Announcement | null>(
ANNOUNCEMENT_QUERY,
{ language },
{ perspective },
);

return (
initialBrandAssets.data !== null &&
initialNav.data !== null &&
isNonNullQueryResponse(initialBrandAssets) &&
isNonNullQueryResponse(initialNav) &&
(isDraftMode ? (
<HeaderPreview
initialNav={{
...initialNav,
data: initialNav.data,
}}
initialBrandAssets={{
...initialBrandAssets,
data: initialBrandAssets.data,
}}
initialNav={initialNav}
initialBrandAssets={initialBrandAssets}
initialAnnouncement={initialAnnouncement}
currentLanguage={language}
pathTitles={pathTitles}
pathTranslations={pathTranslations}
Expand All @@ -57,6 +64,7 @@ export default async function PageHeader({
<Header
navigation={initialNav.data}
assets={initialBrandAssets.data}
announcement={initialAnnouncement.data}
currentLanguage={language}
pathTitles={pathTitles}
pathTranslations={pathTranslations}
Expand Down
24 changes: 24 additions & 0 deletions src/components/navigation/header/header.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,27 @@
composes: button;
background: url("/_assets/menu-close.svg") no-repeat 50% 50%;
}

.announcementWrapper {
display: flex;
flex-direction: column;
padding: 0.75rem;
background-color: var(--primary-bg-blue);
align-items: center;
}

.announcementContent {
display: flex;
gap: 2rem;

@media (max-width: 1024px) {
align-items: center;
flex-direction: column;
gap: 0.5rem;
}
}

.announcementContent p {
font-weight: 600;
color: var(--primary-white);
}
9 changes: 9 additions & 0 deletions src/components/navigation/mockData.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import primaryLogoFile from "src/stories/assets/energiai-primary-logo.svg";
import secondaryLogoFile from "src/stories/assets/energiai-secondary-logo.svg";
import { Announcement } from "studio/lib/interfaces/announcement";
import { InternationalizedString } from "studio/lib/interfaces/global";
import {
LinkType,
Expand Down Expand Up @@ -131,6 +132,14 @@ export const mockLogo = {
favicon: {},
};

export const mockAnnouncement: Announcement = {
language: "no",
text: "Mandag 21.10. er det TDC! Møt oss der!",
hideAfter: new Date(
new Date().setMonth(new Date().getMonth() + 1),
).toISOString(),
};

// Mock Social Media Profiles
export const mockSocialMediaProfiles: SocialMediaProfiles = {
_id: "profile123",
Expand Down
1 change: 1 addition & 0 deletions src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ html {
--primary-light: #f5a4c4;
--primary-bg: #f2f2f2;
--primary-bg-dark: #d9d9d9;
--primary-bg-blue: #0014cd;

--primary-white-bright: #ffffff;
--primary-white: #faf8f5;
Expand Down
11 changes: 11 additions & 0 deletions studio/deskStructure.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
CaseIcon,
CogIcon,
ConfettiIcon,
EarthGlobeIcon,
HeartIcon,
ImagesIcon,
Expand All @@ -22,6 +23,7 @@ import { legalDocumentID } from "./schemas/documents/admin/legalDocuments";
import { compensationsId } from "./schemas/documents/compensations";
import { languageSettingsID } from "./schemas/documents/languageSettings";
import { pageBuilderID } from "./schemas/documents/pageBuilder";
import { announcementID } from "./schemas/documents/siteSettings/announcement";
import { brandAssetsID } from "./schemas/documents/siteSettings/brandAssets";
import { localeID } from "./schemas/documents/siteSettings/locale";
import { soMeLinksID } from "./schemas/documents/siteSettings/socialMediaProfiles";
Expand Down Expand Up @@ -120,6 +122,15 @@ const siteSettingSection = (S: StructureBuilder) =>
.documentId(defaultSeoID)
.title("Default SEO"),
),
S.listItem()
.title("Announcement")
.icon(ConfettiIcon)
.child(
S.document()
.schemaType(announcementID)
.documentId(announcementID)
.title("Announcement"),
),
]),
);

Expand Down
8 changes: 8 additions & 0 deletions studio/lib/interfaces/announcement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ILink } from "./navigation";

export interface Announcement {
language: string;
text: string;
link?: ILink;
hideAfter?: string;
}
13 changes: 13 additions & 0 deletions studio/lib/queries/siteSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
`;
2 changes: 2 additions & 0 deletions studio/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -41,5 +42,6 @@ export const schema: { types: SchemaTypeDefinition[] } = {
locale,
richText,
seo,
announcement,
],
};
36 changes: 36 additions & 0 deletions studio/schemas/documents/siteSettings/announcement.ts
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit b5f27c3

Please sign in to comment.