diff --git a/.circleci/config.yml b/.circleci/config.yml index eb4ec7c5c..f01bb27a0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,7 +27,7 @@ jobs: pip install -r dev-requirements.txt --quiet git clone --depth 1 git@github.com:plotly/dash.git dash-main pip install -e ./dash-main[dev,testing] --quiet - cd dash-main/dash-renderer && npm run build && pip install -e . && cd ./../.. + cd dash-main/dash-renderer && npm ci && npm run build && pip install -e . && cd ./../.. - run: name: Build @@ -215,7 +215,7 @@ jobs: . venv/bin/activate git clone --depth 1 git@github.com:plotly/dash.git dash-main pip install -e ./dash-main[dev,testing] --quiet - cd dash-main/dash-renderer && npm run build && pip install -e . && cd ../.. + cd dash-main/dash-renderer && npm ci && npm run build && pip install -e . && cd ../.. - run: name: Install test requirements diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fb424ef1..dbaec3e13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - [#724](https://github.com/plotly/dash-table/pull/724) Fix `active_cell` docstring: clarify optional nature of the `row_id` nested prop - [#732](https://github.com/plotly/dash-table/pull/732) Fix a bug where opening a dropdown scrolled the table down its last row - [#731](https://github.com/plotly/dash-table/pull/731) Fix a bug where `data=None` and `columns=None` caused the table to throw an error +- [#766](https://github.com/plotly/dash-table/pull/766) Sanitize table `id` for stylesheet injection (fixes usage with Pattern-Matching callbacks) ## Changed - [#758](https://github.com/plotly/dash-table/pull/758) Improve error message for invalid filter queries diff --git a/package-lock.json b/package-lock.json index 171f57624..f873d802a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "dash-table", - "version": "4.6.1", + "version": "4.6.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -8946,6 +8946,12 @@ "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", "dev": true }, + "css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=", + "dev": true + }, "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", diff --git a/package.json b/package.json index ac5b08a9e..10e6f4246 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "@types/react-select": "^3.0.10", "babel-loader": "^8.0.6", "css-loader": "^3.4.2", + "css.escape": "^1.5.1", "cypress": "^3.8.1", "d3-format": "^1.4.3", "es-check": "^5.1.0", diff --git a/src/dash-table/components/ControlledTable/index.tsx b/src/dash-table/components/ControlledTable/index.tsx index af6d3cb3f..c6a179cab 100644 --- a/src/dash-table/components/ControlledTable/index.tsx +++ b/src/dash-table/components/ControlledTable/index.tsx @@ -53,7 +53,7 @@ const MAX_WIDTH_ITERATIONS = 30; export default class ControlledTable extends PureComponent { private readonly menuRef = React.createRef(); - private readonly stylesheet: Stylesheet = new Stylesheet(`#${this.props.id}`); + private readonly stylesheet: Stylesheet = new Stylesheet(`#${CSS.escape(this.props.id)}`); private readonly tableFn = derivedTable(() => this.props); private readonly tableFragments = derivedTableFragments(); private readonly tableStyle = derivedTableStyle(); diff --git a/src/dash-table/index.ts b/src/dash-table/index.ts index 1fd0452ae..e38d50bf3 100644 --- a/src/dash-table/index.ts +++ b/src/dash-table/index.ts @@ -1,3 +1,5 @@ +import 'css.escape'; // polyfill + import Environment from 'core/environment'; import Logger from 'core/Logger'; diff --git a/tests/visual/percy-storybook/Css.percy.tsx b/tests/visual/percy-storybook/Css.percy.tsx new file mode 100644 index 000000000..31d7ae9d3 --- /dev/null +++ b/tests/visual/percy-storybook/Css.percy.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import DataTable from 'dash-table/dash/DataTable'; + +const props = { + setProps: () => { }, + data: [ + { a: 1, b: 2, c: 3 }, + { a: 2, b: 4, c: 6 }, + { a: 3, b: 6, c: 9 } + ], + columns: [ + { id: 'a', name: 'A' }, + { id: 'b', name: 'B' }, + { id: 'c', name: 'C' } + ], + css: [ + { selector: 'td', rule: 'background-color: red !important;' } + ] +}; + +storiesOf('DashTable/CSS override', module) + .add('leading _ without letter', () => ()) + .add('leading number', () => ()) + .add('escaped characters', () => ({}'} + />)) + .add('stringified object as id', () => ());