Skip to content

Commit

Permalink
141 move in cell (plotly#172)
Browse files Browse the repository at this point in the history
* Propagate to ControlledTable on navKeys

* Test if arrow keys can navigate and save cell value

* Add keyboard navigation tests for focused inputs

* Update changelog

* Add onMouseUp functionality for arrow key navigation

* Calculate derivedHandlers for onMouseUp

* Add test for caret selection on focused input

* Rebuild bundle

* Jump to next cell on enter key

* Fix focused input test by doubleclicking instead of enter

* Remove .only from copy paste test

* Remove artifact from it's tomb of copy_paste_test
  • Loading branch information
valentijnnieman committed Oct 30, 2018
1 parent 75fa7c3 commit 8fad8d6
Show file tree
Hide file tree
Showing 14 changed files with 198 additions and 31 deletions.
9 changes: 7 additions & 2 deletions packages/dash-table/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ Derived properties allow the component to expose complex state that can be usefu

## RC8 - Improve props typing

Issue: https://github.com/plotly/dash-table/issues/143
Issue: https://github.com/plotly/dash-table/issues/143

## RC9 - Sort ascending on first click

Expand All @@ -417,4 +417,9 @@ Derived properties allow the component to expose complex state that can be usefu

## RC12 - Rename selected_cell -> selected_cells

Issue: https://github.com/plotly/dash-table/issues/177
Issue: https://github.com/plotly/dash-table/issues/177

## RC13 - Allow keyboard navigation on focused input

Issue: https://github.com/plotly/dash-table/issues/141
Issue: https://github.com/plotly/dash-table/issues/143
4 changes: 2 additions & 2 deletions packages/dash-table/dash_table/bundle.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions packages/dash-table/dash_table/demo.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/dash-table/dash_table/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dash-table",
"version": "3.1.0rc12",
"version": "3.1.0rc13",
"description": "Dash table",
"main": "build/index.js",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/dash-table/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dash-table",
"version": "3.1.0rc12",
"version": "3.1.0rc13",
"description": "Dash table",
"main": "build/index.js",
"scripts": {
Expand Down
16 changes: 13 additions & 3 deletions packages/dash-table/src/dash-table/components/CellInput/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from 'dash-table/components/CellInput/props';

import {
KEY_CODES
KEY_CODES, isNavKey
} from 'dash-table/utils/unicode';
import { ColumnType } from 'dash-table/components/Table/props';
import dropdownHelper from 'dash-table/components/dropdownHelper';
Expand Down Expand Up @@ -93,6 +93,7 @@ export default class CellInput extends PureComponent<ICellProps, ICellState> {
focused,
onClick,
onDoubleClick,
onMouseUp,
onPaste
} = this.propsWithDefaults;

Expand All @@ -105,7 +106,8 @@ export default class CellInput extends PureComponent<ICellProps, ICellState> {
const attributes = {
className: classes.join(' '),
onClick: onClick,
onDoubleClick: onDoubleClick
onDoubleClick: onDoubleClick,
onMouseUp: onMouseUp
};

const readonly = (!active && this.state.value === this.props.value) || !editable;
Expand Down Expand Up @@ -166,7 +168,15 @@ export default class CellInput extends PureComponent<ICellProps, ICellState> {
}

handleKeyDown = (e: KeyboardEvent) => {
if (e.keyCode !== KEY_CODES.ENTER && e.keyCode !== KEY_CODES.TAB) {
const is_focused = this.props.focused;

if (is_focused &&
(e.keyCode !== KEY_CODES.TAB && e.keyCode !== KEY_CODES.ENTER)
) {
return;
}

if(!is_focused && !isNavKey(e.keyCode)) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface ICellHandlerProps {
onChange: (e: ChangeEvent) => void;
onClick: (e: React.MouseEvent) => void;
onDoubleClick: (e: React.MouseEvent) => void;
onMouseUp: (e: React.MouseEvent) => void;
onPaste: (e: React.ClipboardEvent<Element>) => void;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,23 +207,20 @@ export default class ControlledTable extends PureComponent<ControlledTableProps,
return;
}

if (
e.keyCode === KEY_CODES.ENTER &&
!is_focused &&
isEditable(editable, columns[active_cell[1]])
if(!is_focused &&
isNavKey(e.keyCode)
) {
setProps({ is_focused: true });
return;
this.switchCell(e);
}

if (
is_focused &&
(e.keyCode !== KEY_CODES.TAB && e.keyCode !== KEY_CODES.ENTER)
!isNavKey(e.keyCode)
) {
return;
}

if (isNavKey(e.keyCode)) {
if (e.keyCode === KEY_CODES.TAB || e.keyCode === KEY_CODES.ENTER) {
this.switchCell(e);
return;
}
Expand All @@ -236,11 +233,11 @@ export default class ControlledTable extends PureComponent<ControlledTableProps,
} else if (
// if we have any non-meta key enter editable mode

!this.props.is_focused &&
!is_focused &&
isEditable(editable, columns[active_cell[1]]) &&
!isMetaKey(e.keyCode)
) {
setProps({ is_focused: true });
// setProps({ is_focused: true });
}

return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,12 @@
position: absolute;
left: 0;
top: 0;
&.unfocused::selection {
background-color: transparent;
}
&.unfocused {
caret-color: transparent;
}
}

div.dash-cell-value {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { memoizeOneFactory } from 'core/memoizer';
import memoizerCache from 'core/memoizerCache';
import { ICellFactoryOptions } from 'dash-table/components/Table/props';
import { handleChange, handleClick, handleDoubleClick, handlePaste } from 'dash-table/handlers/cellEvents';
import { handleChange, handleClick, handleDoubleClick, handleOnMouseUp, handlePaste } from 'dash-table/handlers/cellEvents';

type CacheArgs = [Handler, number, number];
type GetterArgs = [HandlerFn, number, number];
Expand All @@ -11,6 +11,7 @@ export enum Handler {
Change = 'change',
Click = 'click',
DoubleClick = 'doubleclick',
MouseUp = 'mouseup',
Paste = 'paste'
}

Expand All @@ -32,6 +33,7 @@ const getter = (propsFn: () => ICellFactoryOptions): CacheFn => {
[Handler.Change, handleChange.bind(undefined, propsFn)],
[Handler.Click, handleClick.bind(undefined, propsFn)],
[Handler.DoubleClick, handleDoubleClick.bind(undefined, propsFn)],
[Handler.MouseUp, handleOnMouseUp.bind(undefined, propsFn)],
[Handler.Paste, handlePaste.bind(undefined, propsFn)]
]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const getter = (propsFn: () => ICellFactoryOptions): CacheFn => {
onChange: derivedHandlers(Handler.Change, rowIndex, columnIndex),
onClick: derivedHandlers(Handler.Click, rowIndex, columnIndex),
onDoubleClick: derivedHandlers(Handler.DoubleClick, rowIndex, columnIndex),
onMouseUp: derivedHandlers(Handler.MouseUp, rowIndex, columnIndex),
onPaste: derivedHandlers(Handler.Paste, rowIndex, columnIndex)
} as ICellHandlerProps;
};
Expand Down
27 changes: 21 additions & 6 deletions packages/dash-table/src/dash-table/handlers/cellEvents.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as R from 'ramda';
import { SelectedCells, ICellFactoryOptions } from 'dash-table/components/Table/props';
import isActive from 'dash-table/derived/cell/isActive';

function isCellSelected(selectedCells: SelectedCells, idx: number, i: number) {
return selectedCells && R.contains([idx, i], selectedCells);
Expand All @@ -8,19 +9,15 @@ function isCellSelected(selectedCells: SelectedCells, idx: number, i: number) {
export const handleClick = (propsFn: () => ICellFactoryOptions, idx: number, i: number, e: any) => {
const {
editable,
is_focused,
selected_cells,
setProps
} = propsFn();

const selected = isCellSelected(selected_cells, idx, i);

if (!editable) {
return;
}
if (!is_focused) {
e.preventDefault();
}

const selected = isCellSelected(selected_cells, idx, i);

// don't update if already selected
if (selected) {
Expand Down Expand Up @@ -105,6 +102,24 @@ export const handleChange = (propsFn: () => ICellFactoryOptions, idx: number, i:
setProps({
data: newData
});

};

export const handleOnMouseUp = (propsFn: () => ICellFactoryOptions, idx: number, i: number, e: any) => {
const {
active_cell,
is_focused,
} = propsFn();

const active = isActive(active_cell, idx, i);

if(!is_focused && active) {
e.preventDefault();
// We do this because mouseMove can change the selection, we don't want
// to check for all mouse movements, for performance reasons.
const input = e.target;
input.setSelectionRange(0, input.value ? input.value.length : 0);
}
};

export const handlePaste = (_propsFn: () => ICellFactoryOptions, _idx: number, _i: number, e: ClipboardEvent) => {
Expand Down
72 changes: 72 additions & 0 deletions packages/dash-table/tests/cypress/tests/server/dash_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,78 @@ describe('dash basic', () => {
});
});
});
describe('ArrowKeys navigation', () => {
describe('When active, but not focused', () => {
// https://github.com/plotly/dash-table/issues/141
it('can edit last, update data on "arrowleft", and move one cell to the left', () => {
const startingCell = [249,1]
const targetCell = [249, 0]
DashTable.getCell(startingCell[0], startingCell[1]).click();
DOM.focused.then($input => {
const initialValue = $input.val();

DOM.focused.type(`abc${Key.ArrowLeft}`);

cy.get('#container').should($container => {
expect($container.first()[0].innerText).to.equal(`[249][1] = ${initialValue} -> abc`);
});
});
DashTable.getCell(targetCell[0], targetCell[1]).should('have.class', 'focused');
});

// https://github.com/plotly/dash-table/issues/141
it('can edit last, update data on "arrowup", and move one cell up', () => {
const startingCell = [249,0]
const targetCell = [248, 0]
DashTable.getCell(startingCell[0], startingCell[1]).click();
DOM.focused.then($input => {
const initialValue = $input.val();

DOM.focused.type(`abc${Key.ArrowUp}`);

cy.get('#container').should($container => {
expect($container.first()[0].innerText).to.equal(`[249][0] = ${initialValue} -> abc`);
});
});
DashTable.getCell(targetCell[0], targetCell[1]).should('have.class', 'focused');
});

// https://github.com/plotly/dash-table/issues/141
it('can edit last, update data on "arrowright", and move one cell to the right', () => {
const startingCell = [249,0]
const targetCell = [249, 1]
DashTable.getCell(startingCell[0], startingCell[1]).click();
DOM.focused.then($input => {
const initialValue = $input.val();

DOM.focused.type(`abc${Key.ArrowRight}`);

cy.get('#container').should($container => {
expect($container.first()[0].innerText).to.equal(`[249][0] = ${initialValue} -> abc`);
});
});
DashTable.getCell(targetCell[0], targetCell[1]).should('have.class', 'focused');
});


// https://github.com/plotly/dash-table/issues/141
it('can edit last, update data on "arrowdown", and move one cell down', () => {
const startingCell = [249,0]
const targetCell = [249, 1]
DashTable.getCell(startingCell[0], startingCell[1]).click();
DOM.focused.then($input => {
const initialValue = $input.val();

DOM.focused.type(`abc${Key.ArrowRight}`);

cy.get('#container').should($container => {
expect($container.first()[0].innerText).to.equal(`[249][0] = ${initialValue} -> abc`);
});
});
DashTable.getCell(targetCell[0], targetCell[1]).should('have.class', 'focused');
});
})
})

it('can edit last and update data when clicking outside of cell', () => {
DashTable.getCell(249, 0).click();
Expand Down
Loading

0 comments on commit 8fad8d6

Please sign in to comment.