/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow */ // TODO: direct imports like some-package/src/* are bad. Fix me. import {getCurrentFiberOwnerNameInDevOrNull} from 'react-reconciler/src/ReactCurrentFiber'; import {checkControlledValueProps} from '../shared/ReactControlledValuePropTypes'; import {getToStringValue, toString} from './ToStringValue'; let didWarnValueDefaultValue; if (__DEV__) { didWarnValueDefaultValue = false; } type SelectWithWrapperState = HTMLSelectElement & {| _wrapperState: {|wasMultiple: boolean|}, |}; function getDeclarationErrorAddendum() { const ownerName = getCurrentFiberOwnerNameInDevOrNull(); if (ownerName) { return '\n\nCheck the render method of `' + ownerName + '`.'; } return ''; } const valuePropNames = ['value', 'defaultValue']; /** * Validation function for `value` and `defaultValue`. */ function checkSelectPropTypes(props) { if (__DEV__) { checkControlledValueProps('select', props); for (let i = 0; i < valuePropNames.length; i++) { const propName = valuePropNames[i]; if (props[propName] == null) { continue; } const isArray = Array.isArray(props[propName]); if (props.multiple && !isArray) { console.error( 'The `%s` prop supplied to must be a scalar ' + 'value if `multiple` is false.%s', propName, getDeclarationErrorAddendum(), ); } } } } function updateOptions( node: HTMLSelectElement, multiple: boolean, propValue: any, setDefaultSelected: boolean, ) { type IndexableHTMLOptionsCollection = HTMLOptionsCollection & { [key: number]: HTMLOptionElement, ..., }; const options: IndexableHTMLOptionsCollection = node.options; if (multiple) { const selectedValues = (propValue: Array); const selectedValue = {}; for (let i = 0; i < selectedValues.length; i++) { // Prefix to avoid chaos with special keys. selectedValue['$' + selectedValues[i]] = true; } for (let i = 0; i < options.length; i++) { const selected = selectedValue.hasOwnProperty('$' + options[i].value); if (options[i].selected !== selected) { options[i].selected = selected; } if (selected && setDefaultSelected) { options[i].defaultSelected = true; } } } else { // Do not set `select.value` as exact behavior isn't consistent across all // browsers for all cases. const selectedValue = toString(getToStringValue((propValue: any))); let defaultSelected = null; for (let i = 0; i < options.length; i++) { if (options[i].value === selectedValue) { options[i].selected = true; if (setDefaultSelected) { options[i].defaultSelected = true; } return; } if (defaultSelected === null && !options[i].disabled) { defaultSelected = options[i]; } } if (defaultSelected !== null) { defaultSelected.selected = true; } } } /** * Implements a