-
-
Notifications
You must be signed in to change notification settings - Fork 73
Regression fix multi table typing #468
Changes from all commits
b579f3d
46b8824
c28bf81
4db77d1
5cd918e
87e3f38
04ae9c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/* eslint no-magic-numbers: 0 */ | ||
import * as R from 'ramda'; | ||
import React, {Component} from 'react'; | ||
import { DataTable } from 'dash-table/index'; | ||
import Environment from 'core/environment'; | ||
import { memoizeOne } from 'core/memoizer'; | ||
import Logger from 'core/Logger'; | ||
import AppState, { AppMode } from './AppMode'; | ||
import memoizerCache from 'core/cache/memoizer'; | ||
|
||
import './style.less'; | ||
|
||
class App extends Component<any, any> { | ||
constructor(props: any) { | ||
super(props); | ||
|
||
this.state = { | ||
...AppState, | ||
temp_filtering: '' | ||
}; | ||
} | ||
|
||
renderMode() { | ||
const mode = Environment.searchParams.get('mode'); | ||
|
||
if (mode === AppMode.Filtering) { | ||
return (<div> | ||
<button | ||
className='clear-filters' | ||
onClick={() => { | ||
const tableProps = R.clone(this.state.tableProps); | ||
tableProps.filter_query = ''; | ||
|
||
this.setState({ tableProps }); | ||
}} | ||
>Clear Filter</button> | ||
<input | ||
style={{ width: '500px' }} | ||
value={this.state.temp_filtering} | ||
onChange={ | ||
e => this.setState({ temp_filtering: e.target.value }) | ||
} | ||
onBlur={e => { | ||
const tableProps = R.clone(this.state.tableProps); | ||
tableProps.filter_query = e.target.value; | ||
|
||
this.setState({ tableProps }); | ||
}} /> | ||
</div>); | ||
} else if (mode === AppMode.TaleOfTwoTables) { | ||
const props: any = {}; | ||
Object.entries(this.state.tableProps).forEach(([key, value]) => { | ||
props[key] = this.propCache.get(key)(value); | ||
}); | ||
|
||
return (<DataTable | ||
{...props} | ||
/>); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Two tables mode: shallow copy each prop & memoize -- feed the 2nd table with these new values. |
||
} | ||
} | ||
|
||
render() { | ||
return (<div> | ||
{this.renderMode()} | ||
<DataTable | ||
setProps={this.setProps()} | ||
{...this.state.tableProps} | ||
/> | ||
</div>); | ||
} | ||
|
||
private propCache = memoizerCache<[string]>()(R.clone); | ||
|
||
private setProps = memoizeOne(() => { | ||
return (newProps: any) => { | ||
Logger.debug('--->', newProps); | ||
this.setState((prevState: any) => ({ | ||
tableProps: R.merge(prevState.tableProps, newProps) | ||
})); | ||
}; | ||
}); | ||
} | ||
|
||
export default App; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,21 +8,22 @@ import Logger from 'core/Logger'; | |
|
||
import genRandomId from 'dash-table/utils/generate'; | ||
import isValidProps from './validate'; | ||
import sanitizeProps from './sanitize'; | ||
import Sanitizer from './Sanitizer'; | ||
|
||
export default class DataTable extends Component { | ||
constructor(props) { | ||
super(props); | ||
let id; | ||
this.getId = () => (id = id || genRandomId('table-')); | ||
this.sanitizer = new Sanitizer(); | ||
} | ||
|
||
render() { | ||
if (!isValidProps(this.props)) { | ||
return (<div>Invalid props combination</div>); | ||
} | ||
|
||
const sanitizedProps = sanitizeProps(this.props); | ||
const sanitizedProps = this.sanitizer.sanitize(this.props); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Each table instance has its sanitizer instance |
||
return this.props.id ? | ||
(<RealTable {...sanitizedProps} />) : | ||
(<RealTable {...sanitizedProps} id={this.getId()} />); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,43 @@ import Key from 'cypress/Key'; | |
|
||
import { AppMode, ReadWriteModes } from 'demo/AppMode'; | ||
|
||
Object.values([ | ||
...ReadWriteModes, | ||
AppMode.TaleOfTwoTables | ||
]).forEach(mode => { | ||
describe(`edit, mode=${mode}`, () => { | ||
beforeEach(() => { | ||
cy.visit(`http://localhost:8080?mode=${mode}`); | ||
DashTable.toggleScroll(false); | ||
}); | ||
|
||
// Case: "Pressing enter to confirm change does not work on the last row" | ||
// Issue: https://github.com/plotly/dash-table/issues/50 | ||
it('can edit on "enter"', () => { | ||
DashTable.getCell(0, 1).click(); | ||
// Case: 2+ tables results in infinite rendering loop b/c of shared cache | ||
// Issue: https://github.com/plotly/dash-table/pull/468 | ||
// | ||
// Artificial delay added to make sure re-rendering has time to occur. | ||
cy.wait(1000); | ||
DOM.focused.type(`abc${Key.Enter}`); | ||
DashTable.getCell(0, 1).within(() => cy.get('.dash-cell-value').should('have.html', `abc`)); | ||
}); | ||
|
||
it('can edit when clicking outside of cell', () => { | ||
DashTable.getCell(0, 1).click(); | ||
DOM.focused.type(`abc`); | ||
// Case: 2+ tables results in infinite rendering loop b/c of shared cache | ||
// Issue: https://github.com/plotly/dash-table/pull/468 | ||
// | ||
// Artificial delay added to make sure re-rendering has time to occur. | ||
cy.wait(1000); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The delay is not great but need to guarantee the table has had time to re-render if stuck in a loop. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm OK. I guess if it's an async system causing the infinite loop that makes sense. Please put a comment by each of these waits referencing this PR so we know why it's there and we don't try to optimize it away later, since presumably the test will still pass if it were removed - it just wouldn't be testing what it's supposed to be testing. |
||
DashTable.getCell(0, 0).click(); | ||
DashTable.getCell(0, 1).within(() => cy.get('.dash-cell-value').should('have.html', `abc`)); | ||
}); | ||
}); | ||
}); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Subset of Pulled out from the tests below and applied to one more flavor. |
||
Object.values(ReadWriteModes).forEach(mode => { | ||
describe(`edit, mode=${mode}`, () => { | ||
beforeEach(() => { | ||
|
@@ -106,20 +143,6 @@ Object.values(ReadWriteModes).forEach(mode => { | |
cy.get('.Select-value-label').should('have.html', expectedValue); | ||
}); | ||
}); | ||
|
||
// https://github.com/plotly/dash-table/issues/50 | ||
it('can edit on "enter"', () => { | ||
DashTable.getCell(0, 1).click(); | ||
DOM.focused.type(`abc${Key.Enter}`); | ||
DashTable.getCell(0, 1).within(() => cy.get('.dash-cell-value').should('have.html', `abc`)); | ||
}); | ||
|
||
it('can edit when clicking outside of cell', () => { | ||
DashTable.getCell(0, 1).click(); | ||
DOM.focused.type(`abc`); | ||
DashTable.getCell(0, 0).click(); | ||
DashTable.getCell(0, 1).within(() => cy.get('.dash-cell-value').should('have.html', `abc`)); | ||
}); | ||
}); | ||
}); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renaming to
.tsx
file but mostly just filling in withany
. Can improve some more later. At least now it's TS too.