Skip to content

Commit

Permalink
make aria label required + fix assistive text size
Browse files Browse the repository at this point in the history
  • Loading branch information
DejayJD committed Oct 16, 2023
1 parent 14dd710 commit 344d4f9
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 146 deletions.
12 changes: 9 additions & 3 deletions packages/harmony/src/components/input/TextInput/HelperText.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className={styles.root}>
<Text size='xs' strength='default' color={hasError ? 'error' : 'default'}>
<Text
size={size}
strength='default'
color={hasError ? 'error' : 'default'}
>
{children}
</Text>
</div>
Expand Down
284 changes: 141 additions & 143 deletions packages/harmony/src/components/input/TextInput/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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<HTMLInputElement, TextInputProps>(
(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 = (
<div className={cn(styles.inputRow, layoutStyles.row)}>
<div className={cn(layoutStyles.row, styles.inputContainer)}>
{startAdornmentText ? (
<Text variant='label' size='l' color='subdued'>
{startAdornmentText}
</Text>
) : null}
<input
onFocus={handleFocus}
onBlur={handleBlur}
ref={ref}
className={cn(styles.textInput, inputClassName)}
value={value}
maxLength={maxLength}
disabled={disabled}
placeholder={shouldShowPlaceholder ? placeholder : undefined}
aria-label={label ?? props['aria-label']}
{...other}
/>
</div>
{endAdornmentText ? (
const TextInput = forwardRef<HTMLInputElement, TextInputProps>((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 = (
<div className={cn(styles.inputRow, layoutStyles.row)}>
<div className={cn(layoutStyles.row, styles.inputContainer)}>
{startAdornmentText ? (
<Text variant='label' size='l' color='subdued'>
{endAdornmentText}
{startAdornmentText}
</Text>
) : null}
<input
onFocus={handleFocus}
onBlur={handleBlur}
ref={ref}
className={cn(styles.textInput, inputClassName)}
value={value}
maxLength={maxLength}
disabled={disabled}
placeholder={shouldShowPlaceholder ? placeholder : undefined}
aria-label={label ?? props['aria-label']}
{...other}
/>
</div>
)

return (
<div className={cn(styles.root, className)}>
<div className={cn(styles.inputRoot, inputRootClassName, style)}>
{StartIcon ? <StartIcon /> : null}
{shouldShowLabel ? (
<label className={styles.elevatedLabel}>
<Text
variant='body'
tag='span'
className={cn(styles.label, {
[styles.hasValue]: characterCount > 0
})}
>
{label}
</Text>
{inputRender}
</label>
) : (
inputRender
)}

{showMaxLength && (
<div className={styles.characterCount}>
<Text
variant='body'
size='xs'
tag='span'
color={error ? 'error' : 'default'}
>
{characterCount}/{maxLength}
</Text>
</div>
)}
{EndIcon ? <EndIcon /> : null}
{children}
</div>
{helperText ? (
<HelperText hasError={error}>{helperText}</HelperText>
) : null}
{endAdornmentText ? (
<Text variant='label' size='l' color='subdued'>
{endAdornmentText}
</Text>
) : null}
</div>
)

return (
<div className={cn(styles.root, className)}>
<div className={cn(styles.inputRoot, inputRootClassName, style)}>
{StartIcon ? <StartIcon /> : null}
{shouldShowLabel ? (
<label className={styles.elevatedLabel}>
<Text
variant='body'
tag='span'
className={cn(styles.label, {
[styles.hasValue]: characterCount > 0 || startAdornmentText
})}
>
{label}
</Text>
{inputRender}
</label>
) : (
inputRender
)}

{showMaxLength && (
<div className={styles.characterCount}>
<Text
variant='body'
size='xs'
tag='span'
color={error ? 'error' : 'default'}
>
{characterCount}/{maxLength}
</Text>
</div>
)}
{EndIcon ? <EndIcon /> : null}
{children}
</div>
)
}
)
TextInput.displayName = 'TextInput'
{helperText ? (
<HelperText hasError={error} size={helperTextSize}>
{helperText}
</HelperText>
) : null}
</div>
)
})

// TextInput.displayName = 'TextInput'

export { TextInput }

0 comments on commit 344d4f9

Please sign in to comment.