| 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; |
| const base = { | |||||
| data: { name: 'John Doe', age: 30, gender: 'male' }, | |||||
| t: (text) => { | |||||
| return text; | |||||
| }, | |||||
| }; | |||||
| export const mockDataCardProps = { | |||||
| base, | |||||
| }; |
| 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, | |||||
| }; |
| 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; |
| const base = { | |||||
| sort: '', | |||||
| handleSortChange: () => {}, | |||||
| filter: '', | |||||
| handleFilterChange: () => {}, | |||||
| }; | |||||
| export const mockFilterSortComponentProps = { | |||||
| base, | |||||
| }; |
| 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, | |||||
| }; |
| 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 { useTranslation } from 'next-i18next'; | ||||
| import { useState } from 'react'; | import { useState } from 'react'; | ||||
| import useDebounce from '../../../hooks/use-debounce'; | import useDebounce from '../../../hooks/use-debounce'; | ||||
| import { usePagination } from '../../../hooks/use-pagination'; | import { usePagination } from '../../../hooks/use-pagination'; | ||||
| import { compare } from '../../../utils/helpers/sortHelpers'; | import { compare } from '../../../utils/helpers/sortHelpers'; | ||||
| import DataCard from '../../cards/data-card/DataCard'; | |||||
| import FilterSortComponent from '../filter-sort/FilterSortComponent'; | |||||
| const PaginationComponentRQ = () => { | const PaginationComponentRQ = () => { | ||||
| const [pageIndex, setPageIndex] = useState(1); | const [pageIndex, setPageIndex] = useState(1); | ||||
| const debouncedFilter = useDebounce(filter, 500); | const debouncedFilter = useDebounce(filter, 500); | ||||
| const handleFilterTextChange = (event) => { | |||||
| const handleFilterChange = (event) => { | |||||
| const filterText = event.target.value; | const filterText = event.target.value; | ||||
| setFilter(filterText); | setFilter(filterText); | ||||
| }; | }; | ||||
| .map((item, index) => ( | .map((item, index) => ( | ||||
| // ! DON'T USE index for key, this is for example only | // ! 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}> | <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> | </Grid> | ||||
| )); | )); | ||||
| width: '100%', | 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> | ||||
| </Box> | </Box> |
| import { Box, Button, Grid, Paper, Typography } from '@mui/material'; | |||||
| import { useTranslation } from 'next-i18next'; | |||||
| import { useState } from 'react'; | import { useState } from 'react'; | ||||
| import useDebounce from '../../../hooks/use-debounce'; | |||||
| import useSWRWithFallbackData from '../../../hooks/use-swr-with-initial-data'; | import useSWRWithFallbackData from '../../../hooks/use-swr-with-initial-data'; | ||||
| import { getData } from '../../../requests/dataRequest'; | 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 PaginationComponent = ({ initialData = {} }) => { | ||||
| const [pageIndex, setPageIndex] = useState(1); | const [pageIndex, setPageIndex] = useState(1); | ||||
| const [filter, setFilter] = useState(''); | |||||
| const [sort, setSort] = useState(''); | |||||
| const { t } = useTranslation('pagination'); | |||||
| const fetcher = (page) => getData(page); | const fetcher = (page) => getData(page); | ||||
| const { data: paginationData } = useSWRWithFallbackData(pageIndex, fetcher, { | const { data: paginationData } = useSWRWithFallbackData(pageIndex, fetcher, { | ||||
| fallbackData: initialData, | 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 ( | 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> | |||||
| ); | ); | ||||
| }; | }; | ||||