From e42736d3bbfbea7bc56006447b955c10a91d78b9 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Mon, 30 Sep 2024 12:34:48 +0200 Subject: [PATCH] Fix regression handling selection on multi-indexed Tabulator data (#7336) --- panel/tests/ui/widgets/test_tabulator.py | 17 +++++++++++++++++ panel/tests/widgets/test_tables.py | 8 ++++---- panel/widgets/tables.py | 7 +++---- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/panel/tests/ui/widgets/test_tabulator.py b/panel/tests/ui/widgets/test_tabulator.py index fc69cbf8cc..403379be83 100644 --- a/panel/tests/ui/widgets/test_tabulator.py +++ b/panel/tests/ui/widgets/test_tabulator.py @@ -1543,6 +1543,23 @@ def test_tabulator_selection_selectable_rows(page, df_mixed): assert widget.selected_dataframe.equals(expected_selected) +@pytest.mark.parametrize('pagination', ['remote', 'local', None]) +def test_tabulator_selection_on_multi_index(page, pagination): + index = pd.MultiIndex.from_tuples([(i, j) for i in range(10) for j in range(10)], names=["A", "B"]) + df = pd.DataFrame(index=index, data={"C": range(100)}) + + widget = Tabulator(df, pagination=pagination, selectable='checkbox') + + serve_component(page, widget) + + checkboxes = page.locator('input[type="checkbox"]') + expect(checkboxes).to_have_count(widget.initial_page_size+1 if pagination else len(df)+1) + checkboxes.nth(1).check() + checkboxes.nth(17).check() + + wait_until(lambda: widget.selection == [0, 16], page) + + def test_tabulator_row_content(page, df_mixed): widget = Tabulator(df_mixed, row_content=lambda i: f"{i['str']}-row-content") diff --git a/panel/tests/widgets/test_tables.py b/panel/tests/widgets/test_tables.py index 661b7f382f..5d82e41070 100644 --- a/panel/tests/widgets/test_tables.py +++ b/panel/tests/widgets/test_tables.py @@ -1817,8 +1817,8 @@ def test_tabulator_constant_scalar_filter_on_index_client_side(document, comm, p @pytest.mark.parametrize('pagination', ['local', 'remote', None]) def test_tabulator_constant_scalar_filter_on_multi_index_client_side(document, comm, pagination): - df = makeMixedDataFrame() - table = Tabulator(df.set_index(['A', 'C']), pagination=pagination) + df = makeMixedDataFrame().set_index(['A', 'C']) + table = Tabulator(df, pagination=pagination) table.filters = [ {'field': 'A', 'sorter': 'number', 'type': '=', 'value': 2}, @@ -1831,9 +1831,9 @@ def test_tabulator_constant_scalar_filter_on_multi_index_client_side(document, c 'B': np.array([0.]), 'D': np.array(['2009-01-05T00:00:00.000000000'], dtype='datetime64[ns]') - }) + }).set_index(['A', 'C']) pd.testing.assert_frame_equal( - table._processed, expected if pagination == 'remote' else df[['A', 'C', 'B', 'D']] + table._processed, expected if pagination == 'remote' else df ) @pytest.mark.parametrize('pagination', ['local', 'remote', None]) diff --git a/panel/widgets/tables.py b/panel/widgets/tables.py index dad733ae6c..4f1f5748f9 100644 --- a/panel/widgets/tables.py +++ b/panel/widgets/tables.py @@ -642,9 +642,7 @@ def _process_df_and_convert_to_cds(self, df: pd.DataFrame) -> tuple[pd.DataFrame else: default_index = ('level_0' if 'index' in df.columns else 'index') indexes = [df.index.name or default_index] - if len(indexes) > 1: - df = df.reset_index() - data = ColumnDataSource.from_df(df) + data = ColumnDataSource.from_df(df.reset_index() if len(indexes) > 1 else df) if not self.show_index and len(indexes) > 1: data = {k: v for k, v in data.items() if k not in indexes} return df, {k if isinstance(k, str) else str(k): self._process_column(v) for k, v in data.items()} @@ -1440,7 +1438,6 @@ def _get_data(self): indexes = [df.index.name or default_index] if len(indexes) > 1: page_df = page_df.reset_index() - df = df.reset_index() data = ColumnDataSource.from_df(page_df).items() return df, {k if isinstance(k, str) else str(k): v for k, v in data} @@ -1448,6 +1445,8 @@ def _get_style_data(self, recompute=True): if self.value is None or self.style is None or self.value.empty: return {} df = self._processed + if len(self.indexes) > 1: + df = df.reset_index() if recompute: try: self._computed_styler = styler = df.style