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/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/components/SearchItem/SearchItem.tsx b/src/entities/SearchItem/SearchItem.tsx similarity index 58% rename from src/components/SearchItem/SearchItem.tsx rename to src/entities/SearchItem/SearchItem.tsx index deab9b8a..ca657e43 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' +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.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 new file mode 100644 index 00000000..2760a33c --- /dev/null +++ b/src/features/SearchProduct/ui/SearchProduct.tsx @@ -0,0 +1,79 @@ +import { useRef, 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 = () => { + 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 = (event: React.ChangeEvent) => { + const { value } = event.target + setResultData(searchResponseData) + setQuery(value) + } + + const closeContextMenuHandler = (e: Event) => { + const searchResultNode = searchResultRef.current + + if (searchResultNode) { + const withinBoundaries = e.composedPath().includes(searchResultNode) + + if (!withinBoundaries && visible) { + setVisibility(false) + } + } + } + + useEffect(() => { + if (resultData.success && query.length > 0) { + setVisibility(true) + } else { + setVisibility(false) + } + }, [resultData, query]) + + 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/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 = {} diff --git a/src/components/searchResult/searchResult.tsx b/src/widgets/SearchResult/SearchResult.tsx similarity index 63% rename from src/components/searchResult/searchResult.tsx rename to src/widgets/SearchResult/SearchResult.tsx index c7e21b43..d5163210 100644 --- a/src/components/searchResult/searchResult.tsx +++ b/src/widgets/SearchResult/SearchResult.tsx @@ -1,30 +1,32 @@ -import { FC } from 'react' -import SearchItem from '../SearchItem/SearchItem' +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' 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 - подсказка, получаемая с бэка, при вводе текста в поисковую строку; + * @param {React.MutableRefObject} ref - реф из компонента SearchProduct; */ -const SearchResult: FC = ({ results }) => { +const SearchResult = forwardRef(({ results }, ref) => { return ( -
+
    {results.map((item, index) => { if (item.type === SEARCH_CATEGORY) { return (
  • - +

    {item.name}

    Категория @@ -39,9 +41,11 @@ const SearchResult: FC = ({ results }) => {
  • ) } + + return null })} -
  • +
  • Показать все товары @@ -49,6 +53,8 @@ const SearchResult: FC = ({ results }) => {
) -} +}) + +SearchResult.displayName = 'SearchResult' export default SearchResult