Skip to content

Commit 091a05e

Browse files
committed
project setup
1 parent a1135f7 commit 091a05e

17 files changed

+2781
-2
lines changed

.eslintrc.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "next/core-web-vitals"
3+
}

.gitignore

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# next.js
12+
/.next/
13+
/out/
14+
15+
# production
16+
/build
17+
18+
# misc
19+
.DS_Store
20+
*.pem
21+
22+
# debug
23+
npm-debug.log*
24+
yarn-debug.log*
25+
yarn-error.log*
26+
.pnpm-debug.log*
27+
28+
# local env files
29+
.env*.local
30+
31+
# vercel
32+
.vercel
33+
34+
# typescript
35+
*.tsbuildinfo
36+
next-env.d.ts

.vscode/settings.json

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"typescript.tsdk": "node_modules\\typescript\\lib",
3+
"typescript.enablePromptUseWorkspaceTsdk": true
4+
}

README.md

+44-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,44 @@
1-
# design-engineer-technical-challenge
2-
This challenge is part of the hiring process at MUI for the Design Engineer related positions.
1+
# ⚛️ Design Engineer @ MUI: Technical challenge
2+
3+
This challenge is part of the hiring process at MUI for the Design Engineer position.
4+
The idea is to make as much progress as possible under a given time constraint (3-4 hours).
5+
6+
## Why are we doing this?
7+
8+
At MUI, you'll frequently flesh out product requirements, turning them into implementations that take into account great design & code standards.
9+
This challenge aims at simulating how a potential day-to-day situation plays out.
10+
We'll review how you make design decisions and the quality of the written code.
11+
12+
## Context about MUI
13+
14+
MUI's objective is to become the UI toolkit for React developers.
15+
We're unifying the fragmented ecosystem of dependencies into a single set of simple, beautiful, consistent, and accessible React components.
16+
17+
Our mission is then, ultimately, to make building great React UIs and web apps a breeze ⎯ quicker, simpler, and accessible to more people.
18+
At the end of the day, it's about [_writing less code_](https://youtu.be/GnO7D5UaDig?t=2451).
19+
20+
Head over to [our company Handbook](https://mui-org.notion.site/Why-MUI-d8b8c142a6a44e3aa963f26edf4e03db) to learn more!
21+
22+
## Challenge summary
23+
24+
Given that the Design Engineer position sits right at the intersection between engineering and design, this challenge was tailored to elevate your skills in both areas.
25+
You’ll develop a stacked **Snackbar** component, and here’s the reference design:
26+
27+
![Snackbars example design](public/example.png)
28+
29+
Feel free to add your own creative spin to this design, tweaking it however you want to add more spice and personal taste.
30+
However, we do have a few requirements:
31+
32+
- **Stack:** use MUI Base’s [`UnstyledSnackbar`](https://mui.com/base/react-snackbar/) component or even other headless/unstyled component libraries as the foundation for it.
33+
- **Expand & collapse interaction:** the Snackbar sitting behind should be fully visible as you hover the stack. It then should collapse as you hover out of it.
34+
- **Dark mode:** make sure to support light and dark modes.
35+
36+
## Submission instructions
37+
38+
- **DO NOT** host your project on a public repository.
39+
- Please send us a .zip file containing this project using the upload link that we have provided by email (**with** the _.git_ folder).
40+
- To significantly reduce the size of the archive, you can remove the `/_node_modules_/` and `/docs/.next/` folders.
41+
- If you don't have the upload link, you can simply send it to [email protected].
42+
43+
We're excited and looking forward to seeing what you'll create!
44+
Good luck 🚀

next.config.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/** @type {import('next').NextConfig} */
2+
const nextConfig = {
3+
experimental: {},
4+
}
5+
6+
module.exports = nextConfig

package.json

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "design-engineer-technical-challenge",
3+
"version": "0.1.0",
4+
"private": true,
5+
"scripts": {
6+
"dev": "next dev",
7+
"build": "next build",
8+
"start": "next start",
9+
"lint": "next lint"
10+
},
11+
"dependencies": {
12+
"@emotion/react": "^11.10.6",
13+
"@emotion/server": "^11.10.0",
14+
"@emotion/styled": "^11.10.6",
15+
"@types/node": "18.15.3",
16+
"@types/react": "18.0.28",
17+
"@types/react-dom": "18.0.11",
18+
"eslint": "8.36.0",
19+
"eslint-config-next": "13.2.4",
20+
"next": "13.2.4",
21+
"react": "18.2.0",
22+
"react-dom": "18.2.0",
23+
"typescript": "4.9.5"
24+
}
25+
}

pages/_app.tsx

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as React from 'react';
2+
import Head from 'next/head';
3+
import { AppProps } from 'next/app';
4+
import { CacheProvider, EmotionCache } from '@emotion/react';
5+
import createEmotionCache from '../src/createEmotionCache';
6+
7+
// Client-side cache, shared for the whole session of the user in the browser.
8+
const clientSideEmotionCache = createEmotionCache();
9+
10+
export interface MyAppProps extends AppProps {
11+
emotionCache?: EmotionCache;
12+
}
13+
14+
export default function MyApp(props: MyAppProps) {
15+
const { Component, emotionCache = clientSideEmotionCache, pageProps } = props;
16+
return (
17+
<CacheProvider value={emotionCache}>
18+
<Head>
19+
<meta name="viewport" content="initial-scale=1, width=device-width" />
20+
</Head>
21+
<Component {...pageProps} />
22+
</CacheProvider>
23+
);
24+
}

pages/_document.tsx

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import * as React from 'react';
2+
import Document, {
3+
Html,
4+
Head,
5+
Main,
6+
NextScript,
7+
DocumentProps,
8+
DocumentContext,
9+
} from 'next/document';
10+
import createEmotionServer from '@emotion/server/create-instance';
11+
import { AppType } from 'next/app';
12+
import createEmotionCache from '../src/createEmotionCache';
13+
import { MyAppProps } from './_app';
14+
15+
interface MyDocumentProps extends DocumentProps {
16+
emotionStyleTags: JSX.Element[];
17+
}
18+
19+
export default function MyDocument({ emotionStyleTags }: MyDocumentProps) {
20+
return (
21+
<Html lang="en">
22+
<Head>
23+
<link rel="shortcut icon" href="/favicon.ico" />
24+
<meta name="emotion-insertion-point" content="" />
25+
{emotionStyleTags}
26+
</Head>
27+
<body>
28+
<Main />
29+
<NextScript />
30+
</body>
31+
</Html>
32+
);
33+
}
34+
35+
// `getInitialProps` belongs to `_document` (instead of `_app`),
36+
// it's compatible with static-site generation (SSG).
37+
MyDocument.getInitialProps = async (ctx: DocumentContext) => {
38+
// Resolution order
39+
//
40+
// On the server:
41+
// 1. app.getInitialProps
42+
// 2. page.getInitialProps
43+
// 3. document.getInitialProps
44+
// 4. app.render
45+
// 5. page.render
46+
// 6. document.render
47+
//
48+
// On the server with error:
49+
// 1. document.getInitialProps
50+
// 2. app.render
51+
// 3. page.render
52+
// 4. document.render
53+
//
54+
// On the client
55+
// 1. app.getInitialProps
56+
// 2. page.getInitialProps
57+
// 3. app.render
58+
// 4. page.render
59+
60+
const originalRenderPage = ctx.renderPage;
61+
62+
// You can consider sharing the same Emotion cache between all the SSR requests to speed up performance.
63+
// However, be aware that it can have global side effects.
64+
const cache = createEmotionCache();
65+
const { extractCriticalToChunks } = createEmotionServer(cache);
66+
67+
ctx.renderPage = () =>
68+
originalRenderPage({
69+
enhanceApp: (App: React.ComponentType<React.ComponentProps<AppType> & MyAppProps>) =>
70+
function EnhanceApp(props) {
71+
return <App emotionCache={cache} {...props} />;
72+
},
73+
});
74+
75+
const initialProps = await Document.getInitialProps(ctx);
76+
// This is important. It prevents Emotion to render invalid HTML.
77+
// See https://github.com/mui/material-ui/issues/26561#issuecomment-855286153
78+
const emotionStyles = extractCriticalToChunks(initialProps.html);
79+
const emotionStyleTags = emotionStyles.styles.map((style) => (
80+
<style
81+
data-emotion={`${style.key} ${style.ids.join(' ')}`}
82+
key={style.key}
83+
// eslint-disable-next-line react/no-danger
84+
dangerouslySetInnerHTML={{ __html: style.css }}
85+
/>
86+
));
87+
88+
return {
89+
...initialProps,
90+
emotionStyleTags,
91+
};
92+
};

pages/index.tsx

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as React from 'react';
2+
import Snackbar from '../src/components/Snackbar';
3+
4+
// Update this page to show-case the demo of the Snackbar component
5+
export default function Page() {
6+
return (
7+
<>
8+
<h1>Demo using the Snackbar component</h1>
9+
<Snackbar />
10+
</>
11+
);
12+
}

public/example.png

387 KB
Loading

public/next.svg

+1
Loading

public/thirteen.svg

+1
Loading

public/vercel.svg

+1
Loading

src/components/Snackbar.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Your code goes here
2+
export default function Snackbar() {
3+
return null;
4+
}

src/createEmotionCache.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import createCache from '@emotion/cache';
2+
3+
const isBrowser = typeof document !== 'undefined';
4+
5+
// On the client side, Create a meta tag at the top of the <head> and set it as insertionPoint.
6+
// This assures that MUI styles are loaded first.
7+
// It allows developers to easily override MUI styles with other styling solutions, like CSS modules.
8+
export default function createEmotionCache() {
9+
let insertionPoint;
10+
11+
if (isBrowser) {
12+
const emotionInsertionPoint = document.querySelector<HTMLMetaElement>(
13+
'meta[name="emotion-insertion-point"]',
14+
);
15+
insertionPoint = emotionInsertionPoint ?? undefined;
16+
}
17+
18+
return createCache({ key: 'mui-style', insertionPoint });
19+
}

tsconfig.json

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es5",
4+
"lib": ["dom", "dom.iterable", "esnext"],
5+
"allowJs": true,
6+
"skipLibCheck": true,
7+
"strict": true,
8+
"forceConsistentCasingInFileNames": true,
9+
"noEmit": true,
10+
"esModuleInterop": true,
11+
"module": "esnext",
12+
"moduleResolution": "node",
13+
"resolveJsonModule": true,
14+
"isolatedModules": true,
15+
"jsx": "preserve",
16+
"incremental": true,
17+
"plugins": [
18+
{
19+
"name": "next"
20+
}
21+
],
22+
"paths": {
23+
"@/*": ["./src/*"]
24+
}
25+
},
26+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
27+
"exclude": ["node_modules"]
28+
}

0 commit comments

Comments
 (0)