Skip to content

Commit

Permalink
Block API: Add support for icons for block categories (#10651)
Browse files Browse the repository at this point in the history
  • Loading branch information
tyxla authored and youknowriad committed Oct 27, 2018
1 parent e5b9987 commit 87732a2
Show file tree
Hide file tree
Showing 11 changed files with 319 additions and 58 deletions.
6 changes: 6 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,12 @@
"markdown_source": "https://github.com/raw/WordPress/gutenberg/master/packages/components/src/icon-button/README.md",
"parent": "components"
},
{
"title": "Icon",
"slug": "icon",
"markdown_source": "https://github.com/raw/WordPress/gutenberg/master/packages/components/src/icon/README.md",
"parent": "components"
},
{
"title": "KeyboardShortcuts",
"slug": "keyboard-shortcuts",
Expand Down
6 changes: 6 additions & 0 deletions lib/client-assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -1306,26 +1306,32 @@ function gutenberg_get_block_categories( $post ) {
array(
'slug' => 'common',
'title' => __( 'Common Blocks', 'gutenberg' ),
'icon' => 'screenoptions',
),
array(
'slug' => 'formatting',
'title' => __( 'Formatting', 'gutenberg' ),
'icon' => null,
),
array(
'slug' => 'layout',
'title' => __( 'Layout Elements', 'gutenberg' ),
'icon' => null,
),
array(
'slug' => 'widgets',
'title' => __( 'Widgets', 'gutenberg' ),
'icon' => null,
),
array(
'slug' => 'embed',
'title' => __( 'Embeds', 'gutenberg' ),
'icon' => null,
),
array(
'slug' => 'reusable',
'title' => __( 'Reusable Blocks', 'gutenberg' ),
'icon' => null,
),
);

Expand Down
84 changes: 84 additions & 0 deletions packages/components/src/icon/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Icon

Allows you to render a raw icon without any initial styling or wrappers.

## Usage

#### With a Dashicon

```jsx
import { Icon } from '@wordpress/components';

const MyIcon = () => (
<Icon icon="screenoptions" />
);
```

#### With a function

```jsx
import { Icon } from '@wordpress/components';

const MyIcon = () => (
<Icon icon={ () => <svg><path d="M5 4v3h5.5v12h3V7H19V4z" /></svg> } />
);
```

#### With a Component

```jsx
import { MyIconComponent } from '../my-icon-component';
import { Icon } from '@wordpress/components';

const MyIcon = () => (
<Icon icon={ MyIconComponent } />
);
```

#### With an SVG

```jsx
import { Icon } from '@wordpress/components';

const MyIcon = () => (
<Icon icon={ <svg><path d="M5 4v3h5.5v12h3V7H19V4z" /></svg> } />
);
```

#### Specifying a className

```jsx
import { Icon } from '@wordpress/components';

const MyIcon = () => (
<Icon icon="screenoptions" className="example-class" />
);
```

## Props

The component accepts the following props:

### icon

The icon to render. Supported values are: Dashicons (specified as strings), functions, WPComponent instances and `null`.

- Type: `String|Function|WPComponent|null`
- Required: No
- Default: `null`

### size

The size (width and height) of the icon.

- Type: `Number`
- Required: No
- Default: `20` when a Dashicon is rendered, `24` for all other icons.

### className

An optional additional class name to apply to the rendered icon.

- Type: `String`
- Required: No
- Default: `null`
48 changes: 48 additions & 0 deletions packages/components/src/icon/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* WordPress dependencies
*/
import { cloneElement, createElement, Component, isValidElement } from '@wordpress/element';
import { Dashicon, SVG } from '../';

function Icon( { icon = null, size, className } ) {
let iconSize;

if ( 'string' === typeof icon ) {
// Dashicons should be 20x20 by default
iconSize = size || 20;
return <Dashicon icon={ icon } size={ iconSize } className={ className } />;
}

// Any other icons should be 24x24 by default
iconSize = size || 24;

if ( 'function' === typeof icon ) {
if ( icon.prototype instanceof Component ) {
return createElement( icon, { className, size: iconSize } );
}

return icon();
}

if ( icon && ( icon.type === 'svg' || icon.type === SVG ) ) {
const appliedProps = {
className,
width: iconSize,
height: iconSize,
...icon.props,
};

return <SVG { ...appliedProps } />;
}

if ( isValidElement( icon ) ) {
return cloneElement( icon, {
className,
size: iconSize,
} );
}

return icon;
}

export default Icon;
146 changes: 146 additions & 0 deletions packages/components/src/icon/test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/**
* External dependencies
*/
import { shallow } from 'enzyme';

/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import { Path, SVG } from '../../';

/**
* Internal dependencies
*/
import Icon from '../';

describe( 'Icon', () => {
const className = 'example-class';
const svg = <SVG><Path d="M5 4v3h5.5v12h3V7H19V4z" /></SVG>;

it( 'renders nothing when icon omitted', () => {
const wrapper = shallow( <Icon /> );

expect( wrapper.type() ).toBeNull();
} );

it( 'renders a dashicon by slug', () => {
const wrapper = shallow( <Icon icon="format-image" /> );

expect( wrapper.find( 'Dashicon' ).prop( 'icon' ) ).toBe( 'format-image' );
} );

it( 'renders a dashicon and passes the classname to it', () => {
const wrapper = shallow( <Icon icon="format-image" className={ className } /> );

expect( wrapper.find( 'Dashicon' ).prop( 'className' ) ).toBe( 'example-class' );
} );

it( 'renders a dashicon and with a default size of 20', () => {
const wrapper = shallow( <Icon icon="format-image" /> );

expect( wrapper.find( 'Dashicon' ).prop( 'size' ) ).toBe( 20 );
} );

it( 'renders a dashicon and passes the size to it', () => {
const wrapper = shallow( <Icon icon="format-image" size={ 32 } /> );

expect( wrapper.find( 'Dashicon' ).prop( 'size' ) ).toBe( 32 );
} );

it( 'renders a function', () => {
const wrapper = shallow( <Icon icon={ () => <span /> } /> );

expect( wrapper.name() ).toBe( 'span' );
} );

it( 'renders an element', () => {
const wrapper = shallow( <Icon icon={ <span /> } /> );

expect( wrapper.name() ).toBe( 'span' );
} );

it( 'renders an element and passes the classname to it', () => {
const wrapper = shallow( <Icon icon={ <span /> } className={ className } /> );

expect( wrapper.prop( 'className' ) ).toBe( 'example-class' );
} );

it( 'renders an element and passes the size to it', () => {
const wrapper = shallow( <Icon icon="format-image" size={ 32 } /> );

expect( wrapper.prop( 'size' ) ).toBe( 32 );
} );

it( 'renders an svg element', () => {
const wrapper = shallow( <Icon icon={ svg } /> );

expect( wrapper.name() ).toBe( 'SVG' );
} );

it( 'renders an svg element and passes the classname to it', () => {
const wrapper = shallow( <Icon icon={ svg } className={ className } /> );

expect( wrapper.prop( 'className' ) ).toBe( 'example-class' );
} );

it( 'renders an svg element with a default width and height of 24', () => {
const wrapper = shallow( <Icon icon={ svg } /> );

expect( wrapper.prop( 'width' ) ).toBe( 24 );
expect( wrapper.prop( 'height' ) ).toBe( 24 );
} );

it( 'renders an svg element and passes the size as its width and height', () => {
const wrapper = shallow( <Icon icon={ <SVG width={ 64 } height={ 64 }><Path d="M5 4v3h5.5v12h3V7H19V4z" /></SVG> } size={ 32 } /> );

expect( wrapper.prop( 'width' ) ).toBe( 64 );
expect( wrapper.prop( 'height' ) ).toBe( 64 );
} );

it( 'renders an svg element and does not override width and height if already specified', () => {
const wrapper = shallow( <Icon icon={ svg } size={ 32 } /> );

expect( wrapper.prop( 'width' ) ).toBe( 32 );
expect( wrapper.prop( 'height' ) ).toBe( 32 );
} );

it( 'renders a component', () => {
class MyComponent extends Component {
render() {
return <span />;
}
}
const wrapper = shallow(
<Icon icon={ MyComponent } />
);

expect( wrapper.name() ).toBe( 'MyComponent' );
} );

it( 'renders a component and passes the classname to it', () => {
class MyComponent extends Component {
render( ) {
return <span className={ this.props.className } />;
}
}
const wrapper = shallow(
<Icon icon={ MyComponent } className={ className } />
);

expect( wrapper.prop( 'className' ) ).toBe( 'example-class' );
} );

it( 'renders a component and passes the size to it', () => {
class MyComponent extends Component {
render( ) {
return <span size={ this.props.size } />;
}
}
const wrapper = shallow(
<Icon icon={ MyComponent } size={ 32 } />
);

expect( wrapper.prop( 'size' ) ).toBe( 32 );
} );
} );
1 change: 1 addition & 0 deletions packages/components/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export { default as FontSizePicker } from './font-size-picker';
export { default as FormFileUpload } from './form-file-upload';
export { default as FormToggle } from './form-toggle';
export { default as FormTokenField } from './form-token-field';
export { default as Icon } from './icon';
export { default as IconButton } from './icon-button';
export { default as KeyboardShortcuts } from './keyboard-shortcuts';
export { default as MenuGroup } from './menu-group';
Expand Down
4 changes: 2 additions & 2 deletions packages/components/src/panel/body.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { Component } from '@wordpress/element';
* Internal dependencies
*/
import Button from '../button';
import Dashicon from '../dashicon';
import Icon from '../icon';
import { G, Path, SVG } from '../primitives';

class PanelBody extends Component {
Expand Down Expand Up @@ -61,8 +61,8 @@ class PanelBody extends Component {
<G><Path d="M7.41,8.59L12,13.17l4.59-4.58L18,10l-6,6l-6-6L7.41,8.59z" /></G>
</SVG>
}
{ icon && <Dashicon icon={ icon } className="components-panel__icon" /> }
{ title }
{ icon && <Icon icon={ icon } className="components-panel__icon" size={ 20 } /> }
</Button>
</h2>
) }
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/panel/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@

.components-panel__icon {
color: $dark-gray-500;
margin: -2px 6px -2px 0;
margin: -2px 0 -2px 6px;
}

.components-panel__body-toggle-icon {
Expand Down
Loading

0 comments on commit 87732a2

Please sign in to comment.