bigboyjo
bigboyjo
Explore posts from servers
TTCTheo's Typesafe Cult
Created by bigboyjo on 2/26/2024 in #questions
Using Vitest to test React component that uses custom hook
I'm using Vitest to test a React component that uses a custom hook. The custom hook creates a column for MUI DataGridPro with a custom renderCell function that renders a button in the column. The hook accepts a void callback function that is used for the onClick of the button. When the button is clicked, a modal should open up to display information based on the row. In prod and dev this works as expected, but I'm not sure how to test it. I want a test that will test the clicking of the button to ensure the Modal opens up. If I don't mock the hook, I get an error telling me renderEntryIconColumn is not a function, but when I do mock it and debug the test, renderEntryIconColumn is undefined. Below is the hook and component.
// useEntryIcon.js
const useEntryIcon = (callback) => {
const [entryIcon, setEntryIcon] = useState('floppy')

const { api } = useApiContext()

const renderEntryIconColumn = useCallback(
({
align = 'center',
headerAlign = 'center',
filterable = false,
sortable = false,
resizable = false,
disableColumnMenu = false,
title,
id,
...rest
}) => ({
align,
headerAlign,
filterable,
sortable,
resizable,
disableColumnMenu,
renderCell: ({ id: rowId, row }) => {
const params = { rowId, row }

return (
// EntriesIcon renders an svg based on `entryIcon`
<EntriesIcon
iconType={entryIcon}
title={typeof title === 'string' ? title : title(rowId)}
tabIndex={0}
onClick={(e) => callback(params, e)}
onKeyDown={(e) => {
if (e.key === ' ' || e.key === 'Enter') {
callback(params, e)
}
}}
/>
)
},
...rest,
}),
[callback, entryIcon]
)

useEffect(() => {
api
.get('/api/app-info/entry-icon')
.then(({ data }) => setEntryIcon(data))
.catch(() => setEntryIcon('floppy'))
}, [api])

return renderEntryIconColumn
}
// useEntryIcon.js
const useEntryIcon = (callback) => {
const [entryIcon, setEntryIcon] = useState('floppy')

const { api } = useApiContext()

const renderEntryIconColumn = useCallback(
({
align = 'center',
headerAlign = 'center',
filterable = false,
sortable = false,
resizable = false,
disableColumnMenu = false,
title,
id,
...rest
}) => ({
align,
headerAlign,
filterable,
sortable,
resizable,
disableColumnMenu,
renderCell: ({ id: rowId, row }) => {
const params = { rowId, row }

return (
// EntriesIcon renders an svg based on `entryIcon`
<EntriesIcon
iconType={entryIcon}
title={typeof title === 'string' ? title : title(rowId)}
tabIndex={0}
onClick={(e) => callback(params, e)}
onKeyDown={(e) => {
if (e.key === ' ' || e.key === 'Enter') {
callback(params, e)
}
}}
/>
)
},
...rest,
}),
[callback, entryIcon]
)

useEffect(() => {
api
.get('/api/app-info/entry-icon')
.then(({ data }) => setEntryIcon(data))
.catch(() => setEntryIcon('floppy'))
}, [api])

return renderEntryIconColumn
}
// MyComponent.jsx
function MyComponent({
columns,
rows,
...rest
}) {
const [modalConfig, setModalConfig] = useState({
recordId: '',
memberId: '',
open: false
})
const renderEntryIconColumn = useEntryIcon(({ row }) => {
const { recordId, memberId } = row

if (memberId) {
setModalConfig({ open: true, recordId: recordId.toString(), memberId })
}
}
)
const firstColumn = renderEntryIconColumn({
field: 'entries',
headerName: 'Entries',
id: (rowId) => `entries-icon-${rowId}`,
title: (id) => `render-${id}`,
})

return (
<>
<DataGridPro columns={[firstColumn, ...columns]} rows={rows} {...rest} />
<Modal
{...modalConfig}
onClose={() => {
setModalConfig({ open: false, recordId: '', memberId: '' })
}}
/>
</>
)
}
// MyComponent.jsx
function MyComponent({
columns,
rows,
...rest
}) {
const [modalConfig, setModalConfig] = useState({
recordId: '',
memberId: '',
open: false
})
const renderEntryIconColumn = useEntryIcon(({ row }) => {
const { recordId, memberId } = row

if (memberId) {
setModalConfig({ open: true, recordId: recordId.toString(), memberId })
}
}
)
const firstColumn = renderEntryIconColumn({
field: 'entries',
headerName: 'Entries',
id: (rowId) => `entries-icon-${rowId}`,
title: (id) => `render-${id}`,
})

return (
<>
<DataGridPro columns={[firstColumn, ...columns]} rows={rows} {...rest} />
<Modal
{...modalConfig}
onClose={() => {
setModalConfig({ open: false, recordId: '', memberId: '' })
}}
/>
</>
)
}
1 replies