Skip to content

Commit

Permalink
Simplify layouts (#330)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vadorequest committed May 12, 2021
1 parent db3b090 commit dc1b1b9
Show file tree
Hide file tree
Showing 65 changed files with 526 additions and 746 deletions.
4 changes: 2 additions & 2 deletions src/app/components/BrowserPageBootstrap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ const BrowserPageBootstrap = (props: BrowserPageBootstrapProps): JSX.Element =>
// Init the Cookie Consent popup, which will open on the browser
initCookieConsent({
allowedPages: [ // We only allow it on those pages to avoid display that boring popup on every page
`${window.location.origin}/${locale}/terms`,
`${window.location.origin}/${locale}/privacy`,
`${window.location.origin}/${locale}/demo/terms`,
`${window.location.origin}/${locale}/demo/privacy`,
`${window.location.origin}/${locale}/demo/built-in-features/cookies-consent`,
],
amplitudeInstance,
Expand Down
2 changes: 1 addition & 1 deletion src/layouts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ Page layouts
Summary:

- `core`: Share reusable components between layouts.
- `default`: Used by all non-demo pages. Customise it, or change the core, as you prefer.
- `demo`: Used by all demo pages. You'll eventually get rid of it, but until then it can be a good inspiration.
- `public`: Only used by a single page (at "/public"), meant to be the layout for public pages.
- You can add custom layouts and use them in your pages right away.
- Layouts are flexible, we used the `DemoLayout` in all pages under `pages/[locale]/demo` but **you don't have to**, it was a choice.
- Layouts are usually useful when you want to have a similar UI shared by several pages.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export type HeadProps = {
*
* https://github.com/vercel/next.js#populating-head
*/
const Head: React.FunctionComponent<HeadProps> = (props): JSX.Element => {
const CoreHead: React.FunctionComponent<HeadProps> = (props): JSX.Element => {
const customer: Customer = useCustomer();

const defaultDescription = 'Flexible production-grade boilerplate with Next.js 9, Vercel and TypeScript. Includes multiple opt-in presets using Storybook, Airtable, Analytics, CSS-in-JS, Monitoring, End-to-end testing, Internationalization, CI/CD and SaaS B2B multiple single-tenants (monorepo) support';
Expand Down Expand Up @@ -163,4 +163,4 @@ const Head: React.FunctionComponent<HeadProps> = (props): JSX.Element => {
);
};

export default Head;
export default CoreHead;
210 changes: 210 additions & 0 deletions src/layouts/core/components/CoreLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import { SoftPageProps } from '@/layouts/core/types/SoftPageProps';
import { GenericObject } from '@/modules/core/data/types/GenericObject';
import DefaultErrorLayout from '@/modules/core/errorHandling/DefaultErrorLayout';
import { createLogger } from '@/modules/core/logging/logger';
import PreviewModeBanner from '@/modules/core/previewMode/components/PreviewModeBanner';
import Sentry from '@/modules/core/sentry/sentry';
import ErrorPage from '@/pages/_error';
import {
Amplitude,
LogOnMount,
} from '@amplitude/react-amplitude';
import { css } from '@emotion/react';
import classnames from 'classnames';
import {
NextRouter,
useRouter,
} from 'next/router';
import React from 'react';
import BaseHead, { HeadProps } from './CoreHead';
import CorePageContainer from './CorePageContainer';

const fileLabel = 'layouts/core/components/CoreLayout';
const logger = createLogger({
fileLabel,
});

export type Props = {
/**
* Content to display within the layout.
*
* Essentially, the page's content.
*/
children: React.ReactNode;

/**
* Props forwarded to the Head component.
*
* Essentially, SEO metadata, etc.
* Will use sane defaults if not specified.
*/
headProps?: HeadProps;

/**
* Internal name of the page.
*
* Used by Amplitude, for analytics.
* All events happening on the page will be linked to that page name.
*/
pageName: string;

/**
* Wrapper container for the page.
*
* By default, uses CorePageContainer component.
*/
PageContainer?: React.FunctionComponent;

/**
* Force hiding the nav.
*/
hideNav?: boolean;

/**
* Force hiding the footer.
*/
hideFooter?: boolean;

/**
* Force hiding the preview banner.
*/
hidePreviewBanner?: boolean;

/**
* Component to use as Head.
*
* @default BaseHead
*/
Head?: React.FunctionComponent<HeadProps>;

/**
* Component to use as Footer.
*
* @default BaseFooter
*/
Footer?: React.FunctionComponent;

/**
* Component to use as Nav.
*
* @default BaseNav
*/
Nav?: React.FunctionComponent;
} & SoftPageProps;

/**
* Handles the positioning of top-level elements within the page.
*
* It does the following:
* - Adds a Nav/Footer component, and the dynamic Next.js "Page" component in between.
* - Automatically track page views (Amplitude).
* - Handles errors by displaying the Error page, with the ability to contact technical support (which will send a Sentry User Feedback).
*
* XXX Core component, meant to be used by other layouts, shouldn't be used by other components directly.
*/
const CoreLayout: React.FunctionComponent<Props> = (props): JSX.Element => {
const {
children,
error,
isInIframe = false, // Won't be defined server-side
headProps = {},
pageName,
PageContainer = CorePageContainer,
hideNav,
hideFooter = true,
hidePreviewBanner = true,
Head = BaseHead,
Footer = null,
Nav = null,
} = props;
const router: NextRouter = useRouter();
const isIframeWithFullPagePreview = router?.query?.fullPagePreview === '1';
const isPreviewModeBannerDisplayed = !hidePreviewBanner && process.env.NEXT_PUBLIC_APP_STAGE !== 'production';
const isNavDisplayed = !hideNav && (!isInIframe || isIframeWithFullPagePreview) && Nav;
const isFooterDisplayed = !hideFooter && (!isInIframe || isIframeWithFullPagePreview) && Footer;

Sentry.addBreadcrumb({ // See https://docs.sentry.io/enriching-error-data/breadcrumbs
category: fileLabel,
message: `Rendering ${fileLabel} for page ${pageName}`,
level: Sentry.Severity.Debug,
});

Sentry.configureScope((scope): void => {
scope.setTag('fileLabel', fileLabel);
});

return (
<Amplitude
eventProperties={(inheritedProps): GenericObject => ({
...inheritedProps,
page: {
...inheritedProps.page,
name: pageName,
},
})}
>
<div
css={css`
display: block;
width: 100vw;
height: 100vh;
background-color: white;
margin: 20px;
`}
>
<Head {...headProps} />
<LogOnMount eventType="page-displayed" />

{/* Loaded from components/Head - See https://github.com/mikemaccana/outdated-browser-rework */}
{/*<div*/}
{/* id="outdated"*/}
{/* style={{ display: 'none' }}*/}
{/*></div>*/}

{
isPreviewModeBannerDisplayed && (
<PreviewModeBanner />
)
}

{
(isNavDisplayed) && (
<Nav />
)
}

<div
className={classnames('page-wrapper', isInIframe ? 'is-in-iframe' : 'not-in-iframe')}
>
{
// If an error happened, we display it instead of displaying the page
// We display a custom error instead of the native Next.js error by providing children (removing children will display the native Next.js error)
error ? (
<ErrorPage
statusCode={500}
isReadyToRender={true}
err={error}
>
<DefaultErrorLayout
error={error}
/>
</ErrorPage>
) : (
<PageContainer>
{children}
</PageContainer>
)
}
</div>

{
(isFooterDisplayed) && (
<Footer />
)
}
</div>
</Amplitude>
);
};

export default CoreLayout;
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type Props = {
*
* XXX Core component, meant to be used by other layouts, shouldn't be used by other components directly.
*/
const PageContainer: React.FunctionComponent<Props> = (props): JSX.Element => {
const CorePageContainer: React.FunctionComponent<Props> = (props): JSX.Element => {
const {
children,
} = props;
Expand All @@ -31,4 +31,4 @@ const PageContainer: React.FunctionComponent<Props> = (props): JSX.Element => {

};

export default PageContainer;
export default CorePageContainer;
Loading

0 comments on commit dc1b1b9

Please sign in to comment.