Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display active tab in URL on "My cooperations" and "My offers" pages #2630

Merged
merged 2 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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, UserRoleEnum } from '~/types'
import { CardsViewEnum, UserRoleEnum } 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 @@ -51,6 +58,12 @@ const MyCooperations = () => {
const { filters, clearFilters, setFilterByKey } = filterOptions
const { userRole } = useAppSelector((state) => state.appMain)

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 @@ -75,22 +88,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 @@ -115,7 +119,12 @@ const MyCooperations = () => {
{t(`button.view.${userRole}`)}
</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
44 changes: 27 additions & 17 deletions src/pages/my-offers/MyOffers.tsx
dudchakk marked this conversation as resolved.
Show resolved Hide resolved
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,12 @@ const MyOffers = () => {

const handleOpenDrawer = () => openDrawer()

useEffect(() => {
dudchakk marked this conversation as resolved.
Show resolved Hide resolved
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 +93,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 @@ -117,7 +122,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()
})
})
Loading