From 3f666b4119f24d1a9f5e9715629fc071c1f06ab3 Mon Sep 17 00:00:00 2001 From: Abdallah AlHalees Date: Tue, 4 Feb 2025 10:28:33 +0100 Subject: [PATCH] fix: Reannounce validation error in date range picker on apply (#3210) Co-authored-by: Joan Perals --- .../__tests__/date-range-picker.test.tsx | 59 ++++++++++++++++++- src/date-range-picker/dropdown.tsx | 6 +- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/src/date-range-picker/__tests__/date-range-picker.test.tsx b/src/date-range-picker/__tests__/date-range-picker.test.tsx index a405755eb8..8e6db04b95 100644 --- a/src/date-range-picker/__tests__/date-range-picker.test.tsx +++ b/src/date-range-picker/__tests__/date-range-picker.test.tsx @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import * as React from 'react'; -import { render } from '@testing-library/react'; +import { render, waitFor } from '@testing-library/react'; import Mockdate from 'mockdate'; import { warnOnce } from '@cloudscape-design/component-toolkit/internal'; @@ -313,6 +313,63 @@ describe('Date range picker', () => { changeMode(wrapper, 'relative'); expect(wrapper.findDropdown()!.findValidationError()).toBeNull(); }); + + test('reannounces error text when apply is clicked', async () => { + Mockdate.set(new Date('2020-01-01T12:30:20')); + ({ wrapper } = renderDateRangePicker({ + ...defaultProps, + isValidRange: value => { + if (value === null) { + return { + valid: false, + errorMessage: 'No range selected', + }; + } + if (value.type === 'relative' && value.amount === 10) { + return { valid: false, errorMessage: '10 is not allowed.' }; + } + if (value.type === 'absolute') { + const [endDateWithoutTime] = value.endDate.split('T'); + + if (!endDateWithoutTime) { + return { + valid: false, + errorMessage: 'You must provide an end date', + }; + } + + if (value.startDate < '2020-01-01T00:00:00') { + return { + valid: false, + errorMessage: 'The range cannot start before 2020', + }; + } + } + return { valid: true }; + }, + })); + wrapper.openDropdown(); + changeMode(wrapper, 'absolute'); + wrapper.findDropdown()?.findDateAt('left', 1, 1).click(); + wrapper.findDropdown()!.findApplyButton().click(); + + const liveRegion = document.querySelectorAll('[aria-live=polite]')![3]; + + // announces first validation error + await waitFor(() => expect(liveRegion).toHaveTextContent('You must provide an end date')); + + wrapper.findDropdown()?.findDateAt('left', 1, 3).click(); + + // announces different validation error + await waitFor(() => expect(liveRegion).toHaveTextContent('The range cannot start before 2020')); + + wrapper.findDropdown()!.findApplyButton().click(); + + // reannounces second validation error + await waitFor(() => expect(liveRegion).toHaveTextContent('The range cannot start before 2020.')); + + Mockdate.reset(); + }); }); describe('change event', () => { diff --git a/src/date-range-picker/dropdown.tsx b/src/date-range-picker/dropdown.tsx index 6461bf4d7e..f2cfd81a70 100644 --- a/src/date-range-picker/dropdown.tsx +++ b/src/date-range-picker/dropdown.tsx @@ -10,7 +10,7 @@ import { ButtonProps } from '../button/interfaces'; import { InternalButton } from '../button/internal'; import { useInternalI18n } from '../i18n/context'; import FocusLock from '../internal/components/focus-lock'; -import InternalLiveRegion from '../live-region/internal'; +import InternalLiveRegion, { InternalLiveRegionRef } from '../live-region/internal'; import InternalSpaceBetween from '../space-between/internal'; import Calendar from './calendar'; import { DateRangePickerProps } from './interfaces'; @@ -84,6 +84,7 @@ export function DateRangePickerDropdown({ const i18n = useInternalI18n('date-range-picker'); const isMonthPicker = granularity === 'month'; const hideTime = dateOnly || isMonthPicker; + const liveRegionRef = useRef(null); const [rangeSelectionMode, setRangeSelectionMode] = useState<'absolute' | 'relative'>( getDefaultMode(value, relativeOptions, rangeSelectorMode) @@ -123,6 +124,7 @@ export function DateRangePickerDropdown({ if (newValidationResult.valid === false) { setApplyClicked(true); setValidationResult(newValidationResult); + liveRegionRef.current?.reannounce(); } else { setApplyClicked(false); closeDropdown(); @@ -230,7 +232,7 @@ export function DateRangePickerDropdown({ > {validationResult.errorMessage} -