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

[Breadcrumbs] Fix expand/collapsed Breadcrumbs via keyboard #19724

Merged
merged 11 commits into from
Feb 20, 2020
Merged
1 change: 1 addition & 0 deletions docs/pages/api/breadcrumbs.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ You can learn more about the difference by [reading this guide](/guides/minimizi
| <span class="prop-name required">children&nbsp;*</span> | <span class="prop-type">node</span> | | The breadcrumb children. |
| <span class="prop-name">classes</span> | <span class="prop-type">object</span> | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. |
| <span class="prop-name">component</span> | <span class="prop-type">elementType</span> | <span class="prop-default">'nav'</span> | The component used for the root node. Either a string to use a DOM element or a component. By default, it maps the variant to a good default headline component. |
| <span class="prop-name">expandText</span> | <span class="prop-type">string</span> | <span class="prop-default">'Show path'</span> | Override the default label for the expand button.<br>For localization purposes, you can use the provided [translations](/guides/localization/). |
| <span class="prop-name">itemsAfterCollapse</span> | <span class="prop-type">number</span> | <span class="prop-default">1</span> | If max items is exceeded, the number of items to show after the ellipsis. |
| <span class="prop-name">itemsBeforeCollapse</span> | <span class="prop-type">number</span> | <span class="prop-default">1</span> | If max items is exceeded, the number of items to show before the ellipsis. |
| <span class="prop-name">maxItems</span> | <span class="prop-type">number</span> | <span class="prop-default">8</span> | Specifies the maximum number of breadcrumbs to display. When there are more than the maximum number, only the first `itemsBeforeCollapse` and last `itemsAfterCollapse` will be shown, with an ellipsis in between. |
Expand Down
18 changes: 10 additions & 8 deletions packages/material-ui/src/Breadcrumbs/BreadcrumbCollapsed.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,16 @@ import PropTypes from 'prop-types';
import withStyles from '../styles/withStyles';
import { emphasize } from '../styles/colorManipulator';
import MoreHorizIcon from '../internal/svg-icons/MoreHoriz';
import ButtonBase from '../ButtonBase';

const styles = theme => ({
root: {
display: 'flex',
},
icon: {
width: 24,
height: 16,
marginLeft: theme.spacing(0.5),
marginRight: theme.spacing(0.5),
backgroundColor: theme.palette.grey[100],
color: theme.palette.grey[700],
borderRadius: 2,
marginLeft: theme.spacing(0.5),
marginRight: theme.spacing(0.5),
cursor: 'pointer',
'&:hover, &:focus': {
backgroundColor: theme.palette.grey[200],
Expand All @@ -25,17 +22,22 @@ const styles = theme => ({
backgroundColor: emphasize(theme.palette.grey[200], 0.12),
},
},
icon: {
width: 24,
height: 16,
},
});

/**
* @ignore - internal component.
*/
function BreadcrumbCollapsed(props) {
const { classes, ...other } = props;

oliviertassinari marked this conversation as resolved.
Show resolved Hide resolved
return (
<li className={classes.root} {...other}>
<ButtonBase component="li" className={classes.root} focusRipple {...other}>
<MoreHorizIcon className={classes.icon} />
</li>
</ButtonBase>
);
}

Expand Down
44 changes: 27 additions & 17 deletions packages/material-ui/src/Breadcrumbs/BreadcrumbCollapsed.test.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,42 @@
import React from 'react';
import { assert } from 'chai';
import { createShallow, createMount, getClasses } from '@material-ui/core/test-utils';
import { expect } from 'chai';
import { spy } from 'sinon';
import { getClasses } from '@material-ui/core/test-utils';
import BreadcrumbCollapsed from './BreadcrumbCollapsed';
import MoreHorizIcon from '../internal/svg-icons/MoreHoriz';
import { fireEvent, createClientRender } from 'test/utils/createClientRender';

describe('<BreadcrumbCollapsed />', () => {
let mount;
let shallow;
let classes;
const render = createClientRender();

before(() => {
shallow = createShallow({ dive: true });
mount = createMount({ strict: true });
classes = getClasses(<BreadcrumbCollapsed />);
});

after(() => {
mount.cleanUp();
});

it('should render an <SvgIcon>', () => {
const wrapper = shallow(<BreadcrumbCollapsed />);
it('should render an icon', () => {
const { container } = render(<BreadcrumbCollapsed />);

assert.strictEqual(wrapper.find(MoreHorizIcon).length, 1);
assert.strictEqual(wrapper.hasClass(classes.root), true);
expect(container.querySelectorAll('svg').length).to.equal(1);
expect(container.firstChild).to.have.class(classes.root);
});

it('should mount', () => {
mount(<BreadcrumbCollapsed />);
describe('prop: onKeyDown', () => {
it(`should be called on key down - Enter`, () => {
const handleClick = spy();
const { container } = render(<BreadcrumbCollapsed onClick={handleClick} />);
const expand = container.firstChild;
expand.focus();
fireEvent.keyDown(expand, { key: 'Enter' });
expect(handleClick.callCount).to.equal(1);
});

it(`should be called on key up - Space`, () => {
const handleClick = spy();
const { container } = render(<BreadcrumbCollapsed onClick={handleClick} />);
const expand = container.firstChild;
expand.focus();
fireEvent.keyUp(expand, { key: ' ' });
expect(handleClick.callCount).to.equal(1);
});
});
});
9 changes: 8 additions & 1 deletion packages/material-ui/src/Breadcrumbs/Breadcrumbs.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const Breadcrumbs = React.forwardRef(function Breadcrumbs(props, ref) {
classes,
className,
component: Component = 'nav',
expandText = 'Show path',
Copy link
Member

@oliviertassinari oliviertassinari Feb 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taken from the aria-label Google Drive uses.

itemsAfterCollapse = 1,
itemsBeforeCollapse = 1,
maxItems = 8,
Expand Down Expand Up @@ -82,7 +83,7 @@ const Breadcrumbs = React.forwardRef(function Breadcrumbs(props, ref) {

return [
...allItems.slice(0, itemsBeforeCollapse),
<BreadcrumbCollapsed key="ellipsis" onClick={handleClickExpand} />,
<BreadcrumbCollapsed aria-label={expandText} key="ellipsis" onClick={handleClickExpand} />,
...allItems.slice(allItems.length - itemsAfterCollapse, allItems.length),
];
};
Expand Down Expand Up @@ -149,6 +150,12 @@ Breadcrumbs.propTypes = {
* By default, it maps the variant to a good default headline component.
*/
component: PropTypes.elementType,
/**
* Override the default label for the expand button.
*
* For localization purposes, you can use the provided [translations](/guides/localization/).
*/
expandText: PropTypes.string,
/**
* If max items is exceeded, the number of items to show after the ellipsis.
*/
Expand Down
11 changes: 6 additions & 5 deletions packages/material-ui/src/Breadcrumbs/Breadcrumbs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,14 @@ describe('<Breadcrumbs />', () => {
);

const listitems = getAllByRole('listitem', { hidden: false });
expect(listitems).to.have.length(3);

expect(listitems).to.have.length(2);
expect(getByRole('list')).to.have.text('first//ninth');
expect(listitems[1].querySelector('[data-mui-test="MoreHorizIcon"]')).to.be.ok;
expect(getByRole('button').querySelector('[data-mui-test="MoreHorizIcon"]')).to.be.ok;
});

it('should expand when `BreadcrumbCollapsed` is clicked', () => {
const { getAllByRole } = render(
const { getAllByRole, getByRole } = render(
<Breadcrumbs>
<span>first</span>
<span>second</span>
Expand All @@ -77,9 +78,9 @@ describe('<Breadcrumbs />', () => {
</Breadcrumbs>,
);

getAllByRole('listitem', { hidden: false })[2].click();
getByRole('button').click();

expect(getAllByRole('listitem', { hidden: false })).to.have.length(3);
expect(getAllByRole('listitem', { hidden: false })).to.have.length(9);
});

describe('warnings', () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/material-ui/src/locale/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ export const fiFI = {

export const frFR = {
props: {
MuiBreadcrumbs: {
expandText: 'Montrer le chemin',
},
MuiTablePagination: {
backIconButtonText: 'Page précédente',
labelRowsPerPage: 'Lignes par page :',
Expand Down