Commit a1b6559f authored by Lyza Danger Gardner's avatar Lyza Danger Gardner

Add `IconButton` component

parent 0063b931
'use strict';
const classnames = require('classnames');
const propTypes = require('prop-types');
const { createElement } = require('preact');
const SvgIcon = require('./svg-icon');
/**
* A simple icon-only button
*/
function IconButton({
className = '',
icon,
isActive = false,
title,
onClick = () => null,
}) {
return (
<button
className={classnames('icon-button', className, {
'is-active': isActive,
})}
onClick={onClick}
aria-pressed={isActive}
title={title}
>
<SvgIcon name={icon} className="icon-button__icon" />
</button>
);
}
IconButton.propTypes = {
/** Optional additional class(es) to apply to the component element */
className: propTypes.string,
/** The name of the SVGIcon to render */
icon: propTypes.string.isRequired,
/** Is this button currently in an "active" or "on" state? */
isActive: propTypes.bool,
/** a value used for the `title` and `aria-label` attributes */
title: propTypes.string.isRequired,
/** optional callback for clicks */
onClick: propTypes.func,
};
module.exports = IconButton;
'use strict';
const { createElement } = require('preact');
const { mount } = require('enzyme');
const IconButton = require('../icon-button');
const mockImportedComponents = require('./mock-imported-components');
describe('IconButton', () => {
let fakeOnClick;
function createComponent(props = {}) {
return mount(
<IconButton
icon="fakeIcon"
isActive={false}
title="My Action"
onClick={fakeOnClick}
{...props}
/>
);
}
beforeEach(() => {
fakeOnClick = sinon.stub();
IconButton.$imports.$mock(mockImportedComponents());
});
afterEach(() => {
IconButton.$imports.$restore();
});
it('adds active className if `isActive` is `true`', () => {
const wrapper = createComponent({ isActive: true });
assert.isTrue(wrapper.find('button').hasClass('is-active'));
});
it('renders `SvgIcon` for associated icon', () => {
const wrapper = createComponent();
assert.equal(wrapper.find('SvgIcon').prop('name'), 'fakeIcon');
});
it('sets ARIA `aria-pressed` attribute if `isActive`', () => {
const wrapper = createComponent({ isActive: true });
assert.isTrue(wrapper.find('button').prop('aria-pressed'));
});
it('invokes `onClick` callback when pressed', () => {
const wrapper = createComponent();
wrapper.find('button').simulate('click');
assert.calledOnce(fakeOnClick);
});
it('adds additional class name passed in `className` prop', () => {
const wrapper = createComponent({ className: 'my-class' });
assert.isTrue(wrapper.hasClass('my-class'));
});
it('sets compact style if `useCompactStyle` is set`', () => {
const wrapper = createComponent({ useCompactStyle: true });
assert.isTrue(wrapper.find('button').hasClass('icon-button--compact'));
});
});
@use "../../mixins/buttons";
@use "../../variables" as var;
.icon-button {
@include buttons.button-base;
&.is-active {
color: var.$brand;
&:hover {
color: var.$brand;
}
}
}
@media (pointer: coarse) {
.icon-button {
min-width: var.$touch-target-size;
min-height: var.$touch-target-size;
}
}
......@@ -43,6 +43,7 @@
@use './components/group-list';
@use './components/group-list-item';
@use './components/help-panel';
@use './components/icon-button';
@use './components/logged-out-message';
@use './components/markdown-editor';
@use './components/markdown-view';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment