From b0cdee0b97c36e56d98d21dcc339fde6b67c883c Mon Sep 17 00:00:00 2001 From: Kirill Kurentsov Date: Wed, 27 Dec 2023 12:56:51 +0300 Subject: [PATCH 1/4] =?UTF-8?q?refactor:=20=D0=92=D1=8B=D0=B4=D0=B5=D0=BB?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=B8=D0=B7=20Header=20=D0=BE=D1=82=D0=B4?= =?UTF-8?q?=D0=B5=D0=BB=D1=8C=D0=BD=D1=83=D1=8E=20feature=20-=20SearchProd?= =?UTF-8?q?uct=20#171?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchItem/search-item.module.scss | 57 --------------- src/components/header/header.tsx | 4 +- src/components/search/search.tsx | 55 --------------- .../SearchItem/SearchItem.module.scss} | 36 +++++----- .../SearchItem/SearchItem.tsx | 21 +++--- src/features/SearchProduct/index.tsx | 2 + .../ui/SearchProduct.module.scss} | 10 +-- .../SearchProduct/ui/SearchProduct.tsx | 70 +++++++++++++++++++ .../SearchResult/SearchResult.module.scss} | 2 + .../SearchResult/SearchResult.tsx} | 15 ++-- 10 files changed, 115 insertions(+), 157 deletions(-) delete mode 100644 src/components/SearchItem/search-item.module.scss delete mode 100644 src/components/search/search.tsx rename src/{components/SearchItem/searchItem.module.scss => entities/SearchItem/SearchItem.module.scss} (94%) rename src/{components => entities}/SearchItem/SearchItem.tsx (61%) create mode 100644 src/features/SearchProduct/index.tsx rename src/{components/search/search.module.scss => features/SearchProduct/ui/SearchProduct.module.scss} (58%) create mode 100644 src/features/SearchProduct/ui/SearchProduct.tsx rename src/{components/searchResult/searchResult.module.scss => widgets/SearchResult/SearchResult.module.scss} (84%) rename src/{components/searchResult/searchResult.tsx => widgets/SearchResult/SearchResult.tsx} (76%) diff --git a/src/components/SearchItem/search-item.module.scss b/src/components/SearchItem/search-item.module.scss deleted file mode 100644 index 8371e4e2..00000000 --- a/src/components/SearchItem/search-item.module.scss +++ /dev/null @@ -1,57 +0,0 @@ -.link { - padding: 20px 30px; - display: flex; - align-items: center; - min-height: 30px; - color: #343434; - text-decoration: none; - transition: color 0.25s; -} - -.image { - width: 34px; - height: 34px; - margin-right: 20px; - flex: 0 0 auto; -} - -.number { - margin: 0; - font-size: 14px; - line-height: 12px; - font-weight: 400; - color: #bdc2d3; -} - -.price { - display: flex; - align-items: center; - font-size: 16px; - font-weight: 400; - margin: 0; - color: #343434; -} - -.wrapper { - display: flex; - flex-direction: column; - margin-right: 20px; - flex: 1; -} - -.paragraph { - font-size: 15px; - font-weight: 400; - line-height: 1.2; - margin: 0 0 5px; -} - -.price-wrapper { - display: flex; - flex: 0 0 auto; - column-gap: 20px; -} - -.image-link { - visibility: visible; -} diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx index 2eaab324..1656090e 100644 --- a/src/components/header/header.tsx +++ b/src/components/header/header.tsx @@ -1,7 +1,6 @@ import { useMemo } from 'react' import classNames from 'classnames' import Logo from '../logo/Logo' -import Search from '../search/search' import ArrowIcon from '@/assets/icons/arrow.svg' import LightningIcon from '@/assets/images/header/lightning.svg' import ContextMenuElement from '../ContextMenuElement/ContextMenuElement' @@ -15,6 +14,7 @@ import { CatalogLinksId } from '@/shared/config/catalogLinks/catalogLinks' import Link from '@/shared/ui/Link/Link' import IconCategories from '@/assets/icons/IconCategories.svg' import styles from './header.module.scss' +import SearchProduct from '@/features/SearchProduct' function Header() { const aboutUsNode = useMemo( @@ -152,7 +152,7 @@ function Header() {
- +
diff --git a/src/components/search/search.tsx b/src/components/search/search.tsx deleted file mode 100644 index 7e232b57..00000000 --- a/src/components/search/search.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React, { FC, useEffect, useState } from 'react' -import styles from './search.module.scss' -import { searchResponseData } from '@/mockData/searchData' -import { TResultData } from '@/shared/model/types/common' -import SearchResult from '../searchResult/searchResult' -import { Input, InputSize, InputTheme } from '@/shared/ui/Input/Input' - -// @TODO: Перевести форму на Formik + Yup -// https://github.com/Studio-Yandex-Practicum/maxboom_frontend/issues/92 -const Search: FC> = () => { - const [visible, setVisability] = useState(false) - const [resultData, setResultData] = useState({ data: [], success: false }) // TODO: move to redux - - const inputEventHandler = () => { - setResultData(searchResponseData) - } - - const closeContextMenuHandler = (e: Event) => { - const searchResultNode = document.querySelector(styles.result) - const withinBoundaries = e.composedPath().includes(searchResultNode!) - - if (!withinBoundaries && visible) { - setVisability(false) - } - } - - useEffect(() => { - if (resultData.success) { - setVisability(true) - } - }, [resultData]) - - useEffect(() => { - document.addEventListener('click', closeContextMenuHandler) - return () => { - document.removeEventListener('click', closeContextMenuHandler) - } - }, []) - - return ( -
- - - {visible && } - - ) -} - -export default Search diff --git a/src/components/SearchItem/searchItem.module.scss b/src/entities/SearchItem/SearchItem.module.scss similarity index 94% rename from src/components/SearchItem/searchItem.module.scss rename to src/entities/SearchItem/SearchItem.module.scss index 4b9996af..f15b65b8 100644 --- a/src/components/SearchItem/searchItem.module.scss +++ b/src/entities/SearchItem/SearchItem.module.scss @@ -1,4 +1,4 @@ -@use '../../shared/styles/utils/variables' as var; +@use '@/shared/styles/utils/variables' as var; .link { padding: 20px 30px; @@ -17,23 +17,6 @@ flex: 0 0 auto; } -.number { - margin: 0; - font-size: 14px; - line-height: 12px; - font-weight: 400; - color: var.$body-color-light-grey; -} - -.price { - display: flex; - align-items: center; - font-size: 16px; - font-weight: 400; - margin: 0; - color: var.$body-color; -} - .wrapper { display: flex; flex-direction: column; @@ -48,12 +31,29 @@ margin: 0 0 5px; } +.number { + margin: 0; + font-size: 14px; + line-height: 12px; + font-weight: 400; + color: var.$body-color-light-grey; +} + .price-wrapper { display: flex; flex: 0 0 auto; column-gap: 20px; } +.price { + display: flex; + align-items: center; + font-size: 16px; + font-weight: 400; + margin: 0; + color: var.$body-color; +} + .image-link { visibility: visible; } diff --git a/src/components/SearchItem/SearchItem.tsx b/src/entities/SearchItem/SearchItem.tsx similarity index 61% rename from src/components/SearchItem/SearchItem.tsx rename to src/entities/SearchItem/SearchItem.tsx index deab9b8a..cc1e9877 100644 --- a/src/components/SearchItem/SearchItem.tsx +++ b/src/entities/SearchItem/SearchItem.tsx @@ -1,32 +1,33 @@ -import { FC, useMemo, useState } from 'react' +import { type FC, useMemo, useState } from 'react' import ArrowRightIcon from '@/assets/images/searchItem/arrow-right.svg' import { TProduct } from '@/shared/model/types/common' import Link, { TLinkProps } from '@/shared/ui/Link/Link' import styles from './searchItem.module.scss' /** - * @param {string} image - фото товара - * @param {string} name - название товара - * @param {number} number - артикул товара - * @param {string} price - цена товара с валютой + * Компонент элемента из поисковой выдачи для тултипа-подсказки */ const SearchItem: FC = props => { const { image, name, number, price, ...otherProps } = props - const [isVisible, setVisability] = useState(false) + const [isVisible, setVisibility] = useState(false) const handleMouseEnter = () => { - setVisability(true) + setVisibility(true) } const handleMouseLeave = () => { - setVisability(false) + setVisibility(false) } const arrowNode = useMemo(() => , []) return ( - - product + + product

{name}

{number} diff --git a/src/features/SearchProduct/index.tsx b/src/features/SearchProduct/index.tsx new file mode 100644 index 00000000..72a0e547 --- /dev/null +++ b/src/features/SearchProduct/index.tsx @@ -0,0 +1,2 @@ +import SearchProduct from './ui/SearchProduct' +export default SearchProduct diff --git a/src/components/search/search.module.scss b/src/features/SearchProduct/ui/SearchProduct.module.scss similarity index 58% rename from src/components/search/search.module.scss rename to src/features/SearchProduct/ui/SearchProduct.module.scss index 01c914b6..b2279efd 100644 --- a/src/components/search/search.module.scss +++ b/src/features/SearchProduct/ui/SearchProduct.module.scss @@ -1,4 +1,4 @@ -@use '../../shared/styles/utils/variables' as var; +@use '@/shared/styles/utils/variables' as var; .form { width: 100%; @@ -14,14 +14,6 @@ .button { width: 90px; height: 38px; - display: flex; - align-items: center; - justify-content: center; - background-color: var.$theme-primary-color; - border: none; - box-sizing: border-box; - color: var.$white; - border-radius: 5px; margin-right: 5px; } diff --git a/src/features/SearchProduct/ui/SearchProduct.tsx b/src/features/SearchProduct/ui/SearchProduct.tsx new file mode 100644 index 00000000..a333d049 --- /dev/null +++ b/src/features/SearchProduct/ui/SearchProduct.tsx @@ -0,0 +1,70 @@ +import React, { type FC, useEffect, useState } from 'react' +import { searchResponseData } from '@/mockData/searchData' +import { TResultData } from '@/shared/model/types/common' +import SearchResult from '@/widgets/SearchResult/SearchResult' +import { Input, InputSize, InputTheme } from '@/shared/ui/Input/Input' +import { Button, ButtonDesign, ButtonSize, ButtonTheme } from '@/shared/ui/Button/Button' +import styles from './SearchProduct.module.scss' + +// @TODO: Перевести форму на Formik + Yup +// https://github.com/Studio-Yandex-Practicum/maxboom_frontend/issues/92 + +/** + * Компонент формы для поиска катгорий/товаров по базе данных магазина + */ +const SearchProduct: FC> = () => { + const [visible, setVisibility] = useState(false) + const [resultData, setResultData] = useState({ data: [], success: false }) + + // @TODO: Добавить интеграцию с бэком - подсказки в поиске при вводе текста + // https://github.com/Studio-Yandex-Practicum/maxboom_frontend/issues/172 + const inputEventHandler = () => { + setResultData(searchResponseData) + } + + const closeContextMenuHandler = (e: Event) => { + const searchResultNode = document.querySelector(`.${styles.result}`) + const withinBoundaries = e.composedPath().includes(searchResultNode!) + + if (!withinBoundaries && visible) { + setVisibility(false) + } + } + + useEffect(() => { + if (resultData.success) { + setVisibility(true) + } + }, [resultData]) + + useEffect(() => { + document.addEventListener('click', closeContextMenuHandler) + return () => { + document.removeEventListener('click', closeContextMenuHandler) + } + }, []) + + return ( +
+ + {/* @TODO: Добавить onClick-интеграцию с бэком для отправки поискового запроса + https://github.com/Studio-Yandex-Practicum/maxboom_frontend/issues/173 */} + + {visible && } + + ) +} + +export default SearchProduct diff --git a/src/components/searchResult/searchResult.module.scss b/src/widgets/SearchResult/SearchResult.module.scss similarity index 84% rename from src/components/searchResult/searchResult.module.scss rename to src/widgets/SearchResult/SearchResult.module.scss index 61832a32..ef545279 100644 --- a/src/components/searchResult/searchResult.module.scss +++ b/src/widgets/SearchResult/SearchResult.module.scss @@ -1,6 +1,8 @@ @use '@/shared/styles/utils/variables' as var; .result { + // @TODO: Определить ширину в соответствии с шириной формы поиска + // https://github.com/Studio-Yandex-Practicum/maxboom_frontend/issues/174 width: 800px; background-color: var.$white; position: absolute; diff --git a/src/components/searchResult/searchResult.tsx b/src/widgets/SearchResult/SearchResult.tsx similarity index 76% rename from src/components/searchResult/searchResult.tsx rename to src/widgets/SearchResult/SearchResult.tsx index c7e21b43..41944ef1 100644 --- a/src/components/searchResult/searchResult.tsx +++ b/src/widgets/SearchResult/SearchResult.tsx @@ -1,19 +1,20 @@ -import { FC } from 'react' -import SearchItem from '../SearchItem/SearchItem' +import { type FC } from 'react' +import SearchItem from '@/entities/SearchItem/SearchItem' import { SEARCH_CATEGORY, SEARCH_PRODUCT } from '@/shared/constants/constants' import type { TProduct } from '@/shared/model/types/common' import { TCategory } from '@/models/CategoryModel' import SearchIcon from '@/assets/images/search/search-icon.svg' import Link from '@/shared/ui/Link/Link' -import styles from './searchResult.module.scss' import { Routes } from '@/shared/config/routerConfig/routes' +import styles from './searchResult.module.scss' type TProps = { results: Array } /** - * @param {string} results - массив поисковой выдачи + * Компонент тултипа-подсказки при вводе поискового запроса + * @param {Array} props.results - подсказка, получаемая с бэка, при вводе текста в поисковую строку */ const SearchResult: FC = ({ results }) => { return ( @@ -24,7 +25,7 @@ const SearchResult: FC = ({ results }) => { return (
  • - +

    {item.name}

    Категория @@ -39,9 +40,11 @@ const SearchResult: FC = ({ results }) => {
  • ) } + + return null })} -
  • +
  • Показать все товары From d39f5bd9440b9f8e85e107e114859e142bd78d5d Mon Sep 17 00:00:00 2001 From: Kirill Kurentsov Date: Wed, 27 Dec 2023 13:02:02 +0300 Subject: [PATCH 2/4] hotfix: fix CamelCase issue with SCSS module files --- src/entities/SearchItem/SearchItem.tsx | 2 +- src/widgets/SearchResult/SearchResult.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/entities/SearchItem/SearchItem.tsx b/src/entities/SearchItem/SearchItem.tsx index cc1e9877..ca657e43 100644 --- a/src/entities/SearchItem/SearchItem.tsx +++ b/src/entities/SearchItem/SearchItem.tsx @@ -2,7 +2,7 @@ import { type FC, useMemo, useState } from 'react' import ArrowRightIcon from '@/assets/images/searchItem/arrow-right.svg' import { TProduct } from '@/shared/model/types/common' import Link, { TLinkProps } from '@/shared/ui/Link/Link' -import styles from './searchItem.module.scss' +import styles from './SearchItem.module.scss' /** * Компонент элемента из поисковой выдачи для тултипа-подсказки diff --git a/src/widgets/SearchResult/SearchResult.tsx b/src/widgets/SearchResult/SearchResult.tsx index 41944ef1..3d22bdd5 100644 --- a/src/widgets/SearchResult/SearchResult.tsx +++ b/src/widgets/SearchResult/SearchResult.tsx @@ -6,7 +6,7 @@ import { TCategory } from '@/models/CategoryModel' import SearchIcon from '@/assets/images/search/search-icon.svg' import Link from '@/shared/ui/Link/Link' import { Routes } from '@/shared/config/routerConfig/routes' -import styles from './searchResult.module.scss' +import styles from './SearchResult.module.scss' type TProps = { results: Array From 77a5c1b12920eccfcb7972e9de9c3e2089ef50fc Mon Sep 17 00:00:00 2001 From: Kirill Kurentsov Date: Thu, 11 Jan 2024 13:09:22 +0300 Subject: [PATCH 3/4] =?UTF-8?q?refactor:=20=D0=97=D0=B0=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=BB=20querySelector=20=D0=BD=D0=B0=20useRef?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchProduct/ui/SearchProduct.tsx | 18 +++++++++++------- src/widgets/SearchResult/SearchResult.tsx | 13 ++++++++----- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/features/SearchProduct/ui/SearchProduct.tsx b/src/features/SearchProduct/ui/SearchProduct.tsx index a333d049..7932460c 100644 --- a/src/features/SearchProduct/ui/SearchProduct.tsx +++ b/src/features/SearchProduct/ui/SearchProduct.tsx @@ -1,4 +1,4 @@ -import React, { type FC, useEffect, useState } from 'react' +import { useRef, useEffect, useState } from 'react' import { searchResponseData } from '@/mockData/searchData' import { TResultData } from '@/shared/model/types/common' import SearchResult from '@/widgets/SearchResult/SearchResult' @@ -12,9 +12,10 @@ import styles from './SearchProduct.module.scss' /** * Компонент формы для поиска катгорий/товаров по базе данных магазина */ -const SearchProduct: FC> = () => { +const SearchProduct = () => { const [visible, setVisibility] = useState(false) const [resultData, setResultData] = useState({ data: [], success: false }) + const searchResultRef = useRef(null) // @TODO: Добавить интеграцию с бэком - подсказки в поиске при вводе текста // https://github.com/Studio-Yandex-Practicum/maxboom_frontend/issues/172 @@ -23,11 +24,14 @@ const SearchProduct: FC> = () => { } const closeContextMenuHandler = (e: Event) => { - const searchResultNode = document.querySelector(`.${styles.result}`) - const withinBoundaries = e.composedPath().includes(searchResultNode!) + const searchResultNode = searchResultRef.current - if (!withinBoundaries && visible) { - setVisibility(false) + if (searchResultNode) { + const withinBoundaries = e.composedPath().includes(searchResultNode) + + if (!withinBoundaries && visible) { + setVisibility(false) + } } } @@ -62,7 +66,7 @@ const SearchProduct: FC> = () => { className={styles.button}> Найти - {visible && } + {visible && } ) } diff --git a/src/widgets/SearchResult/SearchResult.tsx b/src/widgets/SearchResult/SearchResult.tsx index 3d22bdd5..d5163210 100644 --- a/src/widgets/SearchResult/SearchResult.tsx +++ b/src/widgets/SearchResult/SearchResult.tsx @@ -1,4 +1,4 @@ -import { type FC } from 'react' +import { forwardRef } from 'react' import SearchItem from '@/entities/SearchItem/SearchItem' import { SEARCH_CATEGORY, SEARCH_PRODUCT } from '@/shared/constants/constants' import type { TProduct } from '@/shared/model/types/common' @@ -14,11 +14,12 @@ type TProps = { /** * Компонент тултипа-подсказки при вводе поискового запроса - * @param {Array} props.results - подсказка, получаемая с бэка, при вводе текста в поисковую строку + * @param {Array} props.results - подсказка, получаемая с бэка, при вводе текста в поисковую строку; + * @param {React.MutableRefObject} ref - реф из компонента SearchProduct; */ -const SearchResult: FC = ({ results }) => { +const SearchResult = forwardRef(({ results }, ref) => { return ( -
    +
      {results.map((item, index) => { if (item.type === SEARCH_CATEGORY) { @@ -52,6 +53,8 @@ const SearchResult: FC = ({ results }) => {
    ) -} +}) + +SearchResult.displayName = 'SearchResult' export default SearchResult From 85598e86500fa2355acbfe9f2a2c3623140cfba9 Mon Sep 17 00:00:00 2001 From: Kirill Kurentsov Date: Thu, 11 Jan 2024 13:56:06 +0300 Subject: [PATCH 4/4] documentation: add stories for SearchProduct, SearchResult, SearchItem --- .../SearchItem/SearchItem.stories.tsx | 22 +++++++++++++++++++ .../ui/SearchProduct.stories.tsx | 12 ++++++++++ .../SearchProduct/ui/SearchProduct.tsx | 11 +++++++--- .../SearchResult/SearchResult.stories.tsx | 15 +++++++++++++ 4 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 src/entities/SearchItem/SearchItem.stories.tsx create mode 100644 src/features/SearchProduct/ui/SearchProduct.stories.tsx create mode 100644 src/widgets/SearchResult/SearchResult.stories.tsx diff --git a/src/entities/SearchItem/SearchItem.stories.tsx b/src/entities/SearchItem/SearchItem.stories.tsx new file mode 100644 index 00000000..35f22c83 --- /dev/null +++ b/src/entities/SearchItem/SearchItem.stories.tsx @@ -0,0 +1,22 @@ +import { Meta, Story } from '@storybook/react' +import { searchResponseData } from '@/mockData/searchData' +import SearchItem from './SearchItem' +import { TProduct } from '@/shared/model/types/common' +import { TLinkProps } from '@/shared/ui/Link/Link' + +export default { + title: 'Entities/SearchItem', + component: SearchItem, + parameters: { + layout: 'centered' + } +} as Meta + +type StoryProps = TProduct & TLinkProps + +const Template: Story = args => + +export const Default: Story = Template.bind({}) +Default.args = { + ...(searchResponseData.data[2] as StoryProps) +} diff --git a/src/features/SearchProduct/ui/SearchProduct.stories.tsx b/src/features/SearchProduct/ui/SearchProduct.stories.tsx new file mode 100644 index 00000000..3c6bee42 --- /dev/null +++ b/src/features/SearchProduct/ui/SearchProduct.stories.tsx @@ -0,0 +1,12 @@ +import { Meta, Story } from '@storybook/react' +import SearchProduct from './SearchProduct' + +export default { + title: 'Features/SearchProduct', + component: SearchProduct +} as Meta + +const Template: Story = () => + +export const Default = Template.bind({}) +Default.args = {} diff --git a/src/features/SearchProduct/ui/SearchProduct.tsx b/src/features/SearchProduct/ui/SearchProduct.tsx index 7932460c..2760a33c 100644 --- a/src/features/SearchProduct/ui/SearchProduct.tsx +++ b/src/features/SearchProduct/ui/SearchProduct.tsx @@ -15,12 +15,15 @@ import styles from './SearchProduct.module.scss' const SearchProduct = () => { const [visible, setVisibility] = useState(false) const [resultData, setResultData] = useState({ data: [], success: false }) + const [query, setQuery] = useState('') const searchResultRef = useRef(null) // @TODO: Добавить интеграцию с бэком - подсказки в поиске при вводе текста // https://github.com/Studio-Yandex-Practicum/maxboom_frontend/issues/172 - const inputEventHandler = () => { + const inputEventHandler = (event: React.ChangeEvent) => { + const { value } = event.target setResultData(searchResponseData) + setQuery(value) } const closeContextMenuHandler = (e: Event) => { @@ -36,10 +39,12 @@ const SearchProduct = () => { } useEffect(() => { - if (resultData.success) { + if (resultData.success && query.length > 0) { setVisibility(true) + } else { + setVisibility(false) } - }, [resultData]) + }, [resultData, query]) useEffect(() => { document.addEventListener('click', closeContextMenuHandler) diff --git a/src/widgets/SearchResult/SearchResult.stories.tsx b/src/widgets/SearchResult/SearchResult.stories.tsx new file mode 100644 index 00000000..716aaec1 --- /dev/null +++ b/src/widgets/SearchResult/SearchResult.stories.tsx @@ -0,0 +1,15 @@ +import { Meta, Story } from '@storybook/react' +import SearchResult from './SearchResult' +import { searchResponseData } from '@/mockData/searchData' + +export default { + title: 'Widgets/SearchResult', + component: SearchResult +} as Meta + +const Template: Story = () => { + return +} + +export const Default = Template.bind({}) +Default.args = {}