Skip to content

Commit

Permalink
Handle composition events in ChangeEventPlugin
Browse files Browse the repository at this point in the history
  • Loading branch information
yesmeck committed Jan 8, 2017
1 parent f589274 commit ae54338
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 2 deletions.
6 changes: 6 additions & 0 deletions scripts/fiber/tests-passing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,12 @@ src/renderers/dom/shared/eventPlugins/__tests__/ChangeEventPlugin-test.js
* should deduplicate input value change events
* should listen for both change and input events when supported
* should only fire events when the value changes for range inputs
* should only fire change once on Webkit
* should only fire change once on Firefox
* should only fire change once on IE9
* should only fire change once on IE10
* should only fire change once on IE11
* should only fire change once on Edge

src/renderers/dom/shared/eventPlugins/__tests__/EnterLeaveEventPlugin-test.js
* should set relatedTarget properly in iframe
Expand Down
34 changes: 32 additions & 2 deletions src/renderers/dom/shared/eventPlugins/ChangeEventPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ var eventTypes = {
'topKeyDown',
'topKeyUp',
'topSelectionChange',
'topCompositionStart',
'topCompositionUpdate',
'topCompositionEnd',
],
},
};
Expand All @@ -62,7 +65,10 @@ function createAndAccumulateChangeEvent(inst, nativeEvent, target) {
var activeElement = null;
var activeElementInst = null;


/**
* For composition events
*/
var lastTopLevelType = null;

/**
* SECTION: handle `change` event
Expand Down Expand Up @@ -281,14 +287,37 @@ function getTargetInstForInputOrChangeEvent(
topLevelType,
targetInst
) {
if (
if (inComposition(topLevelType)) {
return;
} else if (
topLevelType === 'topInput' &&
lastTopLevelType === 'topCompositionEnd'
) {
return getInstIfValueChanged(targetInst);
} else if (
// Webkit fires 'compositionEnd' event after 'input' event.
topLevelType === 'topKeyUp' &&
lastTopLevelType === 'topCompositionEnd'
) {
return getInstIfValueChanged(targetInst);
} else if (
topLevelType === 'topInput' ||
topLevelType === 'topChange'
) {
return getInstIfValueChanged(targetInst);
}
}

var isComposing = false;
function inComposition(topLevelType) {
if (topLevelType === 'topCompositionStart') {
isComposing = true;
} else if (topLevelType === 'topCompositionEnd') {
isComposing = false;
}
return isComposing;
}

/**
* This plugin creates an `onChange` event that normalizes change events
* across form elements. This event fires at a time when it's possible to
Expand Down Expand Up @@ -334,6 +363,7 @@ var ChangeEventPlugin = {

if (getTargetInstFunc) {
var inst = getTargetInstFunc(topLevelType, targetInst);
lastTopLevelType = topLevelType;
if (inst) {
var event = createAndAccumulateChangeEvent(
inst,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,156 @@ describe('ChangeEventPlugin', () => {
ReactTestUtils.SimulateNative.change(input);
expect(called).toBe(2);
});

describe('composition events', () => {
function simulateEvent(inst, event) {
ReactTestUtils.SimulateNative[event](inst);
}

function TestCompositionEvent(Scenario) {
var called = 0;
var value = null;

function cb(e) {
called += 1;
value = e.target.value;
}

var input = ReactTestUtils.renderIntoDocument(
<input type="text" onChange={cb} />
);

Scenario.forEach(el => {
el.run.apply(null, [input].concat(el.args))
});

expect(called).toBe(1);
expect(value).toBe('你');
}

var Scenario = {
Webkit: [
{ run: setUntrackedValue, args: [ 'n' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'compositionStart' ] },
{ run: simulateEvent, args: [ 'compositionUpdate' ] },
{ run: simulateEvent, args: [ 'input' ] },
{ run: simulateEvent, args: [ 'keyUp' ] },
{ run: setUntrackedValue, args: [ 'ni' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'compositionUpdate' ] },
{ run: simulateEvent, args: [ 'input' ] },
{ run: simulateEvent, args: [ 'keyUp' ] },
{ run: setUntrackedValue, args: [ '你' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'compositionUpdate' ] },
{ run: simulateEvent, args: [ 'textInput' ] },
{ run: simulateEvent, args: [ 'input' ] },
{ run: simulateEvent, args: [ 'compositionEnd' ] },
{ run: simulateEvent, args: [ 'keyUp' ] },
],
Firefox: [
{ run: setUntrackedValue, args: [ 'n' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'compositionStart' ] },
{ run: simulateEvent, args: [ 'compositionUpdate' ] },
{ run: simulateEvent, args: [ 'input' ] },
{ run: setUntrackedValue, args: [ 'ni' ] },
{ run: simulateEvent, args: [ 'compositionUpdate' ] },
{ run: simulateEvent, args: [ 'input' ] },
{ run: setUntrackedValue, args: [ '你' ] },
{ run: simulateEvent, args: [ 'compositionUpdate' ] },
{ run: simulateEvent, args: [ 'compositionEnd' ] },
{ run: simulateEvent, args: [ 'input' ] },
{ run: simulateEvent, args: [ 'keyUp' ] },
],
IE9: [
{ run: setUntrackedValue, args: [ 'n' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'compositionStart' ] },
{ run: simulateEvent, args: [ 'compositionUpdate' ] },
{ run: simulateEvent, args: [ 'keyUp' ] },
{ run: setUntrackedValue, args: [ 'ni' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'compositionUpdate' ] },
{ run: simulateEvent, args: [ 'keyUp' ] },
{ run: setUntrackedValue, args: [ '你' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'input' ] },
{ run: simulateEvent, args: [ 'compositionUpdate' ] },
{ run: simulateEvent, args: [ 'compositionEnd' ] },
{ run: simulateEvent, args: [ 'keyUp' ] },
],
IE10: [
{ run: setUntrackedValue, args: [ 'n' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'compositionStart' ] },
{ run: simulateEvent, args: [ 'keyUp' ] },
{ run: setUntrackedValue, args: [ 'ni' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'keyUp' ] },
{ run: setUntrackedValue, args: [ '你' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'keyUp' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'compositionEnd' ] },
{ run: simulateEvent, args: [ 'input' ] },
{ run: simulateEvent, args: [ 'keyUp' ] },
],
IE11: [
{ run: setUntrackedValue, args: [ 'n' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'compositionStart' ] },
{ run: simulateEvent, args: [ 'compositionUpdate' ] },
{ run: simulateEvent, args: [ 'input' ] },
{ run: simulateEvent, args: [ 'keyUp' ] },
{ run: setUntrackedValue, args: [ 'ni' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'input' ] },
{ run: simulateEvent, args: [ 'keyUp' ] },
{ run: setUntrackedValue, args: [ '你' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'compositionEnd' ] },
{ run: simulateEvent, args: [ 'input' ] },
{ run: simulateEvent, args: [ 'keyUp' ] },
],
Edge: [
{ run: setUntrackedValue, args: [ 'n' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'compositionStart' ] },
{ run: simulateEvent, args: [ 'keyUp' ] },
{ run: setUntrackedValue, args: [ 'ni' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'keyUp' ] },
{ run: setUntrackedValue, args: [ '你' ] },
{ run: simulateEvent, args: [ 'keyDown' ] },
{ run: simulateEvent, args: [ 'compositionEnd' ] },
{ run: simulateEvent, args: [ 'input' ] },
],
};

it('should only fire change once on Webkit', () => {
TestCompositionEvent(Scenario.Webkit);
});

it('should only fire change once on Firefox', () => {
TestCompositionEvent(Scenario.Firefox);
});

it('should only fire change once on IE9', () => {
TestCompositionEvent(Scenario.IE9);
});

it('should only fire change once on IE10', () => {
TestCompositionEvent(Scenario.IE10);
});

it('should only fire change once on IE11', () => {
TestCompositionEvent(Scenario.IE11);
});

it('should only fire change once on Edge', () => {
TestCompositionEvent(Scenario.Edge);
});
});
});

0 comments on commit ae54338

Please sign in to comment.