Implement `syncState` (WIP)
This commit is contained in:
parent
41c633e124
commit
596d868cc5
|
@ -12,12 +12,13 @@
|
||||||
import { createHistory } from './history.js'
|
import { createHistory } from './history.js'
|
||||||
import Node from './JSONNode.svelte'
|
import Node from './JSONNode.svelte'
|
||||||
import { existsIn, getIn, setIn } from './utils/immutabilityHelpers.js'
|
import { existsIn, getIn, setIn } from './utils/immutabilityHelpers.js'
|
||||||
import { compileJSONPointer } from './utils/jsonPointer.js'
|
import { compileJSONPointer, parseJSONPointer } from './utils/jsonPointer.js'
|
||||||
import { keyComboFromEvent } from './utils/keyBindings.js'
|
import { keyComboFromEvent } from './utils/keyBindings.js'
|
||||||
import { flattenSearch, search } from './utils/search.js'
|
import { flattenSearch, search } from './utils/search.js'
|
||||||
import { immutableJSONPatch } from './utils/immutableJSONPatch'
|
import { immutableJSONPatch } from './utils/immutableJSONPatch'
|
||||||
import { isEqual, isNumber } from 'lodash-es'
|
import { isEqual, isNumber, initial, last } from 'lodash-es'
|
||||||
import jump from './assets/jump.js/src/jump.js'
|
import jump from './assets/jump.js/src/jump.js'
|
||||||
|
import { syncState } from './utils/syncState.js'
|
||||||
|
|
||||||
let divContents
|
let divContents
|
||||||
|
|
||||||
|
@ -28,15 +29,20 @@
|
||||||
console.timeEnd('render')
|
console.timeEnd('render')
|
||||||
})
|
})
|
||||||
|
|
||||||
export let json = {}
|
export let json = {} // TODO: rename 'json' to 'document'
|
||||||
export let onChangeJson = () => {
|
export let onChangeJson = () => {}
|
||||||
|
|
||||||
|
function expand (path) {
|
||||||
|
return path.length < 1
|
||||||
}
|
}
|
||||||
|
|
||||||
const INITIAL_STATE = {
|
let state
|
||||||
[STATE_EXPANDED]: true
|
|
||||||
}
|
|
||||||
|
|
||||||
let state = INITIAL_STATE
|
$: {
|
||||||
|
console.time('syncState')
|
||||||
|
state = syncState(json, state, [], expand)
|
||||||
|
console.timeEnd('syncState')
|
||||||
|
}
|
||||||
|
|
||||||
let showSearch = false
|
let showSearch = false
|
||||||
let searchText = ''
|
let searchText = ''
|
||||||
|
@ -54,39 +60,85 @@
|
||||||
|
|
||||||
export function set(newJson) {
|
export function set(newJson) {
|
||||||
json = newJson
|
json = newJson
|
||||||
state = INITIAL_STATE
|
state = undefined
|
||||||
history.clear()
|
history.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyPatch (operations) {
|
|
||||||
const patchResult = immutableJSONPatch(json, operations)
|
|
||||||
json = patchResult.json
|
|
||||||
|
|
||||||
state = immutableJSONPatch(state, operations).json
|
|
||||||
|
|
||||||
return patchResult
|
|
||||||
}
|
|
||||||
|
|
||||||
export function patch(operations) {
|
export function patch(operations) {
|
||||||
console.log('patch', operations)
|
const prevState = state
|
||||||
|
|
||||||
const patchResult = applyPatch(operations)
|
const documentPatchResult = immutableJSONPatch(json, operations)
|
||||||
|
const statePatchResult = immutableJSONPatch(state, operations)
|
||||||
|
// TODO: only apply operations to state for relevant operations: move, copy, delete? Figure out
|
||||||
|
|
||||||
history.add({
|
json = documentPatchResult.json
|
||||||
undo: patchResult.revert,
|
state = statePatchResult.json
|
||||||
redo: operations
|
|
||||||
|
// if a property is renamed (move operation), rename it in the object's props
|
||||||
|
// so it maintains its identity and hence its index
|
||||||
|
operations
|
||||||
|
.filter(operation => {
|
||||||
|
return operation.op === 'move' && isEqual(
|
||||||
|
initial(parseJSONPointer(operation.from)),
|
||||||
|
initial(parseJSONPointer(operation.path))
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.forEach(operation => {
|
||||||
|
const pathFrom = parseJSONPointer(operation.from)
|
||||||
|
const to = parseJSONPointer(operation.path)
|
||||||
|
const parentPath = initial(pathFrom)
|
||||||
|
const oldKey = last(pathFrom)
|
||||||
|
const newKey = last(to)
|
||||||
|
const props = getIn(state, parentPath.concat(STATE_PROPS))
|
||||||
|
const index = props.findIndex(item => item.key === oldKey)
|
||||||
|
if (index !== -1) {
|
||||||
|
state = setIn(state, parentPath.concat([STATE_PROPS, index, 'key']), newKey)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
json = patchResult.json
|
history.add({
|
||||||
|
undo: documentPatchResult.revert,
|
||||||
|
redo: operations,
|
||||||
|
prevState: prevState,
|
||||||
|
state: state
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
json,
|
json,
|
||||||
error: patchResult.error,
|
error: documentPatchResult.error,
|
||||||
undo: patchResult.revert,
|
undo: documentPatchResult.revert,
|
||||||
redo: operations
|
redo: operations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleUndo() {
|
||||||
|
if (history.getState().canUndo) {
|
||||||
|
const item = history.undo()
|
||||||
|
if (item) {
|
||||||
|
json = immutableJSONPatch(json, item.undo).json
|
||||||
|
state = item.prevState
|
||||||
|
|
||||||
|
console.log('undo', { item, json, state })
|
||||||
|
|
||||||
|
emitOnChange()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRedo() {
|
||||||
|
if (history.getState().canRedo) {
|
||||||
|
const item = history.redo()
|
||||||
|
if (item) {
|
||||||
|
json = immutableJSONPatch(json, item.redo).json
|
||||||
|
state = item.state
|
||||||
|
|
||||||
|
console.log('redo', { item, json, state })
|
||||||
|
|
||||||
|
emitOnChange()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function doSearch(json, searchText) {
|
function doSearch(json, searchText) {
|
||||||
return search(null, json, searchText)
|
return search(null, json, searchText)
|
||||||
}
|
}
|
||||||
|
@ -177,32 +229,13 @@
|
||||||
showSearch = !showSearch
|
showSearch = !showSearch
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleUndo() {
|
|
||||||
if (history.getState().canUndo) {
|
|
||||||
const item = history.undo()
|
|
||||||
if (item) {
|
|
||||||
applyPatch(item.undo)
|
|
||||||
emitOnChange()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleRedo() {
|
|
||||||
if (history.getState().canRedo) {
|
|
||||||
const item = history.redo()
|
|
||||||
if (item) {
|
|
||||||
applyPatch(item.redo)
|
|
||||||
emitOnChange()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle expanded state of a node
|
* Toggle expanded state of a node
|
||||||
* @param {Path} path
|
* @param {Path} path
|
||||||
* @param {boolean} expanded
|
* @param {boolean} expanded
|
||||||
*/
|
*/
|
||||||
function handleExpand (path, expanded) {
|
function handleExpand (path, expanded) {
|
||||||
|
console.log('handleExpand', path, expanded)
|
||||||
state = setIn(state, path.concat(STATE_EXPANDED), expanded)
|
state = setIn(state, path.concat(STATE_EXPANDED), expanded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,15 +248,6 @@
|
||||||
state = setIn(state, path.concat(STATE_LIMIT), limit)
|
state = setIn(state, path.concat(STATE_LIMIT), limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Update object properties
|
|
||||||
* @param {Path} path
|
|
||||||
* @param {Object} props
|
|
||||||
*/
|
|
||||||
function handleUpdateProps (path, props) {
|
|
||||||
state = setIn(state, path.concat(STATE_PROPS), props)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expand all nodes on given path
|
* Expand all nodes on given path
|
||||||
* @param {Path} path
|
* @param {Path} path
|
||||||
|
@ -341,7 +365,6 @@
|
||||||
onPatch={handlePatch}
|
onPatch={handlePatch}
|
||||||
onExpand={handleExpand}
|
onExpand={handleExpand}
|
||||||
onLimit={handleLimit}
|
onLimit={handleLimit}
|
||||||
onUpdateProps={handleUpdateProps}
|
|
||||||
/>
|
/>
|
||||||
<div class='bottom'></div>
|
<div class='bottom'></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { isObject } from 'lodash-es'
|
import { debounce } from 'lodash-es'
|
||||||
import { onMount } from 'svelte'
|
|
||||||
import {
|
import {
|
||||||
DEBOUNCE_DELAY, DEFAULT_LIMIT,
|
DEBOUNCE_DELAY, DEFAULT_LIMIT,
|
||||||
STATE_EXPANDED, STATE_LIMIT, STATE_PROPS,
|
STATE_EXPANDED, STATE_LIMIT, STATE_PROPS,
|
||||||
|
@ -11,7 +10,6 @@
|
||||||
import Icon from 'svelte-awesome'
|
import Icon from 'svelte-awesome'
|
||||||
import { faCaretDown, faCaretRight } from '@fortawesome/free-solid-svg-icons'
|
import { faCaretDown, faCaretRight } from '@fortawesome/free-solid-svg-icons'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import debounce from 'lodash/debounce'
|
|
||||||
import { findUniqueName } from './utils/stringUtils.js'
|
import { findUniqueName } from './utils/stringUtils.js'
|
||||||
import { isUrl, stringConvert, valueType } from './utils/typeUtils'
|
import { isUrl, stringConvert, valueType } from './utils/typeUtils'
|
||||||
import { updateProps } from './utils/updateProps.js'
|
import { updateProps } from './utils/updateProps.js'
|
||||||
|
@ -26,11 +24,10 @@
|
||||||
export let onChangeKey
|
export let onChangeKey
|
||||||
export let onExpand
|
export let onExpand
|
||||||
export let onLimit
|
export let onLimit
|
||||||
export let onUpdateProps
|
|
||||||
|
|
||||||
$: expanded = state && state[STATE_EXPANDED]
|
$: expanded = state && state[STATE_EXPANDED]
|
||||||
$: limit = state && state[STATE_LIMIT] || DEFAULT_LIMIT
|
$: limit = state && state[STATE_LIMIT]
|
||||||
$: props = state && state[STATE_PROPS] || []
|
$: props = state && state[STATE_PROPS]
|
||||||
|
|
||||||
const escapeUnicode = false // TODO: pass via options
|
const escapeUnicode = false // TODO: pass via options
|
||||||
|
|
||||||
|
@ -39,17 +36,6 @@
|
||||||
|
|
||||||
$: type = valueType (value)
|
$: type = valueType (value)
|
||||||
|
|
||||||
let prevValue = undefined
|
|
||||||
// let props = undefined
|
|
||||||
|
|
||||||
$: if (isObject(value) && value !== prevValue) {
|
|
||||||
prevValue = value
|
|
||||||
const updatedProps = updateProps(value, props)
|
|
||||||
onUpdateProps(path, updatedProps)
|
|
||||||
}
|
|
||||||
|
|
||||||
// $: console.log('props', props)
|
|
||||||
|
|
||||||
$: limited = type === 'array' && value.length > limit
|
$: limited = type === 'array' && value.length > limit
|
||||||
|
|
||||||
$: items = type === 'array'
|
$: items = type === 'array'
|
||||||
|
@ -183,6 +169,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: can we do a handleChangeKey in the child again?
|
||||||
function handleChangeKey (newChildKey, oldChildKey) {
|
function handleChangeKey (newChildKey, oldChildKey) {
|
||||||
if (type === 'object') {
|
if (type === 'object') {
|
||||||
// make sure the key is not a duplicate of an other property
|
// make sure the key is not a duplicate of an other property
|
||||||
|
@ -190,18 +177,18 @@
|
||||||
|
|
||||||
if (uniqueNewChildKey !== oldChildKey) {
|
if (uniqueNewChildKey !== oldChildKey) {
|
||||||
// we need to make sure that the renamed property will keep the same id
|
// we need to make sure that the renamed property will keep the same id
|
||||||
const index = props.findIndex(item => item.key === oldChildKey)
|
// const index = props.findIndex(item => item.key === oldChildKey)
|
||||||
if (index !== -1) {
|
// if (index !== -1) {
|
||||||
// we use splice here to replace the old key with the new new one
|
// // we use splice here to replace the old key with the new new one
|
||||||
// already without Svelte noticing it (no assignment), so we prevent
|
// // already without Svelte noticing it (no assignment), so we prevent
|
||||||
// a needless render. We keep the same id, so the child HTML will be
|
// // a needless render. We keep the same id, so the child HTML will be
|
||||||
// reused
|
// // reused
|
||||||
// TODO: is there a better way to do this?
|
// // TODO: is there a better way to do this?
|
||||||
props.splice(index, 1, {
|
// props.splice(index, 1, {
|
||||||
id: props[index].id,
|
// id: props[index].id,
|
||||||
key: uniqueNewChildKey
|
// key: uniqueNewChildKey
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
onPatch([{
|
onPatch([{
|
||||||
op: 'move',
|
op: 'move',
|
||||||
|
@ -260,7 +247,6 @@
|
||||||
onPatch={onPatch}
|
onPatch={onPatch}
|
||||||
onExpand={onExpand}
|
onExpand={onExpand}
|
||||||
onLimit={onLimit}
|
onLimit={onLimit}
|
||||||
onUpdateProps={onUpdateProps}
|
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
{#if limited}
|
{#if limited}
|
||||||
|
@ -298,7 +284,7 @@
|
||||||
<span class="delimiter">{</span>
|
<span class="delimiter">{</span>
|
||||||
{:else}
|
{:else}
|
||||||
<span class="delimiter"> {</span>
|
<span class="delimiter"> {</span>
|
||||||
<button class="tag" on:click={() => onExpand(path, true)}>{props.length} props</button>
|
<button class="tag" on:click={() => onExpand(path, true)}>{Object.keys(value).length} props</button>
|
||||||
<span class="delimiter">}</span>
|
<span class="delimiter">}</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -315,7 +301,6 @@
|
||||||
onPatch={onPatch}
|
onPatch={onPatch}
|
||||||
onExpand={onExpand}
|
onExpand={onExpand}
|
||||||
onLimit={onLimit}
|
onLimit={onLimit}
|
||||||
onUpdateProps={onUpdateProps}
|
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
const MAX_HISTORY_ITEMS = 1000
|
const MAX_HISTORY_ITEMS = 1000
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} HistoryItem
|
* @typedef {*} HistoryItem
|
||||||
* @property {Object} undo
|
* @property {Object} undo
|
||||||
* @property {Object} redo
|
* @property {Object} redo
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { STATE_EXPANDED, STATE_LIMIT } from '../constants.js'
|
||||||
import { isObjectOrArray } from './typeUtils.js'
|
import { isObjectOrArray } from './typeUtils.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,7 +20,17 @@ import { isObjectOrArray } from './typeUtils.js'
|
||||||
export function shallowClone (value) {
|
export function shallowClone (value) {
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
// copy array items
|
// copy array items
|
||||||
return value.slice()
|
const copy = value.slice()
|
||||||
|
|
||||||
|
// FIXME: this is very ugly hack to copy properties attached to an Array!
|
||||||
|
if (value[STATE_EXPANDED] !== undefined) {
|
||||||
|
copy[STATE_EXPANDED] = value[STATE_EXPANDED]
|
||||||
|
}
|
||||||
|
if (value[STATE_LIMIT] !== undefined) {
|
||||||
|
copy[STATE_LIMIT] = value[STATE_LIMIT]
|
||||||
|
}
|
||||||
|
|
||||||
|
return copy
|
||||||
}
|
}
|
||||||
else if (typeof value === 'object') {
|
else if (typeof value === 'object') {
|
||||||
// copy object properties
|
// copy object properties
|
||||||
|
|
|
@ -6,8 +6,8 @@ import {
|
||||||
setIn
|
setIn
|
||||||
} from './immutabilityHelpers.js'
|
} from './immutabilityHelpers.js'
|
||||||
import { compileJSONPointer, parseJSONPointer } from './jsonPointer.js'
|
import { compileJSONPointer, parseJSONPointer } from './jsonPointer.js'
|
||||||
import initial from 'lodash/initial.js'
|
import initial from 'lodash/initial.js' // FIXME
|
||||||
import isEqual from 'lodash/isEqual.js'
|
import isEqual from 'lodash/isEqual.js' // FIXME
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply a patch to a JSON object
|
* Apply a patch to a JSON object
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
import {
|
||||||
|
DEFAULT_LIMIT,
|
||||||
|
STATE_EXPANDED,
|
||||||
|
STATE_LIMIT,
|
||||||
|
STATE_PROPS
|
||||||
|
} from '../constants.js'
|
||||||
|
import { isObject, isObjectOrArray } from './typeUtils.js'
|
||||||
|
import { updateProps } from './updateProps.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {JSON} document
|
||||||
|
* @param {JSON | undefined} state
|
||||||
|
* @param {Path} path
|
||||||
|
* @param {function (path: Path) : boolean} expand
|
||||||
|
* @returns {JSON | undefined}
|
||||||
|
*/
|
||||||
|
export function syncState (document, state = undefined, path, expand) {
|
||||||
|
if (isObject(document)) {
|
||||||
|
const updatedState = {}
|
||||||
|
|
||||||
|
updatedState[STATE_PROPS] = updateProps(document, state && state[STATE_PROPS])
|
||||||
|
updatedState[STATE_EXPANDED] = state
|
||||||
|
? state[STATE_EXPANDED]
|
||||||
|
: expand(path)
|
||||||
|
|
||||||
|
if (updatedState[STATE_EXPANDED]) {
|
||||||
|
Object.keys(document).forEach(key => {
|
||||||
|
const childDocument = document[key]
|
||||||
|
if (isObjectOrArray(childDocument)) {
|
||||||
|
const childState = state && state[key]
|
||||||
|
updatedState[key] = syncState(childDocument, childState, path.concat(key), expand)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedState
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(document)) {
|
||||||
|
const updatedState = []
|
||||||
|
|
||||||
|
// TODO: can we make the state for array a regular object { limit: 100, items: [...] }?
|
||||||
|
|
||||||
|
updatedState[STATE_LIMIT] = state
|
||||||
|
? state[STATE_LIMIT]
|
||||||
|
: DEFAULT_LIMIT
|
||||||
|
|
||||||
|
updatedState[STATE_EXPANDED] = state
|
||||||
|
? state[STATE_EXPANDED]
|
||||||
|
: expand(path)
|
||||||
|
|
||||||
|
if (updatedState[STATE_EXPANDED]) {
|
||||||
|
for (let i = 0; i < Math.min(document.length, updatedState[STATE_LIMIT]); i++) {
|
||||||
|
const childDocument = document[i]
|
||||||
|
if (isObjectOrArray(childDocument)) {
|
||||||
|
const childState = state && state[i]
|
||||||
|
updatedState[i] = syncState(childDocument, childState, path.concat(i), expand)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedState
|
||||||
|
}
|
||||||
|
|
||||||
|
// primitive values have no state
|
||||||
|
return undefined
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
import {
|
||||||
|
DEFAULT_LIMIT,
|
||||||
|
STATE_EXPANDED,
|
||||||
|
STATE_LIMIT,
|
||||||
|
STATE_PROPS
|
||||||
|
} from '../constants.js'
|
||||||
|
import { syncState } from './syncState.js'
|
||||||
|
import { expect } from './testUtils.js' // FIXME: replace jest with mocha tests, or move to jest
|
||||||
|
|
||||||
|
const test = it // TODO: replace jest with mocha tests, or move to jest
|
||||||
|
|
||||||
|
test('syncState', () => {
|
||||||
|
const document = {
|
||||||
|
array: [1, 2, {c: 6}],
|
||||||
|
object: {a: 4, b: 5},
|
||||||
|
value: 'hello'
|
||||||
|
}
|
||||||
|
|
||||||
|
function expand (path) {
|
||||||
|
return path.length <= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = syncState(document, undefined, [], expand)
|
||||||
|
|
||||||
|
const expectedState = {}
|
||||||
|
expectedState[STATE_EXPANDED] = true
|
||||||
|
expectedState[STATE_PROPS] = [
|
||||||
|
{ 'id': '1', 'key': 'array' },
|
||||||
|
{ 'id': '2', 'key': 'object' },
|
||||||
|
{ 'id': '3', 'key': 'value' }
|
||||||
|
]
|
||||||
|
expectedState.array = []
|
||||||
|
expectedState.array[STATE_EXPANDED] = true
|
||||||
|
expectedState.array[STATE_LIMIT] = DEFAULT_LIMIT
|
||||||
|
expectedState.array[2] = {}
|
||||||
|
expectedState.array[2][STATE_EXPANDED] = false
|
||||||
|
expectedState.array[2][STATE_PROPS] = [
|
||||||
|
{ 'id': '4', 'key': 'c' } // FIXME: props should not be created because node is not expande
|
||||||
|
]
|
||||||
|
expectedState.object = {}
|
||||||
|
expectedState.object[STATE_EXPANDED] = true
|
||||||
|
expectedState.object[STATE_PROPS] = [
|
||||||
|
{ 'id': '5', 'key': 'a' },
|
||||||
|
{ 'id': '6', 'key': 'b' }
|
||||||
|
]
|
||||||
|
|
||||||
|
expect(state).toEqual(expectedState)
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: write more unit tests for syncState
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { isObject } from './typeUtils.js'
|
import { isObject } from './typeUtils.js'
|
||||||
import uniqueId from 'lodash/uniqueId.js'
|
import uniqueId from 'lodash/uniqueId.js' // FIXME
|
||||||
|
|
||||||
export function updateProps (value, prevProps) {
|
export function updateProps (value, prevProps) {
|
||||||
if (isObject(value)) {
|
if (isObject(value)) {
|
||||||
|
|
|
@ -9,7 +9,6 @@ test('updateProps (1)', () => {
|
||||||
|
|
||||||
const props2 = updateProps({a: 1, b: 2}, props1)
|
const props2 = updateProps({a: 1, b: 2}, props1)
|
||||||
expect(props2.map(item => item.key)).toEqual(['b', 'a'])
|
expect(props2.map(item => item.key)).toEqual(['b', 'a'])
|
||||||
expect(props2[0].id).toEqual('1')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test('updateProps (2)', () => {
|
test('updateProps (2)', () => {
|
||||||
|
|
Loading…
Reference in New Issue