From c003bd07cf2548c1166f6be5acd3f55eb4fba376 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Sat, 12 Aug 2023 09:05:04 -0400 Subject: [PATCH 1/3] Fix #2615 - allow special characters in dash-table column IDs --- .../components/ControlledTable/index.tsx | 14 +++++++--- .../dash-table/tests/selenium/test_column.py | 27 +++++++++++++++++++ dash/_jupyter.py | 2 +- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/components/dash-table/src/dash-table/components/ControlledTable/index.tsx b/components/dash-table/src/dash-table/components/ControlledTable/index.tsx index 539a63c5a8..eb67ad248c 100644 --- a/components/dash-table/src/dash-table/components/ControlledTable/index.tsx +++ b/components/dash-table/src/dash-table/components/ControlledTable/index.tsx @@ -177,7 +177,11 @@ export default class ControlledTable extends PureComponent active.row !== active_cell?.row) ) { const target = this.$el.querySelector( - `td[data-dash-row="${active_cell.row}"][data-dash-column="${active_cell.column_id}"]:not(.phantom-cell)` + `td[data-dash-row="${ + active_cell.row + }"][data-dash-column="${CSS.escape( + active_cell.column_id + )}"]:not(.phantom-cell)` ) as HTMLElement; if (target) { target.focus(); @@ -1170,10 +1174,14 @@ export default class ControlledTable extends PureComponent ? table.querySelector( `tr:nth-of-type(${ row + 1 - }) th[data-dash-column="${id}"]:not(.phantom-cell)` + }) th[data-dash-column="${CSS.escape( + id + )}"]:not(.phantom-cell)` ) : table.querySelector( - `td[data-dash-column="${id}"][data-dash-row="${row}"]:not(.phantom-cell)` + `td[data-dash-column="${CSS.escape( + id + )}"][data-dash-row="${row}"]:not(.phantom-cell)` ); (this.refs.tooltip as TableTooltip).updateBounds(cell); diff --git a/components/dash-table/tests/selenium/test_column.py b/components/dash-table/tests/selenium/test_column.py index 3b9e305f8c..9f3df38573 100644 --- a/components/dash-table/tests/selenium/test_column.py +++ b/components/dash-table/tests/selenium/test_column.py @@ -267,3 +267,30 @@ def test_colm008_top_row_by_subset(test): for r in range(3): if target.column(c).exists(r): assert target.column(c).is_selected(r) + + +def test_colm009_newline_id(test): + app = dash.Dash(__name__) + + columns = [ + {"name": "aaabbb", "id": "aaa\nbbb"}, + {"name": "cccddd", "id": "ccc\nddd"}, + ] + data = [{columns[c]["id"]: r + (3 * c) + 1 for c in [0, 1]} for r in [0, 1, 2]] + tooltip_data = [{k: str(v * 11) for k, v in row.items()} for row in data] + + app.layout = DataTable( + id="table", columns=columns, data=data, tooltip_data=tooltip_data + ) + + test.start_server(app) + + target = test.table("table") + cell = target.cell(1, 1) + + target.is_ready() + cell.move_to() + # note first I tried tooltip.exists() and tooltip.get_text() like in ttip001 + # but that didn't work? didn't wait for it perhaps? + test.wait_for_text_to_equal(".dash-tooltip", "55") + assert test.get_log_errors() == [] diff --git a/dash/_jupyter.py b/dash/_jupyter.py index 1c1e1cd6da..4bc5d59493 100644 --- a/dash/_jupyter.py +++ b/dash/_jupyter.py @@ -403,7 +403,7 @@ def wait_for_app(): @staticmethod def _display_in_colab(dashboard_url, port, mode, width, height): # noinspection PyUnresolvedReferences - from google.colab import output # pylint: disable=E0401,C0415 + from google.colab import output # pylint: disable=E0401,E0611,C0415 if mode == "inline": output.serve_kernel_port_as_iframe(port, width=width, height=height) From bdb51ff6a1caff9b98c1ca2ce690391eea860df5 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Sat, 12 Aug 2023 09:11:52 -0400 Subject: [PATCH 2/3] changelog for dash-table column IDs with special characters --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0e189cc5c..79287952b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ This project adheres to [Semantic Versioning](https://semver.org/). ## Fixed -- [#2616](https://github.com/plotly/dash/pull/2616) Add mapping of tsconfig compiler option `moduleResolution`, fixes [#2618](https://github.com/plotly/dash/issues/2618) +- [#2619](https://github.com/plotly/dash/pull/2619) Fix for dash-table column IDs containing special characters +- [#2616](https://github.com/plotly/dash/pull/2616) Add mapping of tsconfig compiler option `moduleResolution`, fixes [#2618](https://github.com/plotly/dash/issues/2618) - [#2596](https://github.com/plotly/dash/pull/2596) Fix react-dom throwing unique key prop error for markdown table, fix [#1433](https://github.com/plotly/dash/issues/1433) - [#2589](https://github.com/plotly/dash/pull/2589) CSS for input elements not scoped to Dash application - [#2599](https://github.com/plotly/dash/pull/2599) Fix background callback cancel inputs used in multiple callbacks and mixed cancel inputs across pages. From 3391ee9910359115489b4763a59c94128a1a6089 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Sat, 12 Aug 2023 15:16:18 -0400 Subject: [PATCH 3/3] DRY column selectors --- .../components/ControlledTable/index.tsx | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/components/dash-table/src/dash-table/components/ControlledTable/index.tsx b/components/dash-table/src/dash-table/components/ControlledTable/index.tsx index eb67ad248c..eb265d1c3e 100644 --- a/components/dash-table/src/dash-table/components/ControlledTable/index.tsx +++ b/components/dash-table/src/dash-table/components/ControlledTable/index.tsx @@ -55,6 +55,9 @@ const INNER_STYLE = { minWidth: '100%' }; +const columnSelector = (column_id: string) => + `[data-dash-column="${CSS.escape(column_id)}"]:not(.phantom-cell)`; + export default class ControlledTable extends PureComponent { private readonly menuRef = React.createRef(); private readonly stylesheet: Stylesheet = new Stylesheet( @@ -176,12 +179,9 @@ export default class ControlledTable extends PureComponent (active.column_id !== active_cell?.column_id || active.row !== active_cell?.row) ) { + const {column_id, row} = active_cell; const target = this.$el.querySelector( - `td[data-dash-row="${ - active_cell.row - }"][data-dash-column="${CSS.escape( - active_cell.column_id - )}"]:not(.phantom-cell)` + `td[data-dash-row="${row}"]${columnSelector(column_id)}` ) as HTMLElement; if (target) { target.focus(); @@ -1170,19 +1170,11 @@ export default class ControlledTable extends PureComponent const {table, tooltip: t} = this.refs as {[key: string]: any}; if (t) { - const cell = header - ? table.querySelector( - `tr:nth-of-type(${ - row + 1 - }) th[data-dash-column="${CSS.escape( - id - )}"]:not(.phantom-cell)` - ) - : table.querySelector( - `td[data-dash-column="${CSS.escape( - id - )}"][data-dash-row="${row}"]:not(.phantom-cell)` - ); + const cell = table.querySelector( + header + ? `tr:nth-of-type(${row + 1}) th${columnSelector(id)}` + : `td[data-dash-row="${row}"]${columnSelector(id)}` + ); (this.refs.tooltip as TableTooltip).updateBounds(cell); }