Skip to content

Commit

Permalink
Added support for 'ref' properties (jorgebucaran#184)
Browse files Browse the repository at this point in the history
  • Loading branch information
mcjazzyfunky committed Mar 30, 2021
1 parent 61806f5 commit dfd95f5
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 3 deletions.
22 changes: 19 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ var patchProperty = (node, key, oldValue, newValue, isSvg) => {
} else if (!oldValue) {
node.addEventListener(key, listener)
}
} else if (key === "ref") {
if (newValue !== oldValue) {
oldValue && passToRef(oldValue, null)
newValue && passToRef(newValue, node)
}
} else if (!isSvg && key !== "list" && key !== "form" && key in node) {
node[key] = newValue == null ? "" : newValue
} else if (newValue == null || newValue === false) {
Expand Down Expand Up @@ -60,13 +65,14 @@ var patchNode = (parent, node, oldVNode, newVNode, isSvg) => {
) {
if (oldVNode.tag !== newVNode.tag) node.nodeValue = newVNode.tag
} else if (oldVNode == null || oldVNode.tag !== newVNode.tag) {
oldVNode && dismissVNode(oldVNode)

node = parent.insertBefore(
createNode((newVNode = vdomify(newVNode)), isSvg),
node
)
if (oldVNode != null) {
parent.removeChild(oldVNode.node)
}

oldVNode && parent.removeChild(oldVNode.node)
} else {
var tmpVKid,
oldVKid,
Expand Down Expand Up @@ -136,6 +142,7 @@ var patchNode = (parent, node, oldVNode, newVNode, isSvg) => {
}
} else if (newHead > newTail) {
while (oldHead <= oldTail) {
dismissVNode(oldVKids[oldHead])
node.removeChild(oldVKids[oldHead++].node)
}
} else {
Expand All @@ -154,6 +161,7 @@ var patchNode = (parent, node, oldVNode, newVNode, isSvg) => {
(newKey != null && newKey === getKey(oldVKids[oldHead + 1]))
) {
if (oldKey == null) {
dismissVNode(oldVKid)
node.removeChild(oldVKid.node)
}
oldHead++
Expand Down Expand Up @@ -203,12 +211,14 @@ var patchNode = (parent, node, oldVNode, newVNode, isSvg) => {

while (oldHead <= oldTail) {
if (getKey((oldVKid = oldVKids[oldHead++])) == null) {
dismissVNode(oldVKid)
node.removeChild(oldVKid.node)
}
}

for (var i in keyed) {
if (newKeyed[i] == null) {
dismissVNode(keyed[i])
node.removeChild(keyed[i].node)
}
}
Expand All @@ -221,6 +231,12 @@ var patchNode = (parent, node, oldVNode, newVNode, isSvg) => {
var vdomify = (newVNode) =>
newVNode !== true && newVNode !== false && newVNode ? newVNode : text("")

var passToRef = (ref, value) =>
ref && (typeof ref === 'function' ? ref(value) : (ref.current = value))

var dismissVNode = (vnode) =>
vnode && vnode.props && vnode.props.ref && passToRef(vnode.props.ref, null)

var recycleNode = (node) =>
node.nodeType === TEXT_NODE
? text(node.nodeValue, node)
Expand Down
140 changes: 140 additions & 0 deletions tests/refs.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { t as tw, equal } from "twist"
import { JSDOM } from "jsdom"
import { h, patch } from "../index.js"

// we enhance twist's original function `t`
// with support for generator functions as source
// for tests as alternative to arrays
function t(name, tests) {
return tw(name, Array.isArray(tests) ? tests : Array.from(tests()))
}

// === ref helpers ====================================================

function createRefObject() {
let current = null
let previous = null

return {
get current() {
return current
},

set current(value) {
previous = current
current = value
},

get previous() {
return previous
}
}
}

function createRefCallback() {
let current = null
let previous = null

const ret = (value) => {
previous = current
current = value
}

Object.defineProperties(ret, {
current: { get: () => current },
previous: { get: () => previous }
})

return ret
}

// === tests =========================================================

export default [
t("ref support for superfine", [
t("test ref helpers (only needed for testing)", [
t("testing createRefObject", function* () {
const refObject = createRefObject()
yield equal(refObject.current, null)
yield equal(refObject.previous, null)

refObject.current = 11
yield equal(refObject.current, 11)
yield equal(refObject.previous, null)

refObject.current = 22
yield equal(refObject.current, 22)
yield equal(refObject.previous, 11)

refObject.current = 33
yield equal(refObject.current, 33)
yield equal(refObject.previous, 22)
}),
t("testing createRefCallback", function* () {
const refCallback = createRefCallback()
yield equal(refCallback.current, null)
yield equal(refCallback.previous, null)

refCallback(111)
yield equal(refCallback.current, 111)
yield equal(refCallback.previous, null)

refCallback(222)
yield equal(refCallback.current, 222)
yield equal(refCallback.previous, 111)

refCallback(333)
yield equal(refCallback.current, 333)
yield equal(refCallback.previous, 222)
}),
]),
t("test behavior of refs", [
t("ref objects and ref callback should work properly", function* () {
const container = new JSDOM('<div id="root"><span><span></div>')
.window.document.getElementById('root')

const render = (vnode) => patch(container.firstChild, vnode)
const refObject = createRefObject()
const refCallback = createRefCallback()

container.appendChild(document.createElement("span"))

render(h("div", { ref: refObject }))
yield equal(refObject.current?.tagName, "DIV")
yield equal(refObject.previous, null)
yield equal(refCallback.current, null)
yield equal(refCallback.previous, null)

render(h("input", { ref: refCallback }))
yield equal(refObject.current, null)
yield equal(refObject.previous?.tagName ,"DIV")
yield equal(refCallback.current?.tagName ,"INPUT")
yield equal(refCallback.previous, null)

render(h("input", { ref: refCallback }))
yield equal(refObject.current, null)
yield equal(refObject.previous?.tagName, "DIV")
yield equal(refCallback.current?.tagName, "INPUT")
yield equal(refCallback.previous, null)

render(h("button", { ref: refObject }))
yield equal(refObject.current?.tagName, "BUTTON")
yield equal(refObject.previous, null)
yield equal(refCallback.current, null)
yield equal(refCallback.previous?.tagName, "INPUT")

render(h("button", { ref: refCallback }))
yield equal(refObject.current, null)
yield equal(refObject.previous?.tagName, "BUTTON")
yield equal(refCallback.current?.tagName ,"BUTTON")
yield equal(refCallback.previous, null)

render(h("input", { ref: refCallback }))
yield equal(refObject.current, null)
yield equal(refObject.previous?.tagName, "BUTTON")
yield equal(refCallback.current?.tagName, "INPUT")
yield equal(refCallback.previous, null)
})
])
])
]

0 comments on commit dfd95f5

Please sign in to comment.