/* * The MIT License (MIT) * * Copyright (c) 2015 - present Instructure, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import { InputHTMLAttributes } from 'react' import type { FormMessage } from '@instructure/ui-form-field/v11_6' import type { OtherHTMLAttributes, PickPropsWithExceptions } from '@instructure/shared-types' import type { PlacementPropValues, PositionConstraint, PositionMountNode } from '@instructure/ui-position' import type { SelectOwnProps } from '@instructure/ui-select/v11_6' import type { WithDeterministicIdProps } from '@instructure/ui-react-utils' import { Renderable } from '@instructure/shared-types' type SimpleSelectOwnProps = PropsPassedToSelect & { /** * The value corresponding to the value of the selected option. If defined, * the component will act controlled and will not manage its own state. */ value?: string | number // TODO: it was using the "controllable" util, in the TS migration mimic that behaviour /** * The value of the option to select by default, when uncontrolled. */ defaultValue?: string /** * Callback fired when a new option is selected. */ onChange?: ( event: React.SyntheticEvent, data: { value?: string | number id?: string } ) => void // passed to Select as onRequestShowOptions /** * Callback fired when the options list is shown. */ onShowOptions?: (event: React.SyntheticEvent) => void // passed to Select as onRequestHideOptions /** * Callback fired when the options list is hidden. */ onHideOptions?: (event: React.SyntheticEvent) => void /** * Content to display in the list when no options are available. */ renderEmptyOption?: Renderable /** * Children of type `` or ``. */ children?: React.ReactNode // TODO: ChildrenPropTypes.oneOf([Group, Option]) } type PropsPassedToSelect = { /** * The form field label. */ renderLabel: Renderable /** * The id of the text input. One is generated if not supplied. */ id?: string /** * The size of the text input. */ size?: 'small' | 'medium' | 'large' /** * Additional helpful text to provide to screen readers about the operation * of the component. Provided via aria-describedby. */ assistiveText?: string /** * Html placeholder text to display when the input has no value. This should * be hint text, not a label replacement. */ placeholder?: string /** * Specifies if interaction with the input is enabled, disabled, or readonly. * When "disabled", the input changes visibly to indicate that it cannot * receive user interactions. When "readonly" the input still cannot receive * user interactions but it keeps the same styles as if it were enabled. */ interaction?: 'enabled' | 'disabled' | 'readonly' /** * Whether or not the text input is required. */ isRequired?: boolean /** * Whether the input is rendered inline with other elements or if it * is rendered as a block level element. */ isInline?: boolean /** * The width of the text input. */ width?: string /** * The number of options that should be visible before having to scroll. Works best when the options are the same height. */ visibleOptionsCount?: number /** * The max height the options list can be before having to scroll. If * set, it will __override__ the `visibleOptionsCount` prop. */ optionsMaxHeight?: string /** * The max width the options list can be before option text wraps. If not * set, the list will only display as wide as the text input. */ optionsMaxWidth?: string /** * Displays messages and validation for the input. It should be an array of * objects with the following shape: * `{ * text: ReactNode, * type: One of: ['newError', 'error', 'hint', 'success', 'screenreader-only'] * }` */ messages?: FormMessage[] /** * The placement of the options list. */ placement?: PlacementPropValues /** * The parent in which to constrain the placement. */ constrain?: PositionConstraint /** * An element or a function returning an element to use mount the options * list to in the DOM (defaults to `document.body`) */ mountNode?: PositionMountNode /** * A ref to the html `input` element. */ inputRef?: (inputElement: HTMLInputElement | null) => void /** * A ref to the html `ul` element. */ listRef?: (listElement: HTMLUListElement | null) => void /** * Content to display before the text input. This will commonly be an icon. */ renderBeforeInput?: Renderable /** * Content to display after the text input. This content will replace the * default arrow icons. */ renderAfterInput?: Renderable /** * Callback fired when text input receives focus. */ onFocus?: (event: React.FocusEvent) => void /** * Callback fired when text input loses focus. */ onBlur?: (event: React.FocusEvent) => void /** * Whether or not the content of the selected `SimpleSelect.Option`'s `renderBeforeLabel` and `renderAfterLabel` appear in the input field. * * If the selected `SimpleSelect.Option` has both `renderBeforeLabel` and `renderAfterLabel` content, both will be displayed in the input field. * * `SimpleSelect.Option`'s `renderBeforeLabel` and `renderAfterLabel` content will not be displayed, if `SimpleSelect`'s `inputValue` is an empty value, null or undefined. * * If `true` and the selected `SimpleSelect.Option` has a `renderAfterLabel` value, it will replace the default arrow icon. * * If `true` and `SimpleSelect`'s `renderBeforeInput` or `renderAfterInput` prop is set, it will display the selected `SimpleSelect.Option`'s `renderBeforeLabel` and `renderAfterLabel` instead of `SimpleSelect`'s `renderBeforeInput` or `renderAfterInput` value. * * If the selected `SimpleSelect.Option`'s `renderAfterLabel` value is empty, default arrow icon will be rendered. */ isOptionContentAppliedToInput?: boolean /** * In `stacked` mode the input is below the label. * * In `inline` mode the input is to the right/left (depending on text direction) of the label, * and the layout will look like `stacked` for small screens. */ layout?: 'stacked' | 'inline' } type PropKeys = keyof SimpleSelectOwnProps type AllowedPropKeys = Readonly> type SimpleSelectProps = PickPropsWithExceptions< SelectOwnProps, | keyof PropsPassedToSelect | 'children' | 'onRequestShowOptions' | 'onRequestHideOptions' | 'onRequestHighlightOption' | 'onRequestSelectOption' | 'inputValue' | 'isShowingOptions' | 'layout' > & SimpleSelectOwnProps & OtherHTMLAttributes< SimpleSelectOwnProps, InputHTMLAttributes > & WithDeterministicIdProps type SimpleSelectState = { inputValue?: string isShowingOptions: boolean highlightedOptionId?: string selectedOptionId?: string } const allowedProps: AllowedPropKeys = [ 'renderLabel', 'value', 'defaultValue', 'id', 'size', 'assistiveText', 'placeholder', 'interaction', 'isRequired', 'isInline', 'width', 'visibleOptionsCount', 'optionsMaxHeight', 'optionsMaxWidth', 'messages', 'placement', 'constrain', 'mountNode', 'onChange', 'onFocus', 'onBlur', 'onShowOptions', 'onHideOptions', 'inputRef', 'listRef', 'renderEmptyOption', 'renderBeforeInput', 'renderAfterInput', 'children', 'layout' ] export type { SimpleSelectProps, SimpleSelectState } export { allowedProps }