Skip to content

Commit e090a8b

Browse files
- force value change on 'enter', not just lost focus (#52)
* - force value change on 'enter', not just lost focus - fix demo bug that merges state updates incorrectly * fix issue 50 + new test for the issue * fix pylint * revert data * pylint fix
1 parent 942cca2 commit e090a8b

File tree

6 files changed

+81
-17
lines changed

6 files changed

+81
-17
lines changed

packages/dash-table/dash_table/bundle.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/dash-table/dash_table/demo.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/dash-table/src/dash-table/components/Cell/index.tsx

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import * as R from 'ramda';
2-
import React, { Component, CSSProperties } from 'react';
2+
import React, {
3+
Component,
4+
CSSProperties,
5+
KeyboardEvent
6+
} from 'react';
37
import Dropdown from 'react-select';
48

59
import { isEqual } from 'core/comparer';
@@ -20,6 +24,10 @@ import {
2024
IConditionalStyle
2125
} from 'dash-table/components/Cell/types';
2226

27+
import {
28+
KEY_CODES
29+
} from 'dash-table/utils/unicode';
30+
2331
export default class Cell extends Component<ICellProps, ICellState> {
2432
private static readonly dropdownAstCache = memoizerCache<[string, string | number, number], [string], SyntaxTree>(
2533
(query: string) => new SyntaxTree(query)
@@ -125,6 +133,7 @@ export default class Cell extends Component<ICellProps, ICellState> {
125133
type='text'
126134
value={this.state.value}
127135
onChange={this.handleChange}
136+
onKeyDown={this.handleKeyDown}
128137
onPaste={this.onPaste}
129138
{...attributes}
130139
/>);
@@ -227,6 +236,20 @@ export default class Cell extends Component<ICellProps, ICellState> {
227236
this.setState({ value: e.target.value });
228237
}
229238

239+
handleKeyDown = (e: KeyboardEvent) => {
240+
if (e.keyCode !== KEY_CODES.ENTER) {
241+
return;
242+
}
243+
244+
const { onChange } = this.props;
245+
246+
onChange({
247+
target: {
248+
value: this.state.value
249+
}
250+
} as any);
251+
}
252+
230253
handleOpenDropdown = () => {
231254
const { dropdown, td }: { [key: string]: any } = this.refs;
232255

@@ -275,7 +298,7 @@ export default class Cell extends Component<ICellProps, ICellState> {
275298
(this.refs.td as HTMLElement).focus();
276299
}
277300

278-
if (!active && this.state.value !== value) {
301+
if (!active && this.state.value !== value) {
279302
onChange({
280303
target: {
281304
value: this.state.value

packages/dash-table/src/dash-table/utils/unicode.js renamed to packages/dash-table/src/dash-table/utils/unicode.ts

+12-12
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export const KEY_CODES = {
4343
A: 65,
4444
X: 88,
4545
C: 67,
46-
V: 86,
46+
V: 86
4747
};
4848

4949
const META_KEYS = [
@@ -74,14 +74,14 @@ const META_KEYS = [
7474
KEY_CODES.ESCAPE,
7575
KEY_CODES.SHIFT,
7676
KEY_CODES.CAPS_LOCK,
77-
KEY_CODES.ALT,
77+
KEY_CODES.ALT
7878
];
7979

8080
const ARROW_KEYS = [
8181
KEY_CODES.ARROW_DOWN,
8282
KEY_CODES.ARROW_UP,
8383
KEY_CODES.ARROW_LEFT,
84-
KEY_CODES.ARROW_RIGHT,
84+
KEY_CODES.ARROW_RIGHT
8585
];
8686

8787
const NAVIGATION_KEYS = [...ARROW_KEYS, KEY_CODES.TAB, KEY_CODES.ENTER];
@@ -92,10 +92,10 @@ const NAVIGATION_KEYS = [...ARROW_KEYS, KEY_CODES.TAB, KEY_CODES.ENTER];
9292
* @param {Number} keyCode
9393
* @returns {Boolean}
9494
*/
95-
export function isPrintableChar(keyCode) {
95+
export function isPrintableChar(keyCode: number) {
9696
return (
9797
// space
98-
keyCode == 32 ||
98+
keyCode === 32 ||
9999
// 0-9
100100
(keyCode >= 48 && keyCode <= 57) ||
101101
// numpad
@@ -115,7 +115,7 @@ export function isPrintableChar(keyCode) {
115115
* @param {Number} keyCode
116116
* @returns {Boolean}
117117
*/
118-
export function isMetaKey(keyCode) {
118+
export function isMetaKey(keyCode: number) {
119119
return META_KEYS.indexOf(keyCode) !== -1;
120120
}
121121

@@ -124,7 +124,7 @@ export function isMetaKey(keyCode) {
124124
* This doesn't mean we must navigate. Enter for example can also
125125
* bring the cell Input into focus.
126126
*/
127-
export function isNavKey(keyCode) {
127+
export function isNavKey(keyCode: number) {
128128
return NAVIGATION_KEYS.indexOf(keyCode) !== -1;
129129
}
130130

@@ -133,7 +133,7 @@ export function isNavKey(keyCode) {
133133
* This doesn't mean we must navigate. Enter for example can also
134134
* bring the cell Input into focus.
135135
*/
136-
export function isArrowKey(keyCode) {
136+
export function isArrowKey(keyCode: number) {
137137
return ARROW_KEYS.indexOf(keyCode) !== -1;
138138
}
139139

@@ -144,7 +144,7 @@ export function isArrowKey(keyCode) {
144144
* @param {Number} keyCode Key code to check.
145145
* @returns {Boolean}
146146
*/
147-
export function isCtrlKey(keyCode) {
147+
export function isCtrlKey(keyCode: number) {
148148
const keys = [];
149149

150150
if (window.navigator.platform.includes('Mac')) {
@@ -167,14 +167,14 @@ export function isCtrlKey(keyCode) {
167167
* @param {Number} keyCode Key code to check.
168168
* @returns {Boolean}
169169
*/
170-
export function isCtrlMetaKey(keyCode) {
170+
export function isCtrlMetaKey(keyCode: number) {
171171
return [
172172
KEY_CODES.CONTROL,
173173
KEY_CODES.COMMAND_LEFT,
174174
KEY_CODES.COMMAND_RIGHT,
175-
KEY_CODES.COMMAND_FIREFOX,
175+
KEY_CODES.COMMAND_FIREFOX
176176
].includes(keyCode);
177177
}
178178

179179
// catch CTRL but not right ALT (which in some systems triggers ALT+CTRL)
180-
export const isCtrlDown = e => (e.ctrlKey || e.metaKey) && !e.altKey;
180+
export const isCtrlDown = (e: KeyboardEvent) => (e.ctrlKey || e.metaKey) && !e.altKey;

packages/dash-table/tests/e2e/cypress/integration/dash_test.ts

+16
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import DashTable from 'cypress/DashTable';
2+
import DOM from 'cypress/DOM';
3+
import Key from 'cypress/Key';
4+
import Resolve from 'cypress/Resolve';
25

36
describe('dash basic', () => {
47
beforeEach(() => {
@@ -12,4 +15,17 @@ describe('dash basic', () => {
1215
cy.get('button.next-page').click();
1316
DashTable.getCell(0, 0).within(() => cy.get('input').should('have.value', '250'));
1417
});
18+
19+
// https://github.com/plotly/dash-table/issues/50
20+
it('can edit last and update dataframe on "enter"', async () => {
21+
DashTable.getCell(249, 0).click();
22+
23+
const initialValue = await Resolve(DOM.focused.then($input => {
24+
return $input.val();
25+
}));
26+
27+
DOM.focused.type(`abc${Key.Enter}`);
28+
29+
cy.get('#container').should('have.value', `[249][0] = ${initialValue} -> abc${initialValue}`);
30+
});
1531
});

packages/dash-table/tests/e2e/dash/v_be_page.py

+26-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
)
1818
)
1919
module_names = ['dash_table']
20-
modules = [__import__(x) for x in module_names]
20+
modules = [__import__(module) for module in module_names]
2121
dash_table = modules[0]
2222

2323
url = (
@@ -85,5 +85,30 @@ def updateDataframe(virtualization_settings):
8585
return df[start_index:end_index]
8686

8787

88+
@app.callback(
89+
Output('container', 'children'),
90+
[
91+
Input('table', 'dataframe'),
92+
Input('table', 'dataframe_previous')
93+
]
94+
)
95+
def findModifiedValue(dataframe, previous):
96+
modification = 'None'
97+
98+
if dataframe is None or previous is None:
99+
return modification
100+
101+
for (y, row) in enumerate(dataframe):
102+
row_prev = previous[y]
103+
104+
for (x, col) in enumerate(row):
105+
if col != row_prev[x]:
106+
modification = '[{}][{}] = {} -> {}'.format(
107+
y, x, row_prev[x], col
108+
)
109+
110+
return modification
111+
112+
88113
if __name__ == '__main__':
89114
app.run_server(port=8081, debug=False)

0 commit comments

Comments
 (0)