Skip to content

Commit

Permalink
Merge pull request #398 from Studio-Yandex-Practicum/enhancement-340-…
Browse files Browse the repository at this point in the history
…sidebar

#340-sidebar
  • Loading branch information
MargaritaShumilina authored May 30, 2024
2 parents b3111e7 + 5585026 commit 59af645
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 64 deletions.
13 changes: 10 additions & 3 deletions src/features/CategoryItem/CategoryItem.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
.category-list__item {
display: flex;
align-items: center;
min-height: 45px;
max-width: 270px;
background-color: var.$body-bg;
padding: 10px 40px 10px 14px;
padding: 5px 40px 5px 14px;
margin-bottom: 5px;
border-radius: 5px;
color: var.$body-color;
Expand All @@ -17,7 +15,16 @@
color: var.$theme-primary-color;
}


.category-list__link {
text-decoration: none;
color: inherit;
gap: 5px
}


.category-list__link_active {
text-decoration: none;
gap: 5px;
color: var.$theme-primary-color;
}
22 changes: 18 additions & 4 deletions src/features/CategoryItem/CategoryItem.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,23 @@ export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
name: 'Тестовое имя категории',
slug: '/test',
count: 999,
id: 999
item: {
id: 3434,
name: 'Тестовая категория',
slug: '/test',
total_count: 1,
branches: [
{
id: 3,
name: 'Тестовая подкатегория',
slug: '/test',
products_count: 5,
branches: []
}
],
root: false,
is_prohibited: false,
is_visible_on_main: false
}
}
}
68 changes: 46 additions & 22 deletions src/features/CategoryItem/CategoryItem.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,63 @@
import type { FC } from 'react'
import { type FC } from 'react'
import { useDispatch } from 'react-redux'
import { NavLink } from 'react-router-dom'

import { setCategoryId } from '@/entities/Category/slice/categoryIdSlice'
import { setCategorySlug } from '@/entities/Category/slice/categorySlugSlice'
import styles from '@/features/CategoryItem/CategoryItem.module.scss'
import { useHandleActiveItem } from '@/features/CategoryItem/models/useHandleActiveItem'
import SideBar from '@/features/SideBar'
import { Routes } from '@/shared/config/routerConfig/routes'
import Link from '@/shared/ui/Link/Link'
import type { MainCategoryInfo } from '@/widgets/CategoryList/types/types'

type Props = {
name: string
slug: string
count: number
id: number
item: MainCategoryInfo
}

/**
* Компонент единицы категории в списке категорий бокового меню
* @param {string} name - название категории;
* @param {string} slug - URL для страницы категориии;
* @param {number} count - количество товаров в категории;
* @param {number} id - id категории;
* @param {MainCategoryInfo} item - главная категория с ветками;
*/
export const CategoryItem: FC<Props> = ({ name, slug, count, id }) => {
export const CategoryItem: FC<Props> = ({ item }) => {
const dispatch = useDispatch()

const { itemActive, branch } = useHandleActiveItem(item)

return (
<li className={styles['category-list__item']}>
<Link
to={`${Routes.CATEGORIES}/${slug}`}
className={styles['category-list__link']}
onClick={() => {
dispatch(setCategoryId(id))
dispatch(setCategorySlug(slug))
}}>
{name} ({count})
</Link>
</li>
<SideBar
key={item.id}
isVisible={true}
title={`${item.name} (${item.total_count})`}
onClick={() => {
dispatch(setCategoryId(item.id))
dispatch(setCategorySlug(item.slug))
}}
categorySidebar={{
to: `${Routes.CATEGORIES}/${item.slug}`,
activeElement: itemActive,
branch: branch,
itemName: item.name
}}>
{item.branches && (
<ul role="list">
{item.branches.map(el => (
<li key={el.id} className={styles['category-list__item']}>
<NavLink
role="link"
to={`${Routes.CATEGORIES}/${el.slug}`}
className={({ isActive }) =>
isActive ? styles['category-list__link_active'] : styles['category-list__link']
}
onClick={() => {
dispatch(setCategoryId(el.id))
dispatch(setCategorySlug(el.slug))
}}>
{el.name} ({el.products_count})
</NavLink>
</li>
))}
</ul>
)}
</SideBar>
)
}
31 changes: 31 additions & 0 deletions src/features/CategoryItem/models/useHandleActiveItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
import { useParams } from 'react-router-dom'

import { getCategorySelector } from '@/widgets/CategoryList/selectors/selectors'
import type { BranchesData, MainCategoryInfo } from '@/widgets/CategoryList/types/types'

/**
* Функция сравнения и вывода активной категории и подкатегории
* @param {MainCategoryInfo} item - значение, которое нужно заменить;
*/
export const useHandleActiveItem = (item: MainCategoryInfo) => {
const { slug } = useParams()
const getMainCategories = useSelector(getCategorySelector)

const [itemActive, setItemActive] = useState<MainCategoryInfo | BranchesData>()
const [branch, setBranch] = useState<MainCategoryInfo | BranchesData>()

const activeMainCategory = getMainCategories.find(el => el.slug === slug)
const activeBranchCategory = item.branches.find(el => el.slug === slug)

useEffect(() => {
if (activeMainCategory) {
setItemActive(activeMainCategory)
} else if (activeBranchCategory) {
setItemActive(activeBranchCategory)
setBranch(getMainCategories.find(el => el.branches.find(el => el.name === itemActive?.name)))
}
}, [itemActive])
return { itemActive, branch }
}
8 changes: 8 additions & 0 deletions src/features/SideBar/model/types/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { BranchesData, MainCategoryInfo } from '@/widgets/CategoryList/types/types'

export type TCategorySidebar = {
to?: string | undefined
activeElement?: MainCategoryInfo | BranchesData
branch?: MainCategoryInfo | BranchesData
itemName?: string
}
51 changes: 39 additions & 12 deletions src/features/SideBar/ui/SideBar.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { KeyboardEvent, KeyboardEventHandler, ReactElement, useState, type FC } from 'react'
import { KeyboardEvent, KeyboardEventHandler, ReactElement, useState, type FC, useEffect } from 'react'

import ArrowIcon from '@/assets/images/sideBarMenu/IconArrowDown.svg'
import { TCategorySidebar } from '@/features/SideBar/model/types/types'
import Link from '@/shared/ui/Link/Link'
import Paragraph from '@/shared/ui/Paragraph/Paragraph'

import styles from './SideBar.module.scss'

export interface ISideBar {
interface ISideBar {
title?: string
isVisible?: boolean
onClick?: () => void
onKeyUp?: KeyboardEventHandler<HTMLLIElement>
children?: ReactElement | JSX.Element | JSX.Element[]
categorySidebar?: TCategorySidebar | undefined
}

/**
Expand All @@ -20,11 +23,30 @@ export interface ISideBar {
* @param {function} onClick - функция выхода из профиля handleLogOut;
* @param {function} onKeyUp - функция выхода из профиля handleLogOut при нажатии клавиши Enter;
* @param {JSX.Element} children - контент;
* Далее, для сайдбара на странице категори;
* @param {
* to: string,
* activeElement: MainCategoryInfo | BranchesData,
* branch: MainCategoryInfo | BranchesData,
* itemName: string
* } categorySidebar - объект с параметрами для сайдбара на странице категори;
*/

const SideBar: FC<ISideBar> = ({ title, isVisible, onClick, onKeyUp, children }) => {
const SideBar: FC<ISideBar> = ({ title, isVisible, onClick, onKeyUp, children, categorySidebar }) => {
const [isActive, setIsActive] = useState(false)

useEffect(() => {
if (
categorySidebar?.activeElement?.name === categorySidebar?.itemName &&
categorySidebar?.activeElement !== undefined
) {
setIsActive(true)
} else if (categorySidebar?.branch) {
setIsActive(true)
} else {
setIsActive(false)
}
}, [categorySidebar?.activeElement?.slug, categorySidebar?.branch])

const handleClick = () => {
setIsActive(!isActive)
}
Expand All @@ -38,17 +60,22 @@ const SideBar: FC<ISideBar> = ({ title, isVisible, onClick, onKeyUp, children })
}

return (
<li
tabIndex={0}
role="button"
onKeyUp={onKeyUp}
onKeyDown={handleKeyDown}
onClick={onClick}
className={styles.sideBar}>
<li tabIndex={0} role="button" onKeyUp={onKeyUp} onKeyDown={handleKeyDown} className={styles.sideBar}>
<div onClick={handleClick} className={styles.sideBar__header}>
<Paragraph className={styles.sideBar__headerText}>{title}</Paragraph>
{categorySidebar?.to ? (
<Link to={categorySidebar?.to}>
<Paragraph className={styles.sideBar__headerText} onClick={onClick}>
{title}
</Paragraph>
</Link>
) : (
<Paragraph className={styles.sideBar__headerText} onClick={onClick}>
{title}
</Paragraph>
)}
{isVisible && (
<ArrowIcon
onClick={handleClick}
className={`${styles.sideBar__headerArrow} ${isActive && styles.sideBar__headerArrow_active}`}
/>
)}
Expand Down
2 changes: 1 addition & 1 deletion src/shared/libs/hooks/useReplaceValueFromLocation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useLocation } from 'react-router'
/**
* Функция извлечения нужных параметров из URL с помощью useLocation
* @param {string} varN - значение, которое нужно заменить;
* @param {string} changeN - на чт нужно заменить;
* @param {string} changeN - на что нужно заменить;
*/
export const useReplaceValueFromLocation = (var1: string, change1: string, var2: string, change2: string) => {
const location = useLocation()
Expand Down
2 changes: 2 additions & 0 deletions src/widgets/CategoryList/CategoryList.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ https://github.com/Studio-Yandex-Practicum/maxboom_frontend/issues/61 */
.category-list__items {
max-height: calc(100% - 50px);
overflow-y: auto;
gap: 10px;
display: grid;
}
24 changes: 2 additions & 22 deletions src/widgets/CategoryList/CategoryList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { getLoading } from '@/pages/ProductsPage/selectors/selectors'
import { NUMBER_OF_CATEGORY_LINES } from '@/shared/constants/constants'
import { useAppDispatch } from '@/shared/libs/hooks/store'
import Heading, { HeadingType } from '@/shared/ui/Heading/Heading'
import { getCategoryBranchesSelector, getCategorySelector } from '@/widgets/CategoryList/selectors/selectors'
import { getCategorySelector } from '@/widgets/CategoryList/selectors/selectors'
import { getCategoryBranches } from '@/widgets/CategoryList/services/getCategoryBranches'
import { getCategories } from '@/widgets/CategoryList/services/getCatergories'

Expand All @@ -21,7 +21,6 @@ import styles from './CategoryList.module.scss'
*/
export const CategoryList: FC = () => {
const dispatch = useAppDispatch()
const categoryBranches = useSelector(getCategoryBranchesSelector)
const getMainCategories = useSelector(getCategorySelector)
const categorySlug = useSelector(selectCategorySlug)

Expand All @@ -44,26 +43,7 @@ export const CategoryList: FC = () => {
? Array(NUMBER_OF_CATEGORY_LINES)
.fill(0)
.map((_, i) => <CategoryItemSkeleton key={i} />)
: categoryBranches.branches?.length > 0
? categoryBranches.branches.map(item => (
<CategoryItem
key={item.id}
id={item.id}
name={item.name}
slug={item.slug}
count={item.products_count}
/>
))
: getMainCategories.map(item => (
<CategoryItem
key={item.id}
id={item.id}
name={item.name}
slug={item.slug}
// Поля count в списке категорий верхнего уровня на бэке нет, если мы его оставляем, то нужно будет запросить вывод количества товаров
count={0}
/>
))}
: getMainCategories.map(item => <CategoryItem item={item} key={item.id} />)}
</ul>
</div>
)
Expand Down
1 change: 1 addition & 0 deletions src/widgets/CategoryList/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface IMainCategorySchema {
}

export interface MainCategoryInfo {
total_count?: number
id: number
name: string
slug: string
Expand Down

0 comments on commit 59af645

Please sign in to comment.