From afa320eb2874f300fdf7cd33dc20633fe3006d4c Mon Sep 17 00:00:00 2001 From: Ziad EL KHOURY HANNA Date: Wed, 31 Jan 2018 12:16:52 +0100 Subject: [PATCH] Add support for reverting the value of a controlled input or select --- lib/update-props.js | 28 +++++++++------- test/unit/patch.test.js | 72 +++++++++++++++++++++++++++++++++-------- 2 files changed, 76 insertions(+), 24 deletions(-) diff --git a/lib/update-props.js b/lib/update-props.js index bbf5ea8..cd9434b 100644 --- a/lib/update-props.js +++ b/lib/update-props.js @@ -55,17 +55,23 @@ function updateProps (domNode, oldVirtualNode, oldProps, newVirtualNode, newProp updateNestedProps(domNode.style, oldValue, newValue, true) } else if (name === 'attributes') { updateAttributes(domNode, oldValue, newValue) - } else { - if (newValue !== oldValue) { - if (name !== 'innerHTML' && newVirtualNode && SVG_TAGS.has(newVirtualNode.tag)) { - domNode.setAttribute(SVG_ATTRIBUTE_TRANSLATIONS.get(name) || name, newValue) - } else if (newVirtualNode && newVirtualNode.tag === 'input' - && name === 'value' && domNode[name] === newValue) { - // Do not update `value` of an `input` unless it differs. - // Every change will reset the cursor position. - } else { - domNode[name] = newValue - } + } else if ( + name === 'value' && + newVirtualNode && ( + newVirtualNode.tag === 'input' || + newVirtualNode.tag === 'select' + ) + ) { + // Do not update `value` of an `input` unless it differs. + // Every change will reset the cursor position. + if (domNode[name] !== newValue) { + domNode[name] = newValue + } + } else if (newValue !== oldValue) { + if (name !== 'innerHTML' && newVirtualNode && SVG_TAGS.has(newVirtualNode.tag)) { + domNode.setAttribute(SVG_ATTRIBUTE_TRANSLATIONS.get(name) || name, newValue) + } else { + domNode[name] = newValue } } } diff --git a/test/unit/patch.test.js b/test/unit/patch.test.js index 826e666..3d499bc 100644 --- a/test/unit/patch.test.js +++ b/test/unit/patch.test.js @@ -66,26 +66,72 @@ describe('patch (oldVirtualNode, newVirtualNode)', () => { assert(!newNode.className) }) - it('correctly updates the `input.value` property', function () { + describe('`input.value` property', function () { + it('conserves the selection when possible', function () { + const virtualNode1 = + const element = render(virtualNode1) + + // Assume the user changed the value to `ping` by + // moving the cursor after the `i` and adding `n`. + // The new value is now `ping` and the cursor + // position is after the `n` on index 3 + element.value = 'ping' + element.selectionStart = 3 + element.selectionEnd = 3 + + // Assume that the input is a "controlled" input so + // it updates the virtual node with the same value + const virtualNode2 = + patch(virtualNode1, virtualNode2) + + // the selection should have stayed in the same position + assert.equal(element.selectionStart, 3) + assert.equal(element.selectionEnd, 3) + }) + + it('correctly updates the `select.value` property', function () { + const virtualNode1 = ( + + ) + const element = render(virtualNode1) + + // Assume the user changed the value to `b`. + element.value = 'b' + + // Assume that the select is a "controlled" select and + // the changes have been rejected, so the virtual node + // remains the same + const virtualNode2 = ( + + ) + patch(virtualNode1, virtualNode2) + + // the value should have been reverted + assert.equal(element.value, 'a') + }) + }) + + it('reverts when change is rejected', function () { const virtualNode1 = const element = render(virtualNode1) - // Assume the user changed the value to `ping` by - // moving the cursor after the `i` and adding `n`. - // The new value is now `ping` and the cursor - // position is after the `n` on index 3 + // Assume the user changed the value to `ping`. element.value = 'ping' - element.selectionStart = 3 - element.selectionEnd = 3 - // Assume that the input is a "controlled" input so - // it updates the virtual node with the same value - const virtualNode2 = + // Assume that the input is a "controlled" input and + // the changes have been rejected, so the virtual node + // remains the same + const virtualNode2 = patch(virtualNode1, virtualNode2) - // the selection should have stayed in the same position - assert.equal(element.selectionStart, 3) - assert.equal(element.selectionEnd, 3) + // the value should have been reverted + assert.equal(element.value, 'pig') }) it('allows attributes to be updated via the special `attributes` property', () => {