Skip to content

Commit

Permalink
Input validation styles (#1831)
Browse files Browse the repository at this point in the history
* adds validation styles to Select

* adds validation styles to TextInputWithTokens, updates stories for TextInputWithTokens

* adds changeset

* Update docs/content/TextInputWithTokens.mdx

Co-authored-by: Rez <rezrah@github.com>

* Update docs/content/TextInputWithTokens.mdx

Co-authored-by: Rez <rezrah@github.com>

* addresses PR feedback

* updates snapshots

* updates storybook and docs to show success validation status

* updates docs to remove inputSize prop

* explicitly list string union in docs for size prop

Co-authored-by: Rez <rezrah@github.com>
  • Loading branch information
mperrotti and rezrah committed Feb 10, 2022
1 parent fad43d6 commit 96473f3
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 66 deletions.
5 changes: 5 additions & 0 deletions .changeset/lemon-stingrays-cheer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react': minor
---

Makes validation styling available for Select and TextInputWithTokens
6 changes: 5 additions & 1 deletion docs/content/TextInput.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ TextInput is a form component to add default styling to the native text input.
type={<>string | React.ComponentType</>}
description="Visual positioned on the right edge inside the input"
/>
<PropsTableRow name="validationStatus" type="'warning' | 'error'" description="Style the input to match the status" />
<PropsTableRow
name="validationStatus"
type="'error' | 'success' | 'warning'"
description="Style the input to match the status"
/>

<PropsTableRow
name="variant"
Expand Down
43 changes: 42 additions & 1 deletion docs/content/TextInputWithTokens.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,37 @@ const MaxHeightExample = () => {
render(MaxHeightExample)
```

### With an error validation status

```javascript live noinline
const BasicExample = () => {
const [tokens, setTokens] = React.useState([
{text: 'zero', id: 0},
{text: 'one', id: 1},
{text: 'two', id: 2}
])
const onTokenRemove = tokenId => {
setTokens(tokens.filter(token => token.id !== tokenId))
}

return (
<>
<Box as="label" display="block" htmlFor="inputWithTokens-basic">
Basic example tokens
</Box>
<TextInputWithTokens
tokens={tokens}
onTokenRemove={onTokenRemove}
id="inputWithTokens-basic"
validationStatus="error"
/>
</>
)
}

render(BasicExample)
```

## Props

<PropsTable>
Expand Down Expand Up @@ -197,13 +228,23 @@ render(MaxHeightExample)
defaultValue="false"
description="Whether tokens should render inline horizontally. By default, tokens wrap to new lines"
/>
<PropsTable.Row name="size" type="TokenSizeKeys" defaultValue="extralarge" description="The size of the tokens" />
<PropsTable.Row
name="size"
type="'small' | 'medium' | 'large' | 'extralarge'"
defaultValue="extralarge"
description="The size of the tokens and text input"
/>
<PropsTable.Row
name="hideTokenRemoveButtons"
type="boolean"
defaultValue="false"
description="Whether the remove buttons should be rendered in the tokens"
/>
<PropsTableRow
name="validationStatus"
type="'error' | 'success' | 'warning'"
description="Style the input to match the status"
/>
<PropsTable.Row
name="visibleTokenCount"
type="number"
Expand Down
6 changes: 4 additions & 2 deletions src/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {get} from './constants'
import TextInputWrapper, {StyledWrapperProps} from './_TextInputWrapper'

type SelectProps = Omit<
Omit<React.HTMLProps<HTMLSelectElement>, 'size'> & StyledWrapperProps,
Omit<React.HTMLProps<HTMLSelectElement>, 'size'> & Omit<StyledWrapperProps, 'variant'>,
'multiple' | 'hasLeadingVisual' | 'hasTrailingVisual' | 'as'
>

Expand Down Expand Up @@ -42,19 +42,21 @@ const ArrowIndicator = styled(ArrowIndicatorSVG)`
`

const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
({children, disabled, placeholder, size, required, ref: _propsRef, ...rest}: SelectProps, ref) => (
({children, disabled, placeholder, size, required, validationStatus, ref: _propsRef, ...rest}: SelectProps, ref) => (
<TextInputWrapper
sx={{
position: 'relative'
}}
size={size}
validationStatus={validationStatus}
>
<StyledSelect
ref={ref}
required={required || Boolean(placeholder)}
disabled={disabled}
aria-required={required}
aria-disabled={disabled}
aria-invalid={validationStatus === 'error' ? 'true' : 'false'}
{...rest}
>
{placeholder && (
Expand Down
18 changes: 14 additions & 4 deletions src/TextInputWithTokens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {TokenSizeKeys} from './Token/TokenBase'
import {TextInputProps} from './TextInput'
import {useProvidedRefOrCreate} from './hooks'
import UnstyledTextInput from './_UnstyledTextInput'
import TextInputWrapper, {textInputHorizPadding} from './_TextInputWrapper'
import TextInputWrapper, {textInputHorizPadding, TextInputSizes} from './_TextInputWrapper'
import Box from './Box'
import Text from './Text'
import {isFocusable} from '@primer/behaviors/utils'
Expand Down Expand Up @@ -42,7 +42,7 @@ type TextInputWithTokensInternalProps<TokenComponentType extends AnyReactCompone
*/
preventTokenWrapping?: boolean
/**
* The size of the tokens
* The size of the tokens and text input
*/
size?: TokenSizeKeys
/**
Expand Down Expand Up @@ -82,7 +82,8 @@ function TextInputWithTokensInnerComponent<TokenComponentType extends AnyReactCo
width: widthProp,
minWidth: minWidthProp,
maxWidth: maxWidthProp,
variant: variantProp,
validationStatus,
variant: variantProp, // deprecated. use `size` instead
visibleTokenCount,
...rest
}: TextInputWithTokensInternalProps<TokenComponentType> & {
Expand Down Expand Up @@ -234,6 +235,12 @@ function TextInputWithTokensInnerComponent<TokenComponentType extends AnyReactCo
}

const visibleTokens = tokensAreTruncated ? tokens.slice(0, visibleTokenCount) : tokens
const inputSizeMap: Record<TokenSizeKeys, TextInputSizes> = {
small: 'small',
medium: 'small',
large: 'medium',
extralarge: 'medium'
}

return (
<TextInputWrapper
Expand All @@ -246,7 +253,9 @@ function TextInputWithTokensInnerComponent<TokenComponentType extends AnyReactCo
width={widthProp}
minWidth={minWidthProp}
maxWidth={maxWidthProp}
variant={variantProp}
size={size && inputSizeMap[size]}
validationStatus={validationStatus}
variant={variantProp} // deprecated. use `size` prop instead
onClick={focusInput}
sx={{
paddingLeft: textInputHorizPadding,
Expand Down Expand Up @@ -306,6 +315,7 @@ function TextInputWithTokensInnerComponent<TokenComponentType extends AnyReactCo
onKeyDown={handleInputKeyDown}
type="text"
sx={{height: '100%'}}
aria-invalid={validationStatus === 'error' ? 'true' : 'false'}
{...inputPropsRest}
/>
</Box>
Expand Down
6 changes: 4 additions & 2 deletions src/_TextInputWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {get} from './constants'
import sx, {SxProp} from './sx'
import {FormValidationStatus} from './utils/types/FormValidationStatus'

export type TextInputSizes = 'small' | 'medium' | 'large'

const sizeDeprecatedVariants = variant({
variants: {
small: {
Expand Down Expand Up @@ -53,8 +55,8 @@ export type StyledWrapperProps = {
hasLeadingVisual?: boolean
hasTrailingVisual?: boolean
/** @deprecated Use `size` prop instead */
variant?: 'small' | 'large'
size?: 'small' | 'large'
variant?: TextInputSizes
size?: TextInputSizes
} & StyledBaseWrapperProps

const textInputBasePadding = '12px'
Expand Down
Loading

0 comments on commit 96473f3

Please sign in to comment.