Skip to content
This repository was archived by the owner on Jun 4, 2024. It is now read-only.

Issue 803 - Fix scroll alignment on scroll/hover, edit, navigate #806

Merged
merged 6 commits into from
Jul 20, 2020
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 .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 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
26 changes: 13 additions & 13 deletions src/dash-table/components/ControlledTable/index.tsx
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 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,
)