Skip to content

Commit

Permalink
Merge pull request #329 from Studio-Yandex-Practicum/enhancement-305-…
Browse files Browse the repository at this point in the history
…skeleton

#305-skeletons
  • Loading branch information
MargaritaShumilina authored Apr 22, 2024
2 parents dc21d21 + 698318b commit 3998766
Show file tree
Hide file tree
Showing 19 changed files with 339 additions and 13 deletions.
15 changes: 15 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"react-dom": "18.2.0",
"react-input-mask": "^2.0.4",
"react-leaflet": "^4.2.1",
"react-loading-skeleton": "^3.4.0",
"react-redux": "8.1.2",
"react-router": "6.15.0",
"react-router-dom": "6.15.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@use '../../../shared/styles/utils/variables' as var;

.sk-page-controls {
width: 100%;
display: flex;
justify-content: space-between;

&__dropdowns {
display: flex;
gap: 12px;
}

&__dropdown {
width: 150px;
height: 36px;
}

&__cards-controls {
display: flex;
gap: 10px;
}

&__cards-control {
width: 36px;
height: 36px;
border-radius: 6px;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Meta, StoryObj } from '@storybook/react'
import { type FC } from 'react'

import { PageControlsSkeletons } from '@/components/PageControls/PageControlsSkeletons/PageControlsSkeletons'

const StorybookWrapper: FC = () => {
return (
<div>
<PageControlsSkeletons />
</div>
)
}

const meta = {
title: 'shared/PageControlsSkeletons',
component: StorybookWrapper,
tags: ['autodocs']
} satisfies Meta<typeof StorybookWrapper>

export default meta
type Story = StoryObj<typeof meta>

export const Default: Story = {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { type FC } from 'react'
import Skeleton from 'react-loading-skeleton'
import 'react-loading-skeleton/dist/skeleton.css'

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

export const PageControlsSkeletons: FC = () => {
return (
<div className={styles['sk-page-controls']}>
<div className={styles['sk-page-controls__dropdowns']}>
<Skeleton className={styles['sk-page-controls__dropdown']} inline={true} />
<Skeleton className={styles['sk-page-controls__dropdown']} inline={true} />
</div>
<ul className={styles['sk-page-controls__cards-controls']}>
<Skeleton className={styles['sk-page-controls__cards-control']} inline={true} />
<Skeleton className={styles['sk-page-controls__cards-control']} inline={true} />
<Skeleton className={styles['sk-page-controls__cards-control']} inline={true} />
</ul>
</div>
)
}
8 changes: 6 additions & 2 deletions src/components/PageDescription/PageDescription.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { FC } from 'react'
import { type FC } from 'react'
import 'react-loading-skeleton/dist/skeleton.css'
import { useParams } from 'react-router-dom'

import { getNoun } from '@/shared/libs/helpers/getNoun'
import Breadcrumbs from '@/shared/ui/Breadcrumbs/Breadcrumbs'
Expand All @@ -17,9 +19,11 @@ type Props = {
* @param {string} heading - наименование-заголовок;
*/
export const PageDescription: FC<Props> = ({ count, heading }) => {
const { slug } = useParams<string>()

const links = [
{ heading: 'Главная', href: '/' },
{ heading: heading, href: '/categories/' + heading }
{ heading: heading, href: '/categories/' + slug }
]

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@use '../../../shared/styles/utils/variables' as var;

.sk-content {
width: 330px;
height:30px
}

.sk-content__description {
display: flex;
flex-direction: column;
align-self: flex-start;
gap: 10px;
}

.sk-content__breadcrumbs {
height: 16px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Meta, StoryObj } from '@storybook/react'
import { type FC } from 'react'

import { PageDescriptionSkeleton } from '@/components/PageDescription/PageDescriptionSkeleton/PageDescriptionSkeleton'

const StorybookWrapper: FC = () => {
return (
<div>
<PageDescriptionSkeleton />
</div>
)
}

const meta = {
title: 'shared/PageDescriptionSkeleton',
component: StorybookWrapper,
tags: ['autodocs']
} satisfies Meta<typeof StorybookWrapper>

export default meta
type Story = StoryObj<typeof meta>

export const Default: Story = {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { type FC } from 'react'
import Skeleton from 'react-loading-skeleton'

import styles from '@/components/PageDescription/PageDescriptionSkeleton/PageDescriptionSkeleton.module.scss'

export const PageDescriptionSkeleton: FC = () => {
return (
<div className={styles['sk-content__description']}>
<div>
<Skeleton className={styles['sk-content']} inline={true} />
</div>
<Skeleton className={styles['sk-content__breadcrumbs']} inline={true} />
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.sk-category-list__item {
display: flex;
min-height: 45px;
border-radius: 5px;
margin-bottom: 5px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Meta, StoryObj } from '@storybook/react'
import { type FC } from 'react'

import { CategoryItemSkeleton } from '@/features/CategoryItem/CategoryItemSkeleton/CategoryItemSkeleton'

const StorybookWrapper: FC = () => {
return (
<div>
<CategoryItemSkeleton />
</div>
)
}

const meta = {
title: 'features/CategoryItemSkeleton',
component: StorybookWrapper,
tags: ['autodocs']
} satisfies Meta<typeof StorybookWrapper>

export default meta
type Story = StoryObj<typeof meta>

export const Default: Story = {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { type FC } from 'react'
import Skeleton from 'react-loading-skeleton'

import styles from '@/features/CategoryItem/CategoryItemSkeleton/CategoryItemSkeleton.module.scss'

export const CategoryItemSkeleton: FC = () => {
return <Skeleton className={styles['sk-category-list__item']} inline={true} />
}
26 changes: 22 additions & 4 deletions src/pages/ProductsPage/ProductsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@ import { useLocation } from 'react-router'
import { selectFilterProducts, selectFilterQuantity } from '@/components/Dropdown/selectors/selectors'
import { setFilterProducts, setProductQuantityFilter } from '@/components/Dropdown/slice/filtersSlice'
import { PageControls } from '@/components/PageControls/PageControls'
import { PageControlsSkeletons } from '@/components/PageControls/PageControlsSkeletons/PageControlsSkeletons'
import { PageDescription } from '@/components/PageDescription/PageDescription'
import { PageDescriptionSkeleton } from '@/components/PageDescription/PageDescriptionSkeleton/PageDescriptionSkeleton'
import { Pagination } from '@/components/Pagination/Pagination'
import WrapperForMainContent from '@/components/WrapperForMainContent/WrapperForMainContent'
import { selectCategorySlug } from '@/entities/Category/selectors/categorySelectors'
import { TOTAL_PAGES } from '@/mockData/productsPageOptions'
import { getProductsOfCategorySelector } from '@/pages/ProductsPage/selectors/selectors'
import { getLoading, getProductsOfCategorySelector } from '@/pages/ProductsPage/selectors/selectors'
import { getProducts } from '@/pages/ProductsPage/services/getProducts'
import { ITEMS_PER_PAGE_OPTION, SORT_OPTION } from '@/shared/constants/constants'
import { ITEMS_PER_PAGE_OPTION, NUMBER_OF_PRODUCTS, SORT_OPTION } from '@/shared/constants/constants'
import { useAppDispatch } from '@/shared/libs/hooks/store'
import { ECardView } from '@/shared/model/types/common'
import { CategoryList } from '@/widgets/CategoryList/CategoryList'
import { ProductSkeleton } from '@/widgets/ProductItem/ProductSkeleton/ProductSkeleton'
import { ProductsList } from '@/widgets/ProductsList/ProductsList'

import styles from './ProductsPage.module.scss'
Expand Down Expand Up @@ -46,6 +49,8 @@ export const ProductsPage = () => {
const selectQuantityFilter = useSelector(selectFilterQuantity)
const filterQuantity = selectQuantityFilter ? `&limit=${selectQuantityFilter.value}` : ''

const isLoading = useSelector(getLoading)

const handleSortChange: React.ChangeEventHandler<HTMLSelectElement> = event => {
const selectedOption = event.target.value
const setCategoryFilters = SORT_OPTION.find(item => item.name === selectedOption)
Expand Down Expand Up @@ -83,12 +88,25 @@ export const ProductsPage = () => {
return (
<>
<WrapperForMainContent>
<PageDescription count={categoriesProducts.count} heading={categoriesProducts.category_name} />
{isLoading ? (
<PageDescriptionSkeleton />
) : (
<PageDescription count={categoriesProducts.count} heading={categoriesProducts.category_name} />
)}
<div className={styles['content-grid']}>
<CategoryList />
<div className={styles['content-main']}>
<section className={styles['content-products']}>
{categoriesProducts.results.length > 0 ? (
{isLoading ? (
<>
<PageControlsSkeletons />
{Array(NUMBER_OF_PRODUCTS)
.fill(0)
.map((_, i) => (
<ProductSkeleton key={i} />
))}
</>
) : categoriesProducts.results.length > 0 ? (
<>
<PageControls
cardView={cardView}
Expand Down
5 changes: 5 additions & 0 deletions src/pages/ProductsPage/selectors/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { StateSchema } from '@/app/providers/StoreProvider'
import { RootState } from '@/app/providers/StoreProvider/config/store'

export const getProductsOfCategorySelector = (state: StateSchema) => {
return state.categoryProduct.productsData
}

export const getLoading = (state: RootState) => {
return state.categoryProduct.isLoading
}
4 changes: 4 additions & 0 deletions src/shared/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,7 @@ export const ITEMS_PER_PAGE_OPTION = [
{ name: '75', value: '75' },
{ name: '100', value: '100' }
]

//For Skeleton
export const NUMBER_OF_CATEGORY_LINES = 15
export const NUMBER_OF_PRODUCTS = 15
19 changes: 12 additions & 7 deletions src/widgets/CategoryList/CategoryList.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { FC, useEffect } from 'react'
import { type FC, useEffect } from 'react'
import { useSelector } from 'react-redux'
import { useParams } from 'react-router-dom'

import { selectCategorySlug } from '@/entities/Category/selectors/categorySelectors'
import { CategoryItem } from '@/features/CategoryItem/CategoryItem'
import { CategoryItemSkeleton } from '@/features/CategoryItem/CategoryItemSkeleton/CategoryItemSkeleton'
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'
Expand All @@ -24,22 +27,24 @@ export const CategoryList: FC = () => {

const { slug } = useParams()

useEffect(() => {
dispatch(getCategoryBranches(slug))
}, [])
const isLoading = useSelector(getLoading)

useEffect(() => {
dispatch(getCategoryBranches(categorySlug))
dispatch(getCategoryBranches(slug))
dispatch(getCategories())
}, [categorySlug])
}, [categorySlug, slug])

return (
<div className={styles['category-list']}>
<Heading type={HeadingType.NORMAL} className={styles['category-list__title']}>
Категории
</Heading>
<ul className={styles['category-list__items']}>
{categoryBranches.branches?.length > 0
{isLoading
? Array(NUMBER_OF_CATEGORY_LINES)
.fill(0)
.map((_, i) => <CategoryItemSkeleton key={i} />)
: categoryBranches.branches?.length > 0
? categoryBranches.branches.map(item => (
<CategoryItem
key={item.id}
Expand Down
Loading

0 comments on commit 3998766

Please sign in to comment.