Skip to content

Latest commit

 

History

History

README.md

Input

Currently only of type "text", as other types may need some special tweaks and implementations.

How to use

The <TextInput> implementation intended to be minimalistic, and mostly concentrated on applying props and props-dependent variablis to layout and styling. The InputProps type indicated props applied to the underlying input element, ComponentProps - those needed for layouting, state calculations and other means.

Keep in mind, that the component is uncontrolled and requires hooks/wrappers to function.

Additional event handlers and any other valid HTMLInputElement props could be applied to the underlying input by just passing them explicitly to the .

type CallBackRef = (input: any) => void

export interface InputProps {
  onChange: (e: ChangeEvent<HTMLInputElement>) => void
  onFocus?: (e: FocusEvent) => void
  onBlur?: (e: FocusEvent) => void
  onKeyDown?: (e: KeyboardEvent<HTMLInputElement>) => void
  value: string
  inputRef?: MutableRefObject<HTMLInputElement | null> | CallBackRef
  disabled?: boolean
  iconLeft?: ReactNode
  iconRight?: ReactNode
  name?: string
  placeholder?: string
  autoFocus?: boolean
}

export interface MetaOptions {
  error?: boolean | string
  success?: boolean | string
  touched?: boolean
  isDirty?: boolean
  instantFeedback?: InstantFeedback
  prevValue: string | undefined
  value: string
  focused?: boolean
}

export interface ComponentProps {
  hint?: string
  error?: boolean | string
  success?: boolean | string
  touched?: boolean
  isDirty?: boolean
  instantFeedback?: "all" | "positiveFirst"
  className?: string
  fieldIndicator?: string | ReactNode
  metaShrinked?: boolean
  label?: string
  handleMetaDisplay?: (metaOptions: MetaOptions) => boolean
}

export type TextInputProps = InputProps & ComponentProps

Notable props:

  • hint - default message under the input field
  • success and error - status indicators, could be boolean or strings (in this case they render instead of hint)
  • instantFeedback - set this to true, if you want to provide validation status as user types, not onBlur/other. With all updates validation status on any value change, with positiveFirst - only when string is successfully validated (success prop), or user started to erase entered data and current string has errors according to error prop.
  • isDirty - boolean flag showing if something was ever entered into the input. Use together with instantFeedback.
  • metaShrinked - set this to true to not render any meta information and reserved space under the input field
  • fieldIndicator - additional information field, which could be used for displaying maxChars string or other meta info.
  • handleMetaDisplay - if instantFeedback / touched and default built-in logic is not suitable for some sophisticated usecases, this function could be used for full control over error/success display.

Meta Display

This API is provided to control the display of error/success (meta) feedback for the input.

export interface MetaOptions {
  error?: boolean | string
  success?: boolean | string
  touched?: boolean
  isDirty?: boolean
  instantFeedback?: InstantFeedback
  prevValue: string | undefined
  value: string
  focused?: boolean
}

handleMetaDisplay function accepts the above arguments passed by the input internally. They include both props, and implementation-level values known only to the TextInput. Here is provided the default behaviour, which could be overriden using the same pattern:

const defaultHandleMetaDisplay = ({
  isDirty,
  instantFeedback,
  value,
  prevValue,
  error,
  success,
  touched,
}: MetaOptions) =>
  touched ||
  Boolean(instantFeedback === "all" && isDirty) ||
  Boolean(instantFeedback === "positiveFirst" && isDirty && success) ||
  Boolean(
    instantFeedback === "positiveFirst" &&
      isDirty &&
      error &&
      prevValue &&
      value.length < prevValue.length
  )

Exposed hooks and application-level wrapping

type FocusHandler = FocusEventHandler
type BlurHandler = FocusEventHandler
type FocusedState = boolean

type UseFocusedState = ({
  onBlur,
  onFocus,
  defaultState,
}: {
  onBlur?: FocusEventHandler
  onFocus?: FocusEventHandler
  defaultState?: boolean
}) => [FocusedState, FocusHandler, BlurHandler]

useFocusedState hook is being used internally by the <TextInput/>, so normally you can just pass the custom blur/focus handlers to the component, and work with side effects as with default <input /> tag.

type BlurHandler = FocusEventHandler
type TouchedState = boolean
type SetTouchedState = React.Dispatch<React.SetStateAction<boolean>>

type UseTouchedState = ({
  onBlur,
  defaultState,
}: {
  onBlur?: BlurHandler
  defaultState?: boolean
}) => [TouchedState, BlurHandler, SetTouchedState]

useTouchedState hook could be useful working with touched/untouched functionality in validated forms. It allows to manage the success and error feedback shown to the user. Apart from value and handler, it also exposes the programmatic method to set touched state for the input. TBD if we will use it, or procees with custom event-oriented form touch.

type InputValue = string
type MaxCharsIndicator = string
type IsDirty = boolean
type UseInputValue = ({
  value,
  onChange,
  maxChars,
}: {
  value?: string
  onChange?: ChangeEventHandler
  maxChars?: number
}) => [InputValue, ChangeEventHandler, MaxCharsIndicator, IsDirty]

useInputValue hook is a short way to add basic functionaluty to the <TextInput /> - a controlled state for input value, and custom side-effects handling for onChange. If maxChars option is passed, it will also return a quantity of entered symbols in relation to maximum allowed as string. isDirty flag indicates if value was changed at least once, required for instantFeedback inputs to work properly.

Further improvements TBD

We may include existing convenience hooks/wrappers and write additional ones (for setting value directly to Redux, for instance). However, for now we need some data on how the control will be used in applications.

For validation handling, there could be a hook, but we need to be very specific about the API. Right now it's possible to use the <TextInput/> together with any library, so when we will decide on our forms library or custom approach, we may start adding wrappers to the netdata-ui. For now it's better to leave convenience wrapping to the application-level code.