@wordpress/editor 12.11.0 → 12.14.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 (141) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/components/document-outline/index.js +7 -9
  3. package/build/components/document-outline/index.js.map +1 -1
  4. package/build/components/editor-history/redo.js +2 -1
  5. package/build/components/editor-history/redo.js.map +1 -1
  6. package/build/components/error-boundary/index.js +3 -0
  7. package/build/components/error-boundary/index.js.map +1 -1
  8. package/build/components/global-keyboard-shortcuts/register-shortcuts.js +11 -1
  9. package/build/components/global-keyboard-shortcuts/register-shortcuts.js.map +1 -1
  10. package/build/components/index.js +49 -8
  11. package/build/components/index.js.map +1 -1
  12. package/build/components/page-attributes/order.js +3 -7
  13. package/build/components/page-attributes/order.js.map +1 -1
  14. package/build/components/page-attributes/parent.js +7 -3
  15. package/build/components/page-attributes/parent.js.map +1 -1
  16. package/build/components/post-format/index.js +5 -8
  17. package/build/components/post-format/index.js.map +1 -1
  18. package/build/components/post-locked-modal/index.js +1 -1
  19. package/build/components/post-locked-modal/index.js.map +1 -1
  20. package/build/components/post-publish-panel/maybe-category-panel.js +2 -4
  21. package/build/components/post-publish-panel/maybe-category-panel.js.map +1 -1
  22. package/build/components/post-schedule/label.js +3 -3
  23. package/build/components/post-schedule/label.js.map +1 -1
  24. package/build/components/post-slug/index.js +8 -13
  25. package/build/components/post-slug/index.js.map +1 -1
  26. package/build/components/post-taxonomies/flat-term-selector.js +1 -0
  27. package/build/components/post-taxonomies/flat-term-selector.js.map +1 -1
  28. package/build/components/post-taxonomies/hierarchical-term-selector.js +1 -0
  29. package/build/components/post-taxonomies/hierarchical-term-selector.js.map +1 -1
  30. package/build/components/post-taxonomies/index.js +3 -1
  31. package/build/components/post-taxonomies/index.js.map +1 -1
  32. package/build/components/post-template/index.js +1 -2
  33. package/build/components/post-template/index.js.map +1 -1
  34. package/build/components/post-title/index.js +8 -2
  35. package/build/components/post-title/index.js.map +1 -1
  36. package/build/components/post-url/check.js +54 -0
  37. package/build/components/post-url/check.js.map +1 -0
  38. package/build/components/post-url/index.js +115 -0
  39. package/build/components/post-url/index.js.map +1 -0
  40. package/build/components/post-url/label.js +30 -0
  41. package/build/components/post-url/label.js.map +1 -0
  42. package/build/components/post-visibility/label.js +5 -0
  43. package/build/components/post-visibility/label.js.map +1 -1
  44. package/build/components/provider/index.native.js +3 -1
  45. package/build/components/provider/index.native.js.map +1 -1
  46. package/build/components/provider/use-block-editor-settings.js +2 -2
  47. package/build/components/provider/use-block-editor-settings.js.map +1 -1
  48. package/build/components/table-of-contents/panel.js +5 -1
  49. package/build/components/table-of-contents/panel.js.map +1 -1
  50. package/build/components/time-to-read/index.js +60 -0
  51. package/build/components/time-to-read/index.js.map +1 -0
  52. package/build/store/selectors.js +3 -3
  53. package/build/store/selectors.js.map +1 -1
  54. package/build-module/components/document-outline/index.js +7 -8
  55. package/build-module/components/document-outline/index.js.map +1 -1
  56. package/build-module/components/editor-history/redo.js +3 -2
  57. package/build-module/components/editor-history/redo.js.map +1 -1
  58. package/build-module/components/error-boundary/index.js +2 -0
  59. package/build-module/components/error-boundary/index.js.map +1 -1
  60. package/build-module/components/global-keyboard-shortcuts/register-shortcuts.js +10 -1
  61. package/build-module/components/global-keyboard-shortcuts/register-shortcuts.js.map +1 -1
  62. package/build-module/components/index.js +6 -3
  63. package/build-module/components/index.js.map +1 -1
  64. package/build-module/components/page-attributes/order.js +3 -6
  65. package/build-module/components/page-attributes/order.js.map +1 -1
  66. package/build-module/components/page-attributes/parent.js +5 -4
  67. package/build-module/components/page-attributes/parent.js.map +1 -1
  68. package/build-module/components/post-format/index.js +5 -8
  69. package/build-module/components/post-format/index.js.map +1 -1
  70. package/build-module/components/post-locked-modal/index.js +1 -1
  71. package/build-module/components/post-locked-modal/index.js.map +1 -1
  72. package/build-module/components/post-publish-panel/maybe-category-panel.js +2 -4
  73. package/build-module/components/post-publish-panel/maybe-category-panel.js.map +1 -1
  74. package/build-module/components/post-schedule/label.js +3 -3
  75. package/build-module/components/post-schedule/label.js.map +1 -1
  76. package/build-module/components/post-slug/index.js +8 -14
  77. package/build-module/components/post-slug/index.js.map +1 -1
  78. package/build-module/components/post-taxonomies/flat-term-selector.js +1 -2
  79. package/build-module/components/post-taxonomies/flat-term-selector.js.map +1 -1
  80. package/build-module/components/post-taxonomies/hierarchical-term-selector.js +1 -2
  81. package/build-module/components/post-taxonomies/hierarchical-term-selector.js.map +1 -1
  82. package/build-module/components/post-taxonomies/index.js +4 -1
  83. package/build-module/components/post-taxonomies/index.js.map +1 -1
  84. package/build-module/components/post-template/index.js +1 -2
  85. package/build-module/components/post-template/index.js.map +1 -1
  86. package/build-module/components/post-title/index.js +8 -2
  87. package/build-module/components/post-title/index.js.map +1 -1
  88. package/build-module/components/post-url/check.js +44 -0
  89. package/build-module/components/post-url/check.js.map +1 -0
  90. package/build-module/components/post-url/index.js +102 -0
  91. package/build-module/components/post-url/index.js.map +1 -0
  92. package/build-module/components/post-url/label.js +18 -0
  93. package/build-module/components/post-url/label.js.map +1 -0
  94. package/build-module/components/post-visibility/label.js +3 -0
  95. package/build-module/components/post-visibility/label.js.map +1 -1
  96. package/build-module/components/provider/index.native.js +3 -1
  97. package/build-module/components/provider/index.native.js.map +1 -1
  98. package/build-module/components/provider/use-block-editor-settings.js +2 -2
  99. package/build-module/components/provider/use-block-editor-settings.js.map +1 -1
  100. package/build-module/components/table-of-contents/panel.js +4 -1
  101. package/build-module/components/table-of-contents/panel.js.map +1 -1
  102. package/build-module/components/time-to-read/index.js +50 -0
  103. package/build-module/components/time-to-read/index.js.map +1 -0
  104. package/build-module/store/selectors.js +4 -4
  105. package/build-module/store/selectors.js.map +1 -1
  106. package/build-style/style-rtl.css +18 -17
  107. package/build-style/style.css +22 -17
  108. package/package.json +30 -29
  109. package/src/components/README.md +1 -1
  110. package/src/components/document-outline/index.js +9 -8
  111. package/src/components/editor-history/redo.js +6 -2
  112. package/src/components/error-boundary/index.js +3 -0
  113. package/src/components/global-keyboard-shortcuts/register-shortcuts.js +13 -0
  114. package/src/components/index.js +9 -3
  115. package/src/components/page-attributes/order.js +1 -9
  116. package/src/components/page-attributes/parent.js +5 -11
  117. package/src/components/post-format/index.js +13 -19
  118. package/src/components/post-format/style.scss +2 -17
  119. package/src/components/post-locked-modal/index.js +1 -1
  120. package/src/components/post-publish-panel/maybe-category-panel.js +3 -5
  121. package/src/components/post-schedule/label.js +1 -1
  122. package/src/components/post-slug/index.js +7 -13
  123. package/src/components/post-slug/test/index.js +7 -6
  124. package/src/components/post-taxonomies/flat-term-selector.js +1 -1
  125. package/src/components/post-taxonomies/hierarchical-term-selector.js +1 -1
  126. package/src/components/post-taxonomies/index.js +3 -1
  127. package/src/components/post-template/index.js +1 -1
  128. package/src/components/post-title/index.js +8 -2
  129. package/src/components/post-trash/style.scss +3 -0
  130. package/src/components/post-url/check.js +38 -0
  131. package/src/components/post-url/index.js +122 -0
  132. package/src/components/post-url/label.js +22 -0
  133. package/src/components/post-url/style.scss +16 -0
  134. package/src/components/post-visibility/label.js +4 -0
  135. package/src/components/provider/index.native.js +3 -1
  136. package/src/components/provider/use-block-editor-settings.js +5 -1
  137. package/src/components/table-of-contents/panel.js +7 -2
  138. package/src/components/time-to-read/index.js +59 -0
  139. package/src/store/selectors.js +5 -6
  140. package/src/store/test/selectors.js +1 -1
  141. package/src/style.scss +1 -0
@@ -1,8 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import { countBy, flatMap, get } from 'lodash';
5
-
6
1
  /**
7
2
  * WordPress dependencies
8
3
  */
@@ -50,7 +45,7 @@ const multipleH1Headings = [
50
45
  * @return {Array} An array of heading blocks enhanced with the properties described above.
51
46
  */
52
47
  const computeOutlineHeadings = ( blocks = [] ) => {
53
- return flatMap( blocks, ( block = {} ) => {
48
+ return blocks.flatMap( ( block = {} ) => {
54
49
  if ( block.name === 'core/heading' ) {
55
50
  return {
56
51
  ...block,
@@ -83,7 +78,13 @@ export const DocumentOutline = ( {
83
78
  // Not great but it's the simplest way to locate the title right now.
84
79
  const titleNode = document.querySelector( '.editor-post-title__input' );
85
80
  const hasTitle = isTitleSupported && title && titleNode;
86
- const countByLevel = countBy( headings, 'level' );
81
+ const countByLevel = headings.reduce(
82
+ ( acc, heading ) => ( {
83
+ ...acc,
84
+ [ heading.level ]: ( acc[ heading.level ] || 0 ) + 1,
85
+ } ),
86
+ {}
87
+ );
87
88
  const hasMultipleH1 = countByLevel[ 1 ] > 1;
88
89
 
89
90
  return (
@@ -155,7 +156,7 @@ export default compose(
155
156
  return {
156
157
  title: getEditedPostAttribute( 'title' ),
157
158
  blocks: getBlocks(),
158
- isTitleSupported: get( postType, [ 'supports', 'title' ], false ),
159
+ isTitleSupported: postType?.supports?.title ?? false,
159
160
  };
160
161
  } )
161
162
  )( DocumentOutline );
@@ -4,7 +4,7 @@
4
4
  import { __, isRTL } from '@wordpress/i18n';
5
5
  import { Button } from '@wordpress/components';
6
6
  import { useSelect, useDispatch } from '@wordpress/data';
7
- import { displayShortcut } from '@wordpress/keycodes';
7
+ import { displayShortcut, isAppleOS } from '@wordpress/keycodes';
8
8
  import { redo as redoIcon, undo as undoIcon } from '@wordpress/icons';
9
9
  import { forwardRef } from '@wordpress/element';
10
10
 
@@ -14,6 +14,10 @@ import { forwardRef } from '@wordpress/element';
14
14
  import { store as editorStore } from '../../store';
15
15
 
16
16
  function EditorHistoryRedo( props, ref ) {
17
+ const shortcut = isAppleOS()
18
+ ? displayShortcut.primaryShift( 'z' )
19
+ : displayShortcut.primary( 'y' );
20
+
17
21
  const hasRedo = useSelect(
18
22
  ( select ) => select( editorStore ).hasEditorRedo(),
19
23
  []
@@ -26,7 +30,7 @@ function EditorHistoryRedo( props, ref ) {
26
30
  icon={ ! isRTL() ? redoIcon : undoIcon }
27
31
  /* translators: button label text should, if possible, be under 16 characters. */
28
32
  label={ __( 'Redo' ) }
29
- shortcut={ displayShortcut.primaryShift( 'z' ) }
33
+ shortcut={ shortcut }
30
34
  // If there are no redo levels we don't want to actually disable this
31
35
  // button, because it will remove focus for keyboard users.
32
36
  // See: https://github.com/WordPress/gutenberg/issues/3486
@@ -7,6 +7,7 @@ import { Button } from '@wordpress/components';
7
7
  import { select } from '@wordpress/data';
8
8
  import { Warning } from '@wordpress/block-editor';
9
9
  import { useCopyToClipboard } from '@wordpress/compose';
10
+ import { doAction } from '@wordpress/hooks';
10
11
 
11
12
  /**
12
13
  * Internal dependencies
@@ -36,6 +37,8 @@ class ErrorBoundary extends Component {
36
37
 
37
38
  componentDidCatch( error ) {
38
39
  this.setState( { error } );
40
+
41
+ doAction( 'editor.ErrorBoundary.errorLogged', error );
39
42
  }
40
43
 
41
44
  reboot() {
@@ -6,6 +6,7 @@ import { useDispatch } from '@wordpress/data';
6
6
  import { __ } from '@wordpress/i18n';
7
7
  import { BlockEditorKeyboardShortcuts } from '@wordpress/block-editor';
8
8
  import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
9
+ import { isAppleOS } from '@wordpress/keycodes';
9
10
 
10
11
  function EditorKeyboardShortcutsRegister() {
11
12
  // Registering the shortcuts.
@@ -39,6 +40,18 @@ function EditorKeyboardShortcutsRegister() {
39
40
  modifier: 'primaryShift',
40
41
  character: 'z',
41
42
  },
43
+ // Disable on Apple OS because it conflicts with the browser's
44
+ // history shortcut. It's a fine alias for both Windows and Linux.
45
+ // Since there's no conflict for Ctrl+Shift+Z on both Windows and
46
+ // Linux, we keep it as the default for consistency.
47
+ aliases: isAppleOS()
48
+ ? []
49
+ : [
50
+ {
51
+ modifier: 'primary',
52
+ character: 'y',
53
+ },
54
+ ],
42
55
  } );
43
56
  }, [ registerShortcut ] );
44
57
 
@@ -51,16 +51,22 @@ export { default as PostSticky } from './post-sticky';
51
51
  export { default as PostStickyCheck } from './post-sticky/check';
52
52
  export { default as PostSwitchToDraftButton } from './post-switch-to-draft-button';
53
53
  export { default as PostTaxonomies } from './post-taxonomies';
54
- export { default as PostTaxonomiesFlatTermSelector } from './post-taxonomies/flat-term-selector';
55
- export { default as PostTaxonomiesHierarchicalTermSelector } from './post-taxonomies/hierarchical-term-selector';
54
+ export { FlatTermSelector as PostTaxonomiesFlatTermSelector } from './post-taxonomies/flat-term-selector';
55
+ export { HierarchicalTermSelector as PostTaxonomiesHierarchicalTermSelector } from './post-taxonomies/hierarchical-term-selector';
56
56
  export { default as PostTaxonomiesCheck } from './post-taxonomies/check';
57
57
  export { default as PostTextEditor } from './post-text-editor';
58
58
  export { default as PostTitle } from './post-title';
59
59
  export { default as PostTrash } from './post-trash';
60
60
  export { default as PostTrashCheck } from './post-trash/check';
61
61
  export { default as PostTypeSupportCheck } from './post-type-support-check';
62
+ export { default as PostURL } from './post-url';
63
+ export { default as PostURLCheck } from './post-url/check';
64
+ export { default as PostURLLabel, usePostURLLabel } from './post-url/label';
62
65
  export { default as PostVisibility } from './post-visibility';
63
- export { default as PostVisibilityLabel } from './post-visibility/label';
66
+ export {
67
+ default as PostVisibilityLabel,
68
+ usePostVisibilityLabel,
69
+ } from './post-visibility/label';
64
70
  export { default as PostVisibilityCheck } from './post-visibility/check';
65
71
  export { default as TableOfContents } from './table-of-contents';
66
72
  export { default as ThemeSupportCheck } from './theme-support-check';
@@ -1,8 +1,3 @@
1
- /**
2
- * External dependencies
3
- */
4
- import { invoke } from 'lodash';
5
-
6
1
  /**
7
2
  * WordPress dependencies
8
3
  */
@@ -24,10 +19,7 @@ export const PageAttributesOrder = ( { onUpdateOrder, order = 0 } ) => {
24
19
  const setUpdatedOrder = ( value ) => {
25
20
  setOrderInput( value );
26
21
  const newOrder = Number( value );
27
- if (
28
- Number.isInteger( newOrder ) &&
29
- invoke( value, [ 'trim' ] ) !== ''
30
- ) {
22
+ if ( Number.isInteger( newOrder ) && value.trim?.() !== '' ) {
31
23
  onUpdateOrder( Number( value ) );
32
24
  }
33
25
  };
@@ -1,14 +1,8 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import {
5
- get,
6
- unescape as unescapeString,
7
- debounce,
8
- repeat,
9
- find,
10
- deburr,
11
- } from 'lodash';
4
+ import { get, unescape as unescapeString, debounce, find } from 'lodash';
5
+ import removeAccents from 'remove-accents';
12
6
 
13
7
  /**
14
8
  * WordPress dependencies
@@ -33,8 +27,8 @@ function getTitle( post ) {
33
27
  }
34
28
 
35
29
  export const getItemPriority = ( name, searchValue ) => {
36
- const normalizedName = deburr( name ).toLowerCase();
37
- const normalizedSearch = deburr( searchValue ).toLowerCase();
30
+ const normalizedName = removeAccents( name || '' ).toLowerCase();
31
+ const normalizedSearch = removeAccents( searchValue || '' ).toLowerCase();
38
32
  if ( normalizedName === normalizedSearch ) {
39
33
  return 0;
40
34
  }
@@ -98,7 +92,7 @@ export function PageAttributesParent() {
98
92
  {
99
93
  value: treeNode.id,
100
94
  label:
101
- repeat( '— ', level ) + unescapeString( treeNode.name ),
95
+ '— '.repeat( level ) + unescapeString( treeNode.name ),
102
96
  rawName: treeNode.name,
103
97
  },
104
98
  ...getOptionsFromTree( treeNode.children || [], level + 1 ),
@@ -81,24 +81,18 @@ export default function PostFormat() {
81
81
  return (
82
82
  <PostFormatCheck>
83
83
  <div className="editor-post-format">
84
- <div className="editor-post-format__content">
85
- <label htmlFor={ postFormatSelectorId }>
86
- { __( 'Post Format' ) }
87
- </label>
88
- <SelectControl
89
- value={ postFormat }
90
- onChange={ ( format ) => onUpdatePostFormat( format ) }
91
- id={ postFormatSelectorId }
92
- options={ formats.map( ( format ) => ( {
93
- label: format.caption,
94
- value: format.id,
95
- } ) ) }
96
- />
97
- </div>
98
-
84
+ <SelectControl
85
+ label={ __( 'Post Format' ) }
86
+ value={ postFormat }
87
+ onChange={ ( format ) => onUpdatePostFormat( format ) }
88
+ id={ postFormatSelectorId }
89
+ options={ formats.map( ( format ) => ( {
90
+ label: format.caption,
91
+ value: format.id,
92
+ } ) ) }
93
+ />
99
94
  { suggestion && suggestion.id !== postFormat && (
100
- <div className="editor-post-format__suggestion">
101
- { __( 'Suggestion:' ) }{ ' ' }
95
+ <p className="editor-post-format__suggestion">
102
96
  <Button
103
97
  variant="link"
104
98
  onClick={ () =>
@@ -107,11 +101,11 @@ export default function PostFormat() {
107
101
  >
108
102
  { sprintf(
109
103
  /* translators: %s: post format */
110
- __( 'Apply format: %s' ),
104
+ __( 'Apply suggested format: %s' ),
111
105
  suggestion.caption
112
106
  ) }
113
107
  </Button>
114
- </div>
108
+ </p>
115
109
  ) }
116
110
  </div>
117
111
  </PostFormatCheck>
@@ -1,18 +1,3 @@
1
- .editor-post-format {
2
- flex-direction: column;
3
- align-items: stretch;
4
- width: 100%;
5
- }
6
-
7
- .editor-post-format__content {
8
- display: inline-flex;
9
- justify-content: space-between;
10
- align-items: center;
11
- width: 100%;
12
- }
13
-
14
- .editor-post-format__suggestion {
15
- padding: $grid-unit-15 * 0.5;
16
- text-align: right;
17
- font-size: $default-font-size;
1
+ [class].editor-post-format__suggestion {
2
+ margin: $grid-unit-05 0 0 0;
18
3
  }
@@ -197,7 +197,7 @@ export default function PostLockedModal() {
197
197
  ? sprintf(
198
198
  /* translators: %s: user's display name */
199
199
  __(
200
- '<strong>%s</strong> now has editing control of this posts (<PreviewLink />). Don’t worry, your changes up to this moment have been saved.'
200
+ '<strong>%s</strong> now has editing control of this post (<PreviewLink />). Don’t worry, your changes up to this moment have been saved.'
201
201
  ),
202
202
  userDisplayName
203
203
  )
@@ -27,13 +27,11 @@ function MaybeCategoryPanel() {
27
27
  'root',
28
28
  'site'
29
29
  )?.default_category;
30
- const defaultCategory = select( coreStore ).getEntityRecords(
30
+ const defaultCategory = select( coreStore ).getEntityRecord(
31
31
  'taxonomy',
32
32
  'category',
33
- {
34
- id: defaultCategoryId,
35
- }
36
- )?.[ 0 ];
33
+ defaultCategoryId
34
+ );
37
35
  const postTypeSupportsCategories =
38
36
  categoriesTaxonomy &&
39
37
  some( categoriesTaxonomy.types, ( type ) => type === postType );
@@ -14,7 +14,7 @@ export default function PostScheduleLabel( props ) {
14
14
  return usePostScheduleLabel( props );
15
15
  }
16
16
 
17
- export function usePostScheduleLabel( { full } ) {
17
+ export function usePostScheduleLabel( { full = false } = {} ) {
18
18
  const { date, isFloating } = useSelect(
19
19
  ( select ) => ( {
20
20
  date: select( editorStore ).getEditedPostAttribute( 'date' ),
@@ -4,8 +4,9 @@
4
4
  import { withDispatch, withSelect } from '@wordpress/data';
5
5
  import { Component } from '@wordpress/element';
6
6
  import { __ } from '@wordpress/i18n';
7
- import { withInstanceId, compose } from '@wordpress/compose';
7
+ import { compose } from '@wordpress/compose';
8
8
  import { safeDecodeURIComponent, cleanForSlug } from '@wordpress/url';
9
+ import { TextControl } from '@wordpress/components';
9
10
 
10
11
  /**
11
12
  * Internal dependencies
@@ -41,25 +42,19 @@ export class PostSlug extends Component {
41
42
  }
42
43
 
43
44
  render() {
44
- const { instanceId } = this.props;
45
45
  const { editedSlug } = this.state;
46
-
47
- const inputId = 'editor-post-slug-' + instanceId;
48
-
49
46
  return (
50
47
  <PostSlugCheck>
51
- <label htmlFor={ inputId }>{ __( 'Slug' ) }</label>
52
- <input
48
+ <TextControl
49
+ label={ __( 'Slug' ) }
53
50
  autoComplete="off"
54
51
  spellCheck="false"
55
- type="text"
56
- id={ inputId }
57
52
  value={ editedSlug }
58
- onChange={ ( event ) =>
59
- this.setState( { editedSlug: event.target.value } )
53
+ onChange={ ( slug ) =>
54
+ this.setState( { editedSlug: slug } )
60
55
  }
61
56
  onBlur={ this.setSlug }
62
- className="editor-post-slug__input"
57
+ className="editor-post-slug"
63
58
  />
64
59
  </PostSlugCheck>
65
60
  );
@@ -86,5 +81,4 @@ export default compose( [
86
81
  },
87
82
  };
88
83
  } ),
89
- withInstanceId,
90
84
  ] )( PostSlug );
@@ -3,6 +3,11 @@
3
3
  */
4
4
  import { shallow } from 'enzyme';
5
5
 
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { TextControl } from '@wordpress/components';
10
+
6
11
  /**
7
12
  * Internal dependencies
8
13
  */
@@ -13,11 +18,7 @@ describe( 'PostSlug', () => {
13
18
  it( 'should update internal slug', () => {
14
19
  const wrapper = shallow( <PostSlug postSlug="index" /> );
15
20
 
16
- wrapper.find( 'input' ).simulate( 'change', {
17
- target: {
18
- value: 'single',
19
- },
20
- } );
21
+ wrapper.find( TextControl ).prop( 'onChange' )( 'single' );
21
22
 
22
23
  expect( wrapper.state().editedSlug ).toEqual( 'single' );
23
24
  } );
@@ -28,7 +29,7 @@ describe( 'PostSlug', () => {
28
29
  <PostSlug postSlug="index" onUpdateSlug={ onUpdateSlug } />
29
30
  );
30
31
 
31
- wrapper.find( 'input' ).simulate( 'blur', {
32
+ wrapper.find( TextControl ).prop( 'onBlur' )( {
32
33
  target: {
33
34
  value: 'single',
34
35
  },
@@ -86,7 +86,7 @@ function findOrCreateTerm( termName, restBase, namespace ) {
86
86
  .then( unescapeTerm );
87
87
  }
88
88
 
89
- function FlatTermSelector( { slug } ) {
89
+ export function FlatTermSelector( { slug } ) {
90
90
  const [ values, setValues ] = useState( [] );
91
91
  const [ search, setSearch ] = useState( '' );
92
92
  const debouncedSearch = useDebounce( setSearch, 500 );
@@ -152,7 +152,7 @@ export function getFilterMatcher( filterValue ) {
152
152
  * @param {string} props.slug Taxonomy slug.
153
153
  * @return {WPElement} Hierarchical term selector component.
154
154
  */
155
- function HierarchicalTermSelector( { slug } ) {
155
+ export function HierarchicalTermSelector( { slug } ) {
156
156
  const [ adding, setAdding ] = useState( false );
157
157
  const [ formName, setFormName ] = useState( '' );
158
158
  /**
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import { filter, identity, includes } from 'lodash';
4
+ import { filter, includes } from 'lodash';
5
5
 
6
6
  /**
7
7
  * WordPress dependencies
@@ -18,6 +18,8 @@ import HierarchicalTermSelector from './hierarchical-term-selector';
18
18
  import FlatTermSelector from './flat-term-selector';
19
19
  import { store as editorStore } from '../../store';
20
20
 
21
+ const identity = ( x ) => x;
22
+
21
23
  export function PostTaxonomies( {
22
24
  postType,
23
25
  taxonomies,
@@ -16,7 +16,7 @@ import { store as coreStore } from '@wordpress/core-data';
16
16
  */
17
17
  import { store as editorStore } from '../../store';
18
18
 
19
- export function PostTemplate( {} ) {
19
+ export function PostTemplate() {
20
20
  const { availableTemplates, selectedTemplate, isViewable } = useSelect(
21
21
  ( select ) => {
22
22
  const {
@@ -149,9 +149,13 @@ function PostTitle( _, forwardedRef ) {
149
149
  plainText,
150
150
  } );
151
151
 
152
- if ( typeof content !== 'string' && content.length ) {
153
- event.preventDefault();
152
+ event.preventDefault();
153
+
154
+ if ( ! content.length ) {
155
+ return;
156
+ }
154
157
 
158
+ if ( typeof content !== 'string' ) {
155
159
  const [ firstBlock ] = content;
156
160
 
157
161
  if (
@@ -164,6 +168,8 @@ function PostTitle( _, forwardedRef ) {
164
168
  } else {
165
169
  onInsertBlockAfter( content );
166
170
  }
171
+ } else {
172
+ onUpdate( content );
167
173
  }
168
174
  }
169
175
 
@@ -1,3 +1,6 @@
1
1
  .editor-post-trash.components-button {
2
+ display: flex;
3
+ justify-content: center;
2
4
  margin-top: $grid-unit-05;
5
+ width: 100%;
3
6
  }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useSelect } from '@wordpress/data';
5
+ import { store as coreStore } from '@wordpress/core-data';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { store as editorStore } from '../../store';
11
+
12
+ export default function PostURLCheck( { children } ) {
13
+ const isVisible = useSelect( ( select ) => {
14
+ const postTypeSlug = select( editorStore ).getCurrentPostType();
15
+ const postType = select( coreStore ).getPostType( postTypeSlug );
16
+ if ( ! postType?.viewable ) {
17
+ return false;
18
+ }
19
+
20
+ const post = select( editorStore ).getCurrentPost();
21
+ if ( ! post.link ) {
22
+ return false;
23
+ }
24
+
25
+ const permalinkParts = select( editorStore ).getPermalinkParts();
26
+ if ( ! permalinkParts ) {
27
+ return false;
28
+ }
29
+
30
+ return true;
31
+ }, [] );
32
+
33
+ if ( ! isVisible ) {
34
+ return null;
35
+ }
36
+
37
+ return children;
38
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useSelect, useDispatch } from '@wordpress/data';
5
+ import { safeDecodeURIComponent, cleanForSlug } from '@wordpress/url';
6
+ import { useState } from '@wordpress/element';
7
+ import { __experimentalInspectorPopoverHeader as InspectorPopoverHeader } from '@wordpress/block-editor';
8
+ import { __ } from '@wordpress/i18n';
9
+ import { TextControl, ExternalLink } from '@wordpress/components';
10
+ import { store as coreStore } from '@wordpress/core-data';
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import { store as editorStore } from '../../store';
16
+
17
+ export default function PostURL( { onClose } ) {
18
+ const {
19
+ isEditable,
20
+ postSlug,
21
+ viewPostLabel,
22
+ postLink,
23
+ permalinkPrefix,
24
+ permalinkSuffix,
25
+ } = useSelect( ( select ) => {
26
+ const postTypeSlug = select( editorStore ).getCurrentPostType();
27
+ const postType = select( coreStore ).getPostType( postTypeSlug );
28
+ const permalinkParts = select( editorStore ).getPermalinkParts();
29
+ return {
30
+ isEditable: select( editorStore ).isPermalinkEditable(),
31
+ postSlug: safeDecodeURIComponent(
32
+ select( editorStore ).getEditedPostSlug()
33
+ ),
34
+ viewPostLabel: postType?.labels.view_item,
35
+ postLink: select( editorStore ).getCurrentPost().link,
36
+ permalinkPrefix: permalinkParts?.prefix,
37
+ permalinkSuffix: permalinkParts?.suffix,
38
+ };
39
+ }, [] );
40
+
41
+ const { editPost } = useDispatch( editorStore );
42
+
43
+ const [ forceEmptyField, setForceEmptyField ] = useState( false );
44
+
45
+ return (
46
+ <div className="editor-post-url">
47
+ <InspectorPopoverHeader title={ __( 'URL' ) } onClose={ onClose } />
48
+ { isEditable && (
49
+ <TextControl
50
+ label={ __( 'Permalink' ) }
51
+ value={ forceEmptyField ? '' : postSlug }
52
+ autoComplete="off"
53
+ spellCheck="false"
54
+ help={
55
+ <>
56
+ { __( 'The last part of the URL.' ) }{ ' ' }
57
+ <ExternalLink
58
+ href={ __(
59
+ 'https://wordpress.org/support/article/settings-sidebar/#permalink'
60
+ ) }
61
+ >
62
+ { __( 'Learn more.' ) }
63
+ </ExternalLink>
64
+ </>
65
+ }
66
+ onChange={ ( newValue ) => {
67
+ editPost( { slug: newValue } );
68
+ // When we delete the field the permalink gets
69
+ // reverted to the original value.
70
+ // The forceEmptyField logic allows the user to have
71
+ // the field temporarily empty while typing.
72
+ if ( ! newValue ) {
73
+ if ( ! forceEmptyField ) {
74
+ setForceEmptyField( true );
75
+ }
76
+ return;
77
+ }
78
+ if ( forceEmptyField ) {
79
+ setForceEmptyField( false );
80
+ }
81
+ } }
82
+ onBlur={ ( event ) => {
83
+ editPost( {
84
+ slug: cleanForSlug( event.target.value ),
85
+ } );
86
+ if ( forceEmptyField ) {
87
+ setForceEmptyField( false );
88
+ }
89
+ } }
90
+ />
91
+ ) }
92
+ { isEditable && (
93
+ <h3 className="editor-post-url__link-label">
94
+ { viewPostLabel ?? __( 'View post' ) }
95
+ </h3>
96
+ ) }
97
+ <p>
98
+ <ExternalLink
99
+ className="editor-post-url__link"
100
+ href={ postLink }
101
+ target="_blank"
102
+ >
103
+ { isEditable ? (
104
+ <>
105
+ <span className="editor-post-url__link-prefix">
106
+ { permalinkPrefix }
107
+ </span>
108
+ <span className="editor-post-url__link-slug">
109
+ { postSlug }
110
+ </span>
111
+ <span className="editor-post-url__link-suffix">
112
+ { permalinkSuffix }
113
+ </span>
114
+ </>
115
+ ) : (
116
+ postLink
117
+ ) }
118
+ </ExternalLink>
119
+ </p>
120
+ </div>
121
+ );
122
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useSelect } from '@wordpress/data';
5
+ import { filterURLForDisplay, safeDecodeURIComponent } from '@wordpress/url';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { store as editorStore } from '../../store';
11
+
12
+ export default function PostURLLabel() {
13
+ return usePostURLLabel();
14
+ }
15
+
16
+ export function usePostURLLabel() {
17
+ const postLink = useSelect(
18
+ ( select ) => select( editorStore ).getCurrentPost().link,
19
+ []
20
+ );
21
+ return filterURLForDisplay( safeDecodeURIComponent( postLink ) );
22
+ }