Reset detail edit data on cancel

This commit is contained in:
Kari Lintulaakso
2025-12-16 15:22:33 +02:00
parent c8e32e4664
commit 17f4ea7496
2 changed files with 157 additions and 11 deletions
@@ -2,7 +2,14 @@ import { useSearchParams } from 'react-router-dom'
import { Box, Button, Paper, Stack, Tab, Tabs, useTheme } from '@mui/material'
import EditIcon from '@mui/icons-material/Edit'
import { useEffect, useState, JSX } from 'react'
import { DetailContextProvider, ModeOptions, ModeType, makeEditData, modeOptionToMode } from './Context/DetailContext'
import {
DetailContextProvider,
ModeOptions,
ModeType,
makeEditData,
modeOptionToMode,
useDetailContext,
} from './Context/DetailContext'
import {
DropdownOption,
DropdownOptionValue,
@@ -54,6 +61,40 @@ export type SetFieldsWithErrorsType = (
updaterFn: (prevFieldsWithErrors: FieldsWithErrorsType) => FieldsWithErrorsType
) => void
const EditToggleButton = <T,>({
canEdit,
isInitiallyNew,
setFieldsWithErrors,
}: {
canEdit: boolean
isInitiallyNew: boolean
setFieldsWithErrors: SetFieldsWithErrorsType
}) => {
const { mode, setMode, resetEditData } = useDetailContext<T>()
if (!canEdit || isInitiallyNew) return null
const handleClick = () => {
if (!mode.read) {
resetEditData()
setFieldsWithErrors(() => ({}))
}
setMode(!mode.read ? 'read' : 'edit')
}
return (
<Button
id="edit-button"
onClick={handleClick}
variant={mode.read ? 'contained' : 'outlined'}
style={{ width: '8em' }}
>
<EditIcon style={{ marginRight: '0.5em' }} /> {mode.read ? 'Edit' : 'Cancel edit'}
</Button>
)
}
export const DetailView = <T extends object>({
tabs,
data,
@@ -214,16 +255,11 @@ export const DetailView = <T extends object>({
</Box>
<Box sx={{ display: 'flex' }} gap={1}>
{!isPersonPage && !isNew && <ContactForm<T> buttonText="Contact" />}
{editRights.edit && !mode.staging && !initialState.mode.new && (
<Button
id="edit-button"
onClick={() => setMode(!mode.read ? 'read' : 'edit')}
variant={mode.read ? 'contained' : 'outlined'}
style={{ width: '8em' }}
>
<EditIcon style={{ marginRight: '0.5em' }} /> {mode.read ? 'Edit' : 'Cancel edit'}
</Button>
)}
<EditToggleButton<T>
canEdit={!!editRights.edit && !mode.staging}
isInitiallyNew={initialState.mode.new}
setFieldsWithErrors={setFieldsWithErrors}
/>
{editRights.delete && !isNew && !isUserPage && (
<Button
id="delete-button"
@@ -0,0 +1,110 @@
import { describe, expect, it, jest } from '@jest/globals'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { MemoryRouter } from 'react-router-dom'
import { DetailView, TabType } from '@/components/DetailView/DetailView'
import { PageContext, PageContextType } from '@/components/Page'
import { useDetailContext } from '@/components/DetailView/Context/DetailContext'
import { TimeUnitDetailsType } from '@/shared/types'
const timeUnitData = {
tu_name: 'old_tu',
tu_display_name: 'Old TU',
rank: 'Age',
low_bnd: 2,
up_bnd: 1,
sequence: 'seq-1',
tu_comment: '',
now_tu_sequence: { sequence: 'seq-1', seq_name: 'Sequence 1' },
now_tau: [],
up_bound: { bid: 1, b_name: 'Upper', age: 20, b_comment: '' },
low_bound: { bid: 2, b_name: 'Lower', age: 10, b_comment: '' },
} as unknown as TimeUnitDetailsType
const pageContextValue: PageContextType<unknown> = {
idList: ['old_tu'],
setIdList: jest.fn(),
idFieldName: 'tu_name',
viewName: 'time-unit',
previousTableUrls: [],
setPreviousTableUrls: jest.fn(),
createTitle: () => timeUnitData.tu_display_name,
createSubtitle: () => '',
editRights: { edit: true },
sqlLimit: 20,
sqlOffset: 0,
sqlColumnFilters: [],
sqlOrderBy: [],
setSqlLimit: jest.fn(),
setSqlOffset: jest.fn(),
setSqlColumnFilters: jest.fn(),
setSqlOrderBy: jest.fn(),
}
const TimeUnitTabContent = () => {
const { editData, setEditData, mode } = useDetailContext<TimeUnitDetailsType>()
return (
<div>
<div data-testid="up-bound-age">{editData.up_bound?.age}</div>
{!mode.read && (
<button
type="button"
onClick={() =>
setEditData({
...editData,
up_bound: { ...(editData.up_bound ?? {}), age: 99 } as TimeUnitDetailsType['up_bound'],
})
}
>
Change upper age
</button>
)}
</div>
)
}
const tabs: TabType[] = [
{
title: 'Time Unit',
content: <TimeUnitTabContent />,
},
]
describe('DetailView cancel edit behavior for Time Unit', () => {
it('resets editData to original values when canceling edits', async () => {
const user = userEvent.setup()
const onWrite = jest.fn(async () => {})
render(
<PageContext.Provider value={pageContextValue}>
<MemoryRouter initialEntries={[`/time-unit/${timeUnitData.tu_name}`]}>
<DetailView<TimeUnitDetailsType>
tabs={tabs}
data={timeUnitData}
validator={() => ({ name: 'noop', error: null })}
onWrite={onWrite}
hasStagingMode
/>
</MemoryRouter>
</PageContext.Provider>
)
expect(screen.getByTestId('up-bound-age').textContent).toBe('20')
await user.click(screen.getByRole('button', { name: /edit/i }))
await user.click(screen.getByRole('button', { name: /change upper age/i }))
expect(screen.getByTestId('up-bound-age').textContent).toBe('99')
await user.click(screen.getByRole('button', { name: /cancel edit/i }))
expect(screen.getByTestId('up-bound-age').textContent).toBe('20')
expect(onWrite).not.toHaveBeenCalled()
await user.click(screen.getByRole('button', { name: /edit/i }))
await user.click(screen.getByRole('button', { name: /change upper age/i }))
expect(screen.getByTestId('up-bound-age').textContent).toBe('99')
})
})