diff --git a/src/wp-includes/icons.php b/src/wp-includes/icons.php new file mode 100644 index 0000000000000..6036c95f9b5fc --- /dev/null +++ b/src/wp-includes/icons.php @@ -0,0 +1,81 @@ +get_registered_icon( $name ); + if ( is_null( $icon ) ) { + return ''; + } + + $svg = $icon['content']; + if ( empty( $svg ) ) { + return ''; + } + + $args = wp_parse_args( + $args, + array( + 'size' => 24, + 'class' => '', + 'label' => '', + ) + ); + + $processor = new WP_HTML_Tag_Processor( $svg ); + if ( ! $processor->next_tag( 'svg' ) ) { + return ''; + } + + if ( is_numeric( $args['size'] ) ) { + $size = absint( $args['size'] ); + $processor->set_attribute( 'width', (string) $size ); + $processor->set_attribute( 'height', (string) $size ); + } + + if ( ! empty( $args['class'] ) ) { + foreach ( preg_split( '/\s+/', $args['class'], -1, PREG_SPLIT_NO_EMPTY ) as $class_name ) { + $processor->add_class( $class_name ); + } + } + + if ( ! empty( $args['label'] ) ) { + $processor->set_attribute( 'role', 'img' ); + $processor->set_attribute( 'aria-label', $args['label'] ); + $processor->remove_attribute( 'aria-hidden' ); + $processor->remove_attribute( 'focusable' ); + } else { + $processor->set_attribute( 'aria-hidden', 'true' ); + $processor->set_attribute( 'focusable', 'false' ); + $processor->remove_attribute( 'role' ); + $processor->remove_attribute( 'aria-label' ); + } + + return $processor->get_updated_html(); +} diff --git a/src/wp-settings.php b/src/wp-settings.php index 7e951681e4d53..bb5739d867582 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -299,6 +299,7 @@ require ABSPATH . WPINC . '/class-wp-connector-registry.php'; require ABSPATH . WPINC . '/connectors.php'; require ABSPATH . WPINC . '/class-wp-icons-registry.php'; +require ABSPATH . WPINC . '/icons.php'; require ABSPATH . WPINC . '/widgets.php'; require ABSPATH . WPINC . '/class-wp-widget.php'; require ABSPATH . WPINC . '/class-wp-widget-factory.php'; diff --git a/tests/phpunit/tests/icons/wpGetIcon.php b/tests/phpunit/tests/icons/wpGetIcon.php new file mode 100644 index 0000000000000..270f94104550c --- /dev/null +++ b/tests/phpunit/tests/icons/wpGetIcon.php @@ -0,0 +1,125 @@ +assertStringStartsWith( 'assertStringContainsString( '', $output ); + } + + /** + * @ticket 64847 + */ + public function test_wp_get_icon_returns_empty_string_for_unknown_icon() { + $output = wp_get_icon( 'this-icon-does-not-exist' ); + $this->assertSame( '', $output ); + } + + /** + * @ticket 64847 + */ + public function test_wp_get_icon_default_attributes() { + $output = wp_get_icon( 'core/plus' ); + // WP_HTML_Tag_Processor lowercases attribute names. + $this->assertStringContainsString( 'viewbox="0 0 24 24"', $output ); + $this->assertStringContainsString( 'width="24"', $output ); + $this->assertStringContainsString( 'height="24"', $output ); + $this->assertStringContainsString( 'aria-hidden="true"', $output ); + $this->assertStringContainsString( 'focusable="false"', $output ); + } + + /** + * @ticket 64847 + */ + public function test_wp_get_icon_custom_size() { + $output = wp_get_icon( 'core/plus', array( 'size' => 32 ) ); + $this->assertStringContainsString( 'width="32"', $output ); + $this->assertStringContainsString( 'height="32"', $output ); + } + + /** + * @ticket 64847 + */ + public function test_wp_get_icon_size_null_leaves_dimensions_untouched() { + $output = wp_get_icon( 'core/plus', array( 'size' => null ) ); + $this->assertStringNotContainsString( 'width=', $output ); + $this->assertStringNotContainsString( 'height=', $output ); + } + + /** + * @ticket 64847 + */ + public function test_wp_get_icon_size_zero_outputs_zero_dimensions() { + $output = wp_get_icon( 'core/plus', array( 'size' => 0 ) ); + $this->assertStringContainsString( 'width="0"', $output ); + $this->assertStringContainsString( 'height="0"', $output ); + } + + /** + * @ticket 64847 + */ + public function test_wp_get_icon_custom_class() { + $output = wp_get_icon( 'core/plus', array( 'class' => 'my-button-icon' ) ); + $this->assertStringContainsString( 'class="my-button-icon"', $output ); + } + + /** + * @ticket 64847 + */ + public function test_wp_get_icon_multiple_classes() { + $output = wp_get_icon( 'core/plus', array( 'class' => 'foo bar baz' ) ); + $this->assertStringContainsString( 'class="foo bar baz"', $output ); + } + + /** + * @ticket 64847 + */ + public function test_wp_get_icon_with_label() { + $output = wp_get_icon( 'core/plus', array( 'label' => 'Add item' ) ); + $this->assertStringContainsString( 'role="img"', $output ); + $this->assertStringContainsString( 'aria-label="Add item"', $output ); + $this->assertStringNotContainsString( 'aria-hidden', $output ); + $this->assertStringNotContainsString( 'focusable', $output ); + } + + /** + * @ticket 64847 + */ + public function test_wp_get_icon_without_label_is_hidden() { + $output = wp_get_icon( 'core/plus' ); + $this->assertStringContainsString( 'aria-hidden="true"', $output ); + $this->assertStringContainsString( 'focusable="false"', $output ); + $this->assertStringNotContainsString( 'role="img"', $output ); + $this->assertStringNotContainsString( 'aria-label', $output ); + } + + /** + * @ticket 64847 + */ + public function test_wp_get_icon_contains_svg_content() { + $output = wp_get_icon( 'core/plus' ); + $this->assertStringContainsString( ' '">' ) ); + $this->assertStringNotContainsString( '