Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slb 295 breadcrumbs #221

Merged
merged 5 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions packages/ui/src/components/Molecules/Breadcrumbs.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { FrameQuery, OperationExecutor } from '@custom/schema';
import { Meta } from '@storybook/react';
import React from 'react';

import { Default as FrameStory } from '../Routes/Frame.stories';
import { BreadCrumbs } from './Breadcrumbs';

export default {
component: BreadCrumbs,
parameters: {
layout: 'fullscreen',
location: new URL('local:/gatsby-turbo'),
},
} satisfies Meta<typeof BreadCrumbs>;

export const Default = {
render: () => {
return (
<OperationExecutor executor={FrameStory.args} id={FrameQuery}>
<BreadCrumbs />
</OperationExecutor>
);
},
};
54 changes: 54 additions & 0 deletions packages/ui/src/components/Molecules/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Link } from '@custom/schema';
import { ChevronRightIcon } from '@heroicons/react/24/outline';
import clsx from 'clsx';
import React from 'react';

import { isTruthy } from '../../utils/isTruthy';
import { useBreadcrumbs } from '../Routes/Menu';

export function BreadCrumbs({ className }: { className?: string }) {
const breadcrumbs = useBreadcrumbs();

if (!breadcrumbs.length) {
return null;
}

return (
<nav className={clsx('pt-5', className)} aria-label="Breadcrumb">
<ol className={'rounded-lg inline-block py-2.5'}>
{breadcrumbs?.filter(isTruthy).map(({ title, target, id }, index) => (
<li className="inline-flex items-center" key={id}>
{index > 0 ? (
<div aria-hidden="true">
<ChevronRightIcon className={'w-4 h-4 text-gray-400 mr-4'} />
</div>
) : null}
<Link
href={target}
title={title}
className={clsx(
'inline-flex items-center text-sm font-medium hover:text-blue-600',
index === breadcrumbs?.length - 1
? 'pointer-events-none text-gray-500'
: 'text-gray-700',
)}
>
{target === '/' && (
<svg
className="w-4 h-4 mr-4"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 20 20"
>
<path d="m19.707 9.293-2-2-7-7a1 1 0 0 0-1.414 0l-7 7-2 2a1 1 0 0 0 1.414 1.414L2 10.414V18a2 2 0 0 0 2 2h3a1 1 0 0 0 1-1v-4a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v4a1 1 0 0 0 1 1h3a2 2 0 0 0 2-2v-7.586l.293.293a1 1 0 0 0 1.414-1.414Z" />
</svg>
)}
<span className="mr-4">{title}</span>
</Link>
</li>
))}
</ol>
</nav>
);
}
4 changes: 3 additions & 1 deletion packages/ui/src/components/Organisms/PageDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';

import { isTruthy } from '../../utils/isTruthy';
import { UnreachableCaseError } from '../../utils/unreachable-case-error';
import { BreadCrumbs } from '../Molecules/Breadcrumbs';
import { PageTransition } from '../Molecules/PageTransition';
import { BlockCta } from './PageContent/BlockCta';
import { BlockForm } from './PageContent/BlockForm';
Expand All @@ -15,8 +16,9 @@ export function PageDisplay(page: PageFragment) {
return (
<PageTransition>
<div>
{page.hero ? <PageHero {...page.hero} /> : null}
{page.hero && <PageHero {...page.hero} />}
<div className="bg-white pt-5 pb-12 lg:px-8">
<BreadCrumbs className="mx-auto max-w-3xl" />
<div className="mx-auto max-w-3xl text-base leading-7 text-gray-700">
{page?.content?.filter(isTruthy).map((block, index) => {
switch (block.__typename) {
Expand Down
92 changes: 92 additions & 0 deletions packages/ui/src/components/Routes/Menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { FrameQuery, NavigationItem, Url, useLocation } from '@custom/schema';
import { useIntl } from 'react-intl';

import { useOperation } from '../../utils/operation';

export type MenuNameType = 'main' | 'footer';

export function useMenus() {
const intl = useIntl();
const locale = intl.locale;
const settings = useOperation(FrameQuery).data;
return {
main: settings?.mainNavigation
?.filter((nav) => nav?.locale === locale)
.pop(),
footer: settings?.footerNavigation
?.filter((nav) => nav?.locale === locale)
.pop(),
};
}

export function useCurrentPath() {
const [loc] = useLocation();
return loc.pathname;
}

export function useMenuItem(path: string, menuName: MenuNameType) {
const menus = useMenus();

return (
menus &&
menus[menuName]?.items.find((menuItem) => menuItem?.target === path)
);
}

export function useMenuChildren(path: string, menuName: MenuNameType) {
const menus = useMenus();
const menuItemFromPath = useMenuItem(path, menuName);
return (
menus &&
menus[menuName]?.items.filter(
(menuItem) => menuItem?.parent === menuItemFromPath?.id,
)
);
}

export function useCurrentMenuItem(menuName: MenuNameType) {
const currentPath = useCurrentPath();
return useMenuItem(currentPath || '', menuName);
}

export function useCurrentMenuChildren(menuName: MenuNameType) {
const currentPath = useCurrentPath();
return useMenuChildren(currentPath || '', menuName);
}

export function useMenuAncestors(path: string, menuName: MenuNameType) {
const menus = useMenus();
const menuItemFromPath = useMenuItem(path, menuName);
let processingMenuItem = menuItemFromPath;
const ancestors: Array<NavigationItem> = [];

// Set current page breadcrumb
if (typeof processingMenuItem !== 'undefined') {
// If not home path then only push into breadcrumbs array
processingMenuItem.target !== '/' && ancestors.push(processingMenuItem);
}

while (
typeof processingMenuItem !== 'undefined' &&
processingMenuItem?.parent
) {
processingMenuItem =
menus &&
menus[menuName]?.items.find(
(menuItem) => menuItem?.id === processingMenuItem?.parent,
);
if (typeof processingMenuItem !== 'undefined') {
ancestors.push(processingMenuItem);
}
}
if (ancestors.length > 0) {
ancestors.push({ id: '_', target: '/' as Url, title: 'Home' });
}

return ancestors.reverse();
}

export const useBreadcrumbs = (menuName?: MenuNameType, path?: string) => {
const currentPath = useCurrentPath();
return useMenuAncestors(path || currentPath || '', menuName || 'main');
};
25 changes: 19 additions & 6 deletions packages/ui/src/components/Routes/Page.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,6 @@ export const Default = {
},
],
path: '/test' as Url,
hero: {
headline: 'Page Hero Headline',
lead: 'A longer lead text that even might break into multiple lines.',
ctaUrl: '/test' as Url,
ctaText: 'Call to action',
},
content: [
{
__typename: 'BlockMarkup',
Expand All @@ -62,6 +56,25 @@ export const Default = {
] as Exclude<ViewPageQuery['page'], undefined>['content'],
},
},
parameters: {
location: new URL('local:/gatsby-turbo'),
},
} satisfies StoryObj<ViewPageQuery>;

export const Hero = {
...Default,
args: {
...Default.args,
page: {
...Default.args.page,
hero: {
headline: 'Page Hero Headline',
lead: 'A longer lead text that even might break into multiple lines.',
ctaUrl: '/test' as Url,
ctaText: 'Call to action',
},
},
},
} satisfies StoryObj<ViewPageQuery>;

export const FullHero = {
Expand Down
Loading