| @@ -0,0 +1,19 @@ | |||
| import { Divider, Paper, Typography } from '@mui/material'; | |||
| const DataCard = ({ data, t }) => { | |||
| return ( | |||
| <Paper sx={{ p: 3, height: '100%' }} elevation={3}> | |||
| <Typography sx={{ fontWeight: 600 }}>{t('Name')}</Typography> | |||
| <Typography display="inline"> {data.name}</Typography> | |||
| <Divider /> | |||
| <Typography sx={{ fontWeight: 600 }}>{t('Age')}</Typography> | |||
| <Typography display="inline"> {data.age}</Typography> | |||
| <Divider /> | |||
| <Typography sx={{ fontWeight: 600 }}>{t('Gender')}</Typography> | |||
| <Typography display="inline"> {data.gender}</Typography> | |||
| <Divider /> | |||
| </Paper> | |||
| ); | |||
| }; | |||
| export default DataCard; | |||
| @@ -0,0 +1,10 @@ | |||
| const base = { | |||
| data: { name: 'John Doe', age: 30, gender: 'male' }, | |||
| t: (text) => { | |||
| return text; | |||
| }, | |||
| }; | |||
| export const mockDataCardProps = { | |||
| base, | |||
| }; | |||
| @@ -0,0 +1,20 @@ | |||
| import DataCard from './DataCard'; | |||
| import { mockDataCardProps } from './DataCard.mock'; | |||
| const obj = { | |||
| title: 'cards/DataCard', | |||
| component: DataCard, | |||
| // More on argTypes: https://storybook.js.org/docs/react/api/argtypes | |||
| argTypes: {}, | |||
| }; //eslint-disable-line | |||
| export default obj; | |||
| // More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args | |||
| const Template = (args) => <DataCard {...args} />; | |||
| export const Base = Template.bind({}); | |||
| // More on args: https://storybook.js.org/docs/react/writing-stories/args | |||
| Base.args = { | |||
| ...mockDataCardProps.base, | |||
| }; | |||
| @@ -0,0 +1,42 @@ | |||
| import { | |||
| FormControl, | |||
| InputLabel, | |||
| MenuItem, | |||
| Select, | |||
| TextField, | |||
| } from '@mui/material'; | |||
| const FilterSortComponent = ({ | |||
| sort, | |||
| handleSortChange, | |||
| filter, | |||
| handleFilterChange, | |||
| }) => { | |||
| return ( | |||
| <> | |||
| <FormControl sx={{ flexGrow: 1 }}> | |||
| <InputLabel id="sort-label">Sort</InputLabel> | |||
| <Select | |||
| label="Sort" | |||
| labelId="sort-label" | |||
| id="sort-select-helper" | |||
| value={sort} | |||
| onChange={handleSortChange} | |||
| > | |||
| <MenuItem value="asc">Name - A-Z</MenuItem> | |||
| <MenuItem value="desc">Name - Z-A</MenuItem> | |||
| </Select> | |||
| </FormControl> | |||
| <TextField | |||
| sx={{ flexGrow: 1 }} | |||
| variant="outlined" | |||
| label="Filter" | |||
| placeholder="Filter" | |||
| value={filter} | |||
| onChange={handleFilterChange} | |||
| /> | |||
| </> | |||
| ); | |||
| }; | |||
| export default FilterSortComponent; | |||
| @@ -0,0 +1,10 @@ | |||
| const base = { | |||
| sort: '', | |||
| handleSortChange: () => {}, | |||
| filter: '', | |||
| handleFilterChange: () => {}, | |||
| }; | |||
| export const mockFilterSortComponentProps = { | |||
| base, | |||
| }; | |||
| @@ -0,0 +1,20 @@ | |||
| import FilterSortComponent from './FilterSortComponent'; | |||
| import { mockFilterSortComponentProps } from './FilterSortComponent.mock'; | |||
| const obj = { | |||
| title: 'pagination/FilterSortComponent', | |||
| component: FilterSortComponent, | |||
| // More on argTypes: https://storybook.js.org/docs/react/api/argtypes | |||
| argTypes: {}, | |||
| }; //eslint-disable-line | |||
| export default obj; | |||
| // More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args | |||
| const Template = (args) => <FilterSortComponent {...args} />; | |||
| export const Base = Template.bind({}); | |||
| // More on args: https://storybook.js.org/docs/react/writing-stories/args | |||
| Base.args = { | |||
| ...mockFilterSortComponentProps.base, | |||
| }; | |||
| @@ -1,21 +1,11 @@ | |||
| import { | |||
| Box, | |||
| Button, | |||
| Divider, | |||
| FormControl, | |||
| Grid, | |||
| InputLabel, | |||
| MenuItem, | |||
| Paper, | |||
| Select, | |||
| TextField, | |||
| Typography, | |||
| } from '@mui/material'; | |||
| import { Box, Button, Grid, Paper, Typography } from '@mui/material'; | |||
| import { useTranslation } from 'next-i18next'; | |||
| import { useState } from 'react'; | |||
| import useDebounce from '../../../hooks/use-debounce'; | |||
| import { usePagination } from '../../../hooks/use-pagination'; | |||
| import { compare } from '../../../utils/helpers/sortHelpers'; | |||
| import DataCard from '../../cards/data-card/DataCard'; | |||
| import FilterSortComponent from '../filter-sort/FilterSortComponent'; | |||
| const PaginationComponentRQ = () => { | |||
| const [pageIndex, setPageIndex] = useState(1); | |||
| @@ -26,7 +16,7 @@ const PaginationComponentRQ = () => { | |||
| const debouncedFilter = useDebounce(filter, 500); | |||
| const handleFilterTextChange = (event) => { | |||
| const handleFilterChange = (event) => { | |||
| const filterText = event.target.value; | |||
| setFilter(filterText); | |||
| }; | |||
| @@ -44,18 +34,7 @@ const PaginationComponentRQ = () => { | |||
| .map((item, index) => ( | |||
| // ! DON'T USE index for key, this is for example only | |||
| <Grid item sx={{ p: 2 }} xs={12} sm={6} md={4} lg={3} key={index}> | |||
| {/* TODO separate into component */} | |||
| <Paper sx={{ p: 3, height: '100%' }} elevation={3}> | |||
| <Typography sx={{ fontWeight: 600 }}>{t('Name')}</Typography> | |||
| <Typography display="inline"> {item.name}</Typography> | |||
| <Divider /> | |||
| <Typography sx={{ fontWeight: 600 }}>{t('Age')}</Typography> | |||
| <Typography display="inline"> {item.age}</Typography> | |||
| <Divider /> | |||
| <Typography sx={{ fontWeight: 600 }}>{t('Gender')}</Typography> | |||
| <Typography display="inline"> {item.gender}</Typography> | |||
| <Divider /> | |||
| </Paper> | |||
| <DataCard data={item} t={t} /> | |||
| </Grid> | |||
| )); | |||
| @@ -89,26 +68,11 @@ const PaginationComponentRQ = () => { | |||
| width: '100%', | |||
| }} | |||
| > | |||
| <FormControl sx={{ flexGrow: 1 }}> | |||
| <InputLabel id="sort-label">Sort</InputLabel> | |||
| <Select | |||
| label="Sort" | |||
| labelId="sort-label" | |||
| id="sort-select-helper" | |||
| value={sort} | |||
| onChange={handleSortChange} | |||
| > | |||
| <MenuItem value="asc">Name - A-Z</MenuItem> | |||
| <MenuItem value="desc">Name - Z-A</MenuItem> | |||
| </Select> | |||
| </FormControl> | |||
| <TextField | |||
| sx={{ flexGrow: 1 }} | |||
| variant="outlined" | |||
| label="Filter" | |||
| placeholder="Filter" | |||
| value={filter} | |||
| onChange={handleFilterTextChange} | |||
| <FilterSortComponent | |||
| sort={sort} | |||
| handleSortChange={handleSortChange} | |||
| filter={filter} | |||
| handleFilterChange={handleFilterChange} | |||
| /> | |||
| </Box> | |||
| </Box> | |||
| @@ -1,33 +1,114 @@ | |||
| import { Box, Button, Grid, Paper, Typography } from '@mui/material'; | |||
| import { useTranslation } from 'next-i18next'; | |||
| import { useState } from 'react'; | |||
| import useDebounce from '../../../hooks/use-debounce'; | |||
| import useSWRWithFallbackData from '../../../hooks/use-swr-with-initial-data'; | |||
| import { getData } from '../../../requests/dataRequest'; | |||
| import { compare } from '../../../utils/helpers/sortHelpers'; | |||
| import DataCard from '../../cards/data-card/DataCard'; | |||
| import FilterSortComponent from '../filter-sort/FilterSortComponent'; | |||
| const PaginationComponent = ({ initialData = {} }) => { | |||
| const [pageIndex, setPageIndex] = useState(1); | |||
| const [filter, setFilter] = useState(''); | |||
| const [sort, setSort] = useState(''); | |||
| const { t } = useTranslation('pagination'); | |||
| const fetcher = (page) => getData(page); | |||
| const { data: paginationData } = useSWRWithFallbackData(pageIndex, fetcher, { | |||
| fallbackData: initialData, | |||
| }); | |||
| const debouncedFilter = useDebounce(filter, 500); | |||
| const handleFilterChange = (event) => { | |||
| const filterText = event.target.value; | |||
| setFilter(filterText); | |||
| }; | |||
| const handleSortChange = (event) => { | |||
| const sort = event.target.value; | |||
| setSort(sort); | |||
| }; | |||
| const dataToDisplay = paginationData?.data | |||
| .filter((item) => | |||
| item.name.toLowerCase().startsWith(debouncedFilter.toLowerCase()) | |||
| ) | |||
| .sort((a, b) => compare(a.name, b.name, sort)) | |||
| .map((item, index) => ( | |||
| // ! DON'T USE index for key, this is for example only | |||
| <Grid item sx={{ p: 2 }} xs={12} sm={6} md={4} lg={3} key={index}> | |||
| <DataCard data={item} t={t} /> | |||
| </Grid> | |||
| )); | |||
| return ( | |||
| <div> | |||
| {paginationData?.data?.map((item) => ( | |||
| <div key={item._id}>{item.name}</div> | |||
| ))} | |||
| <button | |||
| disabled={pageIndex === 1} | |||
| onClick={() => setPageIndex(pageIndex - 1)} | |||
| <Paper | |||
| sx={{ | |||
| display: 'flex', | |||
| flexDirection: 'column', | |||
| justifyContent: 'start', | |||
| py: 2, | |||
| minHeight: 400, | |||
| marginTop: 5, | |||
| }} | |||
| elevation={5} | |||
| > | |||
| <Typography sx={{ my: 4 }} variant="h4" gutterBottom align="center"> | |||
| {t('Title')} | |||
| </Typography> | |||
| <Box | |||
| sx={{ | |||
| display: 'flex', | |||
| justifyContent: 'space-between', | |||
| flexWrap: 'wrap', | |||
| mx: 2, | |||
| }} | |||
| > | |||
| Previous | |||
| </button> | |||
| <button | |||
| disabled={pageIndex * 5 > paginationData?.dataCount} | |||
| onClick={() => setPageIndex(pageIndex + 1)} | |||
| <Box | |||
| sx={{ | |||
| display: 'flex', | |||
| justifyContent: 'space-between', | |||
| width: '100%', | |||
| }} | |||
| > | |||
| <FilterSortComponent | |||
| sort={sort} | |||
| handleSortChange={handleSortChange} | |||
| filter={filter} | |||
| handleFilterChange={handleFilterChange} | |||
| /> | |||
| </Box> | |||
| </Box> | |||
| <Grid container>{dataToDisplay}</Grid> | |||
| <Box | |||
| sx={{ | |||
| width: '100%', | |||
| textAlign: 'center', | |||
| marginTop: 3, | |||
| }} | |||
| > | |||
| Next | |||
| </button> | |||
| </div> | |||
| <Button | |||
| disabled={pageIndex === 1} | |||
| onClick={() => setPageIndex(pageIndex - 1)} | |||
| sx={{ | |||
| marginRight: 5, | |||
| }} | |||
| > | |||
| {t('Btns.PrevBtn')} | |||
| </Button> | |||
| <Button | |||
| disabled={pageIndex * 4 > paginationData?.dataCount} | |||
| onClick={() => setPageIndex(pageIndex + 1)} | |||
| sx={{ | |||
| marginRight: 5, | |||
| }} | |||
| > | |||
| {t('Btns.NextBtn')} | |||
| </Button> | |||
| </Box> | |||
| </Paper> | |||
| ); | |||
| }; | |||