@wordpress/block-editor 12.10.0 → 12.10.2

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 (76) hide show
  1. package/build/components/block-patterns-list/index.js +10 -5
  2. package/build/components/block-patterns-list/index.js.map +1 -1
  3. package/build/components/block-patterns-paging/index.js +4 -2
  4. package/build/components/block-patterns-paging/index.js.map +1 -1
  5. package/build/components/inserter/block-patterns-explorer/patterns-list.js +21 -32
  6. package/build/components/inserter/block-patterns-explorer/patterns-list.js.map +1 -1
  7. package/build/components/inserter/block-patterns-explorer/sidebar.js +0 -15
  8. package/build/components/inserter/block-patterns-explorer/sidebar.js.map +1 -1
  9. package/build/components/inserter/block-patterns-filter.js +137 -0
  10. package/build/components/inserter/block-patterns-filter.js.map +1 -0
  11. package/build/components/inserter/block-patterns-tab.js +77 -37
  12. package/build/components/inserter/block-patterns-tab.js.map +1 -1
  13. package/build/components/inserter/hooks/use-patterns-paging.js +4 -0
  14. package/build/components/inserter/hooks/use-patterns-paging.js.map +1 -1
  15. package/build/components/inserter/search-results.js +1 -1
  16. package/build/components/inserter/search-results.js.map +1 -1
  17. package/build/components/inspector-controls-tabs/styles-tab.js +1 -1
  18. package/build/components/inspector-controls-tabs/styles-tab.js.map +1 -1
  19. package/build/hooks/background.js +28 -23
  20. package/build/hooks/background.js.map +1 -1
  21. package/build/hooks/block-rename-ui.js +2 -1
  22. package/build/hooks/block-rename-ui.js.map +1 -1
  23. package/build/store/private-selectors.js +2 -3
  24. package/build/store/private-selectors.js.map +1 -1
  25. package/build-module/components/block-patterns-list/index.js +11 -6
  26. package/build-module/components/block-patterns-list/index.js.map +1 -1
  27. package/build-module/components/block-patterns-paging/index.js +4 -2
  28. package/build-module/components/block-patterns-paging/index.js.map +1 -1
  29. package/build-module/components/inserter/block-patterns-explorer/patterns-list.js +23 -34
  30. package/build-module/components/inserter/block-patterns-explorer/patterns-list.js.map +1 -1
  31. package/build-module/components/inserter/block-patterns-explorer/sidebar.js +0 -14
  32. package/build-module/components/inserter/block-patterns-explorer/sidebar.js.map +1 -1
  33. package/build-module/components/inserter/block-patterns-filter.js +128 -0
  34. package/build-module/components/inserter/block-patterns-filter.js.map +1 -0
  35. package/build-module/components/inserter/block-patterns-tab.js +75 -35
  36. package/build-module/components/inserter/block-patterns-tab.js.map +1 -1
  37. package/build-module/components/inserter/hooks/use-patterns-paging.js +5 -1
  38. package/build-module/components/inserter/hooks/use-patterns-paging.js.map +1 -1
  39. package/build-module/components/inserter/search-results.js +1 -1
  40. package/build-module/components/inserter/search-results.js.map +1 -1
  41. package/build-module/components/inspector-controls-tabs/styles-tab.js +1 -1
  42. package/build-module/components/inspector-controls-tabs/styles-tab.js.map +1 -1
  43. package/build-module/hooks/background.js +30 -25
  44. package/build-module/hooks/background.js.map +1 -1
  45. package/build-module/hooks/block-rename-ui.js +2 -1
  46. package/build-module/hooks/block-rename-ui.js.map +1 -1
  47. package/build-module/store/private-selectors.js +2 -3
  48. package/build-module/store/private-selectors.js.map +1 -1
  49. package/build-style/style-rtl.css +54 -26
  50. package/build-style/style.css +54 -26
  51. package/package.json +32 -32
  52. package/src/components/block-patterns-list/index.js +19 -12
  53. package/src/components/block-patterns-paging/index.js +58 -55
  54. package/src/components/block-patterns-paging/style.scss +16 -13
  55. package/src/components/inserter/block-patterns-explorer/patterns-list.js +34 -76
  56. package/src/components/inserter/block-patterns-explorer/sidebar.js +0 -15
  57. package/src/components/inserter/block-patterns-filter.js +183 -0
  58. package/src/components/inserter/block-patterns-tab.js +106 -58
  59. package/src/components/inserter/hooks/use-patterns-paging.js +12 -1
  60. package/src/components/inserter/search-results.js +1 -1
  61. package/src/components/inserter/style.scss +14 -5
  62. package/src/components/inspector-controls-tabs/styles-tab.js +1 -1
  63. package/src/hooks/background.js +57 -46
  64. package/src/hooks/background.scss +36 -18
  65. package/src/hooks/block-rename-ui.js +1 -0
  66. package/src/store/private-selectors.js +7 -3
  67. package/build/components/inserter/block-patterns-source-filter.js +0 -54
  68. package/build/components/inserter/block-patterns-source-filter.js.map +0 -1
  69. package/build/components/inserter/block-patterns-sync-filter.js +0 -46
  70. package/build/components/inserter/block-patterns-sync-filter.js.map +0 -1
  71. package/build-module/components/inserter/block-patterns-source-filter.js +0 -44
  72. package/build-module/components/inserter/block-patterns-source-filter.js.map +0 -1
  73. package/build-module/components/inserter/block-patterns-sync-filter.js +0 -38
  74. package/build-module/components/inserter/block-patterns-sync-filter.js.map +0 -1
  75. package/src/components/inserter/block-patterns-source-filter.js +0 -40
  76. package/src/components/inserter/block-patterns-sync-filter.js +0 -35
@@ -4,12 +4,6 @@
4
4
  import { Button, SearchControl } from '@wordpress/components';
5
5
  import { __ } from '@wordpress/i18n';
6
6
 
7
- /**
8
- * Internal dependencies
9
- */
10
- import { default as BlockPatternsSourceFilter } from '../block-patterns-source-filter';
11
- import { allPatternsCategory } from '../block-patterns-tab';
12
-
13
7
  function PatternCategoriesList( {
14
8
  selectedCategory,
15
9
  patternCategories,
@@ -56,8 +50,6 @@ function PatternExplorerSidebar( {
56
50
  selectedCategory,
57
51
  patternCategories,
58
52
  onClickCategory,
59
- patternSourceFilter,
60
- setPatternSourceFilter,
61
53
  searchValue,
62
54
  setSearchValue,
63
55
  } ) {
@@ -68,13 +60,6 @@ function PatternExplorerSidebar( {
68
60
  searchValue={ searchValue }
69
61
  setSearchValue={ setSearchValue }
70
62
  />
71
- <BlockPatternsSourceFilter
72
- value={ patternSourceFilter }
73
- onChange={ ( value ) => {
74
- setPatternSourceFilter( value );
75
- onClickCategory( allPatternsCategory.name );
76
- } }
77
- />
78
63
  { ! searchValue && (
79
64
  <PatternCategoriesList
80
65
  selectedCategory={ selectedCategory }
@@ -0,0 +1,183 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import {
5
+ SVG,
6
+ Path,
7
+ DropdownMenu,
8
+ MenuGroup,
9
+ MenuItemsChoice,
10
+ } from '@wordpress/components';
11
+ import { __ } from '@wordpress/i18n';
12
+ import { Icon } from '@wordpress/icons';
13
+ import { useMemo } from '@wordpress/element';
14
+
15
+ /**
16
+ * Internal dependencies
17
+ */
18
+ import { myPatternsCategory } from './block-patterns-tab';
19
+
20
+ export const PATTERN_TYPES = {
21
+ all: 'all',
22
+ synced: 'synced',
23
+ unsynced: 'unsynced',
24
+ user: 'user',
25
+ theme: 'theme',
26
+ directory: 'directory',
27
+ };
28
+
29
+ export const SYNC_TYPES = {
30
+ all: 'all',
31
+ full: 'fully',
32
+ unsynced: 'unsynced',
33
+ };
34
+
35
+ const getShouldDisableSyncFilter = ( sourceFilter ) =>
36
+ sourceFilter !== PATTERN_TYPES.all && sourceFilter !== PATTERN_TYPES.user;
37
+
38
+ const getShouldDisableNonUserSources = ( category ) => {
39
+ return category.name === myPatternsCategory.name;
40
+ };
41
+
42
+ export function BlockPatternsSyncFilter( {
43
+ setPatternSyncFilter,
44
+ setPatternSourceFilter,
45
+ patternSyncFilter,
46
+ patternSourceFilter,
47
+ scrollContainerRef,
48
+ category,
49
+ } ) {
50
+ // If the category is `myPatterns` then we need to set the source filter to `user`, but
51
+ // we do this by deriving from props rather than calling setPatternSourceFilter otherwise
52
+ // the user may be confused when switching to another category if the haven't explicity set
53
+ // this filter themselves.
54
+ const currentPatternSourceFilter =
55
+ category.name === myPatternsCategory.name
56
+ ? PATTERN_TYPES.user
57
+ : patternSourceFilter;
58
+
59
+ // We need to disable the sync filter option if the source filter is not 'all' or 'user'
60
+ // otherwise applying them will just result in no patterns being shown.
61
+ const shouldDisableSyncFilter = getShouldDisableSyncFilter(
62
+ currentPatternSourceFilter
63
+ );
64
+
65
+ // We also need to disable the directory and theme source filter options if the category
66
+ // is `myPatterns` otherwise applying them will also just result in no patterns being shown.
67
+ const shouldDisableNonUserSources =
68
+ getShouldDisableNonUserSources( category );
69
+
70
+ const patternSyncMenuOptions = useMemo(
71
+ () => [
72
+ { value: SYNC_TYPES.all, label: __( 'All' ) },
73
+ {
74
+ value: SYNC_TYPES.full,
75
+ label: __( 'Synced' ),
76
+ info: __( 'Updated everywhere' ),
77
+ disabled: shouldDisableSyncFilter,
78
+ },
79
+ {
80
+ value: SYNC_TYPES.unsynced,
81
+ label: __( 'Standard' ),
82
+ info: __( 'Edit freely' ),
83
+ disabled: shouldDisableSyncFilter,
84
+ },
85
+ ],
86
+ [ shouldDisableSyncFilter ]
87
+ );
88
+
89
+ const patternSourceMenuOptions = useMemo(
90
+ () => [
91
+ {
92
+ value: PATTERN_TYPES.all,
93
+ label: __( 'All' ),
94
+ disabled: shouldDisableNonUserSources,
95
+ },
96
+ {
97
+ value: PATTERN_TYPES.directory,
98
+ label: __( 'Directory' ),
99
+ info: __( 'Pattern directory & core' ),
100
+ disabled: shouldDisableNonUserSources,
101
+ },
102
+ {
103
+ value: PATTERN_TYPES.theme,
104
+ label: __( 'Theme' ),
105
+ info: __( 'Bundled with the theme' ),
106
+ disabled: shouldDisableNonUserSources,
107
+ },
108
+ {
109
+ value: PATTERN_TYPES.user,
110
+ label: __( 'User' ),
111
+ info: __( 'Custom created' ),
112
+ },
113
+ ],
114
+ [ shouldDisableNonUserSources ]
115
+ );
116
+
117
+ function handleSetSourceFilterChange( newSourceFilter ) {
118
+ setPatternSourceFilter( newSourceFilter );
119
+ if ( getShouldDisableSyncFilter( newSourceFilter ) ) {
120
+ setPatternSyncFilter( SYNC_TYPES.all );
121
+ }
122
+ }
123
+
124
+ return (
125
+ <>
126
+ <DropdownMenu
127
+ popoverProps={ {
128
+ placement: 'right-end',
129
+ } }
130
+ label="Filter patterns"
131
+ icon={
132
+ <Icon
133
+ icon={
134
+ <SVG
135
+ width="24"
136
+ height="24"
137
+ viewBox="0 0 24 24"
138
+ fill="none"
139
+ xmlns="http://www.w3.org/2000/svg"
140
+ >
141
+ <Path
142
+ d="M10 17.5H14V16H10V17.5ZM6 6V7.5H18V6H6ZM8 12.5H16V11H8V12.5Z"
143
+ fill="#1E1E1E"
144
+ />
145
+ </SVG>
146
+ }
147
+ />
148
+ }
149
+ >
150
+ { () => (
151
+ <>
152
+ <MenuGroup label={ __( 'Author' ) }>
153
+ <MenuItemsChoice
154
+ choices={ patternSourceMenuOptions }
155
+ onSelect={ ( value ) => {
156
+ handleSetSourceFilterChange( value );
157
+ scrollContainerRef.current?.scrollTo(
158
+ 0,
159
+ 0
160
+ );
161
+ } }
162
+ value={ currentPatternSourceFilter }
163
+ />
164
+ </MenuGroup>
165
+ <MenuGroup label={ __( 'Type' ) }>
166
+ <MenuItemsChoice
167
+ choices={ patternSyncMenuOptions }
168
+ onSelect={ ( value ) => {
169
+ setPatternSyncFilter( value );
170
+ scrollContainerRef.current?.scrollTo(
171
+ 0,
172
+ 0
173
+ );
174
+ } }
175
+ value={ patternSyncFilter }
176
+ />
177
+ </MenuGroup>
178
+ </>
179
+ ) }
180
+ </DropdownMenu>
181
+ </>
182
+ );
183
+ }
@@ -14,6 +14,9 @@ import {
14
14
  __experimentalItemGroup as ItemGroup,
15
15
  __experimentalItem as Item,
16
16
  __experimentalHStack as HStack,
17
+ __experimentalVStack as VStack,
18
+ __experimentalHeading as Heading,
19
+ __experimentalText as Text,
17
20
  FlexBlock,
18
21
  Button,
19
22
  } from '@wordpress/components';
@@ -28,48 +31,68 @@ import usePatternsState from './hooks/use-patterns-state';
28
31
  import BlockPatternList from '../block-patterns-list';
29
32
  import PatternsExplorerModal from './block-patterns-explorer/explorer';
30
33
  import MobileTabNavigation from './mobile-tab-navigation';
31
- import BlockPatternsPaging from '../block-patterns-paging';
32
34
  import usePatternsPaging from './hooks/use-patterns-paging';
33
- import {
34
- PATTERN_TYPES,
35
- default as BlockPatternsSourceFilter,
36
- } from './block-patterns-source-filter';
37
35
  import {
38
36
  BlockPatternsSyncFilter,
39
37
  SYNC_TYPES,
40
- } from './block-patterns-sync-filter';
38
+ PATTERN_TYPES,
39
+ } from './block-patterns-filter';
41
40
 
42
41
  const noop = () => {};
43
42
 
44
43
  export const allPatternsCategory = {
45
44
  name: 'allPatterns',
46
- label: __( 'All categories' ),
45
+ label: __( 'All patterns' ),
46
+ };
47
+
48
+ export const myPatternsCategory = {
49
+ name: 'myPatterns',
50
+ label: __( 'My patterns' ),
47
51
  };
48
52
 
49
53
  export function isPatternFiltered( pattern, sourceFilter, syncFilter ) {
54
+ const isUserPattern = pattern.name.startsWith( 'core/block' );
55
+ const isDirectoryPattern =
56
+ pattern.source === 'core' ||
57
+ pattern.source?.startsWith( 'pattern-directory' );
58
+
59
+ // If theme source selected, filter out user created patterns and those from
60
+ // the core patterns directory.
50
61
  if (
51
62
  sourceFilter === PATTERN_TYPES.theme &&
52
- pattern.name.startsWith( 'core/block' )
63
+ ( isUserPattern || isDirectoryPattern )
64
+ ) {
65
+ return true;
66
+ }
67
+
68
+ // If the directory source is selected, filter out user created patterns
69
+ // and those bundled with the theme.
70
+ if (
71
+ sourceFilter === PATTERN_TYPES.directory &&
72
+ ( isUserPattern || ! isDirectoryPattern )
53
73
  ) {
54
74
  return true;
55
75
  }
76
+
77
+ // If user source selected, filter out theme patterns. Any pattern without
78
+ // an id wasn't created by a user.
56
79
  if ( sourceFilter === PATTERN_TYPES.user && ! pattern.id ) {
57
80
  return true;
58
81
  }
59
- if (
60
- sourceFilter === PATTERN_TYPES.user &&
61
- syncFilter === SYNC_TYPES.full &&
62
- pattern.syncStatus !== ''
63
- ) {
82
+
83
+ // Filter by sync status.
84
+ if ( syncFilter === SYNC_TYPES.full && pattern.syncStatus !== '' ) {
64
85
  return true;
65
86
  }
87
+
66
88
  if (
67
- sourceFilter === PATTERN_TYPES.user &&
68
89
  syncFilter === SYNC_TYPES.unsynced &&
69
- pattern.syncStatus !== 'unsynced'
90
+ pattern.syncStatus !== 'unsynced' &&
91
+ isUserPattern
70
92
  ) {
71
93
  return true;
72
94
  }
95
+
73
96
  return false;
74
97
  }
75
98
 
@@ -126,6 +149,9 @@ export function usePatternsCategories( rootClientId, sourceFilter = 'all' ) {
126
149
  label: _x( 'Uncategorized' ),
127
150
  } );
128
151
  }
152
+ if ( filteredPatterns.some( ( pattern ) => pattern.id ) ) {
153
+ categories.unshift( myPatternsCategory );
154
+ }
129
155
  if ( filteredPatterns.length > 0 ) {
130
156
  categories.unshift( {
131
157
  name: allPatternsCategory.name,
@@ -173,6 +199,7 @@ export function BlockPatternsCategoryDialog( {
173
199
  className="block-editor-inserter__patterns-category-dialog"
174
200
  >
175
201
  <BlockPatternsCategoryPanel
202
+ key={ category.name }
176
203
  rootClientId={ rootClientId }
177
204
  onInsert={ onInsert }
178
205
  onHover={ onHover }
@@ -190,26 +217,26 @@ export function BlockPatternsCategoryPanel( {
190
217
  onHover = noop,
191
218
  category,
192
219
  showTitlesAsTooltip,
193
- patternFilter,
194
220
  } ) {
195
221
  const [ allPatterns, , onClickPattern ] = usePatternsState(
196
222
  onInsert,
197
223
  rootClientId
198
224
  );
199
225
  const [ patternSyncFilter, setPatternSyncFilter ] = useState( 'all' );
226
+ const [ patternSourceFilter, setPatternSourceFilter ] = useState( 'all' );
200
227
 
201
228
  const availableCategories = usePatternsCategories(
202
229
  rootClientId,
203
- patternFilter
230
+ patternSourceFilter
204
231
  );
205
- const container = useRef();
232
+ const scrollContainerRef = useRef();
206
233
  const currentCategoryPatterns = useMemo(
207
234
  () =>
208
235
  allPatterns.filter( ( pattern ) => {
209
236
  if (
210
237
  isPatternFiltered(
211
238
  pattern,
212
- patternFilter,
239
+ patternSourceFilter,
213
240
  patternSyncFilter
214
241
  )
215
242
  ) {
@@ -219,6 +246,9 @@ export function BlockPatternsCategoryPanel( {
219
246
  if ( category.name === allPatternsCategory.name ) {
220
247
  return true;
221
248
  }
249
+ if ( category.name === myPatternsCategory.name && pattern.id ) {
250
+ return true;
251
+ }
222
252
  if ( category.name !== 'uncategorized' ) {
223
253
  return pattern.categories?.includes( category.name );
224
254
  }
@@ -239,7 +269,7 @@ export function BlockPatternsCategoryPanel( {
239
269
  allPatterns,
240
270
  availableCategories,
241
271
  category.name,
242
- patternFilter,
272
+ patternSourceFilter,
243
273
  patternSyncFilter,
244
274
  ]
245
275
  );
@@ -247,33 +277,66 @@ export function BlockPatternsCategoryPanel( {
247
277
  const pagingProps = usePatternsPaging(
248
278
  currentCategoryPatterns,
249
279
  category,
250
- container
280
+ scrollContainerRef
251
281
  );
282
+ const { changePage } = pagingProps;
252
283
 
253
284
  // Hide block pattern preview on unmount.
254
285
  // eslint-disable-next-line react-hooks/exhaustive-deps
255
286
  useEffect( () => () => onHover( null ), [] );
256
287
 
288
+ const onSetPatternSyncFilter = useCallback(
289
+ ( value ) => {
290
+ setPatternSyncFilter( value );
291
+ changePage( 1 );
292
+ },
293
+ [ setPatternSyncFilter, changePage ]
294
+ );
295
+ const onSetPatternSourceFilter = useCallback(
296
+ ( value ) => {
297
+ setPatternSourceFilter( value );
298
+ changePage( 1 );
299
+ },
300
+ [ setPatternSourceFilter, changePage ]
301
+ );
302
+
257
303
  return (
258
- <div
259
- className="block-editor-inserter__patterns-category-panel"
260
- ref={ container }
261
- >
262
- <div className="block-editor-inserter__patterns-category-panel-title">
263
- { category.label }
264
- </div>
265
- <p>{ category.description }</p>
266
- { patternFilter === PATTERN_TYPES.user && (
267
- <BlockPatternsSyncFilter
268
- patternSyncFilter={ patternSyncFilter }
269
- setPatternSyncFilter={ setPatternSyncFilter }
270
- />
271
- ) }
272
- { ! currentCategoryPatterns.length && (
273
- <div>{ __( 'No results found' ) }</div>
274
- ) }
304
+ <div className="block-editor-inserter__patterns-category-panel">
305
+ <VStack
306
+ spacing={ 2 }
307
+ className="block-editor-inserter__patterns-category-panel-header"
308
+ >
309
+ <HStack>
310
+ <FlexBlock>
311
+ <Heading level={ 4 } as="div">
312
+ { category.label }
313
+ </Heading>
314
+ </FlexBlock>
315
+ <BlockPatternsSyncFilter
316
+ patternSyncFilter={ patternSyncFilter }
317
+ patternSourceFilter={ patternSourceFilter }
318
+ setPatternSyncFilter={ onSetPatternSyncFilter }
319
+ setPatternSourceFilter={ onSetPatternSourceFilter }
320
+ scrollContainerRef={ scrollContainerRef }
321
+ category={ category }
322
+ />
323
+ </HStack>
324
+ { category.description && (
325
+ <Text>{ category.description }</Text>
326
+ ) }
327
+ { ! currentCategoryPatterns.length && (
328
+ <Text
329
+ variant="muted"
330
+ className="block-editor-inserter__patterns-category-no-results"
331
+ >
332
+ { __( 'No results found' ) }
333
+ </Text>
334
+ ) }
335
+ </VStack>
336
+
275
337
  { currentCategoryPatterns.length > 0 && (
276
338
  <BlockPatternList
339
+ ref={ scrollContainerRef }
277
340
  shownPatterns={ pagingProps.categoryPatternsAsyncList }
278
341
  blockPatterns={ pagingProps.categoryPatterns }
279
342
  onClickPattern={ onClickPattern }
@@ -283,12 +346,10 @@ export function BlockPatternsCategoryPanel( {
283
346
  category={ category.name }
284
347
  isDraggable
285
348
  showTitlesAsTooltip={ showTitlesAsTooltip }
286
- patternFilter={ patternFilter }
349
+ patternFilter={ patternSourceFilter }
350
+ pagingProps={ pagingProps }
287
351
  />
288
352
  ) }
289
- { pagingProps.numPages > 1 && (
290
- <BlockPatternsPaging { ...pagingProps } />
291
- ) }
292
353
  </div>
293
354
  );
294
355
  }
@@ -300,12 +361,8 @@ function BlockPatternsTabs( {
300
361
  rootClientId,
301
362
  } ) {
302
363
  const [ showPatternsExplorer, setShowPatternsExplorer ] = useState( false );
303
- const [ patternSourceFilter, setPatternSourceFilter ] = useState( 'all' );
304
364
 
305
- const categories = usePatternsCategories(
306
- rootClientId,
307
- patternSourceFilter
308
- );
365
+ const categories = usePatternsCategories( rootClientId );
309
366
 
310
367
  const initialCategory = selectedCategory || categories[ 0 ];
311
368
  const isMobile = useViewportMatch( 'medium', '<' );
@@ -317,23 +374,13 @@ function BlockPatternsTabs( {
317
374
  aria-label={ __( 'Block pattern categories' ) }
318
375
  className="block-editor-inserter__block-patterns-tabs"
319
376
  >
320
- <BlockPatternsSourceFilter
321
- value={ patternSourceFilter }
322
- onChange={ ( value ) => {
323
- setPatternSourceFilter( value );
324
- onSelectCategory( allPatternsCategory, value );
325
- } }
326
- />
327
377
  <ItemGroup role="list">
328
378
  { categories.map( ( category ) => (
329
379
  <Item
330
380
  role="listitem"
331
381
  key={ category.name }
332
382
  onClick={ () =>
333
- onSelectCategory(
334
- category,
335
- patternSourceFilter
336
- )
383
+ onSelectCategory( category )
337
384
  }
338
385
  className={
339
386
  category === selectedCategory
@@ -380,6 +427,7 @@ function BlockPatternsTabs( {
380
427
  <MobileTabNavigation categories={ categories }>
381
428
  { ( category ) => (
382
429
  <BlockPatternsCategoryPanel
430
+ key={ category.name }
383
431
  onInsert={ onInsert }
384
432
  rootClientId={ rootClientId }
385
433
  category={ category }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { useMemo, useState } from '@wordpress/element';
4
+ import { useMemo, useState, useEffect } from '@wordpress/element';
5
5
  import { useAsyncList, usePrevious } from '@wordpress/compose';
6
6
  import { getScrollContainer } from '@wordpress/dom';
7
7
 
@@ -54,6 +54,17 @@ export default function usePatternsPaging(
54
54
 
55
55
  setCurrentPage( page );
56
56
  };
57
+
58
+ useEffect(
59
+ function scrollToTopOnCategoryChange() {
60
+ const scrollContainer = getScrollContainer(
61
+ scrollContainerRef?.current
62
+ );
63
+ scrollContainer?.scrollTo( 0, 0 );
64
+ },
65
+ [ currentCategory, scrollContainerRef ]
66
+ );
67
+
57
68
  return {
58
69
  totalItems,
59
70
  categoryPatterns,
@@ -184,7 +184,7 @@ function InserterSearchResults( {
184
184
  const patternsUI = !! filteredBlockPatterns.length && (
185
185
  <InserterPanel
186
186
  title={
187
- <VisuallyHidden>{ __( 'Block Patterns' ) }</VisuallyHidden>
187
+ <VisuallyHidden>{ __( 'Block patterns' ) }</VisuallyHidden>
188
188
  }
189
189
  >
190
190
  <div className="block-editor-inserter__quick-inserter-patterns">
@@ -299,13 +299,10 @@ $block-inserter-tabs-height: 44px;
299
299
  border-left: $border-width solid $gray-200;
300
300
  border-right: $border-width solid $gray-200;
301
301
  position: absolute;
302
- padding: $grid-unit-40 $grid-unit-30;
303
302
  top: 0;
304
303
  left: 0;
305
304
  height: 100%;
306
305
  width: 100%;
307
- overflow-y: auto;
308
- scrollbar-gutter: stable both-edges;
309
306
 
310
307
  @include break-medium {
311
308
  left: 100%;
@@ -314,7 +311,10 @@ $block-inserter-tabs-height: 44px;
314
311
  }
315
312
 
316
313
  .block-editor-block-patterns-list {
317
- margin-top: $grid-unit-30;
314
+ overflow-y: auto;
315
+ flex-grow: 1;
316
+ height: 100%;
317
+ padding: $grid-unit-40 $grid-unit-30;
318
318
  }
319
319
  }
320
320
 
@@ -331,10 +331,18 @@ $block-inserter-tabs-height: 44px;
331
331
 
332
332
  .block-editor-inserter__patterns-category-panel {
333
333
  padding: 0 $grid-unit-20;
334
-
334
+ display: flex;
335
+ flex-direction: column;
336
+ height: 100%;
335
337
  @include break-medium {
336
338
  padding: 0;
337
339
  }
340
+ .block-editor-inserter__patterns-category-panel-header {
341
+ padding: 16px $grid-unit-30;
342
+ }
343
+ .block-editor-inserter__patterns-category-no-results {
344
+ margin-top: $grid-unit-30;
345
+ }
338
346
  }
339
347
 
340
348
  .block-editor-inserter__preview-content {
@@ -472,6 +480,7 @@ $block-inserter-tabs-height: 44px;
472
480
  display: grid;
473
481
  grid-gap: $grid-unit-40;
474
482
  grid-template-columns: repeat(1, 1fr);
483
+ margin-bottom: $grid-unit-20;
475
484
 
476
485
  @include break-xlarge() {
477
486
  grid-template-columns: repeat(2, 1fr);
@@ -34,7 +34,7 @@ const StylesTab = ( { blockName, clientId, hasBlockStyles } ) => {
34
34
  />
35
35
  <InspectorControls.Slot
36
36
  group="background"
37
- label={ __( 'Background image' ) }
37
+ label={ __( 'Background' ) }
38
38
  />
39
39
  <InspectorControls.Slot group="filter" />
40
40
  <InspectorControls.Slot