From 413a50ec77f8cf9d86c6981a2f7b60b73c72314c Mon Sep 17 00:00:00 2001 From: Arkadiy Stepanov Date: Tue, 30 Jul 2024 13:33:21 +0200 Subject: [PATCH 1/3] Add possibility to navigate year picker in calendar --- .../Datepicker/calendars/Calendar.tsx | 4 ++++ .../Datepicker/calendars/CalendarGrid.tsx | 3 +++ .../Datepicker/calendars/CalendarHeader.tsx | 19 ++++++++++++++---- .../Datepicker/calendars/RangeCalendar.tsx | 4 ++++ .../Datepicker/calendars/YearGrid.tsx | 20 ++++++++++++++++--- 5 files changed, 43 insertions(+), 7 deletions(-) diff --git a/packages/eds-core-react/src/components/Datepicker/calendars/Calendar.tsx b/packages/eds-core-react/src/components/Datepicker/calendars/Calendar.tsx index ad33b82837..bc22e309ff 100644 --- a/packages/eds-core-react/src/components/Datepicker/calendars/Calendar.tsx +++ b/packages/eds-core-react/src/components/Datepicker/calendars/Calendar.tsx @@ -37,6 +37,8 @@ export const Calendar = forwardRef( ref: RefObject, ) => { const [showYearPicker, setShowYearPicker] = useState(false) + const [yearPickerPage, setYearPickerPage] = useState(0) + const { locale } = useLocale() const calendarState = useCalendarState({ ...props, @@ -74,6 +76,7 @@ export const Calendar = forwardRef( nextMonthDisabled={nextButtonProps.isDisabled} setShowYearPicker={setShowYearPicker} showYearPicker={showYearPicker} + setYearPickerPage={setYearPickerPage} /> )} @@ -82,6 +85,7 @@ export const Calendar = forwardRef( state={calendarState} setShowYearPicker={setShowYearPicker} showYearPicker={showYearPicker} + yearPickerPage={yearPickerPage} /> {Footer && ( diff --git a/packages/eds-core-react/src/components/Datepicker/calendars/CalendarGrid.tsx b/packages/eds-core-react/src/components/Datepicker/calendars/CalendarGrid.tsx index 93e88e9cfe..b1450e2d25 100644 --- a/packages/eds-core-react/src/components/Datepicker/calendars/CalendarGrid.tsx +++ b/packages/eds-core-react/src/components/Datepicker/calendars/CalendarGrid.tsx @@ -12,11 +12,13 @@ export function CalendarGrid({ state, showYearPicker, setShowYearPicker, + yearPickerPage, ...props }: { state: CalendarState | RangeCalendarState showYearPicker: boolean setShowYearPicker: (showYearPicker: boolean) => void + yearPickerPage: number } & AriaCalendarGridProps) { const { locale } = useLocale() const { gridProps, headerProps, weekDays } = useCalendarGrid( @@ -35,6 +37,7 @@ export function CalendarGrid({ state.setFocusedDate(state.focusedDate.set({ year })) setShowYearPicker(false) }} + yearPickerPage={yearPickerPage} /> ) : ( diff --git a/packages/eds-core-react/src/components/Datepicker/calendars/CalendarHeader.tsx b/packages/eds-core-react/src/components/Datepicker/calendars/CalendarHeader.tsx index 478d740be6..1080e58b60 100644 --- a/packages/eds-core-react/src/components/Datepicker/calendars/CalendarHeader.tsx +++ b/packages/eds-core-react/src/components/Datepicker/calendars/CalendarHeader.tsx @@ -10,6 +10,7 @@ import { } from '@equinor/eds-icons' import { CalendarDate } from '@internationalized/date' import { tokens } from '@equinor/eds-tokens' +import { Dispatch, SetStateAction } from 'react' const HeaderWrapper = styled.div` display: flex; @@ -63,6 +64,7 @@ export function CalendarHeader({ nextMonthDisabled, showYearPicker, setShowYearPicker, + setYearPickerPage, }: { state: CalendarState | RangeCalendarState title: string @@ -70,6 +72,7 @@ export function CalendarHeader({ nextMonthDisabled?: boolean showYearPicker: boolean setShowYearPicker: (showYearPicker: boolean) => void + setYearPickerPage?: Dispatch> }) { return ( @@ -77,8 +80,12 @@ export function CalendarHeader({ @@ -103,8 +110,12 @@ export function CalendarHeader({
diff --git a/packages/eds-core-react/src/components/Datepicker/calendars/RangeCalendar.tsx b/packages/eds-core-react/src/components/Datepicker/calendars/RangeCalendar.tsx index 557ff8df10..2ea50b79fb 100644 --- a/packages/eds-core-react/src/components/Datepicker/calendars/RangeCalendar.tsx +++ b/packages/eds-core-react/src/components/Datepicker/calendars/RangeCalendar.tsx @@ -76,6 +76,7 @@ export const RangeCalendar = forwardRef( setShowYearPicker={setShowYearPicker} showYearPicker={showYearPicker} yearPickerPage={yearPickerPage} + setYearPickerPage={setYearPickerPage} /> {Footer && ( diff --git a/packages/eds-core-react/src/components/Datepicker/calendars/YearGrid.tsx b/packages/eds-core-react/src/components/Datepicker/calendars/YearGrid.tsx index e204c40ab7..9a9e81682d 100644 --- a/packages/eds-core-react/src/components/Datepicker/calendars/YearGrid.tsx +++ b/packages/eds-core-react/src/components/Datepicker/calendars/YearGrid.tsx @@ -3,7 +3,13 @@ import styled from 'styled-components' import { tokens } from '@equinor/eds-tokens' import { FocusScope, useFocusManager } from 'react-aria' -import { KeyboardEvent } from 'react' +import { + Dispatch, + KeyboardEvent, + SetStateAction, + useEffect, + useRef, +} from 'react' const Grid = styled.div` display: grid; @@ -45,26 +51,67 @@ const GridFocusManager = ({ year: selectedYear, setFocusedYear, yearPickerPage, + setYearPickerPage, }: { setFocusedYear: (year: number) => void year: number yearPickerPage?: number + setYearPickerPage?: Dispatch> }) => { const focusManager = useFocusManager() - const onKeyDown = (e: KeyboardEvent) => { + + const prevYear = useRef() + const page = yearPickerPage * TOTAL_VISIBLE_YEARS + + const years = Array.from( + { length: TOTAL_VISIBLE_YEARS }, + (_, i) => i + (selectedYear + page - RANGE_OFFSET), + ) + + useEffect(() => { + if (prevYear.current === undefined) { + prevYear.current = yearPickerPage + return + } + + yearPickerPage > prevYear.current + ? focusManager.focusFirst() + : focusManager.focusLast() + + prevYear.current = yearPickerPage + }, [yearPickerPage, focusManager]) + + const onKeyDown = (year: number) => (e: KeyboardEvent) => { const target = e.currentTarget const parent = target.parentElement as HTMLDivElement + + const isFirstYear = years.at(0) === year + const isLastYear = years.at(-1) === year + + switch (e.key) { case 'ArrowRight': e.preventDefault() + if (isLastYear) { + setYearPickerPage((page) => page + 1) + break + } focusManager.focusNext({ wrap: true }) break case 'ArrowLeft': e.preventDefault() + if (isFirstYear) { + setYearPickerPage((page) => page - 1) + break + } focusManager.focusPrevious({ wrap: true }) break case 'ArrowDown': { e.preventDefault() + if (isLastYear) { + setYearPickerPage((page) => page + 1) + break + } const selfIndex = Array.from(parent.children).indexOf(target) const focusElement = Array.from(parent.children).at(selfIndex + 5) focusManager.focusNext({ from: focusElement }) @@ -72,6 +119,10 @@ const GridFocusManager = ({ } case 'ArrowUp': { e.preventDefault() + if (isFirstYear) { + setYearPickerPage((page) => page - 1) + break + } const selfIndex = Array.from(parent.children).indexOf(target) const focusElement = Array.from(parent.children).at(selfIndex - 5) focusManager.focusPrevious({ from: focusElement }) @@ -80,16 +131,10 @@ const GridFocusManager = ({ } } - const page = yearPickerPage * TOTAL_VISIBLE_YEARS - - const years = Array.from( - { length: TOTAL_VISIBLE_YEARS }, - (_, i) => i + (selectedYear + page - RANGE_OFFSET), - ) return years.map((year) => ( setFocusedYear(year)} aria-label={`Set year to ${year}`} tabIndex={0} @@ -104,10 +149,12 @@ export const YearGrid = ({ setFocusedYear, year: selectedYear, yearPickerPage, + setYearPickerPage, }: { setFocusedYear: (year: number) => void year: number yearPickerPage: number + setYearPickerPage?: Dispatch> }) => { return ( @@ -116,6 +163,7 @@ export const YearGrid = ({ year={selectedYear} setFocusedYear={setFocusedYear} yearPickerPage={yearPickerPage} + setYearPickerPage={setYearPickerPage} /> From 0c46d0b3df3d03421f54a2387df533690b21bddc Mon Sep 17 00:00:00 2001 From: Arkadiy Stepanov Date: Mon, 26 Aug 2024 10:00:44 +0200 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=9A=A7=20Fix=20focus=20on=20button=20?= =?UTF-8?q?navigation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/Datepicker/calendars/YearGrid.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/eds-core-react/src/components/Datepicker/calendars/YearGrid.tsx b/packages/eds-core-react/src/components/Datepicker/calendars/YearGrid.tsx index 9a9e81682d..3c49ba23f9 100644 --- a/packages/eds-core-react/src/components/Datepicker/calendars/YearGrid.tsx +++ b/packages/eds-core-react/src/components/Datepicker/calendars/YearGrid.tsx @@ -61,6 +61,8 @@ const GridFocusManager = ({ const focusManager = useFocusManager() const prevYear = useRef() + const navByKeyboard = useRef(false) + const page = yearPickerPage * TOTAL_VISIBLE_YEARS const years = Array.from( @@ -74,6 +76,13 @@ const GridFocusManager = ({ return } + if (!navByKeyboard.current) { + focusManager.focusFirst() + return + } + + navByKeyboard.current = false + yearPickerPage > prevYear.current ? focusManager.focusFirst() : focusManager.focusLast() @@ -88,11 +97,11 @@ const GridFocusManager = ({ const isFirstYear = years.at(0) === year const isLastYear = years.at(-1) === year - switch (e.key) { case 'ArrowRight': e.preventDefault() if (isLastYear) { + navByKeyboard.current = true setYearPickerPage((page) => page + 1) break } @@ -101,6 +110,7 @@ const GridFocusManager = ({ case 'ArrowLeft': e.preventDefault() if (isFirstYear) { + navByKeyboard.current = true setYearPickerPage((page) => page - 1) break } @@ -109,6 +119,7 @@ const GridFocusManager = ({ case 'ArrowDown': { e.preventDefault() if (isLastYear) { + navByKeyboard.current = true setYearPickerPage((page) => page + 1) break } @@ -120,6 +131,7 @@ const GridFocusManager = ({ case 'ArrowUp': { e.preventDefault() if (isFirstYear) { + navByKeyboard.current = true setYearPickerPage((page) => page - 1) break }