mirror of
https://github.com/inventree/InvenTree.git
synced 2025-11-14 11:56:44 +00:00
Char fix (#10827)
* Remove debouncing from text field * Add debounce to data import field * Only apply for strings values * Fix unit test * More unit test tweaks
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { TextInput, Tooltip } from '@mantine/core';
|
||||
import { useDebouncedValue } from '@mantine/hooks';
|
||||
import { IconCopyCheck, IconX } from '@tabler/icons-react';
|
||||
import {
|
||||
type ReactNode,
|
||||
@@ -40,30 +39,26 @@ export default function TextField({
|
||||
|
||||
const { value } = useMemo(() => field, [field]);
|
||||
|
||||
const [rawText, setRawText] = useState<string>(value || '');
|
||||
const [textValue, setTextValue] = useState<string>(value || '');
|
||||
|
||||
const [debouncedText] = useDebouncedValue(rawText, 100);
|
||||
const onTextChange = useCallback(
|
||||
(value: any) => {
|
||||
setTextValue(value);
|
||||
onChange(value);
|
||||
},
|
||||
[onChange]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setRawText(value || '');
|
||||
setTextValue(value || '');
|
||||
}, [value]);
|
||||
|
||||
const onTextChange = useCallback((value: any) => {
|
||||
setRawText(value);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (debouncedText !== value) {
|
||||
onChange(debouncedText);
|
||||
}
|
||||
}, [debouncedText]);
|
||||
|
||||
// Construct a "right section" for the text field
|
||||
const textFieldRightSection: ReactNode = useMemo(() => {
|
||||
if (definition.rightSection) {
|
||||
// Use the specified override value
|
||||
return definition.rightSection;
|
||||
} else if (value) {
|
||||
} else if (textValue) {
|
||||
if (!definition.required && !definition.disabled) {
|
||||
// Render a button to clear the text field
|
||||
return (
|
||||
@@ -78,7 +73,7 @@ export default function TextField({
|
||||
);
|
||||
}
|
||||
} else if (
|
||||
!value &&
|
||||
!textValue &&
|
||||
definition.placeholder &&
|
||||
placeholderAutofill &&
|
||||
!definition.disabled
|
||||
@@ -94,7 +89,7 @@ export default function TextField({
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
}, [placeholderAutofill, definition, value]);
|
||||
}, [placeholderAutofill, definition, textValue]);
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
@@ -103,19 +98,19 @@ export default function TextField({
|
||||
id={fieldId}
|
||||
aria-label={`text-field-${field.name}`}
|
||||
type={definition.field_type}
|
||||
value={rawText || ''}
|
||||
value={textValue || ''}
|
||||
error={definition.error ?? error?.message}
|
||||
radius='sm'
|
||||
onChange={(event) => onTextChange(event.currentTarget.value)}
|
||||
onBlur={(event) => {
|
||||
if (event.currentTarget.value != value) {
|
||||
onChange(event.currentTarget.value);
|
||||
if (event.currentTarget.value != textValue) {
|
||||
onTextChange(event.currentTarget.value);
|
||||
}
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.code === 'Enter') {
|
||||
// Bypass debounce on enter key
|
||||
onChange(event.currentTarget.value);
|
||||
onTextChange(event.currentTarget.value);
|
||||
}
|
||||
onKeyDown(event.code);
|
||||
}}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { ApiEndpoints } from '@lib/enums/ApiEndpoints';
|
||||
import { apiUrl } from '@lib/functions/Api';
|
||||
import type { ApiFormFieldType } from '@lib/types/Forms';
|
||||
import { useDebouncedValue } from '@mantine/hooks';
|
||||
import { useApi } from '../../contexts/ApiContext';
|
||||
import type { ImportSessionState } from '../../hooks/UseImportSession';
|
||||
import { StandaloneField } from '../forms/StandaloneField';
|
||||
@@ -83,6 +84,14 @@ function ImporterDefaultField({
|
||||
}) {
|
||||
const api = useApi();
|
||||
|
||||
const [rawValue, setRawValue] = useState<any>('');
|
||||
|
||||
const fieldType: string = useMemo(() => {
|
||||
return session.availableFields[fieldName]?.type;
|
||||
}, [fieldName, session.availableFields]);
|
||||
|
||||
const [value] = useDebouncedValue(rawValue, fieldType == 'string' ? 500 : 10);
|
||||
|
||||
const onChange = useCallback(
|
||||
(value: any) => {
|
||||
// Update the default value for the field
|
||||
@@ -105,6 +114,11 @@ function ImporterDefaultField({
|
||||
[fieldName, session, session.fieldDefaults]
|
||||
);
|
||||
|
||||
// Update the default value after the debounced value changes
|
||||
useEffect(() => {
|
||||
onChange(value);
|
||||
}, [value]);
|
||||
|
||||
const fieldDef: ApiFormFieldType = useMemo(() => {
|
||||
let def: any = session.availableFields[fieldName];
|
||||
|
||||
@@ -114,7 +128,10 @@ function ImporterDefaultField({
|
||||
value: session.fieldDefaults[fieldName],
|
||||
field_type: def.type,
|
||||
description: def.help_text,
|
||||
onValueChange: onChange
|
||||
required: false,
|
||||
onValueChange: (value: string) => {
|
||||
setRawValue(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ test('Forms - Supplier Validation', async ({ browser }) => {
|
||||
// Check for validation errors
|
||||
await page.getByText('Form Error').waitFor();
|
||||
await page.getByText('Errors exist for one or more').waitFor();
|
||||
await page.getByText('This field may not be blank.').waitFor();
|
||||
await page.getByText('This field is required').waitFor();
|
||||
await page.getByText('Enter a valid URL.').waitFor();
|
||||
|
||||
// Fill out another field, expect that the errors persist
|
||||
@@ -106,7 +106,7 @@ test('Forms - Supplier Validation', async ({ browser }) => {
|
||||
.getByLabel('text-field-description', { exact: true })
|
||||
.fill('A description');
|
||||
await page.waitForTimeout(250);
|
||||
await page.getByText('This field may not be blank.').waitFor();
|
||||
await page.getByText('This field is required').waitFor();
|
||||
await page.getByText('Enter a valid URL.').waitFor();
|
||||
|
||||
// Generate a unique supplier name
|
||||
|
||||
@@ -116,9 +116,15 @@ test('Importing - BOM', async ({ browser }) => {
|
||||
|
||||
// Delete selected rows
|
||||
await page
|
||||
.getByRole('dialog', { name: 'Importing Data Upload File 2' })
|
||||
.getByRole('dialog', { name: 'Importing Data Upload File' })
|
||||
.getByLabel('action-button-delete-selected')
|
||||
.waitFor();
|
||||
await page.waitForTimeout(200);
|
||||
await page
|
||||
.getByRole('dialog', { name: 'Importing Data Upload File' })
|
||||
.getByLabel('action-button-delete-selected')
|
||||
.click();
|
||||
|
||||
await page.getByRole('button', { name: 'Delete', exact: true }).click();
|
||||
|
||||
await page.getByText('Success', { exact: true }).waitFor();
|
||||
|
||||
Reference in New Issue
Block a user