best-doctor/ke

View on GitHub
src/DetailView/widgets/ForeignKeySelect.tsx

Summary

Maintainability
B
6 hrs
Test Coverage
C
77%
// Это легаси
/* eslint-disable react/jsx-props-no-spreading */
import React, { useEffect } from 'react'
import type { MenuPlacement } from 'react-select'
import { BoxProps } from '@chakra-ui/react'

import type { Accessor, DetailObject, WidgetProps } from '../../typing'

import { useWidgetInitialization } from '../../common/hooks/useWidgetInitialization'
import { ValidationWrapper } from '../../common/components/ValidationWrapper'
import { AsyncSelectWidget } from '../../common/components/AsyncSelectWidget'
import { WidgetWrapper } from '../../common/components/WidgetWrapper'
import { getAccessor, getCopyHandler, getPayload } from '../utils/dataAccess'
import { ExtendedProps } from '../../common/components/ReactSelectCustomization'
import { useCreateTestId } from '../../django-spa/aspects'
import { Provider } from '../../admin/providers'

export type ForeignKeySelectWidgetProps = WidgetProps &
  ExtendedProps & {
    optionLabel: Function
    optionValue: Function
    isClearable?: boolean
    isDisabled?: Accessor<boolean>
    defaultOptions?: boolean
    searchParamName?: string
    styles?: Accessor<object>
    optionLabelMenu?: (option: unknown, mainObject: DetailObject) => string
    optionLabelValue?: (option: unknown, mainObject: DetailObject) => string
    placeholder?: string
    containerProps?: BoxProps
    labelContainerProps?: BoxProps
    staleTime?: Accessor<number>
    allowAllDefinedValues?: Accessor<boolean>
    provider?: Provider
    menuPlacement?: MenuPlacement
  }

/**
 * Render select-input with async loaded options from backend
 * onChange - can return full loaded model for option, not just key
 *
 * Based on AsyncSelectWidget, so have common props.
 *
 * props.optionLabel - got loaded model and return label for option
 * props.optionValue - got loaded model and return label for value
 * props.targetPayload - got selected model and return widget payload
 * props.styles - react-select styles
 * props.optionLabelMenu - got loaded model and return menu label for option
 * props.optionLabelValue - got loaded model and return value label for option
 *
 * @param props - widget props
 */
const ForeignKeySelectWidget = (props: ForeignKeySelectWidgetProps): JSX.Element => {
  const {
    name,
    mainDetailObject,
    provider,
    helpText,
    targetPayload,
    optionLabel,
    optionValue,
    isClearable = false,
    defaultOptions = false,
    style,
    styles,
    setInitialValue,
    submitChange,
    notBlockingValidators = [],
    blockingValidators = [],
    containerStore,
    useClipboard,
    copyValue,
    searchParamName,
    notifier,
    cacheTime,
    optionLabelMenu,
    optionLabelValue,
    isDisabled = false,
    placeholder,
    containerProps,
    labelContainerProps,
    staleTime,
    widgetClassName,
    componentsClasses,
    allowAllDefinedValues,
    menuPlacement,
  } = props

  const context = containerStore.getState()

  const { targetUrl, content, dataResourceUrl, isRequired, widgetDescription } = useWidgetInitialization(
    { ...props, context },
    { allowAllDefinedValues: getAccessor(allowAllDefinedValues) }
  )
  const effectiveCacheTime = getAccessor(cacheTime, mainDetailObject, context)
  const selectStyle = getAccessor(styles, mainDetailObject, context)

  const [value, setValue] = React.useState<object | null>(content as object | null)
  setInitialValue(value ? getPayload(value, name, targetPayload) : null)

  useEffect(() => {
    setValue(content as object | null)
  }, [content])

  const handleChangeValue = (changeValue: React.ChangeEvent<HTMLInputElement>): void => {
    setValue(changeValue)

    const widgetPayload = getPayload(changeValue, name, targetPayload)

    submitChange({ url: targetUrl, payload: widgetPayload })
  }

  const handleCopyValue = getCopyHandler(value, copyValue, () => optionLabel(value, mainDetailObject))
  const { getDataTestId } = useCreateTestId()

  return (
    <WidgetWrapper
      name={name}
      style={style}
      helpText={helpText}
      description={widgetDescription}
      useClipboard={useClipboard}
      copyValue={handleCopyValue}
      notifier={notifier}
      required={isRequired}
      containerProps={containerProps}
      labelContainerProps={labelContainerProps}
      {...getDataTestId(props)}
    >
      <ValidationWrapper
        notBlockingValidators={notBlockingValidators}
        blockingValidators={blockingValidators}
        provider={provider}
        detailObject={mainDetailObject}
        context={context as Record<string, unknown>}
      >
        <AsyncSelectWidget
          cacheTime={effectiveCacheTime}
          dataResourceUrl={dataResourceUrl}
          handleChange={handleChangeValue}
          value={value}
          isClearable={isClearable}
          defaultOptions={defaultOptions}
          getOptionLabel={(val: object | null) => optionLabel(val, mainDetailObject)}
          getOptionValue={optionValue}
          getOptionLabelMenu={
            optionLabelMenu ? (val: object | null) => optionLabelMenu(val, mainDetailObject) : undefined
          }
          getOptionLabelValue={
            optionLabelValue ? (val: object | null) => optionLabelValue(val, mainDetailObject) : undefined
          }
          searchParamName={searchParamName}
          styles={selectStyle}
          isDisabled={getAccessor(isDisabled, mainDetailObject, context)}
          placeholder={placeholder}
          staleTime={staleTime}
          className={widgetClassName}
          componentsClasses={componentsClasses}
          name={name}
          menuPlacement={menuPlacement}
        />
      </ValidationWrapper>
    </WidgetWrapper>
  )
}

export { ForeignKeySelectWidget }