diff --git a/packages/react-core/src/components/CalendarMonth/CalendarMonth.tsx b/packages/react-core/src/components/CalendarMonth/CalendarMonth.tsx
index c49cc6b715e..de4cb47d7f4 100644
--- a/packages/react-core/src/components/CalendarMonth/CalendarMonth.tsx
+++ b/packages/react-core/src/components/CalendarMonth/CalendarMonth.tsx
@@ -180,11 +180,8 @@ export const CalendarMonth = ({
useEffect(() => {
// Calendar month should not be focused on page load
- // Datepicker should place focus in calendar month when opened
if ((shouldFocus || isDateFocused) && focusedDateValidated && focusRef.current) {
focusRef.current.focus();
- } else {
- setShouldFocus(true);
}
}, [focusedDate, isDateFocused, focusedDateValidated, focusRef]);
diff --git a/packages/react-core/src/components/DatePicker/DatePicker.tsx b/packages/react-core/src/components/DatePicker/DatePicker.tsx
index 4ab92e187dc..089b03eabb6 100644
--- a/packages/react-core/src/components/DatePicker/DatePicker.tsx
+++ b/packages/react-core/src/components/DatePicker/DatePicker.tsx
@@ -2,6 +2,7 @@ import * as React from 'react';
import { css } from '@patternfly/react-styles';
import styles from '@patternfly/react-styles/css/components/DatePicker/date-picker';
import buttonStyles from '@patternfly/react-styles/css/components/Button/button';
+import calendarMonthStyles from '@patternfly/react-styles/css/components/CalendarMonth/calendar-month';
import { TextInput, TextInputProps } from '../TextInput/TextInput';
import { Popover, PopoverProps } from '../Popover/Popover';
import { InputGroup, InputGroupItem } from '../InputGroup';
@@ -214,9 +215,15 @@ const DatePickerBase = (
[setPopoverOpen, popoverOpen, selectOpen]
);
+ const createFocusSelectorString = (modifierClass: string) =>
+ `.${calendarMonthStyles.calendarMonthDatesCell}.${modifierClass} .${calendarMonthStyles.calendarMonthDate}`;
+ const focusSelectorForSelectedDate = createFocusSelectorString(calendarMonthStyles.modifiers.selected);
+ const focusSelectorForUnselectedDate = createFocusSelectorString(calendarMonthStyles.modifiers.current);
+
return (
}
showClose={false}
diff --git a/packages/react-core/src/components/Popover/Popover.tsx b/packages/react-core/src/components/Popover/Popover.tsx
index 2ce5c627a4f..5a9edfecccd 100644
--- a/packages/react-core/src/components/Popover/Popover.tsx
+++ b/packages/react-core/src/components/Popover/Popover.tsx
@@ -78,6 +78,10 @@ export interface PopoverProps {
closeBtnAriaLabel?: string;
/** Distance of the popover to its target. Defaults to 25. */
distance?: number;
+ /** The element to focus when the popover becomes visible. By default the first
+ * focusable element will receive focus.
+ */
+ elementToFocus?: HTMLElement | SVGElement | string;
/**
* If true, tries to keep the popover in view by flipping it if necessary.
* If the position is set to 'auto', this prop is ignored.
@@ -269,6 +273,7 @@ export const Popover: React.FunctionComponent = ({
triggerRef,
hasNoPadding = false,
hasAutoWidth = false,
+ elementToFocus,
...rest
}: PopoverProps) => {
// could make this a prop in the future (true | false | 'toggle')
@@ -285,7 +290,7 @@ export const Popover: React.FunctionComponent = ({
React.useEffect(() => {
if (triggerManually) {
if (isVisible) {
- show();
+ show(undefined, true);
} else {
hide();
}
@@ -404,6 +409,7 @@ export const Popover: React.FunctionComponent = ({
hide(event);
}
};
+
const content = (
= ({
focusTrapOptions={{
returnFocusOnDeactivate: true,
clickOutsideDeactivates: true,
+ // FocusTrap's initialFocus can accept false as a value to prevent initial focus.
+ // We want to prevent this in case false is ever passed in.
+ initialFocus: elementToFocus || undefined,
+ checkCanFocusTrap: (containers) =>
+ new Promise((resolve) => {
+ const interval = setInterval(() => {
+ if (containers.every((container) => getComputedStyle(container).visibility !== 'hidden')) {
+ resolve();
+ clearInterval(interval);
+ }
+ }, 10);
+ }),
tabbableOptions: { displayCheck: 'none' },
fallbackFocus: () => {
diff --git a/packages/react-core/src/components/Popover/examples/Popover.md b/packages/react-core/src/components/Popover/examples/Popover.md
index 3eb2be0243e..66cb0855941 100644
--- a/packages/react-core/src/components/Popover/examples/Popover.md
+++ b/packages/react-core/src/components/Popover/examples/Popover.md
@@ -89,3 +89,11 @@ Here the popover goes over the navigation, so the prop `appendTo` is set to the
```ts file="./PopoverAlert.tsx"
```
+
+### Custom focus
+
+Use the `elementToFocus` property to customize which element inside the Popover receives focus when opened.
+
+```ts file="./PopoverCustomFocus.tsx"
+
+```
diff --git a/packages/react-core/src/components/Popover/examples/PopoverCustomFocus.tsx b/packages/react-core/src/components/Popover/examples/PopoverCustomFocus.tsx
new file mode 100644
index 00000000000..5bf84ffdadf
--- /dev/null
+++ b/packages/react-core/src/components/Popover/examples/PopoverCustomFocus.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import { Popover, Button } from '@patternfly/react-core';
+
+export const PopoverCustomFocus: React.FunctionComponent = () => (
+ Popover header
}
+ bodyContent={
+
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit.{' '}
+
+