Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
68 changes: 68 additions & 0 deletions src/wp-includes/icons.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php
/**
* Icons API: Icon rendering helper functions.
*
* @package WordPress
* @subpackage Icons
* @since 7.1.0
*/

/**
* Returns the SVG markup for a registered icon.
*
* @since 7.1.0
*
* @param string $name The namespaced icon name (e.g. 'core/plus').
* @param array $args {
* Optional. Arguments for the icon. Default empty array.
*
* @type int $size Width and height in pixels. Default 24.
* @type string $class Additional CSS class names. Default empty string.
* @type string $label Accessible label. If provided, the SVG gets
* role="img" and aria-label. If omitted, the SVG
* gets aria-hidden="true". Default empty string.
* }
* @return string SVG markup for the icon, or empty string if not found.
*/
function wp_get_icon( $name, $args = array() ) {
$icon = WP_Icons_Registry::get_instance()->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 '';
}

$processor->set_attribute( 'width', (string) $args['size'] );
$processor->set_attribute( 'height', (string) $args['size'] );
$processor->add_class( 'wp-icon' );

if ( ! empty( $args['class'] ) ) {
$processor->add_class( $args['class'] );
}

if ( ! empty( $args['label'] ) ) {
$processor->set_attribute( 'role', 'img' );
$processor->set_attribute( 'aria-label', $args['label'] );
} else {
$processor->set_attribute( 'aria-hidden', 'true' );
}

return $processor->get_updated_html();
}
1 change: 1 addition & 0 deletions src/wp-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,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';
Expand Down
97 changes: 97 additions & 0 deletions tests/phpunit/tests/icons/wpGetIcon.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php
/**
* Unit tests covering the wp_get_icon() function.
*
* @package WordPress
* @subpackage Icons
* @since 7.1.0
*
* @group icons
*
* @covers ::wp_get_icon
*/
class Tests_Icons_WpGetIcon extends WP_UnitTestCase {

/**
* @ticket 64847
*/
public function test_wp_get_icon_returns_svg_for_known_icon() {
$output = wp_get_icon( 'core/plus' );
$this->assertStringStartsWith( '<svg ', $output );
$this->assertStringContainsString( '</svg>', $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( 'class="wp-icon"', $output );
$this->assertStringContainsString( 'aria-hidden="true"', $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_custom_class() {
$output = wp_get_icon( 'core/plus', array( 'class' => 'my-button-icon' ) );
$this->assertStringContainsString( 'class="wp-icon my-button-icon"', $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 );
}

/**
* @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->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( '<path ', $output );
}

/**
* @ticket 64847
*/
public function test_wp_get_icon_escapes_attributes() {
$output = wp_get_icon( 'core/plus', array( 'class' => '"><script>alert(1)</script>' ) );
$this->assertStringNotContainsString( '<script>', $output );
}
}
Loading