Skip to content

Commit

Permalink
initial setup of material UI
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-burel committed Jun 12, 2020
1 parent 935fec9 commit da2f01d
Show file tree
Hide file tree
Showing 12 changed files with 492 additions and 47 deletions.
16 changes: 16 additions & 0 deletions cypress/integration/vns/mui.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
describe("material-ui", () => {
describe("ssr", () => {
it("does render a page", () => {
cy.visit("/vns/debug/mui");
cy.contains("material ui", { matchCase: false, timeout: 0 }).should(
"exist"
);
});
});
describe("client-side", () => {
it("does render a page", () => {
cy.visit("/vns/debug/mui");
cy.contains("material ui", { matchCase: false }).should("exist");
});
});
});
15 changes: 13 additions & 2 deletions docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@ There is nothing worse than a slow Storybook build, you can debug your Webpack b

Run `yarn run analyze-bundle` to get insight on your Webpack build.

## Material UI

Initial setup based on [official Next example](https://github.com/mui-org/material-ui/tree/master/examples/nextjs).

We try to reduce the foot print of Material UI for an easy remove. In next iterations, we'll try to make it fully pluggable, like in Vulcan Meteor, so you can easily swap your UI system.



## TODO

### i18n
Expand All @@ -171,13 +179,16 @@ Material UI
 ### Others

Error boundary
HTML language in custom document
NPM package.json version in custom document
Switch between MUI and Tailwind
MUI and i18n in Storybook
Redirection demo for private pages => demo a page that is not available for example, and redirect to home with an HTTP request
Graphql code generator
Redirection demo for private pages
Remove debug routes from bundle
Document contribution process
Cleaner debug call (active only when DEBUG=1)
Storybook static build
MUI and i18n in Storybook
Pure JS support (no TS), in cypress, in code, in storybook, in jest...
PErformance testing?
Jest for the custom server
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@apollo/react-hooks": "3.1.3",
"@apollo/react-ssr": "3.1.3",
"@apollo/react-testing": "3.1.4",
"@material-ui/core": "^4.10.2",
"apollo-cache-inmemory": "1.6.6",
"apollo-client": "2.6.9",
"apollo-link": "1.2.14",
Expand Down
88 changes: 88 additions & 0 deletions packages/@vulcan/next-material-ui/components/Link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// @see https://github.com/mui-org/material-ui/blob/master/examples/nextjs/src/Link.js
// /* eslint-disable jsx-a11y/anchor-has-content */
import React, { Ref } from "react";
import PropTypes from "prop-types";
import clsx from "clsx";
import { useRouter } from "next/router";
import NextLink, { LinkProps as NextLinkProps } from "next/link";
import MuiLink, { LinkProps as MuiLinkProps } from "@material-ui/core/Link";

type LinkProps = NextLinkProps &
MuiLinkProps & {
activeClassName?: string;
naked?: boolean;
};

const NextComposed = React.forwardRef(function NextComposed(
props: LinkProps,
ref: Ref<HTMLAnchorElement>
) {
const { as, href, ...other } = props;

return (
<NextLink href={href} as={as}>
<a ref={ref} {...other} />
</NextLink>
);
});

NextComposed.propTypes = {
as: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
// href: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), // FIXME: provokes a TS error
prefetch: PropTypes.bool,
};

// A styled version of the Next.js Link component:
// https://nextjs.org/docs/#with-link
function Link(props: LinkProps) {
const {
href,
activeClassName = "active",
className: classNameProps,
innerRef,
naked,
...other
} = props;

const router = useRouter();
const pathname = typeof href === "string" ? href : href.pathname;
const className = clsx(classNameProps, {
[activeClassName]: router.pathname === pathname && activeClassName,
});

if (naked) {
return (
<NextComposed
className={className}
ref={innerRef}
href={href}
{...other}
/>
);
}

return (
<MuiLink
component={NextComposed}
className={className}
ref={innerRef}
href={href}
{...other}
/>
);
}

Link.propTypes = {
activeClassName: PropTypes.string,
as: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
className: PropTypes.string,
href: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
innerRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
naked: PropTypes.bool,
onClick: PropTypes.func,
prefetch: PropTypes.bool,
};

export default React.forwardRef((props: LinkProps, ref) => (
<Link {...props} innerRef={ref} />
));
51 changes: 51 additions & 0 deletions packages/@vulcan/next-material-ui/getMuiDocumentInitialProps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from "react";
import Document, { DocumentContext } from "next/document";
import { ServerStyleSheets } from "@material-ui/core/styles";

// @see https://github.com/mui-org/material-ui/blob/master/examples/nextjs/pages/_document.js
const getMuiDocumentInitialProps = async (ctx: DocumentContext) => {
// Resolution order
//
// On the server:
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. document.getInitialProps
// 4. app.render
// 5. page.render
// 6. document.render
//
// On the server with error:
// 1. document.getInitialProps
// 2. app.render
// 3. page.render
// 4. document.render
//
// On the client
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. app.render
// 4. page.render

// Render app and page and get the context of the page with collected side effects.
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;

ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
});

// get parent props
// NOTE: we need to have already enhanced ctx, so it has to be done here
const initialProps = await Document.getInitialProps(ctx);

return {
// Styles fragment is rendered after the app and page rendering finish.
...initialProps,
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement(),
],
};
};
export default getMuiDocumentInitialProps;
5 changes: 5 additions & 0 deletions packages/@vulcan/next-material-ui/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { default as useMuiApp } from "./useMuiApp";

export { default as getMuiDocumentInitialProps } from "./getMuiDocumentInitialProps";

export { default as Link } from "./components/Link";
13 changes: 13 additions & 0 deletions packages/@vulcan/next-material-ui/useMuiApp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @see https://github.com/mui-org/material-ui/blob/master/examples/nextjs/pages/_app.js
import { useEffect } from "react";

const useMuiApp = () => {
useEffect(() => {
// Remove the server-side injected CSS on each render
const jssStyles = document.querySelector("#jss-server-side");
if (jssStyles) {
jssStyles.parentElement.removeChild(jssStyles);
}
}, []);
};
export default useMuiApp;
8 changes: 8 additions & 0 deletions src/lib/material-ui/defaultTheme.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Mui theme
// @see https://material-ui.com/customization/default-theme
import { createMuiTheme } from "@material-ui/core/styles";

// Create a theme instance.
const theme = createMuiTheme({});

export default theme;
21 changes: 20 additions & 1 deletion src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import App, { AppProps } from "next/app";
// Comment if you don't need i18n
import { appWithTranslation } from "~/lib/i18n";
// Comment if you don't need Material UI
import { ThemeProvider } from "@material-ui/core/styles";
import { useMuiApp } from "@vulcan/next-material-ui";
import defaultTheme from "~/lib/material-ui/defaultTheme";
import Head from "next/head";

/*
// Uncomment to enable app-wide Apollo SSR
Expand All @@ -15,7 +20,21 @@ import { getDataFromTree } from "@apollo/react-ssr";
// import "@vulcan/react-i18n"; // enable i18n

function VNSApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
useMuiApp(); // comment to disable Material UI
return (
<>
<Head>
<title>Vulcan Next Starter</title>
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width"
/>
</Head>
<ThemeProvider theme={defaultTheme}>
<Component {...pageProps} />
</ThemeProvider>
</>
);
}

// Neeeded for next-i18n next to work
Expand Down
44 changes: 44 additions & 0 deletions src/pages/_document.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// @see https://github.com/mui-org/material-ui/blob/master/examples/nextjs/pages/_document.js
import React from "react";
import Document, {
Html,
Head,
Main,
NextScript,
DocumentInitialProps,
} from "next/document";
import theme from "~/lib/material-ui/defaultTheme";
import { getMuiDocumentInitialProps } from "@vulcan/next-material-ui";

export default class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>
{/* PWA primary color */}
<meta name="theme-color" content={theme.palette.primary.main} />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}

// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with server-side generation (SSG).
MyDocument.getInitialProps = async (ctx) => {
const muiAndDocumentInitialProps: DocumentInitialProps = await getMuiDocumentInitialProps(
ctx
);

return {
...muiAndDocumentInitialProps,
};
};
20 changes: 20 additions & 0 deletions src/pages/vns/debug/mui.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from "react";
import { Box, Button, Typography, Container } from "@material-ui/core"; // Next has tree shaking
import { Link } from "@vulcan/next-material-ui";

export default function MuiPage() {
return (
<Container maxWidth="sm">
<Box my={4}>
<Typography variant="h4" component="h1" gutterBottom>
Material UI
</Typography>
<Button>
<Link href="/" color="secondary">
Go to the home page
</Link>
</Button>
</Box>
</Container>
);
}
Loading

0 comments on commit da2f01d

Please sign in to comment.