Render display labels for sequence selection

This commit is contained in:
Kari Lintulaakso
2025-11-24 16:59:37 +02:00
parent 37ed30bd15
commit c674cc50bf
4 changed files with 139 additions and 13 deletions
@@ -347,18 +347,32 @@ export const FieldWithTableSelection = <T extends object, ParentType extends obj
sourceField,
selectorTable,
disabled,
displayValue,
getDisplayValue,
}: {
targetField: keyof ParentType
sourceField: keyof T
selectorTable: ReactElement
disabled?: boolean
displayValue?: string | number | null
getDisplayValue?: (selected: T) => string | number | null | undefined
}) => {
const { editData, setEditData, validator, fieldsWithErrors, setFieldsWithErrors } = useDetailContext<ParentType>()
const errorObject = validator(editData, targetField as keyof EditDataType<ParentType>)
const { error } = errorObject
const [open, setOpen] = useState(false)
const [selectedDisplayValue, setSelectedDisplayValue] = useState<string | number | null | undefined>(displayValue)
useEffect(() => {
setSelectedDisplayValue(displayValue)
}, [displayValue])
const selectorFn = (selected: T) => {
setEditData({ ...editData, [targetField]: selected[sourceField] })
const newDisplayValue = getDisplayValue ? getDisplayValue(selected) : selected[sourceField]
setSelectedDisplayValue(
typeof newDisplayValue === 'string' || typeof newDisplayValue === 'number' ? newDisplayValue : null
)
setOpen(false)
}
@@ -392,7 +406,7 @@ export const FieldWithTableSelection = <T extends object, ParentType extends obj
size="small"
error={!!error}
helperText={error ?? ''}
value={editData[targetField as keyof EditDataType<ParentType>] ?? undefined}
value={selectedDisplayValue ?? editData[targetField as keyof EditDataType<ParentType>] ?? undefined}
onClick={() => setOpen(true)}
disabled={disabled}
sx={{ backgroundColor: disabled ? 'grey' : '' }}
@@ -0,0 +1,99 @@
import { describe, expect, it } from '@jest/globals'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { DetailContext, DetailContextType, modeOptionToMode } from '@/components/DetailView/Context/DetailContext'
import { SequenceSelect } from './SequenceSelect'
import { TimeUnitDetailsType } from '@/shared/types'
import { ReactNode } from 'react'
import '@testing-library/jest-dom'
jest.mock('./SequenceTable', () => ({
SequenceTable: ({
selectorFn,
}: {
selectorFn?: (sequence: { sequence: number; display_value?: string }) => void
}) => (
<button
type="button"
onClick={() =>
selectorFn?.({
sequence: 2,
display_value: 'Updated Sequence Display',
})
}
>
open selector
</button>
),
}))
type ContextWrapperProps = {
children: ReactNode
data?: Partial<TimeUnitDetailsType>
}
const renderWithContext = ({ children, data }: ContextWrapperProps) => {
const timeUnit = {
sequence: 1,
tu_name: 'TU-1',
tu_display_name: 'TU display',
rank: 'Age',
low_bnd: 0,
up_bnd: 0,
now_tu_sequence: {
sequence: 1,
seq_name: 'Central Paratethys',
},
now_tau: [],
low_bound: {
bid: 1,
b_name: 'low',
age: 1,
b_comment: '',
},
up_bound: {
bid: 2,
b_name: 'up',
age: 2,
b_comment: '',
},
...data,
} as unknown as TimeUnitDetailsType
const contextValue: DetailContextType<TimeUnitDetailsType> = {
data: timeUnit,
mode: modeOptionToMode.edit,
setMode: () => undefined,
editData: timeUnit,
setEditData: () => undefined,
textField: () => <></>,
bigTextField: () => <></>,
dropdown: () => <></>,
dropdownWithSearch: () => <></>,
radioSelection: () => <></>,
validator: (_editData, fieldName) => ({ name: String(fieldName), error: null }),
fieldsWithErrors: {},
setFieldsWithErrors: () => undefined,
}
return render(
<DetailContext.Provider value={contextValue as DetailContextType<unknown>}>{children}</DetailContext.Provider>
)
}
describe('SequenceSelect', () => {
it('shows display label in edit mode when sequence has a name', () => {
renderWithContext({ children: <SequenceSelect /> })
expect(screen.getByDisplayValue('Central Paratethys')).toBeTruthy()
})
it('updates display label when a new sequence is selected', async () => {
const user = userEvent.setup()
renderWithContext({ children: <SequenceSelect /> })
await user.click(screen.getByRole('button', { name: /open selector/i }))
expect(screen.getByDisplayValue('Updated Sequence Display')).toBeTruthy()
})
})
@@ -0,0 +1,21 @@
import { FieldWithTableSelection } from '@/components/DetailView/common/editingComponents'
import { useDetailContext } from '@/components/DetailView/Context/DetailContext'
import { SequenceDetailsType, TimeUnitDetailsType } from '@/shared/types'
import { SequenceTable } from './SequenceTable'
export const SequenceSelect = () => {
const { data } = useDetailContext<TimeUnitDetailsType>()
const displayValue = data.now_tu_sequence?.seq_name ?? ''
return (
<FieldWithTableSelection<SequenceDetailsType & { display_value?: string }, TimeUnitDetailsType>
key="sequence"
sourceField="sequence"
targetField="sequence"
selectorTable={<SequenceTable />}
displayValue={displayValue}
getDisplayValue={sequence => sequence.display_value ?? sequence.seq_name ?? ''}
/>
)
}
@@ -1,11 +1,11 @@
import { SequenceDetailsType, TimeBound, TimeUnitDetailsType } from '@/shared/types'
import { FieldWithTableSelection, TimeBoundSelection } from '@/components/DetailView/common/editingComponents'
import { TimeBound, TimeUnitDetailsType } from '@/shared/types'
import { TimeBoundSelection } from '@/components/DetailView/common/editingComponents'
import { emptyOption } from '@/components/DetailView/common/misc'
import { ArrayFrame, Grouped } from '@/components/DetailView/common/tabLayoutHelpers'
import { useDetailContext } from '@/components/DetailView/Context/DetailContext'
import { SequenceTable } from '@/components/Sequence/SequenceTable'
import { Box } from '@mui/material'
import { EditingForm, EditingFormField } from '@/components/DetailView/common/EditingForm'
import { SequenceSelect } from '@/components/Sequence/SequenceSelect'
export const TimeUnitTab = () => {
const { textField, dropdown, data, editData, setEditData, fieldsWithErrors, mode } =
@@ -28,15 +28,7 @@ export const TimeUnitTab = () => {
const timeUnit = [
['Name', textField('tu_display_name', { type: 'text', disabled: mode.new ? false : true })],
['Rank', dropdown('rank', rankOptions, 'Rank')],
[
'Sequence',
<FieldWithTableSelection<SequenceDetailsType, TimeUnitDetailsType>
key="sequence"
sourceField="sequence"
targetField="sequence"
selectorTable={<SequenceTable />}
/>,
],
['Sequence', <SequenceSelect key="sequence" />],
['Comment', textField('tu_comment')],
]