diff --git a/packages/components/package-lock.json b/packages/components/package-lock.json index 55d169e8b9..9fc7e03a18 100644 --- a/packages/components/package-lock.json +++ b/packages/components/package-lock.json @@ -1,12 +1,12 @@ { "name": "@labkey/components", - "version": "7.24.1", + "version": "7.24.2-fb-mvtcOptions.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@labkey/components", - "version": "7.24.1", + "version": "7.24.2-fb-mvtcOptions.1", "license": "SEE LICENSE IN LICENSE.txt", "dependencies": { "@hello-pangea/dnd": "18.0.1", diff --git a/packages/components/package.json b/packages/components/package.json index 19e985a1a3..acb8039336 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -1,6 +1,6 @@ { "name": "@labkey/components", - "version": "7.24.1", + "version": "7.24.2-fb-mvtcOptions.1", "description": "Components, models, actions, and utility functions for LabKey applications and pages", "sideEffects": false, "files": [ diff --git a/packages/components/releaseNotes/components.md b/packages/components/releaseNotes/components.md index a352005755..3e1283f9c8 100644 --- a/packages/components/releaseNotes/components.md +++ b/packages/components/releaseNotes/components.md @@ -1,6 +1,10 @@ # @labkey/components Components, models, actions, and utility functions for LabKey applications and pages +### version 7.X +*Released*: X March 2026 +- GitHub Issue 955: limit text choice option length to 200 characters + ### version 7.24.1 *Released*: 25 March 2026 - Factor `EditingForm` out of `EditableDetailPanel` and load model with update columns diff --git a/packages/components/src/internal/components/domainproperties/TextChoiceAddValuesModal.test.tsx b/packages/components/src/internal/components/domainproperties/TextChoiceAddValuesModal.test.tsx index 93f43c3382..6d6862d056 100644 --- a/packages/components/src/internal/components/domainproperties/TextChoiceAddValuesModal.test.tsx +++ b/packages/components/src/internal/components/domainproperties/TextChoiceAddValuesModal.test.tsx @@ -79,6 +79,33 @@ describe('TextChoiceAddValuesModal', () => { validateCounterText('2 values', '3 new values'); }); + test('value exceeding max length disables apply and shows error', async () => { + render(); + const longValue = 'a'.repeat(201); + await userEvent.type(document.querySelector('textarea'), longValue); + expect(document.querySelector('.btn-success').hasAttribute('disabled')).toBeTruthy(); + let errorEls = document.querySelectorAll('.domain-text-choices-error'); + expect(errorEls).toHaveLength(1); + expect(errorEls[0].textContent).toContain('Value exceeds maximum of 200 characters'); + + // clear the long value, error should be gone + await userEvent.clear(document.querySelector('textarea')); + expect(document.querySelectorAll('.domain-text-choices-error')).toHaveLength(0); + + // enter a valid short value, no error + await userEvent.type(document.querySelector('textarea'), 'short value'); + expect(document.querySelector('.btn-success').hasAttribute('disabled')).toBeFalsy(); + expect(document.querySelectorAll('.domain-text-choices-error')).toHaveLength(0); + + // multiline input where second line exceeds max length + await userEvent.clear(document.querySelector('textarea')); + await userEvent.type(document.querySelector('textarea'), 'valid\n' + 'b'.repeat(201)); + expect(document.querySelector('.btn-success').hasAttribute('disabled')).toBeTruthy(); + errorEls = document.querySelectorAll('.domain-text-choices-error'); + expect(errorEls).toHaveLength(1); + expect(errorEls[0].textContent).toContain('Value exceeds maximum of 200 characters'); + }); + test('initial already equal to max', async () => { render(); expect(document.querySelector('.btn-success').hasAttribute('disabled')).toBeTruthy(); diff --git a/packages/components/src/internal/components/domainproperties/TextChoiceAddValuesModal.tsx b/packages/components/src/internal/components/domainproperties/TextChoiceAddValuesModal.tsx index 3e6758d806..ad7cdc2914 100644 --- a/packages/components/src/internal/components/domainproperties/TextChoiceAddValuesModal.tsx +++ b/packages/components/src/internal/components/domainproperties/TextChoiceAddValuesModal.tsx @@ -4,7 +4,7 @@ import { Utils } from '@labkey/api'; import { Modal } from '../../Modal'; -import { MAX_VALID_TEXT_CHOICES } from './constants'; +import { MAX_TEXT_CHOICE_VALUE_LENGTH, MAX_VALID_TEXT_CHOICES } from './constants'; import { getValidValuesFromArray } from './models'; interface Props { @@ -22,27 +22,31 @@ export const TextChoiceAddValuesModal: FC = memo(props => { return valueStr?.trim().length > 0 ? getValidValuesFromArray(valueStr.split('\n').map(v => v.trim())) : []; }, [valueStr]); const maxValuesToAdd = useMemo(() => maxValueCount - initialValueCount, [initialValueCount]); + const tooLongValue = useMemo(() => parsedValues.find(v => v.length > MAX_TEXT_CHOICE_VALUE_LENGTH), [parsedValues]); const hasFieldName = useMemo(() => fieldName?.length > 0, [fieldName]); const onChange = useCallback(evt => { setValueStr(evt.target.value); }, []); const onConfirm = useCallback(() => { - if (parsedValues.length <= maxValuesToAdd) { + if (parsedValues.length <= maxValuesToAdd && !tooLongValue) { onApply(parsedValues); } - }, [parsedValues, maxValuesToAdd, onApply]); - const canConfirm = parsedValues.length > 0 && parsedValues.length <= maxValuesToAdd; + }, [parsedValues, maxValuesToAdd, tooLongValue, onApply]); + const canConfirm = parsedValues.length > 0 && parsedValues.length <= maxValuesToAdd && !tooLongValue; const title = `Add Text Choice Values${hasFieldName ? ' for ' + fieldName : ''}`; const valueNoun = Utils.pluralize(maxValuesToAdd, 'value', 'values'); return (

Enter each value on a new line. {valueNoun} can be added.