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. 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..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,8 +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="${active_cell.column_id}"]:not(.phantom-cell)` + `td[data-dash-row="${row}"]${columnSelector(column_id)}` ) as HTMLElement; if (target) { target.focus(); @@ -1166,15 +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="${id}"]:not(.phantom-cell)` - ) - : table.querySelector( - `td[data-dash-column="${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); } 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)