Skip to content

Commit

Permalink
feat(standalone-icon): Add interactivity to icon
Browse files Browse the repository at this point in the history
  • Loading branch information
lzcabrera committed Oct 19, 2017
1 parent 6b50b5b commit 6c60974
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 25 deletions.
40 changes: 37 additions & 3 deletions src/components/Icons/StandaloneIcon/StandaloneIcon.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,40 @@
import React from 'react'
import PropTypes from 'prop-types'

import safeRest from '../../../utils/safeRest'

import Icon from '../Icon/Icon'

import styles from './StandaloneIcon.modules.scss'

/**
* An icon that has meaning within the context of the page, which should be communicated to screen readers.
*
* <span class="docs--badge__new">new!</span> <span class="docs--badge__version">v0.24.0</span>
*/
const StandaloneIcon = ({ symbol, variant, size, a11yText, ...rest }) => (
<Icon {...rest} symbol={symbol} variant={variant} size={size} aria-label={a11yText} />
)
class StandaloneIcon extends React.Component {
onClick = event => {
const {onClick} = this.props

if (onClick) {
onClick(event)
}
}

render() {
const {symbol, variant, size, interactive, onClick, a11yText, ...rest} = this.props

if (interactive) {
return (
<button {...safeRest(rest)} className={styles.interactive} aria-label={a11yText}>
<Icon symbol={symbol} variant={variant} size={size} />
</button>
)
}

return <Icon {...rest} symbol={symbol} variant={variant} size={size} aria-label={a11yText} />
}
}

StandaloneIcon.propTypes = {
/**
Expand Down Expand Up @@ -40,6 +64,14 @@ StandaloneIcon.propTypes = {
* The icon size in pixels.
*/
size: PropTypes.oneOf([16, 24, 48]),
/**
* Make the icon interactive
*/
interactive: PropTypes.bool,
/**
* A callback function to be invoked when the interactive icon is clicked.
*/
onClick: PropTypes.func,
/**
* A description of the icon for screen readers.
*/
Expand All @@ -49,6 +81,8 @@ StandaloneIcon.propTypes = {
StandaloneIcon.defaultProps = {
variant: undefined,
size: 24,
interactive: false,
onClick: undefined,
}

export default StandaloneIcon
4 changes: 4 additions & 0 deletions src/components/Icons/StandaloneIcon/StandaloneIcon.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ Standalone icons can also be interactive elements. This example shows the prefer
</button>
```

```
<StandaloneIcon symbol="spyglass" size={48} a11yText="Search this site." interactive />
```

This will be added as a built in feature in the future, until then follow the above example.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.interactive {
appearance: none;
background: none;
border: 0;
cursor: pointer;
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,84 @@
import React from 'react'
import { shallow, render } from 'enzyme'
import {shallow, render} from 'enzyme'
import toJson from 'enzyme-to-json'

import StandaloneIcon from '../StandaloneIcon'

describe('StandaloneIcon', () => {
const defaultProps = {
symbol: 'spyglass',
a11yText: 'Some text for the screen readers.'
}
const doShallow = (props = {}) => shallow(<StandaloneIcon {...defaultProps} {...props} />)

it('renders', () => {
const icon = render(<StandaloneIcon symbol="spyglass" a11yText="Some screen reader text." />)
expect(toJson(icon)).toMatchSnapshot()
})
describe('Plain StandaloneIcon', () => {
const defaultProps = {
symbol: 'spyglass',
a11yText: 'Some text for the screen readers.',
}
const doShallow = (props = {}) => shallow(<StandaloneIcon {...defaultProps} {...props} />)

it('renders', () => {
const icon = render(<StandaloneIcon symbol="spyglass" a11yText="Some screen reader text." />)
expect(toJson(icon)).toMatchSnapshot()
})

it('passes attributes to the Icon component', () => {
const icon = doShallow({symbol: 'checkmark', variant: 'secondary', size: 16, id: 'the-icon'})

it('passes attributes to the Icon component', () => {
const icon = doShallow({ symbol: 'checkmark', variant: 'secondary', size: 16, id: 'the-icon' })
expect(icon).toHaveProp('symbol', 'checkmark')
expect(icon).toHaveProp('variant', 'secondary')
expect(icon).toHaveProp('size', 16)
expect(icon).toHaveProp('id', 'the-icon')
})

expect(icon).toHaveProp('symbol', 'checkmark')
expect(icon).toHaveProp('variant', 'secondary')
expect(icon).toHaveProp('size', 16)
expect(icon).toHaveProp('id', 'the-icon')
it('is accessible for screen readers', () => {
const icon = doShallow({a11yText: 'Some screen reader text'})

expect(icon).not.toHaveProp('aria-hidden')
expect(icon).toHaveProp('aria-label', 'Some screen reader text')
})
})

it('is accessible for screen readers', () => {
const icon = doShallow({ a11yText: 'Some screen reader text' })
describe('Interactive StandaloneIcon', () => {
const onClickMock = jest.fn()

const defaultProps = {
symbol: 'spyglass',
a11yText: 'Some text for the screen readers.',
interactive: true,
onClick: onClickMock,
}
const doShallow = (props = {}) => shallow(<StandaloneIcon {...defaultProps} {...props} />)

it('renders an HTML button tag', () => {
const interactiveIcon = doShallow({interactive: true, onClick: onClickMock})

expect(icon).not.toHaveProp('aria-hidden')
expect(icon).toHaveProp('aria-label', 'Some screen reader text')
expect(interactiveIcon).toHaveTagName('button')
})

it('the aria-label attribute is applied to the button tag', () => {})

it('passes additional attributes to the button element', () => {
const interactiveIcon = doShallow({
id: 'the-interactiveIcon',
role: 'button',
})

expect(interactiveIcon).toHaveProp('id', 'the-interactiveIcon')
expect(interactiveIcon).toHaveProp('role', 'button')
})

it('does not allow custom CSS', () => {
const interactiveIcon = doShallow({
className: 'my-custom-class',
style: {color: 'hotpink'},
})

expect(interactiveIcon).not.toHaveProp('className', 'my-custom-class')
expect(interactiveIcon).not.toHaveProp('style')
})
})
})

// Interactive
// <button onClick="" aria-label="Search this site" id="button-id">
// <StandaloneIcon symbol="spyglass" size={48}/>
// </button>
//
// Non-Interactive
// <StandaloneIcon symbol="spyglass" size={48} a11yText="Search this site" data-blah="hi" />
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`StandaloneIcon renders 1`] = `
exports[`StandaloneIcon Plain StandaloneIcon renders 1`] = `
<i
aria-label="Some screen reader text."
class="iconSpyglass size24"
Expand Down

0 comments on commit 6c60974

Please sign in to comment.