From 344d4f91f2de54da6be57d9ebc5104571ae409aa Mon Sep 17 00:00:00 2001 From: JD Francis Date: Mon, 16 Oct 2023 16:11:35 -0500 Subject: [PATCH] make aria label required + fix assistive text size --- .../components/input/TextInput/HelperText.tsx | 12 +- .../components/input/TextInput/TextInput.tsx | 284 +++++++++--------- 2 files changed, 150 insertions(+), 146 deletions(-) diff --git a/packages/harmony/src/components/input/TextInput/HelperText.tsx b/packages/harmony/src/components/input/TextInput/HelperText.tsx index 48ad822602a..906a1480b3f 100644 --- a/packages/harmony/src/components/input/TextInput/HelperText.tsx +++ b/packages/harmony/src/components/input/TextInput/HelperText.tsx @@ -1,19 +1,25 @@ import type { ReactNode } from 'react' -import { Text } from 'components/typography' +import { Text, TextSize } from 'components/typography' import styles from './HelperText.module.css' type HelperTextProps = { children: ReactNode hasError?: boolean + size?: TextSize } export const HelperText = (props: HelperTextProps) => { - const { children, hasError } = props + const { children, hasError, size = 's' } = props + console.log(size) return (
- + {children}
diff --git a/packages/harmony/src/components/input/TextInput/TextInput.tsx b/packages/harmony/src/components/input/TextInput/TextInput.tsx index 208463d27c7..a06d6a7efea 100644 --- a/packages/harmony/src/components/input/TextInput/TextInput.tsx +++ b/packages/harmony/src/components/input/TextInput/TextInput.tsx @@ -3,7 +3,7 @@ import { forwardRef, type ComponentPropsWithoutRef } from 'react' import cn from 'classnames' import layoutStyles from 'components/layout/layout.module.css' -import { IconComponent, Text } from 'components/typography' +import { IconComponent, Text, TextSize } from 'components/typography' import { HelperText } from './HelperText' import styles from './TextInput.module.css' @@ -71,155 +71,153 @@ export type TextInputProps = Omit< required?: boolean } & ( // Make either label or aria-label required prop | { - /** - * Label Text (Required if aria-label is not provided) - */ - label: string - ['aria-label']?: string - } + /** + * Label Text (Required if aria-label is not provided) + */ + label: string + ['aria-label']?: string + } | { - /** - * Label Text (Required if aria-label is not provided) - */ - ['aria-label']: string - label?: string - } + /** + * Label Text (Required if aria-label is not provided) + */ + ['aria-label']: string + label?: string + } ) -const TextInput = forwardRef( - (props: TextInputProps, ref) => { - const { - required, - className, - inputRootClassName, - maxLength, - showMaxLength, - size = TextInputSize.DEFAULT, - hideLabel, - label: labelProp, - value, - children, - warning: warningProp, - error, - className: inputClassName, - disabled, - onFocus: onFocusProp, - onBlur: onBlurProp, - placeholder, - helperText, - startAdornmentText, - endAdornmentText, - startIcon: StartIcon, - endIcon: EndIcon, - ...other - } = props - - const characterCount = value !== undefined ? `${value}`.length : 0 - const nearCharacterLimit = maxLength && characterCount >= 0.9 * maxLength - - // Hide the label when: requested, when the size is small, or when adornment text is present - const shouldShowLabel = - !hideLabel && - size !== TextInputSize.SMALL && - endAdornmentText === undefined && - startAdornmentText === undefined - - const label = required ? `${labelProp} *` : labelProp - - /** - * Since Firefox doesn't support the :has() pseudo selector, - * manually track the focused state and use classes for focus, required, and disabled - */ - const [isFocused, handleFocus, handleBlur] = useFocusState( - onFocusProp, - onBlurProp - ) - - // Whenever a label isn't visible the placeholder shows in it's place - const shouldShowPlaceholder = isFocused || !shouldShowLabel - - const style = { - [styles.default]: size === TextInputSize.DEFAULT, - [styles.small]: size === TextInputSize.SMALL, - [styles.warning]: warningProp || nearCharacterLimit, - [styles.error]: error, - [styles.focused]: isFocused, - [styles.disabled]: disabled, - [styles.required]: required - } - - const inputRender = ( -
-
- {startAdornmentText ? ( - - {startAdornmentText} - - ) : null} - -
- {endAdornmentText ? ( +const TextInput = forwardRef((props, ref) => { + const { + required, + className, + inputRootClassName, + maxLength, + showMaxLength, + size = TextInputSize.DEFAULT, + hideLabel, + label: labelProp, + value, + children, + warning: warningProp, + error, + className: inputClassName, + disabled, + onFocus: onFocusProp, + onBlur: onBlurProp, + placeholder, + helperText, + startAdornmentText, + endAdornmentText, + startIcon: StartIcon, + endIcon: EndIcon, + ...other + } = props + + const characterCount = value !== undefined ? `${value}`.length : 0 + const nearCharacterLimit = maxLength && characterCount >= 0.9 * maxLength + + // Hide the label when requested or when the size is set to small + const shouldShowLabel = !hideLabel && size !== TextInputSize.SMALL + const label = required ? `${labelProp} *` : labelProp + + const helperTextSize: TextSize = size === TextInputSize.SMALL ? 'xs' : 's' + + /** + * Since Firefox doesn't support the :has() pseudo selector, + * manually track the focused state and use classes for focus, required, and disabled + */ + const [isFocused, handleFocus, handleBlur] = useFocusState( + onFocusProp, + onBlurProp + ) + + // Whenever a label isn't visible the placeholder shows in it's place + const shouldShowPlaceholder = isFocused || !shouldShowLabel + + const style = { + [styles.default]: size === TextInputSize.DEFAULT, + [styles.small]: size === TextInputSize.SMALL, + [styles.warning]: warningProp || nearCharacterLimit, + [styles.error]: error, + [styles.focused]: isFocused, + [styles.disabled]: disabled, + [styles.required]: required + } + + const inputRender = ( +
+
+ {startAdornmentText ? ( - {endAdornmentText} + {startAdornmentText} ) : null} +
- ) - - return ( -
-
- {StartIcon ? : null} - {shouldShowLabel ? ( - - ) : ( - inputRender - )} - - {showMaxLength && ( -
- - {characterCount}/{maxLength} - -
- )} - {EndIcon ? : null} - {children} -
- {helperText ? ( - {helperText} - ) : null} + {endAdornmentText ? ( + + {endAdornmentText} + + ) : null} +
+ ) + + return ( +
+
+ {StartIcon ? : null} + {shouldShowLabel ? ( + + ) : ( + inputRender + )} + + {showMaxLength && ( +
+ + {characterCount}/{maxLength} + +
+ )} + {EndIcon ? : null} + {children}
- ) - } -) -TextInput.displayName = 'TextInput' + {helperText ? ( + + {helperText} + + ) : null} +
+ ) +}) + +// TextInput.displayName = 'TextInput' export { TextInput }