@samitouri / QOS-React / commits / 00ba97a354

Use KeyboardEvent.char in the fallback logic if available (#9792)

When a user types an emoji via Touch keyboard in IE, React's fallback logic creates the `BeforeInput` event based on the `keypress`. However, the length of an emoji character (e.g. `\uD83D\uDE0A`) is two, so the `which` property does not represent an emoji correctly. Because IE's KeyboardEvent has the `char` property holding an entire emoji, we can use it directly instead of converting from the `which`.

tokikuch committed Jun 16, 2017 at 15:14 UTC 00ba97a354e841701b4b83983c3a3904895e7b87
2 files changed +36 -13
src/renderers/dom/shared/eventPlugins/BeforeInputEventPlugin.js
+12 -2
@@ -374,8 +374,18 @@ function getFallbackBeforeInputChars(topLevelType: TopLevelTypes, nativeEvent) {
374 * being used. Ex: `Cmd+C`. No character is inserted, and no
375 * `input` event will occur.
376 */
377 - if (nativeEvent.which && !isKeypressCommand(nativeEvent)) {
378 - return String.fromCharCode(nativeEvent.which);
377 + if (!isKeypressCommand(nativeEvent)) {
378 + // IE fires the `keypress` event when a user types an emoji via
379 + // Touch keyboard of Windows. In such a case, the `char` property
380 + // holds an emoji character like `\uD83D\uDE0A`. Because its length
381 + // is 2, the property `which` does not represent an emoji correctly.
382 + // In such a case, we directly return the `char` property instead of
383 + // using `which`.
384 + if (nativeEvent.char && nativeEvent.char.length > 1) {
385 + return nativeEvent.char;
386 + } else if (nativeEvent.which) {
387 + return String.fromCharCode(nativeEvent.which);
388 + }
389 }
390 return null;
391 case 'topCompositionEnd':
src/renderers/dom/shared/eventPlugins/__tests__/BeforeInputEventPlugin-test.js
+24 -11
@@ -19,6 +19,7 @@ var EventMapping = {
19 compositionend: 'topCompositionEnd',
20 keyup: 'topKeyUp',
21 keydown: 'topKeyDown',
22 + keypress: 'topKeyPress',
23 textInput: 'topTextInput',
24 textinput: null, // Not defined now
25 };
@@ -122,7 +123,8 @@ describe('BeforeInputEventPlugin', function() {
123 // instead of a standard name `textInput`. As of now, React does not have
124 // a corresponding topEvent to IE's textinput, but both events are added to
125 // this scenario data for future use.
125 - var Scenario_Composition = [
126 + var Test_Scenario = [
127 + // Composition test
128 {run: accumulateEvents, arg: ['compositionstart', {data: ''}]},
129 {run: accumulateEvents, arg: ['textInput', {data: 'A'}]},
130 {run: accumulateEvents, arg: ['textinput', {data: 'A'}]},
@@ -136,6 +138,13 @@ describe('BeforeInputEventPlugin', function() {
138 {run: accumulateEvents, arg: ['textinput', {data: 'xyz'}]},
139 {run: accumulateEvents, arg: ['keyup', {keyCode: 32}]},
140 {run: accumulateEvents, arg: ['compositionend', {data: 'Hello'}]},
141 +
142 + // Emoji test
143 + {
144 + run: accumulateEvents,
145 + arg: ['keypress', {char: '\uD83D\uDE0A', which: 65}],
146 + },
147 + {run: accumulateEvents, arg: ['textInput', {data: '\uD83D\uDE0A'}]},
148 ];
149
150 /* Defined expected results as a factory of result data because we need
@@ -170,6 +179,12 @@ describe('BeforeInputEventPlugin', function() {
179 {type: null}, // keyUp of 32
180 {type: ModuleCache.SyntheticCompositionEvent, data: {data: 'Hello'}},
181 {type: null},
182 +
183 + // Emoji test
184 + {type: null},
185 + {type: null},
186 + {type: null},
187 + {type: ModuleCache.SyntheticInputEvent, data: {data: '\uD83D\uDE0A'}},
188 ];
189
190 // For IE11, we use fallback data instead of IE's textinput events.
@@ -205,6 +220,12 @@ describe('BeforeInputEventPlugin', function() {
220 // at a time of compositionend
221 {type: ModuleCache.SyntheticCompositionEvent, data: {}},
222 {type: ModuleCache.SyntheticInputEvent, data: {data: 'XYZ'}},
223 +
224 + // Emoji test
225 + {type: null},
226 + {type: ModuleCache.SyntheticInputEvent, data: {data: '\uD83D\uDE0A'}},
227 + {type: null},
228 + {type: null},
229 ];
230
231 function TestEditableReactComponent(Emulator, Scenario, ExpectedResult) {
@@ -225,18 +246,10 @@ describe('BeforeInputEventPlugin', function() {
246 }
247
248 it('extract onBeforeInput from native textinput events', function() {
228 - TestEditableReactComponent(
229 - simulateWebkit,
230 - Scenario_Composition,
231 - Expected_Webkit,
232 - );
249 + TestEditableReactComponent(simulateWebkit, Test_Scenario, Expected_Webkit);
250 });
251
252 it('extract onBeforeInput from fallback objects', function() {
236 - TestEditableReactComponent(
237 - simulateIE11,
238 - Scenario_Composition,
239 - Expected_IE11,
240 - );
253 + TestEditableReactComponent(simulateIE11, Test_Scenario, Expected_IE11);
254 });
255 });