From ef250bb83d5269bd8460449beb7eae12fcdf7181 Mon Sep 17 00:00:00 2001 From: Samuli Toivonen Date: Wed, 11 Dec 2024 13:55:20 +0200 Subject: [PATCH 1/3] date picker translation updates --- frontend/src/lib-components/i18n.tsx | 2 ++ .../src/lib-customizations/defaults/components/i18n/en.tsx | 6 ++++-- .../src/lib-customizations/defaults/components/i18n/fi.tsx | 6 ++++-- .../src/lib-customizations/defaults/components/i18n/sv.tsx | 6 ++++-- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/frontend/src/lib-components/i18n.tsx b/frontend/src/lib-components/i18n.tsx index e6ba612106f..2acd13930e7 100644 --- a/frontend/src/lib-components/i18n.tsx +++ b/frontend/src/lib-components/i18n.tsx @@ -37,6 +37,8 @@ export interface Translations { dateTooEarly: string dateTooLate: string } + open: string + close: string } documentTemplates: { templateQuestions: { diff --git a/frontend/src/lib-customizations/defaults/components/i18n/en.tsx b/frontend/src/lib-customizations/defaults/components/i18n/en.tsx index 0fbdd1a71af..12acf087e8a 100644 --- a/frontend/src/lib-customizations/defaults/components/i18n/en.tsx +++ b/frontend/src/lib-customizations/defaults/components/i18n/en.tsx @@ -28,12 +28,14 @@ const components: Translations = { datePicker: { placeholder: 'dd.mm.yyyy', description: - 'Type the date in dd.mm.yyyy format. You can get to month picker with the tab key.', + 'Type the date in dd.mm.yyyy format. You can get to month picker with the Down Arrow key.', validationErrors: { validDate: 'Valid date format is dd.mm.yyyy', dateTooEarly: 'Pick a later date', dateTooLate: 'Pick an earlier date' - } + }, + open: 'Open date picker', + close: 'Close date picker' }, documentTemplates: { // only on employee frontend diff --git a/frontend/src/lib-customizations/defaults/components/i18n/fi.tsx b/frontend/src/lib-customizations/defaults/components/i18n/fi.tsx index e7a51eeb595..b3a3fbb7cb0 100644 --- a/frontend/src/lib-customizations/defaults/components/i18n/fi.tsx +++ b/frontend/src/lib-customizations/defaults/components/i18n/fi.tsx @@ -28,12 +28,14 @@ const components: Translations = { datePicker: { placeholder: 'pp.kk.vvvv', description: - 'Kirjoita päivämäärä kenttään muodossa pp.kk.vvvv. Tab-näppäimellä pääset kuukausivalitsimeen.', + 'Kirjoita päivämäärä kenttään muodossa pp.kk.vvvv. Nuoli alas -näppäimellä pääset kuukausivalitsimeen.', validationErrors: { validDate: 'Anna muodossa pp.kk.vvvv', dateTooEarly: 'Valitse myöhäisempi päivä', dateTooLate: 'Valitse aikaisempi päivä' - } + }, + open: 'Avaa päivämäärävalitsin', + close: 'Sulje päivämäärävalitsin' }, documentTemplates: { templateQuestions: { diff --git a/frontend/src/lib-customizations/defaults/components/i18n/sv.tsx b/frontend/src/lib-customizations/defaults/components/i18n/sv.tsx index 38581adf49a..f99261d4022 100644 --- a/frontend/src/lib-customizations/defaults/components/i18n/sv.tsx +++ b/frontend/src/lib-customizations/defaults/components/i18n/sv.tsx @@ -28,12 +28,14 @@ const components: Translations = { datePicker: { placeholder: 'dd.mm.åååå', description: - 'Skriv in datumet i formatet dd.mm.åååå. Du kan komma till månadsväljaren med tabbtangenten.', + 'Skriv in datumet i formatet dd.mm.åååå. Du kan komma till månadsväljaren med pil ned-tangenten.', validationErrors: { validDate: 'Ange i format dd.mm.åååå', dateTooEarly: 'Välj ett senare datum', dateTooLate: 'Välj ett tidigare datum' - } + }, + open: 'Öppna datumväljaren', + close: 'Stäng datumväljaren' }, documentTemplates: { // only on employee frontend From 0e8ca1fc61bb8e42ed00a95fc225d02d2d061bca Mon Sep 17 00:00:00 2001 From: Samuli Toivonen Date: Wed, 11 Dec 2024 13:57:19 +0200 Subject: [PATCH 2/3] open date picker from click instead of on focus, a11y fix --- .../molecules/date-picker/DatePickerDay.tsx | 1 + .../molecules/date-picker/DatePickerInput.tsx | 35 +++----------- .../date-picker/DatePickerLowLevel.tsx | 48 ++++++++++++++++--- 3 files changed, 50 insertions(+), 34 deletions(-) diff --git a/frontend/src/lib-components/molecules/date-picker/DatePickerDay.tsx b/frontend/src/lib-components/molecules/date-picker/DatePickerDay.tsx index 692b99b82b1..d0d981d62f8 100644 --- a/frontend/src/lib-components/molecules/date-picker/DatePickerDay.tsx +++ b/frontend/src/lib-components/molecules/date-picker/DatePickerDay.tsx @@ -44,6 +44,7 @@ export default React.memo(function DatePickerDay({ return ( {useBrowserPicker ? ( - + ) : ( - + )} - - {i18n.datePicker.description} - ) }) -interface InternalProps extends Omit { - ariaId: string -} +type InternalProps = Omit const DateInputText = React.memo(function DateInputText({ value, @@ -69,8 +61,7 @@ const DateInputText = React.memo(function DateInputText({ disabled, 'data-qa': dataQa, id, - required, - ariaId + required }: InternalProps) { const i18n = useTranslations() @@ -88,7 +79,7 @@ const DateInputText = React.memo(function DateInputText({ onChange={handleChange} onFocus={onFocus} onBlur={onBlur} - aria-describedby={ariaId} + aria-label={i18n.datePicker.description} info={info} hideErrorsBeforeTouched={hideErrorsBeforeTouched} readonly={disabled} @@ -112,8 +103,7 @@ const DateInputNative = React.memo(function DateInputNative({ id, required, minDate, - maxDate, - ariaId + maxDate }: InternalProps) { const i18n = useTranslations() @@ -140,7 +130,7 @@ const DateInputNative = React.memo(function DateInputNative({ onChangeTarget={handleChange} onFocus={onFocus} onBlur={onBlur} - aria-describedby={ariaId} + aria-label={i18n.datePicker.description} info={info} hideErrorsBeforeTouched={hideErrorsBeforeTouched} readonly={disabled} @@ -154,14 +144,3 @@ const DateInputNative = React.memo(function DateInputNative({ /> ) }) - -const HelpTextForScreenReader = styled.p` - border: 0; - clip: rect(0 0 0 0); - height: 1px; - width: 1px; - margin: -1px; - padding: 0; - overflow: hidden; - position: absolute; -` diff --git a/frontend/src/lib-components/molecules/date-picker/DatePickerLowLevel.tsx b/frontend/src/lib-components/molecules/date-picker/DatePickerLowLevel.tsx index 2484c217759..cb29f4c4e8b 100644 --- a/frontend/src/lib-components/molecules/date-picker/DatePickerLowLevel.tsx +++ b/frontend/src/lib-components/molecules/date-picker/DatePickerLowLevel.tsx @@ -8,6 +8,9 @@ import styled from 'styled-components' import { useBoolean } from 'lib-common/form/hooks' import LocalDate from 'lib-common/local-date' +import { IconOnlyButton } from 'lib-components/atoms/buttons/IconOnlyButton' +import { useTranslations } from 'lib-components/i18n' +import { faCalendarAlt } from 'lib-icons' import { InputInfo } from '../../atoms/form/InputField' import { fontWeights } from '../../typography' @@ -18,11 +21,12 @@ import DatePickerInput from './DatePickerInput' import { nativeDatePickerEnabled } from './helpers' const inputWidth = 120 +const iconWidth = 36 const DatePickerWrapper = styled.div` position: relative; - display: inline-block; - width: ${inputWidth}px; + display: flex; + width: ${inputWidth + iconWidth}px; ` const minMargin = 16 const overflow = 100 @@ -123,6 +127,7 @@ export interface DatePickerLowLevelProps { minDate?: LocalDate maxDate?: LocalDate useBrowserPicker?: boolean + openOnFocus?: boolean } export default React.memo(function DatePickerLowLevel({ @@ -141,7 +146,8 @@ export default React.memo(function DatePickerLowLevel({ minDate, maxDate, useBrowserPicker = nativeDatePickerEnabled, - 'data-qa': dataQa + 'data-qa': dataQa, + openOnFocus = false }: DatePickerLowLevelProps) { const [showDatePicker, useShowDatePicker] = useBoolean(false) const wrapperRef = useRef(null) @@ -150,6 +156,8 @@ export default React.memo(function DatePickerLowLevel({ const showDatePickerOn = useShowDatePicker.on const showDatePickerOff = useShowDatePicker.off + const i18n = useTranslations() + const handleUserKeyPress = useCallback( (e: React.KeyboardEvent) => { if (e.key === 'Esc' || e.key === 'Escape' || e.key === 'Enter') { @@ -159,8 +167,11 @@ export default React.memo(function DatePickerLowLevel({ } showDatePickerOff() } + if (!showDatePicker && e.key === 'ArrowDown') { + showDatePickerOn() + } }, - [showDatePickerOff] + [showDatePicker, showDatePickerOff, showDatePickerOn] ) const handleDayClick = useCallback( @@ -176,10 +187,12 @@ export default React.memo(function DatePickerLowLevel({ const handleFocus = useCallback( (e: React.FocusEvent) => { - showDatePickerOn() + if (openOnFocus) { + showDatePickerOn() + } onFocus?.(e) }, - [onFocus, showDatePickerOn] + [onFocus, openOnFocus, showDatePickerOn] ) const handleBlur = useCallback( @@ -251,6 +264,14 @@ export default React.memo(function DatePickerLowLevel({ return () => undefined }, [showDatePickerOff, showDatePicker]) + const toggleDatePicker = useCallback(() => { + if (showDatePicker) { + showDatePickerOff() + } else { + showDatePickerOn() + } + }, [showDatePicker, showDatePickerOff, showDatePickerOn]) + return ( + {!nativeDatePickerEnabled && showDatePicker ? ( @@ -286,3 +317,8 @@ export default React.memo(function DatePickerLowLevel({ ) }) + +const StyledIconButton = styled(IconOnlyButton)` + width: 100%; + margin: 0 0 0 4px; +` From f711ba824f404195445151b34e68639d46fa80ab Mon Sep 17 00:00:00 2001 From: Samuli Toivonen Date: Wed, 11 Dec 2024 15:58:47 +0200 Subject: [PATCH 3/3] update date picker tests --- .../molecules/date-picker/DatePicker.spec.tsx | 6 ++++-- .../molecules/date-picker/DateRangePicker.spec.tsx | 6 ++++-- frontend/src/lib-components/utils/TestContextProvider.tsx | 4 +++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/frontend/src/lib-components/molecules/date-picker/DatePicker.spec.tsx b/frontend/src/lib-components/molecules/date-picker/DatePicker.spec.tsx index 540b3b96968..59b2332c430 100644 --- a/frontend/src/lib-components/molecules/date-picker/DatePicker.spec.tsx +++ b/frontend/src/lib-components/molecules/date-picker/DatePicker.spec.tsx @@ -26,7 +26,9 @@ const translations: Translations = { validDate: 'Invalid date', dateTooEarly: 'Date too early', dateTooLate: 'Date too late' - } + }, + open: 'Open date picker', + close: 'Close date picker' } } @@ -38,7 +40,7 @@ describe('DatePicker', () => { ) const get = () => - screen.getByRole('textbox', { description: 'Date picker description' }) + screen.getByRole('textbox', { name: 'Date picker description' }) it('attributes', () => { render( diff --git a/frontend/src/lib-components/molecules/date-picker/DateRangePicker.spec.tsx b/frontend/src/lib-components/molecules/date-picker/DateRangePicker.spec.tsx index 3947712fb73..8140dc4a8ba 100644 --- a/frontend/src/lib-components/molecules/date-picker/DateRangePicker.spec.tsx +++ b/frontend/src/lib-components/molecules/date-picker/DateRangePicker.spec.tsx @@ -26,7 +26,9 @@ const translations: Translations = { validDate: 'Invalid date', dateTooEarly: 'Date too early', dateTooLate: 'Date too late' - } + }, + open: 'Open date picker', + close: 'Close date picker' } } @@ -44,7 +46,7 @@ describe('DateRangePicker', () => { ) const [start, end] = screen.getAllByRole('textbox', { - description: 'Date picker description' + name: 'Date picker description' }) expect(start).toHaveAttribute('placeholder', 'Placeholder') expect(end).toHaveAttribute('placeholder', 'Placeholder') diff --git a/frontend/src/lib-components/utils/TestContextProvider.tsx b/frontend/src/lib-components/utils/TestContextProvider.tsx index 898e76899d7..36cfdae7e62 100644 --- a/frontend/src/lib-components/utils/TestContextProvider.tsx +++ b/frontend/src/lib-components/utils/TestContextProvider.tsx @@ -43,7 +43,9 @@ export const testTranslations: Translations = { validDate: '', dateTooEarly: '', dateTooLate: '' - } + }, + open: '', + close: '' }, documentTemplates: { templateQuestions: {