Skip to content

Commit

Permalink
Merge pull request #2114 from benedikt-budig/fix-copy-to-clipboard-fo…
Browse files Browse the repository at this point in the history
…r-unselectable-cells

Enable copying text to clipboard for tables with `cell_selectable=False`
  • Loading branch information
alexcjohnson authored Jul 7, 2022
2 parents f698058 + 22876c6 commit 88d21cd
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
- [#2109](https://github.com/plotly/dash/pull/2109) Add `maxHeight` to Dropdown options menu.

### Fixed
- [#2114](https://github.com/plotly/dash/pull/2114) Fix bug [#1978](https://github.com/plotly/dash/issues/1978) where text could not be copied from cells in tables with `cell_selectable=False`.
- [#2102](https://github.com/plotly/dash/pull/2102) Fix bug as reported in [dash-labs #113](https://github.com/plotly/dash-labs/issues/113) where files starting with `.` were not excluded when building `dash.page_registry`.
- [#2098](https://github.com/plotly/dash/pull/2098) Accept HTTP code 400 as well as 401 for JWT expiry
- [#2097](https://github.com/plotly/dash/pull/2097) Fix bug [#2095](https://github.com/plotly/dash/issues/2095) with TypeScript compiler and `React.FC` empty valueDeclaration error & support empty props components.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -794,14 +794,17 @@ export default class ControlledTable extends PureComponent<ControlledTableProps>
include_headers_on_copy_paste
} = this.props;

TableClipboardHelper.toClipboard(
e,
selected_cells,
columns,
visibleColumns,
viewport.data,
include_headers_on_copy_paste
);
// if no cells are selected, fall back to the browser's default copy event handling
if (selected_cells.length) {
TableClipboardHelper.toClipboard(
e,
selected_cells,
columns,
visibleColumns,
viewport.data,
include_headers_on_copy_paste
);
}
this.$el.focus();
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@
height: 100%;
width: 100%;

&.unfocused::selection {
&.unfocused.selectable::selection {
background-color: transparent;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,13 @@ function getCellType(
export default (propsFn: () => ICellFactoryProps) => new Contents(propsFn);

class Contents {
private cell_selectable: boolean;
constructor(
propsFn: () => ICellFactoryProps,
private readonly handlers = derivedCellEventHandlerProps(propsFn)
) {}
) {
this.cell_selectable = propsFn().cell_selectable;
}

partialGet = memoizeOne(
(
Expand Down Expand Up @@ -167,6 +170,7 @@ class Contents {
const className = [
...(active ? ['input-active'] : []),
isFocused ? 'focused' : 'unfocused',
...(this.cell_selectable ? ['selectable'] : []),
'dash-cell-value'
].join(' ');

Expand Down
33 changes: 33 additions & 0 deletions components/dash-table/tests/selenium/test_basic_copy_paste.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ def get_app():
sort_action="native",
include_headers_on_copy_paste=True,
),
DataTable(
id="table3",
data=df[0:10],
columns=[
{"name": i, "id": i, "deletable": True} for i in rawDf.columns
],
cell_selectable=False,
sort_action="native",
),
]
)

Expand Down Expand Up @@ -301,3 +310,27 @@ def test_tbcp009_copy_9_and_10_click(test):
)

assert test.get_log_errors() == []


def test_tbcp010_copy_from_unselectable_cells_table(test):
test.start_server(get_app())

source = test.table("table3") # this table has cell_selectable=False
target = test.table("table2")

# double click cell to (natively) mark the contained text
source.cell(2, 2).double_click()
assert source.cell(2, 2).get_text() == test.get_selected_text()

# copy the source text to clipboard using CTRL+C
test.copy()

# assert the target cell value is different before paste
target.cell(1, 1).click()
assert target.cell(1, 1).get_text() != source.cell(2, 2).get_text()

# assert the target cell value has changed to the pasted value
test.paste()
assert target.cell(1, 1).get_text() == source.cell(2, 2).get_text()

assert test.get_log_errors() == []

0 comments on commit 88d21cd

Please sign in to comment.