ICTU/quality-time

View on GitHub
components/frontend/src/fields/MultipleChoiceInput.js

Summary

Maintainability
A
3 hrs
Test Coverage
import { array, bool, func } from "prop-types"
import { useState } from "react"

import { ReadOnlyOrEditable } from "../context/Permissions"
import { Form } from "../semantic_ui_react_wrappers"
import { labelPropType, permissionsPropType, stringsPropType } from "../sharedPropTypes"
import { ReadOnlyInput } from "./ReadOnlyInput"

function assembleOptions(optionList, values) {
    // Create a sorted list of unique options. Also include the current values, or they won't be displayed for some reason
    let options = new Set()
    optionList.forEach((option) => {
        options.add(option)
    })
    values.forEach((value) => {
        options.add({ key: value, text: value, value: value })
    })
    options = Array.from(options)
    options.sort((a, b) => a.text.localeCompare(b.text))
    return options
}

export function MultipleChoiceInput(props) {
    let { allowAdditions, editableLabel, onSearchChange, required, set_value, requiredPermissions, ...otherProps } =
        props
    const [values, setValues] = useState(props.value || [])
    const [searchQuery, setSearchQuery] = useState("")
    return (
        <Form>
            <ReadOnlyOrEditable
                requiredPermissions={requiredPermissions}
                readOnlyComponent={<ReadOnlyInput {...otherProps} value={values.join(", ")} />}
                editableComponent={
                    <Form.Dropdown
                        {...otherProps}
                        allowAdditions={allowAdditions}
                        error={required && values.length === 0}
                        fluid
                        label={editableLabel || props.label}
                        multiple
                        onAddItem={() => setSearchQuery("")}
                        onBlur={() => {
                            if (searchQuery && !values.includes(searchQuery)) {
                                // Save the data on loss of focus like we do with other input types
                                let newValues = values.concat(searchQuery)
                                setValues(newValues)
                                set_value(newValues)
                            }
                            setSearchQuery("")
                        }}
                        onChange={(_event, data) => {
                            setValues(data.value)
                            set_value(data.value)
                            setSearchQuery("")
                        }}
                        onSearchChange={(event, data) => {
                            event.preventDefault()
                            setSearchQuery(data.searchQuery)
                            if (onSearchChange) {
                                onSearchChange(data.searchQuery)
                            }
                        }}
                        options={assembleOptions(props.options || [], values)}
                        search
                        searchQuery={searchQuery}
                        selection
                        value={values}
                    />
                }
            />
        </Form>
    )
}
MultipleChoiceInput.propTypes = {
    allowAdditions: bool,
    editableLabel: labelPropType,
    label: labelPropType,
    onSearchChange: func,
    options: array,
    required: bool,
    requiredPermissions: permissionsPropType,
    set_value: func,
    value: stringsPropType,
}