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 2 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
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
110 changes: 81 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):
return 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,64 @@ 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

scroll_by(test, 200)

test.driver.execute_script(
"document.querySelector('#table .row-1').scrollBy(200, 0);"
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: -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_UP)

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,
)