Skip to content

Commit

Permalink
[C-2768] Update InputV2, add TextField/TextAreaField (#3587)
Browse files Browse the repository at this point in the history
  • Loading branch information
dylanjeffers committed Jun 13, 2023
1 parent 5fc2b22 commit c732f93
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
}

.root input::placeholder,
.placeholder {
.label {
color: var(--neutral-light-4);
font-weight: var(--font-medium);
}
Expand All @@ -76,7 +76,7 @@
* Flex container so that the absolutely positioned elevated placeholder
* starts out centered vertically
**/
.elevatedPlaceholderLabel {
.elevatedLabel {
position: relative;
display: flex;
align-items: center;
Expand All @@ -86,28 +86,26 @@
}

/** Add the "*" to required fields, but have it disappear when elevated **/
.required:not(.focused)
.elevatedPlaceholderLabel
.placeholder:not(.hasValue)::after {
.required:not(.focused) .elevatedLabel .placeholder:not(.hasValue)::after {
content: ' *';
}

/** Push the input down a bit to make room for the elevated placeholder **/
.elevatedPlaceholderLabel input {
.elevatedLabel input {
padding-top: calc(var(--font-xs));
}

/** Position the elevated placeholder absoutely on top of the input **/
.placeholder {
/** Position the elevated label absoutely on top of the input **/
.label {
position: absolute;
z-index: 2;
transition: all 0.3s ease;
left: var(--unit-4);
}

/** Move the elevated placeholder to the top left if focused or has text **/
.focused .elevatedPlaceholderLabel .placeholder,
.placeholder.hasValue {
/** Move the elevated label to the top left if focused or has text **/
.focused .elevatedLabel .label,
.hasValue {
transform: translate(0px, calc(-1em - var(--gap)));
font-size: var(--font-xs);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export enum InputV2Variant {
ELEVATED_PLACEHOLDER
}

type InputV2Props = Omit<ComponentPropsWithoutRef<'input'>, 'size'> & {
export type InputV2Props = Omit<ComponentPropsWithoutRef<'input'>, 'size'> & {
size?: InputV2Size
variant?: InputV2Variant
showMaxLength?: boolean
Expand All @@ -26,12 +26,13 @@ type InputV2Props = Omit<ComponentPropsWithoutRef<'input'>, 'size'> & {
warning?: boolean
error?: boolean
inputClassName?: string
label?: string
}

export const InputV2 = (props: InputV2Props) => {
const {
required,
placeholder: placeholderProp,
label: labelProp,
className,
maxLength,
showMaxLength,
Expand All @@ -46,22 +47,14 @@ export const InputV2 = (props: InputV2Props) => {
disabled,
onFocus: onFocusProp,
onBlur: onBlurProp,
placeholder,
...other
} = props

const characterCount = value ? `${value}`.length : 0
const nearCharacterLimit = maxLength && characterCount >= 0.9 * maxLength
const elevatePlaceholder = variant === InputV2Variant.ELEVATED_PLACEHOLDER
const placeholder =
required && !elevatePlaceholder ? `${placeholderProp} *` : placeholderProp

const style = {
[styles.large]: size === InputV2Size.LARGE,
[styles.medium]: size === InputV2Size.MEDIUM,
[styles.small]: size === InputV2Size.SMALL,
[styles.warning]: warningProp || nearCharacterLimit,
[styles.error]: error
}
const label = required && !elevatePlaceholder ? `${labelProp} *` : labelProp

/**
* Since Firefox doesn't support the :has() pseudo selector,
Expand All @@ -72,40 +65,42 @@ export const InputV2 = (props: InputV2Props) => {
onBlurProp
)

const style = {
[styles.large]: size === InputV2Size.LARGE,
[styles.medium]: size === InputV2Size.MEDIUM,
[styles.small]: size === InputV2Size.SMALL,
[styles.warning]: warningProp || nearCharacterLimit,
[styles.error]: error,
[styles.focused]: isFocused,
[styles.disabled]: disabled,
[styles.required]: required
}

const input = (
<input
onFocus={handleFocus}
onBlur={handleBlur}
ref={inputRef}
placeholder={!elevatePlaceholder ? placeholder : undefined}
required={required}
className={inputClassName}
value={value}
maxLength={maxLength}
disabled={disabled}
placeholder={isFocused ? placeholder : undefined}
{...other}
/>
)

return (
<div
className={cn(
styles.root,
{ [styles.focused]: isFocused },
{ [styles.disabled]: disabled },
{ [styles.required]: required },
style,
className
)}
>
<div className={cn(styles.root, style, className)}>
{elevatePlaceholder ? (
<label className={styles.elevatedPlaceholderLabel}>
<label className={styles.elevatedLabel}>
<span
className={cn(styles.placeholder, {
className={cn(styles.label, {
[styles.hasValue]: characterCount > 0
})}
>
{placeholder}
{label}
</span>
{input}
</label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,17 @@
.root.focus {
border-color: var(--secondary);
}
.root.error {

.root.error,
.root.error.focused {
border-color: var(--accent-red);
}

.root.error:hover,
.root.error:hover.focused {
border-color: var(--accent-red-dark-1);
}

.root > textarea::placeholder {
color: var(--neutral-light-4);
font-weight: 500;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@ const getMaxHeight = ({
? maxVisibleRows * sizeToLineHeight[size] + sizeToVerticalPadding[size]
: undefined

type TextAreaV2Props = ComponentPropsWithoutRef<'textarea'> & {
export type TextAreaV2Props = ComponentPropsWithoutRef<'textarea'> & {
grows?: boolean
resize?: boolean
size?: TextAreaSize
heightBuffer?: number
maxVisibleRows?: number
showMaxLength?: boolean
error?: boolean
}

const CHARACTER_LIMIT_WARN_THRESHOLD_PERCENT = 0.875
Expand All @@ -64,6 +65,7 @@ export const TextAreaV2 = forwardRef<HTMLTextAreaElement, TextAreaV2Props>(
children,
onFocus: onFocusProp,
onBlur: onBlurProp,
error,
...other
} = props

Expand Down Expand Up @@ -113,7 +115,7 @@ export const TextAreaV2 = forwardRef<HTMLTextAreaElement, TextAreaV2Props>(
ref={rootRef}
className={cn(
styles.root,
{ [styles.focused]: isFocused },
{ [styles.focused]: isFocused, [styles.error]: error },
style,
className
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useField } from 'formik'

import { TextAreaV2, TextAreaV2Props } from 'components/data-entry/TextAreaV2'

type TextAreaFieldProps = TextAreaV2Props & {
name: string
}

export const TextAreaField = (props: TextAreaFieldProps) => {
const { name, ...other } = props
const [field, meta] = useField(name)

const hasError = Boolean(meta.touched && meta.error)

return <TextAreaV2 {...field} error={hasError} {...other} />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useField } from 'formik'

import { InputV2, InputV2Props } from 'components/data-entry/InputV2'

type TextFieldProps = InputV2Props & {
name: string
}

export const TextField = (props: TextFieldProps) => {
const { name, ...other } = props
const [field, meta] = useField(name)

const hasError = Boolean(meta.touched && meta.error)

return <InputV2 {...field} error={hasError} {...other} />
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ export const SearchUsersModal = (props: SearchUsersModalProps) => {
<InputV2
inputRef={(el) => el?.focus()}
variant={InputV2Variant.ELEVATED_PLACEHOLDER}
placeholder={messages.searchUsers}
label={messages.searchUsers}
size={InputV2Size.LARGE}
value={query}
onChange={handleChange}
Expand Down

0 comments on commit c732f93

Please sign in to comment.