Skip to content

Commit

Permalink
✨ Sticky Menu Component
Browse files Browse the repository at this point in the history
  • Loading branch information
padms committed Jan 2, 2025
1 parent e612fcd commit 1ca1edf
Show file tree
Hide file tree
Showing 15 changed files with 211 additions and 77 deletions.
5 changes: 5 additions & 0 deletions sanityv3/schemas/documents/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ export default {
description: 'You can override the hero image as the SoMe image by uploading another image here.',
fieldset: 'metadata',
},
{
name: 'stickyMenu',
title: 'Sticky Menu',
type: 'stickyMenu',
},
...sharedHeroFields,
{
title: 'Is Campain',
Expand Down
2 changes: 2 additions & 0 deletions sanityv3/schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import transcript from './objects/transcript'
import anchorLinkList from './objects/anchorLinkList/anchorLinkList'
import anchorLinkReference from './objects/anchorLinkList/anchorLinkReference'
import imageForText from './objects/imageForText'
import stickyMenu from './objects/stickyMenu'

const {
pageNotFound,
Expand Down Expand Up @@ -210,6 +211,7 @@ const RemainingSchemas = [
anchorLinkList,
anchorLinkReference,
imageForText,
stickyMenu,
]

// Then we give our schema to the builder and provide the result to Sanity
Expand Down
33 changes: 33 additions & 0 deletions sanityv3/schemas/objects/stickyMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Rule } from 'sanity'

export default {
name: 'stickyMenu',
title: 'Sticky Menu',
type: 'object',
fields: [
{
title: 'Title',
name: 'title',
type: 'string',
},
{
title: 'Links',
name: 'links',
type: 'array',
of: [
{
type: 'anchorLinkReference',
title: 'Anchor reference',
},
{ type: 'downloadableFile', title: 'Downloadable file' },
],
validation: (Rule: Rule) => Rule.unique().max(2),
},
{
title: 'Color',
description: 'Default is white.',
name: 'backgroundColor',
type: 'colorlist',
},
],
}
47 changes: 47 additions & 0 deletions web/core/Link/StickyMenuLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { forwardRef } from 'react'
import { twMerge } from 'tailwind-merge'
import { BaseLink, BaseLinkProps } from './BaseLink'
import { TransformableIcon } from '../../icons/TransformableIcon'
import { arrow_down, download, library_pdf } from '@equinor/eds-icons'

export type StickMenuLinkProps = {
isDownloadable?: boolean
} & BaseLinkProps

/** Sticky menu link style */
export const StickyMenuLink = forwardRef<HTMLAnchorElement, StickMenuLinkProps>(function StickyMenuLink(
{ children, type = 'internalUrl', className = '', href = '', isDownloadable = false, ...rest },
ref,
) {
const classNames = twMerge(
`
group
inline-flex
align-baseline
w-fit
text-slate-80
leading-0
`,
className,
)
const contentClassNames = `
relative
w-fit
hover:underline
no-underline
`

return (
<BaseLink className={classNames} ref={ref} href={href} {...rest}>
{isDownloadable && <TransformableIcon iconData={library_pdf} className="mr-1" />}
<span className={contentClassNames}>{children}</span>
<TransformableIcon
iconData={isDownloadable ? download : arrow_down}
className="text-energy-red-100
dark:text-white-100
ml-2"
/>
</BaseLink>
)
})
export default StickyMenuLink
7 changes: 7 additions & 0 deletions web/lib/queries/common/anchorLinkReferenceFields.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const anchorLinkReferenceFields = /* groq */ `
_type == "anchorLinkReference" =>{
"type": _type,
"id": _key,
title,
anchorReference,
}`
6 changes: 2 additions & 4 deletions web/lib/queries/common/pageContentFields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { keyNumbersFields } from './keyNumbersFields'
import { noDrafts, sameLang } from './langAndDrafts'
import promoteMagazine from './promotions/promoteMagazine'
import { lastUpdatedTimeQuery, publishDateTimeQuery } from './publishDateTime'
import { anchorLinkReferenceFields } from './anchorLinkReferenceFields'

const pageContentFields = /* groq */ `
_type == "keyNumbers" =>{
Expand Down Expand Up @@ -589,10 +590,7 @@ _type == "keyNumbers" =>{
title,
columns,
"anchorList":anchorList[]{
"type": _type,
"id": _key,
title,
anchorReference,
${anchorLinkReferenceFields}
}
},
_type == "imageForText" => {
Expand Down
13 changes: 13 additions & 0 deletions web/lib/queries/common/stickyMenu.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import downloadableFileFields from './actions/downloadableFileFields'
import { anchorLinkReferenceFields } from './anchorLinkReferenceFields'

export const stickyMenu = /* groq */ `"stickyMenu":
content->stickyMenu{
title,
"type": _type,
"links": links[]{
${downloadableFileFields},
${anchorLinkReferenceFields}
}
}
`
2 changes: 2 additions & 0 deletions web/lib/queries/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { eventContentFields } from './common/eventContentFields'
import { heroFields } from './common/heroFields'
import { seoAndSomeFields } from './common/seoAndSomeFields'
import { breadcrumbsQuery } from './common/breadcrumbs'
import { stickyMenu } from './common/stickyMenu'

const allSlugsQuery = /* groq */ `
"currentSlug": {
Expand All @@ -22,6 +23,7 @@ export const routeQuery = /* groq */ `
${allSlugsQuery},
"title": content->title,
"seoAndSome": content->${seoAndSomeFields},
${stickyMenu},
"hero": content->${heroFields},
"template": content->_type,
content->_type == "page" => {
Expand Down
101 changes: 38 additions & 63 deletions web/pageComponents/shared/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/* eslint-disable jsx-a11y/anchor-is-valid */
import styled, { createGlobalStyle } from 'styled-components'
import { createGlobalStyle } from 'styled-components'
import { CSSProperties } from 'react'
import { useRouter } from 'next/router'
import { default as NextLink } from 'next/link'
import { Topbar, BackgroundContainer } from '@components'
import { AllSlugsType, LocalizationSwitch } from './LocalizationSwitch'
import type { MenuData, SimpleMenuData } from '../../types/index'
import type { MenuData, SimpleMenuData, StickyMenuData } from '../../types/index'
import SiteMenu from './siteMenu/SiteMenu'
import SimpleSiteMenu from './siteMenu/simple/SimpleSiteMenu'
import { Flags } from '../../common/helpers/datasetHelpers'
Expand All @@ -19,60 +19,17 @@ import getConfig from 'next/config'
import { getAllSitesLink } from '../../common/helpers/getAllSitesLink'
import { Icon } from '@equinor/eds-core-react'
import { ButtonLink } from '@core/Link'
import StickyMenuLink from '@core/Link/StickyMenuLink'

const TopbarOffset = createGlobalStyle`
body {
padding-top: var(--topbar-height);
}
`

const HeaderRelative = styled.header`
position: relative;
`

const TopbarContainer = styled(Topbar.InnerContainer)`
display: grid;
grid-template-areas: 'logo menu';
grid-template-rows: 1fr;
align-items: center;
grid-column-gap: var(--space-large);
column-gap: var(--space-large);
`
const LogoLinkInGrid = styled(LogoLink)`
grid-area: logo;
`

/* This div is needed because of the grid layout to wrap focus lock panes.
We might look into to improve this at some point. */
const ControlChild = styled.div``

const ControlsContainer = styled.div`
grid-area: menu;
justify-self: right;
display: grid;
grid-template-columns: repeat(var(--columns), auto);
grid-column-gap: var(--space-small);
column-gap: var(--space-small);
align-items: center;
@media (min-width: 600px) {
grid-column-gap: var(--space-medium);
column-gap: var(--space-medium);
}
`

const StyledAllSites = styled(NextLink)`
cursor: pointer;
font-size: var(--typeScale-1);
text-decoration: none;
&:hover {
text-decoration: underline;
}
`

export type HeaderProps = {
menuData?: MenuData | SimpleMenuData
slugs: AllSlugsType
stickyMenuData?: StickyMenuData
}

const HeadTags = ({ slugs }: { slugs: AllSlugsType }) => {
Expand Down Expand Up @@ -126,13 +83,13 @@ const HeadTags = ({ slugs }: { slugs: AllSlugsType }) => {
const AllSites = () => {
const allSitesURL = getAllSitesLink('external')
return (
<StyledAllSites href={allSitesURL} prefetch={false}>
<NextLink className="cursor-pointer text-base no-underline hover:underline" href={allSitesURL} prefetch={false}>
<FormattedMessage id="all_sites" defaultMessage="All Sites" />
</StyledAllSites>
</NextLink>
)
}

const Header = ({ slugs, menuData }: HeaderProps) => {
const Header = ({ slugs, menuData, stickyMenuData }: HeaderProps) => {
const router = useRouter()
const localization = {
activeLocale: router.locale || defaultLanguage.locale,
Expand All @@ -153,23 +110,27 @@ const Header = ({ slugs, menuData }: HeaderProps) => {
const intl = useIntl()
const searchLabel = intl.formatMessage({ id: 'search', defaultMessage: 'Search' })

const anchorReference = stickyMenuData?.links.find((it) => it.type == 'anchorLinkReference')
const resourceLink = stickyMenuData?.links.find((it) => it.type == 'downloadableFile')

return (
<HeaderRelative>
<div className="sticky top-0 z-10">
<HeadTags slugs={slugs} />
<TopbarOffset />
<BackgroundContainer>
<Topbar>
<TopbarContainer>
<LogoLinkInGrid />
<ControlsContainer
<Topbar.InnerContainer className="grid grid-areas-logo_menu grid-rows-1 items-center gap-x-large ">
<LogoLink className="grid-area-logo" />
<div
style={
{
'--columns': columns,
} as CSSProperties
}
className="grid grid-area-menu justify-self-end grid-cols-[repeat(var(--columns),auto)] gap-x-small items-center sm:gap-x-medium"
>
{hasSearch && (
<ControlChild>
<div>
<ButtonLink
variant="ghost"
aria-expanded="true"
Expand All @@ -180,27 +141,41 @@ const Header = ({ slugs, menuData }: HeaderProps) => {
<Icon size={24} data={search} />
<FormattedMessage id="search" />
</ButtonLink>
</ControlChild>
</div>
)}
{hasMoreThanOneLanguage && (
<LocalizationSwitch activeLocale={localization.activeLocale} allSlugs={validSlugs} />
)}
{shouldDisplayAllSites ? (
<AllSites />
) : menuData && Flags.HAS_FANCY_MENU ? (
<ControlChild>
<div>
<SiteMenu data={menuData as MenuData} />
</ControlChild>
</div>
) : (
<ControlChild>
<div>
<SimpleSiteMenu data={menuData as SimpleMenuData} />
</ControlChild>
</div>
)}
</ControlsContainer>
</TopbarContainer>
</div>
</Topbar.InnerContainer>
</Topbar>
{stickyMenuData && (
<div className="w-full bg-moss-green-50 p-4 grid grid-cols-2 lg:grid-cols-5 gap-2 lg:px-16 lg:gap-16 shadow-top-bar z-40">
<div className="text-center font-bold lg:col-span-3 text-md col-span-2"> {stickyMenuData?.title}</div>
<StickyMenuLink className="mr-4" href={`#${anchorReference?.anchorReference}`}>
{anchorReference?.title}
</StickyMenuLink>
{resourceLink && (
<StickyMenuLink href={resourceLink.href} isDownloadable>
{' '}
{resourceLink.label}
</StickyMenuLink>
)}
</div>
)}
</BackgroundContainer>
</HeaderRelative>
</div>
)
}

Expand Down
8 changes: 6 additions & 2 deletions web/pageComponents/shared/LogoLink.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { AnchorHTMLAttributes } from 'react'
import { LogoSecondary } from '@components'
import NextLink from 'next/link'
import { twMerge } from 'tailwind-merge'

type LogoLinkProps = AnchorHTMLAttributes<HTMLAnchorElement>

export const LogoLink = ({ ...rest }: LogoLinkProps) => {
export const LogoLink = ({ className, ...rest }: LogoLinkProps) => {
return (
<NextLink
href="/"
aria-label="Equinor home page"
{...rest}
className="flex items-center justify-self-start h-full focus:outline-none focus-visible:envis-outline"
className={twMerge(
'flex items-center justify-self-start h-full focus:outline-none focus-visible:envis-outline',
className,
)}
prefetch={false}
>
<LogoSecondary className="-mt-[12%]" />
Expand Down
2 changes: 1 addition & 1 deletion web/pages/[[...slug]].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ Page.getLayout = (page: AppProps) => {
return (
<Layout footerData={data?.footerData} intl={data?.intl}>
<>
<Header slugs={slugs} menuData={data?.menuData} />
<Header slugs={slugs} menuData={data?.menuData} stickyMenuData={data?.pageData?.stickyMenu} />
{page}
</>
</Layout>
Expand Down
Loading

0 comments on commit 1ca1edf

Please sign in to comment.