From d0a5d2dafb364e6d7b086c48fbc7bfd3fe35ad03 Mon Sep 17 00:00:00 2001 From: Guillermo Angulo Date: Mon, 28 Jun 2021 19:55:47 -0300 Subject: [PATCH] Add Search component --- .env.development | 4 + package.json | 3 + src/components/Menu/index.tsx | 6 +- src/components/Search/index.tsx | 214 ++++++++++++++++++ src/components/Search/stories.tsx | 14 ++ src/components/Search/styles.ts | 362 ++++++++++++++++++++++++++++++ src/components/Search/test.tsx | 89 ++++++++ src/utils/meilisearchClient.ts | 9 + yarn.lock | 212 ++++++++++++++++- 9 files changed, 901 insertions(+), 12 deletions(-) create mode 100644 src/components/Search/index.tsx create mode 100644 src/components/Search/stories.tsx create mode 100644 src/components/Search/styles.ts create mode 100644 src/components/Search/test.tsx create mode 100644 src/utils/meilisearchClient.ts diff --git a/.env.development b/.env.development index 9a53940..085090e 100644 --- a/.env.development +++ b/.env.development @@ -4,3 +4,7 @@ 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_KEY= diff --git a/package.json b/package.json index 45f20a5..e16fc46 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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", @@ -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", diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx index 1e19890..7effbc6 100644 --- a/src/components/Menu/index.tsx +++ b/src/components/Menu/index.tsx @@ -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' @@ -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 @@ -51,9 +51,7 @@ const Menu = ({ username, loading }: MenuProps) => { {!loading && ( <> - - - + diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx new file mode 100644 index 0000000..d040ff2 --- /dev/null +++ b/src/components/Search/index.tsx @@ -0,0 +1,214 @@ +import { useEffect, useRef, useState } from 'react' +import Image from 'next/image' +import Link from 'next/link' + +import { searchClient } from 'utils/meilisearchClient' +import { + InstantSearch, + Highlight, + Configure, + connectStateResults, + connectSearchBox, + connectHits +} from 'react-instantsearch-dom' +import { getImageUrl } from 'utils/getImageUrl' +import formatPrice from 'utils/format-price' + +import { GameFragment_cover } from 'graphql/generated/GameFragment' + +import { + Search as SearchIcon, + Close as CloseIcon +} from '@styled-icons/material-outlined' +import { Apple, Windows, Linux } from '@styled-icons/fa-brands' +import * as S from './styles' +import Button from 'components/Button' + +const Search = () => { + const [isOpen, setIsOpen] = useState(false) + + useEffect(() => { + document.body.style.overflow = isOpen ? 'hidden' : 'unset' + + return () => { + document.body.style.overflow = 'unset' + } + }, [isOpen]) + + return ( + + + setIsOpen(!isOpen)} + isOpen={isOpen} + /> + {/** Maximum number of results */} + + + + setIsOpen(!isOpen)} aria-hidden={!isOpen} /> + + ) +} + +export default Search + +export type SearchBoxProps = { + handleVisibility: () => void + isOpen: boolean + currentRefinement: string + refine: (v: string) => void +} + +const SearchBox = connectSearchBox( + ({ handleVisibility, isOpen, currentRefinement, refine }: SearchBoxProps) => { + const inputRef = useRef(null) + + useEffect(() => { + if (isOpen && inputRef.current) { + inputRef.current.focus() + } + }, [isOpen]) + + return ( + + + refine(event.currentTarget.value)} + /> + + + {isOpen ? ( + + ) : ( + + )} + + + + ) + } +) + +const Results = connectStateResults(({ searchState }) => + searchState?.query ? : null +) + +type Platform = { + name: 'windows' | 'linux' | 'mac' +} + +export type GameHitProps = { + id: string + name: string + short_description: string + cover: GameFragment_cover | null + slug: string + price: number + platforms: Platform[] + release_date: string | null +} + +export type HitsProps = { + hits: GameHitProps[] +} + +const Hits = connectHits(({ hits }: HitsProps) => ( + + {hits.length ? ( + hits.map((hit) => ( + + + + )) + ) : ( + + )} + +)) + +export type HitProps = { + hit: GameHitProps +} + +const Hit = ({ hit }: HitProps) => { + const platformIcons = { + linux: , + mac: , + windows: + } + const releaseYear = + hit.release_date && new Date(hit.release_date).getFullYear() + + return ( + + + + {hit.name} + + + + + {releaseYear && ( + + {releaseYear} + + )} + + + {formatPrice(hit.price)} + + {hit.platforms.map((icon: Platform) => ( + + {platformIcons[icon.name]} + + ))} + + + + + + + + + ) +} + +const NoResults = () => ( + + A gamer in a couch playing videogame + + + + No Results Found + + + Try searching another term. + + + + + + +) diff --git a/src/components/Search/stories.tsx b/src/components/Search/stories.tsx new file mode 100644 index 0000000..5eec90c --- /dev/null +++ b/src/components/Search/stories.tsx @@ -0,0 +1,14 @@ +import { Story, Meta } from '@storybook/react/types-6-0' +import Search from '.' + +export default { + title: 'Search', + component: Search, + parameters: { + backgrounds: { + default: 'won-dark' + } + } +} as Meta + +export const Default: Story = () => diff --git a/src/components/Search/styles.ts b/src/components/Search/styles.ts new file mode 100644 index 0000000..fc65310 --- /dev/null +++ b/src/components/Search/styles.ts @@ -0,0 +1,362 @@ +import styled, { css, DefaultTheme } from 'styled-components' +import media from 'styled-media-query' +import { darken } from 'polished' + +export const Overlay = styled.div` + ${({ theme }) => css` + background: rgba(0, 0, 0, 0.5); + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: ${theme.layers.overlay}; + `} +` + +type WrapperProps = { + isOpen?: boolean +} + +export const Result = styled.a` + ${({ theme }) => css` + display: flex; + padding: 0.6rem ${theme.spacings.xxsmall}; + background: ${theme.colors.lightBg}; + color: ${theme.colors.darkGray}; + height: 10rem; + font-size: ${theme.font.sizes.small}; + cursor: pointer; + text-decoration: none; + border-radius: ${theme.border.radius}; + transition: background ${theme.transition.fast}, + color ${theme.transition.fast}; + + &:hover { + color: ${theme.colors.white}; + background: ${theme.colors.primary}; + + * > time { + color: ${theme.colors.lightGray}; + } + } + + ${media.lessThan('medium')` + font-size: ${theme.font.sizes.xsmall}; + `} + `} +` + +export const Title = styled.div` + ${({ theme }) => css` + font-size: ${theme.font.sizes.medium}; + font-weight: ${theme.font.bold}; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + + ${media.lessThan('medium')` + font-size: ${theme.font.sizes.small}; + `} + `} +` + +export const Description = styled.div` + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +` + +export const Info = styled.div` + ${({ theme }) => css` + align-self: center; + padding: 0 ${theme.spacings.xsmall}; + overflow: hidden; + + em { + font-style: normal; + font-weight: ${theme.font.bold}; + color: ${theme.colors.black}; + background: #fdf39a; + border-radius: 0.2rem; + } + + ${media.lessThan('small')` + padding: 0; + `} + `} +` + +export const ImageWrapper = styled.div` + position: relative; + min-width: 22rem; + + img { + max-width: 100%; + max-height: 100%; + } + + ${media.lessThan('medium')` + min-width: 12rem; + height: 6rem; + align-self: center; + `} + + ${media.lessThan('small')` + display: none; + `} +` + +export const Details = styled.div` + ${({ theme }) => css` + display: flex; + gap: ${theme.spacings.xxsmall}; + `} +` + +export const Price = styled.strong` + ${({ theme }) => css` + display: inline-flex; + font-weight: ${theme.font.bold}; + padding: 0 0.4rem; + border-radius: ${theme.border.radius}; + background-color: ${theme.colors.secondary}; + color: ${theme.colors.white}; + `} +` + +export const List = styled.ul` + ${({ theme }) => css` + position: absolute; + width: 100%; + max-height: 54rem; + margin-top: ${theme.spacings.xxsmall}; + background: ${theme.colors.lightBg}; + box-shadow: 0 0.2rem 1.6rem 0.1rem ${theme.colors.black}; + overflow-y: auto; + overflow-x: hidden; + list-style: none; + z-index: ${theme.layers.alwaysOnTop}; + + li:not(:last-child) { + border-bottom: 0.1rem solid ${theme.colors.lightGray}; + } + + ${media.lessThan('large')` + position: fixed; + max-height: calc(100vh - (7rem + 4.2rem + ${theme.spacings.xxsmall} + ${theme.spacings.small})); //Sticky Note + Input height + Menu padding-top + List margin-top + right: 0; + left: 0; + `} + `} +` + +export const ListItem = styled.li` + ${({ theme }) => css` + padding: 0.2rem ${theme.spacings.xxsmall}; + + &:first-child { + padding-top: ${theme.spacings.xxsmall}; + } + &:last-child { + padding-bottom: ${theme.spacings.xxsmall}; + } + `} +` + +export const SearchForm = styled.form` + display: flex; + justify-content: flex-end; +` + +export const Input = styled.input` + ${({ theme }) => css` + color: ${theme.colors.white}; + font-family: ${theme.font.family}; + font-size: ${theme.font.sizes.medium}; + padding: ${theme.spacings.xxsmall} 0; + padding-right: ${theme.spacings.small}; + background: transparent; + border: 0; + outline: none; + + &::placeholder { + color: ${theme.colors.white}; + } + + &::-webkit-search-cancel-button { + -webkit-appearance: none; + } + `} +` + +const inputModifiers = { + open: (theme: DefaultTheme) => css` + background: ${theme.colors.darkGray}; + border-color: ${theme.colors.white}; + width: 100%; + ${Input} { + transition: opacity ${theme.transition.default} 0.2s; + width: 100%; + opacity: 1; + pointer-events: auto; + visibility: visible; + } + `, + close: (theme: DefaultTheme) => css` + background: none; + border-color: transparent; + width: 0; + ${Input} { + transition: opacity ${theme.transition.fast}; + width: 0; + opacity: 0; + pointer-events: none; + visibility: hidden; + } + ` +} + +export const InputWrapper = styled.div` + ${({ theme, isOpen }) => css` + position: relative; + max-height: 4.2rem; + display: flex; + align-items: center; + justify-content: flex-end; + padding: 0 ${theme.spacings.xsmall}; + border-radius: ${theme.border.radius}; + z-index: ${theme.layers.alwaysOnTop}; + border: 0.1rem solid; + transition: width ${theme.transition.default}, + border-color ${theme.transition.fast} 0.1s, + background ${theme.transition.fast} 0.1s; + + ${isOpen && inputModifiers.open(theme)} + ${!isOpen && inputModifiers.close(theme)} + `} +` + +export const Icon = styled.div` + ${({ theme }) => css` + color: ${theme.colors.white}; + cursor: pointer; + transition: color ${theme.transition.default}; + + &:hover { + color: ${darken(0.3, theme.colors.white)}; + } + + & > svg { + width: 2.4rem; + height: 2.4rem; + } + `} +` + +export const Platform = styled.div` + ${({ theme }) => css` + display: flex; + align-items: center; + justify-content: center; + gap: ${theme.spacings.xxsmall}; + `} +` + +export const PlatformIcon = styled.span` + ${({ theme }) => css` + & > svg { + fill: ${theme.colors.lightGray}; + width: 1.4rem; + height: 1.4rem; + } + `} +` + +export const ReleaseYear = styled.time` + ${({ theme }) => css` + font-size: ${theme.font.sizes.xsmall}; + font-weight: ${theme.font.bold}; + color: ${theme.colors.gray}; + margin: 0 ${theme.spacings.xxsmall}; + transition: color ${theme.transition.fast}; + `} +` + +const wrapperModifiers = { + open: () => css` + opacity: 1; + pointer-events: auto; + visibility: visible; + `, + close: () => css` + opacity: 0; + pointer-events: none; + visibility: hidden; + ` +} + +export const Wrapper = styled.div` + ${({ theme, isOpen }) => css` + position: relative; + width: 100%; + + ${Overlay} { + transition: opacity ${theme.transition.default}; + ${isOpen && wrapperModifiers.open()} + ${!isOpen && wrapperModifiers.close()} + } + + ${List} { + transform-origin: top center; + transition: transform 0.3s ease-in, opacity ${theme.transition.default}; + transform: ${isOpen + ? 'translateY(0)' + : `translateY(-${theme.spacings.xsmall})`}; + ${isOpen && wrapperModifiers.open()} + ${!isOpen && wrapperModifiers.close()} + } + `} +` + +export const NoResultsWrapper = styled.li` + ${({ theme }) => css` + height: calc(10rem + (${theme.spacings.xxsmall} * 2)); + display: flex; + align-items: center; + justify-content: center; + + ${media.lessThan('medium')` + img { + display: none; + } + `} + `} +` +export const NoResultsTitle = styled.div` + ${({ theme }) => css` + font-weight: ${theme.font.bold}; + font-size: ${theme.font.sizes.large}; + ${media.lessThan('medium')` + font-size: ${theme.font.sizes.medium}; + `} + `} +` + +export const NoResultsInfo = styled.div` + ${({ theme }) => css` + display: flex; + flex-direction: column; + padding: 0 ${theme.spacings.small}; + `} +` + +export const NoResultsDescription = styled.p` + ${({ theme }) => css` + font-size: ${theme.font.sizes.medium}; + ${media.lessThan('medium')` + font-size: ${theme.font.sizes.small}; + `} + `} +` diff --git a/src/components/Search/test.tsx b/src/components/Search/test.tsx new file mode 100644 index 0000000..a05284f --- /dev/null +++ b/src/components/Search/test.tsx @@ -0,0 +1,89 @@ +import userEvent from '@testing-library/user-event' +import { render, screen, waitFor } from 'utils/test-utils' + +import Search, { SearchBoxProps } from '.' + +jest.mock('next/link', () => ({ + __esModule: true, + default: function Mock({ children }: { children: React.ReactNode }) { + return
{children}
+ } +})) + +jest.mock('utils/meilisearchClient', () => ({ + searchClient: jest.fn().mockResolvedValue({}) +})) + +jest.mock('react-instantsearch-dom', () => ({ + InstantSearch: function Mock({ children }: { children: React.ReactNode }) { + return
{children}
+ }, + Highlight: function Mock({ attribute }: { attribute: string }) { + return {attribute} + }, + Configure: () => null, + connectStateResults: jest + .fn() + .mockImplementation( + (component) => () => + component({ searching: false, searchState: { query: '' } }) + ), + connectSearchBox: jest + .fn() + .mockImplementation( + (component) => (props: SearchBoxProps) => + component({ ...props, currentRefinement: '', refine: jest.fn() }) + ), + connectHits: jest + .fn() + .mockImplementation((component) => () => component({ hits: [] })) +})) + +describe('', () => { + it('should render a hidden input as initial state', () => { + render() + + expect(screen.getByTestId(/mock instantsearch/i)).toBeInTheDocument() + + const input = screen.getByPlaceholderText(/search here…/i) + + expect(input).toHaveStyle({ opacity: '0' }) + + expect(screen.getByLabelText(/open search/i)).toBeInTheDocument() + expect(screen.queryByLabelText(/close search/i)).not.toBeInTheDocument() + }) + + it('should open/close search input when clicking icons', async () => { + render() + userEvent.click(screen.getByLabelText(/open search/i)) + + const input = screen.getByRole('searchbox') + + await waitFor(() => { + expect(input).toHaveFocus() + }) + + expect(input).toHaveStyle({ width: '100%', opacity: '1' }) + + userEvent.click(screen.getByLabelText(/close search/i)) + + expect(input).toHaveStyle({ width: '0' }) + }) + + it('should handle hidding input when clicking on overlay', () => { + render() + + const search = screen.getByRole('search').parentElement! + const overlay = search.nextElementSibling + + userEvent.click(screen.getByLabelText(/open search/i)) + + expect(overlay).toHaveStyle({ opacity: 1 }) + expect(overlay!.getAttribute('aria-hidden')).toBe('false') + + userEvent.click(overlay!) + + expect(overlay).toHaveStyle({ opacity: 0 }) + expect(overlay!.getAttribute('aria-hidden')).toBe('true') + }) +}) diff --git a/src/utils/meilisearchClient.ts b/src/utils/meilisearchClient.ts new file mode 100644 index 0000000..65a2a22 --- /dev/null +++ b/src/utils/meilisearchClient.ts @@ -0,0 +1,9 @@ +import { instantMeiliSearch } from '@meilisearch/instant-meilisearch' + +if (!process.env.NEXT_PUBLIC_MEILISEARCH_SERVER) + throw new Error('Missing env.NEXT_PUBLIC_MEILISEARCH_SERVER') + +const server = process.env.NEXT_PUBLIC_MEILISEARCH_SERVER +const key = process.env.NEXT_PUBLIC_MEILISEARCH_PUBLIC_KEY ?? '' + +export const searchClient = instantMeiliSearch(server, key) diff --git a/yarn.lock b/yarn.lock index 1261349..6c58414 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,110 @@ # yarn lockfile v1 +"@algolia/cache-browser-local-storage@4.10.2": + version "4.10.2" + resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.10.2.tgz#9925c7c0ce94257564b8948b60fc427c4a98124c" + integrity sha512-B3NInwobEAim4J4Y0mgZermoi0DCXdTT/Q+4ehLamqUqxLw8To5zc9izjg7B8JaFSQsqflRdCeRmYEv2gYDY7g== + dependencies: + "@algolia/cache-common" "4.10.2" + +"@algolia/cache-common@4.10.2": + version "4.10.2" + resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.10.2.tgz#0113419518419895118d132bed4115345a865ce3" + integrity sha512-xcGbV0+6gLu2C7XoJdD+Pp6wWjROle6PNDsa6O21vS7fw1a03xb2bEnFdl1U31bs69P1z8IRy3h+8RVBouvhhw== + +"@algolia/cache-in-memory@4.10.2": + version "4.10.2" + resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.10.2.tgz#2d34d4155425b385d19ff197a8943a4b5084c790" + integrity sha512-zPIcxHQEJXy+M35A+v9Y5u5BAQOKR2aFK0kYpAdW/OrgxYcrFHtVCxwIWB/ZhGbkDtzCW8/8tJeddcD5YsHX9Q== + dependencies: + "@algolia/cache-common" "4.10.2" + +"@algolia/client-account@4.10.2": + version "4.10.2" + resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.10.2.tgz#c53d18d4f57ab5343c21e0ed795421964ba0cbb9" + integrity sha512-iuIU+xUtjgR9p4Hpujlr8mePDPSrVIk3peg+RAUhxniLBDaI+OhgHyhP6Lmh9flWk+JfRg91Rhk46xuxMLqwfA== + dependencies: + "@algolia/client-common" "4.10.2" + "@algolia/client-search" "4.10.2" + "@algolia/transporter" "4.10.2" + +"@algolia/client-analytics@4.10.2": + version "4.10.2" + resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.10.2.tgz#93c881cfb9e5df389725d821327fa801f1baa2c6" + integrity sha512-u47J65NHs0fMryDrMeuLMGjXDOKt/muF9WlfbMslT2Cvdd7PZwl9KYnT7xMhnmBB8TDiDMmEQkDykhnCOnwVNw== + dependencies: + "@algolia/client-common" "4.10.2" + "@algolia/client-search" "4.10.2" + "@algolia/requester-common" "4.10.2" + "@algolia/transporter" "4.10.2" + +"@algolia/client-common@4.10.2": + version "4.10.2" + resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.10.2.tgz#a715e8feb2a2b6ea38765f53e8ae6ffc4ed80aba" + integrity sha512-sfgZCv9ha9aHbe3ErAYb1blg2qx4XTLvQqP1jq8asU75rrH9XBTtSzQQO43GlArwhtwCHLgcWquN3WgPlLzkiQ== + dependencies: + "@algolia/requester-common" "4.10.2" + "@algolia/transporter" "4.10.2" + +"@algolia/client-personalization@4.10.2": + version "4.10.2" + resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.10.2.tgz#89d761bcf60ce13b8565c2ae8ab644c3a3d114c8" + integrity sha512-2UhUNo/czfA/keOC3+vFyMnFGV/E1Zkm+ek9Fsk/9miS39UMhx2CmH5vKSIJ7jxLSin7zBaCwKt65phfYty1pg== + dependencies: + "@algolia/client-common" "4.10.2" + "@algolia/requester-common" "4.10.2" + "@algolia/transporter" "4.10.2" + +"@algolia/client-search@4.10.2": + version "4.10.2" + resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.10.2.tgz#ad281b04ec4e6eaff68fb5be330f0bdf965ce011" + integrity sha512-ZdOh6XS6Y9bcekfG4y0VhdoIYfsTounsgXX4Bt3X2RCcmY3uotgaq2EVY58E6q6nvfgBfPHW18+AZCHKTWHAAw== + dependencies: + "@algolia/client-common" "4.10.2" + "@algolia/requester-common" "4.10.2" + "@algolia/transporter" "4.10.2" + +"@algolia/logger-common@4.10.2": + version "4.10.2" + resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.10.2.tgz#f28e966a6b878af2917ed2e1518f46650a6fb8ad" + integrity sha512-UJaU6arzmW+FT5fCv5NIbxNMtEoGcf+UENmZxxu7k7UWPARR2XL4ljJ45Jv14Z5dlz32LXWtR1PRmNfkDMk22Q== + +"@algolia/logger-console@4.10.2": + version "4.10.2" + resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.10.2.tgz#9d3dcbb077242db92f0f0a1795ec95c3bc839599" + integrity sha512-JrCrZ7CGs/TsyNR2AWe9Vdd6rsuxfvfcpqbu+CY7LBUYEnV8GERkf7FnDNaKVNsFJqClILCGh3U8CzQ1G5L+kA== + dependencies: + "@algolia/logger-common" "4.10.2" + +"@algolia/requester-browser-xhr@4.10.2": + version "4.10.2" + resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.10.2.tgz#2286e2f10fff3651f719b8d7d3defc8c032fcce0" + integrity sha512-LveaAp7/oCBotv1aZ4VHz8fCcJA7v/28ayh+Ljlm+hYXsxgs6NAYKz7iBpxGN7q5MV8GM+MThRYNFoT0cHTMxQ== + dependencies: + "@algolia/requester-common" "4.10.2" + +"@algolia/requester-common@4.10.2": + version "4.10.2" + resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.10.2.tgz#8b62f0848454ec5b07bd3599f5fb2b87ec7c4de8" + integrity sha512-3J2W0fAaURLGK0lEGeNb8eWJnQcsu+oIcfJTCIYkYT5T9w21M65kUUyD9QSf/K137qQts3tzGniUR3LxfovlXA== + +"@algolia/requester-node-http@4.10.2": + version "4.10.2" + resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.10.2.tgz#edb691e34e18aacc15107193319e1a712e024649" + integrity sha512-IBqsalCGgn0CrOP1PKRB5rufEOvHlrSQUFEGXZ8mxmE/zU8CLX2LKqdHbEFeNDLFl+l+8HW5BGVDGD2rvG+hSg== + dependencies: + "@algolia/requester-common" "4.10.2" + +"@algolia/transporter@4.10.2": + version "4.10.2" + resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.10.2.tgz#ae0fa7c99b9bf8efa5ac83843558be1074e7c045" + integrity sha512-I3QDRSookQtPSUEnxT2XCShhipCT4beJBpWhteNwMrWQF/SqTsveqSR6bX0G49lDh9MOmYrOlCegteuKuT/tEw== + dependencies: + "@algolia/cache-common" "4.10.2" + "@algolia/logger-common" "4.10.2" + "@algolia/requester-common" "4.10.2" + "@apollo/client@^3.3.20": version "3.3.20" resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.3.20.tgz#8f0935fa991857e9cf2e73c9bd378ad7ec97caf8" @@ -1611,6 +1715,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.1.2", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.6": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" + integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/runtime@^7.10.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.10.3" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.3.tgz#670d002655a7c366540c67f6fd3342cd09500364" @@ -1625,13 +1736,6 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.14.0", "@babel/runtime@^7.14.6": - version "7.14.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" - integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== - dependencies: - regenerator-runtime "^0.13.4" - "@babel/template@^7.10.4", "@babel/template@^7.3.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" @@ -2339,6 +2443,13 @@ resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b" integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA== +"@meilisearch/instant-meilisearch@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@meilisearch/instant-meilisearch/-/instant-meilisearch-0.5.0.tgz#dbad2ff0f7e6ced2cb8ecc176e7b7e82ee34f6b8" + integrity sha512-f2pocX10r8MR+dNr4pN7AoUjjGV30hZqBojGiRZRaVkcSm9+TPJ6fDljo1mp6CPMIi6ja3pC/ALSEpAgPINAWg== + dependencies: + meilisearch "^0.18.1" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -3964,6 +4075,23 @@ dependencies: "@types/react" "*" +"@types/react-instantsearch-core@*": + version "6.10.2" + resolved "https://registry.yarnpkg.com/@types/react-instantsearch-core/-/react-instantsearch-core-6.10.2.tgz#634a887233ce76cc0f37a37f30909fe77a24d73b" + integrity sha512-dG/XHdrPWjVvQTTOg4Q5somVfE6xePOEFJXVeVsRNB+Pj8tzfFR6niFOStf791wGM9BKVBmmy2rCAMhcbfROnw== + dependencies: + "@types/react" "*" + algoliasearch ">=4" + algoliasearch-helper ">=3" + +"@types/react-instantsearch-dom@^6.10.1": + version "6.10.1" + resolved "https://registry.yarnpkg.com/@types/react-instantsearch-dom/-/react-instantsearch-dom-6.10.1.tgz#9a0aa032c18e38c429f0d2a7c432959beb45f5a7" + integrity sha512-LISZFa3NHTB8455e+5q/igQVt11ElnUQcYp1/O+ju46Suck83EjyS4YXacUx+zTJGlagIxJadGMV+V7amJAzTQ== + dependencies: + "@types/react" "*" + "@types/react-instantsearch-core" "*" + "@types/react-slick@^0.23.4": version "0.23.4" resolved "https://registry.yarnpkg.com/@types/react-slick/-/react-slick-0.23.4.tgz#c97e2a9e7e3d1933c68593b8e82752fab1e8ce53" @@ -4623,6 +4751,33 @@ ajv@^8.0.1: require-from-string "^2.0.2" uri-js "^4.2.2" +algoliasearch-helper@>=3, algoliasearch-helper@^3.4.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/algoliasearch-helper/-/algoliasearch-helper-3.5.3.tgz#fbf8b328bc103efdefde59a7d25eaffe85b2490f" + integrity sha512-DtSlOKAJ6TGkQD6u58g6/ABdMmHf3pAj6xVL5hJF+D4z9ldDRf/f5v6puNIxGOlJRwGVvFGyz34beYNqhLDUbQ== + dependencies: + events "^1.1.1" + +algoliasearch@>=4: + version "4.10.2" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.10.2.tgz#23e88c71cb381d5b59430baa5d417186cc8ff683" + integrity sha512-BAYCe97XRfO15irJKBRjBnrp9tSqN0jppklLIXKdtUcXlibcPQtuAeGUP2cPiz6bJd3ISuoYzLFNt4/fQYtLMw== + dependencies: + "@algolia/cache-browser-local-storage" "4.10.2" + "@algolia/cache-common" "4.10.2" + "@algolia/cache-in-memory" "4.10.2" + "@algolia/client-account" "4.10.2" + "@algolia/client-analytics" "4.10.2" + "@algolia/client-common" "4.10.2" + "@algolia/client-personalization" "4.10.2" + "@algolia/client-search" "4.10.2" + "@algolia/logger-common" "4.10.2" + "@algolia/logger-console" "4.10.2" + "@algolia/requester-browser-xhr" "4.10.2" + "@algolia/requester-common" "4.10.2" + "@algolia/requester-node-http" "4.10.2" + "@algolia/transporter" "4.10.2" + anser@1.4.9: version "1.4.9" resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.9.tgz#1f85423a5dcf8da4631a341665ff675b96845760" @@ -6891,6 +7046,13 @@ create-react-context@0.3.0: gud "^1.0.0" warning "^4.0.3" +cross-fetch@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" + integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== + dependencies: + node-fetch "2.6.1" + cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -8263,6 +8425,11 @@ eventemitter2@^6.4.3: resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.4.tgz#aa96e8275c4dbeb017a5d0e03780c65612a1202b" integrity sha512-HLU3NDY6wARrLCEwyGKRBvuWYyvW6mHYv72SJJAH3iJN3a6eVUvkjFkcxah1bcTgGVBBrFdIopBJPhCQFMLyXw== +events@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= + events@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59" @@ -12230,6 +12397,13 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +meilisearch@^0.18.1: + version "0.18.2" + resolved "https://registry.yarnpkg.com/meilisearch/-/meilisearch-0.18.2.tgz#e26d33a4bf163b2d96b14015063ec5979a004793" + integrity sha512-L65u+yNNtIyai3V1XB7BOq+fAt6MzB2KtsqvaW6GNahPtWCZF9SKvWOl6fjhzH3KOHDyT++A65UoYJP4J7vnVA== + dependencies: + cross-fetch "^3.1.4" + memfs@^3.1.2: version "3.2.2" resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.2.2.tgz#5de461389d596e3f23d48bb7c2afb6161f4df40e" @@ -14401,7 +14575,7 @@ react-error-overlay@^6.0.9: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a" integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew== -react-fast-compare@^3.0.1, react-fast-compare@^3.2.0: +react-fast-compare@^3.0.0, react-fast-compare@^3.0.1, react-fast-compare@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== @@ -14426,6 +14600,28 @@ react-inspector@^5.1.0: is-dom "^1.0.0" prop-types "^15.0.0" +react-instantsearch-core@^6.11.2: + version "6.11.2" + resolved "https://registry.yarnpkg.com/react-instantsearch-core/-/react-instantsearch-core-6.11.2.tgz#5d70b04b02a91f2729e664156e6cd5203fae2c26" + integrity sha512-DSvS8XRESmhuBp9q+lhhsGqEKupWJioe95CCelUH0RoB8RtdC2vXRvBMDBEqTf7vG5K7b/dbbObBj5PnMqv5Sw== + dependencies: + "@babel/runtime" "^7.1.2" + algoliasearch-helper "^3.4.3" + prop-types "^15.6.2" + react-fast-compare "^3.0.0" + +react-instantsearch-dom@^6.11.2: + version "6.11.2" + resolved "https://registry.yarnpkg.com/react-instantsearch-dom/-/react-instantsearch-dom-6.11.2.tgz#048e8934dfac472eb59a16fa0125fda0669334c6" + integrity sha512-n6d0E9rreGHIo88OuWHagabAwS9/6NQ/vxRivi0n1Zfqhkaota6QTh83Ay2HIBnio1kRuJgqJhLpO+85R4vzvQ== + dependencies: + "@babel/runtime" "^7.1.2" + algoliasearch-helper "^3.4.3" + classnames "^2.2.5" + prop-types "^15.6.2" + react-fast-compare "^3.0.0" + react-instantsearch-core "^6.11.2" + react-is@17.0.2, react-is@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"