Skip to content

Commit

Permalink
Display active tab in URL
Browse files Browse the repository at this point in the history
  • Loading branch information
YaroslavChuiko committed Oct 23, 2024
1 parent 8f0f4e4 commit 5faab5d
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 35 deletions.
37 changes: 37 additions & 0 deletions src/components/tab-filter-list/TabFilterList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Box, SxProps } from '@mui/material'
import Tab from '~/components/tab/Tab'

import { useTranslation } from 'react-i18next'

import { TabType } from '~/types'

type TabFilterListProps = {
tabsData: Record<string, TabType<string>>
activeTab: string
onClick: (tabName: string, tabValue: string) => void
sx?: SxProps
}

const TabFilterList = ({
tabsData,
activeTab,
onClick,
sx
}: TabFilterListProps) => {
const { t } = useTranslation()

return (
<Box sx={sx}>
{Object.entries(tabsData).map(([tabName, tab]) => (
<Tab
activeTab={activeTab === tabName}
key={tab.label}
onClick={() => onClick(tabName, tab.value)}
>
{t(tab.label)}
</Tab>
))}
</Box>
)
}
export default TabFilterList
45 changes: 27 additions & 18 deletions src/pages/my-cooperations/MyCooperations.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useLayoutEffect, useState } from 'react'
import { useCallback, useEffect, useLayoutEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Link } from 'react-router-dom'
import { Link, useSearchParams } from 'react-router-dom'
import Box from '@mui/material/Box'
import Typography from '@mui/material/Typography'

Expand All @@ -11,7 +11,6 @@ import useAxios from '~/hooks/use-axios'
import useSort from '~/hooks/table/use-sort'
import useBreakpoints from '~/hooks/use-breakpoints'
import useFilter from '~/hooks/table/use-filter'
import Tab from '~/components/tab/Tab'
import Loader from '~/components/loader/Loader'
import AppButton from '~/components/app-button/AppButton'
import AppPagination from '~/components/app-pagination/AppPagination'
Expand All @@ -30,8 +29,11 @@ import {
tabsInfo
} from '~/pages/my-cooperations/MyCooperations.constants'
import { itemsLoadLimit } from '~/constants'
import { CardsViewEnum, TabType } from '~/types'
import { CardsViewEnum } from '~/types'
import { styles } from '~/pages/my-cooperations/MyCooperations.styles'
import TabFilterList from '~/components/tab-filter-list/TabFilterList'

type TabKey = keyof typeof tabsInfo

const MyCooperations = () => {
const [itemsView, setItemsView] = useState<CardsViewEnum>(
Expand All @@ -40,8 +42,13 @@ const MyCooperations = () => {
const { t } = useTranslation()
const dispatch = useAppDispatch()
const breakpoints = useBreakpoints()
const [searchParams, setSearchParams] = useSearchParams()
const activeTab = searchParams.get('tab')
const filterOptions = useFilter({
initialFilters
initialFilters: {
...initialFilters,
status: activeTab ? tabsInfo[activeTab as TabKey].value : ''
}
})
const sortOptions = useSort({
initialSort
Expand All @@ -50,6 +57,12 @@ const MyCooperations = () => {
const { sort, resetSort } = sortOptions
const { filters, clearFilters, setFilterByKey } = filterOptions

useEffect(() => {
if (!activeTab) {
setSearchParams({ tab: Object.keys(tabsInfo)[0] })
}
}, [activeTab, setSearchParams])

const itemsPerPage = getScreenBasedLimit(breakpoints, itemsLoadLimit)
const showTable = !breakpoints.isMobile && itemsView === CardsViewEnum.Inline

Expand All @@ -74,22 +87,13 @@ const MyCooperations = () => {
defaultResponse
})

const handleTabClick = (tab: TabType<string>) => {
const handleTabClick = (tabName: string, tabValue: string) => {
clearFilters()
resetSort()
setFilterByKey('status')(tab.value)
setFilterByKey('status')(tabValue)
setSearchParams({ tab: tabName })
}

const tabs = Object.values(tabsInfo).map((tab) => (
<Tab
activeTab={filters.status === tab.value}
key={tab.label}
onClick={() => handleTabClick(tab)}
>
{t(tab.label)}
</Tab>
))

const sortFields = sortTranslationKeys.map(({ title, value }) => ({
title: t(title),
value
Expand All @@ -107,7 +111,12 @@ const MyCooperations = () => {
{t('button.viewMyOffers')}
</AppButton>
</Box>
<Box sx={styles.tabs}>{tabs}</Box>
<TabFilterList
activeTab={activeTab ?? ''}
onClick={handleTabClick}
sx={styles.tabs}
tabsData={tabsInfo}
/>
<CooperationOfferToolbar
filterOptions={filterOptions}
onChangeView={setItemsView}
Expand Down
42 changes: 25 additions & 17 deletions src/pages/my-offers/MyOffers.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useLayoutEffect, useState } from 'react'
import { useCallback, useEffect, useLayoutEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'

import Box from '@mui/material/Box'
Expand All @@ -9,10 +9,11 @@ import AppDrawer from '~/components/app-drawer/AppDrawer'
import AppPagination from '~/components/app-pagination/AppPagination'
import Loader from '~/components/loader/Loader'
import PageWrapper from '~/components/page-wrapper/PageWrapper'
import Tab from '~/components/tab/Tab'
import TabFilterList from '~/components/tab-filter-list/TabFilterList'
import CooperationOfferToolbar from '~/containers/my-cooperations/cooperation-offer-toolbar/CooperationOfferToolbar'
import MyOffersContainer from '~/containers/my-offers/my-offers-container/MyOffersContainer'
import CreateOffer from '~/containers/offer-page/create-offer/CreateOffer'
import { useSearchParams } from 'react-router-dom'
import useFilter from '~/hooks/table/use-filter'
import usePagination from '~/hooks/table/use-pagination'
import useSort from '~/hooks/table/use-sort'
Expand All @@ -32,18 +33,25 @@ import {
sortTranslationKeys,
tabsInfo
} from '~/pages/my-offers/MyOffers.constants'
import { CardsViewEnum, TabType } from '~/types'
import { CardsViewEnum } from '~/types'
import { setPageLoad } from '~/redux/reducer'

type TabName = keyof typeof tabsInfo

const MyOffers = () => {
const [itemsView, setItemsView] = useState<CardsViewEnum>(
CardsViewEnum.Inline
)
const { t } = useTranslation()
const dispatch = useAppDispatch()
const breakpoints = useBreakpoints()
const [searchParams, setSearchParams] = useSearchParams()
const activeTab = searchParams.get('tab')
const filterOptions = useFilter({
initialFilters
initialFilters: {
...initialFilters,
status: activeTab ? tabsInfo[activeTab as TabName].value : ''
}
})
const sortOptions = useSort({
initialSort
Expand All @@ -56,6 +64,10 @@ const MyOffers = () => {

const handleOpenDrawer = () => openDrawer()

useEffect(() => {
if (!activeTab) setSearchParams({ tab: Object.keys(tabsInfo)[0] })
}, [activeTab, setSearchParams])

const itemsPerPage = getScreenBasedLimit(breakpoints, itemsLoadLimit)
const showTable = !breakpoints.isMobile && itemsView === CardsViewEnum.Inline

Expand All @@ -79,22 +91,13 @@ const MyOffers = () => {
defaultResponse
})

const handleTabClick = (tab: TabType<string>) => {
const handleTabClick = (tabName: string, tabValue: string) => {
setSearchParams({ tab: tabName })
clearFilters()
resetSort()
setFilterByKey('status')(tab.value)
setFilterByKey('status')(tabValue)
}

const tabs = Object.values(tabsInfo).map((tab) => (
<Tab
activeTab={filters.status === tab.value}
key={tab.label}
onClick={() => handleTabClick(tab)}
>
{t(tab.label)}
</Tab>
))

const sortFields = sortTranslationKeys.map(({ title, value }) => ({
title: t(title),
value
Expand All @@ -115,7 +118,12 @@ const MyOffers = () => {
<CreateOffer closeDrawer={closeDrawer} />
</AppDrawer>
</Box>
<Box sx={styles.tabs}>{tabs}</Box>
<TabFilterList
activeTab={activeTab ?? ''}
onClick={handleTabClick}
sx={styles.tabs}
tabsData={tabsInfo}
/>
<CooperationOfferToolbar
filterOptions={filterOptions}
onChangeView={setItemsView}
Expand Down
38 changes: 38 additions & 0 deletions tests/unit/components/tab-filter-list/TabFilterList.spec.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react'
import { render, fireEvent, screen } from '@testing-library/react'
import TabFilterList from '~/components/tab-filter-list/TabFilterList'

const mockData = {
tab1: {
label: 'Tab 1',
value: 'tab1'
},
tab2: {
label: 'Tab 2',
value: 'tab2'
}
}

const handleClickMock = vi.fn()

describe('TabFilterList', () => {
beforeEach(() => {
render(
<TabFilterList
activeTab='tab1'
onClick={handleClickMock}
tabsData={mockData}
/>
)
})

it('renders TabFilterList correctly', () => {
expect(screen.getByText('Tab 1')).toBeInTheDocument()
expect(screen.getByText('Tab 2')).toBeInTheDocument()
})

it('calls handleClick when a tab is clicked', () => {
fireEvent.click(screen.getByText('Tab 2'))
expect(handleClickMock).toHaveBeenCalled()
})
})

0 comments on commit 5faab5d

Please sign in to comment.