Skip to content

Commit

Permalink
Issue 803 - Fix scroll alignment on scroll/hover, edit, navigate (plo…
Browse files Browse the repository at this point in the history
  • Loading branch information
Marc-Andre-Rivet committed Jul 20, 2020
1 parent 0a23385 commit b678783
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 43 deletions.
2 changes: 1 addition & 1 deletion packages/dash-table/.Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ demo/.*\.js
demo/.*\.html
demo/.*\.css

# ignore python files/folders
# ignore Python files/folders
setup.py
usage.py
setup.py
Expand Down
4 changes: 4 additions & 0 deletions packages/dash-table/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]
### Fixed
- [#806](https://github.com/plotly/dash-table/pull/806) Fix a bug where fixed rows a misaligned after navigating or editing cells [#803](https://github.com/plotly/dash-table/issues/803)

## [4.8.1] - 2020-06-19
### Fixed
- [#798](https://github.com/plotly/dash-table/pull/798) Fix a bug where headers are not aligned with columns after an update [#797](https://github.com/plotly/dash-table/issues/797)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -385,22 +385,22 @@ export default class ControlledTable extends PureComponent<ControlledTableProps>
// Force first column containers width to match visible portion of table
r0c0.style.width = `${lastFixedTdRight}px`;
r1c0.style.width = `${lastFixedTdRight}px`;

if (!cycle) {
// Force second column containers width to match visible portion of table
const firstVisibleTd = r1c1.querySelector(`tr:first-of-type > *:nth-of-type(${fixed_columns + 1})`);
if (firstVisibleTd) {
const r1c1FragmentBounds = r1c1.getBoundingClientRect();
const firstTdBounds = firstVisibleTd.getBoundingClientRect();

const width = firstTdBounds.left - r1c1FragmentBounds.left;
r0c1.style.marginLeft = `-${width}px`;
r1c1.style.marginLeft = `-${width}px`;
}
}
}
}

// Force second column containers width to match visible portion of table
const firstVisibleTd = r1c1.querySelector(`tr:first-of-type > *:nth-of-type(${fixed_columns + 1})`);
if (firstVisibleTd) {
const r1c1FragmentBounds = r1c1.getBoundingClientRect();
const firstTdBounds = firstVisibleTd.getBoundingClientRect();

const width = firstTdBounds.left - r1c1FragmentBounds.left;
const { r1 } = this.refs as Refs;

r0c1.style.marginLeft = `-${width + r1.scrollLeft}px`;
r1c1.style.marginLeft = `-${width}px`;
}

if (!cycle) {
const currentWidth = parseInt(currentTableWidth, 10);
const nextWidth = parseInt(getComputedStyle(r1c1Table).width, 10);
Expand Down
112 changes: 83 additions & 29 deletions packages/dash-table/tests/selenium/test_scrolling.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,40 @@

import pandas as pd
import pytest
from selenium.webdriver.common.keys import Keys

df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/solar.csv")

base_props = dict(
id="table",
columns=[{"name": i, "id": i} for i in df.columns],
row_selectable="single",
row_deletable=True,
data=df.to_dict("records"),
editable=True,
fixed_rows={"headers": True, "data": 1},
style_cell=dict(width=150),
style_table=dict(width=500),
)


def get_margin(test):
return test.driver.execute_script(
"return parseFloat(getComputedStyle(document.querySelector('#table .cell-0-1')).marginLeft);"
)


def get_scroll(test):
return test.driver.execute_script(
"return document.querySelector('#table .row-1').scrollLeft;"
)


def scroll_by(test, value):
test.driver.execute_script(
"document.querySelector('#table .row-1').scrollBy({}, 0);".format(value)
)


@pytest.mark.parametrize(
"fixed_rows",
Expand All @@ -24,17 +55,6 @@
"ops", [dict(), dict(row_selectable="single", row_deletable=True)]
)
def test_scrol001_fixed_alignment(test, fixed_rows, fixed_columns, ops):
base_props = dict(
id="table",
columns=[{"name": i, "id": i} for i in df.columns],
row_selectable="single",
row_deletable=True,
data=df.to_dict("records"),
fixed_rows={"headers": True, "data": 1},
style_cell=dict(width=150),
style_table=dict(width=500),
)

props = {**base_props, **fixed_rows, **fixed_columns, **ops}

app = dash.Dash(__name__)
Expand All @@ -48,32 +68,66 @@ def test_scrol001_fixed_alignment(test, fixed_rows, fixed_columns, ops):
fixed_width = test.driver.execute_script(
"return parseFloat(getComputedStyle(document.querySelector('#table .cell-0-0')).width) || 0;"
)
margin_left = test.driver.execute_script(
"return parseFloat(getComputedStyle(document.querySelector('#table .cell-0-1')).marginLeft);"
)

assert -margin_left == fixed_width
assert -get_margin(test) == fixed_width

test.driver.execute_script(
"document.querySelector('#table .row-1').scrollBy(200, 0);"
scroll_by(test, 200)

wait.until(
lambda: -get_margin(test) == fixed_width + 200, 3,
)

scroll_by(test, -200)

wait.until(
lambda: -test.driver.execute_script(
"return parseFloat(getComputedStyle(document.querySelector('#table .cell-0-1')).marginLeft);"
)
== fixed_width + 200,
3,
lambda: -get_margin(test) == fixed_width, 3,
)

test.driver.execute_script(
"document.querySelector('#table .row-1').scrollBy(-200, 0);"

@pytest.mark.parametrize(
"fixed_rows",
[dict(fixed_rows=dict(headers=True)), dict(fixed_rows=dict(headers=True, data=1))],
)
@pytest.mark.parametrize(
"fixed_columns",
[
dict(),
dict(fixed_columns=dict(headers=True)),
dict(fixed_columns=dict(headers=True, data=1)),
],
)
@pytest.mark.parametrize(
"ops", [dict(), dict(row_selectable="single", row_deletable=True)]
)
def test_scrol002_edit_navigate(test, fixed_rows, fixed_columns, ops):
props = {**base_props, **fixed_rows, **fixed_columns, **ops}

app = dash.Dash(__name__)
app.layout = DataTable(**props)

test.start_server(app)

target = test.table("table")
assert target.is_ready()

fixed_width = test.driver.execute_script(
"return parseFloat(getComputedStyle(document.querySelector('#table .cell-0-0')).width) || 0;"
)

scroll_by(test, 200)

# alignment is ok after editing a cell
target.cell(0, 3).click()
test.send_keys("abc" + Keys.ENTER)

wait.until(lambda: target.cell(1, 3).is_selected(), 3)
wait.until(lambda: -get_margin(test) == fixed_width + get_scroll(test), 3)

# alignment is ok after navigating
test.send_keys(Keys.ARROW_DOWN)
test.send_keys(Keys.ARROW_RIGHT)

wait.until(lambda: target.cell(2, 4).is_selected(), 3)
wait.until(
lambda: -test.driver.execute_script(
"return parseFloat(getComputedStyle(document.querySelector('#table .cell-0-1')).marginLeft);"
)
== fixed_width,
3,
lambda: -get_margin(test) == fixed_width + get_scroll(test), 3,
)

0 comments on commit b678783

Please sign in to comment.