@wordpress/core-data 6.18.0 → 6.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/README.md +13 -1
- package/build/actions.js +34 -30
- package/build/actions.js.map +1 -1
- package/build/entity-provider.js +2 -99
- package/build/entity-provider.js.map +1 -1
- package/build/entity-types/helpers.js.map +1 -1
- package/build/footnotes/get-footnotes-order.js +35 -0
- package/build/footnotes/get-footnotes-order.js.map +1 -0
- package/build/footnotes/get-rich-text-values-cached.js +39 -0
- package/build/footnotes/get-rich-text-values-cached.js.map +1 -0
- package/build/footnotes/index.js +96 -0
- package/build/footnotes/index.js.map +1 -0
- package/build/hooks/use-entity-record.js +6 -3
- package/build/hooks/use-entity-record.js.map +1 -1
- package/build/hooks/use-resource-permissions.js.map +1 -1
- package/build/private-selectors.js +4 -17
- package/build/private-selectors.js.map +1 -1
- package/build/reducer.js +33 -145
- package/build/reducer.js.map +1 -1
- package/build/resolvers.js +20 -1
- package/build/resolvers.js.map +1 -1
- package/build/selectors.js +21 -25
- package/build/selectors.js.map +1 -1
- package/build/utils/get-nested-value.js +30 -0
- package/build/utils/get-nested-value.js.map +1 -0
- package/build/utils/index.js +7 -0
- package/build/utils/index.js.map +1 -1
- package/build/utils/set-nested-value.js +11 -6
- package/build/utils/set-nested-value.js.map +1 -1
- package/build-module/actions.js +32 -28
- package/build-module/actions.js.map +1 -1
- package/build-module/entity-provider.js +2 -99
- package/build-module/entity-provider.js.map +1 -1
- package/build-module/entity-types/helpers.js.map +1 -1
- package/build-module/footnotes/get-footnotes-order.js +27 -0
- package/build-module/footnotes/get-footnotes-order.js.map +1 -0
- package/build-module/footnotes/get-rich-text-values-cached.js +33 -0
- package/build-module/footnotes/get-rich-text-values-cached.js.map +1 -0
- package/build-module/footnotes/index.js +88 -0
- package/build-module/footnotes/index.js.map +1 -0
- package/build-module/hooks/use-entity-record.js +6 -3
- package/build-module/hooks/use-entity-record.js.map +1 -1
- package/build-module/hooks/use-resource-permissions.js.map +1 -1
- package/build-module/private-selectors.js +3 -15
- package/build-module/private-selectors.js.map +1 -1
- package/build-module/reducer.js +30 -144
- package/build-module/reducer.js.map +1 -1
- package/build-module/resolvers.js +18 -0
- package/build-module/resolvers.js.map +1 -1
- package/build-module/selectors.js +19 -29
- package/build-module/selectors.js.map +1 -1
- package/build-module/utils/get-nested-value.js +24 -0
- package/build-module/utils/get-nested-value.js.map +1 -0
- package/build-module/utils/index.js +1 -0
- package/build-module/utils/index.js.map +1 -1
- package/build-module/utils/set-nested-value.js +11 -6
- package/build-module/utils/set-nested-value.js.map +1 -1
- package/build-types/actions.d.ts +1 -6
- package/build-types/actions.d.ts.map +1 -1
- package/build-types/entity-provider.d.ts.map +1 -1
- package/build-types/footnotes/get-footnotes-order.d.ts +2 -0
- package/build-types/footnotes/get-footnotes-order.d.ts.map +1 -0
- package/build-types/footnotes/get-rich-text-values-cached.d.ts +2 -0
- package/build-types/footnotes/get-rich-text-values-cached.d.ts.map +1 -0
- package/build-types/footnotes/index.d.ts +4 -0
- package/build-types/footnotes/index.d.ts.map +1 -0
- package/build-types/hooks/use-entity-record.d.ts +2 -0
- package/build-types/hooks/use-entity-record.d.ts.map +1 -1
- package/build-types/index.d.ts +3 -2
- package/build-types/index.d.ts.map +1 -1
- package/build-types/private-selectors.d.ts +3 -13
- package/build-types/private-selectors.d.ts.map +1 -1
- package/build-types/reducer.d.ts +7 -23
- package/build-types/reducer.d.ts.map +1 -1
- package/build-types/resolvers.d.ts +4 -0
- package/build-types/resolvers.d.ts.map +1 -1
- package/build-types/selectors.d.ts +19 -14
- package/build-types/selectors.d.ts.map +1 -1
- package/build-types/utils/get-nested-value.d.ts +14 -0
- package/build-types/utils/get-nested-value.d.ts.map +1 -0
- package/build-types/utils/index.d.ts +1 -0
- package/build-types/utils/set-nested-value.d.ts +8 -4
- package/build-types/utils/set-nested-value.d.ts.map +1 -1
- package/package.json +16 -15
- package/src/actions.js +36 -26
- package/src/entity-provider.js +2 -134
- package/src/entity-types/helpers.ts +2 -2
- package/src/footnotes/get-footnotes-order.js +30 -0
- package/src/footnotes/get-rich-text-values-cached.js +35 -0
- package/src/footnotes/index.js +119 -0
- package/src/hooks/test/use-entity-record.js +4 -0
- package/src/hooks/use-entity-record.ts +12 -3
- package/src/hooks/use-resource-permissions.ts +1 -1
- package/src/private-selectors.ts +4 -17
- package/src/reducer.js +36 -155
- package/src/resolvers.js +25 -0
- package/src/selectors.ts +39 -51
- package/src/test/reducer.js +0 -233
- package/src/test/selectors.js +0 -54
- package/src/utils/get-nested-value.js +27 -0
- package/src/utils/index.js +1 -0
- package/src/utils/set-nested-value.js +12 -6
- package/src/utils/test/get-nested-value.js +61 -0
- package/src/utils/test/set-nested-value.js +7 -0
- package/tsconfig.json +1 -0
- package/tsconfig.tsbuildinfo +1 -1
package/src/test/reducer.js
CHANGED
|
@@ -9,7 +9,6 @@ import deepFreeze from 'deep-freeze';
|
|
|
9
9
|
import {
|
|
10
10
|
terms,
|
|
11
11
|
entities,
|
|
12
|
-
undo,
|
|
13
12
|
embedPreviews,
|
|
14
13
|
userPermissions,
|
|
15
14
|
autosaves,
|
|
@@ -142,238 +141,6 @@ describe( 'entities', () => {
|
|
|
142
141
|
} );
|
|
143
142
|
} );
|
|
144
143
|
|
|
145
|
-
describe( 'undo', () => {
|
|
146
|
-
let lastValues;
|
|
147
|
-
let undoState;
|
|
148
|
-
let expectedUndoState;
|
|
149
|
-
|
|
150
|
-
const createExpectedDiff = ( property, { from, to } ) => ( {
|
|
151
|
-
kind: 'someKind',
|
|
152
|
-
name: 'someName',
|
|
153
|
-
recordId: 'someRecordId',
|
|
154
|
-
property,
|
|
155
|
-
from,
|
|
156
|
-
to,
|
|
157
|
-
} );
|
|
158
|
-
const createNextEditAction = ( edits, isCached ) => {
|
|
159
|
-
let action = {
|
|
160
|
-
kind: 'someKind',
|
|
161
|
-
name: 'someName',
|
|
162
|
-
recordId: 'someRecordId',
|
|
163
|
-
edits,
|
|
164
|
-
};
|
|
165
|
-
action = {
|
|
166
|
-
type: 'EDIT_ENTITY_RECORD',
|
|
167
|
-
...action,
|
|
168
|
-
meta: {
|
|
169
|
-
undo: {
|
|
170
|
-
isCached,
|
|
171
|
-
edits: lastValues,
|
|
172
|
-
},
|
|
173
|
-
},
|
|
174
|
-
};
|
|
175
|
-
lastValues = { ...lastValues, ...edits };
|
|
176
|
-
return action;
|
|
177
|
-
};
|
|
178
|
-
const createNextUndoState = ( ...args ) => {
|
|
179
|
-
let action = {};
|
|
180
|
-
if ( args[ 0 ] === 'isUndo' || args[ 0 ] === 'isRedo' ) {
|
|
181
|
-
// We need to "apply" the undo level here and build
|
|
182
|
-
// the action to move the offset.
|
|
183
|
-
const lastEdits =
|
|
184
|
-
undoState.list[
|
|
185
|
-
undoState.list.length -
|
|
186
|
-
( args[ 0 ] === 'isUndo' ? 1 : 0 ) +
|
|
187
|
-
undoState.offset
|
|
188
|
-
];
|
|
189
|
-
lastEdits.forEach( ( { property, from, to } ) => {
|
|
190
|
-
lastValues[ property ] = args[ 0 ] === 'isUndo' ? from : to;
|
|
191
|
-
} );
|
|
192
|
-
action = {
|
|
193
|
-
type: args[ 0 ] === 'isUndo' ? 'UNDO' : 'REDO',
|
|
194
|
-
};
|
|
195
|
-
} else if ( args[ 0 ] === 'isCreate' ) {
|
|
196
|
-
action = { type: 'CREATE_UNDO_LEVEL' };
|
|
197
|
-
} else if ( args.length ) {
|
|
198
|
-
action = createNextEditAction( ...args );
|
|
199
|
-
}
|
|
200
|
-
return deepFreeze( undo( undoState, action ) );
|
|
201
|
-
};
|
|
202
|
-
beforeEach( () => {
|
|
203
|
-
lastValues = {};
|
|
204
|
-
undoState = undefined;
|
|
205
|
-
expectedUndoState = { list: [], offset: 0 };
|
|
206
|
-
} );
|
|
207
|
-
|
|
208
|
-
it( 'initializes', () => {
|
|
209
|
-
expect( createNextUndoState() ).toEqual( expectedUndoState );
|
|
210
|
-
} );
|
|
211
|
-
|
|
212
|
-
it( 'stacks undo levels', () => {
|
|
213
|
-
undoState = createNextUndoState();
|
|
214
|
-
|
|
215
|
-
// Check that the first edit creates an undo level for the current state and
|
|
216
|
-
// one for the new one.
|
|
217
|
-
undoState = createNextUndoState( { value: 1 } );
|
|
218
|
-
expectedUndoState.list.push( [
|
|
219
|
-
createExpectedDiff( 'value', { from: undefined, to: 1 } ),
|
|
220
|
-
] );
|
|
221
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
222
|
-
|
|
223
|
-
// Check that the second and third edits just create an undo level for
|
|
224
|
-
// themselves.
|
|
225
|
-
undoState = createNextUndoState( { value: 2 } );
|
|
226
|
-
expectedUndoState.list.push( [
|
|
227
|
-
createExpectedDiff( 'value', { from: 1, to: 2 } ),
|
|
228
|
-
] );
|
|
229
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
230
|
-
undoState = createNextUndoState( { value: 3 } );
|
|
231
|
-
expectedUndoState.list.push( [
|
|
232
|
-
createExpectedDiff( 'value', { from: 2, to: 3 } ),
|
|
233
|
-
] );
|
|
234
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
235
|
-
} );
|
|
236
|
-
|
|
237
|
-
it( 'stacks multi-property undo levels', () => {
|
|
238
|
-
undoState = createNextUndoState();
|
|
239
|
-
|
|
240
|
-
undoState = createNextUndoState( { value: 1 } );
|
|
241
|
-
undoState = createNextUndoState( { value2: 2 } );
|
|
242
|
-
expectedUndoState.list.push(
|
|
243
|
-
[ createExpectedDiff( 'value', { from: undefined, to: 1 } ) ],
|
|
244
|
-
[ createExpectedDiff( 'value2', { from: undefined, to: 2 } ) ]
|
|
245
|
-
);
|
|
246
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
247
|
-
|
|
248
|
-
// Check that that creating another undo level merges the "edits"
|
|
249
|
-
undoState = createNextUndoState( { value: 2 } );
|
|
250
|
-
expectedUndoState.list.push( [
|
|
251
|
-
createExpectedDiff( 'value', { from: 1, to: 2 } ),
|
|
252
|
-
] );
|
|
253
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
254
|
-
} );
|
|
255
|
-
|
|
256
|
-
it( 'handles undos/redos', () => {
|
|
257
|
-
undoState = createNextUndoState();
|
|
258
|
-
undoState = createNextUndoState( { value: 1 } );
|
|
259
|
-
undoState = createNextUndoState( { value: 2 } );
|
|
260
|
-
undoState = createNextUndoState( { value: 3 } );
|
|
261
|
-
expectedUndoState.list.push(
|
|
262
|
-
[ createExpectedDiff( 'value', { from: undefined, to: 1 } ) ],
|
|
263
|
-
[ createExpectedDiff( 'value', { from: 1, to: 2 } ) ],
|
|
264
|
-
[ createExpectedDiff( 'value', { from: 2, to: 3 } ) ]
|
|
265
|
-
);
|
|
266
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
267
|
-
|
|
268
|
-
// Check that undoing and redoing an equal
|
|
269
|
-
// number of steps does not lose edits.
|
|
270
|
-
undoState = createNextUndoState( 'isUndo' );
|
|
271
|
-
expectedUndoState.offset--;
|
|
272
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
273
|
-
undoState = createNextUndoState( 'isUndo' );
|
|
274
|
-
expectedUndoState.offset--;
|
|
275
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
276
|
-
undoState = createNextUndoState( 'isRedo' );
|
|
277
|
-
expectedUndoState.offset++;
|
|
278
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
279
|
-
undoState = createNextUndoState( 'isRedo' );
|
|
280
|
-
expectedUndoState.offset++;
|
|
281
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
282
|
-
|
|
283
|
-
// Check that another edit will go on top when there
|
|
284
|
-
// is no undo level offset.
|
|
285
|
-
undoState = createNextUndoState( { value: 4 } );
|
|
286
|
-
expectedUndoState.list.push( [
|
|
287
|
-
createExpectedDiff( 'value', { from: 3, to: 4 } ),
|
|
288
|
-
] );
|
|
289
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
290
|
-
|
|
291
|
-
// Check that undoing and editing will slice of
|
|
292
|
-
// all the levels after the current one.
|
|
293
|
-
undoState = createNextUndoState( 'isUndo' );
|
|
294
|
-
undoState = createNextUndoState( 'isUndo' );
|
|
295
|
-
|
|
296
|
-
undoState = createNextUndoState( { value: 5 } );
|
|
297
|
-
expectedUndoState.list.pop();
|
|
298
|
-
expectedUndoState.list.pop();
|
|
299
|
-
expectedUndoState.list.push( [
|
|
300
|
-
createExpectedDiff( 'value', { from: 2, to: 5 } ),
|
|
301
|
-
] );
|
|
302
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
303
|
-
} );
|
|
304
|
-
|
|
305
|
-
it( 'handles flattened undos/redos', () => {
|
|
306
|
-
undoState = createNextUndoState();
|
|
307
|
-
undoState = createNextUndoState( { value: 1 } );
|
|
308
|
-
undoState = createNextUndoState( { transientValue: 2 }, true );
|
|
309
|
-
undoState = createNextUndoState( { value: 3 } );
|
|
310
|
-
expectedUndoState.list.push(
|
|
311
|
-
[
|
|
312
|
-
createExpectedDiff( 'value', { from: undefined, to: 1 } ),
|
|
313
|
-
createExpectedDiff( 'transientValue', {
|
|
314
|
-
from: undefined,
|
|
315
|
-
to: 2,
|
|
316
|
-
} ),
|
|
317
|
-
],
|
|
318
|
-
[ createExpectedDiff( 'value', { from: 1, to: 3 } ) ]
|
|
319
|
-
);
|
|
320
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
321
|
-
} );
|
|
322
|
-
|
|
323
|
-
it( 'handles explicit undo level creation', () => {
|
|
324
|
-
undoState = createNextUndoState();
|
|
325
|
-
|
|
326
|
-
// Check that nothing happens if there are no pending
|
|
327
|
-
// transient edits.
|
|
328
|
-
undoState = createNextUndoState( { value: 1 } );
|
|
329
|
-
undoState = createNextUndoState( 'isCreate' );
|
|
330
|
-
expectedUndoState.list.push( [
|
|
331
|
-
createExpectedDiff( 'value', { from: undefined, to: 1 } ),
|
|
332
|
-
] );
|
|
333
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
334
|
-
|
|
335
|
-
// Check that transient edits are merged into the last
|
|
336
|
-
// edits.
|
|
337
|
-
undoState = createNextUndoState( { transientValue: 2 }, true );
|
|
338
|
-
undoState = createNextUndoState( 'isCreate' );
|
|
339
|
-
expectedUndoState.list[ expectedUndoState.list.length - 1 ].push(
|
|
340
|
-
createExpectedDiff( 'transientValue', { from: undefined, to: 2 } )
|
|
341
|
-
);
|
|
342
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
343
|
-
|
|
344
|
-
// Check that create after undo does nothing.
|
|
345
|
-
undoState = createNextUndoState( { value: 3 } );
|
|
346
|
-
undoState = createNextUndoState( 'isUndo' );
|
|
347
|
-
undoState = createNextUndoState( 'isCreate' );
|
|
348
|
-
expectedUndoState.list.push( [
|
|
349
|
-
createExpectedDiff( 'value', { from: 1, to: 3 } ),
|
|
350
|
-
] );
|
|
351
|
-
expectedUndoState.offset = -1;
|
|
352
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
353
|
-
} );
|
|
354
|
-
|
|
355
|
-
it( 'explicitly creates an undo level when undoing while there are pending transient edits', () => {
|
|
356
|
-
undoState = createNextUndoState();
|
|
357
|
-
undoState = createNextUndoState( { value: 1 } );
|
|
358
|
-
undoState = createNextUndoState( { transientValue: 2 }, true );
|
|
359
|
-
undoState = createNextUndoState( 'isUndo' );
|
|
360
|
-
expectedUndoState.list.push( [
|
|
361
|
-
createExpectedDiff( 'value', { from: undefined, to: 1 } ),
|
|
362
|
-
createExpectedDiff( 'transientValue', { from: undefined, to: 2 } ),
|
|
363
|
-
] );
|
|
364
|
-
expectedUndoState.offset--;
|
|
365
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
366
|
-
} );
|
|
367
|
-
|
|
368
|
-
it( 'does not create new levels for the same function edits', () => {
|
|
369
|
-
const value = () => {};
|
|
370
|
-
undoState = createNextUndoState();
|
|
371
|
-
undoState = createNextUndoState( { value } );
|
|
372
|
-
undoState = createNextUndoState( { value: () => {} } );
|
|
373
|
-
expect( undoState ).toEqual( expectedUndoState );
|
|
374
|
-
} );
|
|
375
|
-
} );
|
|
376
|
-
|
|
377
144
|
describe( 'embedPreviews()', () => {
|
|
378
145
|
it( 'returns an empty object by default', () => {
|
|
379
146
|
const state = embedPreviews( undefined, {} );
|
package/src/test/selectors.js
CHANGED
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
getAutosave,
|
|
23
23
|
getAutosaves,
|
|
24
24
|
getCurrentUser,
|
|
25
|
-
getReferenceByDistinctEdits,
|
|
26
25
|
} from '../selectors';
|
|
27
26
|
// getEntityRecord and __experimentalGetEntityRecordNoResolver selectors share the same tests.
|
|
28
27
|
describe.each( [
|
|
@@ -835,56 +834,3 @@ describe( 'getCurrentUser', () => {
|
|
|
835
834
|
expect( getCurrentUser( state ) ).toEqual( currentUser );
|
|
836
835
|
} );
|
|
837
836
|
} );
|
|
838
|
-
|
|
839
|
-
describe( 'getReferenceByDistinctEdits', () => {
|
|
840
|
-
it( 'should return referentially equal values across empty states', () => {
|
|
841
|
-
const state = { undo: { list: [] } };
|
|
842
|
-
expect( getReferenceByDistinctEdits( state ) ).toBe(
|
|
843
|
-
getReferenceByDistinctEdits( state )
|
|
844
|
-
);
|
|
845
|
-
|
|
846
|
-
const beforeState = { undo: { list: [] } };
|
|
847
|
-
const afterState = { undo: { list: [] } };
|
|
848
|
-
expect( getReferenceByDistinctEdits( beforeState ) ).toBe(
|
|
849
|
-
getReferenceByDistinctEdits( afterState )
|
|
850
|
-
);
|
|
851
|
-
} );
|
|
852
|
-
|
|
853
|
-
it( 'should return referentially equal values across unchanging non-empty state', () => {
|
|
854
|
-
const undoStates = { list: [ {} ] };
|
|
855
|
-
const state = { undo: undoStates };
|
|
856
|
-
expect( getReferenceByDistinctEdits( state ) ).toBe(
|
|
857
|
-
getReferenceByDistinctEdits( state )
|
|
858
|
-
);
|
|
859
|
-
|
|
860
|
-
const beforeState = { undo: undoStates };
|
|
861
|
-
const afterState = { undo: undoStates };
|
|
862
|
-
expect( getReferenceByDistinctEdits( beforeState ) ).toBe(
|
|
863
|
-
getReferenceByDistinctEdits( afterState )
|
|
864
|
-
);
|
|
865
|
-
} );
|
|
866
|
-
|
|
867
|
-
describe( 'when adding edits', () => {
|
|
868
|
-
it( 'should return referentially different values across changing states', () => {
|
|
869
|
-
const beforeState = { undo: { list: [ {} ] } };
|
|
870
|
-
beforeState.undo.offset = 0;
|
|
871
|
-
const afterState = { undo: { list: [ {}, {} ] } };
|
|
872
|
-
afterState.undo.offset = 1;
|
|
873
|
-
expect( getReferenceByDistinctEdits( beforeState ) ).not.toBe(
|
|
874
|
-
getReferenceByDistinctEdits( afterState )
|
|
875
|
-
);
|
|
876
|
-
} );
|
|
877
|
-
} );
|
|
878
|
-
|
|
879
|
-
describe( 'when using undo', () => {
|
|
880
|
-
it( 'should return referentially different values across changing states', () => {
|
|
881
|
-
const beforeState = { undo: { list: [ {}, {} ] } };
|
|
882
|
-
beforeState.undo.offset = 1;
|
|
883
|
-
const afterState = { undo: { list: [ {}, {} ] } };
|
|
884
|
-
afterState.undo.offset = 0;
|
|
885
|
-
expect( getReferenceByDistinctEdits( beforeState ) ).not.toBe(
|
|
886
|
-
getReferenceByDistinctEdits( afterState )
|
|
887
|
-
);
|
|
888
|
-
} );
|
|
889
|
-
} );
|
|
890
|
-
} );
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper util to return a value from a certain path of the object.
|
|
3
|
+
* Path is specified as either:
|
|
4
|
+
* - a string of properties, separated by dots, for example: "x.y".
|
|
5
|
+
* - an array of properties, for example `[ 'x', 'y' ]`.
|
|
6
|
+
* You can also specify a default value in case the result is nullish.
|
|
7
|
+
*
|
|
8
|
+
* @param {Object} object Input object.
|
|
9
|
+
* @param {string|Array} path Path to the object property.
|
|
10
|
+
* @param {*} defaultValue Default value if the value at the specified path is undefined.
|
|
11
|
+
* @return {*} Value of the object property at the specified path.
|
|
12
|
+
*/
|
|
13
|
+
export default function getNestedValue( object, path, defaultValue ) {
|
|
14
|
+
if (
|
|
15
|
+
! object ||
|
|
16
|
+
typeof object !== 'object' ||
|
|
17
|
+
( typeof path !== 'string' && ! Array.isArray( path ) )
|
|
18
|
+
) {
|
|
19
|
+
return object;
|
|
20
|
+
}
|
|
21
|
+
const normalizedPath = Array.isArray( path ) ? path : path.split( '.' );
|
|
22
|
+
let value = object;
|
|
23
|
+
normalizedPath.forEach( ( fieldName ) => {
|
|
24
|
+
value = value?.[ fieldName ];
|
|
25
|
+
} );
|
|
26
|
+
return value !== undefined ? value : defaultValue;
|
|
27
|
+
}
|
package/src/utils/index.js
CHANGED
|
@@ -7,3 +7,4 @@ export { default as replaceAction } from './replace-action';
|
|
|
7
7
|
export { default as withWeakMapCache } from './with-weak-map-cache';
|
|
8
8
|
export { default as isRawAttribute } from './is-raw-attribute';
|
|
9
9
|
export { default as setNestedValue } from './set-nested-value';
|
|
10
|
+
export { default as getNestedValue } from './get-nested-value';
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* Arrays are created for missing index properties while objects are created
|
|
5
5
|
* for all other missing properties.
|
|
6
6
|
*
|
|
7
|
+
* Path is specified as either:
|
|
8
|
+
* - a string of properties, separated by dots, for example: "x.y".
|
|
9
|
+
* - an array of properties, for example `[ 'x', 'y' ]`.
|
|
10
|
+
*
|
|
7
11
|
* This function intentionally mutates the input object.
|
|
8
12
|
*
|
|
9
13
|
* Inspired by _.set().
|
|
@@ -12,24 +16,26 @@
|
|
|
12
16
|
*
|
|
13
17
|
* @todo Needs to be deduplicated with its copy in `@wordpress/edit-site`.
|
|
14
18
|
*
|
|
15
|
-
* @param {Object}
|
|
16
|
-
* @param {Array}
|
|
17
|
-
* @param {*}
|
|
19
|
+
* @param {Object} object Object to modify
|
|
20
|
+
* @param {Array|string} path Path of the property to set.
|
|
21
|
+
* @param {*} value Value to set.
|
|
18
22
|
*/
|
|
19
23
|
export default function setNestedValue( object, path, value ) {
|
|
20
24
|
if ( ! object || typeof object !== 'object' ) {
|
|
21
25
|
return object;
|
|
22
26
|
}
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
const normalizedPath = Array.isArray( path ) ? path : path.split( '.' );
|
|
29
|
+
|
|
30
|
+
normalizedPath.reduce( ( acc, key, idx ) => {
|
|
25
31
|
if ( acc[ key ] === undefined ) {
|
|
26
|
-
if ( Number.isInteger(
|
|
32
|
+
if ( Number.isInteger( normalizedPath[ idx + 1 ] ) ) {
|
|
27
33
|
acc[ key ] = [];
|
|
28
34
|
} else {
|
|
29
35
|
acc[ key ] = {};
|
|
30
36
|
}
|
|
31
37
|
}
|
|
32
|
-
if ( idx ===
|
|
38
|
+
if ( idx === normalizedPath.length - 1 ) {
|
|
33
39
|
acc[ key ] = value;
|
|
34
40
|
}
|
|
35
41
|
return acc[ key ];
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import getNestedValue from '../get-nested-value';
|
|
5
|
+
|
|
6
|
+
describe( 'getNestedValue', () => {
|
|
7
|
+
it( 'should return the same object unmodified if path is an empty array', () => {
|
|
8
|
+
const input = { x: 'y' };
|
|
9
|
+
const result = getNestedValue( input, [] );
|
|
10
|
+
expect( result ).toEqual( input );
|
|
11
|
+
} );
|
|
12
|
+
|
|
13
|
+
it( 'should return the nested value', () => {
|
|
14
|
+
const input = { x: { y: { z: 123 } } };
|
|
15
|
+
const result = getNestedValue( input, [ 'x', 'y', 'z' ] );
|
|
16
|
+
|
|
17
|
+
expect( result ).toEqual( 123 );
|
|
18
|
+
} );
|
|
19
|
+
|
|
20
|
+
it( 'should return the nested value if the path is a string', () => {
|
|
21
|
+
const input = { x: { y: { z: 123 } } };
|
|
22
|
+
const result = getNestedValue( input, 'x.y.z' );
|
|
23
|
+
|
|
24
|
+
expect( result ).toEqual( 123 );
|
|
25
|
+
} );
|
|
26
|
+
|
|
27
|
+
it( 'should return the shallow value', () => {
|
|
28
|
+
const input = { x: { y: { z: 123 } } };
|
|
29
|
+
const result = getNestedValue( input, 'x' );
|
|
30
|
+
|
|
31
|
+
expect( result ).toEqual( { y: { z: 123 } } );
|
|
32
|
+
} );
|
|
33
|
+
|
|
34
|
+
it( 'should return the default value if the nested value is undefined', () => {
|
|
35
|
+
const input = { x: { y: { z: undefined } } };
|
|
36
|
+
const result = getNestedValue( input, [ 'x', 'y', 'z' ], 456 );
|
|
37
|
+
|
|
38
|
+
expect( result ).toEqual( 456 );
|
|
39
|
+
} );
|
|
40
|
+
|
|
41
|
+
it( 'should return the nested value if it is different to undefined', () => {
|
|
42
|
+
const input = { x: { y: { z: null } } };
|
|
43
|
+
const result = getNestedValue( input, 'x.y.z', 456 );
|
|
44
|
+
|
|
45
|
+
expect( result ).toBeNull();
|
|
46
|
+
} );
|
|
47
|
+
|
|
48
|
+
it( 'should return the default value if the nested value does not exist', () => {
|
|
49
|
+
const input = { x: { y: { z: 123 } } };
|
|
50
|
+
const result = getNestedValue( input, [ 'x', 'y', 'z1' ], 456 );
|
|
51
|
+
|
|
52
|
+
expect( result ).toEqual( 456 );
|
|
53
|
+
} );
|
|
54
|
+
|
|
55
|
+
it( 'should return undefined if the nested value does not exist', () => {
|
|
56
|
+
const input = { x: { y: { z: 123 } } };
|
|
57
|
+
const result = getNestedValue( input, [ 'x', 'y', 'z1' ] );
|
|
58
|
+
|
|
59
|
+
expect( result ).toBeUndefined();
|
|
60
|
+
} );
|
|
61
|
+
} );
|
|
@@ -19,6 +19,13 @@ describe( 'setNestedValue', () => {
|
|
|
19
19
|
expect( result ).toEqual( { x: { y: { z: 456 } } } );
|
|
20
20
|
} );
|
|
21
21
|
|
|
22
|
+
it( 'should set values at deep level having a string as path', () => {
|
|
23
|
+
const input = { x: { y: { z: 123 } } };
|
|
24
|
+
const result = setNestedValue( input, 'x.y.z', 456 );
|
|
25
|
+
|
|
26
|
+
expect( result ).toEqual( { x: { y: { z: 456 } } } );
|
|
27
|
+
} );
|
|
28
|
+
|
|
22
29
|
it( 'should create nested objects if necessary', () => {
|
|
23
30
|
const result = setNestedValue( {}, [ 'x', 'y', 'z' ], 123 );
|
|
24
31
|
|