@wordpress/editor 12.0.0 → 12.0.4

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 (53) hide show
  1. package/build/components/autosave-monitor/index.js +5 -0
  2. package/build/components/autosave-monitor/index.js.map +1 -1
  3. package/build/components/editor-help/index.native.js +4 -3
  4. package/build/components/editor-help/index.native.js.map +1 -1
  5. package/build/components/entities-saved-states/entity-type-list.js +22 -13
  6. package/build/components/entities-saved-states/entity-type-list.js.map +1 -1
  7. package/build/components/entities-saved-states/index.js +23 -4
  8. package/build/components/entities-saved-states/index.js.map +1 -1
  9. package/build/components/post-taxonomies/flat-term-selector.js +154 -211
  10. package/build/components/post-taxonomies/flat-term-selector.js.map +1 -1
  11. package/build/components/post-title/index.js +4 -2
  12. package/build/components/post-title/index.js.map +1 -1
  13. package/build/components/provider/use-block-editor-settings.js +1 -1
  14. package/build/components/provider/use-block-editor-settings.js.map +1 -1
  15. package/build/store/utils/notice-builder.js +15 -18
  16. package/build/store/utils/notice-builder.js.map +1 -1
  17. package/build/utils/get-template-part-icon.js +1 -1
  18. package/build/utils/get-template-part-icon.js.map +1 -1
  19. package/build-module/components/autosave-monitor/index.js +5 -0
  20. package/build-module/components/autosave-monitor/index.js.map +1 -1
  21. package/build-module/components/editor-help/index.native.js +5 -4
  22. package/build-module/components/editor-help/index.native.js.map +1 -1
  23. package/build-module/components/entities-saved-states/entity-type-list.js +24 -13
  24. package/build-module/components/entities-saved-states/entity-type-list.js.map +1 -1
  25. package/build-module/components/entities-saved-states/index.js +23 -4
  26. package/build-module/components/entities-saved-states/index.js.map +1 -1
  27. package/build-module/components/post-taxonomies/flat-term-selector.js +156 -214
  28. package/build-module/components/post-taxonomies/flat-term-selector.js.map +1 -1
  29. package/build-module/components/post-title/index.js +4 -2
  30. package/build-module/components/post-title/index.js.map +1 -1
  31. package/build-module/components/provider/use-block-editor-settings.js +1 -1
  32. package/build-module/components/provider/use-block-editor-settings.js.map +1 -1
  33. package/build-module/store/utils/notice-builder.js +15 -18
  34. package/build-module/store/utils/notice-builder.js.map +1 -1
  35. package/build-module/utils/get-template-part-icon.js +2 -2
  36. package/build-module/utils/get-template-part-icon.js.map +1 -1
  37. package/build-style/style-rtl.css +1 -1
  38. package/build-style/style.css +1 -1
  39. package/package.json +19 -19
  40. package/src/components/autosave-monitor/index.js +5 -0
  41. package/src/components/autosave-monitor/test/index.js +10 -4
  42. package/src/components/editor-help/index.native.js +116 -106
  43. package/src/components/editor-help/style.scss +4 -0
  44. package/src/components/entities-saved-states/entity-type-list.js +29 -10
  45. package/src/components/entities-saved-states/index.js +38 -8
  46. package/src/components/post-saved-state/style.scss +2 -0
  47. package/src/components/post-taxonomies/flat-term-selector.js +220 -254
  48. package/src/components/post-title/index.js +4 -2
  49. package/src/components/provider/use-block-editor-settings.js +0 -2
  50. package/src/store/test/actions.js +4 -2
  51. package/src/store/utils/notice-builder.js +17 -19
  52. package/src/store/utils/test/notice-builder.js +1 -1
  53. package/src/utils/get-template-part-icon.js +2 -2
@@ -19,7 +19,7 @@ import { close as closeIcon } from '@wordpress/icons';
19
19
  */
20
20
  import EntityTypeList from './entity-type-list';
21
21
 
22
- const TRANSLATED_SITE_PROTPERTIES = {
22
+ const TRANSLATED_SITE_PROPERTIES = {
23
23
  title: __( 'Title' ),
24
24
  description: __( 'Tagline' ),
25
25
  site_logo: __( 'Logo' ),
@@ -27,6 +27,13 @@ const TRANSLATED_SITE_PROTPERTIES = {
27
27
  page_on_front: __( 'Page on front' ),
28
28
  };
29
29
 
30
+ const PUBLISH_ON_SAVE_ENTITIES = [
31
+ {
32
+ kind: 'postType',
33
+ name: 'wp_navigation',
34
+ },
35
+ ];
36
+
30
37
  export default function EntitiesSavedStates( { close } ) {
31
38
  const saveButtonRef = useRef();
32
39
  const { dirtyEntityRecords } = useSelect( ( select ) => {
@@ -49,7 +56,7 @@ export default function EntitiesSavedStates( { close } ) {
49
56
  siteEditsAsEntities.push( {
50
57
  kind: 'root',
51
58
  name: 'site',
52
- title: TRANSLATED_SITE_PROTPERTIES[ property ] || property,
59
+ title: TRANSLATED_SITE_PROPERTIES[ property ] || property,
53
60
  property,
54
61
  } );
55
62
  }
@@ -63,14 +70,27 @@ export default function EntitiesSavedStates( { close } ) {
63
70
  };
64
71
  }, [] );
65
72
  const {
73
+ editEntityRecord,
66
74
  saveEditedEntityRecord,
67
75
  __experimentalSaveSpecifiedEntityEdits: saveSpecifiedEntityEdits,
68
76
  } = useDispatch( coreStore );
69
77
 
70
78
  // To group entities by type.
71
- const partitionedSavables = Object.values(
72
- groupBy( dirtyEntityRecords, 'name' )
73
- );
79
+ const partitionedSavables = groupBy( dirtyEntityRecords, 'name' );
80
+
81
+ // Sort entity groups.
82
+ const {
83
+ site: siteSavables,
84
+ wp_template: templateSavables,
85
+ wp_template_part: templatePartSavables,
86
+ ...contentSavables
87
+ } = partitionedSavables;
88
+ const sortedPartitionedSavables = [
89
+ siteSavables,
90
+ templateSavables,
91
+ templatePartSavables,
92
+ ...Object.values( contentSavables ),
93
+ ].filter( Array.isArray );
74
94
 
75
95
  // Unchecked entities to be ignored by save function.
76
96
  const [ unselectedEntities, _setUnselectedEntities ] = useState( [] );
@@ -118,6 +138,16 @@ export default function EntitiesSavedStates( { close } ) {
118
138
  if ( 'root' === kind && 'site' === name ) {
119
139
  siteItemsToSave.push( property );
120
140
  } else {
141
+ if (
142
+ PUBLISH_ON_SAVE_ENTITIES.some(
143
+ ( typeToPublish ) =>
144
+ typeToPublish.kind === kind &&
145
+ typeToPublish.name === name
146
+ )
147
+ ) {
148
+ editEntityRecord( kind, name, key, { status: 'publish' } );
149
+ }
150
+
121
151
  saveEditedEntityRecord( kind, name, key );
122
152
  }
123
153
  } );
@@ -160,15 +190,15 @@ export default function EntitiesSavedStates( { close } ) {
160
190
  </div>
161
191
 
162
192
  <div className="entities-saved-states__text-prompt">
163
- <strong>{ __( 'Select the changes you want to save' ) }</strong>
193
+ <strong>{ __( 'Are you ready to save?' ) }</strong>
164
194
  <p>
165
195
  { __(
166
- 'Some changes may affect other areas of your site.'
196
+ 'The following changes have been made to your site, templates, and content.'
167
197
  ) }
168
198
  </p>
169
199
  </div>
170
200
 
171
- { partitionedSavables.map( ( list ) => {
201
+ { sortedPartitionedSavables.map( ( list ) => {
172
202
  return (
173
203
  <EntityTypeList
174
204
  key={ list[ 0 ].name }
@@ -8,6 +8,8 @@
8
8
  white-space: nowrap;
9
9
 
10
10
  // The disabled version of the save state should be legible as an indicator.
11
+ &.is-saving[aria-disabled="true"],
12
+ &.is-saving[aria-disabled="true"]:hover,
11
13
  &.is-saved[aria-disabled="true"],
12
14
  &.is-saved[aria-disabled="true"]:hover {
13
15
  background: transparent;
@@ -1,31 +1,20 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import {
5
- debounce,
6
- escape as escapeString,
7
- find,
8
- get,
9
- invoke,
10
- isEmpty,
11
- uniqBy,
12
- } from 'lodash';
4
+ import { escape as escapeString, find, get, uniqBy } from 'lodash';
13
5
 
14
6
  /**
15
7
  * WordPress dependencies
16
8
  */
17
9
  import { __, _x, sprintf } from '@wordpress/i18n';
18
- import { Component } from '@wordpress/element';
19
- import {
20
- FormTokenField,
21
- withFilters,
22
- withSpokenMessages,
23
- } from '@wordpress/components';
24
- import { withSelect, withDispatch } from '@wordpress/data';
10
+ import { useEffect, useMemo, useState } from '@wordpress/element';
11
+ import { FormTokenField, withFilters } from '@wordpress/components';
12
+ import { useSelect, useDispatch } from '@wordpress/data';
25
13
  import { store as coreStore } from '@wordpress/core-data';
26
- import { compose } from '@wordpress/compose';
14
+ import { useDebounce } from '@wordpress/compose';
27
15
  import apiFetch from '@wordpress/api-fetch';
28
16
  import { addQueryArgs } from '@wordpress/url';
17
+ import { speak } from '@wordpress/a11y';
29
18
 
30
19
  /**
31
20
  * Internal dependencies
@@ -34,6 +23,14 @@ import { store as editorStore } from '../../store';
34
23
  import { unescapeString, unescapeTerm, unescapeTerms } from '../../utils/terms';
35
24
  import MostUsedTerms from './most-used-terms';
36
25
 
26
+ /**
27
+ * Shared reference to an empty array for cases where it is important to avoid
28
+ * returning a new array reference on every invocation.
29
+ *
30
+ * @type {Array<any>}
31
+ */
32
+ const EMPTY_ARRAY = [];
33
+
37
34
  /**
38
35
  * Module constants
39
36
  */
@@ -42,7 +39,8 @@ const DEFAULT_QUERY = {
42
39
  per_page: MAX_TERMS_SUGGESTIONS,
43
40
  orderby: 'count',
44
41
  order: 'desc',
45
- _fields: 'id,name,count',
42
+ _fields: 'id,name',
43
+ context: 'view',
46
44
  };
47
45
 
48
46
  const isSameTermName = ( termA, termB ) =>
@@ -56,171 +54,195 @@ const termNamesToIds = ( names, terms ) => {
56
54
  );
57
55
  };
58
56
 
59
- class FlatTermSelector extends Component {
60
- constructor() {
61
- super( ...arguments );
62
- this.onChange = this.onChange.bind( this );
63
- this.searchTerms = debounce( this.searchTerms.bind( this ), 500 );
64
- this.findOrCreateTerm = this.findOrCreateTerm.bind( this );
65
- this.appendTerm = this.appendTerm.bind( this );
66
- this.state = {
67
- loading: ! isEmpty( this.props.terms ),
68
- availableTerms: [],
69
- selectedTerms: [],
70
- };
71
- }
57
+ // Tries to create a term or fetch it if it already exists.
58
+ function findOrCreateTerm( termName, restBase ) {
59
+ const escapedTermName = escapeString( termName );
72
60
 
73
- componentDidMount() {
74
- if ( ! isEmpty( this.props.terms ) ) {
75
- this.initRequest = this.fetchTerms( {
76
- include: this.props.terms.join( ',' ),
77
- per_page: -1,
78
- } );
79
- this.initRequest.then(
80
- () => {
81
- this.setState( { loading: false } );
82
- },
83
- ( xhr ) => {
84
- if ( xhr.statusText === 'abort' ) {
85
- return;
86
- }
87
- this.setState( {
88
- loading: false,
89
- } );
90
- }
61
+ return apiFetch( {
62
+ path: `/wp/v2/${ restBase }`,
63
+ method: 'POST',
64
+ data: { name: escapedTermName },
65
+ } )
66
+ .catch( ( error ) => {
67
+ const errorCode = error.code;
68
+ if ( errorCode === 'term_exists' ) {
69
+ // If the terms exist, fetch it instead of creating a new one.
70
+ const addRequest = apiFetch( {
71
+ path: addQueryArgs( `/wp/v2/${ restBase }`, {
72
+ ...DEFAULT_QUERY,
73
+ search: escapedTermName,
74
+ } ),
75
+ } ).then( unescapeTerms );
76
+
77
+ return addRequest.then( ( searchResult ) => {
78
+ return find( searchResult, ( result ) =>
79
+ isSameTermName( result.name, termName )
80
+ );
81
+ } );
82
+ }
83
+
84
+ return Promise.reject( error );
85
+ } )
86
+ .then( unescapeTerm );
87
+ }
88
+
89
+ function FlatTermSelector( { slug } ) {
90
+ const [ values, setValues ] = useState( [] );
91
+ const [ search, setSearch ] = useState( '' );
92
+ const debouncedSearch = useDebounce( setSearch, 500 );
93
+
94
+ const {
95
+ terms,
96
+ termIds,
97
+ taxonomy,
98
+ hasAssignAction,
99
+ hasCreateAction,
100
+ hasResolvedTerms,
101
+ } = useSelect(
102
+ ( select ) => {
103
+ const { getCurrentPost, getEditedPostAttribute } = select(
104
+ editorStore
91
105
  );
92
- }
93
- }
106
+ const {
107
+ getEntityRecords,
108
+ getTaxonomy,
109
+ hasFinishedResolution,
110
+ } = select( coreStore );
111
+ const post = getCurrentPost();
112
+ const _taxonomy = getTaxonomy( slug );
113
+ const _termIds = _taxonomy
114
+ ? getEditedPostAttribute( _taxonomy.rest_base )
115
+ : EMPTY_ARRAY;
94
116
 
95
- componentWillUnmount() {
96
- invoke( this.initRequest, [ 'abort' ] );
97
- invoke( this.searchRequest, [ 'abort' ] );
98
- }
117
+ const query = {
118
+ ...DEFAULT_QUERY,
119
+ include: _termIds.join( ',' ),
120
+ per_page: -1,
121
+ };
99
122
 
100
- componentDidUpdate( prevProps ) {
101
- if ( prevProps.terms !== this.props.terms ) {
102
- this.updateSelectedTerms( this.props.terms );
103
- }
104
- }
123
+ return {
124
+ hasCreateAction: _taxonomy
125
+ ? get(
126
+ post,
127
+ [
128
+ '_links',
129
+ 'wp:action-create-' + _taxonomy.rest_base,
130
+ ],
131
+ false
132
+ )
133
+ : false,
134
+ hasAssignAction: _taxonomy
135
+ ? get(
136
+ post,
137
+ [
138
+ '_links',
139
+ 'wp:action-assign-' + _taxonomy.rest_base,
140
+ ],
141
+ false
142
+ )
143
+ : false,
144
+ taxonomy: _taxonomy,
145
+ termIds: _termIds,
146
+ terms: _termIds.length
147
+ ? getEntityRecords( 'taxonomy', slug, query )
148
+ : EMPTY_ARRAY,
149
+ hasResolvedTerms: hasFinishedResolution( 'getEntityRecords', [
150
+ 'taxonomy',
151
+ slug,
152
+ query,
153
+ ] ),
154
+ };
155
+ },
156
+ [ slug ]
157
+ );
105
158
 
106
- fetchTerms( params = {} ) {
107
- const { taxonomy } = this.props;
108
- const query = { ...DEFAULT_QUERY, ...params };
109
- const request = apiFetch( {
110
- path: addQueryArgs( `/wp/v2/${ taxonomy.rest_base }`, query ),
111
- } );
112
- request.then( unescapeTerms ).then( ( terms ) => {
113
- this.setState( ( state ) => ( {
114
- availableTerms: state.availableTerms.concat(
115
- terms.filter(
116
- ( term ) =>
117
- ! find(
118
- state.availableTerms,
119
- ( availableTerm ) =>
120
- availableTerm.id === term.id
121
- )
122
- )
123
- ),
124
- } ) );
125
- this.updateSelectedTerms( this.props.terms );
126
- } );
159
+ const { searchResults } = useSelect(
160
+ ( select ) => {
161
+ const { getEntityRecords } = select( coreStore );
127
162
 
128
- return request;
129
- }
163
+ return {
164
+ searchResults: !! search
165
+ ? getEntityRecords( 'taxonomy', slug, {
166
+ ...DEFAULT_QUERY,
167
+ search,
168
+ } )
169
+ : EMPTY_ARRAY,
170
+ };
171
+ },
172
+ [ search ]
173
+ );
130
174
 
131
- updateSelectedTerms( terms = [] ) {
132
- const selectedTerms = terms.reduce( ( accumulator, termId ) => {
133
- const termObject = find(
134
- this.state.availableTerms,
135
- ( term ) => term.id === termId
175
+ // Update terms state only after the selectors are resolved.
176
+ // We're using this to avoid terms temporarily disappearing on slow networks
177
+ // while core data makes REST API requests.
178
+ useEffect( () => {
179
+ if ( hasResolvedTerms ) {
180
+ const newValues = terms.map( ( term ) =>
181
+ unescapeString( term.name )
136
182
  );
137
- if ( termObject ) {
138
- accumulator.push( termObject.name );
139
- }
140
183
 
141
- return accumulator;
142
- }, [] );
143
- this.setState( {
144
- selectedTerms,
145
- } );
184
+ setValues( newValues );
185
+ }
186
+ }, [ terms, hasResolvedTerms ] );
187
+
188
+ const suggestions = useMemo( () => {
189
+ return ( searchResults ?? [] ).map( ( term ) =>
190
+ unescapeString( term.name )
191
+ );
192
+ }, [ searchResults ] );
193
+
194
+ const { editPost } = useDispatch( editorStore );
195
+
196
+ if ( ! hasAssignAction ) {
197
+ return null;
146
198
  }
147
199
 
148
- findOrCreateTerm( termName ) {
149
- const { taxonomy } = this.props;
150
- const termNameEscaped = escapeString( termName );
151
- // Tries to create a term or fetch it if it already exists.
152
- return apiFetch( {
153
- path: `/wp/v2/${ taxonomy.rest_base }`,
154
- method: 'POST',
155
- data: { name: termNameEscaped },
156
- } )
157
- .catch( ( error ) => {
158
- const errorCode = error.code;
159
- if ( errorCode === 'term_exists' ) {
160
- // If the terms exist, fetch it instead of creating a new one.
161
- this.addRequest = apiFetch( {
162
- path: addQueryArgs( `/wp/v2/${ taxonomy.rest_base }`, {
163
- ...DEFAULT_QUERY,
164
- search: termNameEscaped,
165
- } ),
166
- } ).then( unescapeTerms );
167
- return this.addRequest.then( ( searchResult ) => {
168
- return find( searchResult, ( result ) =>
169
- isSameTermName( result.name, termName )
170
- );
171
- } );
172
- }
173
- return Promise.reject( error );
174
- } )
175
- .then( unescapeTerm );
200
+ function onUpdateTerms( newTermIds ) {
201
+ editPost( { [ taxonomy.rest_base ]: newTermIds } );
176
202
  }
177
203
 
178
- onChange( termNames ) {
204
+ function onChange( termNames ) {
205
+ const availableTerms = [ ...terms, ...( searchResults ?? [] ) ];
179
206
  const uniqueTerms = uniqBy( termNames, ( term ) => term.toLowerCase() );
180
- this.setState( { selectedTerms: uniqueTerms } );
181
207
  const newTermNames = uniqueTerms.filter(
182
208
  ( termName ) =>
183
- ! find( this.state.availableTerms, ( term ) =>
209
+ ! find( availableTerms, ( term ) =>
184
210
  isSameTermName( term.name, termName )
185
211
  )
186
212
  );
187
213
 
214
+ // Optimistically update term values.
215
+ // The selector will always re-fetch terms later.
216
+ setValues( uniqueTerms );
217
+
188
218
  if ( newTermNames.length === 0 ) {
189
- return this.props.onUpdateTerms(
190
- termNamesToIds( uniqueTerms, this.state.availableTerms ),
191
- this.props.taxonomy.rest_base
219
+ return onUpdateTerms(
220
+ termNamesToIds( uniqueTerms, availableTerms )
192
221
  );
193
222
  }
194
- Promise.all( newTermNames.map( this.findOrCreateTerm ) ).then(
195
- ( newTerms ) => {
196
- const newAvailableTerms = this.state.availableTerms.concat(
197
- newTerms
198
- );
199
- this.setState( { availableTerms: newAvailableTerms } );
200
- return this.props.onUpdateTerms(
201
- termNamesToIds( uniqueTerms, newAvailableTerms ),
202
- this.props.taxonomy.rest_base
203
- );
204
- }
205
- );
206
- }
207
223
 
208
- searchTerms( search = '' ) {
209
- invoke( this.searchRequest, [ 'abort' ] );
210
- if ( search.length >= 3 ) {
211
- this.searchRequest = this.fetchTerms( { search } );
224
+ if ( ! hasCreateAction ) {
225
+ return;
212
226
  }
213
- }
214
227
 
215
- appendTerm( newTerm ) {
216
- const { onUpdateTerms, taxonomy, terms = [], slug, speak } = this.props;
228
+ Promise.all(
229
+ newTermNames.map( ( termName ) =>
230
+ findOrCreateTerm( termName, taxonomy.rest_base )
231
+ )
232
+ ).then( ( newTerms ) => {
233
+ const newAvailableTerms = availableTerms.concat( newTerms );
234
+ return onUpdateTerms(
235
+ termNamesToIds( uniqueTerms, newAvailableTerms )
236
+ );
237
+ } );
238
+ }
217
239
 
218
- if ( terms.includes( newTerm.id ) ) {
240
+ function appendTerm( newTerm ) {
241
+ if ( termIds.includes( newTerm.id ) ) {
219
242
  return;
220
243
  }
221
244
 
222
- const newTerms = [ ...terms, newTerm.id ];
223
-
245
+ const newTermIds = [ ...termIds, newTerm.id ];
224
246
  const termAddedMessage = sprintf(
225
247
  /* translators: %s: term name. */
226
248
  _x( '%s added', 'term' ),
@@ -232,109 +254,53 @@ class FlatTermSelector extends Component {
232
254
  );
233
255
 
234
256
  speak( termAddedMessage, 'assertive' );
235
-
236
- this.setState( {
237
- availableTerms: [ ...this.state.availableTerms, newTerm ],
238
- } );
239
-
240
- onUpdateTerms( newTerms, taxonomy.rest_base );
257
+ onUpdateTerms( newTermIds );
241
258
  }
242
259
 
243
- render() {
244
- const { slug, taxonomy, hasAssignAction } = this.props;
245
-
246
- if ( ! hasAssignAction ) {
247
- return null;
248
- }
249
-
250
- const { loading, availableTerms, selectedTerms } = this.state;
251
- const termNames = availableTerms.map( ( term ) => term.name );
252
- const newTermLabel = get(
253
- taxonomy,
254
- [ 'labels', 'add_new_item' ],
255
- slug === 'post_tag' ? __( 'Add new tag' ) : __( 'Add new Term' )
256
- );
257
- const singularName = get(
258
- taxonomy,
259
- [ 'labels', 'singular_name' ],
260
- slug === 'post_tag' ? __( 'Tag' ) : __( 'Term' )
261
- );
262
- const termAddedLabel = sprintf(
263
- /* translators: %s: term name. */
264
- _x( '%s added', 'term' ),
265
- singularName
266
- );
267
- const termRemovedLabel = sprintf(
268
- /* translators: %s: term name. */
269
- _x( '%s removed', 'term' ),
270
- singularName
271
- );
272
- const removeTermLabel = sprintf(
273
- /* translators: %s: term name. */
274
- _x( 'Remove %s', 'term' ),
275
- singularName
276
- );
260
+ const newTermLabel = get(
261
+ taxonomy,
262
+ [ 'labels', 'add_new_item' ],
263
+ slug === 'post_tag' ? __( 'Add new tag' ) : __( 'Add new Term' )
264
+ );
265
+ const singularName = get(
266
+ taxonomy,
267
+ [ 'labels', 'singular_name' ],
268
+ slug === 'post_tag' ? __( 'Tag' ) : __( 'Term' )
269
+ );
270
+ const termAddedLabel = sprintf(
271
+ /* translators: %s: term name. */
272
+ _x( '%s added', 'term' ),
273
+ singularName
274
+ );
275
+ const termRemovedLabel = sprintf(
276
+ /* translators: %s: term name. */
277
+ _x( '%s removed', 'term' ),
278
+ singularName
279
+ );
280
+ const removeTermLabel = sprintf(
281
+ /* translators: %s: term name. */
282
+ _x( 'Remove %s', 'term' ),
283
+ singularName
284
+ );
277
285
 
278
- return (
279
- <>
280
- <FormTokenField
281
- value={ selectedTerms }
282
- suggestions={ termNames }
283
- onChange={ this.onChange }
284
- onInputChange={ this.searchTerms }
285
- maxSuggestions={ MAX_TERMS_SUGGESTIONS }
286
- disabled={ loading }
287
- label={ newTermLabel }
288
- messages={ {
289
- added: termAddedLabel,
290
- removed: termRemovedLabel,
291
- remove: removeTermLabel,
292
- } }
293
- />
294
- <MostUsedTerms
295
- taxonomy={ taxonomy }
296
- onSelect={ this.appendTerm }
297
- />
298
- </>
299
- );
300
- }
286
+ return (
287
+ <>
288
+ <FormTokenField
289
+ value={ values }
290
+ suggestions={ suggestions }
291
+ onChange={ onChange }
292
+ onInputChange={ debouncedSearch }
293
+ maxSuggestions={ MAX_TERMS_SUGGESTIONS }
294
+ label={ newTermLabel }
295
+ messages={ {
296
+ added: termAddedLabel,
297
+ removed: termRemovedLabel,
298
+ remove: removeTermLabel,
299
+ } }
300
+ />
301
+ <MostUsedTerms taxonomy={ taxonomy } onSelect={ appendTerm } />
302
+ </>
303
+ );
301
304
  }
302
305
 
303
- export default compose(
304
- withSelect( ( select, { slug } ) => {
305
- const { getCurrentPost } = select( editorStore );
306
- const { getTaxonomy } = select( coreStore );
307
- const taxonomy = getTaxonomy( slug );
308
- return {
309
- hasCreateAction: taxonomy
310
- ? get(
311
- getCurrentPost(),
312
- [ '_links', 'wp:action-create-' + taxonomy.rest_base ],
313
- false
314
- )
315
- : false,
316
- hasAssignAction: taxonomy
317
- ? get(
318
- getCurrentPost(),
319
- [ '_links', 'wp:action-assign-' + taxonomy.rest_base ],
320
- false
321
- )
322
- : false,
323
- terms: taxonomy
324
- ? select( editorStore ).getEditedPostAttribute(
325
- taxonomy.rest_base
326
- )
327
- : [],
328
- taxonomy,
329
- };
330
- } ),
331
- withDispatch( ( dispatch ) => {
332
- return {
333
- onUpdateTerms( terms, restBase ) {
334
- dispatch( editorStore ).editPost( { [ restBase ]: terms } );
335
- },
336
- };
337
- } ),
338
- withSpokenMessages,
339
- withFilters( 'editor.PostTaxonomyType' )
340
- )( FlatTermSelector );
306
+ export default withFilters( 'editor.PostTaxonomyType' )( FlatTermSelector );
@@ -199,7 +199,7 @@ export default function PostTitle() {
199
199
  preserveWhiteSpace: true,
200
200
  } );
201
201
 
202
- /* eslint-disable jsx-a11y/heading-has-content, jsx-a11y/no-noninteractive-element-interactions */
202
+ /* eslint-disable jsx-a11y/heading-has-content, jsx-a11y/no-noninteractive-element-to-interactive-role */
203
203
  return (
204
204
  <PostTypeSupportCheck supportKeys="title">
205
205
  <h1
@@ -207,6 +207,8 @@ export default function PostTitle() {
207
207
  contentEditable
208
208
  className={ className }
209
209
  aria-label={ decodedPlaceholder }
210
+ role="textbox"
211
+ aria-multiline="true"
210
212
  onFocus={ onSelect }
211
213
  onBlur={ onUnselect }
212
214
  onKeyDown={ onKeyDown }
@@ -215,5 +217,5 @@ export default function PostTitle() {
215
217
  />
216
218
  </PostTypeSupportCheck>
217
219
  );
218
- /* eslint-enable jsx-a11y/heading-has-content, jsx-a11y/no-noninteractive-element-interactions */
220
+ /* eslint-enable jsx-a11y/heading-has-content, jsx-a11y/no-noninteractive-element-to-interactive-role */
219
221
  }
@@ -94,8 +94,6 @@ function useBlockEditorSettings( settings, hasTemplate ) {
94
94
  '__experimentalBlockPatternCategories',
95
95
  '__experimentalBlockPatterns',
96
96
  '__experimentalFeatures',
97
- '__experimentalGlobalStylesBaseStyles',
98
- '__experimentalGlobalStylesUserEntityId',
99
97
  '__experimentalPreferredStyleVariations',
100
98
  '__experimentalSetIsInserterOpened',
101
99
  '__unstableGalleryWithImageBlocks',