@wordpress/block-editor 15.15.0 → 15.16.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.
Files changed (94) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/block-inspector/index.cjs +2 -1
  3. package/build/components/block-inspector/index.cjs.map +2 -2
  4. package/build/components/block-visibility/modal.cjs +2 -2
  5. package/build/components/block-visibility/modal.cjs.map +1 -1
  6. package/build/components/block-visibility/viewport-visibility-info.cjs +6 -1
  7. package/build/components/block-visibility/viewport-visibility-info.cjs.map +2 -2
  8. package/build/components/global-styles/background-panel.cjs +142 -33
  9. package/build/components/global-styles/background-panel.cjs.map +2 -2
  10. package/build/components/global-styles/color-panel.cjs +18 -7
  11. package/build/components/global-styles/color-panel.cjs.map +2 -2
  12. package/build/components/global-styles/hooks.cjs +8 -4
  13. package/build/components/global-styles/hooks.cjs.map +2 -2
  14. package/build/components/inspector-controls-tabs/styles-tab.cjs +2 -1
  15. package/build/components/inspector-controls-tabs/styles-tab.cjs.map +2 -2
  16. package/build/hooks/background.cjs +74 -21
  17. package/build/hooks/background.cjs.map +3 -3
  18. package/build/hooks/cross-origin-isolation.cjs +6 -6
  19. package/build/hooks/cross-origin-isolation.cjs.map +2 -2
  20. package/build/hooks/custom-css.cjs +5 -0
  21. package/build/hooks/custom-css.cjs.map +2 -2
  22. package/build/hooks/fit-text.cjs +46 -58
  23. package/build/hooks/fit-text.cjs.map +3 -3
  24. package/build/hooks/utils.cjs +5 -1
  25. package/build/hooks/utils.cjs.map +2 -2
  26. package/build/store/actions.cjs +8 -4
  27. package/build/store/actions.cjs.map +2 -2
  28. package/build/store/private-selectors.cjs +2 -2
  29. package/build/store/private-selectors.cjs.map +2 -2
  30. package/build/store/reducer.cjs +62 -95
  31. package/build/store/reducer.cjs.map +2 -2
  32. package/build/store/selectors.cjs +2 -2
  33. package/build/store/selectors.cjs.map +2 -2
  34. package/build-module/components/block-inspector/index.mjs +2 -1
  35. package/build-module/components/block-inspector/index.mjs.map +2 -2
  36. package/build-module/components/block-visibility/modal.mjs +2 -2
  37. package/build-module/components/block-visibility/modal.mjs.map +1 -1
  38. package/build-module/components/block-visibility/viewport-visibility-info.mjs +6 -1
  39. package/build-module/components/block-visibility/viewport-visibility-info.mjs.map +2 -2
  40. package/build-module/components/global-styles/background-panel.mjs +141 -34
  41. package/build-module/components/global-styles/background-panel.mjs.map +2 -2
  42. package/build-module/components/global-styles/color-panel.mjs +17 -7
  43. package/build-module/components/global-styles/color-panel.mjs.map +2 -2
  44. package/build-module/components/global-styles/hooks.mjs +8 -4
  45. package/build-module/components/global-styles/hooks.mjs.map +2 -2
  46. package/build-module/components/inspector-controls-tabs/styles-tab.mjs +2 -1
  47. package/build-module/components/inspector-controls-tabs/styles-tab.mjs.map +2 -2
  48. package/build-module/hooks/background.mjs +76 -22
  49. package/build-module/hooks/background.mjs.map +2 -2
  50. package/build-module/hooks/cross-origin-isolation.mjs +6 -6
  51. package/build-module/hooks/cross-origin-isolation.mjs.map +2 -2
  52. package/build-module/hooks/custom-css.mjs +5 -0
  53. package/build-module/hooks/custom-css.mjs.map +2 -2
  54. package/build-module/hooks/fit-text.mjs +46 -58
  55. package/build-module/hooks/fit-text.mjs.map +2 -2
  56. package/build-module/hooks/utils.mjs +5 -1
  57. package/build-module/hooks/utils.mjs.map +2 -2
  58. package/build-module/store/actions.mjs +8 -4
  59. package/build-module/store/actions.mjs.map +2 -2
  60. package/build-module/store/private-selectors.mjs +2 -2
  61. package/build-module/store/private-selectors.mjs.map +2 -2
  62. package/build-module/store/reducer.mjs +62 -94
  63. package/build-module/store/reducer.mjs.map +2 -2
  64. package/build-module/store/selectors.mjs +2 -2
  65. package/build-module/store/selectors.mjs.map +2 -2
  66. package/build-style/content-rtl.css +2 -2
  67. package/build-style/content.css +2 -2
  68. package/build-style/style-rtl.css +35 -7
  69. package/build-style/style.css +35 -7
  70. package/package.json +39 -39
  71. package/src/components/background-image-control/style.scss +0 -4
  72. package/src/components/block-inspector/index.js +1 -0
  73. package/src/components/block-visibility/viewport-visibility-info.js +8 -1
  74. package/src/components/fit-text-size-warning/style.scss +1 -5
  75. package/src/components/global-styles/background-panel.js +157 -11
  76. package/src/components/global-styles/color-panel.js +23 -7
  77. package/src/components/global-styles/hooks.js +12 -4
  78. package/src/components/global-styles/test/background-panel.js +44 -1
  79. package/src/components/inspector-controls-tabs/styles-tab.js +1 -0
  80. package/src/hooks/background.js +122 -21
  81. package/src/hooks/background.scss +45 -0
  82. package/src/hooks/cross-origin-isolation.js +6 -6
  83. package/src/hooks/custom-css.js +6 -0
  84. package/src/hooks/fit-text.js +73 -83
  85. package/src/hooks/test/cross-origin-isolation.js +7 -3
  86. package/src/hooks/utils.js +4 -0
  87. package/src/store/actions.js +9 -8
  88. package/src/store/private-selectors.js +2 -2
  89. package/src/store/reducer.js +84 -128
  90. package/src/store/selectors.js +2 -2
  91. package/src/store/test/private-selectors.js +67 -34
  92. package/src/store/test/reducer.js +436 -66
  93. package/src/store/test/selectors.js +81 -63
  94. package/src/style.scss +1 -0
@@ -37,14 +37,13 @@ import {
37
37
  settings,
38
38
  lastBlockAttributesChange,
39
39
  lastBlockInserted,
40
- blockEditingModes,
41
40
  expandedBlock,
42
41
  zoomLevel,
43
42
  editedContentOnlySection,
44
43
  withDerivedBlockEditingModes,
45
44
  viewportModalClientIds,
46
45
  } from '../reducer';
47
-
46
+ import { getBlockOrder, getBlocks } from '../selectors';
48
47
  import { unlock } from '../../lock-unlock';
49
48
  import { sectionRootClientIdKey, isIsolatedEditorKey } from '.././private-keys';
50
49
 
@@ -287,6 +286,7 @@ describe( 'state', () => {
287
286
  } )
288
287
  ),
289
288
  controlledInnerBlocks: {},
289
+ blockEditingModes: new Map(),
290
290
  } );
291
291
 
292
292
  const newChildBlock = createBlock( 'core/test-child-block', {
@@ -345,6 +345,7 @@ describe( 'state', () => {
345
345
  } )
346
346
  ),
347
347
  controlledInnerBlocks: {},
348
+ blockEditingModes: new Map(),
348
349
  } );
349
350
  expect( state.tree.get( 'chicken' ) ).not.toBe(
350
351
  existingState.tree.get( 'chicken' )
@@ -387,6 +388,7 @@ describe( 'state', () => {
387
388
  } )
388
389
  ),
389
390
  controlledInnerBlocks: {},
391
+ blockEditingModes: new Map(),
390
392
  } );
391
393
 
392
394
  const newChildBlock = createBlock( 'core/test-child-block', {
@@ -445,6 +447,7 @@ describe( 'state', () => {
445
447
  } )
446
448
  ),
447
449
  controlledInnerBlocks: {},
450
+ blockEditingModes: new Map(),
448
451
  } );
449
452
  expect( state.tree.get( 'chicken' ) ).not.toBe(
450
453
  existingState.tree.get( 'chicken' )
@@ -516,6 +519,7 @@ describe( 'state', () => {
516
519
  ),
517
520
  tree: new Map(),
518
521
  controlledInnerBlocks: {},
522
+ blockEditingModes: new Map(),
519
523
  } );
520
524
 
521
525
  const newChildBlock1 = createBlock( 'core/test-child-block', {
@@ -610,6 +614,7 @@ describe( 'state', () => {
610
614
  } )
611
615
  ),
612
616
  controlledInnerBlocks: {},
617
+ blockEditingModes: new Map(),
613
618
  } );
614
619
 
615
620
  expect( state.tree.get( '' ).innerBlocks[ 0 ] ).toBe(
@@ -685,6 +690,7 @@ describe( 'state', () => {
685
690
  } )
686
691
  ),
687
692
  controlledInnerBlocks: {},
693
+ blockEditingModes: new Map(),
688
694
  } );
689
695
 
690
696
  const newChildBlock = createBlock( 'core/test-block' );
@@ -737,6 +743,7 @@ describe( 'state', () => {
737
743
  } )
738
744
  ),
739
745
  controlledInnerBlocks: {},
746
+ blockEditingModes: new Map(),
740
747
  } );
741
748
 
742
749
  // The block object of the parent should be updated.
@@ -758,6 +765,7 @@ describe( 'state', () => {
758
765
  isIgnoredChange: false,
759
766
  tree: new Map(),
760
767
  controlledInnerBlocks: {},
768
+ blockEditingModes: new Map(),
761
769
  } );
762
770
  } );
763
771
 
@@ -1138,52 +1146,6 @@ describe( 'state', () => {
1138
1146
  } );
1139
1147
  } );
1140
1148
 
1141
- it( 'should update the reusable block reference if the temporary id is swapped', () => {
1142
- const original = blocks( undefined, {
1143
- type: 'RESET_BLOCKS',
1144
- blocks: [
1145
- {
1146
- clientId: 'chicken',
1147
- name: 'core/block',
1148
- attributes: {
1149
- ref: 'random-clientId',
1150
- },
1151
- isValid: false,
1152
- innerBlocks: [],
1153
- },
1154
- ],
1155
- } );
1156
-
1157
- const state = blocks( deepFreeze( original ), {
1158
- type: 'SAVE_REUSABLE_BLOCK_SUCCESS',
1159
- id: 'random-clientId',
1160
- updatedId: 3,
1161
- } );
1162
-
1163
- expect( state.byClientId.get( 'chicken' ) ).toEqual( {
1164
- clientId: 'chicken',
1165
- name: 'core/block',
1166
- isValid: false,
1167
- } );
1168
-
1169
- expect( state.attributes.get( 'chicken' ) ).toEqual( {
1170
- ref: 3,
1171
- } );
1172
-
1173
- expect( state.tree.get( '' ).innerBlocks[ 0 ] ).toBe(
1174
- state.tree.get( 'chicken' )
1175
- );
1176
- expect( state.tree.get( 'chicken' ) ).toEqual( {
1177
- clientId: 'chicken',
1178
- name: 'core/block',
1179
- isValid: false,
1180
- innerBlocks: [],
1181
- attributes: {
1182
- ref: 3,
1183
- },
1184
- } );
1185
- } );
1186
-
1187
1149
  it( 'should move the block up', () => {
1188
1150
  const original = blocks( undefined, {
1189
1151
  type: 'RESET_BLOCKS',
@@ -2478,6 +2440,203 @@ describe( 'state', () => {
2478
2440
  expect( state.controlledInnerBlocks.chicken ).toBe( true );
2479
2441
  } );
2480
2442
 
2443
+ it( 'should preserve controlledInnerBlocks blocks across RESET_BLOCKS', () => {
2444
+ const original = blocks( undefined, {
2445
+ type: 'RESET_BLOCKS',
2446
+ blocks: [
2447
+ {
2448
+ clientId: 'chicken',
2449
+ name: 'core/test-block',
2450
+ attributes: {},
2451
+ innerBlocks: [],
2452
+ },
2453
+ ],
2454
+ } );
2455
+ const withControlled = blocks( original, {
2456
+ type: 'SET_HAS_CONTROLLED_INNER_BLOCKS',
2457
+ clientId: 'chicken',
2458
+ hasControlledInnerBlocks: true,
2459
+ } );
2460
+
2461
+ const withControlledContent = blocks( withControlled, {
2462
+ type: 'REPLACE_INNER_BLOCKS',
2463
+ rootClientId: 'chicken',
2464
+ blocks: [
2465
+ {
2466
+ clientId: 'content',
2467
+ innerBlocks: [
2468
+ {
2469
+ clientId: 'content-inner',
2470
+ innerBlocks: [],
2471
+ },
2472
+ ],
2473
+ },
2474
+ ],
2475
+ } );
2476
+
2477
+ expect(
2478
+ getBlocks(
2479
+ { blocks: withControlledContent },
2480
+ 'chicken'
2481
+ ).map( ( b ) => b.clientId )
2482
+ ).toEqual( [ 'content' ] );
2483
+ expect(
2484
+ getBlocks(
2485
+ { blocks: withControlledContent },
2486
+ 'content'
2487
+ ).map( ( b ) => b.clientId )
2488
+ ).toEqual( [ 'content-inner' ] );
2489
+
2490
+ const state = blocks( withControlledContent, {
2491
+ type: 'RESET_BLOCKS',
2492
+ blocks: [
2493
+ {
2494
+ clientId: 'chicken',
2495
+ name: 'core/test-block',
2496
+ attributes: {},
2497
+ innerBlocks: [],
2498
+ },
2499
+ ],
2500
+ } );
2501
+
2502
+ expect( state.controlledInnerBlocks.chicken ).toBe( true );
2503
+ expect(
2504
+ getBlocks( { blocks: state }, 'chicken' ).map(
2505
+ ( b ) => b.clientId
2506
+ )
2507
+ ).toEqual( [ 'content' ] );
2508
+ expect(
2509
+ getBlocks( { blocks: state }, 'content' ).map(
2510
+ ( b ) => b.clientId
2511
+ )
2512
+ ).toEqual( [ 'content-inner' ] );
2513
+ } );
2514
+
2515
+ it( 'should forget controlledInnerBlocks during full RESET_BLOCKS', () => {
2516
+ const templateBlock = {
2517
+ clientId: 'template',
2518
+ name: 'core/post-content',
2519
+ attributes: {},
2520
+ innerBlocks: [],
2521
+ };
2522
+ const contentBlock = {
2523
+ clientId: 'content',
2524
+ name: 'core/paragraph',
2525
+ attributes: {},
2526
+ innerBlocks: [],
2527
+ };
2528
+
2529
+ let state = blocks( undefined, {
2530
+ type: 'RESET_BLOCKS',
2531
+ blocks: [ templateBlock ],
2532
+ } );
2533
+
2534
+ state = blocks( state, {
2535
+ type: 'SET_HAS_CONTROLLED_INNER_BLOCKS',
2536
+ clientId: 'template',
2537
+ hasControlledInnerBlocks: true,
2538
+ } );
2539
+
2540
+ state = blocks( state, {
2541
+ type: 'REPLACE_INNER_BLOCKS',
2542
+ rootClientId: 'template',
2543
+ blocks: [ contentBlock ],
2544
+ } );
2545
+
2546
+ // Reset blocks completely, we expect that the controlled blocks are forgotten.
2547
+ state = blocks( state, {
2548
+ type: 'RESET_BLOCKS',
2549
+ blocks: [],
2550
+ } );
2551
+
2552
+ // Reset back to the template.
2553
+ state = blocks( state, {
2554
+ type: 'RESET_BLOCKS',
2555
+ blocks: [ templateBlock ],
2556
+ } );
2557
+
2558
+ // Expect that the `template`/`content` blocks are reconstructed.
2559
+ const fullState = { blocks: state };
2560
+ expect( getBlocks( fullState, 'template' ) ).toEqual( [] );
2561
+ expect( getBlockOrder( fullState, 'template' ) ).toEqual(
2562
+ []
2563
+ );
2564
+ } );
2565
+
2566
+ it( 'should not leave stale controlled tree entries after root replacement and reset', () => {
2567
+ const templateBlock = {
2568
+ clientId: 'template',
2569
+ name: 'core/post-content',
2570
+ attributes: {},
2571
+ innerBlocks: [],
2572
+ };
2573
+ const contentBlock = {
2574
+ clientId: 'content',
2575
+ name: 'core/paragraph',
2576
+ attributes: {},
2577
+ innerBlocks: [],
2578
+ };
2579
+
2580
+ // Initialize template, simulates root `useBlockSync` initialization.
2581
+ let state = blocks( undefined, {
2582
+ type: 'RESET_BLOCKS',
2583
+ blocks: [ templateBlock ],
2584
+ } );
2585
+
2586
+ // Add a controlled child to the template using two actions, simulates inner `useBlockSync` initialization.
2587
+ state = blocks( state, {
2588
+ type: 'SET_HAS_CONTROLLED_INNER_BLOCKS',
2589
+ clientId: 'template',
2590
+ hasControlledInnerBlocks: true,
2591
+ } );
2592
+
2593
+ state = blocks( state, {
2594
+ type: 'REPLACE_INNER_BLOCKS',
2595
+ rootClientId: 'template',
2596
+ blocks: [ contentBlock ],
2597
+ } );
2598
+
2599
+ // Reset blocks completely, simulates root `useBlockSync` cleanup.
2600
+ state = blocks( state, {
2601
+ type: 'RESET_BLOCKS',
2602
+ blocks: [],
2603
+ } );
2604
+
2605
+ // Unset controlled inner blocks, simulates inner `useBlockSync` cleanup.
2606
+ state = blocks( state, {
2607
+ type: 'SET_HAS_CONTROLLED_INNER_BLOCKS',
2608
+ clientId: 'template',
2609
+ hasControlledInnerBlocks: false,
2610
+ } );
2611
+
2612
+ // Initialize template again, simulates `useBlockSync` after navigation.
2613
+ state = blocks( state, {
2614
+ type: 'RESET_BLOCKS',
2615
+ blocks: [ templateBlock ],
2616
+ } );
2617
+
2618
+ // Set controlled inner blocks again, this time to empty.
2619
+ state = blocks( state, {
2620
+ type: 'SET_HAS_CONTROLLED_INNER_BLOCKS',
2621
+ clientId: 'template',
2622
+ hasControlledInnerBlocks: true,
2623
+ } );
2624
+
2625
+ state = blocks( state, {
2626
+ type: 'REPLACE_INNER_BLOCKS',
2627
+ rootClientId: 'template',
2628
+ blocks: [],
2629
+ } );
2630
+
2631
+ // At this point the template has empty content, and `useInnerBlockTemplateSync` should apply
2632
+ // its template content. It will check if `getBlocks` is empty before applying the template.
2633
+ const fullState = { blocks: state };
2634
+ expect( getBlockOrder( fullState, 'template' ) ).toEqual(
2635
+ []
2636
+ );
2637
+ expect( getBlocks( fullState, 'template' ) ).toEqual( [] );
2638
+ } );
2639
+
2481
2640
  it( 'should not create new state references when setting controlled inner blocks on a block with no inner blocks', () => {
2482
2641
  const original = blocks( undefined, {
2483
2642
  type: 'RESET_BLOCKS',
@@ -3579,17 +3738,18 @@ describe( 'state', () => {
3579
3738
 
3580
3739
  describe( 'blockEditingModes', () => {
3581
3740
  it( 'should return an empty map by default', () => {
3582
- expect( blockEditingModes( undefined, {} ) ).toEqual( new Map() );
3741
+ const state = blocks( undefined, {} );
3742
+ expect( state.blockEditingModes ).toEqual( new Map() );
3583
3743
  } );
3584
3744
 
3585
3745
  it( 'should set the editing mode for a block', () => {
3586
- const state = new Map();
3587
- const newState = blockEditingModes( state, {
3746
+ const state = blocks( undefined, {} );
3747
+ const newState = blocks( state, {
3588
3748
  type: 'SET_BLOCK_EDITING_MODE',
3589
3749
  clientId: '14501cc2-90a6-4f52-aa36-ab6e896135d1',
3590
3750
  mode: 'default',
3591
3751
  } );
3592
- expect( newState ).toEqual(
3752
+ expect( newState.blockEditingModes ).toEqual(
3593
3753
  new Map( [
3594
3754
  [ '14501cc2-90a6-4f52-aa36-ab6e896135d1', 'default' ],
3595
3755
  ] )
@@ -3597,28 +3757,126 @@ describe( 'state', () => {
3597
3757
  } );
3598
3758
 
3599
3759
  it( 'should clear the editing mode for a block', () => {
3600
- const state = new Map( [
3601
- [ '14501cc2-90a6-4f52-aa36-ab6e896135d1', 'default' ],
3602
- ] );
3603
- const newState = blockEditingModes( state, {
3760
+ let state = blocks( undefined, {} );
3761
+ state = blocks( state, {
3762
+ type: 'SET_BLOCK_EDITING_MODE',
3763
+ clientId: '14501cc2-90a6-4f52-aa36-ab6e896135d1',
3764
+ mode: 'default',
3765
+ } );
3766
+ const newState = blocks( state, {
3604
3767
  type: 'UNSET_BLOCK_EDITING_MODE',
3605
3768
  clientId: '14501cc2-90a6-4f52-aa36-ab6e896135d1',
3606
3769
  } );
3607
- expect( newState ).toEqual( new Map() );
3770
+ expect( newState.blockEditingModes ).toEqual( new Map() );
3608
3771
  } );
3609
3772
 
3610
- it( 'should clear editing modes when blocks are reset', () => {
3611
- const state = new Map( [
3612
- [ '', 'disabled' ],
3613
- [ '14501cc2-90a6-4f52-aa36-ab6e896135d1', 'default' ],
3614
- ] );
3615
- const newState = blockEditingModes( state, {
3773
+ it( 'should preserve editing modes when blocks are reset', () => {
3774
+ // Add a template with two template parts.
3775
+ let state = blocks( undefined, {} );
3776
+ state = blocks( state, {
3777
+ type: 'RESET_BLOCKS',
3778
+ blocks: [
3779
+ {
3780
+ name: 'core/template-part',
3781
+ clientId: 'template-part-1',
3782
+ attributes: {},
3783
+ innerBlocks: [],
3784
+ },
3785
+ {
3786
+ name: 'core/template-part',
3787
+ clientId: 'template-part-2',
3788
+ attributes: {},
3789
+ innerBlocks: [],
3790
+ },
3791
+ ],
3792
+ } );
3793
+
3794
+ // In each of the template parts add a controlled content (a paragraph block).
3795
+ state = blocks( state, {
3796
+ type: 'SET_HAS_CONTROLLED_INNER_BLOCKS',
3797
+ clientId: 'template-part-1',
3798
+ hasControlledInnerBlocks: true,
3799
+ } );
3800
+ state = blocks( state, {
3801
+ type: 'REPLACE_INNER_BLOCKS',
3802
+ rootClientId: 'template-part-1',
3803
+ blocks: [
3804
+ {
3805
+ name: 'core/paragraph',
3806
+ clientId: 'paragraph-1',
3807
+ attributes: {},
3808
+ innerBlocks: [],
3809
+ },
3810
+ ],
3811
+ } );
3812
+ state = blocks( state, {
3813
+ type: 'SET_HAS_CONTROLLED_INNER_BLOCKS',
3814
+ clientId: 'template-part-2',
3815
+ hasControlledInnerBlocks: true,
3816
+ } );
3817
+ state = blocks( state, {
3818
+ type: 'REPLACE_INNER_BLOCKS',
3819
+ rootClientId: 'template-part-2',
3820
+ blocks: [
3821
+ {
3822
+ name: 'core/paragraph',
3823
+ clientId: 'paragraph-2',
3824
+ attributes: {},
3825
+ innerBlocks: [],
3826
+ },
3827
+ ],
3828
+ } );
3829
+
3830
+ // Set block editing modes, just like `DisableNonPageContentBlocks` would do:
3831
+ // - the root block to 'disabled'
3832
+ // - the template parts to 'contentOnly'
3833
+ // - the template part children to 'disabled'
3834
+ state = blocks( state, {
3835
+ type: 'SET_BLOCK_EDITING_MODE',
3836
+ clientId: '',
3837
+ mode: 'disabled',
3838
+ } );
3839
+ state = blocks( state, {
3840
+ type: 'SET_BLOCK_EDITING_MODE',
3841
+ clientId: 'template-part-1',
3842
+ mode: 'contentOnly',
3843
+ } );
3844
+ state = blocks( state, {
3845
+ type: 'SET_BLOCK_EDITING_MODE',
3846
+ clientId: 'template-part-2',
3847
+ mode: 'contentOnly',
3848
+ } );
3849
+ state = blocks( state, {
3850
+ type: 'SET_BLOCK_EDITING_MODE',
3851
+ clientId: 'paragraph-1',
3852
+ mode: 'disabled',
3853
+ } );
3854
+ state = blocks( state, {
3855
+ type: 'SET_BLOCK_EDITING_MODE',
3856
+ clientId: 'paragraph-2',
3857
+ mode: 'disabled',
3858
+ } );
3859
+
3860
+ // Reset the template, keeping only one of the template parts.
3861
+ state = blocks( state, {
3616
3862
  type: 'RESET_BLOCKS',
3863
+ blocks: [
3864
+ {
3865
+ name: 'core/template-part',
3866
+ clientId: 'template-part-1',
3867
+ attributes: {},
3868
+ innerBlocks: [],
3869
+ },
3870
+ ],
3617
3871
  } );
3618
- expect( newState ).toEqual(
3872
+
3873
+ // Check that the editing modes for valid blocks are preserved, and the
3874
+ // editing modes for removed blocks are cleared.
3875
+ expect( state.blockEditingModes ).toEqual(
3619
3876
  new Map( [
3620
- // Root mode should be maintained.
3621
3877
  [ '', 'disabled' ],
3878
+ [ 'template-part-1', 'contentOnly' ],
3879
+ [ 'paragraph-1', 'disabled' ],
3622
3880
  ] )
3623
3881
  );
3624
3882
  } );
@@ -3649,6 +3907,119 @@ describe( 'state', () => {
3649
3907
  } );
3650
3908
  } );
3651
3909
 
3910
+ describe( 'editedContentOnlySection', () => {
3911
+ it( 'returns undefined by default', () => {
3912
+ expect(
3913
+ editedContentOnlySection( undefined, { type: 'UNKNOWN' } )
3914
+ ).toBeUndefined();
3915
+ } );
3916
+
3917
+ it( 'sets the clientId on EDIT_CONTENT_ONLY_SECTION', () => {
3918
+ const state = editedContentOnlySection( undefined, {
3919
+ type: 'EDIT_CONTENT_ONLY_SECTION',
3920
+ clientId: 'block-1',
3921
+ } );
3922
+ expect( state ).toBe( 'block-1' );
3923
+ } );
3924
+
3925
+ it( 'clears the clientId when EDIT_CONTENT_ONLY_SECTION has no clientId', () => {
3926
+ const state = editedContentOnlySection( 'block-1', {
3927
+ type: 'EDIT_CONTENT_ONLY_SECTION',
3928
+ } );
3929
+ expect( state ).toBeUndefined();
3930
+ } );
3931
+
3932
+ it( 'clears when the edited section is directly removed via REMOVE_BLOCKS', () => {
3933
+ const state = editedContentOnlySection( 'block-1', {
3934
+ type: 'REMOVE_BLOCKS',
3935
+ clientIds: [ 'block-1' ],
3936
+ } );
3937
+ expect( state ).toBeUndefined();
3938
+ } );
3939
+
3940
+ it( 'keeps state when REMOVE_BLOCKS targets other blocks', () => {
3941
+ const state = editedContentOnlySection( 'block-1', {
3942
+ type: 'REMOVE_BLOCKS',
3943
+ clientIds: [ 'block-2', 'block-3' ],
3944
+ } );
3945
+ expect( state ).toBe( 'block-1' );
3946
+ } );
3947
+
3948
+ it( 'clears when the edited section is directly replaced via REPLACE_BLOCKS', () => {
3949
+ const state = editedContentOnlySection( 'block-1', {
3950
+ type: 'REPLACE_BLOCKS',
3951
+ clientIds: [ 'block-1' ],
3952
+ blocks: [ { clientId: 'block-new', innerBlocks: [] } ],
3953
+ } );
3954
+ expect( state ).toBeUndefined();
3955
+ } );
3956
+
3957
+ it( 'keeps state when REPLACE_BLOCKS targets other blocks', () => {
3958
+ const state = editedContentOnlySection( 'block-1', {
3959
+ type: 'REPLACE_BLOCKS',
3960
+ clientIds: [ 'block-2' ],
3961
+ blocks: [ { clientId: 'block-new', innerBlocks: [] } ],
3962
+ } );
3963
+ expect( state ).toBe( 'block-1' );
3964
+ } );
3965
+
3966
+ it( 'clears when RESET_BLOCKS does not include the edited section', () => {
3967
+ const state = editedContentOnlySection( 'block-1', {
3968
+ type: 'RESET_BLOCKS',
3969
+ blocks: [
3970
+ { clientId: 'block-2', innerBlocks: [] },
3971
+ { clientId: 'block-3', innerBlocks: [] },
3972
+ ],
3973
+ } );
3974
+ expect( state ).toBeUndefined();
3975
+ } );
3976
+
3977
+ it( 'keeps state when RESET_BLOCKS includes the edited section at the top level', () => {
3978
+ const state = editedContentOnlySection( 'block-1', {
3979
+ type: 'RESET_BLOCKS',
3980
+ blocks: [
3981
+ { clientId: 'block-1', innerBlocks: [] },
3982
+ { clientId: 'block-2', innerBlocks: [] },
3983
+ ],
3984
+ } );
3985
+ expect( state ).toBe( 'block-1' );
3986
+ } );
3987
+
3988
+ it( 'keeps state when RESET_BLOCKS includes the edited section nested in innerBlocks', () => {
3989
+ const state = editedContentOnlySection( 'block-1', {
3990
+ type: 'RESET_BLOCKS',
3991
+ blocks: [
3992
+ {
3993
+ clientId: 'block-parent',
3994
+ innerBlocks: [
3995
+ {
3996
+ clientId: 'block-1',
3997
+ innerBlocks: [],
3998
+ },
3999
+ ],
4000
+ },
4001
+ ],
4002
+ } );
4003
+ expect( state ).toBe( 'block-1' );
4004
+ } );
4005
+
4006
+ it( 'does not clear on unrelated actions when state is set', () => {
4007
+ const state = editedContentOnlySection( 'block-1', {
4008
+ type: 'SELECT_BLOCK',
4009
+ clientId: 'block-2',
4010
+ } );
4011
+ expect( state ).toBe( 'block-1' );
4012
+ } );
4013
+
4014
+ it( 'does not run cleanup logic when state is already empty', () => {
4015
+ const state = editedContentOnlySection( undefined, {
4016
+ type: 'REMOVE_BLOCKS',
4017
+ clientIds: [ 'block-1' ],
4018
+ } );
4019
+ expect( state ).toBeUndefined();
4020
+ } );
4021
+ } );
4022
+
3652
4023
  describe( 'insertionPoint', () => {
3653
4024
  it( 'should default to null', () => {
3654
4025
  const state = insertionPoint( undefined, {} );
@@ -3691,7 +4062,6 @@ describe( 'state', () => {
3691
4062
  settings,
3692
4063
  zoomLevel,
3693
4064
  blockListSettings,
3694
- blockEditingModes,
3695
4065
  editedContentOnlySection,
3696
4066
  } )
3697
4067
  );