Ignore symbols and functions in select tag (#13389)
* wip: ignore symbols and functions in select tag * fix: Use ToStringValue as a maybe type * refactor: remove unnecessary test * refactor: remove implicit return from tests
Rauno Freiberg committed
Aug 14, 2018 at 20:31 UTC
de5102c4cd9a0ece37872c5a1c6b1e1345b8afef
3 files changed
+178
-3
packages/react-dom/src/__tests__/ReactDOMSelect-test.js
+172
@@ -808,4 +808,176 @@ describe('ReactDOMSelect', () => {
808
expect(node.options[1].selected).toBe(false); // b
809
expect(node.options[2].selected).toBe(false); // c
810
});
811
+
812
+ describe('When given a Symbol value', () => {
813
+ it('treats initial Symbol value as an empty string', () => {
814
+ let node;
815
+
816
+ expect(() => {
817
+ node = ReactTestUtils.renderIntoDocument(
818
+ <select onChange={noop} value={Symbol('foobar')}>
819
+ <option value={Symbol('foobar')}>A Symbol!</option>
820
+ <option value="monkey">A monkey!</option>
821
+ <option value="giraffe">A giraffe!</option>
822
+ </select>,
823
+ );
824
+ }).toWarnDev('Invalid value for prop `value`');
825
+
826
+ expect(node.value).toBe('');
827
+ });
828
+
829
+ it('treats updated Symbol value as an empty string', () => {
830
+ let node;
831
+
832
+ expect(() => {
833
+ node = ReactTestUtils.renderIntoDocument(
834
+ <select onChange={noop} value="monkey">
835
+ <option value={Symbol('foobar')}>A Symbol!</option>
836
+ <option value="monkey">A monkey!</option>
837
+ <option value="giraffe">A giraffe!</option>
838
+ </select>,
839
+ );
840
+ }).toWarnDev('Invalid value for prop `value`');
841
+
842
+ expect(node.value).toBe('monkey');
843
+
844
+ node = ReactTestUtils.renderIntoDocument(
845
+ <select onChange={noop} value={Symbol('foobar')}>
846
+ <option value={Symbol('foobar')}>A Symbol!</option>
847
+ <option value="monkey">A monkey!</option>
848
+ <option value="giraffe">A giraffe!</option>
849
+ </select>,
850
+ );
851
+
852
+ expect(node.value).toBe('');
853
+ });
854
+
855
+ it('treats initial Symbol defaultValue as an empty string', () => {
856
+ let node;
857
+
858
+ expect(() => {
859
+ node = ReactTestUtils.renderIntoDocument(
860
+ <select defaultValue={Symbol('foobar')}>
861
+ <option value={Symbol('foobar')}>A Symbol!</option>
862
+ <option value="monkey">A monkey!</option>
863
+ <option value="giraffe">A giraffe!</option>
864
+ </select>,
865
+ );
866
+ }).toWarnDev('Invalid value for prop `value`');
867
+
868
+ expect(node.value).toBe('');
869
+ });
870
+
871
+ it('treats updated Symbol defaultValue as an empty string', () => {
872
+ let node;
873
+
874
+ expect(() => {
875
+ node = ReactTestUtils.renderIntoDocument(
876
+ <select defaultValue="monkey">
877
+ <option value={Symbol('foobar')}>A Symbol!</option>
878
+ <option value="monkey">A monkey!</option>
879
+ <option value="giraffe">A giraffe!</option>
880
+ </select>,
881
+ );
882
+ }).toWarnDev('Invalid value for prop `value`');
883
+
884
+ expect(node.value).toBe('monkey');
885
+
886
+ node = ReactTestUtils.renderIntoDocument(
887
+ <select defaultValue={Symbol('foobar')}>
888
+ <option value={Symbol('foobar')}>A Symbol!</option>
889
+ <option value="monkey">A monkey!</option>
890
+ <option value="giraffe">A giraffe!</option>
891
+ </select>,
892
+ );
893
+
894
+ expect(node.value).toBe('');
895
+ });
896
+ });
897
+
898
+ describe('When given a function value', () => {
899
+ it('treats initial function value as an empty string', () => {
900
+ let node;
901
+
902
+ expect(() => {
903
+ node = ReactTestUtils.renderIntoDocument(
904
+ <select onChange={noop} value={() => {}}>
905
+ <option value={() => {}}>A function!</option>
906
+ <option value="monkey">A monkey!</option>
907
+ <option value="giraffe">A giraffe!</option>
908
+ </select>,
909
+ );
910
+ }).toWarnDev('Invalid value for prop `value`');
911
+
912
+ expect(node.value).toBe('');
913
+ });
914
+
915
+ it('treats initial function defaultValue as an empty string', () => {
916
+ let node;
917
+
918
+ expect(() => {
919
+ node = ReactTestUtils.renderIntoDocument(
920
+ <select defaultValue={() => {}}>
921
+ <option value={() => {}}>A function!</option>
922
+ <option value="monkey">A monkey!</option>
923
+ <option value="giraffe">A giraffe!</option>
924
+ </select>,
925
+ );
926
+ }).toWarnDev('Invalid value for prop `value`');
927
+
928
+ expect(node.value).toBe('');
929
+ });
930
+
931
+ it('treats updated function value as an empty string', () => {
932
+ let node;
933
+
934
+ expect(() => {
935
+ node = ReactTestUtils.renderIntoDocument(
936
+ <select onChange={noop} value="monkey">
937
+ <option value={() => {}}>A function!</option>
938
+ <option value="monkey">A monkey!</option>
939
+ <option value="giraffe">A giraffe!</option>
940
+ </select>,
941
+ );
942
+ }).toWarnDev('Invalid value for prop `value`');
943
+
944
+ expect(node.value).toBe('monkey');
945
+
946
+ node = ReactTestUtils.renderIntoDocument(
947
+ <select onChange={noop} value={() => {}}>
948
+ <option value={() => {}}>A function!</option>
949
+ <option value="monkey">A monkey!</option>
950
+ <option value="giraffe">A giraffe!</option>
951
+ </select>,
952
+ );
953
+
954
+ expect(node.value).toBe('');
955
+ });
956
+
957
+ it('treats updated function defaultValue as an empty string', () => {
958
+ let node;
959
+
960
+ expect(() => {
961
+ node = ReactTestUtils.renderIntoDocument(
962
+ <select defaultValue="monkey">
963
+ <option value={() => {}}>A function!</option>
964
+ <option value="monkey">A monkey!</option>
965
+ <option value="giraffe">A giraffe!</option>
966
+ </select>,
967
+ );
968
+ }).toWarnDev('Invalid value for prop `value`');
969
+
970
+ expect(node.value).toBe('monkey');
971
+
972
+ node = ReactTestUtils.renderIntoDocument(
973
+ <select defaultValue={() => {}}>
974
+ <option value={() => {}}>A function!</option>
975
+ <option value="monkey">A monkey!</option>
976
+ <option value="giraffe">A giraffe!</option>
977
+ </select>,
978
+ );
979
+
980
+ expect(node.value).toBe('');
981
+ });
982
+ });
983
});
packages/react-dom/src/client/ReactDOMFiberOption.js
+2
-1
@@ -10,6 +10,7 @@
10
import React from 'react';
11
import warning from 'shared/warning';
12
import {validateDOMNesting, updatedAncestorInfo} from './validateDOMNesting';
13
+import {getToStringValue, toString} from './ToStringValue';
14
15
let didWarnSelectedSetOnOption = false;
16
@@ -73,7 +74,7 @@ export function validateProps(element: Element, props: Object) {
74
export function postMountWrapper(element: Element, props: Object) {
75
// value="" should make a value attribute (#6219)
76
if (props.value != null) {
76
- element.setAttribute('value', props.value);
77
+ element.setAttribute('value', toString(getToStringValue(props.value)));
78
}
79
}
80
packages/react-dom/src/client/ReactDOMFiberSelect.js
+4
-2
@@ -12,6 +12,8 @@ import {getCurrentFiberOwnerNameInDevOrNull} from 'react-reconciler/src/ReactCur
12
import warning from 'shared/warning';
13
14
import ReactControlledValuePropTypes from '../shared/ReactControlledValuePropTypes';
15
+import {getToStringValue, toString} from './ToStringValue';
16
+import type {ToStringValue} from './ToStringValue';
17
18
let didWarnValueDefaultValue;
19
@@ -21,7 +23,7 @@ if (__DEV__) {
23
24
type SelectWithWrapperState = HTMLSelectElement & {
25
_wrapperState: {
24
- initialValue: ?string,
26
+ initialValue: ?ToStringValue,
27
wasMultiple: boolean,
28
},
29
};
@@ -98,7 +100,7 @@ function updateOptions(
100
} else {
101
// Do not set `select.value` as exact behavior isn't consistent across all
102
// browsers for all cases.
101
- let selectedValue = '' + (propValue: string);
103
+ let selectedValue = toString(getToStringValue((propValue: any)));
104
let defaultSelected = null;
105
for (let i = 0; i < options.length; i++) {
106
if (options[i].value === selectedValue) {