@wordpress/block-editor 15.14.0 → 15.14.1-next.v.202603102151.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 (52) hide show
  1. package/build/components/block-tools/index.cjs +4 -3
  2. package/build/components/block-tools/index.cjs.map +2 -2
  3. package/build/components/iframe/index.cjs +14 -6
  4. package/build/components/iframe/index.cjs.map +2 -2
  5. package/build/components/observe-typing/index.cjs +9 -13
  6. package/build/components/observe-typing/index.cjs.map +2 -2
  7. package/build/components/provider/index.cjs +22 -6
  8. package/build/components/provider/index.cjs.map +2 -2
  9. package/build/hooks/block-fields/index.cjs +52 -31
  10. package/build/hooks/block-fields/index.cjs.map +2 -2
  11. package/build/hooks/cross-origin-isolation.cjs +7 -73
  12. package/build/hooks/cross-origin-isolation.cjs.map +2 -2
  13. package/build/private-apis.cjs +1 -0
  14. package/build/private-apis.cjs.map +2 -2
  15. package/build/store/private-keys.cjs +3 -0
  16. package/build/store/private-keys.cjs.map +2 -2
  17. package/build/store/selectors.cjs +9 -7
  18. package/build/store/selectors.cjs.map +2 -2
  19. package/build-module/components/block-tools/index.mjs +4 -3
  20. package/build-module/components/block-tools/index.mjs.map +2 -2
  21. package/build-module/components/iframe/index.mjs +14 -6
  22. package/build-module/components/iframe/index.mjs.map +2 -2
  23. package/build-module/components/observe-typing/index.mjs +9 -13
  24. package/build-module/components/observe-typing/index.mjs.map +2 -2
  25. package/build-module/components/provider/index.mjs +22 -6
  26. package/build-module/components/provider/index.mjs.map +2 -2
  27. package/build-module/hooks/block-fields/index.mjs +53 -32
  28. package/build-module/hooks/block-fields/index.mjs.map +2 -2
  29. package/build-module/hooks/cross-origin-isolation.mjs +7 -73
  30. package/build-module/hooks/cross-origin-isolation.mjs.map +2 -2
  31. package/build-module/private-apis.mjs +3 -1
  32. package/build-module/private-apis.mjs.map +2 -2
  33. package/build-module/store/private-keys.mjs +2 -0
  34. package/build-module/store/private-keys.mjs.map +2 -2
  35. package/build-module/store/selectors.mjs +9 -7
  36. package/build-module/store/selectors.mjs.map +2 -2
  37. package/build-style/style-rtl.css +8 -5
  38. package/build-style/style.css +8 -5
  39. package/package.json +39 -39
  40. package/src/components/block-tools/index.js +11 -4
  41. package/src/components/iframe/index.js +19 -6
  42. package/src/components/observe-typing/index.js +10 -14
  43. package/src/components/provider/index.js +47 -5
  44. package/src/components/responsive-block-control/style.scss +1 -0
  45. package/src/hooks/block-fields/index.js +44 -19
  46. package/src/hooks/block-fields/styles.scss +7 -9
  47. package/src/hooks/cross-origin-isolation.js +8 -107
  48. package/src/hooks/test/cross-origin-isolation.js +11 -42
  49. package/src/private-apis.js +2 -0
  50. package/src/store/private-keys.js +1 -0
  51. package/src/store/selectors.js +27 -9
  52. package/src/store/test/selectors.js +540 -0
@@ -5,6 +5,8 @@ import {
5
5
  registerBlockType,
6
6
  unregisterBlockType,
7
7
  setFreeformContentHandlerName,
8
+ setDefaultBlockName,
9
+ getDefaultBlockName,
8
10
  } from '@wordpress/blocks';
9
11
  import { RawHTML } from '@wordpress/element';
10
12
  import { symbol } from '@wordpress/icons';
@@ -16,6 +18,7 @@ import { select, dispatch } from '@wordpress/data';
16
18
  import * as selectors from '../selectors';
17
19
  import { store } from '../';
18
20
  import { lock } from '../../lock-unlock';
21
+ import { sectionRootClientIdKey } from '../private-keys';
19
22
 
20
23
  const {
21
24
  getBlockName,
@@ -72,6 +75,8 @@ const {
72
75
  wasBlockJustInserted,
73
76
  getBlocksByName,
74
77
  getBlockEditingMode,
78
+ canRemoveBlock,
79
+ canMoveBlock,
75
80
  } = selectors;
76
81
 
77
82
  describe( 'selectors', () => {
@@ -187,6 +192,20 @@ describe( 'selectors', () => {
187
192
  ancestor: [ 'core/test-block-ancestor' ],
188
193
  } );
189
194
 
195
+ registerBlockType( 'core/test-content-block', {
196
+ apiVersion: 3,
197
+ save: ( props ) => props.attributes.text,
198
+ category: 'text',
199
+ title: 'Test Content Block',
200
+ icon: 'test',
201
+ attributes: {
202
+ text: {
203
+ type: 'string',
204
+ role: 'content',
205
+ },
206
+ },
207
+ } );
208
+
190
209
  setFreeformContentHandlerName( 'core/freeform' );
191
210
 
192
211
  cachedSelectors.forEach( ( { clear } ) => clear() );
@@ -203,6 +222,7 @@ describe( 'selectors', () => {
203
222
  unregisterBlockType( 'core/test-block-parent' );
204
223
  unregisterBlockType( 'core/test-block-requires-ancestor' );
205
224
  unregisterBlockType( 'core/test-block-requires-ancestor-parent' );
225
+ unregisterBlockType( 'core/test-content-block' );
206
226
 
207
227
  setFreeformContentHandlerName( undefined );
208
228
  } );
@@ -3090,6 +3110,163 @@ describe( 'selectors', () => {
3090
3110
  ).toBe( false );
3091
3111
  } );
3092
3112
 
3113
+ it( 'allows inserting blocks into a non-section contentOnly container', () => {
3114
+ // When the parent has contentOnly editing mode but is NOT
3115
+ // within a section hierarchy, the contentOnly insertion
3116
+ // restriction does not apply.
3117
+ const state = {
3118
+ blocks: {
3119
+ byClientId: new Map(
3120
+ Object.entries( {
3121
+ parent: { name: 'core/test-block-a' },
3122
+ child: { name: 'core/test-block-b' },
3123
+ } )
3124
+ ),
3125
+ attributes: new Map(
3126
+ Object.entries( {
3127
+ parent: {},
3128
+ child: {},
3129
+ } )
3130
+ ),
3131
+ parents: new Map(
3132
+ Object.entries( {
3133
+ parent: '',
3134
+ child: 'parent',
3135
+ } )
3136
+ ),
3137
+ order: new Map( [
3138
+ [ '', [ 'parent' ] ],
3139
+ [ 'parent', [ 'child' ] ],
3140
+ ] ),
3141
+ },
3142
+ blockListSettings: {
3143
+ parent: {},
3144
+ },
3145
+ settings: {},
3146
+ blockEditingModes: new Map(),
3147
+ derivedBlockEditingModes: new Map( [
3148
+ [ 'parent', 'contentOnly' ],
3149
+ ] ),
3150
+ };
3151
+ expect(
3152
+ canInsertBlockType( state, 'core/test-block-b', 'parent' )
3153
+ ).toBe( true );
3154
+ } );
3155
+
3156
+ it( 'prevents inserting content blocks into a non-insertable contentOnly container within a section', () => {
3157
+ // When a contentOnly container is inside a section and is not
3158
+ // the section root or a content-to-content match, the
3159
+ // contentOnly insertion restriction blocks it.
3160
+ const state = {
3161
+ blocks: {
3162
+ byClientId: new Map(
3163
+ Object.entries( {
3164
+ section: { name: 'core/test-block-b' },
3165
+ container: {
3166
+ name: 'core/test-block-a',
3167
+ },
3168
+ } )
3169
+ ),
3170
+ attributes: new Map(
3171
+ Object.entries( {
3172
+ section: {
3173
+ // patternName makes this block a section.
3174
+ metadata: { patternName: 'test-pattern' },
3175
+ },
3176
+ container: {},
3177
+ } )
3178
+ ),
3179
+ parents: new Map(
3180
+ Object.entries( {
3181
+ section: '',
3182
+ container: 'section',
3183
+ } )
3184
+ ),
3185
+ order: new Map( [
3186
+ [ '', [ 'section' ] ],
3187
+ [ 'section', [ 'container' ] ],
3188
+ [ 'container', [] ],
3189
+ ] ),
3190
+ },
3191
+ blockListSettings: {
3192
+ section: {},
3193
+ container: {},
3194
+ },
3195
+ settings: {
3196
+ [ sectionRootClientIdKey ]: '',
3197
+ },
3198
+ blockEditingModes: new Map(),
3199
+ derivedBlockEditingModes: new Map( [
3200
+ [ 'container', 'contentOnly' ],
3201
+ ] ),
3202
+ };
3203
+ // A content block into a non-content container that is not the
3204
+ // section root is blocked by the contentOnly gating.
3205
+ expect(
3206
+ canInsertBlockType(
3207
+ state,
3208
+ 'core/test-content-block',
3209
+ 'container'
3210
+ )
3211
+ ).toBe( false );
3212
+ } );
3213
+
3214
+ it( 'allows inserting content blocks into a content container within a section', () => {
3215
+ // When the container is a content block and the block being
3216
+ // inserted is also a content block, insertion is allowed
3217
+ // even within a section (content-to-content match).
3218
+ const state = {
3219
+ blocks: {
3220
+ byClientId: new Map(
3221
+ Object.entries( {
3222
+ section: { name: 'core/test-block-b' },
3223
+ container: {
3224
+ name: 'core/test-content-block',
3225
+ },
3226
+ } )
3227
+ ),
3228
+ attributes: new Map(
3229
+ Object.entries( {
3230
+ section: {
3231
+ // patternName makes this block a section.
3232
+ metadata: { patternName: 'test-pattern' },
3233
+ },
3234
+ container: {},
3235
+ } )
3236
+ ),
3237
+ parents: new Map(
3238
+ Object.entries( {
3239
+ section: '',
3240
+ container: 'section',
3241
+ } )
3242
+ ),
3243
+ order: new Map( [
3244
+ [ '', [ 'section' ] ],
3245
+ [ 'section', [ 'container' ] ],
3246
+ [ 'container', [] ],
3247
+ ] ),
3248
+ },
3249
+ blockListSettings: {
3250
+ section: {},
3251
+ container: {},
3252
+ },
3253
+ settings: {
3254
+ [ sectionRootClientIdKey ]: '',
3255
+ },
3256
+ blockEditingModes: new Map(),
3257
+ derivedBlockEditingModes: new Map( [
3258
+ [ 'container', 'contentOnly' ],
3259
+ ] ),
3260
+ };
3261
+ expect(
3262
+ canInsertBlockType(
3263
+ state,
3264
+ 'core/test-content-block',
3265
+ 'container'
3266
+ )
3267
+ ).toBe( true );
3268
+ } );
3269
+
3093
3270
  it( 'should allow blocks to be inserted if both parent and ancestor restrictions are met', () => {
3094
3271
  const state = {
3095
3272
  blocks: {
@@ -3499,6 +3676,7 @@ describe( 'selectors', () => {
3499
3676
  'core/freeform',
3500
3677
  'core/test-block-ancestor',
3501
3678
  'core/test-block-parent',
3679
+ 'core/test-content-block',
3502
3680
  'core/block/1',
3503
3681
  'core/block/2',
3504
3682
  ] );
@@ -3518,6 +3696,7 @@ describe( 'selectors', () => {
3518
3696
  'core/freeform',
3519
3697
  'core/test-block-ancestor',
3520
3698
  'core/test-block-parent',
3699
+ 'core/test-content-block',
3521
3700
  'core/block/1',
3522
3701
  'core/block/2',
3523
3702
  ] );
@@ -4267,6 +4446,367 @@ describe( 'selectors', () => {
4267
4446
  ).toBe( false );
4268
4447
  } );
4269
4448
  } );
4449
+
4450
+ describe( 'canRemoveBlock', () => {
4451
+ it( 'allows removal from a non-section contentOnly container', () => {
4452
+ // When the parent has contentOnly editing mode but is NOT
4453
+ // within a section, the contentOnly removal restriction
4454
+ // does not apply.
4455
+ const state = {
4456
+ blocks: {
4457
+ byClientId: new Map(
4458
+ Object.entries( {
4459
+ parent: { name: 'core/test-block-a' },
4460
+ child: { name: 'core/test-block-b' },
4461
+ } )
4462
+ ),
4463
+ attributes: new Map(
4464
+ Object.entries( {
4465
+ parent: {},
4466
+ child: {},
4467
+ } )
4468
+ ),
4469
+ parents: new Map(
4470
+ Object.entries( {
4471
+ parent: '',
4472
+ child: 'parent',
4473
+ } )
4474
+ ),
4475
+ order: new Map( [
4476
+ [ '', [ 'parent' ] ],
4477
+ [ 'parent', [ 'child' ] ],
4478
+ ] ),
4479
+ },
4480
+ blockListSettings: {
4481
+ parent: {},
4482
+ },
4483
+ settings: {},
4484
+ blockEditingModes: new Map(),
4485
+ derivedBlockEditingModes: new Map( [
4486
+ [ 'parent', 'contentOnly' ],
4487
+ ] ),
4488
+ };
4489
+ expect( canRemoveBlock( state, 'child' ) ).toBe( true );
4490
+ } );
4491
+
4492
+ it( 'prevents removal from a contentOnly container within a section', () => {
4493
+ // When the parent is within a section and has contentOnly
4494
+ // editing mode, blocks in a non-insertable container
4495
+ // cannot be removed.
4496
+ const state = {
4497
+ blocks: {
4498
+ byClientId: new Map(
4499
+ Object.entries( {
4500
+ section: { name: 'core/test-block-b' },
4501
+ container: {
4502
+ name: 'core/test-block-a',
4503
+ },
4504
+ child: { name: 'core/test-content-block' },
4505
+ } )
4506
+ ),
4507
+ attributes: new Map(
4508
+ Object.entries( {
4509
+ section: {
4510
+ // patternName makes this block a section.
4511
+ metadata: { patternName: 'test-pattern' },
4512
+ },
4513
+ container: {},
4514
+ child: {},
4515
+ } )
4516
+ ),
4517
+ parents: new Map(
4518
+ Object.entries( {
4519
+ section: '',
4520
+ container: 'section',
4521
+ child: 'container',
4522
+ } )
4523
+ ),
4524
+ order: new Map( [
4525
+ [ '', [ 'section' ] ],
4526
+ [ 'section', [ 'container' ] ],
4527
+ [ 'container', [ 'child' ] ],
4528
+ ] ),
4529
+ },
4530
+ blockListSettings: {
4531
+ section: {},
4532
+ container: {},
4533
+ },
4534
+ settings: {
4535
+ [ sectionRootClientIdKey ]: '',
4536
+ },
4537
+ blockEditingModes: new Map(),
4538
+ derivedBlockEditingModes: new Map( [
4539
+ [ 'container', 'contentOnly' ],
4540
+ ] ),
4541
+ };
4542
+ expect( canRemoveBlock( state, 'child' ) ).toBe( false );
4543
+ } );
4544
+
4545
+ it( 'allows removal of a content block from a content container within a section', () => {
4546
+ // When both the container and the child are content blocks,
4547
+ // the contentOnly gating allows removal
4548
+ // (content-to-content match).
4549
+ const state = {
4550
+ blocks: {
4551
+ byClientId: new Map(
4552
+ Object.entries( {
4553
+ section: { name: 'core/test-block-b' },
4554
+ container: {
4555
+ name: 'core/test-content-block',
4556
+ },
4557
+ child: { name: 'core/test-content-block' },
4558
+ } )
4559
+ ),
4560
+ attributes: new Map(
4561
+ Object.entries( {
4562
+ section: {
4563
+ // patternName makes this block a section.
4564
+ metadata: { patternName: 'test-pattern' },
4565
+ },
4566
+ container: {},
4567
+ child: {},
4568
+ } )
4569
+ ),
4570
+ parents: new Map(
4571
+ Object.entries( {
4572
+ section: '',
4573
+ container: 'section',
4574
+ child: 'container',
4575
+ } )
4576
+ ),
4577
+ order: new Map( [
4578
+ [ '', [ 'section' ] ],
4579
+ [ 'section', [ 'container' ] ],
4580
+ [ 'container', [ 'child' ] ],
4581
+ ] ),
4582
+ },
4583
+ blockListSettings: {
4584
+ section: {},
4585
+ container: {},
4586
+ },
4587
+ settings: {
4588
+ [ sectionRootClientIdKey ]: '',
4589
+ },
4590
+ blockEditingModes: new Map(),
4591
+ derivedBlockEditingModes: new Map( [
4592
+ [ 'container', 'contentOnly' ],
4593
+ ] ),
4594
+ };
4595
+ expect( canRemoveBlock( state, 'child' ) ).toBe( true );
4596
+ } );
4597
+
4598
+ it( 'allows removing one of multiple default blocks in a contentOnly section container', () => {
4599
+ // When in contentOnly mode within a section, default blocks
4600
+ // can be removed as long as at least one other default block
4601
+ // remains.
4602
+ const previousDefaultBlockName = getDefaultBlockName();
4603
+ setDefaultBlockName( 'core/test-content-block' );
4604
+ const state = {
4605
+ blocks: {
4606
+ byClientId: new Map(
4607
+ Object.entries( {
4608
+ section: { name: 'core/test-block-b' },
4609
+ container: {
4610
+ name: 'core/test-block-a',
4611
+ },
4612
+ child1: {
4613
+ name: 'core/test-content-block',
4614
+ },
4615
+ child2: {
4616
+ name: 'core/test-content-block',
4617
+ },
4618
+ } )
4619
+ ),
4620
+ attributes: new Map(
4621
+ Object.entries( {
4622
+ section: {
4623
+ // patternName makes this block a section.
4624
+ metadata: { patternName: 'test-pattern' },
4625
+ },
4626
+ container: {},
4627
+ child1: {},
4628
+ child2: {},
4629
+ } )
4630
+ ),
4631
+ parents: new Map(
4632
+ Object.entries( {
4633
+ section: '',
4634
+ container: 'section',
4635
+ child1: 'container',
4636
+ child2: 'container',
4637
+ } )
4638
+ ),
4639
+ order: new Map( [
4640
+ [ '', [ 'section' ] ],
4641
+ [ 'section', [ 'container' ] ],
4642
+ [ 'container', [ 'child1', 'child2' ] ],
4643
+ ] ),
4644
+ },
4645
+ blockListSettings: {
4646
+ section: {},
4647
+ container: {},
4648
+ },
4649
+ settings: {
4650
+ [ sectionRootClientIdKey ]: '',
4651
+ },
4652
+ blockEditingModes: new Map(),
4653
+ derivedBlockEditingModes: new Map( [
4654
+ [ 'container', 'contentOnly' ],
4655
+ ] ),
4656
+ };
4657
+ expect( canRemoveBlock( state, 'child1' ) ).toBe( true );
4658
+ setDefaultBlockName( previousDefaultBlockName );
4659
+ } );
4660
+ } );
4661
+
4662
+ describe( 'canMoveBlock', () => {
4663
+ it( 'allows moving within a non-section contentOnly container', () => {
4664
+ // When the parent has contentOnly editing mode but is NOT
4665
+ // within a section, the contentOnly move restriction does
4666
+ // not apply.
4667
+ const state = {
4668
+ blocks: {
4669
+ byClientId: new Map(
4670
+ Object.entries( {
4671
+ parent: { name: 'core/test-block-a' },
4672
+ child: { name: 'core/test-block-b' },
4673
+ } )
4674
+ ),
4675
+ attributes: new Map(
4676
+ Object.entries( {
4677
+ parent: {},
4678
+ child: {},
4679
+ } )
4680
+ ),
4681
+ parents: new Map(
4682
+ Object.entries( {
4683
+ parent: '',
4684
+ child: 'parent',
4685
+ } )
4686
+ ),
4687
+ order: new Map( [
4688
+ [ '', [ 'parent' ] ],
4689
+ [ 'parent', [ 'child' ] ],
4690
+ ] ),
4691
+ },
4692
+ blockListSettings: {
4693
+ parent: {},
4694
+ },
4695
+ settings: {},
4696
+ blockEditingModes: new Map(),
4697
+ derivedBlockEditingModes: new Map( [
4698
+ [ 'parent', 'contentOnly' ],
4699
+ ] ),
4700
+ };
4701
+ expect( canMoveBlock( state, 'child' ) ).toBe( true );
4702
+ } );
4703
+
4704
+ it( 'prevents moving within a contentOnly container inside a section', () => {
4705
+ // When the parent is within a section and has contentOnly
4706
+ // editing mode, blocks in a non-insertable container
4707
+ // cannot be moved.
4708
+ const state = {
4709
+ blocks: {
4710
+ byClientId: new Map(
4711
+ Object.entries( {
4712
+ section: { name: 'core/test-block-b' },
4713
+ container: {
4714
+ name: 'core/test-block-a',
4715
+ },
4716
+ child: { name: 'core/test-content-block' },
4717
+ } )
4718
+ ),
4719
+ attributes: new Map(
4720
+ Object.entries( {
4721
+ section: {
4722
+ // patternName makes this block a section.
4723
+ metadata: { patternName: 'test-pattern' },
4724
+ },
4725
+ container: {},
4726
+ child: {},
4727
+ } )
4728
+ ),
4729
+ parents: new Map(
4730
+ Object.entries( {
4731
+ section: '',
4732
+ container: 'section',
4733
+ child: 'container',
4734
+ } )
4735
+ ),
4736
+ order: new Map( [
4737
+ [ '', [ 'section' ] ],
4738
+ [ 'section', [ 'container' ] ],
4739
+ [ 'container', [ 'child' ] ],
4740
+ ] ),
4741
+ },
4742
+ blockListSettings: {
4743
+ section: {},
4744
+ container: {},
4745
+ },
4746
+ settings: {
4747
+ [ sectionRootClientIdKey ]: '',
4748
+ },
4749
+ blockEditingModes: new Map(),
4750
+ derivedBlockEditingModes: new Map( [
4751
+ [ 'container', 'contentOnly' ],
4752
+ ] ),
4753
+ };
4754
+ expect( canMoveBlock( state, 'child' ) ).toBe( false );
4755
+ } );
4756
+
4757
+ it( 'allows moving a content block within a content container inside a section', () => {
4758
+ // When both the container and the child are content blocks,
4759
+ // the contentOnly gating allows moving
4760
+ // (content-to-content match).
4761
+ const state = {
4762
+ blocks: {
4763
+ byClientId: new Map(
4764
+ Object.entries( {
4765
+ section: { name: 'core/test-block-b' },
4766
+ container: {
4767
+ name: 'core/test-content-block',
4768
+ },
4769
+ child: { name: 'core/test-content-block' },
4770
+ } )
4771
+ ),
4772
+ attributes: new Map(
4773
+ Object.entries( {
4774
+ section: {
4775
+ // patternName makes this block a section.
4776
+ metadata: { patternName: 'test-pattern' },
4777
+ },
4778
+ container: {},
4779
+ child: {},
4780
+ } )
4781
+ ),
4782
+ parents: new Map(
4783
+ Object.entries( {
4784
+ section: '',
4785
+ container: 'section',
4786
+ child: 'container',
4787
+ } )
4788
+ ),
4789
+ order: new Map( [
4790
+ [ '', [ 'section' ] ],
4791
+ [ 'section', [ 'container' ] ],
4792
+ [ 'container', [ 'child' ] ],
4793
+ ] ),
4794
+ },
4795
+ blockListSettings: {
4796
+ section: {},
4797
+ container: {},
4798
+ },
4799
+ settings: {
4800
+ [ sectionRootClientIdKey ]: '',
4801
+ },
4802
+ blockEditingModes: new Map(),
4803
+ derivedBlockEditingModes: new Map( [
4804
+ [ 'container', 'contentOnly' ],
4805
+ ] ),
4806
+ };
4807
+ expect( canMoveBlock( state, 'child' ) ).toBe( true );
4808
+ } );
4809
+ } );
4270
4810
  } );
4271
4811
 
4272
4812
  describe( 'getInserterItems with core blocks prioritization', () => {