Skip to content
This repository was archived by the owner on Aug 29, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dash_table/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dash_table/demo.js

Large diffs are not rendered by default.

27 changes: 25 additions & 2 deletions src/dash-table/components/Cell/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import * as R from 'ramda';
import React, { Component, CSSProperties } from 'react';
import React, {
Component,
CSSProperties,
KeyboardEvent
} from 'react';
import Dropdown from 'react-select';

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

import {
KEY_CODES
} from 'dash-table/utils/unicode';

export default class Cell extends Component<ICellProps, ICellState> {
private static readonly dropdownAstCache = memoizerCache<[string, string | number, number], [string], SyntaxTree>(
(query: string) => new SyntaxTree(query)
Expand Down Expand Up @@ -125,6 +133,7 @@ export default class Cell extends Component<ICellProps, ICellState> {
type='text'
value={this.state.value}
onChange={this.handleChange}
onKeyDown={this.handleKeyDown}
onPaste={this.onPaste}
{...attributes}
/>);
Expand Down Expand Up @@ -227,6 +236,20 @@ export default class Cell extends Component<ICellProps, ICellState> {
this.setState({ value: e.target.value });
}

handleKeyDown = (e: KeyboardEvent) => {
if (e.keyCode !== KEY_CODES.ENTER) {
return;
}

const { onChange } = this.props;

onChange({
target: {
value: this.state.value
}
} as any);
}

handleOpenDropdown = () => {
const { dropdown, td }: { [key: string]: any } = this.refs;

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

if (!active && this.state.value !== value) {
if (!active && this.state.value !== value) {
onChange({
target: {
value: this.state.value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const KEY_CODES = {
A: 65,
X: 88,
C: 67,
V: 86,
V: 86
};

const META_KEYS = [
Expand Down Expand Up @@ -74,14 +74,14 @@ const META_KEYS = [
KEY_CODES.ESCAPE,
KEY_CODES.SHIFT,
KEY_CODES.CAPS_LOCK,
KEY_CODES.ALT,
KEY_CODES.ALT
];

const ARROW_KEYS = [
KEY_CODES.ARROW_DOWN,
KEY_CODES.ARROW_UP,
KEY_CODES.ARROW_LEFT,
KEY_CODES.ARROW_RIGHT,
KEY_CODES.ARROW_RIGHT
];

const NAVIGATION_KEYS = [...ARROW_KEYS, KEY_CODES.TAB, KEY_CODES.ENTER];
Expand All @@ -92,10 +92,10 @@ const NAVIGATION_KEYS = [...ARROW_KEYS, KEY_CODES.TAB, KEY_CODES.ENTER];
* @param {Number} keyCode
* @returns {Boolean}
*/
export function isPrintableChar(keyCode) {
export function isPrintableChar(keyCode: number) {
return (
// space
keyCode == 32 ||
keyCode === 32 ||
// 0-9
(keyCode >= 48 && keyCode <= 57) ||
// numpad
Expand All @@ -115,7 +115,7 @@ export function isPrintableChar(keyCode) {
* @param {Number} keyCode
* @returns {Boolean}
*/
export function isMetaKey(keyCode) {
export function isMetaKey(keyCode: number) {
return META_KEYS.indexOf(keyCode) !== -1;
}

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

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

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

if (window.navigator.platform.includes('Mac')) {
Expand All @@ -167,14 +167,14 @@ export function isCtrlKey(keyCode) {
* @param {Number} keyCode Key code to check.
* @returns {Boolean}
*/
export function isCtrlMetaKey(keyCode) {
export function isCtrlMetaKey(keyCode: number) {
return [
KEY_CODES.CONTROL,
KEY_CODES.COMMAND_LEFT,
KEY_CODES.COMMAND_RIGHT,
KEY_CODES.COMMAND_FIREFOX,
KEY_CODES.COMMAND_FIREFOX
].includes(keyCode);
}

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Easy switch to Typescript

16 changes: 16 additions & 0 deletions tests/e2e/cypress/integration/dash_test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import DashTable from 'cypress/DashTable';
import DOM from 'cypress/DOM';
import Key from 'cypress/Key';
import Resolve from 'cypress/Resolve';

describe('dash basic', () => {
beforeEach(() => {
Expand All @@ -12,4 +15,17 @@ describe('dash basic', () => {
cy.get('button.next-page').click();
DashTable.getCell(0, 0).within(() => cy.get('input').should('have.value', '250'));
});

// https://github.com/plotly/dash-table/issues/50
it('can edit last and update dataframe on "enter"', async () => {
DashTable.getCell(249, 0).click();

const initialValue = await Resolve(DOM.focused.then($input => {
return $input.val();
}));

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

cy.get('#container').should('have.value', `[249][0] = ${initialValue} -> abc${initialValue}`);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The BE will update the container field if 'Enter' triggered the df update as it should be.

});
});
27 changes: 26 additions & 1 deletion tests/e2e/dash/v_be_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
)
)
module_names = ['dash_table']
modules = [__import__(x) for x in module_names]
modules = [__import__(module) for module in module_names]
dash_table = modules[0]

url = (
Expand Down Expand Up @@ -85,5 +85,30 @@ def updateDataframe(virtualization_settings):
return df[start_index:end_index]


@app.callback(
Output('container', 'children'),
[
Input('table', 'dataframe'),
Input('table', 'dataframe_previous')
]
)
def findModifiedValue(dataframe, previous):
modification = 'None'

if dataframe is None or previous is None:
return modification

for (y, row) in enumerate(dataframe):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You don't need the () when unpacking.

row_prev = previous[y]

for (x, col) in enumerate(row):
if col != row_prev[x]:
modification = '[{}][{}] = {} -> {}'.format(
y, x, row_prev[x], col
)

return modification


if __name__ == '__main__':
app.run_server(port=8081, debug=False)