Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
c86c42f
add keyword functionality to icon search
n8finch Sep 2, 2026
7dd693f
add keyword functionality to icon block
n8finch Sep 2, 2026
10d0f53
update test
n8finch Sep 2, 2026
deb93b5
Update packages/icons/src/manifest.json
n8finch Sep 4, 2026
0c6e943
Update packages/icons/src/manifest.json
n8finch Sep 4, 2026
a858bbf
Update packages/icons/CHANGELOG.md
n8finch Sep 4, 2026
3d7148e
Update packages/icons/src/manifest.json
n8finch Sep 4, 2026
2083b56
Update packages/icons/src/manifest.json
n8finch Sep 4, 2026
aa0411b
resolve merge conflicts
n8finch Sep 4, 2026
70f7ea1
reject null keywords, match search Unicode-aware, and unregister test…
n8finch Sep 4, 2026
1366fd8
move storybook icon keywords to manifest
n8finch Sep 4, 2026
6baa88e
Assert on width/height attributes so stroke-width no longer trips the…
n8finch Sep 4, 2026
3e621b5
resolve merge conflicts
n8finch Sep 11, 2026
819c813
resolve merge conflicts
n8finch Sep 14, 2026
6892c91
Update lib/class-wp-rest-icons-controller-gutenberg.php
n8finch Sep 16, 2026
3e014f6
Merge branch 'trunk' into update/add-keyword-search-for-icons
n8finch Sep 16, 2026
b575ed8
remove UTF-8 arg
n8finch Sep 16, 2026
6ebcbc6
update keyword passthrough
n8finch Sep 16, 2026
5979bb6
Update lib/class-wp-icons-registry-gutenberg.php
n8finch Sep 17, 2026
d5181d2
Update lib/class-wp-icons-registry-gutenberg.php
n8finch Sep 17, 2026
7602735
Update lib/icons.php
n8finch Sep 17, 2026
6fa3c58
update changelog note
n8finch Sep 17, 2026
2d9ef93
resolve merge conflicts
n8finch Sep 17, 2026
8de3e8e
Merge branch 'trunk' into update/add-keyword-search-for-icons
t-hamano Sep 21, 2026
f7413d7
Tag Cloud: Add background and link colour support (#83264)
aaronrobertshaw Sep 21, 2026
535d5d0
Storybook: List every top-level folder in the sidebar order, design s…
juanmaguitar Sep 21, 2026
86b1397
Featured image field: support the `editor.PostFeaturedImage` filter i…
ntsekouras Sep 21, 2026
e7b0539
Storybook: Fold Foundations, DataViews, Fields, Admin UI and Icons un…
juanmaguitar Sep 21, 2026
efefb92
Block Editor: replace denylisted layout components in the Duotone fil…
mirka Sep 21, 2026
d61f8b2
Storybook: Group Widget Primitives, Widget Dashboard and Grid under W…
juanmaguitar Sep 21, 2026
a750737
Storybook: Group the editor UI under Editor (#82432)
juanmaguitar Sep 21, 2026
5df7118
Components: Deprecate Elevation (#83269)
mirka Sep 21, 2026
d89fbef
Post Time to Read: Add background image, size, and gradient support (…
aaronrobertshaw Sep 21, 2026
2e76d5a
Buttons: Add background image, size, and gradient support (#83203)
aaronrobertshaw Sep 21, 2026
703f25e
Storybook: Group component packages under Components (#82433)
juanmaguitar Sep 21, 2026
548d5f0
Terms List: Add background image, size, and gradient support (#83257)
aaronrobertshaw Sep 21, 2026
e6f552c
UI: Add SwitchControl component (#83146)
mirka Sep 21, 2026
ad5a320
Release tools: Move the plugin ZIP build out of bin/ (#83087)
manzoorwanijk Sep 21, 2026
71e550a
Storybook: Remove the legacy Tokens pages (#82434)
juanmaguitar Sep 21, 2026
10eaa2a
Storybook: Show the component status in the sidebar as status-* tags …
juanmaguitar Sep 21, 2026
0e67859
Storybook: Guard the set of top-level sidebar folders with a test (#8…
juanmaguitar Sep 21, 2026
e083d53
Storybook: Rewrite the introduction page as the front door of the des…
juanmaguitar Sep 21, 2026
a9cd388
Workflows: Refine and complete the push and pull_request branch filte…
desrosj Sep 21, 2026
7499eee
Merge branch 'trunk' into update/add-keyword-search-for-icons
n8finch Sep 21, 2026
03812d3
update changelogs, comments, types
n8finch Sep 21, 2026
97c2556
fix precision alignment
n8finch Sep 21, 2026
19ef827
change mb_stringpos to stringpos
n8finch Sep 21, 2026
81b49bf
update collection validation
n8finch Sep 22, 2026
5f6b358
resolve merge conflicts
n8finch Sep 22, 2026
32e43c6
add sanitize
n8finch Sep 22, 2026
7a41cf6
Merge branch 'trunk' of https://github.com/WordPress/gutenberg into u…
n8finch Sep 23, 2026
f8dfe8c
add backport changelog file
n8finch Sep 23, 2026
1a83934
resolve merge conflicts
n8finch Sep 23, 2026
7207f50
update tests
n8finch Sep 23, 2026
9b37909
remove check
n8finch Sep 23, 2026
2fac0a2
Update packages/icons/README.md
n8finch Sep 23, 2026
31007a0
Update packages/icons/CHANGELOG.md
n8finch Sep 23, 2026
cc21398
update tests
n8finch Sep 23, 2026
302d6f0
revert manifests and Storybook
n8finch Sep 24, 2026
3736e7f
resolve merge conflicts
n8finch Sep 24, 2026
3c9dc7d
resolve merge conflicts
n8finch Sep 25, 2026
5e5e662
update test setup
n8finch Sep 25, 2026
68d663d
resolve merge conflicts
n8finch Sep 28, 2026
6cb78f8
Update packages/icons/CHANGELOG.md
n8finch Sep 28, 2026
2926fbb
Apply batched suggestions from code review
n8finch Sep 28, 2026
8b950d9
Update packages/icons/README.md
n8finch Sep 28, 2026
6b0f64e
update alignment for comment
n8finch Sep 28, 2026
5738713
Merge branch 'trunk' of https://github.com/WordPress/gutenberg into u…
n8finch Sep 28, 2026
bde665a
Merge branch 'trunk' into update/add-keyword-search-for-icons
t-hamano Sep 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 95 additions & 15 deletions lib/class-wp-icons-registry-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,17 @@ protected function __construct() {}
* @param array $icon_properties {
* List of properties for the icon.
*
* @type string $label Required. A human-readable label for the icon.
* @type string $content Optional. SVG markup for the icon.
* If not provided, the content will be retrieved from the `file_path` if set.
* If both `content` and `file_path` are not set, the icon will not be registered.
* @type string $file_path Optional. The full path to the file containing the icon content.
* @type bool $public Optional. Whether the icon is exposed through the REST API, and
* therefore selectable in the editor's Icon block. Non-public icons
* stay available to server-side code via {@see wp_get_icon()}.
* Default true.
* @type string $label Required. A human-readable label for the icon.
* @type string $content Optional. SVG markup for the icon.
* If not provided, the content will be retrieved from the `file_path` if set.
* If both `content` and `file_path` are not set, the icon will not be registered.
* @type string $file_path Optional. The full path to the file containing the icon content.
* @type bool $public Optional. Whether the icon is exposed through the REST API, and
* therefore selectable in the editor's Icon block. Non-public icons
* stay available to server-side code via {@see wp_get_icon()}.
* Default true.
* @type string[] $keywords Optional. Additional search terms for the icon, matched by
* `get_registered_icons()` alongside the name and label.
Comment thread
n8finch marked this conversation as resolved.
Resolved
* }
* @return bool True if the icon was registered with success and false otherwise.
*/
Expand Down Expand Up @@ -154,7 +156,7 @@ public function register( $icon_name, $icon_properties ) {
return false;
}

$allowed_keys = array_fill_keys( array( 'label', 'content', 'file_path', 'public' ), 1 );
$allowed_keys = array_fill_keys( array( 'label', 'content', 'file_path', 'public', 'keywords' ), 1 );
foreach ( array_keys( $icon_properties ) as $key ) {
if ( ! array_key_exists( $key, $allowed_keys ) ) {
_doing_it_wrong(
Expand Down Expand Up @@ -201,6 +203,28 @@ public function register( $icon_name, $icon_properties ) {
return false;
}

if ( array_key_exists( 'keywords', $icon_properties ) ) {
if ( ! is_array( $icon_properties['keywords'] ) ) {
_doing_it_wrong(
__METHOD__,
__( 'Icon keywords must be an array of strings.', 'gutenberg' ),
'7.1.0'
Comment thread
n8finch marked this conversation as resolved.
Resolved
);
return false;
}

foreach ( $icon_properties['keywords'] as $keyword ) {
if ( ! is_string( $keyword ) ) {
_doing_it_wrong(
__METHOD__,
__( 'Icon keywords must be an array of strings.', 'gutenberg' ),
'7.1.0'
Comment thread
n8finch marked this conversation as resolved.
Resolved
);
return false;
}
}
}

if (
( ! isset( $icon_properties['content'] ) && ! isset( $icon_properties['file_path'] ) ) ||
( isset( $icon_properties['content'] ) && isset( $icon_properties['file_path'] ) )
Expand Down Expand Up @@ -347,16 +371,69 @@ protected function get_content( $icon_name ) {
}

/**
* Modified to also search in icon labels
* Determines whether a string contains a search term, ignoring case.
*
* Labels and keywords are translated, so the comparison is made with
* `mb_stripos()` where mbstring is available, which case-folds multibyte
* characters that `stripos()` only matches byte for byte. `stripos()` is
* the fallback, since mbstring is not guaranteed to be loaded.
*
* @param string $haystack String to search in.
* @param string $needle Search term.
* @return bool True if the haystack contains the search term, false otherwise.
*/
private function contains_search_term( $haystack, $needle ) {
Comment thread
n8finch marked this conversation as resolved.
Resolved
if ( '' === $needle ) {
return true;
}

if ( function_exists( 'mb_stripos' ) ) {
return false !== mb_stripos( $haystack, $needle, 0 );
}

return false !== stripos( $haystack, $needle );
}

/**
* Determines whether an icon matches a search term.
*
* The term is matched case-insensitively against the icon's name, its label,
* and any of its keywords.
*
* @param array $icon Registered icon properties.
* @param string $search Search term.
* @return bool True if the icon matches the search term, false otherwise.
*/
protected function icon_matches_search( $icon, $search ) {
if ( $this->contains_search_term( $icon['name'], $search ) ) {
return true;
}

if ( $this->contains_search_term( $icon['label'], $search ) ) {
return true;
}

foreach ( $icon['keywords'] ?? array() as $keyword ) {
if ( $this->contains_search_term( $keyword, $search ) ) {
return true;
}
}

return false;
}

/**
* Modified to also search in icon labels and keywords.
*
* @param string $search Optional. Search term matched against each icon's name,
* label, and keywords. Default empty string, which returns
* every registered icon.
*/
public function get_registered_icons( $search = '' ) {
$icons = array();

foreach ( $this->registered_icons as $icon ) {
if ( ! empty( $search )
&& false === stripos( $icon['name'], $search )
&& false === stripos( $icon['label'], $search )
) {
if ( ! empty( $search ) && ! $this->icon_matches_search( $icon, $search ) ) {
continue;
}

Expand Down Expand Up @@ -396,6 +473,9 @@ public static function get_instance() {
if ( isset( $icon['public'] ) ) {
$icon_properties['public'] = $icon['public'];
}
if ( ! empty( $icon['keywords'] ) ) {
$icon_properties['keywords'] = $icon['keywords'];
}
$gutenberg_registry->register( $icon['name'], $icon_properties );
}
}
Expand Down
18 changes: 16 additions & 2 deletions lib/class-wp-rest-icons-controller-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ public function get_icon( $name ) {
/**
* Prepare a raw icon before it gets output in a REST API response.
*
* Adds a `collection` field to the base response while keeping the
* namespaced icon name (e.g. `core/arrow-left`) as the `name` field.
* Adds `collection` and `keywords` fields to the base response while keeping
* the namespaced icon name (e.g. `core/arrow-left`) as the `name` field.
*
* @param array $item Raw icon as registered.
* @param WP_REST_Request $request Request object.
Expand All @@ -176,6 +176,12 @@ public function prepare_item_for_response( $item, $request ) {
$response->set_data( $data );
}

if ( rest_is_field_included( 'keywords', $fields ) ) {
$data = $response->get_data();
$data['keywords'] = isset( $item['keywords'] ) ? array_values( $item['keywords'] ) : array();
$response->set_data( $data );
}

return $response;
}

Expand All @@ -198,6 +204,14 @@ public function get_item_schema() {
'context' => array( 'view', 'edit', 'embed' ),
);

$schema['properties']['keywords'] = array(
'description' => __( 'Additional search terms for the icon.', 'gutenberg' ),
'type' => 'array',
'items' => array( 'type' => 'string' ),
'readonly' => true,
'context' => array( 'view', 'edit', 'embed' ),
);
Comment thread
n8finch marked this conversation as resolved.
Resolved

$this->schema = $schema;

return $this->add_additional_fields_schema( $this->schema );
Expand Down
5 changes: 5 additions & 0 deletions lib/icons.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ function gutenberg_register_default_icons() {
$icon_args['public'] = $icon_data['public'];
}

// Keywords are optional, so only pass them through when present.
if ( ! empty( $icon_data['keywords'] ) && is_array( $icon_data['keywords'] ) ) {
$icon_args['keywords'] = $icon_data['keywords'];
}
Comment thread
n8finch marked this conversation as resolved.
Resolved

wp_register_icon( 'core/' . $icon_name, $icon_args );
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/block-library/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
### Enhancements

- Columns: Add transforms between Columns and the Row variation that preserve column widths through flex child sizing controls.
- Icon: Match an icon's keywords as well as its name and label when searching the icon library. ([#82367](https://github.com/WordPress/gutenberg/pull/82367))
Comment thread
t-hamano marked this conversation as resolved.
Resolved
- Gallery: Add an opt-in Grid layout while preserving the existing Flex layout for current galleries.([#81909](https://github.com/WordPress/gutenberg/pull/81909)).

### Bug Fixes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ export default function CustomInserterModal( { onClose, value, onChange } ) {
const iconLabel = normalizeSearchInput( icon.label );

return (
iconName.includes( input ) || iconLabel.includes( input )
iconName.includes( input ) ||
iconLabel.includes( input ) ||
( icon.keywords ?? [] ).some( ( keyword ) =>
normalizeSearchInput( keyword ).includes( input )
)
);
} );
}
Expand Down
6 changes: 5 additions & 1 deletion packages/icons/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Enhancements

- Add optional `keywords` to the icon manifest, giving each public icon extra search terms, and emit them from the generated `manifest.php`. The manifest is now the single source of these terms: the Storybook icon library reads them from it instead of keeping its own list, and the icons it had keywords for carry them in the manifest. ([#82367](https://github.com/WordPress/gutenberg/pull/82367))
Comment thread
n8finch marked this conversation as resolved.
Resolved

### Breaking Changes

- A further 236 icons are now stroke-based. Use CSS `color` rather than `fill` to recolor them. ([#82540](https://github.com/WordPress/gutenberg/pull/82540), [#82754](https://github.com/WordPress/gutenberg/pull/82754))
Expand Down Expand Up @@ -99,7 +103,7 @@

### Breaking Changes

- Rename Tabs-related icons: `tabs-menu` to `tab-list`, `tabs-menu-item` to `tab`, and `tab` to `tab-panel`. ([#77418](https://github.com/WordPress/gutenberg/pull/77418))
- Rename Tabs-related icons: `tabs-menu` to `tab-list`, `tabs-menu-item` to `tab`, and `tab` to `tab-panel`. ([#77418](https://github.com/WordPress/gutenberg/pull/77418))

## 12.2.0 (2026-04-15)

Expand Down
1 change: 1 addition & 0 deletions packages/icons/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ To add a new icon to the library, follow these steps:
- Omit the field to keep the icon in the JS library only. It is not shipped to core.
- `true` ships the icon to core and exposes it through the SVG Icons API, which makes it selectable in the Icon block.
- `false` ships the icon to core and registers it for server-side use through `wp_get_icon()`, but hides it from the REST API and the Icon block. Use this for icons that belong to the admin UI rather than to post content, such as admin menu icons.
- `keywords` (optional): An array of lowercase search terms for the icon, such as `[ "hamburger", "navigation" ]` for Menu. Public and non-public icons alike may have them: the icons registry and the icon library match them alongside the name and label, and so does the Storybook icon library, which reads this manifest rather than keeping its own list. Terms for public icons are localized in the generated `manifest.php`.
4. **Do not edit `manifest.php`**: The `manifest.php` file is automatically generated from `manifest.json` by the build script. Do not edit it manually, as your changes will be overwritten when the build runs.

After adding your icon, run `npm run build` to generate the TypeScript files and update `manifest.php`.
Expand Down
15 changes: 14 additions & 1 deletion packages/icons/lib/generate-manifest-php.cjs
Comment thread
n8finch marked this conversation as resolved.
Resolved
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,22 @@ function generatePHPArray( manifest ) {
const publicEntry =
item.public === false ? "\n\t\t'public' => false," : '';

// Keywords are optional, and are localized like labels since they are
// matched against user-entered search terms.
const keywords = item.keywords?.length
? `\n\t\t'keywords' => array( ${ item.keywords
.map(
( keyword ) =>
`_x( '${ escapePHPString(
keyword
) }', 'icon keyword', 'gutenberg' )`
)
.join( ', ' ) } ),`
: '';

return `${ key } => array(
'label' => _x( '${ label }', 'icon label', 'gutenberg' ),
'filePath' => '${ filePath }',${ publicEntry }
'filePath' => '${ filePath }',${ publicEntry }${ keywords }
),`;
} );

Expand Down
Loading
Loading