Updating IconWithFallback to functional component, adding stories and docs (#12797)
parent
e7b82915fa
commit
07f16d37be
@ -1,8 +1,9 @@ |
|||||||
.icon-border { |
.icon-border { |
||||||
border-radius: 50%; |
border-radius: 50%; |
||||||
border: 1px solid #f2f3f4; |
border: 1px solid $ui-1; |
||||||
background: #ececf0; |
background: $ui-1; |
||||||
display: flex; |
display: flex; |
||||||
justify-content: center; |
justify-content: center; |
||||||
align-items: center; |
align-items: center; |
||||||
|
overflow: hidden; |
||||||
} |
} |
||||||
|
@ -0,0 +1,27 @@ |
|||||||
|
import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; |
||||||
|
|
||||||
|
import IconWithFallback from '.'; |
||||||
|
|
||||||
|
# IconWithFallback |
||||||
|
|
||||||
|
Icon component that takes an image src and uses `onError` to fallback to the first letter of the icon `name` |
||||||
|
|
||||||
|
<Canvas> |
||||||
|
<Story id="ui-components-ui-icon-with-fallback-icon-with-fallback-stories-js--default-story" /> |
||||||
|
</Canvas> |
||||||
|
|
||||||
|
## Component API |
||||||
|
|
||||||
|
<ArgsTable of={IconWithFallback} /> |
||||||
|
|
||||||
|
## Usage |
||||||
|
|
||||||
|
The following describes the props and example usage for this component. |
||||||
|
|
||||||
|
### Fallback |
||||||
|
|
||||||
|
If the image src errors `onError` the image tag will be replace with a span and the first letter of the `name` prop |
||||||
|
|
||||||
|
<Canvas> |
||||||
|
<Story id="ui-components-ui-icon-with-fallback-icon-with-fallback-stories-js--fallback" /> |
||||||
|
</Canvas> |
@ -1,46 +1,61 @@ |
|||||||
import React, { PureComponent } from 'react'; |
import React, { useState } from 'react'; |
||||||
import PropTypes from 'prop-types'; |
import PropTypes from 'prop-types'; |
||||||
import classnames from 'classnames'; |
import classnames from 'classnames'; |
||||||
|
|
||||||
export default class IconWithFallback extends PureComponent { |
const IconWithFallback = ({ |
||||||
static propTypes = { |
name = '', |
||||||
icon: PropTypes.string, |
icon = null, |
||||||
name: PropTypes.string, |
size, |
||||||
size: PropTypes.number, |
className, |
||||||
className: PropTypes.string, |
fallbackClassName, |
||||||
fallbackClassName: PropTypes.string, |
...props |
||||||
}; |
}) => { |
||||||
|
const [iconError, setIconError] = useState(false); |
||||||
static defaultProps = { |
const style = size ? { height: `${size}px`, width: `${size}px` } : {}; |
||||||
name: '', |
|
||||||
icon: null, |
|
||||||
}; |
|
||||||
|
|
||||||
state = { |
const handleOnError = () => { |
||||||
iconError: false, |
setIconError(true); |
||||||
}; |
}; |
||||||
|
|
||||||
render() { |
return !iconError && icon ? ( |
||||||
const { icon, name, size, className, fallbackClassName } = this.props; |
|
||||||
const style = size ? { height: `${size}px`, width: `${size}px` } : {}; |
|
||||||
|
|
||||||
return !this.state.iconError && icon ? ( |
|
||||||
<img |
<img |
||||||
onError={() => this.setState({ iconError: true })} |
onError={handleOnError} |
||||||
src={icon} |
src={icon} |
||||||
style={style} |
style={style} |
||||||
className={className} |
className={className} |
||||||
alt="" |
alt={name.length ? name : 'icon'} |
||||||
|
{...props} |
||||||
/> |
/> |
||||||
) : ( |
) : ( |
||||||
<i |
<span |
||||||
className={classnames( |
className={classnames('icon-with-fallback__fallback', fallbackClassName)} |
||||||
'icon-with-fallback__fallback', |
|
||||||
fallbackClassName, |
|
||||||
)} |
|
||||||
> |
> |
||||||
{name.length ? name.charAt(0).toUpperCase() : ''} |
{name.length ? name.charAt(0).toUpperCase() : ''} |
||||||
</i> |
</span> |
||||||
); |
); |
||||||
} |
}; |
||||||
} |
|
||||||
|
IconWithFallback.propTypes = { |
||||||
|
/** |
||||||
|
* The img src of the icon |
||||||
|
*/ |
||||||
|
icon: PropTypes.string, |
||||||
|
/** |
||||||
|
* The name of the icon also used for the alt attribute of the image |
||||||
|
*/ |
||||||
|
name: PropTypes.string, |
||||||
|
/** |
||||||
|
* The size of the icon. Recommended sizes adhere to 8px grid: 16, 24, 32, 40 |
||||||
|
*/ |
||||||
|
size: PropTypes.number, |
||||||
|
/** |
||||||
|
* className to apply to the image tag |
||||||
|
*/ |
||||||
|
className: PropTypes.string, |
||||||
|
/** |
||||||
|
* Additional className to apply to the fallback span tag |
||||||
|
*/ |
||||||
|
fallbackClassName: PropTypes.string, |
||||||
|
}; |
||||||
|
|
||||||
|
export default IconWithFallback; |
||||||
|
@ -1,5 +1,5 @@ |
|||||||
.icon-with-fallback { |
.icon-with-fallback { |
||||||
&__fallback { |
&__fallback { |
||||||
color: black; |
color: $ui-black; |
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -0,0 +1,49 @@ |
|||||||
|
import React from 'react'; |
||||||
|
|
||||||
|
import README from './README.mdx'; |
||||||
|
import IconWithFallback from '.'; |
||||||
|
|
||||||
|
export default { |
||||||
|
title: 'Components/UI/IconWithFallback', |
||||||
|
id: __filename, |
||||||
|
component: IconWithFallback, |
||||||
|
parameters: { |
||||||
|
docs: { |
||||||
|
page: README, |
||||||
|
}, |
||||||
|
}, |
||||||
|
argTypes: { |
||||||
|
icon: { |
||||||
|
control: 'text', |
||||||
|
}, |
||||||
|
name: { |
||||||
|
control: 'text', |
||||||
|
}, |
||||||
|
size: { |
||||||
|
control: 'number', |
||||||
|
}, |
||||||
|
className: { |
||||||
|
control: 'text', |
||||||
|
}, |
||||||
|
fallbackClassName: { |
||||||
|
control: 'text', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
export const DefaultStory = (args) => <IconWithFallback {...args} />; |
||||||
|
|
||||||
|
DefaultStory.storyName = 'Default'; |
||||||
|
|
||||||
|
DefaultStory.args = { |
||||||
|
name: 'ast', |
||||||
|
icon: './AST.png', |
||||||
|
size: 24, |
||||||
|
}; |
||||||
|
|
||||||
|
export const Fallback = (args) => <IconWithFallback {...args} />; |
||||||
|
|
||||||
|
Fallback.args = { |
||||||
|
name: 'ast', |
||||||
|
size: 24, |
||||||
|
}; |
@ -0,0 +1,39 @@ |
|||||||
|
import * as React from 'react'; |
||||||
|
import { render } from '@testing-library/react'; |
||||||
|
import IconWithFallback from '.'; |
||||||
|
|
||||||
|
describe('IconWithFallback', () => { |
||||||
|
const args = { |
||||||
|
name: 'Snap name', |
||||||
|
icon: './AST.png', |
||||||
|
className: 'classname-test', |
||||||
|
fallbackClassName: 'fallback-classname-test', |
||||||
|
}; |
||||||
|
|
||||||
|
it('should render without crashing', () => { |
||||||
|
const { container } = render(<IconWithFallback />); |
||||||
|
expect(container.querySelector('span')).toBeDefined(); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should render an icon image', () => { |
||||||
|
const { getByAltText } = render(<IconWithFallback {...args} />); |
||||||
|
const image = getByAltText(args.name); |
||||||
|
expect(image).toBeDefined(); |
||||||
|
expect(image).toHaveAttribute('src', args.icon); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should render with a fallback letter from the name prop', () => { |
||||||
|
const { getByText } = render(<IconWithFallback {...args} icon="" />); |
||||||
|
expect(getByText('S')).toBeDefined(); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should render with a classname', () => { |
||||||
|
const { getByAltText } = render(<IconWithFallback {...args} />); |
||||||
|
expect(getByAltText(args.name)).toHaveClass(args.className); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should render with a fallback classname', () => { |
||||||
|
const { getByText } = render(<IconWithFallback {...args} icon="" />); |
||||||
|
expect(getByText('S')).toHaveClass(args.fallbackClassName); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,27 @@ |
|||||||
|
import { Story, Canvas, ArgsTable } from '@storybook/addon-docs'; |
||||||
|
|
||||||
|
import SiteIcon from '.'; |
||||||
|
|
||||||
|
# SiteIcon |
||||||
|
|
||||||
|
SiteIcon uses the `IconBorder` and `IconWithFallback` components to create an icon within a gray ellipse |
||||||
|
|
||||||
|
<Canvas> |
||||||
|
<Story id="ui-components-ui-site-icon-site-icon-stories-js--default-story" /> |
||||||
|
</Canvas> |
||||||
|
|
||||||
|
## Component API |
||||||
|
|
||||||
|
<ArgsTable of={SiteIcon} /> |
||||||
|
|
||||||
|
## Usage |
||||||
|
|
||||||
|
The following describes the props and example usage for this component. |
||||||
|
|
||||||
|
### Fallback |
||||||
|
|
||||||
|
`SiteIcon` wraps the `IconWithFallback` component which has a fallback `onError` and will display the first letter of the `name` prop |
||||||
|
|
||||||
|
<Canvas> |
||||||
|
<Story id="ui-components-ui-site-icon-site-icon-stories-js--fallback" /> |
||||||
|
</Canvas> |
@ -0,0 +1,43 @@ |
|||||||
|
import React from 'react'; |
||||||
|
|
||||||
|
import README from './README.mdx'; |
||||||
|
import SiteIcon from '.'; |
||||||
|
|
||||||
|
export default { |
||||||
|
title: 'Components/UI/SiteIcon', |
||||||
|
id: __filename, |
||||||
|
component: SiteIcon, |
||||||
|
parameters: { |
||||||
|
docs: { |
||||||
|
page: README, |
||||||
|
}, |
||||||
|
}, |
||||||
|
argTypes: { |
||||||
|
icon: { |
||||||
|
control: 'text', |
||||||
|
}, |
||||||
|
name: { |
||||||
|
control: 'text', |
||||||
|
}, |
||||||
|
size: { |
||||||
|
control: 'number', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
export const DefaultStory = (args) => <SiteIcon {...args} />; |
||||||
|
|
||||||
|
DefaultStory.storyName = 'Default'; |
||||||
|
|
||||||
|
DefaultStory.args = { |
||||||
|
name: 'eth', |
||||||
|
icon: './images/eth_logo.svg', |
||||||
|
size: 24, |
||||||
|
}; |
||||||
|
|
||||||
|
export const Fallback = (args) => <SiteIcon {...args} />; |
||||||
|
|
||||||
|
Fallback.args = { |
||||||
|
name: 'eth', |
||||||
|
size: 24, |
||||||
|
}; |
@ -0,0 +1,24 @@ |
|||||||
|
import * as React from 'react'; |
||||||
|
import { render } from '@testing-library/react'; |
||||||
|
import SiteIcon from '.'; |
||||||
|
|
||||||
|
describe('SiteIcon', () => { |
||||||
|
const args = { |
||||||
|
name: 'Snap name', |
||||||
|
icon: './images/eth_logo.svg', |
||||||
|
className: 'classname-test', |
||||||
|
fallbackClassName: 'fallback-classname-test', |
||||||
|
}; |
||||||
|
|
||||||
|
it('should render without crashing', () => { |
||||||
|
const { getByText } = render(<SiteIcon name={args.name} />); |
||||||
|
expect(getByText('S')).toBeDefined(); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should render an icon image', () => { |
||||||
|
const { getByAltText } = render(<SiteIcon {...args} />); |
||||||
|
const image = getByAltText(args.name); |
||||||
|
expect(image).toBeDefined(); |
||||||
|
expect(image).toHaveAttribute('src', args.icon); |
||||||
|
}); |
||||||
|
}); |
Loading…
Reference in new issue