From ccfcc0e86d0d2546d8dfa878ce19dc5cf502be30 Mon Sep 17 00:00:00 2001 From: Eric Olkowski Date: Mon, 29 Jul 2024 11:46:35 -0400 Subject: [PATCH 1/6] feat(TextInputGroup): added validation support --- .../TextInputGroup/TextInputGroup.tsx | 6 ++++- .../TextInputGroup/TextInputGroupIcon.tsx | 25 +++++++++++++++++++ .../TextInputGroup/TextInputGroupMain.tsx | 8 ++++-- 3 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 packages/react-core/src/components/TextInputGroup/TextInputGroupIcon.tsx diff --git a/packages/react-core/src/components/TextInputGroup/TextInputGroup.tsx b/packages/react-core/src/components/TextInputGroup/TextInputGroup.tsx index 1df27886894..9aada1cf24b 100644 --- a/packages/react-core/src/components/TextInputGroup/TextInputGroup.tsx +++ b/packages/react-core/src/components/TextInputGroup/TextInputGroup.tsx @@ -11,6 +11,8 @@ export interface TextInputGroupProps extends React.HTMLProps { isDisabled?: boolean; /** Flag to indicate the toggle has no border or background */ isPlain?: boolean; + /** Status variant of the text input group. */ + status?: 'success' | 'warning' | 'error'; /** @hide A reference object to attach to the input box */ innerRef?: React.RefObject; } @@ -24,6 +26,7 @@ export const TextInputGroup: React.FunctionComponent = ({ className, isDisabled, isPlain, + status, innerRef, ...props }: TextInputGroupProps) => { @@ -31,13 +34,14 @@ export const TextInputGroup: React.FunctionComponent = ({ const textInputGroupRef = innerRef || ref; return ( - +
{ + /** Content rendered inside the text input group utilities div */ + children?: React.ReactNode; + /** Additional classes applied to the text input group utilities container */ + className?: string; + /** Flag indicating if the icon is a status icon and should inherit status styling. */ + isStatus?: boolean; +} + +export const TextInputGroupIcon: React.FunctionComponent = ({ + children, + className, + isStatus, + ...props +}: TextInputGroupIconProps) => ( + + {children} + +); + +TextInputGroupIcon.displayName = 'TextInputGroupIcon'; diff --git a/packages/react-core/src/components/TextInputGroup/TextInputGroupMain.tsx b/packages/react-core/src/components/TextInputGroup/TextInputGroupMain.tsx index 268dab37d7e..a77ddec15b1 100644 --- a/packages/react-core/src/components/TextInputGroup/TextInputGroupMain.tsx +++ b/packages/react-core/src/components/TextInputGroup/TextInputGroupMain.tsx @@ -2,6 +2,8 @@ import * as React from 'react'; import styles from '@patternfly/react-styles/css/components/TextInputGroup/text-input-group'; import { css } from '@patternfly/react-styles'; import { TextInputGroupContext } from './TextInputGroup'; +import { TextInputGroupIcon } from './TextInputGroupIcon'; +import { statusIcons, ValidatedOptions } from '../../helpers'; export interface TextInputGroupMainProps extends Omit, 'onChange'> { /** Content rendered inside the text input group main div */ @@ -78,9 +80,10 @@ const TextInputGroupMainBase: React.FunctionComponent = inputId, ...props }: TextInputGroupMainProps) => { - const { isDisabled } = React.useContext(TextInputGroupContext); + const { isDisabled, status } = React.useContext(TextInputGroupContext); const ref = React.useRef(null); const textInputGroupInputInputRef = innerRef || ref; + const StatusIcon = statusIcons[status === ValidatedOptions.error ? 'danger' : status]; const handleChange = (event: React.FormEvent) => { onChange(event, event.currentTarget.value); @@ -100,7 +103,7 @@ const TextInputGroupMainBase: React.FunctionComponent = id={inputId} /> )} - {icon && {icon}} + {icon && {icon}} = {...(isExpanded !== undefined && { 'aria-expanded': isExpanded })} {...(ariaControls && { 'aria-controls': ariaControls })} /> + {status && {}
); From 45577b728d6c7b053f056f08506258b298806200 Mon Sep 17 00:00:00 2001 From: Eric Olkowski Date: Mon, 29 Jul 2024 14:10:30 -0400 Subject: [PATCH 2/6] Added example --- .../TextInputGroup/examples/TextInputGroup.md | 12 ++++ .../examples/TextInputGroupValidation.tsx | 64 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 packages/react-core/src/components/TextInputGroup/examples/TextInputGroupValidation.tsx diff --git a/packages/react-core/src/components/TextInputGroup/examples/TextInputGroup.md b/packages/react-core/src/components/TextInputGroup/examples/TextInputGroup.md index 0dbf2094410..22d6193021a 100644 --- a/packages/react-core/src/components/TextInputGroup/examples/TextInputGroup.md +++ b/packages/react-core/src/components/TextInputGroup/examples/TextInputGroup.md @@ -13,19 +13,31 @@ import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon'; ### Basic ```ts file="./TextInputGroupBasic.tsx" + ``` ### Disabled ```ts file="./TextInputGroupDisabled.tsx" + ``` ### Utilities and icon ```ts file="./TextInputGroupUtilitiesAndIcon.tsx" + +``` + +### With validation + +You can add validation to a `` by passing the `status` property with a value of either "success", "warning", or "error". + +```ts file="./TextInputGroupValidation.tsx" + ``` ### Filters ```ts file="./TextInputGroupFilters.tsx" + ``` diff --git a/packages/react-core/src/components/TextInputGroup/examples/TextInputGroupValidation.tsx b/packages/react-core/src/components/TextInputGroup/examples/TextInputGroupValidation.tsx new file mode 100644 index 00000000000..7ad87b95134 --- /dev/null +++ b/packages/react-core/src/components/TextInputGroup/examples/TextInputGroupValidation.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { + TextInputGroup, + TextInputGroupMain, + TextInputGroupUtilities, + Button, + ValidatedOptions, + Flex, + FlexItem +} from '@patternfly/react-core'; +import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon'; +import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon'; + +export const TextInputGroupValidation: React.FunctionComponent = () => { + const [successValue, setSuccessValue] = React.useState('Success validation'); + const [warningValue, setWarningValue] = React.useState('Warning validation with icon at start'); + const [errorValue, setErrorValue] = React.useState('Error validation with icon at start and utilities'); + + /** show the input clearing button only when the input is not empty */ + const showClearButton = !!errorValue; + + /** render the utilities component only when a component it contains is being rendered */ + const showUtilities = showClearButton; + + /** callback for clearing the text input */ + const clearInput = () => { + setErrorValue(''); + }; + + return ( + + + + setSuccessValue(value)} /> + + + + + } + value={warningValue} + onChange={(_event, value) => setWarningValue(value)} + /> + + + + + } + value={errorValue} + onChange={(_event, value) => setErrorValue(value)} + /> + {showUtilities && ( + + {showClearButton && ( +