diff --git a/frontend/libs/studio-components/src/components/StudioCodelistEditor/StudioCodeListEditor.test.tsx b/frontend/libs/studio-components/src/components/StudioCodelistEditor/StudioCodeListEditor.test.tsx
index b78acd0001b..ea8ff99beaa 100644
--- a/frontend/libs/studio-components/src/components/StudioCodelistEditor/StudioCodeListEditor.test.tsx
+++ b/frontend/libs/studio-components/src/components/StudioCodelistEditor/StudioCodeListEditor.test.tsx
@@ -46,11 +46,13 @@ const codeList: CodeList = [
helpText: 'Test 3 help text',
},
];
+const onBlurAny = jest.fn();
const onChange = jest.fn();
const onInvalid = jest.fn();
const defaultProps: StudioCodeListEditorProps = {
codeList,
texts,
+ onBlurAny,
onChange,
onInvalid,
};
@@ -119,8 +121,7 @@ describe('StudioCodeListEditor', () => {
const labelInput = screen.getByRole('textbox', { name: texts.itemLabel(1) });
const newValue = 'new text';
await user.type(labelInput, newValue);
- await user.tab();
- expect(onChange).toHaveBeenCalledTimes(1);
+ expect(onChange).toHaveBeenCalledTimes(newValue.length);
expect(onChange).toHaveBeenLastCalledWith([
{ ...codeList[0], label: newValue },
codeList[1],
@@ -134,8 +135,7 @@ describe('StudioCodeListEditor', () => {
const valueInput = screen.getByRole('textbox', { name: texts.itemValue(1) });
const newValue = 'new text';
await user.type(valueInput, newValue);
- await user.tab();
- expect(onChange).toHaveBeenCalledTimes(1);
+ expect(onChange).toHaveBeenCalledTimes(newValue.length);
expect(onChange).toHaveBeenLastCalledWith([
{ ...codeList[0], value: newValue },
codeList[1],
@@ -149,8 +149,7 @@ describe('StudioCodeListEditor', () => {
const descriptionInput = screen.getByRole('textbox', { name: texts.itemDescription(1) });
const newValue = 'new text';
await user.type(descriptionInput, newValue);
- await user.tab();
- expect(onChange).toHaveBeenCalledTimes(1);
+ expect(onChange).toHaveBeenCalledTimes(newValue.length);
expect(onChange).toHaveBeenLastCalledWith([
{ ...codeList[0], description: newValue },
codeList[1],
@@ -164,8 +163,7 @@ describe('StudioCodeListEditor', () => {
const helpTextInput = screen.getByRole('textbox', { name: texts.itemHelpText(1) });
const newValue = 'new text';
await user.type(helpTextInput, newValue);
- await user.tab();
- expect(onChange).toHaveBeenCalledTimes(1);
+ expect(onChange).toHaveBeenCalledTimes(newValue.length);
expect(onChange).toHaveBeenLastCalledWith([
{ ...codeList[0], helpText: newValue },
codeList[1],
@@ -196,6 +194,21 @@ describe('StudioCodeListEditor', () => {
]);
});
+ it('Calls the onBlurAny callback with the current code list when an item in the table is blurred', async () => {
+ const user = userEvent.setup();
+ renderCodeListEditor();
+ const valueInput = screen.getByRole('textbox', { name: texts.itemValue(1) });
+ const newValue = 'new text';
+ await user.type(valueInput, newValue);
+ await user.tab();
+ expect(onBlurAny).toHaveBeenCalledTimes(1);
+ expect(onBlurAny).toHaveBeenLastCalledWith([
+ { ...codeList[0], value: newValue },
+ codeList[1],
+ codeList[2],
+ ]);
+ });
+
it('Updates itself when the user changes something', async () => {
const user = userEvent.setup();
renderCodeListEditor();
@@ -267,8 +280,7 @@ describe('StudioCodeListEditor', () => {
const validValueInput = screen.getByRole('textbox', { name: texts.itemValue(3) });
const newValue = 'new value';
await user.type(validValueInput, newValue);
- await user.tab();
- expect(onInvalid).toHaveBeenCalledTimes(1);
+ expect(onInvalid).toHaveBeenCalledTimes(newValue.length);
});
it('Does not trigger onInvalid if an invalid code list is changed to a valid state', async () => {
diff --git a/frontend/libs/studio-components/src/components/StudioCodelistEditor/StudioCodeListEditor.tsx b/frontend/libs/studio-components/src/components/StudioCodelistEditor/StudioCodeListEditor.tsx
index a4cff5e76da..d1da350578a 100644
--- a/frontend/libs/studio-components/src/components/StudioCodelistEditor/StudioCodeListEditor.tsx
+++ b/frontend/libs/studio-components/src/components/StudioCodelistEditor/StudioCodeListEditor.tsx
@@ -23,23 +23,32 @@ import { areThereCodeListErrors, findCodeListErrors, isCodeListValid } from './v
import type { ValueErrorMap } from './types/ValueErrorMap';
import { StudioFieldset } from '../StudioFieldset';
import { StudioErrorMessage } from '../StudioErrorMessage';
+import type { Override } from '../../types/Override';
+import type { StudioInputTableProps } from '../StudioInputTable/StudioInputTable';
export type StudioCodeListEditorProps = {
codeList: CodeList;
- onChange: (codeList: CodeList) => void;
+ onBlurAny?: (codeList: CodeList) => void;
+ onChange?: (codeList: CodeList) => void;
onInvalid?: () => void;
texts: CodeListEditorTexts;
};
export function StudioCodeListEditor({
codeList,
+ onBlurAny,
onChange,
onInvalid,
texts,
}: StudioCodeListEditorProps): ReactElement {
return (
-
+
);
}
@@ -48,6 +57,7 @@ type StatefulCodeListEditorProps = Omit;
function StatefulCodeListEditor({
codeList: defaultCodeList,
+ onBlurAny,
onChange,
onInvalid,
}: StatefulCodeListEditorProps): ReactElement {
@@ -60,18 +70,32 @@ function StatefulCodeListEditor({
const handleChange = useCallback(
(newCodeList: CodeList) => {
setCodeList(newCodeList);
- isCodeListValid(newCodeList) ? onChange(newCodeList) : onInvalid?.();
+ isCodeListValid(newCodeList) ? onChange?.(newCodeList) : onInvalid?.();
},
[onChange, onInvalid],
);
- return ;
+ const handleBlurAny = useCallback(() => {
+ onBlurAny?.(codeList);
+ }, [onBlurAny, codeList]);
+
+ return (
+
+ );
}
-type InternalCodeListEditorProps = Omit;
+type InternalCodeListEditorProps = Override<
+ Pick,
+ Omit
+>;
function ControlledCodeListEditor({
codeList,
+ onBlurAny,
onChange,
}: InternalCodeListEditorProps): ReactElement {
const { texts } = useStudioCodeListEditorContext();
@@ -86,12 +110,18 @@ function ControlledCodeListEditor({
return (
-
+
);
}
+
type InternalCodeListEditorWithErrorsProps = InternalCodeListEditorProps & ErrorsProps;
function CodeListTable(props: InternalCodeListEditorWithErrorsProps): ReactElement {
@@ -107,11 +137,14 @@ function EmptyCodeListTable(): ReactElement {
return {texts.emptyCodeList};
}
-function CodeListTableWithContent(props: InternalCodeListEditorWithErrorsProps): ReactElement {
+function CodeListTableWithContent({
+ onBlurAny,
+ ...rest
+}: InternalCodeListEditorWithErrorsProps): ReactElement {
return (
-
+
-
+
);
}
@@ -145,7 +178,7 @@ function TableBody({
[codeList, onChange],
);
- const handleBlur = useCallback(
+ const handleChange = useCallback(
(index: number, newItem: CodeListItem) => {
const updatedCodeList = changeCodeListItem(codeList, index, newItem);
onChange(updatedCodeList);
@@ -161,7 +194,7 @@ function TableBody({
item={item}
key={index}
number={index + 1}
- onBlur={(newItem) => handleBlur(index, newItem)}
+ onChange={(newItem) => handleChange(index, newItem)}
onDeleteButtonClick={() => handleDeleteButtonClick(index)}
/>
))}
diff --git a/frontend/libs/studio-components/src/components/StudioCodelistEditor/StudioCodeListEditorRow/StudioCodeListEditorRow.tsx b/frontend/libs/studio-components/src/components/StudioCodelistEditor/StudioCodeListEditorRow/StudioCodeListEditorRow.tsx
index 161b6d04264..501f9f4fcac 100644
--- a/frontend/libs/studio-components/src/components/StudioCodelistEditor/StudioCodeListEditorRow/StudioCodeListEditorRow.tsx
+++ b/frontend/libs/studio-components/src/components/StudioCodelistEditor/StudioCodeListEditorRow/StudioCodeListEditorRow.tsx
@@ -13,7 +13,7 @@ type StudioCodeListEditorRowProps = {
error: ValueError | null;
item: CodeListItem;
number: number;
- onBlur: (newItem: CodeListItem) => void;
+ onChange: (newItem: CodeListItem) => void;
onDeleteButtonClick: () => void;
};
@@ -21,7 +21,7 @@ export function StudioCodeListEditorRow({
error,
item,
number,
- onBlur,
+ onChange,
onDeleteButtonClick,
}: StudioCodeListEditorRowProps) {
const { texts } = useStudioCodeListEditorContext();
@@ -29,33 +29,33 @@ export function StudioCodeListEditorRow({
const handleLabelChange = useCallback(
(label: string) => {
const updatedItem = changeLabel(item, label);
- onBlur(updatedItem);
+ onChange(updatedItem);
},
- [item, onBlur],
+ [item, onChange],
);
const handleDescriptionChange = useCallback(
(description: string) => {
const updatedItem = changeDescription(item, description);
- onBlur(updatedItem);
+ onChange(updatedItem);
},
- [item, onBlur],
+ [item, onChange],
);
const handleValueChange = useCallback(
(value: string) => {
const updatedItem = changeValue(item, value);
- onBlur(updatedItem);
+ onChange(updatedItem);
},
- [item, onBlur],
+ [item, onChange],
);
const handleHelpTextChange = useCallback(
(helpText: string) => {
const updatedItem = changeHelpText(item, helpText);
- onBlur(updatedItem);
+ onChange(updatedItem);
},
- [item, onBlur],
+ [item, onChange],
);
return (
@@ -64,22 +64,22 @@ export function StudioCodeListEditorRow({
autoComplete='off'
error={error && texts.valueErrors[error]}
label={texts.itemValue(number)}
- onBlur={handleValueChange}
+ onChange={handleValueChange}
value={item.value}
/>
@@ -90,23 +90,23 @@ export function StudioCodeListEditorRow({
type TextfieldCellProps = {
error?: string;
label: string;
- onBlur: (newString: string) => void;
+ onChange: (newString: string) => void;
value: CodeListItemValue;
autoComplete?: HTMLInputAutoCompleteAttribute;
};
-function TextfieldCell({ error, label, value, onBlur, autoComplete }: TextfieldCellProps) {
+function TextfieldCell({ error, label, value, onChange, autoComplete }: TextfieldCellProps) {
const ref = useRef(null);
useEffect((): void => {
ref.current?.setCustomValidity(error || '');
}, [error]);
- const handleBlur = useCallback(
+ const handleChange = useCallback(
(event: React.ChangeEvent): void => {
- onBlur(event.target.value);
+ onChange(event.target.value);
},
- [onBlur],
+ [onChange],
);
const handleFocus = useCallback((event: FocusEvent): void => {
@@ -118,7 +118,7 @@ function TextfieldCell({ error, label, value, onBlur, autoComplete }: TextfieldC
aria-label={label}
autoComplete={autoComplete}
className={classes.textfieldCell}
- onBlur={handleBlur}
+ onChange={handleChange}
onFocus={handleFocus}
ref={ref}
value={(value as string) ?? ''}
diff --git a/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeList/CodeLists/CodeLists.tsx b/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeList/CodeLists/CodeLists.tsx
index b2a4c57cfb4..c2553e24db5 100644
--- a/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeList/CodeLists/CodeLists.tsx
+++ b/frontend/libs/studio-content-library/src/ContentLibrary/LibraryBody/pages/CodeList/CodeLists/CodeLists.tsx
@@ -24,7 +24,7 @@ type CodeListProps = {
function CodeList({ codeList, onUpdateCodeList }: CodeListProps) {
const editorTexts: CodeListEditorTexts = useOptionListEditorTexts();
- const handleUpdateCodeList = (updatedCodeList: StudioComponentsCodeList): void => {
+ const handleBlurAny = (updatedCodeList: StudioComponentsCodeList): void => {
const updatedCodeListWithMetadata = updateCodeListWithMetadata(codeList, updatedCodeList);
onUpdateCodeList(updatedCodeListWithMetadata);
};
@@ -36,7 +36,7 @@ function CodeList({ codeList, onUpdateCodeList }: CodeListProps) {
diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/OptionListEditor.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/OptionListEditor.tsx
index 2f581ec6407..767d9861a24 100644
--- a/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/OptionListEditor.tsx
+++ b/frontend/packages/ux-editor/src/components/config/editModal/EditOptions/OptionTabs/EditTab/OptionListEditor/OptionListEditor.tsx
@@ -90,7 +90,7 @@ function EditLibraryOptionListEditorModal({
const optionListHasChanged = (options: Option[]): boolean =>
JSON.stringify(options) !== JSON.stringify(localOptionList);
- const handleOptionsChange = (options: Option[]) => {
+ const handleBlurAny = (options: Option[]) => {
if (optionListHasChanged(options)) {
updateOptionList({ optionListId: optionsId, optionsList: options });
setLocalOptionList(options);
@@ -126,7 +126,7 @@ function EditLibraryOptionListEditorModal({
>
@@ -147,7 +147,7 @@ function EditManualOptionListEditorModal({
const modalRef = useRef(null);
const editorTexts = useOptionListEditorTexts();
- const handleOptionsChange = (options: Option[]) => {
+ const handleBlurAny = (options: Option[]) => {
if (component.optionsId) {
delete component.optionsId;
}
@@ -175,7 +175,7 @@ function EditManualOptionListEditorModal({
>