@wordpress/block-editor 9.3.0 → 9.4.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 (171) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/block-pattern-setup/index.js +3 -9
  3. package/build/components/block-pattern-setup/index.js.map +1 -1
  4. package/build/components/block-pattern-setup/setup-toolbar.js +3 -8
  5. package/build/components/block-pattern-setup/setup-toolbar.js.map +1 -1
  6. package/build/components/block-preview/auto.js +21 -5
  7. package/build/components/block-preview/auto.js.map +1 -1
  8. package/build/components/block-settings-menu/block-edit-visually-button.js +70 -0
  9. package/build/components/block-settings-menu/block-edit-visually-button.js.map +1 -0
  10. package/build/components/block-settings-menu/block-settings-dropdown.js +1 -1
  11. package/build/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  12. package/build/components/block-settings-menu/index.js +6 -2
  13. package/build/components/block-settings-menu/index.js.map +1 -1
  14. package/build/components/block-settings-menu-controls/index.js +4 -1
  15. package/build/components/block-settings-menu-controls/index.js.map +1 -1
  16. package/build/components/block-title/use-block-display-title.js +3 -10
  17. package/build/components/block-title/use-block-display-title.js.map +1 -1
  18. package/build/components/colors-gradients/dropdown.js +2 -1
  19. package/build/components/colors-gradients/dropdown.js.map +1 -1
  20. package/build/components/duotone/components.js +145 -0
  21. package/build/components/duotone/components.js.map +1 -0
  22. package/build/components/duotone/index.js +40 -0
  23. package/build/components/duotone/index.js.map +1 -0
  24. package/build/components/duotone/utils.js +38 -0
  25. package/build/components/duotone/utils.js.map +1 -0
  26. package/build/components/duotone-control/index.js +17 -5
  27. package/build/components/duotone-control/index.js.map +1 -1
  28. package/build/components/index.js +14 -0
  29. package/build/components/index.js.map +1 -1
  30. package/build/components/inserter/index.js +3 -3
  31. package/build/components/inserter/index.js.map +1 -1
  32. package/build/components/media-placeholder/index.js +1 -0
  33. package/build/components/media-placeholder/index.js.map +1 -1
  34. package/build/components/media-placeholder/index.native.js +4 -4
  35. package/build/components/media-placeholder/index.native.js.map +1 -1
  36. package/build/components/media-replace-flow/index.js +3 -7
  37. package/build/components/media-replace-flow/index.js.map +1 -1
  38. package/build/components/publish-date-time-picker/index.js +3 -0
  39. package/build/components/publish-date-time-picker/index.js.map +1 -1
  40. package/build/components/rich-text/use-input-rules.js +4 -13
  41. package/build/components/rich-text/use-input-rules.js.map +1 -1
  42. package/build/components/rich-text/use-paste-handler.js +20 -5
  43. package/build/components/rich-text/use-paste-handler.js.map +1 -1
  44. package/build/elements/index.js +11 -3
  45. package/build/elements/index.js.map +1 -1
  46. package/build/hooks/aria-label.js +71 -0
  47. package/build/hooks/aria-label.js.map +1 -0
  48. package/build/hooks/duotone.js +33 -160
  49. package/build/hooks/duotone.js.map +1 -1
  50. package/build/hooks/index.js +3 -7
  51. package/build/hooks/index.js.map +1 -1
  52. package/build/hooks/layout.js +6 -4
  53. package/build/hooks/layout.js.map +1 -1
  54. package/build/index.js +0 -7
  55. package/build/index.js.map +1 -1
  56. package/build/layouts/flex.js +2 -2
  57. package/build/layouts/flex.js.map +1 -1
  58. package/build/store/actions.js +10 -14
  59. package/build/store/actions.js.map +1 -1
  60. package/build/store/reducer.js +18 -9
  61. package/build/store/reducer.js.map +1 -1
  62. package/build/utils/selection.js +34 -0
  63. package/build/utils/selection.js.map +1 -0
  64. package/build-module/components/block-pattern-setup/index.js +3 -9
  65. package/build-module/components/block-pattern-setup/index.js.map +1 -1
  66. package/build-module/components/block-pattern-setup/setup-toolbar.js +3 -8
  67. package/build-module/components/block-pattern-setup/setup-toolbar.js.map +1 -1
  68. package/build-module/components/block-preview/auto.js +20 -5
  69. package/build-module/components/block-preview/auto.js.map +1 -1
  70. package/build-module/components/block-settings-menu/block-edit-visually-button.js +56 -0
  71. package/build-module/components/block-settings-menu/block-edit-visually-button.js.map +1 -0
  72. package/build-module/components/block-settings-menu/block-settings-dropdown.js +2 -4
  73. package/build-module/components/block-settings-menu/block-settings-dropdown.js.map +1 -1
  74. package/build-module/components/block-settings-menu/index.js +6 -3
  75. package/build-module/components/block-settings-menu/index.js.map +1 -1
  76. package/build-module/components/block-settings-menu-controls/index.js +5 -2
  77. package/build-module/components/block-settings-menu-controls/index.js.map +1 -1
  78. package/build-module/components/block-title/use-block-display-title.js +3 -9
  79. package/build-module/components/block-title/use-block-display-title.js.map +1 -1
  80. package/build-module/components/colors-gradients/dropdown.js +2 -1
  81. package/build-module/components/colors-gradients/dropdown.js.map +1 -1
  82. package/build-module/components/duotone/components.js +130 -0
  83. package/build-module/components/duotone/components.js.map +1 -0
  84. package/build-module/components/duotone/index.js +3 -0
  85. package/build-module/components/duotone/index.js.map +1 -0
  86. package/build-module/components/duotone/utils.js +30 -0
  87. package/build-module/components/duotone/utils.js.map +1 -0
  88. package/build-module/components/duotone-control/index.js +18 -6
  89. package/build-module/components/duotone-control/index.js.map +1 -1
  90. package/build-module/components/index.js +1 -0
  91. package/build-module/components/index.js.map +1 -1
  92. package/build-module/components/inserter/index.js +3 -2
  93. package/build-module/components/inserter/index.js.map +1 -1
  94. package/build-module/components/media-placeholder/index.js +1 -0
  95. package/build-module/components/media-placeholder/index.js.map +1 -1
  96. package/build-module/components/media-placeholder/index.native.js +5 -3
  97. package/build-module/components/media-placeholder/index.native.js.map +1 -1
  98. package/build-module/components/media-replace-flow/index.js +3 -6
  99. package/build-module/components/media-replace-flow/index.js.map +1 -1
  100. package/build-module/components/publish-date-time-picker/index.js +2 -0
  101. package/build-module/components/publish-date-time-picker/index.js.map +1 -1
  102. package/build-module/components/rich-text/use-input-rules.js +3 -11
  103. package/build-module/components/rich-text/use-input-rules.js.map +1 -1
  104. package/build-module/components/rich-text/use-paste-handler.js +20 -5
  105. package/build-module/components/rich-text/use-paste-handler.js.map +1 -1
  106. package/build-module/elements/index.js +7 -1
  107. package/build-module/elements/index.js.map +1 -1
  108. package/build-module/hooks/aria-label.js +59 -0
  109. package/build-module/hooks/aria-label.js.map +1 -0
  110. package/build-module/hooks/duotone.js +22 -140
  111. package/build-module/hooks/duotone.js.map +1 -1
  112. package/build-module/hooks/index.js +1 -1
  113. package/build-module/hooks/index.js.map +1 -1
  114. package/build-module/hooks/layout.js +6 -4
  115. package/build-module/hooks/layout.js.map +1 -1
  116. package/build-module/index.js +1 -1
  117. package/build-module/index.js.map +1 -1
  118. package/build-module/layouts/flex.js +2 -2
  119. package/build-module/layouts/flex.js.map +1 -1
  120. package/build-module/store/actions.js +6 -11
  121. package/build-module/store/actions.js.map +1 -1
  122. package/build-module/store/reducer.js +19 -10
  123. package/build-module/store/reducer.js.map +1 -1
  124. package/build-module/utils/selection.js +24 -0
  125. package/build-module/utils/selection.js.map +1 -0
  126. package/build-style/style-rtl.css +5 -1
  127. package/build-style/style.css +5 -1
  128. package/package.json +28 -28
  129. package/src/components/block-draggable/test/helpers.native.js +3 -3
  130. package/src/components/block-list/style.scss +1 -1
  131. package/src/components/block-pattern-setup/index.js +2 -10
  132. package/src/components/block-pattern-setup/setup-toolbar.js +2 -9
  133. package/src/components/block-preview/auto.js +17 -3
  134. package/src/components/block-settings-menu/block-edit-visually-button.js +52 -0
  135. package/src/components/block-settings-menu/block-settings-dropdown.js +3 -2
  136. package/src/components/block-settings-menu/index.js +15 -11
  137. package/src/components/block-settings-menu-controls/index.js +3 -2
  138. package/src/components/block-title/use-block-display-title.js +9 -7
  139. package/src/components/colors-gradients/dropdown.js +1 -0
  140. package/src/components/duotone/components.js +133 -0
  141. package/src/components/duotone/index.js +7 -0
  142. package/src/components/duotone/utils.js +25 -0
  143. package/src/components/duotone-control/index.js +12 -7
  144. package/src/components/duotone-control/style.scss +5 -0
  145. package/src/components/index.js +1 -0
  146. package/src/components/inserter/index.js +3 -5
  147. package/src/components/link-control/test/fixtures/index.js +3 -4
  148. package/src/components/link-control/test/index.js +58 -69
  149. package/src/components/media-placeholder/index.js +1 -0
  150. package/src/components/media-placeholder/index.native.js +9 -5
  151. package/src/components/media-replace-flow/index.js +2 -8
  152. package/src/components/media-upload/README.md +8 -0
  153. package/src/components/publish-date-time-picker/index.js +2 -0
  154. package/src/components/responsive-block-control/README.md +3 -1
  155. package/src/components/responsive-block-control/test/index.js +1 -2
  156. package/src/components/rich-text/use-input-rules.js +6 -15
  157. package/src/components/rich-text/use-paste-handler.js +17 -5
  158. package/src/elements/index.js +8 -1
  159. package/src/elements/test/index.js +18 -0
  160. package/src/hooks/aria-label.js +67 -0
  161. package/src/hooks/duotone.js +18 -139
  162. package/src/hooks/index.js +1 -1
  163. package/src/hooks/layout.js +20 -9
  164. package/src/index.js +0 -1
  165. package/src/layouts/flex.js +2 -2
  166. package/src/store/actions.js +8 -21
  167. package/src/store/reducer.js +21 -9
  168. package/src/store/test/reducer.js +138 -10
  169. package/src/store/test/selectors.js +3 -6
  170. package/src/utils/selection.js +26 -0
  171. package/src/utils/test/selection.js +39 -0
@@ -4,7 +4,6 @@
4
4
  import { render, unmountComponentAtNode } from 'react-dom';
5
5
  import { act, Simulate } from 'react-dom/test-utils';
6
6
  import { queryByText, queryByRole } from '@testing-library/react';
7
- import { default as lodash, first, last, nth, uniqueId } from 'lodash';
8
7
  /**
9
8
  * WordPress dependencies
10
9
  */
@@ -18,13 +17,20 @@ import { useSelect } from '@wordpress/data';
18
17
  * Internal dependencies
19
18
  */
20
19
  import LinkControl from '../';
21
- import { fauxEntitySuggestions, fetchFauxEntitySuggestions } from './fixtures';
20
+ import {
21
+ fauxEntitySuggestions,
22
+ fetchFauxEntitySuggestions,
23
+ uniqueId,
24
+ } from './fixtures';
22
25
 
23
26
  // Mock debounce() so that it runs instantly.
24
- lodash.debounce = jest.fn( ( callback ) => {
25
- callback.cancel = jest.fn();
26
- return callback;
27
- } );
27
+ jest.mock( 'lodash', () => ( {
28
+ ...jest.requireActual( 'lodash' ),
29
+ debounce: ( fn ) => {
30
+ fn.cancel = jest.fn();
31
+ return fn;
32
+ },
33
+ } ) );
28
34
 
29
35
  const mockFetchSearchSuggestions = jest.fn();
30
36
 
@@ -357,7 +363,7 @@ describe( 'Searching for a link', () => {
357
363
 
358
364
  it( 'should display only search suggestions when current input value is not URL-like', async () => {
359
365
  const searchTerm = 'Hello world';
360
- const firstFauxSuggestion = first( fauxEntitySuggestions );
366
+ const firstFauxSuggestion = fauxEntitySuggestions[ 0 ];
361
367
 
362
368
  act( () => {
363
369
  render( <LinkControl />, container );
@@ -377,9 +383,9 @@ describe( 'Searching for a link', () => {
377
383
 
378
384
  const searchResultElements = getSearchResults();
379
385
 
380
- const firstSearchResultItemHTML =
381
- first( searchResultElements ).innerHTML;
382
- const lastSearchResultItemHTML = last( searchResultElements ).innerHTML;
386
+ const firstSearchResultItemHTML = searchResultElements[ 0 ].innerHTML;
387
+ const lastSearchResultItemHTML =
388
+ searchResultElements[ searchResultElements.length - 1 ].innerHTML;
383
389
 
384
390
  expect( searchResultElements ).toHaveLength(
385
391
  fauxEntitySuggestions.length
@@ -502,7 +508,8 @@ describe( 'Searching for a link', () => {
502
508
  const searchResultElements = getSearchResults();
503
509
 
504
510
  const lastSearchResultItemHTML =
505
- last( searchResultElements ).innerHTML;
511
+ searchResultElements[ searchResultElements.length - 1 ]
512
+ .innerHTML;
506
513
  const additionalDefaultFallbackURLSuggestionLength = 1;
507
514
 
508
515
  // We should see a search result for each of the expect search suggestions
@@ -974,11 +981,9 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
974
981
  '[role="listbox"] [role="option"]'
975
982
  );
976
983
 
977
- const createButton = first(
978
- Array.from( searchResultElements ).filter( ( result ) =>
979
- result.innerHTML.includes( 'Create:' )
980
- )
981
- );
984
+ const createButton = Array.from( searchResultElements ).filter(
985
+ ( result ) => result.innerHTML.includes( 'Create:' )
986
+ )[ 0 ];
982
987
 
983
988
  expect( createButton ).not.toBeNull();
984
989
  expect( createButton.innerHTML ).toEqual(
@@ -1071,11 +1076,9 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1071
1076
  '[role="listbox"] [role="option"]'
1072
1077
  );
1073
1078
 
1074
- const createButton = first(
1075
- Array.from( searchResultElements ).filter( ( result ) =>
1076
- result.innerHTML.includes( 'Create:' )
1077
- )
1078
- );
1079
+ const createButton = Array.from( searchResultElements ).filter(
1080
+ ( result ) => result.innerHTML.includes( 'Create:' )
1081
+ )[ 0 ];
1079
1082
 
1080
1083
  await act( async () => {
1081
1084
  Simulate.click( createButton );
@@ -1143,11 +1146,9 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1143
1146
  const searchResultElements = container.querySelectorAll(
1144
1147
  '[role="listbox"] [role="option"]'
1145
1148
  );
1146
- const createButton = first(
1147
- Array.from( searchResultElements ).filter( ( result ) =>
1148
- result.innerHTML.includes( 'Create:' )
1149
- )
1150
- );
1149
+ const createButton = Array.from( searchResultElements ).filter(
1150
+ ( result ) => result.innerHTML.includes( 'Create:' )
1151
+ )[ 0 ];
1151
1152
 
1152
1153
  // Step down into the search results, highlighting the first result item.
1153
1154
  act( () => {
@@ -1210,11 +1211,9 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1210
1211
  '[role="listbox"] [role="option"]'
1211
1212
  );
1212
1213
 
1213
- const createButton = first(
1214
- Array.from( searchResultElements ).filter( ( result ) =>
1215
- result.innerHTML.includes( 'Custom suggestion text' )
1216
- )
1217
- );
1214
+ const createButton = Array.from( searchResultElements ).filter(
1215
+ ( result ) => result.innerHTML.includes( 'Custom suggestion text' )
1216
+ )[ 0 ];
1218
1217
 
1219
1218
  expect( createButton ).not.toBeNull();
1220
1219
  } );
@@ -1241,11 +1240,9 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1241
1240
  const searchResultElements = container.querySelectorAll(
1242
1241
  '[role="listbox"] [role="option"]'
1243
1242
  );
1244
- const createButton = first(
1245
- Array.from( searchResultElements ).filter( ( result ) =>
1246
- result.innerHTML.includes( 'Create:' )
1247
- )
1248
- );
1243
+ const createButton = Array.from( searchResultElements ).filter(
1244
+ ( result ) => result.innerHTML.includes( 'Create:' )
1245
+ )[ 0 ];
1249
1246
 
1250
1247
  // Verify input has no value.
1251
1248
  expect( searchInput.value ).toBe( '' );
@@ -1275,11 +1272,9 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1275
1272
  const searchResultElements = container.querySelectorAll(
1276
1273
  '[role="listbox"] [role="option"]'
1277
1274
  );
1278
- const createButton = first(
1279
- Array.from( searchResultElements ).filter( ( result ) =>
1280
- result.innerHTML.includes( 'New page' )
1281
- )
1282
- );
1275
+ const createButton = Array.from( searchResultElements ).filter(
1276
+ ( result ) => result.innerHTML.includes( 'New page' )
1277
+ )[ 0 ];
1283
1278
 
1284
1279
  // Verify input has no value.
1285
1280
  expect( searchInput.value ).toBe( '' );
@@ -1321,11 +1316,9 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1321
1316
  '[role="listbox"] [role="option"]'
1322
1317
  );
1323
1318
 
1324
- const createButton = first(
1325
- Array.from( searchResultElements ).filter( ( result ) =>
1326
- result.innerHTML.includes( 'New page' )
1327
- )
1328
- );
1319
+ const createButton = Array.from( searchResultElements ).filter(
1320
+ ( result ) => result.innerHTML.includes( 'New page' )
1321
+ )[ 0 ];
1329
1322
 
1330
1323
  expect( createButton ).toBeFalsy(); // Shouldn't exist!
1331
1324
  }
@@ -1366,11 +1359,9 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1366
1359
  let searchResultElements = container.querySelectorAll(
1367
1360
  '[role="listbox"] [role="option"]'
1368
1361
  );
1369
- let createButton = first(
1370
- Array.from( searchResultElements ).filter( ( result ) =>
1371
- result.innerHTML.includes( 'Create:' )
1372
- )
1373
- );
1362
+ let createButton = Array.from( searchResultElements ).filter(
1363
+ ( result ) => result.innerHTML.includes( 'Create:' )
1364
+ )[ 0 ];
1374
1365
 
1375
1366
  await act( async () => {
1376
1367
  Simulate.click( createButton );
@@ -1407,18 +1398,16 @@ describe( 'Creating Entities (eg: Posts, Pages)', () => {
1407
1398
  searchResultElements = container.querySelectorAll(
1408
1399
  '[role="listbox"] [role="option"]'
1409
1400
  );
1410
- createButton = first(
1411
- Array.from( searchResultElements ).filter( ( result ) =>
1412
- result.innerHTML.includes( 'New page' )
1413
- )
1414
- );
1401
+ createButton = Array.from( searchResultElements ).filter(
1402
+ ( result ) => result.innerHTML.includes( 'New page' )
1403
+ )[ 0 ];
1415
1404
  } );
1416
1405
  } );
1417
1406
  } );
1418
1407
 
1419
1408
  describe( 'Selecting links', () => {
1420
1409
  it( 'should display a selected link corresponding to the provided "currentLink" prop', () => {
1421
- const selectedLink = first( fauxEntitySuggestions );
1410
+ const selectedLink = fauxEntitySuggestions[ 0 ];
1422
1411
 
1423
1412
  const LinkControlConsumer = () => {
1424
1413
  const [ link ] = useState( selectedLink );
@@ -1447,7 +1436,7 @@ describe( 'Selecting links', () => {
1447
1436
  } );
1448
1437
 
1449
1438
  it( 'should hide "selected" link UI and display search UI prepopulated with previously selected link title when "Change" button is clicked', () => {
1450
- const selectedLink = first( fauxEntitySuggestions );
1439
+ const selectedLink = fauxEntitySuggestions[ 0 ];
1451
1440
 
1452
1441
  const LinkControlConsumer = () => {
1453
1442
  const [ link, setLink ] = useState( selectedLink );
@@ -1484,7 +1473,7 @@ describe( 'Selecting links', () => {
1484
1473
 
1485
1474
  describe( 'Selection using mouse click', () => {
1486
1475
  it.each( [
1487
- [ 'entity', 'hello world', first( fauxEntitySuggestions ) ], // Entity search.
1476
+ [ 'entity', 'hello world', fauxEntitySuggestions[ 0 ] ], // Entity search.
1488
1477
  [
1489
1478
  'url',
1490
1479
  'https://www.wordpress.org',
@@ -1528,7 +1517,7 @@ describe( 'Selecting links', () => {
1528
1517
 
1529
1518
  const searchResultElements = getSearchResults();
1530
1519
 
1531
- const firstSearchSuggestion = first( searchResultElements );
1520
+ const firstSearchSuggestion = searchResultElements[ 0 ];
1532
1521
 
1533
1522
  // Simulate selecting the first of the search suggestions.
1534
1523
  act( () => {
@@ -1557,7 +1546,7 @@ describe( 'Selecting links', () => {
1557
1546
 
1558
1547
  describe( 'Selection using keyboard', () => {
1559
1548
  it.each( [
1560
- [ 'entity', 'hello world', first( fauxEntitySuggestions ) ], // Entity search.
1549
+ [ 'entity', 'hello world', fauxEntitySuggestions[ 0 ] ], // Entity search.
1561
1550
  [
1562
1551
  'url',
1563
1552
  'https://www.wordpress.org',
@@ -1607,8 +1596,8 @@ describe( 'Selecting links', () => {
1607
1596
 
1608
1597
  const searchResultElements = getSearchResults();
1609
1598
 
1610
- const firstSearchSuggestion = first( searchResultElements );
1611
- const secondSearchSuggestion = nth( searchResultElements, 1 );
1599
+ const firstSearchSuggestion = searchResultElements[ 0 ];
1600
+ const secondSearchSuggestion = searchResultElements[ 1 ];
1612
1601
 
1613
1602
  let selectedSearchResultElement = container.querySelector(
1614
1603
  '[role="option"][aria-selected="true"]'
@@ -1711,8 +1700,8 @@ describe( 'Selecting links', () => {
1711
1700
 
1712
1701
  const searchResultElements = getSearchResults();
1713
1702
 
1714
- const firstSearchSuggestion = first( searchResultElements );
1715
- const secondSearchSuggestion = nth( searchResultElements, 1 );
1703
+ const firstSearchSuggestion = searchResultElements[ 0 ];
1704
+ const secondSearchSuggestion = searchResultElements[ 1 ];
1716
1705
 
1717
1706
  let selectedSearchResultElement = container.querySelector(
1718
1707
  '[role="option"][aria-selected="true"]'
@@ -1758,7 +1747,7 @@ describe( 'Selecting links', () => {
1758
1747
 
1759
1748
  describe( 'Addition Settings UI', () => {
1760
1749
  it( 'should display "New Tab" setting (in "off" mode) by default when a link is selected', async () => {
1761
- const selectedLink = first( fauxEntitySuggestions );
1750
+ const selectedLink = fauxEntitySuggestions[ 0 ];
1762
1751
  const expectedSettingText = 'Open in new tab';
1763
1752
 
1764
1753
  const LinkControlConsumer = () => {
@@ -1791,7 +1780,7 @@ describe( 'Addition Settings UI', () => {
1791
1780
  } );
1792
1781
 
1793
1782
  it( 'should display a setting control with correct default state for each of the custom settings provided', async () => {
1794
- const selectedLink = first( fauxEntitySuggestions );
1783
+ const selectedLink = fauxEntitySuggestions[ 0 ];
1795
1784
 
1796
1785
  const customSettings = [
1797
1786
  {
@@ -2266,7 +2255,7 @@ describe( 'Rich link previews', () => {
2266
2255
  } );
2267
2256
 
2268
2257
  describe( 'Controlling link title text', () => {
2269
- const selectedLink = first( fauxEntitySuggestions );
2258
+ const selectedLink = fauxEntitySuggestions[ 0 ];
2270
2259
 
2271
2260
  it( 'should not show a means to alter the link title text by default', async () => {
2272
2261
  act( () => {
@@ -2285,7 +2274,7 @@ describe( 'Controlling link title text', () => {
2285
2274
  'should not show the link title text input when the URL is `%s`',
2286
2275
  async ( urlValue ) => {
2287
2276
  const selectedLinkWithoutURL = {
2288
- ...first( fauxEntitySuggestions ),
2277
+ ...fauxEntitySuggestions[ 0 ],
2289
2278
  url: urlValue,
2290
2279
  };
2291
2280
 
@@ -328,6 +328,7 @@ export function MediaPlaceholder( {
328
328
  multiple={ multiple }
329
329
  onSelect={ onSelect }
330
330
  allowedTypes={ allowedTypes }
331
+ mode={ 'browse' }
331
332
  value={
332
333
  Array.isArray( value )
333
334
  ? value.map( ( { id } ) => id )
@@ -2,7 +2,6 @@
2
2
  * External dependencies
3
3
  */
4
4
  import { View, Text, TouchableWithoutFeedback } from 'react-native';
5
- import { uniqWith } from 'lodash';
6
5
 
7
6
  /**
8
7
  * WordPress dependencies
@@ -23,12 +22,17 @@ import { Icon, plusCircleFilled } from '@wordpress/icons';
23
22
  */
24
23
  import styles from './styles.scss';
25
24
 
25
+ const isMediaEqual = ( media1, media2 ) =>
26
+ media1.id === media2.id || media1.url === media2.url;
27
+
26
28
  // Remove duplicates after gallery append.
27
29
  const dedupMedia = ( media ) =>
28
- uniqWith(
29
- media,
30
- ( media1, media2 ) =>
31
- media1.id === media2.id || media1.url === media2.url
30
+ media.reduce(
31
+ ( dedupedMedia, mediaItem ) =>
32
+ dedupedMedia.some( ( item ) => isMediaEqual( item, mediaItem ) )
33
+ ? dedupedMedia
34
+ : [ ...dedupedMedia, mediaItem ],
35
+ []
32
36
  );
33
37
 
34
38
  function MediaPlaceholder( props ) {
@@ -1,8 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import { uniqueId } from 'lodash';
5
-
6
1
  /**
7
2
  * WordPress dependencies
8
3
  */
@@ -38,6 +33,7 @@ import LinkControl from '../link-control';
38
33
  import { store as blockEditorStore } from '../../store';
39
34
 
40
35
  const noop = () => {};
36
+ let uniqueId = 0;
41
37
 
42
38
  const MediaReplaceFlow = ( {
43
39
  mediaURL,
@@ -64,9 +60,7 @@ const MediaReplaceFlow = ( {
64
60
  return select( blockEditorStore ).getSettings().mediaUpload;
65
61
  }, [] );
66
62
  const editMediaButtonRef = useRef();
67
- const errorNoticeID = uniqueId(
68
- 'block-editor/media-replace-flow/error-notice/'
69
- );
63
+ const errorNoticeID = `block-editor/media-replace-flow/error-notice/${ ++uniqueId }`;
70
64
 
71
65
  const onUploadError = ( message ) => {
72
66
  const safeMessage = stripHTML( message );
@@ -64,6 +64,14 @@ If allowedTypes is unset all mime types should be allowed.
64
64
  - Required: No
65
65
  - Platform: Web | Mobile
66
66
 
67
+ ### mode
68
+
69
+ Value of Frame content default mode like 'browse', 'upload' etc.
70
+
71
+ - Type: `String`
72
+ - Required: No
73
+ - Default: false
74
+ - Platform: Web
67
75
  ### multiple
68
76
 
69
77
  Whether to allow multiple selections or not.
@@ -4,6 +4,7 @@
4
4
  import { DateTimePicker } from '@wordpress/components';
5
5
  import { __ } from '@wordpress/i18n';
6
6
  import { forwardRef } from '@wordpress/element';
7
+ import { __experimentalGetSettings as getSettings } from '@wordpress/date';
7
8
 
8
9
  /**
9
10
  * Internal dependencies
@@ -27,6 +28,7 @@ function PublishDateTimePicker(
27
28
  onClose={ onClose }
28
29
  />
29
30
  <DateTimePicker
31
+ startOfWeek={ getSettings().l10n.startOfWeek }
30
32
  __nextRemoveHelpButton
31
33
  __nextRemoveResetButton
32
34
  onChange={ onChange }
@@ -171,8 +171,10 @@ const renderDefaultControl = ( labelComponent, viewport ) => {
171
171
  An optional render function (prop) used to render the controls for the _responsive_ settings. If not provided, by default, responsive controls will be _automatically_ rendered using the component returned by the `renderDefaultControl` prop. For _complete_ control over the output of the responsive controls, you may return a component here and it will be rendered when the control group is in "responsive" mode.
172
172
 
173
173
  ```jsx
174
+ let uniqueId = 0;
175
+
174
176
  const renderResponsiveControls = ( viewports ) => {
175
- const inputId = uniqueId(); // lodash
177
+ const inputId = ++uniqueId;
176
178
 
177
179
  return viewports.map( ( { id, label } ) => {
178
180
  return (
@@ -3,7 +3,6 @@
3
3
  */
4
4
  import { render, unmountComponentAtNode } from 'react-dom';
5
5
  import { act, Simulate } from 'react-dom/test-utils';
6
- import { uniqueId } from 'lodash';
7
6
 
8
7
  /**
9
8
  * WordPress dependencies
@@ -31,7 +30,7 @@ afterEach( () => {
31
30
  container = null;
32
31
  } );
33
32
 
34
- const inputId = uniqueId();
33
+ const inputId = 'input-12345678';
35
34
 
36
35
  const sizeOptions = [
37
36
  {
@@ -1,8 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import { findKey } from 'lodash';
5
-
6
1
  /**
7
2
  * WordPress dependencies
8
3
  */
@@ -17,21 +12,17 @@ import { useDispatch } from '@wordpress/data';
17
12
  */
18
13
  import { store as blockEditorStore } from '../../store';
19
14
  import { preventEventDiscovery } from './prevent-event-discovery';
20
-
21
- // A robust way to retain selection position through various
22
- // transforms is to insert a special character at the position and
23
- // then recover it.
24
- const START_OF_SELECTED_AREA = '\u0086';
15
+ import {
16
+ retrieveSelectedAttribute,
17
+ START_OF_SELECTED_AREA,
18
+ } from '../../utils/selection';
25
19
 
26
20
  function findSelection( blocks ) {
27
21
  let i = blocks.length;
28
22
 
29
23
  while ( i-- ) {
30
- const attributeKey = findKey(
31
- blocks[ i ].attributes,
32
- ( v ) =>
33
- typeof v === 'string' &&
34
- v.indexOf( START_OF_SELECTED_AREA ) !== -1
24
+ const attributeKey = retrieveSelectedAttribute(
25
+ blocks[ i ].attributes
35
26
  );
36
27
 
37
28
  if ( attributeKey ) {
@@ -254,17 +254,29 @@ export function usePasteHandler( props ) {
254
254
  }
255
255
 
256
256
  /**
257
- * Normalizes a given string of HTML to remove the Windows specific "Fragment" comments
258
- * and any preceeding and trailing whitespace.
257
+ * Normalizes a given string of HTML to remove the Windows-specific "Fragment"
258
+ * comments and any preceeding and trailing content.
259
259
  *
260
260
  * @param {string} html the html to be normalized
261
261
  * @return {string} the normalized html
262
262
  */
263
263
  function removeWindowsFragments( html ) {
264
- const startReg = /.*<!--StartFragment-->/s;
265
- const endReg = /<!--EndFragment-->.*/s;
264
+ const startStr = '<!--StartFragment-->';
265
+ const startIdx = html.indexOf( startStr );
266
+ if ( startIdx > -1 ) {
267
+ html = html.substring( startIdx + startStr.length );
268
+ } else {
269
+ // No point looking for EndFragment
270
+ return html;
271
+ }
272
+
273
+ const endStr = '<!--EndFragment-->';
274
+ const endIdx = html.indexOf( endStr );
275
+ if ( endIdx > -1 ) {
276
+ html = html.substring( 0, endIdx );
277
+ }
266
278
 
267
- return html.replace( startReg, '' ).replace( endReg, '' );
279
+ return html;
268
280
  }
269
281
 
270
282
  /**
@@ -1 +1,8 @@
1
- export const __experimentalElementButtonClassName = 'wp-element-button';
1
+ const ELEMENT_CLASS_NAMES = {
2
+ button: 'wp-element-button',
3
+ caption: 'wp-element-caption',
4
+ };
5
+
6
+ export const __experimentalGetElementClassName = ( element ) => {
7
+ return ELEMENT_CLASS_NAMES[ element ] ? ELEMENT_CLASS_NAMES[ element ] : '';
8
+ };
@@ -0,0 +1,18 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __experimentalGetElementClassName } from '@wordpress/block-editor';
5
+
6
+ describe( 'element class names', () => {
7
+ it( 'should return the correct class name for button', () => {
8
+ expect( __experimentalGetElementClassName( 'button' ) ).toEqual(
9
+ 'wp-element-button'
10
+ );
11
+ } );
12
+
13
+ it( 'should return an empty string for an unknown element', () => {
14
+ expect(
15
+ __experimentalGetElementClassName( 'unknown-element' )
16
+ ).toEqual( '' );
17
+ } );
18
+ } );
@@ -0,0 +1,67 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { addFilter } from '@wordpress/hooks';
5
+ import { hasBlockSupport } from '@wordpress/blocks';
6
+
7
+ const ARIA_LABEL_SCHEMA = {
8
+ type: 'string',
9
+ source: 'attribute',
10
+ attribute: 'aria-label',
11
+ selector: '*',
12
+ };
13
+
14
+ /**
15
+ * Filters registered block settings, extending attributes with ariaLabel using aria-label
16
+ * of the first node.
17
+ *
18
+ * @param {Object} settings Original block settings.
19
+ *
20
+ * @return {Object} Filtered block settings.
21
+ */
22
+ export function addAttribute( settings ) {
23
+ // Allow blocks to specify their own attribute definition with default values if needed.
24
+ if ( settings?.attributes?.ariaLabel?.type ) {
25
+ return settings;
26
+ }
27
+ if ( hasBlockSupport( settings, 'ariaLabel' ) ) {
28
+ // Gracefully handle if settings.attributes is undefined.
29
+ settings.attributes = {
30
+ ...settings.attributes,
31
+ ariaLabel: ARIA_LABEL_SCHEMA,
32
+ };
33
+ }
34
+
35
+ return settings;
36
+ }
37
+
38
+ /**
39
+ * Override props assigned to save component to inject aria-label, if block
40
+ * supports ariaLabel. This is only applied if the block's save result is an
41
+ * element and not a markup string.
42
+ *
43
+ * @param {Object} extraProps Additional props applied to save element.
44
+ * @param {Object} blockType Block type.
45
+ * @param {Object} attributes Current block attributes.
46
+ *
47
+ * @return {Object} Filtered props applied to save element.
48
+ */
49
+ export function addSaveProps( extraProps, blockType, attributes ) {
50
+ if ( hasBlockSupport( blockType, 'ariaLabel' ) ) {
51
+ extraProps[ 'aria-label' ] =
52
+ attributes.ariaLabel === '' ? null : attributes.ariaLabel;
53
+ }
54
+
55
+ return extraProps;
56
+ }
57
+
58
+ addFilter(
59
+ 'blocks.registerBlockType',
60
+ 'core/ariaLabel/attribute',
61
+ addAttribute
62
+ );
63
+ addFilter(
64
+ 'blocks.getSaveContent.extraProps',
65
+ 'core/ariaLabel/save-props',
66
+ addSaveProps
67
+ );