Skip to content
This repository has been archived by the owner on May 24, 2024. It is now read-only.

Commit

Permalink
[terra-date-picker] [terra-date-time-picker] Fixed DatePicker focus i…
Browse files Browse the repository at this point in the history
…ssues (#2167)
  • Loading branch information
adavijit authored May 22, 2024
1 parent 7ad5f7e commit 283f504
Show file tree
Hide file tree
Showing 23 changed files with 43 additions and 32 deletions.
4 changes: 4 additions & 0 deletions packages/terra-date-picker/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

* Fixed
* Focus is now not moving to the calendar button when navigating using `tab` key.
* Focus is now retained on the calendar button when the calendar is closed in mobile view.

## 4.110.1 - (May 20, 2024)

* Fixed
Expand Down
56 changes: 36 additions & 20 deletions packages/terra-date-picker/src/DatePicker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,6 @@ const propTypes = {
* String that labels the current element. 'aria-label' must be present for accessibility.
*/
ariaLabel: PropTypes.string,
/**
* @private
* Whether or not to disable focus on the calendar button when the calendar picker dismisses.
*/
disableButtonFocusOnClose: PropTypes.bool,
/**
* Whether the date input should be disabled.
*/
Expand Down Expand Up @@ -150,7 +145,6 @@ const propTypes = {
};

const defaultProps = {
disableButtonFocusOnClose: false,
disabled: false,
excludeDates: undefined,
filterDate: undefined,
Expand Down Expand Up @@ -188,6 +182,7 @@ class DatePicker extends React.Component {
this.datePickerContainer = React.createRef();
this.isDefaultDateAcceptable = props.isDefaultDateAcceptable;
this.containerHasFocus = false;
this.inputChanged = false;
this.handleBlur = this.handleBlur.bind(this);
this.handleBreakpointChange = this.handleBreakpointChange.bind(this);
this.handleChange = this.handleChange.bind(this);
Expand Down Expand Up @@ -226,6 +221,12 @@ class DatePicker extends React.Component {
this.isDefaultDateAcceptable = (this.props.isDefaultDateAcceptable) ? this.props.isDefaultDateAcceptable : this.validateDefaultDate();
}

componentWillUnmount() {
if (this.timeoutId) {
clearTimeout(this.timeoutId);
}
}

handleBreakpointChange(activeBreakpoint) {
const showPortalPicker = !this.props.isInline && (activeBreakpoint === 'tiny' || activeBreakpoint === 'small');

Expand All @@ -248,7 +249,7 @@ class DatePicker extends React.Component {
// as well as manually entering a valid date or clearing the date,
// Until a fix is made, we need to return if the event type is 'change' or 'keydown' indicating that onSelect was
// invoked from a manual change. See https://github.com/Hacker0x01/react-datepicker/issues/990
if (event.type === 'change' || event.type === 'keydown' || !selectedDate || !selectedDate.isValid()) {
if (event.type === 'change' || event.type === 'keydown' || !selectedDate || !selectedDate.isValid() || event.type === 'blur') {
return;
}

Expand All @@ -259,25 +260,17 @@ class DatePicker extends React.Component {
this.props.onSelect(event, selectedDate.format(DateUtil.ISO_EXTENDED_DATE_FORMAT));
}

if (!this.props.disableButtonFocusOnClose) {
// Allows time for focus-trap to release focus on the picker before returning focus to the calendar button.
setTimeout(() => {
/*
* Make sure the reference to calendarButton still exists before calling focus because it is possible that it is now
* nullified after the 100 ms timeout due to a force remount of this component with a new `key` prop value.
* Reference https://github.com/cerner/terra-framework/issues/1086
*/
if (this.calendarButton) {
this.calendarButton.focus();
}
}, 100);
// If the portal picker is displayed, focus on the calendar button on selecting a date
if (this.state.showPortalPicker) {
this.focusCalendarButton();
}
}

handleOnClickOutside(event) {
if (this.props.onClickOutside) {
this.props.onClickOutside(event);
}
this.focusCalendarButton();
}

handleBlur(event) {
Expand All @@ -287,6 +280,9 @@ class DatePicker extends React.Component {

// Handle blur only if focus has moved out of the entire date picker component.
if (!this.datePickerContainer.current.contains(activeTarget)) {
this.focusCalendarButton();
this.inputChanged = false;

if (this.props.onBlur) {
const metadata = this.getMetadata();
this.props.onBlur(event, metadata);
Expand All @@ -297,6 +293,8 @@ class DatePicker extends React.Component {
}

handleChange(date, event, value) {
this.inputChanged = true;

if (event.type === 'change') {
this.dateValue = value;
}
Expand Down Expand Up @@ -407,10 +405,28 @@ class DatePicker extends React.Component {
return isAcceptable;
}

focusCalendarButton() {
// Clear any existing timeout before setting a new one
if (this.timeoutId) {
clearTimeout(this.timeoutId);
}
// Allows time for focus-trap to release focus on the picker before returning focus to the calendar button.
this.timeoutId = setTimeout(() => {
/*
* Make sure the reference to calendarButton still exists before calling focus because it is possible that it is now
* nullified after the 100 ms timeout due to a force remount of this component with a new `key` prop value.
* Reference https://github.com/cerner/terra-framework/issues/1086
*/

if (this.calendarButton && this.inputChanged) {
this.calendarButton.focus();
}
}, 100);
}

render() {
const {
ariaLabel,
disableButtonFocusOnClose,
excludeDates,
filterDate,
includeDates,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
exports[`should render a calendar filter with default date 1`] = `
<DatePicker
calendarClassName="calendar-filter"
disableButtonFocusOnClose={false}
disabled={false}
errorId=""
intl={
Expand Down Expand Up @@ -49,7 +48,6 @@ exports[`should render a calendar filter with default date 1`] = `
exports[`should render a calendar filter with excluded dates 1`] = `
<DatePicker
calendarClassName="calendar-filter"
disableButtonFocusOnClose={false}
disabled={false}
errorId=""
excludeDates={
Expand Down Expand Up @@ -99,7 +97,6 @@ exports[`should render a calendar filter with excluded dates 1`] = `
exports[`should render a calendar filter with filtered dates 1`] = `
<DatePicker
calendarClassName="calendar-filter"
disableButtonFocusOnClose={false}
disabled={false}
errorId=""
filterDate={[Function]}
Expand Down Expand Up @@ -145,7 +142,6 @@ exports[`should render a calendar filter with filtered dates 1`] = `
exports[`should render a calendar filter with included dates 1`] = `
<DatePicker
calendarClassName="calendar-filter"
disableButtonFocusOnClose={false}
disabled={false}
errorId=""
includeDates={
Expand Down Expand Up @@ -195,7 +191,6 @@ exports[`should render a calendar filter with included dates 1`] = `
exports[`should render a calendar filter with min and max dates 1`] = `
<DatePicker
calendarClassName="calendar-filter"
disableButtonFocusOnClose={false}
disabled={false}
errorId=""
intl={
Expand Down Expand Up @@ -240,7 +235,6 @@ exports[`should render a calendar filter with min and max dates 1`] = `
exports[`should render a calendar filter with onChange 1`] = `
<DatePicker
calendarClassName="calendar-filter"
disableButtonFocusOnClose={false}
disabled={false}
errorId=""
intl={
Expand Down Expand Up @@ -286,7 +280,6 @@ exports[`should render a calendar filter with onChange 1`] = `
exports[`should render a controlled calendar filter 1`] = `
<DatePicker
calendarClassName="calendar-filter"
disableButtonFocusOnClose={false}
disabled={false}
errorId=""
intl={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ exports[`correctly applies the theme context className 1`] = `
name="date-input"
>
<DatePicker
disableButtonFocusOnClose={false}
disabled={false}
errorId=""
intl={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,6 @@ exports[`should render a DatePickerField with props 1`] = `
>
<DatePicker
ariaLabel="Label Test"
disableButtonFocusOnClose={false}
disabled={true}
errorId=""
excludeDates={
Expand Down Expand Up @@ -1195,7 +1194,6 @@ exports[`should render a default DatePickerField component 1`] = `
>
<DatePicker
ariaLabel="Label"
disableButtonFocusOnClose={false}
disabled={false}
errorId=""
id="test-date-picker"
Expand Down Expand Up @@ -2174,7 +2172,6 @@ exports[`should render a valid DatePickerField with props 1`] = `
>
<DatePicker
ariaLabel="Label Test"
disableButtonFocusOnClose={false}
disabled={true}
errorId=""
excludeDates={
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/terra-date-time-picker/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

* Removed
* Removed passing `disableButtonFocusOnClose` prop to the `DatePicker` Component.

## 4.118.1 - (May 20, 2024)

* Changed
Expand Down
1 change: 0 additions & 1 deletion packages/terra-date-time-picker/src/DateTimePicker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,6 @@ class DateTimePicker extends React.Component {
value={dateValue}
name="input"
disabled={disabled}
disableButtonFocusOnClose
isIncomplete={isIncomplete}
isInvalid={isInvalid}
required={required}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 283f504

Please sign in to comment.