Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block API: Add support for icons for block categories #10651

Merged
merged 17 commits into from
Oct 27, 2018
Merged
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
6 changes: 6 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,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 @@ -1286,26 +1286,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