Skip to content

Commit

Permalink
feat(react-grid): allow to focus each column and change sorting (#448)
Browse files Browse the repository at this point in the history
  • Loading branch information
MaximKudriavtsev authored Nov 9, 2017
1 parent ef3f730 commit 0550848
Show file tree
Hide file tree
Showing 18 changed files with 377 additions and 155 deletions.
3 changes: 2 additions & 1 deletion packages/dx-react-demos/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"\\.css$": "<rootDir>/css-stub.js"
},
"setupFiles": [
"babel-polyfill"
"babel-polyfill",
"<rootDir>/setup.js"
]
},
"dependencies": {
Expand Down
3 changes: 3 additions & 0 deletions packages/dx-react-demos/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
if (typeof (window) !== 'undefined') {
window.requestAnimationFrame = () => {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,14 @@ import Demo from './demo';

describe('BS3 featured: virtual scrolling demo', () => {
let getRect;
let originalRaf;

beforeEach(() => {
getRect = jest.spyOn(Element.prototype, 'getBoundingClientRect');
originalRaf = window.requestAnimationFrame;
window.requestAnimationFrame = jest.fn().mockImplementationOnce(callback => callback());
});

afterEach(() => {
getRect.mockRestore();
window.requestAnimationFrame = originalRaf;
});

it('should work', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import { mount } from 'enzyme';
import Demo from './demo';

describe('BS3 featured: remote data demo', () => {
describe('MUI featured: remote data demo', () => {
beforeEach(() => {
window.fetch = jest.fn(() => Promise.resolve());
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,31 @@ import { ResizingControl } from './table-header-cell/resizing-control';
import { GroupingControl } from './table-header-cell/grouping-control';
import { SortingControl } from './table-header-cell/sorting-control';

const ENTER_KEY_CODE = 13;
const SPACE_KEY_CODE = 32;

export class TableHeaderCell extends React.PureComponent {
constructor(props) {
super(props);

this.state = {
dragging: false,
};

this.onCellClick = (e) => {
this.onClick = (e) => {
const { allowSorting, changeSortingDirection } = this.props;
if (!allowSorting) return;
e.stopPropagation();
const isActionKeyDown = e.keyCode === ENTER_KEY_CODE || e.keyCode === SPACE_KEY_CODE;
const isMouseClick = e.keyCode === undefined;

if (!allowSorting || !(isActionKeyDown || isMouseClick)) return;

const cancelSortingRelatedKey = e.metaKey || e.ctrlKey;
const cancel = (isMouseClick && cancelSortingRelatedKey)
|| (isActionKeyDown && cancelSortingRelatedKey);

e.preventDefault();
changeSortingDirection({
keepOther: e.shiftKey || cancelSortingRelatedKey,
cancel: cancelSortingRelatedKey,
cancel,
});
};
}
Expand All @@ -49,9 +58,10 @@ export class TableHeaderCell extends React.PureComponent {
} : {}),
...(allowSorting || allowDragging ? { cursor: 'pointer' } : null),
...(dragging || tableColumn.draft ? { opacity: 0.3 } : null),
padding: '5px',
...style,
}}
onClick={this.onCellClick}
onClick={this.onClick}
>
{allowGroupingByClick && (
<GroupingControl
Expand All @@ -66,13 +76,15 @@ export class TableHeaderCell extends React.PureComponent {
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
padding: '3px',
}}
>
{allowSorting ? (
<SortingControl
align={align}
sortingDirection={sortingDirection}
columnTitle={columnTitle}
onClick={this.onClick}
/>
) : (
columnTitle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,4 +232,66 @@ describe('TableHeaderCell', () => {
textOverflow: 'ellipsis',
});
});
describe('with keyboard navigation', () => {
const ENTER_KEY_CODE = 13;
const SPACE_KEY_CODE = 32;

it('should handle the "Enter" and "Space" keys down', () => {
const changeSortingDirection = jest.fn();
const tree = mount((
<TableHeaderCell
changeSortingDirection={changeSortingDirection}
column={{ align: 'right', title: 'test' }}
allowSorting
/>
));

const targetElement = tree.find('SortingControl');
targetElement.simulate('keydown', { keyCode: ENTER_KEY_CODE });
expect(changeSortingDirection)
.toHaveBeenCalled();

changeSortingDirection.mockClear();
targetElement.simulate('keydown', { keyCode: SPACE_KEY_CODE });
expect(changeSortingDirection)
.toHaveBeenCalled();

changeSortingDirection.mockClear();
targetElement.simulate('keydown', { keyCode: 51 });
expect(changeSortingDirection)
.not.toHaveBeenCalled();
});

it('should keep other sorting parameters on sorting change when the "Shift" key is pressed', () => {
const changeSortingDirection = jest.fn();
const tree = mount((
<TableHeaderCell
changeSortingDirection={changeSortingDirection}
column={{ align: 'right', title: 'test' }}
allowSorting
/>
));

const targetElement = tree.find('SortingControl');
targetElement.simulate('keydown', { keyCode: ENTER_KEY_CODE, shiftKey: true });
expect(changeSortingDirection)
.toHaveBeenCalledWith({ keepOther: true, cancel: undefined });
});

it('should handle the "Ctrl" key with sorting', () => {
const changeSortingDirection = jest.fn();
const tree = mount((
<TableHeaderCell
changeSortingDirection={changeSortingDirection}
column={{ align: 'right', title: 'test' }}
allowSorting
/>
));

const targetElement = tree.find('SortingControl');
targetElement.simulate('keydown', { keyCode: ENTER_KEY_CODE, ctrlKey: true });
expect(changeSortingDirection)
.toHaveBeenCalledWith({ keepOther: true, cancel: true });
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,22 @@ import PropTypes from 'prop-types';

import { SortingIndicator } from '../parts/sorting-indicator';

export const SortingControl = ({ align, sortingDirection, columnTitle }) =>
const handleMouseDown = (e) => { e.currentTarget.style.outline = 'none'; };
const handleBlur = (e) => { e.currentTarget.style.outline = ''; };

export const SortingControl = ({
align, sortingDirection, columnTitle, onClick,
}) =>
(align === 'right' ? (
<span
className={sortingDirection ? 'text-primary' : ''}
tabIndex={0} // eslint-disable-line jsx-a11y/no-noninteractive-tabindex
onMouseDown={handleMouseDown}
onBlur={handleBlur}
onKeyDown={onClick}
style={{
margin: '2px',
}}
>
<SortingIndicator
direction={sortingDirection}
Expand All @@ -18,6 +30,13 @@ export const SortingControl = ({ align, sortingDirection, columnTitle }) =>
) : (
<span
className={sortingDirection ? 'text-primary' : ''}
tabIndex={0} // eslint-disable-line jsx-a11y/no-noninteractive-tabindex
onMouseDown={handleMouseDown}
onBlur={handleBlur}
onKeyDown={onClick}
style={{
margin: '2px',
}}
>
{columnTitle}
&nbsp;
Expand All @@ -32,6 +51,7 @@ SortingControl.propTypes = {
align: PropTypes.string.isRequired,
sortingDirection: PropTypes.oneOf(['asc', 'desc']),
columnTitle: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
};

SortingControl.defaultProps = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import { mount } from 'enzyme';

import { SortingControl } from './sorting-control';

describe('with keyboard navigation', () => {
it('can get focus', () => {
const tree = mount((
<SortingControl
align="Right"
columnTitle="Test"
/>
));

expect(tree.find('span').prop('tabIndex'))
.toBe(0);
});
});
5 changes: 5 additions & 0 deletions packages/dx-react-grid-material-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
"lint": "eslint \"src/**\"",
"lint:fix": "yarn run lint -- --fix"
},
"jest": {
"setupFiles": [
"<rootDir>/setup.js"
]
},
"devDependencies": {
"@devexpress/dx-grid-core": "1.0.0-alpha.14",
"@devexpress/dx-react-core": "1.0.0-alpha.14",
Expand Down
3 changes: 3 additions & 0 deletions packages/dx-react-grid-material-ui/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
if (typeof (window) !== 'undefined') {
window.requestAnimationFrame = () => {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@ import { TableRow } from '../templates/table-row';

const defaultHeaderCellTemplate = props => <TableHeaderCell {...props} />;
const defaultHeaderRowTemplate = props => <TableRow {...props} />;
const defaultMessages = {
sortingHint: 'Sort',
};

export class TableHeaderRow extends React.PureComponent {
render() {
const { headerCellTemplate, headerRowTemplate, ...restProps } = this.props;
const {
headerCellTemplate, headerRowTemplate,
messages, ...restProps
} = this.props;

return (
<TableHeaderRowBase
Expand All @@ -22,6 +28,7 @@ export class TableHeaderRow extends React.PureComponent {
headerRowTemplate,
defaultHeaderRowTemplate,
)}
messages={{ ...defaultMessages, ...messages }}
{...restProps}
/>
);
Expand All @@ -31,9 +38,13 @@ export class TableHeaderRow extends React.PureComponent {
TableHeaderRow.propTypes = {
headerCellTemplate: PropTypes.func,
headerRowTemplate: PropTypes.func,
messages: PropTypes.shape({
sortingHint: PropTypes.string,
}),
};

TableHeaderRow.defaultProps = {
headerCellTemplate: undefined,
headerRowTemplate: undefined,
messages: {},
};
Loading

0 comments on commit 0550848

Please sign in to comment.