diff --git a/packages/react-core/src/components/Button/examples/ButtonInlineSpanLink.tsx b/packages/react-core/src/components/Button/examples/ButtonInlineSpanLink.tsx index 7e532c196c9..131607eb0c8 100644 --- a/packages/react-core/src/components/Button/examples/ButtonInlineSpanLink.tsx +++ b/packages/react-core/src/components/Button/examples/ButtonInlineSpanLink.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { Button } from '@patternfly/react-core'; +import { Button, KeyTypes } from '@patternfly/react-core'; const handleKeydown = (event: React.KeyboardEvent) => { const { key } = event; - const isEnterKey: boolean = key === 'Enter'; - const isEnterOrSpaceKey: boolean = isEnterKey || key === 'Space'; + const isEnterKey: boolean = key === KeyTypes.Enter; + const isEnterOrSpaceKey: boolean = isEnterKey || key === KeyTypes.Space; if (isEnterOrSpaceKey) { event.preventDefault(); diff --git a/packages/react-core/src/components/Form/FormGroupLabelHelp.tsx b/packages/react-core/src/components/Form/FormGroupLabelHelp.tsx index 05fb26c62b2..0fc16d53865 100644 --- a/packages/react-core/src/components/Form/FormGroupLabelHelp.tsx +++ b/packages/react-core/src/components/Form/FormGroupLabelHelp.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { Button, ButtonProps } from '../Button'; import QuestionCircleIcon from '@patternfly/react-icons/dist/esm/icons/question-circle-icon'; +import { KeyTypes } from '../../helpers/constants'; /** A help button to be passed to the FormGroup's labelHelp property. This should be wrapped or linked * to our Popover component. @@ -10,15 +11,48 @@ export interface FormGroupLabelHelpProps extends ButtonProps { 'aria-label': string; /** Additional classes added to the help button. */ className?: string; + /** @hide Forwarded ref */ + innerRef?: React.Ref; } -export const FormGroupLabelHelp: React.FunctionComponent = ({ +const FormGroupLabelHelpBase: React.FunctionComponent = ({ 'aria-label': ariaLabel, className, + innerRef, ...props -}) => ( - -); +}) => { + const ref = React.useRef(null); + const buttonRef = innerRef || ref; + + const isMutableRef = (ref: React.Ref): ref is React.MutableRefObject => + typeof ref === 'object' && ref !== null && 'current' in ref && ref.current !== undefined; + + const handleKeyDown = (event: React.KeyboardEvent) => { + if ([KeyTypes.Space, KeyTypes.Enter].includes(event.key) && isMutableRef(buttonRef) && buttonRef.current) { + event.preventDefault(); + buttonRef.current.click(); + } + }; + + return ( + + ); +}; + +export const FormGroupLabelHelp = React.forwardRef((props: FormGroupLabelHelpProps, ref: React.Ref) => ( + +)); + FormGroupLabelHelp.displayName = 'FormGroupLabelHelp'; diff --git a/packages/react-core/src/components/Form/examples/FormBasic.tsx b/packages/react-core/src/components/Form/examples/FormBasic.tsx index 4c71a1fdb61..a9e1b420b2e 100644 --- a/packages/react-core/src/components/Form/examples/FormBasic.tsx +++ b/packages/react-core/src/components/Form/examples/FormBasic.tsx @@ -18,6 +18,7 @@ export const FormBasic: React.FunctionComponent = () => { const [name, setName] = React.useState(''); const [email, setEmail] = React.useState(''); const [phone, setPhone] = React.useState(''); + const labelHelpRef = React.useRef(null); const handleNameChange = (_event, name: string) => { setName(name); @@ -37,6 +38,7 @@ export const FormBasic: React.FunctionComponent = () => { label="Full name" labelHelp={ The{' '} @@ -63,7 +65,7 @@ export const FormBasic: React.FunctionComponent = () => { } > - + } isRequired diff --git a/packages/react-core/src/components/Form/examples/FormGroupLabelInfo.tsx b/packages/react-core/src/components/Form/examples/FormGroupLabelInfo.tsx index 3d728b8508c..9491659d507 100644 --- a/packages/react-core/src/components/Form/examples/FormGroupLabelInfo.tsx +++ b/packages/react-core/src/components/Form/examples/FormGroupLabelInfo.tsx @@ -12,6 +12,7 @@ import { export const FormGroupLabelInfo: React.FunctionComponent = () => { const [name, setName] = React.useState(''); + const labelHelpRef = React.useRef(null); const handleNameChange = (_event, name: string) => { setName(name); @@ -24,6 +25,7 @@ export const FormGroupLabelInfo: React.FunctionComponent = () => { labelInfo="Additional label info" labelHelp={ The{' '} @@ -50,7 +52,7 @@ export const FormGroupLabelInfo: React.FunctionComponent = () => { } > - + } isRequired diff --git a/packages/react-core/src/components/Form/examples/FormLimitWidth.tsx b/packages/react-core/src/components/Form/examples/FormLimitWidth.tsx index b5dc906b3f5..11b10786dcc 100644 --- a/packages/react-core/src/components/Form/examples/FormLimitWidth.tsx +++ b/packages/react-core/src/components/Form/examples/FormLimitWidth.tsx @@ -18,6 +18,7 @@ export const FormLimitWidth: React.FunctionComponent = () => { const [name, setName] = React.useState(''); const [email, setEmail] = React.useState(''); const [phone, setPhone] = React.useState(''); + const labelHelpRef = React.useRef(null); const handleNameChange = (_event, name: string) => { setName(name); @@ -37,6 +38,7 @@ export const FormLimitWidth: React.FunctionComponent = () => { label="Full name" labelHelp={ The{' '} @@ -63,7 +65,7 @@ export const FormLimitWidth: React.FunctionComponent = () => { } > - + } isRequired diff --git a/packages/react-core/src/components/Modal/examples/ModalWithForm.tsx b/packages/react-core/src/components/Modal/examples/ModalWithForm.tsx index 7bbd967b56c..a257a414d95 100644 --- a/packages/react-core/src/components/Modal/examples/ModalWithForm.tsx +++ b/packages/react-core/src/components/Modal/examples/ModalWithForm.tsx @@ -15,6 +15,9 @@ export const ModalWithForm: React.FunctionComponent = () => { const [nameValue, setNameValue] = React.useState(''); const [emailValue, setEmailValue] = React.useState(''); const [addressValue, setAddressValue] = React.useState(''); + const nameLabelHelpRef = React.useRef(null); + const emailLabelHelpRef = React.useRef(null); + const addressLabelHelpRef = React.useRef(null); const handleModalToggle = (_event: KeyboardEvent | React.MouseEvent) => { setModalOpen(!isModalOpen); @@ -56,6 +59,7 @@ export const ModalWithForm: React.FunctionComponent = () => { label="Name" labelHelp={ The @@ -82,7 +86,7 @@ export const ModalWithForm: React.FunctionComponent = () => { } > - + } isRequired @@ -101,6 +105,7 @@ export const ModalWithForm: React.FunctionComponent = () => { label="E-mail" labelHelp={ The @@ -123,7 +128,7 @@ export const ModalWithForm: React.FunctionComponent = () => { } > - + } isRequired @@ -142,6 +147,7 @@ export const ModalWithForm: React.FunctionComponent = () => { label="Address" labelHelp={ The @@ -163,7 +169,7 @@ export const ModalWithForm: React.FunctionComponent = () => { } > - + } isRequired diff --git a/packages/react-core/src/demos/SearchInput/SearchInput.md b/packages/react-core/src/demos/SearchInput/SearchInput.md index 8cd5b890b76..e3e5e2ebd44 100644 --- a/packages/react-core/src/demos/SearchInput/SearchInput.md +++ b/packages/react-core/src/demos/SearchInput/SearchInput.md @@ -128,9 +128,9 @@ SearchAutocomplete = () => { event.preventDefault(); // by default, the up and down arrow keys scroll the window // the tab, enter, and space keys will close the menu, and the tab key will move browser // focus forward one element (by default) - } else if (event.key === 'Tab' || event.key === 'Enter' || event.key === 'Space') { + } else if (event.key === 'Tab' || event.key === 'Enter' || event.key === ' ') { setIsAutocompleteOpen(false); - if (event.key === 'Enter' || event.key === 'Space') { + if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); } } diff --git a/packages/react-core/src/demos/examples/PasswordStrength/PasswordStrengthDemo.tsx b/packages/react-core/src/demos/examples/PasswordStrength/PasswordStrengthDemo.tsx index de1d61ccb0d..47d083b5304 100644 --- a/packages/react-core/src/demos/examples/PasswordStrength/PasswordStrengthDemo.tsx +++ b/packages/react-core/src/demos/examples/PasswordStrength/PasswordStrengthDemo.tsx @@ -89,9 +89,15 @@ export const PasswordStrengthDemo: React.FunctionComponent = () => { } }; + const labelHelpRef = React.useRef(null); + const iconPopover = ( - Password Requirements} bodyContent={
Password rules
}> - + Password Requirements} + bodyContent={
Password rules
} + > +
); diff --git a/packages/react-core/src/next/components/Modal/examples/ModalWithForm.tsx b/packages/react-core/src/next/components/Modal/examples/ModalWithForm.tsx index bc7cf11f7a1..b5e23b1d7cc 100644 --- a/packages/react-core/src/next/components/Modal/examples/ModalWithForm.tsx +++ b/packages/react-core/src/next/components/Modal/examples/ModalWithForm.tsx @@ -7,6 +7,9 @@ export const ModalWithForm: React.FunctionComponent = () => { const [nameValue, setNameValue] = React.useState(''); const [emailValue, setEmailValue] = React.useState(''); const [addressValue, setAddressValue] = React.useState(''); + const nameLabelHelpRef = React.useRef(null); + const emailLabelHelpRef = React.useRef(null); + const addressLabelHelpRef = React.useRef(null); const handleModalToggle = (_event: KeyboardEvent | React.MouseEvent) => { setModalOpen(!isModalOpen); @@ -47,6 +50,7 @@ export const ModalWithForm: React.FunctionComponent = () => { label="Name" labelHelp={ The @@ -73,7 +77,7 @@ export const ModalWithForm: React.FunctionComponent = () => { } > - + } isRequired @@ -92,6 +96,7 @@ export const ModalWithForm: React.FunctionComponent = () => { label="E-mail" labelHelp={ The @@ -114,7 +119,7 @@ export const ModalWithForm: React.FunctionComponent = () => { } > - + } isRequired @@ -133,6 +138,7 @@ export const ModalWithForm: React.FunctionComponent = () => { label="Address" labelHelp={ The @@ -154,7 +160,7 @@ export const ModalWithForm: React.FunctionComponent = () => { } > - + } isRequired diff --git a/packages/react-integration/demo-app-ts/src/components/demos/FormDemo/FormDemo.tsx b/packages/react-integration/demo-app-ts/src/components/demos/FormDemo/FormDemo.tsx index 6b2ec6c20d1..66b414c8272 100644 --- a/packages/react-integration/demo-app-ts/src/components/demos/FormDemo/FormDemo.tsx +++ b/packages/react-integration/demo-app-ts/src/components/demos/FormDemo/FormDemo.tsx @@ -43,6 +43,9 @@ export class FormDemo extends Component { this.handleCheckboxChange = this.handleCheckboxChange.bind(this); } + + labelHelpRef: React.RefObject = React.createRef(); + handleTextInputChange = (_event: React.FormEvent, value: string) => { this.setState({ value, isValid: /^\d+$/.test(value) }); }; @@ -114,6 +117,7 @@ export class FormDemo extends Component { labelInfo="Age info" labelHelp={ The age of a person} bodyContent={
@@ -122,7 +126,7 @@ export class FormDemo extends Component {
} > - +
} type="number" diff --git a/packages/react-table/src/components/Table/Table.tsx b/packages/react-table/src/components/Table/Table.tsx index da1a0e88b14..13942db0f2b 100644 --- a/packages/react-table/src/components/Table/Table.tsx +++ b/packages/react-table/src/components/Table/Table.tsx @@ -5,7 +5,7 @@ import stylesTreeView from '@patternfly/react-styles/css/components/Table/table- import { css } from '@patternfly/react-styles'; import { toCamel } from './utils'; import { IVisibility } from './utils/decorators/classNames'; -import { useOUIAProps, OUIAProps, handleArrows, setTabIndex } from '@patternfly/react-core'; +import { useOUIAProps, OUIAProps, handleArrows, setTabIndex, KeyTypes } from '@patternfly/react-core'; import { TableGridBreakpoint, TableVariant } from './TableTypes'; export interface BaseCellProps { @@ -169,7 +169,7 @@ const TableBase: React.FunctionComponent = ({ const rows = (Array.from(tableRef.current.querySelectorAll('tbody tr')) as Element[]).filter( (el) => !el.classList.contains('pf-m-disabled') && !(el as HTMLElement).hidden ); - if (key === 'Space' || key === 'Enter') { + if (key === KeyTypes.Space || key === KeyTypes.Enter) { (activeElement as HTMLElement).click(); event.preventDefault(); }