Skip to content

Commit c352849

Browse files
authored
Update to Next 13, rework play page (#35)
* Update to Next.js 13 - Update all dependencies to latest version. * Update images to use new Image component * Refactor to Next.js 13 app directory * Add react-query * Move API logic to src * Add VSCode workspace settings * Update play page - Hero to guess is the one with the highest net worth. - Show 10 heroes from the match instead of every hero. * Update CI workflow - Add exitOnceUploaded for Chromatic. - Add e2e build script for Cypress' build parsing: cypress-io/github-action#272 * Add basic 404 page * Clean up Match component - Remove console.log. - Remove unnecessary div. - Add no-console ESLint rule. * Add unit test for Loading - Remove unused utils file.
1 parent 8cb87f2 commit c352849

File tree

100 files changed

+6880
-4475
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

100 files changed

+6880
-4475
lines changed

.eslintrc.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ module.exports = {
2626
},
2727
plugins: ['@typescript-eslint', 'typescript-sort-keys'],
2828
rules: {
29-
'@typescript-eslint/array-type': [2, { default: 'generic' }],
29+
'@typescript-eslint/array-type': [2],
3030
'@typescript-eslint/no-shadow': 2,
3131
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 2,
3232
'@typescript-eslint/no-unnecessary-condition': 2,
@@ -35,6 +35,7 @@ module.exports = {
3535
'@typescript-eslint/prefer-optional-chain': 2,
3636
'@typescript-eslint/prefer-string-starts-ends-with': 2,
3737
'@typescript-eslint/restrict-plus-operands': 2,
38+
'no-console': 2,
3839
'react/prop-types': 0,
3940
'react/react-in-jsx-scope': 0,
4041
'sort-keys': [2, 'asc', { natural: true }],

.github/workflows/master.yaml

+5-4
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ jobs:
3131
${{ runner.os }}-node-
3232
- name: Install dependencies
3333
run: npm ci
34+
- name: Create mock service worker
35+
run: npm run msw
3436
- name: Run unit tests
3537
run: npm run coverage
36-
env:
37-
CI: true
3838
- name: Build website
3939
run: npm run build
4040
- name: Report coverage
@@ -44,8 +44,9 @@ jobs:
4444
with:
4545
autoAcceptChanges: master
4646
buildScriptName: build-storybook
47-
token: ${{ secrets.GITHUB_TOKEN }}
47+
exitOnceUploaded: true
4848
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
49+
token: ${{ secrets.GITHUB_TOKEN }}
4950

5051
e2e:
5152
name: Run E2E tests
@@ -58,5 +59,5 @@ jobs:
5859
- name: Run Cypress
5960
uses: cypress-io/github-action@v4
6061
with:
61-
build: npm run build
62+
build: npm run e2e:build
6263
start: npm start

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ yarn-error.log*
3333
# vercel
3434
.vercel
3535

36-
# Generated sitemap
36+
# Generated files
37+
public/mockServiceWorker.js
3738
public/robots.txt
3839
public/sitemap.xml
3940
public/sitemap-0.xml

.storybook/main.js

+14-4
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@ const path = require('path');
22
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
33

44
module.exports = {
5-
core: {
6-
builder: 'webpack5',
7-
},
85
addons: [
96
'@storybook/addon-links',
107
'@storybook/addon-essentials',
@@ -26,8 +23,21 @@ module.exports = {
2623
},
2724
'storybook-addon-next-router',
2825
],
26+
core: {
27+
builder: 'webpack5',
28+
},
29+
env: {
30+
// Until storybook-addon-next-router is not updated for Next.js 13
31+
// See https://github.com/lifeiscontent/storybook-addon-next-router/issues/68
32+
__NEXT_NEW_LINK_BEHAVIOR: true,
33+
},
2934
staticDirs: ['../public'],
30-
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
35+
stories: [
36+
'../app/**/*.stories.mdx',
37+
'../app/**/*.stories.@(js|jsx|ts|tsx)',
38+
'../src/**/*.stories.mdx',
39+
'../src/**/*.stories.@(js|jsx|ts|tsx)',
40+
],
3141
webpackFinal: (config) => {
3242
config.resolve.plugins = [
3343
...(config.resolve.plugins || []),

.storybook/preview.js

+24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,30 @@
1+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
2+
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
3+
import { initialize, mswDecorator } from 'msw-storybook-addon';
14
import { RouterContext } from 'next/dist/shared/lib/router-context';
5+
import * as NextImage from 'next/image';
26
import '../styles/globals.scss';
37

8+
initialize();
9+
10+
const OriginalNextImage = NextImage.default;
11+
Object.defineProperty(NextImage, 'default', {
12+
configurable: true,
13+
value: (props) => <OriginalNextImage {...props} unoptimized />,
14+
});
15+
16+
const queryClient = new QueryClient();
17+
18+
export const decorators = [
19+
mswDecorator,
20+
(Story) => (
21+
<QueryClientProvider client={queryClient}>
22+
<Story />
23+
<ReactQueryDevtools />
24+
</QueryClientProvider>
25+
),
26+
];
27+
428
export const parameters = {
529
actions: { argTypesRegex: '^on[A-Z].*' },
630
controls: {

.vscode/settings.json

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

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ The live version is available [here](https://www.guessthehero.com/).
1515
# Install dependencies
1616
$ npm install
1717

18+
# Create mock service worker
19+
$ npm run msw
20+
1821
# Serve application in development at http://localhost:3000
1922
$ npm run dev
2023

app/Index.stories.tsx

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { ComponentMeta, ComponentStory } from '@storybook/react';
2+
import Component from './page';
3+
4+
export default {
5+
component: Component,
6+
parameters: {
7+
layout: 'fullscreen',
8+
},
9+
title: 'Pages/Index',
10+
} as ComponentMeta<typeof Component>;
11+
12+
const Template: ComponentStory<typeof Component> = (args) => (
13+
<Component {...args} />
14+
);
15+
16+
export const Index = Template.bind({});

src/components/pages/About/About.stories.tsx app/about/About.stories.tsx

+1-11
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
11
import { ComponentMeta, ComponentStory } from '@storybook/react';
2-
import Base from '../../molecules/Base';
3-
import Component from './About';
2+
import Component from './page';
43

54
export default {
65
component: Component,
7-
decorators: [
8-
(Story) => (
9-
<div id="__next" style={{ minHeight: '100vh' }}>
10-
<Base>
11-
<Story />
12-
</Base>
13-
</div>
14-
),
15-
],
166
parameters: {
177
layout: 'fullscreen',
188
},

app/about/head.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const Head = () => (
2+
<>
3+
<title>About - Guess the Hero</title>
4+
<meta
5+
content="Read more information about the website, how it works and how you can contribute."
6+
name="description"
7+
/>
8+
</>
9+
);
10+
11+
export default Head;

src/components/pages/About/About.tsx app/about/page.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1+
import clsx from 'clsx';
12
import { FC } from 'react';
23
import Card from 'src/components/atoms/Card';
3-
import styles from './About.module.scss';
4-
import clsx from 'clsx';
4+
import styles from './styles.module.scss';
55

66
const About: FC = () => (
77
<div className={clsx('container', styles.container)}>
File renamed without changes.

app/error.tsx

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use client';
2+
3+
interface Props {
4+
error: Error;
5+
reset: VoidFunction;
6+
}
7+
8+
const ErrorPage: React.FC<Props> = ({ error, reset }) => (
9+
<div className="container">
10+
<h1>Error</h1>
11+
<p>{error.message}</p>
12+
<button className="btn" onClick={reset}>
13+
Try again
14+
</button>
15+
</div>
16+
);
17+
18+
export default ErrorPage;

app/head.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const Head = () => (
2+
<>
3+
<title>A Dota 2 quiz - Guess the Hero</title>
4+
<meta
5+
content="A Dota 2 quiz game. Guess the hero from items bought in a match. See the purchased items of a hero and other optional stats and use them to try to guess the hero."
6+
name="description"
7+
/>
8+
</>
9+
);
10+
11+
export default Head;

src/components/pages/Heroes/Heroes.spec.tsx app/heroes/Heroes.spec.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import heroesJson from 'dotaconstants/build/heroes.json';
21
import { render, screen } from '@testing-library/react';
3-
import Heroes from '.';
2+
import heroesJson from 'dotaconstants/build/heroes.json';
3+
import Heroes from './page';
44

55
describe('Heroes', () => {
66
it('should render every hero', () => {

src/components/pages/Heroes/Heroes.stories.tsx app/heroes/Heroes.stories.tsx

+1-11
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
11
import { ComponentMeta, ComponentStory } from '@storybook/react';
2-
import Base from '../../molecules/Base';
3-
import Component from './Heroes';
2+
import Component from './page';
43

54
export default {
65
component: Component,
7-
decorators: [
8-
(Story) => (
9-
<div id="__next" style={{ minHeight: '100vh' }}>
10-
<Base>
11-
<Story />
12-
</Base>
13-
</div>
14-
),
15-
],
166
parameters: {
177
layout: 'fullscreen',
188
},

app/heroes/head.tsx

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const Head = () => (
2+
<>
3+
<title>Dota 2 heroes - Guess the Hero</title>
4+
<meta content="See all of Dota 2's heroes." name="description" />
5+
</>
6+
);
7+
8+
export default Head;

src/components/pages/Heroes/Heroes.tsx app/heroes/page.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import heroesJson from 'dotaconstants/build/heroes.json';
33
import { FC } from 'react';
44
import Card from 'src/components/atoms/Card';
55
import HeroIcon from 'src/components/atoms/HeroIcon';
6-
import styles from './Heroes.module.scss';
6+
import styles from './styles.module.scss';
77

88
const ATTRIBUTES = ['Strength', 'Agility', 'Intelligence'];
99

File renamed without changes.

src/components/pages/Items/Items.stories.tsx app/items/Items.stories.tsx

+1-11
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,8 @@
11
import { ComponentMeta, ComponentStory } from '@storybook/react';
2-
import Base from '../../molecules/Base';
3-
import Component from './Items';
2+
import Component from './page';
43

54
export default {
65
component: Component,
7-
decorators: [
8-
(Story) => (
9-
<div id="__next" style={{ minHeight: '100vh' }}>
10-
<Base>
11-
<Story />
12-
</Base>
13-
</div>
14-
),
15-
],
166
parameters: {
177
layout: 'fullscreen',
188
},

app/items/head.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const Head = () => (
2+
<>
3+
<title>Dota 2 items - Guess the Hero</title>
4+
<meta
5+
content="Get to know all the items Dota 2 has to offer and learn all about them. Check their abilities, costs, lore and even more."
6+
name="description"
7+
/>
8+
</>
9+
);
10+
11+
export default Head;

src/components/pages/Items/Items.tsx app/items/page.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import clsx from 'clsx';
22
import { FC } from 'react';
33
import ItemList from 'src/components/molecules/ItemList';
4-
import styles from './Items.module.scss';
4+
import styles from './styles.module.scss';
55

66
const ITEMS = [
77
{
File renamed without changes.

app/layout.tsx

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use client';
2+
3+
import { QueryClientProvider } from '@tanstack/react-query';
4+
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
5+
import { FC, ReactNode } from 'react';
6+
import Footer from 'src/components/atoms/Footer';
7+
import Header from 'src/components/atoms/Header';
8+
import queryClient from 'src/data/query-client';
9+
import 'styles/globals.scss';
10+
11+
const RootLayout: FC<{ children: ReactNode }> = ({ children }) => (
12+
<html lang="en">
13+
<head>
14+
<meta charSet="utf-8" />
15+
<meta name="viewport" content="width=device-width" />
16+
<link rel="shortcut icon" href="/favicon.png" />
17+
</head>
18+
<body>
19+
<QueryClientProvider client={queryClient}>
20+
<Header />
21+
<main>{children}</main>
22+
<Footer />
23+
<div className="absolute" id="modal" />
24+
<ReactQueryDevtools />
25+
</QueryClientProvider>
26+
</body>
27+
</html>
28+
);
29+
30+
export default RootLayout;

app/loading.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Loading from 'src/components/molecules/Loading';
2+
3+
export default Loading;

app/not-found.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Link from 'next/link';
2+
3+
const NotFound: React.FC = () => (
4+
<div className="container">
5+
<h1>Not found</h1>
6+
<Link href="/">To home page</Link>
7+
</div>
8+
);
9+
10+
export default NotFound;
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
import clsx from 'clsx';
22
import Link from 'next/link';
33
import { FC } from 'react';
4-
import Button from 'src/components/atoms/Button';
54
import Card from 'src/components/atoms/Card';
6-
import styles from './Home.module.scss';
5+
import styles from './styles.module.scss';
76

8-
const Home: FC = () => (
7+
const Index: FC = () => (
98
<div className={clsx('container', styles.container)}>
109
<h1>Test your Dota 2 knowledge</h1>
1110
<Card>
1211
<p>How well can you guess a hero from seeing their purchased items?</p>
13-
<Link href="/game" passHref={true}>
14-
<Button>Play now</Button>
12+
<Link className="btn" href="/play">
13+
Play now
1514
</Link>
1615
</Card>
1716
</div>
1817
);
1918

20-
export default Home;
19+
export default Index;

app/play/head.tsx

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const Head = () => (
2+
<>
3+
<title>Dota 2 - Guess the Hero</title>
4+
<meta
5+
content="Play a quiz game of Dota 2. Guess the hero from a random match only by seeing their inventory."
6+
name="description"
7+
/>
8+
</>
9+
);
10+
11+
export default Head;

0 commit comments

Comments
 (0)