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

Search component #64

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ NEXTAUTH_URL=http://localhost:3000
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
NEXT_PUBLIC_IMAGE_HOST=http://localhost:1337
NEXT_PUBLIC_GA_TRACKING=

NEXT_PUBLIC_MEILISEARCH_SERVER=http://localhost:7700
NEXT_PUBLIC_MEILISEARCH_PUBLIC_KEY=
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
},
"dependencies": {
"@apollo/client": "^3.3.20",
"@meilisearch/instant-meilisearch": "^0.5.0",
"@stripe/react-stripe-js": "^1.4.1",
"@stripe/stripe-js": "^1.15.1",
"@styled-icons/boxicons-regular": "^10.34.0",
Expand All @@ -57,6 +58,7 @@
"polished": "^4.1.3",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-instantsearch-dom": "^6.11.2",
"react-slick": "^0.28.1",
"storybook-addon-next-router": "^2.0.4",
"styled-components": "5.3.0",
Expand All @@ -78,6 +80,7 @@
"@types/jest": "^26.0.23",
"@types/node": "^15.12.4",
"@types/react": "^17.0.11",
"@types/react-instantsearch-dom": "^6.10.1",
"@types/react-slick": "^0.23.4",
"@types/styled-components": "^5.1.10",
"@typescript-eslint/eslint-plugin": "^4.28.0",
Expand Down
6 changes: 5 additions & 1 deletion src/components/Banner/__snapshots__/test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ exports[`<Banner /> should render correctly 1`] = `
padding: 0.8rem 4.8rem;
}
.c5 > span {
white-space: nowrap;
}
.c5:focus {
box-shadow: 0 0 0 3px #3CD3C1;
}
Expand Down Expand Up @@ -156,4 +160,4 @@ exports[`<Banner /> should render correctly 1`] = `
</a>
</div>
</main>
`;
`
4 changes: 4 additions & 0 deletions src/components/Button/__snapshots__/test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ exports[`<Button /> should render the medium size by default 1`] = `
padding: 0.8rem 3.2rem;
}
.c0 > span {
white-space: nowrap;
}
.c0:focus {
box-shadow: 0 0 0 3px #3CD3C1;
}
Expand Down
4 changes: 4 additions & 0 deletions src/components/Button/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ export const Wrapper = styled.button<WrapperProps>`
padding: ${theme.spacings.xxsmall};
text-decoration: none;
& > span {
white-space: nowrap;
}
&:focus {
box-shadow: 0 0 0 3px ${theme.colors.secondary};
}
Expand Down
4 changes: 4 additions & 0 deletions src/components/Empty/__snapshots__/test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ exports[`<Empty /> should render correctly 1`] = `
padding: 0.8rem 3.2rem;
}
.c3 > span {
white-space: nowrap;
}
.c3:focus {
box-shadow: 0 0 0 3px #3CD3C1;
}
Expand Down
4 changes: 4 additions & 0 deletions src/components/FormSignIn/__snapshots__/test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ exports[`<FormSignIn /> should render the form 1`] = `
width: 100%;
}
.c8 > span {
white-space: nowrap;
}
.c8:focus {
box-shadow: 0 0 0 3px #3CD3C1;
}
Expand Down
4 changes: 4 additions & 0 deletions src/components/FormSignUp/__snapshots__/test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ exports[`<FormSignUp /> should render the form 1`] = `
width: 100%;
}
.c7 > span {
white-space: nowrap;
}
.c7:focus {
box-shadow: 0 0 0 3px #3CD3C1;
}
Expand Down
10 changes: 9 additions & 1 deletion src/components/GameCard/__snapshots__/test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ exports[`<GameCard /> should render correctly 1`] = `
color: #F231A5;
}
.c7 > span {
white-space: nowrap;
}
.c7:focus {
box-shadow: 0 0 0 3px #3CD3C1;
}
Expand Down Expand Up @@ -81,6 +85,10 @@ exports[`<GameCard /> should render correctly 1`] = `
font-size: 1.2rem;
}
.c11 > span {
white-space: nowrap;
}
.c11:focus {
box-shadow: 0 0 0 3px #3CD3C1;
}
Expand Down Expand Up @@ -294,4 +302,4 @@ exports[`<GameCard /> should render correctly 1`] = `
</div>
</div>
</article>
`;
`
10 changes: 9 additions & 1 deletion src/components/GameInfo/__snapshots__/test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ exports[`<GameInfo /> should render game informations 1`] = `
padding: 0.8rem 4.8rem;
}
.c6 > span {
white-space: nowrap;
}
.c6:focus {
box-shadow: 0 0 0 3px #3CD3C1;
}
Expand Down Expand Up @@ -79,6 +83,10 @@ exports[`<GameInfo /> should render game informations 1`] = `
color: #F231A5;
}
.c8 > span {
white-space: nowrap;
}
.c8:focus {
box-shadow: 0 0 0 3px #3CD3C1;
}
Expand Down Expand Up @@ -309,4 +317,4 @@ exports[`<GameInfo /> should render game informations 1`] = `
</button>
</div>
</div>
`;
`
6 changes: 5 additions & 1 deletion src/components/Highlight/__snapshots__/test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ exports[`<Highlight /> should render headings and button 1`] = `
padding: 0.8rem 3.2rem;
}
.c5 > span {
white-space: nowrap;
}
.c5:focus {
box-shadow: 0 0 0 3px #3CD3C1;
}
Expand Down Expand Up @@ -139,4 +143,4 @@ exports[`<Highlight /> should render headings and button 1`] = `
</a>
</div>
</section>
`;
`
6 changes: 2 additions & 4 deletions src/components/Menu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import Link from 'next/link'

import { useState } from 'react'
import { Menu2 as MenuIcon } from '@styled-icons/remix-fill/Menu2'
import { Search as SearchIcon } from '@styled-icons/material-outlined/Search'
import { Close as CloseIcon } from '@styled-icons/material-outlined/Close'

import Button from 'components/Button'
Expand All @@ -12,6 +11,7 @@ import * as S from './styles'
import CartDropdown from 'components/CartDropdown'
import CartIcon from 'components/CartIcon'
import UserDropdown from 'components/UserDropdown'
import Search from 'components/Search'

export type MenuProps = {
username?: string | null
Expand Down Expand Up @@ -51,9 +51,7 @@ const Menu = ({ username, loading }: MenuProps) => {
{!loading && (
<>
<S.MenuGroup>
<S.IconWrapper>
<SearchIcon aria-label="Search" />
</S.IconWrapper>
<Search />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we added the search here, we'll need to mock inside tests to not break the unit tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point! I'll add the mock and push changes.

<S.IconWrapper>
<MediaMatch greaterThan="medium">
<CartDropdown />
Expand Down
9 changes: 8 additions & 1 deletion src/components/Menu/test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,20 @@ useRouter.mockImplementation(() => ({
query: {}
}))

jest.mock('components/Search', () => ({
__esModule: true,
default: function Mock() {
return <div data-testid="Mock Search" />
}
}))

describe('<Menu />', () => {
it('should render the menu', () => {
render(<Menu />)

expect(screen.getByLabelText(/open menu/i)).toBeInTheDocument()
expect(screen.getByRole('img', { name: /won games/i })).toBeInTheDocument()
expect(screen.getByLabelText(/search/i)).toBeInTheDocument()
expect(screen.getByTestId(/mock search/i)).toBeInTheDocument()
expect(screen.getAllByLabelText(/shopping cart/i)).toHaveLength(2)
})

Expand Down
68 changes: 68 additions & 0 deletions src/components/Search/Hits/Hit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import Image from 'next/image'
import Link from 'next/link'

import { Highlight } from 'react-instantsearch-dom'
import { getImageUrl } from 'utils/getImageUrl'
import formatPrice from 'utils/format-price'

import { Apple, Windows, Linux } from '@styled-icons/fa-brands'
import * as S from './styles'

import { GameHitProps, Platform } from '.'

export type HitProps = {
hit: GameHitProps
}

const Hit = ({ hit }: HitProps) => {
const platformIcons = {
linux: <Linux title="Linux" />,
mac: <Apple title="Mac" />,
windows: <Windows title="Windows" />
}
const releaseYear =
hit.release_date && new Date(hit.release_date).getFullYear()

return (
<Link href={`game/${hit.slug}`} passHref>
<S.Result>
<S.ImageWrapper>
<Image
src={`${getImageUrl(hit.cover?.url)}`}
alt={hit.name}
layout="fill"
objectFit="cover"
/>
</S.ImageWrapper>
<S.Info>
<S.Title>
<Highlight attribute="name" hit={hit} />
{releaseYear && (
<S.ReleaseYear
itemProp="releaseYear"
dateTime={hit.release_date!}
>
{releaseYear}
</S.ReleaseYear>
)}
</S.Title>
<S.Details>
<S.Price>{formatPrice(hit.price)}</S.Price>
<S.Platform>
{hit.platforms.map((icon: Platform) => (
<S.PlatformIcon key={`${hit.id}${icon.name}`}>
{platformIcons[icon.name]}
</S.PlatformIcon>
))}
</S.Platform>
</S.Details>
<S.Description>
<Highlight attribute="short_description" hit={hit} />
</S.Description>
</S.Info>
</S.Result>
</Link>
)
}

export default Hit
47 changes: 47 additions & 0 deletions src/components/Search/Hits/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { connectHits, connectStateResults } from 'react-instantsearch-dom'

import NoResults from '../NoResults'
import Hit from './Hit'

import * as S from './styles'

export type Platform = {
name: 'windows' | 'linux' | 'mac'
}

export type Cover = {
url: string
}

export type GameHitProps = {
id: string
name: string
short_description: string
cover: Cover | null
slug: string
price: number
platforms: Platform[]
release_date: string | null
}

export type HitsProps = {
hits: GameHitProps[]
}

const Hits = connectHits(({ hits }: HitsProps) => (
<S.List>
{hits.length ? (
hits.map((hit) => (
<S.ListItem key={hit.id}>
<Hit hit={hit} />
</S.ListItem>
))
) : (
<NoResults />
)}
</S.List>
))

export default connectStateResults(({ searchState }) =>
searchState?.query ? <Hits /> : null
)
28 changes: 28 additions & 0 deletions src/components/Search/Hits/mocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { GameHitProps } from '.'

export default [
{
id: '1',
name: 'Game 1',
short_description: 'Description',
cover: {
url: '/cover-1.jpg'
},
slug: 'game-1',
price: 25,
platforms: [{ name: 'windows' }],
release_date: '2015-12-05'
},
{
id: '2',
name: 'Game 2',
short_description: 'Description',
cover: {
url: '/cover-2.jpg'
},
slug: 'game-2',
price: 12,
platforms: [{ name: 'linux' }],
release_date: '2006-10-30'
}
] as GameHitProps[]
Loading