diff --git a/.github/workflows/ephemeral-env.yml b/.github/workflows/ephemeral-env.yml index ae7a0d6b17977..9c9aa230c4c95 100644 --- a/.github/workflows/ephemeral-env.yml +++ b/.github/workflows/ephemeral-env.yml @@ -11,6 +11,7 @@ jobs: runs-on: ubuntu-latest outputs: slash-command: ${{ steps.eval-body.outputs.result }} + feature-flags: ${{ steps.eval-feature-flags.outputs.result }} steps: - name: Debug @@ -28,6 +29,22 @@ jobs: const result = pattern.exec(context.payload.comment.body) return result === null ? 'noop' : result[1] + - name: Eval comment body for feature flags + uses: actions/github-script@v3 + id: eval-feature-flags + with: + script: | + const pattern = /FEATURE_(\w+)=(\w+)/g; + let results = []; + [...context.payload.comment.body.matchAll(pattern)].forEach(match => { + const config = { + name: `SUPERSET_FEATURE_${match[1]}`, + value: match[2], + }; + results.push(config); + }); + return results; + - name: Limit to committers if: > steps.eval-body.outputs.result != 'noop' && @@ -100,6 +117,10 @@ jobs: container-name: superset-ci image: ${{ steps.login-ecr.outputs.registry }}/superset-ci:pr-${{ github.event.issue.number }} + - name: Update env vars in the Amazon ECS task definition + run: | + cat <<< "$(jq '.containerDefinitions[0].environment += ${{ needs.ephemeral_env_comment.outputs.feature-flags }}' < ${{ steps.task-def.outputs.task-definition }})" > ${{ steps.task-def.outputs.task-definition }} + - name: Describe ECS service id: describe-services run: | diff --git a/.github/workflows/superset-websocket.yml b/.github/workflows/superset-websocket.yml new file mode 100644 index 0000000000000..8a8dc9de2cac0 --- /dev/null +++ b/.github/workflows/superset-websocket.yml @@ -0,0 +1,33 @@ +name: WebSocket server +on: + push: + paths: + - "superset-websocket/**" + pull_request: + paths: + - "superset-websocket/**" + +jobs: + app-checks: + if: github.event.pull_request.draft == false + runs-on: ubuntu-20.04 + steps: + - name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )" + uses: actions/checkout@v2 + with: + persist-credentials: false + - name: Install dependencies + working-directory: ./superset-websocket + run: npm install + - name: lint + working-directory: ./superset-websocket + run: npm run lint + - name: prettier + working-directory: ./superset-websocket + run: npm run prettier-check + - name: unit tests + working-directory: ./superset-websocket + run: npm run test + - name: build + working-directory: ./superset-websocket + run: npm run build diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 40818ab9fe563..483e3e0ed301c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -251,9 +251,16 @@ Finally, never submit a PR that will put master branch in broken state. If the P #### Test Environments -- Members of the Apache GitHub org can launch an ephemeral test environment directly on a pull request by creating a comment containing (only) the command `/testenv up` +- Members of the Apache GitHub org can launch an ephemeral test environment directly on a pull request by creating a comment containing (only) the command `/testenv up`. + - Note that org membership must be public in order for this validation to function properly. +- Feature flags may be set for a test environment by specifying the flag name (prefixed with `FEATURE_`) and value after the command. + - Format: `/testenv up FEATURE_=true|false` + - Example: `/testenv up FEATURE_DASHBOARD_NATIVE_FILTERS=true` + - Multiple feature flags may be set in single command, separated by whitespace - A comment will be created by the workflow script with the address and login information for the ephemeral environment. - Test environments may be created once the Docker build CI workflow for the PR has completed successfully. +- Test environments do not currently update automatically when new commits are added to a pull request. +- Test environments do not currently support async workers, though this is planned. - Running test environments will be shutdown upon closing the pull request. #### Merging @@ -435,7 +442,7 @@ superset db upgrade # Create default roles and permissions superset init -# Load some data to play with +# Load some data to play with (you must create an Admin user with the username `admin` for this command to work) superset load-examples # Start the Flask dev web server from inside your virtualenv. @@ -1271,6 +1278,7 @@ The following configuration settings are available for async queries (see config - `GLOBAL_ASYNC_QUERIES_REDIS_STREAM_LIMIT_FIREHOSE` - the maximum number of events for all users (FIFO eviction) - `GLOBAL_ASYNC_QUERIES_JWT_COOKIE_NAME` - the async query feature uses a [JWT](https://tools.ietf.org/html/rfc7519) cookie for authentication, this setting is the cookie's name - `GLOBAL_ASYNC_QUERIES_JWT_COOKIE_SECURE` - JWT cookie secure option +- `GLOBAL_ASYNC_QUERIES_JWT_COOKIE_DOMAIN` - JWT cookie domain option ([see docs for set_cookie](https://tedboy.github.io/flask/interface_api.response_object.html#flask.Response.set_cookie)) - `GLOBAL_ASYNC_QUERIES_JWT_SECRET` - JWT's use a secret key to sign and validate the contents. This value should be at least 32 bytes and have sufficient randomness for proper security - `GLOBAL_ASYNC_QUERIES_TRANSPORT` - currently the only available option is (HTTP) `polling`, but support for a WebSocket will be added in future versions - `GLOBAL_ASYNC_QUERIES_POLLING_DELAY` - the time (in ms) between polling requests diff --git a/RELEASING/README.md b/RELEASING/README.md index eac5e7fd5cbed..3f5fc291a2844 100644 --- a/RELEASING/README.md +++ b/RELEASING/README.md @@ -126,6 +126,12 @@ Example: python changelog.py --previous_version 0.37 --current_version 0.38 changelog ``` +You can get a list of pull requests with labels started with blocking, risk, hold, revert and security by using the parameter `--risk`. +Example: +```bash +python changelog.py --previous_version 0.37 --current_version 0.38 changelog --access_token {GITHUB_TOKEN} --risk +``` + The script will checkout both branches and compare all the PR's, copy the output and paste it on the `CHANGELOG.md` Then, in `UPDATING.md`, a file that contains a list of notifications around diff --git a/UPDATING.md b/UPDATING.md index f8f32f7a18678..77eb720a1da8e 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -25,6 +25,8 @@ assists people when migrating to a new version. ## Next - [13772](https://github.com/apache/superset/pull/13772): Row level security (RLS) is now enabled by default. To activate the feature, please run `superset init` to expose the RLS menus to Admin users. +- [13980](https://github.com/apache/superset/pull/13980): Data health checks no longer use the metadata database as an interim cache. Though non-breaking, deployments which implement complex logic should likely memoize the callback function. Refer to documentation in the confg.py file for more detail. + ### Breaking Changes ### Potential Downtime ### Deprecations diff --git a/docs/.nvmrc b/docs/.nvmrc new file mode 100644 index 0000000000000..dae199aecb180 --- /dev/null +++ b/docs/.nvmrc @@ -0,0 +1 @@ +v12 diff --git a/docs/installation.rst b/docs/installation.rst index 9da1de85dd1ac..dcbbf88f38b3e 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -1133,6 +1133,7 @@ The following configuration settings are available for async queries (see config - ``GLOBAL_ASYNC_QUERIES_REDIS_STREAM_LIMIT_FIREHOSE`` - the maximum number of events for all users (FIFO eviction) - ``GLOBAL_ASYNC_QUERIES_JWT_COOKIE_NAME`` - the async query feature uses a `JWT `_ cookie for authentication, this setting is the cookie's name - ``GLOBAL_ASYNC_QUERIES_JWT_COOKIE_SECURE`` - JWT cookie secure option +- ``GLOBAL_ASYNC_QUERIES_JWT_COOKIE_DOMAIN`` - JWT cookie domain option (`see docs for set_cookie ` - ``GLOBAL_ASYNC_QUERIES_JWT_SECRET`` - JWT's use a secret key to sign and validate the contents. This value should be at least 32 bytes and have sufficient randomness for proper security - ``GLOBAL_ASYNC_QUERIES_TRANSPORT`` - currently the only available option is (HTTP) `polling`, but support for a WebSocket will be added in future versions - ``GLOBAL_ASYNC_QUERIES_POLLING_DELAY`` - the time (in ms) between polling requests @@ -1570,7 +1571,7 @@ You can enable or disable features with flag from ``superset_config.py``: .. code-block:: python - DEFAULT_FEATURE_FLAGS = { + FEATURE_FLAGS = { 'CLIENT_CACHE': False, 'ENABLE_EXPLORE_JSON_CSRF_PROTECTION': False, 'PRESTO_EXPAND_DATA': False, diff --git a/docs/package-lock.json b/docs/package-lock.json index 31b38f17ad1c1..ae9e79baca62a 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -8696,39 +8696,6 @@ "once": "^1.4.0" } }, - "engine.io": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz", - "integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==", - "requires": { - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "0.3.1", - "debug": "~4.1.0", - "engine.io-parser": "~2.2.0", - "ws": "^7.1.2" - }, - "dependencies": { - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ws": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", - "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" - } - } - }, "engine.io-client": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.4.tgz", @@ -22587,18 +22554,23 @@ } }, "socket.io": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz", - "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.4.1.tgz", + "integrity": "sha512-Si18v0mMXGAqLqCVpTxBa8MGqriHGQh8ccEOhmsmNS3thNCGBwO8WGrwMibANsWtQQ5NStdZwHqZR3naJVFc3w==", "requires": { "debug": "~4.1.0", - "engine.io": "~3.4.0", + "engine.io": "~3.5.0", "has-binary2": "~1.0.2", "socket.io-adapter": "~1.1.0", - "socket.io-client": "2.3.0", + "socket.io-client": "2.4.0", "socket.io-parser": "~3.4.0" }, "dependencies": { + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + }, "debug": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", @@ -22606,6 +22578,115 @@ "requires": { "ms": "^2.1.1" } + }, + "engine.io": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.5.0.tgz", + "integrity": "sha512-21HlvPUKaitDGE4GXNtQ7PLP0Sz4aWLddMPw2VTyFz1FVZqu/kZsJUO8WNpKuE/OCL7nkfRaOui2ZCJloGznGA==", + "requires": { + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "debug": "~4.1.0", + "engine.io-parser": "~2.2.0", + "ws": "~7.4.2" + } + }, + "engine.io-client": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.5.1.tgz", + "integrity": "sha512-oVu9kBkGbcggulyVF0kz6BV3ganqUeqXvD79WOFKa+11oK692w1NyFkuEj4xrkFRpZhn92QOqTk4RQq5LiBXbQ==", + "requires": { + "component-emitter": "~1.3.0", + "component-inherit": "0.0.3", + "debug": "~3.1.0", + "engine.io-parser": "~2.2.0", + "has-cors": "1.1.0", + "indexof": "0.0.1", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "ws": "~7.4.2", + "xmlhttprequest-ssl": "~1.5.4", + "yeast": "0.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "isarray": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", + "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" + }, + "parseqs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz", + "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==" + }, + "parseuri": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz", + "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==" + }, + "socket.io-client": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.4.0.tgz", + "integrity": "sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ==", + "requires": { + "backo2": "1.0.2", + "component-bind": "1.0.0", + "component-emitter": "~1.3.0", + "debug": "~3.1.0", + "engine.io-client": "~3.5.0", + "has-binary2": "~1.0.2", + "indexof": "0.0.1", + "parseqs": "0.0.6", + "parseuri": "0.0.6", + "socket.io-parser": "~3.3.0", + "to-array": "0.1.4" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "socket.io-parser": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.2.tgz", + "integrity": "sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg==", + "requires": { + "component-emitter": "~1.3.0", + "debug": "~3.1.0", + "isarray": "2.0.1" + } + } + } + }, + "ws": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz", + "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==" } } }, @@ -23876,9 +23957,9 @@ } }, "three": { - "version": "0.68.87", - "resolved": "https://registry.npmjs.org/three/-/three-0.68.87.tgz", - "integrity": "sha1-UNgklPHEHjgT1wzEG4fSJiBYc4A=" + "version": "0.125.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.125.0.tgz", + "integrity": "sha512-qL36qUGsPQ/Ofo/RZdXwHwM7A8wzUSAIyawtjIebJSPvounUQeneSqxI0aBY2iwKpseGy+RUtj3C5f/z4poyXw==" }, "through": { "version": "2.3.8", @@ -25936,9 +26017,9 @@ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", + "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==" }, "yaeti": { "version": "0.0.6", diff --git a/docs/package.json b/docs/package.json index a5a7e14d37fd4..9458f576d0ed6 100644 --- a/docs/package.json +++ b/docs/package.json @@ -39,7 +39,7 @@ "react-helmet": "^6.1.0", "swagger-ui-react": "^3.36.2", "theme-ui": "^0.3.1", - "three": "^0.68.0" + "three": "^0.125.0" }, "devDependencies": { "eslint-config-airbnb": "^18.2.1", diff --git a/docs/sqllab.rst b/docs/sqllab.rst index 0a324536f4a90..fe667f5d48f47 100644 --- a/docs/sqllab.rst +++ b/docs/sqllab.rst @@ -155,7 +155,7 @@ can optionally specify a custom formatter. Eg: return [{"Cost": f"US$ {cost:.2f}"}] - DEFAULT_FEATURE_FLAGS = { + FEATURE_FLAGS = { "ESTIMATE_QUERY_COST": True, "QUERY_COST_FORMATTERS_BY_ENGINE": {"presto": presto_query_cost_formatter}, } diff --git a/docs/src/pages/docs/Miscellaneous/index.mdx b/docs/src/pages/docs/Miscellaneous/index.mdx index 10a6f00087224..8b67947f65350 100644 --- a/docs/src/pages/docs/Miscellaneous/index.mdx +++ b/docs/src/pages/docs/Miscellaneous/index.mdx @@ -6,89 +6,57 @@ index: 1 version: 1 --- -## Country Map Tools +## The Country Map Visualization -This tool is used in slices for visualization number or string by region, province or department of -your countries. So, if you want to use tools, you need ISO 3166-2 code of region, province or -department. +The Country Map visualization allows you to plot lightweight choropleth maps of +your countries by province, states, or other subdivision types. It does not rely +on any third-party map services but would require you to provide the +[ISO-3166-2](https://en.wikipedia.org/wiki/ISO_3166-2) codes of your country's +top-level subdivisions. Comparing to a province or state's full names, the ISO +code is less ambiguous and is unique to all regions in the world. -ISO 3166-2 is part of the ISO 3166 standard published by the International Organization for -Standardization (ISO), and defines codes for identifying the principal subdivisions (e.g., provinces -or states) of all countries coded in ISO 3166-1 +## Included Maps -The purpose of ISO 3166-2 is to establish an international standard of short and unique alphanumeric -codes to represent the relevant administrative divisions and dependent territories of all countries -in a more convenient and less ambiguous form than their full names. Each complete ISO 3166-2 code -consists of two parts, separated by a hyphen: - -The first part is the ISO 3166-1 alpha-2 code of the country; The second part is a string of up to -three alphanumeric characters, which is usually obtained from national sources and stems from coding -systems already in use in the country concerned, but may also be developed by the ISO itself. - -We can apply these concepts to specify the state of Texas in the country of United States: - -``` -US-TX -``` - -### Included Codes - -The ISO codes for the following countries are included in Superset: +The Country Maps visualization already ships with the maps for the following countries: - Belgium +- Brazil +- Bulgaria +- Canada - China - Egypt - France - Germany +- India +- Iran +- Italy - Japan +- Korea - Liechtenstein - Morocco +- Myanmar +- Netherlands +- Portugal - Russia - Singapore - Spain - Switzerland -- United Kingdom +- Syria +- Thailand +- Timorleste +- UK - Ukraine +- Uruguay - USA +- Zambia -### Adding New Countries - -To add a new country in country map tools, you need to follow the following steps: +## Adding a New Country -- You need shapefiles which contain data of your map. You can get this file on this site: - https://www.diva-gis.org/gdata -- You need to add ISO 3166-2 with column name ISO for all record in your file. It’s important - because it’s a norm for mapping your data with geojson file -- You need to convert shapefile to geojson file. This action can make with ogr2ogr tools: - https://www.gdal.org/ogr2ogr.html -- Put your geojson file in next folder : superset-frontend/src/visualizations/CountryMap/countries - with the next name : nameofyourcountries.geojson -- You can to reduce size of geojson file on this site: https://mapshaper.org/ -- Go in file `superset-frontend/src/explore/controls.jsx` -- Add your country in component ‘select_country’. Here's an example: +To add a new country to the list, you'd have to edit files in +[@superset-ui/legacy-plugin-chart-country-map](https://github.com/apache-superset/superset-ui/tree/master/plugins/legacy-plugin-chart-country-map). -``` - type: 'SelectControl', - label: 'Country Name Type', - default: 'France', - choices: [ - 'Belgium', - 'Brazil', - 'China', - 'Egypt', - 'France', - 'Germany', - 'Italy', - 'Japan', - 'Korea', - 'Morocco', - 'Netherlands', - 'Russia', - 'Singapore', - 'Spain', - 'Uk', - 'Usa', - ].map(s => [s, s]), - description: 'The name of country that Superset should display', -}, -``` +1. Generate a new GeoJSON file for your country following the guide in [this Jupyter notebook](https://github.com/apache-superset/superset-ui/blob/master/plugins/legacy-plugin-chart-country-map/scripts/Country%20Map%20GeoJSON%20Generator.ipynb). +2. Edit the countries list in [legacy-plugin-chart-country-map/src/countries.js](https://github.com/apache-superset/superset-ui/blob/master/plugins/legacy-plugin-chart-country-map/src/countries.js). +3. Ping one of the Superset committers to get the `@superset-ui/legacy-plugin-chart-country-map` package published, or + publish it under another name yourself. +4. Update npm dependencies in `superset-frontend/package.json` to install the updated plugin package. diff --git a/docs/src/pages/docs/Miscellaneous/issue_codes.mdx b/docs/src/pages/docs/Miscellaneous/issue_codes.mdx index cf259ab701f3c..36e4df214687b 100644 --- a/docs/src/pages/docs/Miscellaneous/issue_codes.mdx +++ b/docs/src/pages/docs/Miscellaneous/issue_codes.mdx @@ -129,5 +129,40 @@ running a command. Please reach out to your administrator. Superset encountered an unexpected error. ``` -Someething unexpected happened in the Superset backend. Please reach out +Something unexpected happened in the Superset backend. Please reach out to your administrator. + +## Issue 1012 + +``` +The username provided when connecting to a database is not valid. +``` + +The user provided a username that doesn't exist in the database. Please check +that the username is typed correctly and exists in the database. + +## Issue 1013 + +``` +The password provided when connecting to a database is not valid. +``` + +The user provided a password that is incorrect. Please check that the +password is typed correctly. + +## Issue 1014 + +``` +Either the username or the password used are incorrect. +``` + +Either the username provided does not exist or the password was written incorrectly. Please +check that the username and password were typed correctly. + +## Issue 1015 + +``` +Either the database is spelled incorrectly or does not exist. +``` + +Either the database was written incorrectly or it does not exist. Check that it was typed correctly. diff --git a/docs/src/pages/docs/installation/configuring.mdx b/docs/src/pages/docs/installation/configuring.mdx index a8be730326b84..c725bf5950311 100644 --- a/docs/src/pages/docs/installation/configuring.mdx +++ b/docs/src/pages/docs/installation/configuring.mdx @@ -185,7 +185,7 @@ functionalities in Superset, but will be only affected by a subset of users. You can enable or disable features with flag from `superset_config.py`: ```python -DEFAULT_FEATURE_FLAGS = { +FEATURE_FLAGS = { 'CLIENT_CACHE': False, 'ENABLE_EXPLORE_JSON_CSRF_PROTECTION': False, 'PRESTO_EXPAND_DATA': False, diff --git a/docs/src/pages/docs/installation/index.mdx b/docs/src/pages/docs/installation/index.mdx index 7c895d43e779a..601753917ad2b 100644 --- a/docs/src/pages/docs/installation/index.mdx +++ b/docs/src/pages/docs/installation/index.mdx @@ -32,12 +32,13 @@ part of the base Docker installation on Linux, once you have a working engine, f **Windows** -Superset is not officially supported on Windows unfortunately. The best option for Windows users to +Superset is not officially supported on Windows unfortunately. One option for Windows users to try out Superset locally is to install an Ubuntu Desktop VM via [VirtualBox](https://www.virtualbox.org/) and proceed with the Docker on Linux instructions inside of that VM. We recommend assigning at least 8GB of RAM to the virtual machine as well as provisioning a hard drive of at least 40GB, so that there will be enough space for both the OS and -all of the required dependencies. +all of the required dependencies. Docker Desktop [recently added support for Windows Subsystem for Linux (WSL) 2](https://docs.docker.com/docker-for-windows/wsl/), which may be another option. + ### 2. Clone Superset's Github repository @@ -59,12 +60,6 @@ Navigate to the folder you created in step 1: $ cd superset ``` -We recommend that you check out and run the code from the last tagged release: - -```bash -$ git checkout latest -``` - Then, run the following command: ```bash @@ -74,14 +69,20 @@ $ docker-compose -f docker-compose-non-dev.yml up You should see a wall of logging output from the containers being launched on your machine. Once this output slows, you should have a running instance of Superset on your local machine! -**Note:** this will bring up superset in a non-dev mode, changes to the codebase will not be reflected. -If you would like to run superset in dev mode, simply replace the previous command with: +**Note:** This will bring up superset in a non-dev mode, changes to the codebase will not be reflected. +If you would like to run superset in dev mode to test local changes, simply replace the previous command with: `docker-compose up`, +and wait for the `superset_node` container to finish building the assets. -```bash -$ docker-compose up -``` +#### Configuring Docker Compose + +The following is for users who want to configure how Superset starts up in Docker Compose; otherwise, you can skip to the next section. + +You can configure the Docker Compose settings for dev and non-dev mode with `docker/.env` and `docker/.env-non-dev` respectively. These environment files set the environment for most containers in the Docker Compose setup, and some variables affect multiple containers and others only single ones. + +One important variable is `SUPERSET_LOAD_EXAMPLES` which determines whether the `superset_init` container will load example data and visualizations into the database and Superset. Thiese examples are quite helpful for most people, but probably unnecessary for experienced users. The loading process can sometimes take a few minutes and a good amount of CPU, so you may want to disable it on a resource-constrained device. + +**Note:** Users often want to connect to other databases from Superset. Currently, the easiest way to do this is to modify the `docker-compose-non-dev.yml` file and add your database as a service that the other services depend on (via `x-superset-depends-on`). Others have attempted to set `network_mode: host` on the Superset services, but these generally break the installation, because the configuration requires use of the Docker Compose DNS resolver for the service names. If you have a good solution for this, let us know! -and wait for the `superset_node` container to finish building the assets. ### 4. Log in to Superset @@ -99,6 +100,3 @@ username: admin ```bash password: admin ``` - -Congrats! You have successfully installed Superset! Click 'Next' to learn how to connect a database -driver. diff --git a/helm/superset/templates/NOTES.txt b/helm/superset/templates/NOTES.txt index d6cb98c4da5e2..07fcba38102a8 100644 --- a/helm/superset/templates/NOTES.txt +++ b/helm/superset/templates/NOTES.txt @@ -31,7 +31,6 @@ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "superset.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') echo http://$SERVICE_IP:{{ .Values.service.port }} {{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "superset.name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") echo "Visit http://127.0.0.1:8088 to use your application" - kubectl port-forward $POD_NAME 8088:80 + kubectl port-forward service/superset 8088:8088 --namespace {{ .Release.Namespace }} {{- end }} diff --git a/helm/superset/templates/deployment-beat.yaml b/helm/superset/templates/deployment-beat.yaml index 714d5f45b25cd..128bc6d767dc6 100644 --- a/helm/superset/templates/deployment-beat.yaml +++ b/helm/superset/templates/deployment-beat.yaml @@ -24,6 +24,10 @@ metadata: chart: {{ template "superset.chart" . }} release: {{ .Release.Name }} heritage: {{ .Release.Service }} +{{- if .Values.supersetCeleryBeat.deploymentAnnotations }} + annotations: + {{ toYaml .Values.supersetCeleryBeat.deploymentAnnotations | nindent 4 }} +{{- end }} spec: # This must be a singleton replicas: 1 @@ -44,6 +48,9 @@ spec: # Optionally force the thing to reload force-reload: {{ randAlphaNum 5 | quote }} {{ end }} + {{- if .Values.supersetCeleryBeat.podAnnotations }} + {{ toYaml .Values.supersetCeleryBeat.podAnnotations | nindent 8 }} + {{- end }} labels: app: {{ template "superset.name" . }}-celerybeat release: {{ .Release.Name }} diff --git a/helm/superset/templates/deployment-worker.yaml b/helm/superset/templates/deployment-worker.yaml index 4cf5034423c50..4001e76e920a5 100644 --- a/helm/superset/templates/deployment-worker.yaml +++ b/helm/superset/templates/deployment-worker.yaml @@ -23,6 +23,10 @@ metadata: chart: {{ template "superset.chart" . }} release: {{ .Release.Name }} heritage: {{ .Release.Service }} +{{- if .Values.supersetWorker.deploymentAnnotations }} + annotations: + {{ toYaml .Values.supersetWorker.deploymentAnnotations | nindent 4 }} +{{- end }} spec: replicas: {{ .Values.replicaCount }} selector: @@ -42,6 +46,9 @@ spec: # Optionally force the thing to reload force-reload: {{ randAlphaNum 5 | quote }} {{ end }} + {{- if .Values.supersetWorker.podAnnotations }} + {{ toYaml .Values.supersetWorker.podAnnotations | nindent 8 }} + {{- end }} labels: app: {{ template "superset.name" . }}-worker release: {{ .Release.Name }} diff --git a/helm/superset/templates/deployment.yaml b/helm/superset/templates/deployment.yaml index 456d33046fc40..2a611ca8f5bec 100644 --- a/helm/superset/templates/deployment.yaml +++ b/helm/superset/templates/deployment.yaml @@ -23,6 +23,10 @@ metadata: chart: {{ template "superset.chart" . }} release: {{ .Release.Name }} heritage: {{ .Release.Service }} +{{- if .Values.supersetNode.deploymentAnnotations }} + annotations: + {{ toYaml .Values.supersetNode.deploymentAnnotations | nindent 4 }} +{{- end }} spec: replicas: {{ .Values.replicaCount }} selector: @@ -45,6 +49,9 @@ spec: # Optionally force the thing to reload force-reload: {{ randAlphaNum 5 | quote }} {{- end }} + {{- if .Values.supersetNode.podAnnotations }} + {{ toYaml .Values.supersetNode.podAnnotations | nindent 8 }} + {{- end }} labels: app: {{ template "superset.name" . }} release: {{ .Release.Name }} diff --git a/helm/superset/templates/secret-superset-config.yaml b/helm/superset/templates/secret-superset-config.yaml index 2f37da831100d..ddf0befcd2f2b 100644 --- a/helm/superset/templates/secret-superset-config.yaml +++ b/helm/superset/templates/secret-superset-config.yaml @@ -20,7 +20,7 @@ metadata: name: {{ template "superset.fullname" . }}-config labels: app: {{ template "superset.fullname" . }} - chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + chart: {{ template "superset.chart" . }} release: "{{ .Release.Name }}" heritage: "{{ .Release.Service }}" type: Opaque diff --git a/helm/superset/values.yaml b/helm/superset/values.yaml index d927c3d6b3765..1a25419d16606 100644 --- a/helm/superset/values.yaml +++ b/helm/superset/values.yaml @@ -163,6 +163,12 @@ supersetNode: name: '{{ tpl .Values.envFromSecret . }}' command: [ "/bin/sh", "-c", "until nc -zv $DB_HOST $DB_PORT -w1; do echo 'waiting for db'; sleep 1; done" ] + ## Annotations to be added to supersetNode deployment + deploymentAnnotations: {} + + ## Annotations to be added to supersetNode pods + podAnnotations: {} + ## ## Superset worker configuration supersetWorker: @@ -180,6 +186,12 @@ supersetWorker: name: '{{ tpl .Values.envFromSecret . }}' command: [ "/bin/sh", "-c", "until nc -zv $DB_HOST $DB_PORT -w1; do echo 'waiting for db'; sleep 1; done" ] + ## Annotations to be added to supersetWorker deployment + deploymentAnnotations: {} + + ## Annotations to be added to supersetWorker pods + podAnnotations: {} + ## ## Superset beat configuration (to trigger scheduled jobs like reports) supersetCeleryBeat: @@ -199,6 +211,12 @@ supersetCeleryBeat: name: '{{ tpl .Values.envFromSecret . }}' command: [ "/bin/sh", "-c", "until nc -zv $DB_HOST $DB_PORT -w1; do echo 'waiting for db'; sleep 1; done" ] + ## Annotations to be added to supersetCeleryBeat deployment + deploymentAnnotations: {} + + ## Annotations to be added to supersetCeleryBeat pods + podAnnotations: {} + ## ## Init job configuration init: diff --git a/requirements/base.txt b/requirements/base.txt index 2c3c3b0664fc0..3596304d98922 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -109,6 +109,8 @@ geographiclib==1.50 # via geopy geopy==2.0.0 # via apache-superset +graphlib-backport==1.0.3 +# via apache-superset gunicorn==20.0.4 # via apache-superset holidays==0.10.3 diff --git a/scripts/benchmark_migration.py b/scripts/benchmark_migration.py new file mode 100644 index 0000000000000..0faa92a88552b --- /dev/null +++ b/scripts/benchmark_migration.py @@ -0,0 +1,215 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +import importlib.util +import logging +import re +import time +from collections import defaultdict +from inspect import getsource +from pathlib import Path +from types import ModuleType +from typing import Dict, List, Set, Type + +import click +from flask_appbuilder import Model +from flask_migrate import downgrade, upgrade +from graphlib import TopologicalSorter # pylint: disable=wrong-import-order +from sqlalchemy import inspect + +from superset import db +from superset.utils.mock_data import add_sample_rows + +logger = logging.getLogger(__name__) + + +def import_migration_script(filepath: Path) -> ModuleType: + """ + Import migration script as if it were a module. + """ + spec = importlib.util.spec_from_file_location(filepath.stem, filepath) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) # type: ignore + return module + + +def extract_modified_tables(module: ModuleType) -> Set[str]: + """ + Extract the tables being modified by a migration script. + + This function uses a simple approach of looking at the source code of + the migration script looking for patterns. It could be improved by + actually traversing the AST. + """ + + tables: Set[str] = set() + for function in {"upgrade", "downgrade"}: + source = getsource(getattr(module, function)) + tables.update(re.findall(r'alter_table\(\s*"(\w+?)"\s*\)', source, re.DOTALL)) + tables.update(re.findall(r'add_column\(\s*"(\w+?)"\s*,', source, re.DOTALL)) + tables.update(re.findall(r'drop_column\(\s*"(\w+?)"\s*,', source, re.DOTALL)) + + return tables + + +def find_models(module: ModuleType) -> List[Type[Model]]: + """ + Find all models in a migration script. + """ + models: List[Type[Model]] = [] + tables = extract_modified_tables(module) + + # add models defined explicitly in the migration script + queue = list(module.__dict__.values()) + while queue: + obj = queue.pop() + if hasattr(obj, "__tablename__"): + tables.add(obj.__tablename__) + elif isinstance(obj, list): + queue.extend(obj) + elif isinstance(obj, dict): + queue.extend(obj.values()) + + # add implicit models + # pylint: disable=no-member, protected-access + for obj in Model._decl_class_registry.values(): + if hasattr(obj, "__table__") and obj.__table__.fullname in tables: + models.append(obj) + + # sort topologically so we can create entities in order and + # maintain relationships (eg, create a database before creating + # a slice) + sorter = TopologicalSorter() + for model in models: + inspector = inspect(model) + dependent_tables: List[str] = [] + for column in inspector.columns.values(): + for foreign_key in column.foreign_keys: + dependent_tables.append(foreign_key.target_fullname.split(".")[0]) + sorter.add(model.__tablename__, *dependent_tables) + order = list(sorter.static_order()) + models.sort(key=lambda model: order.index(model.__tablename__)) + + return models + + +@click.command() +@click.argument("filepath") +@click.option("--limit", default=1000, help="Maximum number of entities.") +@click.option("--force", is_flag=True, help="Do not prompt for confirmation.") +@click.option("--no-auto-cleanup", is_flag=True, help="Do not remove created models.") +def main( + filepath: str, limit: int = 1000, force: bool = False, no_auto_cleanup: bool = False +) -> None: + auto_cleanup = not no_auto_cleanup + session = db.session() + + print(f"Importing migration script: {filepath}") + module = import_migration_script(Path(filepath)) + + revision: str = getattr(module, "revision", "") + down_revision: str = getattr(module, "down_revision", "") + if not revision or not down_revision: + raise Exception( + "Not a valid migration script, couldn't find down_revision/revision" + ) + + print(f"Migration goes from {down_revision} to {revision}") + current_revision = db.engine.execute( + "SELECT version_num FROM alembic_version" + ).scalar() + print(f"Current version of the DB is {current_revision}") + + print("\nIdentifying models used in the migration:") + models = find_models(module) + model_rows: Dict[Type[Model], int] = {} + for model in models: + rows = session.query(model).count() + print(f"- {model.__name__} ({rows} rows in table {model.__tablename__})") + model_rows[model] = rows + session.close() + + if current_revision != down_revision: + if not force: + click.confirm( + "\nRunning benchmark will downgrade the Superset DB to " + f"{down_revision} and upgrade to {revision} again. There may " + "be data loss in downgrades. Continue?", + abort=True, + ) + downgrade(revision=down_revision) + + print("Benchmarking migration") + results: Dict[str, float] = {} + start = time.time() + upgrade(revision=revision) + duration = time.time() - start + results["Current"] = duration + print(f"Migration on current DB took: {duration:.2f} seconds") + + min_entities = 10 + new_models: Dict[Type[Model], List[Model]] = defaultdict(list) + while min_entities <= limit: + downgrade(revision=down_revision) + print(f"Running with at least {min_entities} entities of each model") + for model in models: + missing = min_entities - model_rows[model] + if missing > 0: + print(f"- Adding {missing} entities to the {model.__name__} model") + try: + added_models = add_sample_rows(session, model, missing) + except Exception: + session.rollback() + raise + model_rows[model] = min_entities + session.commit() + + if auto_cleanup: + new_models[model].extend(added_models) + + start = time.time() + upgrade(revision=revision) + duration = time.time() - start + print(f"Migration for {min_entities}+ entities took: {duration:.2f} seconds") + results[f"{min_entities}+"] = duration + min_entities *= 10 + + if auto_cleanup: + print("Cleaning up DB") + # delete in reverse order of creation to handle relationships + for model, entities in list(new_models.items())[::-1]: + session.query(model).filter( + model.id.in_(entity.id for entity in entities) + ).delete(synchronize_session=False) + session.commit() + + if current_revision != revision and not force: + click.confirm(f"\nRevert DB to {revision}?", abort=True) + upgrade(revision=revision) + print("Reverted") + + print("\nResults:\n") + for label, duration in results.items(): + print(f"{label}: {duration:.2f} s") + + +if __name__ == "__main__": + from superset.app import create_app + + app = create_app() + with app.app_context(): + # pylint: disable=no-value-for-parameter + main() diff --git a/setup.cfg b/setup.cfg index 9dd35f5fe0039..8fd75a0bd6c56 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,7 +30,7 @@ combine_as_imports = true include_trailing_comma = true line_length = 88 known_first_party = superset -known_third_party =alembic,apispec,backoff,bleach,cachelib,celery,click,colorama,contextlib2,cron_descriptor,croniter,cryptography,dateutil,flask,flask_appbuilder,flask_babel,flask_caching,flask_compress,flask_login,flask_migrate,flask_sqlalchemy,flask_talisman,flask_testing,flask_wtf,freezegun,geohash,geopy,holidays,humanize,isodate,jinja2,jwt,markdown,markupsafe,marshmallow,marshmallow_enum,msgpack,numpy,pandas,parameterized,parsedatetime,pathlib2,pgsanity,pkg_resources,polyline,prison,pyarrow,pyhive,pyparsing,pytest,pytz,redis,requests,retry,selenium,setuptools,simplejson,slack,sqlalchemy,sqlalchemy_utils,sqlparse,typing_extensions,werkzeug,wtforms,wtforms_json,yaml +known_third_party =alembic,apispec,backoff,bleach,cachelib,celery,click,colorama,contextlib2,cron_descriptor,croniter,cryptography,dateutil,flask,flask_appbuilder,flask_babel,flask_caching,flask_compress,flask_jwt_extended,flask_login,flask_migrate,flask_sqlalchemy,flask_talisman,flask_testing,flask_wtf,freezegun,geohash,geopy,graphlib,holidays,humanize,isodate,jinja2,jwt,markdown,markupsafe,marshmallow,marshmallow_enum,msgpack,numpy,pandas,parameterized,parsedatetime,pathlib2,pgsanity,pkg_resources,polyline,prison,pyarrow,pyhive,pyparsing,pytest,pytz,redis,requests,retry,selenium,setuptools,simplejson,slack,sqlalchemy,sqlalchemy_utils,sqlparse,typing_extensions,werkzeug,wtforms,wtforms_json,yaml multi_line_output = 3 order_by_type = false diff --git a/setup.py b/setup.py index f839e2a32ef29..985aab45551fe 100644 --- a/setup.py +++ b/setup.py @@ -82,6 +82,7 @@ def get_git_sha(): "flask-migrate", "flask-wtf", "geopy", + "graphlib-backport", "gunicorn>=20.0.2, <20.1", "humanize", "isodate", @@ -103,7 +104,7 @@ def get_git_sha(): "selenium>=3.141.0", "simplejson>=3.15.0", "slackclient==2.5.0", # PINNED! slack changes file upload api in the future versions - "sqlalchemy>=1.3.16, <2.0, !=1.3.21", + "sqlalchemy>=1.3.16, <1.4, !=1.3.21", "sqlalchemy-utils>=0.36.6,<0.37", "sqlparse==0.3.0", # PINNED! see https://github.com/andialbrecht/sqlparse/issues/562 "typing-extensions>=3.7.4.3,<4", # needed to support typing.Literal on py37 diff --git a/superset-frontend/babel.config.js b/superset-frontend/babel.config.js index 51ae332c2341d..5aa3420cf1ac0 100644 --- a/superset-frontend/babel.config.js +++ b/superset-frontend/babel.config.js @@ -63,6 +63,7 @@ module.exports = { targets: { node: 'current' }, }, ], + ['@emotion/babel-preset-css-prop'], ], plugins: ['babel-plugin-dynamic-import-node'], }, diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/load.test.ts b/superset-frontend/cypress-base/cypress/integration/dashboard/load.test.ts index b03cdd27965f2..99f5f729f99b2 100644 --- a/superset-frontend/cypress-base/cypress/integration/dashboard/load.test.ts +++ b/superset-frontend/cypress-base/cypress/integration/dashboard/load.test.ts @@ -23,12 +23,28 @@ import { } from './dashboard.helper'; describe('Dashboard load', () => { - before(() => { + beforeEach(() => { cy.login(); - cy.visit(WORLD_HEALTH_DASHBOARD); }); it('should load dashboard', () => { + cy.visit(WORLD_HEALTH_DASHBOARD); WORLD_HEALTH_CHARTS.forEach(waitForChartLoad); }); + + it('should load in edit mode', () => { + cy.visit(`${WORLD_HEALTH_DASHBOARD}?edit=true&standalone=true`); + cy.get('[data-test="discard-changes-button"]').should('be.visible'); + }); + + it('should load in standalone mode', () => { + cy.visit(`${WORLD_HEALTH_DASHBOARD}?edit=true&standalone=true`); + cy.get('#app-menu').should('not.exist'); + }); + + it('should load in edit/standalone mode', () => { + cy.visit(`${WORLD_HEALTH_DASHBOARD}?edit=true&standalone=true`); + cy.get('[data-test="discard-changes-button"]').should('be.visible'); + cy.get('#app-menu').should('not.exist'); + }); }); diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts b/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts index f279b8e0643d8..fbb1aea9d0614 100644 --- a/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts +++ b/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts @@ -61,9 +61,12 @@ describe('Nativefilters', () => { .click() .type('Country name'); - cy.get('.ant-modal').find('[data-test="datasource-input"]').click(); + cy.get('.ant-modal') + .find('[data-test="datasource-input"]') + .click() + .type('wb_health_population'); - cy.get('[data-test="datasource-input"]') + cy.get('.ant-modal [data-test="datasource-input"] .Select__menu') .contains('wb_health_population') .click(); @@ -155,9 +158,12 @@ describe('Nativefilters', () => { .click() .type('Country name'); - cy.get('.ant-modal').find('[data-test="datasource-input"]').click(); + cy.get('.ant-modal') + .find('[data-test="datasource-input"]') + .click() + .type('wb_health_population'); - cy.get('[data-test="datasource-input"]') + cy.get('.ant-modal [data-test="datasource-input"] .Select__menu') .contains('wb_health_population') .click(); @@ -187,9 +193,10 @@ describe('Nativefilters', () => { cy.get('.ant-modal') .find('[data-test="datasource-input"]') .last() - .click(); + .click() + .type('wb_health_population'); - cy.get('[data-test="datasource-input"]') + cy.get('.ant-modal [data-test="datasource-input"] .Select__menu') .last() .contains('wb_health_population') .click(); diff --git a/superset-frontend/cypress-base/cypress/integration/explore/annotations.test.ts b/superset-frontend/cypress-base/cypress/integration/explore/annotations.test.ts index ebbaddf72dbce..a771ea5d2d4a2 100644 --- a/superset-frontend/cypress-base/cypress/integration/explore/annotations.test.ts +++ b/superset-frontend/cypress-base/cypress/integration/explore/annotations.test.ts @@ -29,7 +29,7 @@ describe('Annotations', () => { const layerLabel = 'Goal line'; - cy.get('[data-test=annotation_layers] button').click(); + cy.get('[data-test=annotation_layers]').click(); cy.get('[data-test="popover-content"]').within(() => { cy.get('[data-test=annotation-layer-name-header]') diff --git a/superset-frontend/cypress-base/cypress/integration/explore/visualizations/gauge.test.js b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/gauge.test.js new file mode 100644 index 0000000000000..8b5b2ffd0b72c --- /dev/null +++ b/superset-frontend/cypress-base/cypress/integration/explore/visualizations/gauge.test.js @@ -0,0 +1,63 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +describe('Visualization > Gauge', () => { + const GAUGE_FORM_DATA = { + datasource: '2__table', + viz_type: 'gauge_chart', + metric: 'count', + adhoc_filters: [], + slice_id: 49, + row_limit: 10, + }; + + function verify(formData) { + cy.visitChartByParams(JSON.stringify(formData)); + cy.verifySliceSuccess({ waitAlias: '@getJson' }); + } + + beforeEach(() => { + cy.login(); + cy.intercept('POST', '/api/v1/chart/data*').as('getJson'); + }); + + it('should work', () => { + verify(GAUGE_FORM_DATA); + cy.get('.chart-container .gauge_chart canvas').should('have.length', 1); + }); + + it('should work with simple filter', () => { + verify({ + ...GAUGE_FORM_DATA, + adhoc_filters: [ + { + expressionType: 'SIMPLE', + subject: 'country_code', + operator: '==', + comparator: 'USA', + clause: 'WHERE', + sqlExpression: null, + isExtra: false, + isNew: false, + filterOptionName: 'filter_jaemvkxd5h_ku22m3wyo', + }, + ], + }); + cy.get('.chart-container .gauge_chart canvas').should('have.length', 1); + }); +}); diff --git a/superset-frontend/jest.config.js b/superset-frontend/jest.config.js index d2355ceaa3283..ce7226a58b0fe 100644 --- a/superset-frontend/jest.config.js +++ b/superset-frontend/jest.config.js @@ -28,7 +28,7 @@ module.exports = { testEnvironment: 'jsdom', setupFilesAfterEnv: ['/spec/helpers/setup.ts'], testURL: 'http://localhost', - collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}'], + collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}', '!**/*.stories.*'], coverageDirectory: '/coverage/', transform: { '^.+\\.jsx?$': 'babel-jest', diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 524237ac946e3..480b9c883a568 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -13,34 +13,35 @@ "@babel/runtime-corejs3": "^7.12.5", "@data-ui/sparkline": "^0.0.84", "@emotion/core": "^10.0.35", - "@superset-ui/chart-controls": "^0.17.27", - "@superset-ui/core": "^0.17.27", - "@superset-ui/legacy-plugin-chart-calendar": "^0.17.27", - "@superset-ui/legacy-plugin-chart-chord": "^0.17.27", - "@superset-ui/legacy-plugin-chart-country-map": "^0.17.27", - "@superset-ui/legacy-plugin-chart-event-flow": "^0.17.27", - "@superset-ui/legacy-plugin-chart-force-directed": "^0.17.27", - "@superset-ui/legacy-plugin-chart-heatmap": "^0.17.27", - "@superset-ui/legacy-plugin-chart-histogram": "^0.17.27", - "@superset-ui/legacy-plugin-chart-horizon": "^0.17.27", - "@superset-ui/legacy-plugin-chart-map-box": "^0.17.27", - "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.17.27", - "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.17.27", - "@superset-ui/legacy-plugin-chart-partition": "^0.17.27", - "@superset-ui/legacy-plugin-chart-pivot-table": "^0.17.27", - "@superset-ui/legacy-plugin-chart-rose": "^0.17.27", - "@superset-ui/legacy-plugin-chart-sankey": "^0.17.27", - "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.17.27", - "@superset-ui/legacy-plugin-chart-sunburst": "^0.17.27", - "@superset-ui/legacy-plugin-chart-treemap": "^0.17.27", - "@superset-ui/legacy-plugin-chart-world-map": "^0.17.27", - "@superset-ui/legacy-preset-chart-big-number": "^0.17.27", + "@superset-ui/chart-controls": "^0.17.32", + "@superset-ui/core": "^0.17.32", + "@superset-ui/legacy-plugin-chart-calendar": "^0.17.32", + "@superset-ui/legacy-plugin-chart-chord": "^0.17.32", + "@superset-ui/legacy-plugin-chart-country-map": "^0.17.32", + "@superset-ui/legacy-plugin-chart-event-flow": "^0.17.32", + "@superset-ui/legacy-plugin-chart-force-directed": "^0.17.32", + "@superset-ui/legacy-plugin-chart-heatmap": "^0.17.32", + "@superset-ui/legacy-plugin-chart-histogram": "^0.17.32", + "@superset-ui/legacy-plugin-chart-horizon": "^0.17.32", + "@superset-ui/legacy-plugin-chart-map-box": "^0.17.32", + "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.17.32", + "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.17.32", + "@superset-ui/legacy-plugin-chart-partition": "^0.17.32", + "@superset-ui/legacy-plugin-chart-pivot-table": "^0.17.32", + "@superset-ui/legacy-plugin-chart-rose": "^0.17.32", + "@superset-ui/legacy-plugin-chart-sankey": "^0.17.32", + "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.17.32", + "@superset-ui/legacy-plugin-chart-sunburst": "^0.17.32", + "@superset-ui/legacy-plugin-chart-treemap": "^0.17.32", + "@superset-ui/legacy-plugin-chart-world-map": "^0.17.32", + "@superset-ui/legacy-preset-chart-big-number": "^0.17.32", "@superset-ui/legacy-preset-chart-deckgl": "^0.4.6", - "@superset-ui/legacy-preset-chart-nvd3": "^0.17.27", - "@superset-ui/plugin-chart-echarts": "^0.17.27", - "@superset-ui/plugin-chart-table": "^0.17.28", - "@superset-ui/plugin-chart-word-cloud": "^0.17.27", - "@superset-ui/preset-chart-xy": "^0.17.27", + "@superset-ui/legacy-preset-chart-nvd3": "^0.17.32", + "@superset-ui/plugin-chart-echarts": "^0.17.32", + "@superset-ui/plugin-chart-pivot-table": "^0.17.33", + "@superset-ui/plugin-chart-table": "^0.17.32", + "@superset-ui/plugin-chart-word-cloud": "^0.17.32", + "@superset-ui/preset-chart-xy": "^0.17.32", "@vx/responsive": "^0.0.195", "abortcontroller-polyfill": "^1.1.9", "antd": "^4.9.4", @@ -108,7 +109,7 @@ "react-split": "^2.0.9", "react-sticky": "^6.0.3", "react-syntax-highlighter": "^15.3.0", - "react-table": "^7.2.1", + "react-table": "^7.6.3", "react-transition-group": "^2.5.3", "react-ultimate-pagination": "^1.2.0", "react-virtualized": "9.19.1", @@ -122,7 +123,7 @@ "regenerator-runtime": "^0.13.5", "rison": "^0.1.1", "shortid": "^2.2.6", - "urijs": "^1.19.4", + "urijs": "^1.19.6", "use-immer": "^0.4.2", "use-query-params": "^1.1.9" }, @@ -497,7 +498,7 @@ "version": "7.12.10", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/generator": "^7.12.10", @@ -523,7 +524,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/highlight": "^7.10.4" } @@ -532,7 +533,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/types": "^7.12.11", "jsesc": "^2.5.1", @@ -543,7 +544,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/helper-get-function-arity": "^7.12.10", "@babel/template": "^7.12.7", @@ -554,7 +555,7 @@ "version": "7.12.10", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/types": "^7.12.10" } @@ -563,7 +564,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/types": "^7.12.11" } @@ -572,13 +573,13 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "devOptional": true + "dev": true }, "node_modules/@babel/core/node_modules/@babel/highlight": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", @@ -589,7 +590,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", - "devOptional": true, + "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -601,7 +602,7 @@ "version": "7.12.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/parser": "^7.12.7", @@ -612,7 +613,7 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/code-frame": "^7.12.11", "@babel/generator": "^7.12.11", @@ -629,7 +630,7 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -640,7 +641,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "devOptional": true, + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -652,7 +653,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "devOptional": true, + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -666,7 +667,7 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "devOptional": true, + "dev": true, "dependencies": { "safe-buffer": "~5.1.1" } @@ -675,7 +676,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "devOptional": true, + "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -687,7 +688,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "devOptional": true, + "dev": true, "dependencies": { "minimist": "^1.2.5" }, @@ -702,13 +703,13 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true + "dev": true }, "node_modules/@babel/core/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "devOptional": true, + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -1283,7 +1284,7 @@ "version": "7.12.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.12.1", "@babel/helper-replace-supers": "^7.12.1", @@ -1300,7 +1301,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/highlight": "^7.10.4" } @@ -1309,7 +1310,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/types": "^7.12.11", "jsesc": "^2.5.1", @@ -1320,7 +1321,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/helper-get-function-arity": "^7.12.10", "@babel/template": "^7.12.7", @@ -1331,7 +1332,7 @@ "version": "7.12.10", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/types": "^7.12.10" } @@ -1340,7 +1341,7 @@ "version": "7.12.7", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/types": "^7.12.7" } @@ -1349,7 +1350,7 @@ "version": "7.12.5", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/types": "^7.12.5" } @@ -1358,7 +1359,7 @@ "version": "7.12.10", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/types": "^7.12.10" } @@ -1367,7 +1368,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/helper-member-expression-to-functions": "^7.12.7", "@babel/helper-optimise-call-expression": "^7.12.10", @@ -1379,7 +1380,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/types": "^7.12.11" } @@ -1388,13 +1389,13 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "devOptional": true + "dev": true }, "node_modules/@babel/helper-module-transforms/node_modules/@babel/highlight": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", @@ -1405,7 +1406,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", - "devOptional": true, + "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -1417,7 +1418,7 @@ "version": "7.12.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/parser": "^7.12.7", @@ -1428,7 +1429,7 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/code-frame": "^7.12.11", "@babel/generator": "^7.12.11", @@ -1445,7 +1446,7 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -1456,7 +1457,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "devOptional": true, + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -1468,7 +1469,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "devOptional": true, + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -1482,7 +1483,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "devOptional": true, + "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -1494,13 +1495,13 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true + "dev": true }, "node_modules/@babel/helper-module-transforms/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "devOptional": true, + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -1607,7 +1608,7 @@ "version": "7.12.1", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/types": "^7.12.1" } @@ -1616,13 +1617,13 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "devOptional": true + "dev": true }, "node_modules/@babel/helper-simple-access/node_modules/@babel/types": { "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -1876,7 +1877,7 @@ "version": "7.12.5", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/template": "^7.10.4", "@babel/traverse": "^7.12.5", @@ -1887,7 +1888,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/highlight": "^7.10.4" } @@ -1896,7 +1897,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/types": "^7.12.11", "jsesc": "^2.5.1", @@ -1907,7 +1908,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/helper-get-function-arity": "^7.12.10", "@babel/template": "^7.12.7", @@ -1918,7 +1919,7 @@ "version": "7.12.10", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/types": "^7.12.10" } @@ -1927,7 +1928,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/types": "^7.12.11" } @@ -1936,13 +1937,13 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "devOptional": true + "dev": true }, "node_modules/@babel/helpers/node_modules/@babel/highlight": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", @@ -1953,7 +1954,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", - "devOptional": true, + "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -1965,7 +1966,7 @@ "version": "7.12.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/parser": "^7.12.7", @@ -1976,7 +1977,7 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/code-frame": "^7.12.11", "@babel/generator": "^7.12.11", @@ -1993,7 +1994,7 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "devOptional": true, + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -2004,7 +2005,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "devOptional": true, + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -2016,7 +2017,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "devOptional": true, + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -2030,7 +2031,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "devOptional": true, + "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -2042,13 +2043,13 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true + "dev": true }, "node_modules/@babel/helpers/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "devOptional": true, + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -6315,18 +6316,6 @@ "@emotion/utils": "^1.0.0", "@emotion/weak-memoize": "^0.2.5", "hoist-non-react-statics": "^3.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0", - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@types/react": { - "optional": true - } } }, "node_modules/@emotion/react/node_modules/@emotion/cache": { @@ -9740,11 +9729,6 @@ "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz", "integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==" }, - "node_modules/@scarf/scarf": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.0.6.tgz", - "integrity": "sha512-y4+DuXrAd1W5UIY3zTcsosi/1GyYT8k5jGnZ/wG7UUHVrU+MHlH4Mp87KK2/lvMW4+H7HVcdB+aJhqywgXksjA==" - }, "node_modules/@seznam/compose-react-refs": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@seznam/compose-react-refs/-/compose-react-refs-1.0.4.tgz", @@ -9882,12 +9866,6 @@ "refractor": "^3.1.0" } }, - "node_modules/@storybook/addon-actions/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@storybook/addon-actions/node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -10120,12 +10098,6 @@ "refractor": "^3.1.0" } }, - "node_modules/@storybook/addon-backgrounds/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@storybook/addon-backgrounds/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -10395,12 +10367,6 @@ "refractor": "^3.1.0" } }, - "node_modules/@storybook/addon-controls/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@storybook/addon-controls/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -10799,12 +10765,6 @@ "refractor": "^3.1.0" } }, - "node_modules/@storybook/addon-docs/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@storybook/addon-docs/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -11103,12 +11063,6 @@ "refractor": "^3.1.0" } }, - "node_modules/@storybook/addon-essentials/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@storybook/addon-essentials/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -11225,12 +11179,6 @@ "refractor": "^3.1.0" } }, - "node_modules/@storybook/addon-knobs/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@storybook/addon-links": { "version": "6.1.17", "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-6.1.17.tgz", @@ -11260,12 +11208,6 @@ "node": ">=0.6" } }, - "node_modules/@storybook/addon-links/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@storybook/addon-toolbars": { "version": "6.1.20", "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-6.1.20.tgz", @@ -11523,12 +11465,6 @@ "refractor": "^3.1.0" } }, - "node_modules/@storybook/addon-toolbars/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@storybook/addon-toolbars/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -11760,12 +11696,6 @@ "refractor": "^3.1.0" } }, - "node_modules/@storybook/addon-viewport/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@storybook/addon-viewport/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -11792,12 +11722,6 @@ "regenerator-runtime": "^0.13.7" } }, - "node_modules/@storybook/addons/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@storybook/api": { "version": "6.1.17", "resolved": "https://registry.npmjs.org/@storybook/api/-/api-6.1.17.tgz", @@ -11840,12 +11764,6 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, - "node_modules/@storybook/api/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@storybook/channel-postmessage": { "version": "6.1.17", "resolved": "https://registry.npmjs.org/@storybook/channel-postmessage/-/channel-postmessage-6.1.17.tgz", @@ -11916,12 +11834,6 @@ "node": ">=0.6" } }, - "node_modules/@storybook/client-api/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@storybook/client-logger": { "version": "6.1.17", "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-6.1.17.tgz", @@ -13961,12 +13873,6 @@ "refractor": "^3.1.0" } }, - "node_modules/@storybook/core/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@storybook/core/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -15279,12 +15185,6 @@ "node": ">=0.6" } }, - "node_modules/@storybook/react/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@storybook/react/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -15693,12 +15593,6 @@ "node": ">=0.6" } }, - "node_modules/@storybook/source-loader/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@storybook/source-loader/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -16010,12 +15904,6 @@ "refractor": "^3.1.0" } }, - "node_modules/@storybook/ui/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/@storybook/ui/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -16026,27 +15914,19 @@ } }, "node_modules/@superset-ui/chart-controls": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.17.27.tgz", - "integrity": "sha512-gAldp/Vy5c8lXW8QCoYZ4juOIb9q5M33cwQGIn+bO3z63IAW7fp5Q7/K38hyFS2Xvzemv5zSEU49+MNK2ZuQ2Q==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.17.32.tgz", + "integrity": "sha512-seHKefTTiT6IHDv/OaS83J0ZVw4Apq/c9kpK97SW/GLrZWQUNRc9gB9b7EBiBUoxeKszw6nQMzW/VJiZTrolaw==", "dependencies": { - "@superset-ui/core": "0.17.27", + "@superset-ui/core": "0.17.32", "lodash": "^4.17.15", "prop-types": "^15.7.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-bootstrap": "*", - "antd": "^4.9.4", - "react": "^16.13.1", - "react-bootstrap": "^0.33.1", - "react-icons": "^4.2.0" } }, "node_modules/@superset-ui/core": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.17.27.tgz", - "integrity": "sha512-DdwdbpJEQKqe4rD/mcoy8Og+LDJSN6Lvhbg1SP7zHLjAQtYeLgOYoWd+V+engCA1XGbfrigu3+dC8Xn+SjUZsg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.17.32.tgz", + "integrity": "sha512-uaSzlZmolqV3XVDVYYy7sVOB76gMkUrKnc+Wng8BDejzK7Cbr5VicbnNNDNvQuh9llfIop2ptNYAtBcdeqPnig==", "dependencies": { "@babel/runtime": "^7.1.2", "@emotion/core": "^10.0.28", @@ -16078,14 +15958,6 @@ "rison": "^0.1.1", "seedrandom": "^3.0.5", "whatwg-fetch": "^3.0.0" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-bootstrap": "*", - "@types/react-loadable": "*", - "react": "^16.13.1", - "react-bootstrap": "^0.33.1", - "react-loadable": "^5.5.0" } }, "node_modules/@superset-ui/core/node_modules/@vx/responsive": { @@ -16098,9 +15970,6 @@ "lodash": "^4.17.10", "prop-types": "^15.6.1", "resize-observer-polyfill": "1.5.1" - }, - "peerDependencies": { - "react": "^15.0.0-0 || ^16.0.0-0" } }, "node_modules/@superset-ui/core/node_modules/d3-array": { @@ -16124,19 +15993,16 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-calendar": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.17.27.tgz", - "integrity": "sha512-Ill80Nv7/8sJqB9l9cO0/5p6Z6Gfsbyw97IkMyQTlgaI2qGWufilmDHCyGT/Qtu+5LxhTKCehIg+Pi9behNzsQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.17.32.tgz", + "integrity": "sha512-rFRqw0A+YjGiRwd8c4RFY/SIyC5jY44jdPnJrCi3vOovLRFY+YQ101AW+beA+o7gLBjyLYqZOc8DZK9eEcJIMA==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3-array": "^2.0.3", "d3-selection": "^1.4.0", "d3-tip": "^0.9.1", "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": "^16.13.1" } }, "node_modules/@superset-ui/legacy-plugin-chart-calendar/node_modules/d3-array": { @@ -16148,24 +16014,24 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-chord": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.17.27.tgz", - "integrity": "sha512-wUa4TXumY22bqHXvuuC/5+YU4stwZe2UnFraHx32jGGRjk5DzdrmHaBo4VtgBX26wZl5dK9SBXs1zuwVoMX4Sw==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.17.32.tgz", + "integrity": "sha512-Rc1gdRCu985vJQrJDBQOKl6uWbbqq3Hbt9ZXuLJPkjbu+McTIrsFF9ihxsrLjYlgFtSJWZB3AX/CtkLTXinMKw==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "prop-types": "^15.6.2", "react": "^16.13.1" } }, "node_modules/@superset-ui/legacy-plugin-chart-country-map": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.17.27.tgz", - "integrity": "sha512-7hX1gqLjXsN7Byx496APLyqOcB4Vn30Wntn9oYpUn0KJkeVvjJYvOnk9GALmMVdEvYtfoqJprk29lESXo7jzqQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.17.32.tgz", + "integrity": "sha512-0Vv2xAdGnwL76XuKeAbDeh7fXJbbY1XwY6Jrke1gvlOgpx6d/i0PeQrwHduUPWvCo1cDwPv8sIP7HYPh5XwBCw==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-array": "^2.0.3", "prop-types": "^15.6.2" @@ -16180,40 +16046,34 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-event-flow": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.17.27.tgz", - "integrity": "sha512-EDCVOWSofe9um5/cBSNGvpvm+yfSbYY4hNK82j0zy7WvMmLU1xiVjTU3tz6Wn962R+Lg4hXAlsDlyGFh/egx4Q==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.17.32.tgz", + "integrity": "sha512-s4DrFW4nGZEMCRgs9Ln9OmoTjDC2BlZ3p+XEjp3+QlE+AfcesH0ZxcbgImo03hIBLemby7MkSW3aORA+1+drMA==", "dependencies": { "@data-ui/event-flow": "^0.0.84", - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": "^15 || ^16" } }, "node_modules/@superset-ui/legacy-plugin-chart-force-directed": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.17.27.tgz", - "integrity": "sha512-K+dqjW9R2fZZHmioQTcdMGTSRaXPc9WgJLWWOZ1EO/VMbInJAjPNldnDAmMBuX8RaipB2dZVcCH16b/zJrZtSA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.17.32.tgz", + "integrity": "sha512-M6iZM7P80b3iib1oCblLeqO424ZHOeCInTSS8/vMc/BRPjX0savYM4IBQnVJibkTA6CGEsgAqbDYlPQiqF3TwQ==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "prop-types": "^15.7.2" - }, - "peerDependencies": { - "react": "^16.13.1" } }, "node_modules/@superset-ui/legacy-plugin-chart-heatmap": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.17.27.tgz", - "integrity": "sha512-NO2lppl+p9Q0hoRtrJcu2D9SM7TyMcgMn6H0JuIYDL5UKv7f2QmiyfAryLvyLvrI4eejYJsBH71VtkC61m3mVg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.17.32.tgz", + "integrity": "sha512-kdKD6FEytz5ByPjdmXpjCqqZ9H70HPlCautoK7RGyXVLqqzw4qiz2/8x1tk2aEaqWN3eFWnBKOoHOer7vjw30w==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-svg-legend": "^1.x", "d3-tip": "^0.9.1", @@ -16221,21 +16081,18 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-histogram": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.17.27.tgz", - "integrity": "sha512-n+h0owePVmCNZ+fzMm0X3l1BDHEMGmfVhtlhaYJXHd6YInsNvoLQGY7MrLKHRwonagkoYcwLjk3dlDF9jzzOjQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.17.32.tgz", + "integrity": "sha512-f/vj/5I3/3ne4RITQEDiJxmuojFoj56XVd/waG+2gz7rWmKoMlQlbW3Oj1AMwpiKMWZYfUQ//twgDRz/skbSPg==", "dependencies": { "@data-ui/histogram": "^0.0.84", "@data-ui/theme": "^0.0.84", - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@vx/legend": "^0.0.198", "@vx/responsive": "^0.0.199", "@vx/scale": "^0.0.197", "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": "^15 || ^16" } }, "node_modules/@superset-ui/legacy-plugin-chart-histogram/node_modules/@vx/group": { @@ -16272,9 +16129,6 @@ "lodash": "^4.17.10", "prop-types": "^15.6.1", "resize-observer-polyfill": "1.5.1" - }, - "peerDependencies": { - "react": "^15.0.0-0 || ^16.0.0-0" } }, "node_modules/@superset-ui/legacy-plugin-chart-histogram/node_modules/@vx/scale": { @@ -16300,18 +16154,15 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-horizon": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.17.27.tgz", - "integrity": "sha512-j4A7Dd9J7bH3pTRvaE7DtysxBEoFWgb0qnowHboVy53Dd2qi8uzs/UZaphpIY+V6APewM9DD9UDg1yQ037Vbzw==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.17.32.tgz", + "integrity": "sha512-lfD+a+WPlGxIzqWiAjLVW4wqTUheZ4jF8SneZetP0KT2TT2fdkmoxu3qem87nX+YpyAOJYYVh3zuDx7vqaoV5g==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3-array": "^2.0.3", "d3-scale": "^3.0.1", "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": "^15 || ^16" } }, "node_modules/@superset-ui/legacy-plugin-chart-horizon/node_modules/d3-array": { @@ -16335,21 +16186,18 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-map-box": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.17.27.tgz", - "integrity": "sha512-Fdc3nWVL2OxLbaouF9BIP/y+0aeD4ZUNm6gehGsHKXBjx6FMWRnsdLoyL/sz5BCR5h9y7y3hpbCb3Th/JOVf2w==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.17.32.tgz", + "integrity": "sha512-QhJbNMFUpYPwv6bPTCq5KkYecybkpSju9RhtbSKaqGyA6dNezhPF3WIWyR5oSW5Wm6E7EoTKRi2UDPJsBOO09g==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "immutable": "^3.8.2", "mapbox-gl": "^0.53.0", "prop-types": "^15.6.2", "react-map-gl": "^4.0.10", "supercluster": "^4.1.1", "viewport-mercator-project": "^6.1.1" - }, - "peerDependencies": { - "react": "^15 || ^16" } }, "node_modules/@superset-ui/legacy-plugin-chart-map-box/node_modules/immutable": { @@ -16361,141 +16209,123 @@ } }, "node_modules/@superset-ui/legacy-plugin-chart-paired-t-test": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.17.27.tgz", - "integrity": "sha512-vrF5wPH12TMpOahjls/rUYuJNye/cqrM3Y1/84+VPNh+5Y3Vc/2TjxTX9x+ZJY/bUkSL6dcf1U4gIbaqg+LtAg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.17.32.tgz", + "integrity": "sha512-O2dwG4/VuS3oHCYE3cpX52sqduDRLTMZIelmI4L0FcDO5Z1hpfxhFDFIfcqpk6KzxJnuwoCJ88Sf42zSS83HCw==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "distributions": "^1.0.0", "prop-types": "^15.6.2", "reactable": "^1.1.0" - }, - "peerDependencies": { - "react": "^15 || ^16" } }, "node_modules/@superset-ui/legacy-plugin-chart-parallel-coordinates": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.17.27.tgz", - "integrity": "sha512-W/u4mneqJXAhxK53fSJkjnPCB5NYUcom1uxb7kXbMujuzgAK9OnWL9Mbs9JhDkW/H/benAqSk2vUO/1LMEAefA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.17.32.tgz", + "integrity": "sha512-grHQYcOTzC+FQ3NGs87sPl9abCaradsucE0AUiG9ZM3/WX7M5SrTJ5fQgPmBejUVMzXGeJVcW8CvUYkL4wTsfg==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "prop-types": "^15.7.2" - }, - "peerDependencies": { - "react": "^16.13.1" } }, "node_modules/@superset-ui/legacy-plugin-chart-partition": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.17.27.tgz", - "integrity": "sha512-qqgDsu/HYuPBe4wvM070J2Dsxu+csg4CEi22Zrm5J0pVOk6NVehDNtD3ik+dzbBy0WipoWguV+jojnZtoKu+Gg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.17.32.tgz", + "integrity": "sha512-mn8gzhggUpeNGMBLOK0tEfalYZdrDUdHxKCrAkIMheS/L+/pZhb7gs2DPY7R746SvsT9ZRr5Q3IBv+Nv5XSDig==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-hierarchy": "^1.1.8", "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": "^16.13.1" } }, "node_modules/@superset-ui/legacy-plugin-chart-pivot-table": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.17.27.tgz", - "integrity": "sha512-chAV2F2BAAlVcZprt5Tcj0TUJc6AxWvxcXhE0QsS5eRDiPip0yBfpUSuLOA+2n+r8rfacuPrPso8YoWxHqP7UQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.17.32.tgz", + "integrity": "sha512-fQ6+Qg3Mj9g3aLvNWn+C9SW1x7kQglotoc1cl0/L5SfVswsW4eqgZSWkLlvYGU3JlL5r+durIqD97gt5cmiB7g==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "datatables.net-bs": "^1.10.15", "prop-types": "^15.6.2" } }, "node_modules/@superset-ui/legacy-plugin-chart-rose": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.17.27.tgz", - "integrity": "sha512-JCFW3UTmghMamLqneiePXJjYB+ovJSRSQJPiRG8DrOGTlXpOGeRSjjy5Chg9Kbm58nSw9KpB2coTD7asehsYqQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.17.32.tgz", + "integrity": "sha512-HjzYOZP++kWVVclNYOtq2To6EAn6j+XZjllgd9EGn7TuF+e1DBHjHTOVyTWyJCMSMR1451kpjQouzZLzTIwQvw==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "nvd3": "1.8.6", "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": "^16.13.1" } }, "node_modules/@superset-ui/legacy-plugin-chart-sankey": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.17.27.tgz", - "integrity": "sha512-bpaVj0j6bi8QsidS50nsGYJeo9xxIg6SGyeXDO0iJKWYqoQVvuYiDGL/wW78kDBRSgrqbpopLTxNfpeUYBYFPA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.17.32.tgz", + "integrity": "sha512-xaX/b1aRgh5XoyjFIKOJkj/8WaI9oAP0tbBVLFaxzGhZzQvWwxOluTA1QqeR6ndKeLgbX8LZxc5xXd3kLLsBLA==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-sankey": "^0.4.2", "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": "^16.13.1" } }, "node_modules/@superset-ui/legacy-plugin-chart-sankey-loop": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.17.27.tgz", - "integrity": "sha512-e7cu8tCE+YKpOxdbFr1gBbaqWrdJlrwjC3iZasM61WynIgXPwn8DXwnBYGJ5JhhUmptPIjb69s2JoyouWLsvyw==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.17.32.tgz", + "integrity": "sha512-7R+1+MJ5Sel8z8dklMmU45/RktOkpmfLcE9zu7S7/vG3aI9ORrp5HFSl6PfzmQ71Q/uHww2m+18LAGMkFsxxTA==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3-sankey-diagram": "^0.7.3", "d3-selection": "^1.4.0", "prop-types": "^15.6.2" } }, "node_modules/@superset-ui/legacy-plugin-chart-sunburst": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.17.27.tgz", - "integrity": "sha512-fwafwAhckYG3EAGyEa1saNuR5in5FIOex6HRv3LThHfaw2CRccKKyQk00kYOF2h1/JfLWpnFpRRNUSrBDYlQ8Q==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.17.32.tgz", + "integrity": "sha512-zqg3uB/1n+x++PTeiKo2w6qCmTR2sLqWkTR8Towlg1wLNOJHSNbkxDa9CDx6TfKciUCGlJc/FtS4G9lLpwN5Yg==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "prop-types": "^15.6.2" } }, "node_modules/@superset-ui/legacy-plugin-chart-treemap": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.17.27.tgz", - "integrity": "sha512-pcGIBkZAzBbpBim7zhuE3LcUD3yq0D1XRmzIOogYMUvZyzJt1psquap53dW0as9nfP3IUwKSWPXohx4WSQ2cyQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.17.32.tgz", + "integrity": "sha512-4g3plLNN/QNi2Zs9MkOTSdZtucaCFE5a/nKsuGKa0ZBc8Nm8xAS0u32fA/sBgdoV25uJTLedGfJMzgvqZ43epw==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3-hierarchy": "^1.1.8", "d3-selection": "^1.4.0", "prop-types": "^15.6.2" } }, "node_modules/@superset-ui/legacy-plugin-chart-world-map": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.17.27.tgz", - "integrity": "sha512-XqtbOOqHryT25H5R++ngOLTRM2NpMRJ8Mu/t8E3cp2iCzUaNIAQfi/F/jko5sc9uU7POgNdTsU/PovC3vhL8KA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.17.32.tgz", + "integrity": "sha512-0/fLJ9kxLD8/R5eNCDLCCDuzwsX6m4r7N6Iqdliqk5HM9mBOJAhB/gpeE1jNoFvCwl4ntxnuToP6QN6xrjw0Qg==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-array": "^2.4.0", "d3-color": "^1.4.1", "datamaps": "^0.5.8", "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": "^16.13.1" } }, "node_modules/@superset-ui/legacy-plugin-chart-world-map/node_modules/d3-array": { @@ -16512,20 +16342,17 @@ "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" }, "node_modules/@superset-ui/legacy-preset-chart-big-number": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.17.27.tgz", - "integrity": "sha512-fdbDk5WgVTVPszHLR+uoaFQQYVfN5EscD7jzEe2Szv1STbpFHdlcPsa5OER4Yx3CctrCkLgKUNNxcZQ3+3WRCQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.17.32.tgz", + "integrity": "sha512-mSKwdG88WhySuqpmrCHa/R+cDfwqph/Vee4RsOrGzBFS/fPrlCotUbcLaoZnKJtso4cIIynSJRn29CB8bvd82A==", "dependencies": { "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@types/d3-color": "^1.2.2", "@types/shortid": "^0.0.29", "d3-color": "^1.2.3", "shortid": "^2.2.14" - }, - "peerDependencies": { - "react": "^15 || ^16" } }, "node_modules/@superset-ui/legacy-preset-chart-deckgl": { @@ -16554,13 +16381,13 @@ } }, "node_modules/@superset-ui/legacy-preset-chart-nvd3": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.17.27.tgz", - "integrity": "sha512-qP/G9dGTWm3nag8pstyhkmWROiEIMnVJsl0SHQ1foBXPeqYq0HXir0cQ4yzE8EdQiHREd6OECHw/K9TbNnYC8w==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.17.32.tgz", + "integrity": "sha512-P6HeR8/ik2edSSm+Sxe9TTaCcomb0fAtp7RKOB8lXIGU6oksdvxFcxCB9mbVlnn2ChH9nlUOw5JKo9v3r50w3w==", "dependencies": { "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-tip": "^0.9.1", "dompurify": "^2.0.6", @@ -16571,49 +16398,50 @@ "nvd3-fork": "^2.0.5", "prop-types": "^15.6.2", "urijs": "^1.18.10" - }, - "peerDependencies": { - "react": "^15 || ^16" } }, "node_modules/@superset-ui/plugin-chart-echarts": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.17.27.tgz", - "integrity": "sha512-CvZ5UrNo+3wwX5MeiWbCWMyciCJPnknTlGVKxjpCUosbikqq0YaBDb4mSg3pfKsuEnhLx7CRqXxYwYY/Pbgx9A==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.17.32.tgz", + "integrity": "sha512-/ifeCsqh1/iSBMv41JWl4HaNUlLmKCVIQyDMTsdJr4MsvYQG1HHtDUpa3eHnMS7RlrLVZLVmlr59LS6S4Dcoww==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@types/mathjs": "^6.0.7", "d3-array": "^1.2.0", "echarts": "^5.0.2", "mathjs": "^8.0.1" + } + }, + "node_modules/@superset-ui/plugin-chart-pivot-table": { + "version": "0.17.33", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-pivot-table/-/plugin-chart-pivot-table-0.17.33.tgz", + "integrity": "sha512-AOJb87OHLEmHZt+bFvt954ZrVvd/VAyxxpjaXSvQiDYpUHuhdUthNX0D3DcD7Gdc5HT2JI1myO2f/eMDTpmrMA==", + "dependencies": { + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", + "@superset-ui/react-pivottable": "^0.12.5" }, "peerDependencies": { "react": "^16.13.1" } }, "node_modules/@superset-ui/plugin-chart-table": { - "version": "0.17.28", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.17.28.tgz", - "integrity": "sha512-E5F4Xgas9crKTPxiA8YwjZXNOXIodfbUXtgfUq4hDeiK+AlJdjE6Yh9FKKHqkko4Ua6laWYmQ3lj+tJ0576fXw==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.17.32.tgz", + "integrity": "sha512-q3REacsT0oDTnM2ZaFy82Gc+ys62fhEBgZl/dkdxK/3To5jZ29++yU+ctYxdYuf0zFqGZltYRh9Ges03MwaS+g==", "dependencies": { "@emotion/core": "^10.0.28", - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", - "@types/d3-array": "^2.0.0", - "@types/react-table": "^7.0.19", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", + "@types/d3-array": "^2.9.0", + "@types/react-table": "^7.0.29", "d3-array": "^2.4.0", - "match-sorter": "^6.1.0", + "match-sorter": "^6.3.0", "memoize-one": "^5.1.1", - "react-table": "^7.2.1", - "regenerator-runtime": "^0.13.5", - "xss": "^1.0.6" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.13.1", - "react-dom": "^16.13.1", - "react-icons": "^4.2.0" + "react-table": "^7.6.3", + "regenerator-runtime": "^0.13.7", + "xss": "^1.0.8" } }, "node_modules/@superset-ui/plugin-chart-table/node_modules/d3-array": { @@ -16625,22 +16453,18 @@ } }, "node_modules/@superset-ui/plugin-chart-word-cloud": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.17.27.tgz", - "integrity": "sha512-2PlHOM3rkBOrCU1MTj7HRifHTcd+2OKmdknNLUsI3cycmUjeWbSY9zhGf6z5cW7KgTBTSfJYXwiqxbrwxiNsdw==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.17.32.tgz", + "integrity": "sha512-G+ghQ5PW3rJMnbxnoo4yfG8HXJ5N23iFXfH/x4AqWgL0RyTJry/dsyvCpKMQNzvsB2M0dQbY5sy4/uG5JIo7Zg==", "dependencies": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@types/d3-cloud": "^1.2.1", "@types/d3-scale": "^2.0.2", "d3-cloud": "^1.2.5", "d3-scale": "^3.0.1", "emotion-theming": "^10.0.27", "encodable": "^0.7.6" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.13.1" } }, "node_modules/@superset-ui/plugin-chart-word-cloud/node_modules/d3-array": { @@ -16664,14 +16488,14 @@ } }, "node_modules/@superset-ui/preset-chart-xy": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.17.27.tgz", - "integrity": "sha512-XlRf3OneDU8WABa2ZsCqkWagpw72PlWnuw1Aa3rnHjMSy8ZkGANv1KyCfld8Bewx/+k7kfYDGl0UC+oGnsGpoA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.17.32.tgz", + "integrity": "sha512-tViT1tIBxzhpAbOZI465mF46M5UQ42qA89mGOdzqJLTVe5aggev3oNN8vP35rVsHPz9M96vN3fe8/G1aEqGi/g==", "dependencies": { "@data-ui/theme": "^0.0.84", "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@vx/axis": "^0.0.198", "@vx/legend": "^0.0.198", "@vx/scale": "^0.0.197", @@ -16679,9 +16503,6 @@ "encodable": "^0.7.6", "lodash": "^4.17.11", "reselect": "^4.0.0" - }, - "peerDependencies": { - "react": "^16.2" } }, "node_modules/@superset-ui/preset-chart-xy/node_modules/@vx/axis": { @@ -16790,6 +16611,22 @@ "d3-time-format": "2" } }, + "node_modules/@superset-ui/react-pivottable": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@superset-ui/react-pivottable/-/react-pivottable-0.12.5.tgz", + "integrity": "sha512-9Nmj/sRdc6U/OUWBqfqHAaV05MCmNLt65I/Ar39brpLcaYKWRIYpBcUT3n/HOhCr8ALilnsLdQzzfjjaNAuv5w==", + "dependencies": { + "immutability-helper": "^3.1.1", + "prop-types": "^15.7.2", + "react-draggable": "^4.4.3", + "react-sortablejs": "^6.0.0", + "sortablejs": "^1.13.0" + }, + "peerDependencies": { + "react": ">=15.0.0", + "react-dom": ">=15.0.0" + } + }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", @@ -18428,9 +18265,9 @@ "integrity": "sha1-dvjy6RWa5WKWWy+g5vvuGqZDobw=" }, "node_modules/@types/d3-array": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-2.8.0.tgz", - "integrity": "sha512-Q0ubcGHAmCRPh90/hoYB4eKWhxYKUxphwSeQrlz2tiabQ8S9zqhaE2CZJtCaLH2cjqKcjr52WPvmOA7ha0O4ZA==" + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-2.9.0.tgz", + "integrity": "sha512-sdBMGfNvLUkBypPMEhOcKcblTQfgHbqbYrUqRE31jOwdDHBJBxz4co2MDAq93S4Cp++phk4UiwoEg/1hK3xXAQ==" }, "node_modules/@types/d3-cloud": { "version": "1.2.3", @@ -18832,6 +18669,7 @@ "version": "0.32.22", "resolved": "https://registry.npmjs.org/@types/react-bootstrap/-/react-bootstrap-0.32.22.tgz", "integrity": "sha512-pjUVcJzogMxns3lbvMqnnU+I8EOYxl3aI13tS2vvRm0RdAe1rs7Ds/VZA29GI6p8p3Un6NqKUpW3+dgwAjyzxg==", + "dev": true, "dependencies": { "@types/react": "*" } @@ -18887,6 +18725,7 @@ "version": "5.5.4", "resolved": "https://registry.npmjs.org/@types/react-loadable/-/react-loadable-5.5.4.tgz", "integrity": "sha512-otKcjNCfVUzdBMdwOqFITTmBruIXw6GeoZitTBvJ6BMrif8Utu2JLy42GWukNnYI7ewJdncUCooz5Y/1dBz4+w==", + "dev": true, "dependencies": { "@types/react": "*", "@types/webpack": "*" @@ -18965,9 +18804,9 @@ } }, "node_modules/@types/react-table": { - "version": "7.0.19", - "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-7.0.19.tgz", - "integrity": "sha512-RYyEY7Yry6F2JsKhHeFsGdzuFF1hMqBStQrrazDzpBl4m/ECGHJxFVQjLBRzRwK+47ZKNPm79f7qEpHirbiCLA==", + "version": "7.0.29", + "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-7.0.29.tgz", + "integrity": "sha512-RCGVKGlTDv3jbj37WJ5HhN3sPb0W/2rqlvyGUtvawnnyrxgI2BGgASvU93rq2jwanVp5J9l1NYAeiGlNhdaBGw==", "dependencies": { "@types/react": "*" } @@ -19103,6 +18942,12 @@ "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==", "dev": true }, + "node_modules/@types/sortablejs": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.10.6.tgz", + "integrity": "sha512-QRz8Z+uw2Y4Gwrtxw8hD782zzuxxugdcq8X/FkPsXUa1kfslhGzy13+4HugO9FXNo+jlWVcE6DYmmegniIQ30A==", + "peer": true + }, "node_modules/@types/source-list-map": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", @@ -19117,7 +18962,8 @@ "node_modules/@types/tapable": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.4.tgz", - "integrity": "sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ==" + "integrity": "sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ==", + "dev": true }, "node_modules/@types/testing-library__jest-dom": { "version": "5.9.5", @@ -19153,6 +18999,7 @@ "version": "4.39.1", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.39.1.tgz", "integrity": "sha512-rgO9ihNu/l72Sjx3shqwc9r6gi+tOMsqxhMEZhOEVIZt82GFOeUyEdpTk1BO2HqEHLS/XJW8ldUTIIfIMMyYFQ==", + "dev": true, "dependencies": { "@types/anymatch": "*", "@types/node": "*", @@ -19190,6 +19037,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -31304,7 +31152,7 @@ "version": "1.0.0-beta.1", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", - "devOptional": true, + "dev": true, "engines": { "node": ">=6.9.0" } @@ -31586,7 +31434,7 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "devOptional": true, + "dev": true, "engines": { "node": ">=4" } @@ -32580,6 +32428,11 @@ "resolved": "https://registry.npmjs.org/immer/-/immer-8.0.1.tgz", "integrity": "sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==" }, + "node_modules/immutability-helper": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-3.1.1.tgz", + "integrity": "sha512-Q0QaXjPjwIju/28TsugCHNEASwoCcJSyJV3uO1sOIQGI0jKgm9f41Lvz0DZj3n46cNCyAZTsEYoY4C2bVRUzyQ==" + }, "node_modules/immutable": { "version": "4.0.0-rc.12", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0-rc.12.tgz", @@ -39654,7 +39507,7 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "devOptional": true, + "dev": true, "bin": { "jsesc": "bin/jsesc" }, @@ -40529,9 +40382,9 @@ } }, "node_modules/match-sorter": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.1.0.tgz", - "integrity": "sha512-sKPMf4kbF7Dm5Crx0bbfLpokK68PUJ/0STUIOPa1ZmTZEA3lCaPK3gapQR573oLmvdkTfGojzySkIwuq6Z6xRQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.0.tgz", + "integrity": "sha512-efYOf/wUpNb8FgNY+cOD2EIJI1S5I7YPKsw0LBp7wqPh5pmMS6i/wr3ZWwfwrAw1NvqTA2KUReVRWDX84lUcOQ==", "dependencies": { "@babel/runtime": "^7.12.5", "remove-accents": "0.4.2" @@ -45740,9 +45593,6 @@ }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/prismjs": { @@ -47961,7 +47811,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.3.tgz", "integrity": "sha512-jV4TE59MBuWm7gb6Ns3Q1mxX8Azffb7oTtDtBgFkxRvhDp38YAARmRplrj0+XGkhOJB5XziArX+4HUUABtyZ0w==", - "dev": true, "dependencies": { "classnames": "^2.2.5", "prop-types": "^15.6.0" @@ -48001,10 +47850,7 @@ "node_modules/react-error-boundary": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-1.2.5.tgz", - "integrity": "sha512-5CPSeLJA2igJNppAgFRwnTL9aK3ojenk65enNzhVyoxYNbHpIJXnChUO7+4vPhkncRA9wvQMXq6Azp2XeXd+iQ==", - "peerDependencies": { - "react": "^16.0.0-beta.1" - } + "integrity": "sha512-5CPSeLJA2igJNppAgFRwnTL9aK3ojenk65enNzhVyoxYNbHpIJXnChUO7+4vPhkncRA9wvQMXq6Azp2XeXd+iQ==" }, "node_modules/react-error-overlay": { "version": "6.0.9", @@ -48099,10 +47945,7 @@ "node_modules/react-icons": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.2.0.tgz", - "integrity": "sha512-rmzEDFt+AVXRzD7zDE21gcxyBizD/3NqjbX6cmViAgdqfJ2UiLer8927/QhhrXQV7dEj/1EGuOTPp7JnLYVJKQ==", - "peerDependencies": { - "react": "*" - } + "integrity": "sha512-rmzEDFt+AVXRzD7zDE21gcxyBizD/3NqjbX6cmViAgdqfJ2UiLer8927/QhhrXQV7dEj/1EGuOTPp7JnLYVJKQ==" }, "node_modules/react-input-autosize": { "version": "2.2.2", @@ -48516,6 +48359,21 @@ "prop-types": "^15.5.7" } }, + "node_modules/react-sortablejs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/react-sortablejs/-/react-sortablejs-6.0.0.tgz", + "integrity": "sha512-vzi+TWOnofcYg+dYnC/Iz/ZZkBGG76uM6KaLwuAqBk0349JQxIy3PZizbK0TJdLlK6NnLt4CiEyyQXSSnVYvEw==", + "dependencies": { + "classnames": "^2.2.6", + "tiny-invariant": "^1.1.0" + }, + "peerDependencies": { + "@types/sortablejs": "^1.10.0", + "react": "^16.9.0", + "react-dom": "^16.9.0", + "sortablejs": "^1.10.0" + } + }, "node_modules/react-split": { "version": "2.0.9", "resolved": "https://registry.npmjs.org/react-split/-/react-split-2.0.9.tgz", @@ -48613,12 +48471,9 @@ } }, "node_modules/react-table": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.2.1.tgz", - "integrity": "sha512-cICU3w5yFWTDluz9yFePziEQXGt3PK5R1Va3InP7qMGnZOKm8kv0GiDMli+VOqE1uM1dBYPb9BEad15up1ac6g==", - "dependencies": { - "@scarf/scarf": "^1.0.4" - } + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.6.3.tgz", + "integrity": "sha512-hfPF13zDLxPMpLKzIKCE8RZud9T/XrRTsaCIf8zXpWZIZ2juCl7qrGpo3AQw9eAetXV5DP7s2GDm+hht7qq5Dw==" }, "node_modules/react-test-renderer": { "version": "16.9.0", @@ -49125,9 +48980,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", - "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" }, "node_modules/regenerator-transform": { "version": "0.14.3", @@ -50418,7 +50273,7 @@ "version": "5.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", - "devOptional": true, + "dev": true, "bin": { "semver": "bin/semver" } @@ -51161,6 +51016,11 @@ "uuid": "bin/uuid" } }, + "node_modules/sortablejs": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.13.0.tgz", + "integrity": "sha512-RBJirPY0spWCrU5yCmWM1eFs/XgX2J5c6b275/YyxFRgnzPhKl/TDeU2hNR8Dt7ITq66NRPM4UlOt+e5O4CFHg==" + }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -51835,12 +51695,6 @@ "node": ">=0.6" } }, - "node_modules/storybook-addon-paddings/node_modules/regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "node_modules/storybook-addon-paddings/node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -53645,9 +53499,9 @@ "optional": true }, "node_modules/tiny-invariant": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz", - "integrity": "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", + "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" }, "node_modules/tiny-warning": { "version": "1.0.3", @@ -54563,9 +54417,9 @@ } }, "node_modules/urijs": { - "version": "1.19.4", - "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.4.tgz", - "integrity": "sha512-2YF/wdFu02Gsly/wyx+S/f5w/oCF0ihVSgK/Sn8fcY/ZYMYtqxgi03Vi3V7HqyQP8mj8xHMuNFTBIPufmPRdoA==" + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.6.tgz", + "integrity": "sha512-eSXsXZ2jLvGWeLYlQA3Gh36BcjF+0amo92+wHPyN1mdR8Nxf75fuEuYTd9c0a+m/vhCjRK0ESlE9YNLW+E1VEw==" }, "node_modules/urix": { "version": "0.1.0", @@ -57715,7 +57569,7 @@ "version": "7.12.10", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz", "integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==", - "devOptional": true, + "dev": true, "requires": { "@babel/code-frame": "^7.10.4", "@babel/generator": "^7.12.10", @@ -57738,7 +57592,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "devOptional": true, + "dev": true, "requires": { "@babel/highlight": "^7.10.4" } @@ -57747,7 +57601,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", - "devOptional": true, + "dev": true, "requires": { "@babel/types": "^7.12.11", "jsesc": "^2.5.1", @@ -57758,7 +57612,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", - "devOptional": true, + "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.12.10", "@babel/template": "^7.12.7", @@ -57769,7 +57623,7 @@ "version": "7.12.10", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", - "devOptional": true, + "dev": true, "requires": { "@babel/types": "^7.12.10" } @@ -57778,7 +57632,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", - "devOptional": true, + "dev": true, "requires": { "@babel/types": "^7.12.11" } @@ -57787,13 +57641,13 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "devOptional": true + "dev": true }, "@babel/highlight": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "devOptional": true, + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", @@ -57804,13 +57658,13 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", - "devOptional": true + "dev": true }, "@babel/template": { "version": "7.12.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", - "devOptional": true, + "dev": true, "requires": { "@babel/code-frame": "^7.10.4", "@babel/parser": "^7.12.7", @@ -57821,7 +57675,7 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", - "devOptional": true, + "dev": true, "requires": { "@babel/code-frame": "^7.12.11", "@babel/generator": "^7.12.11", @@ -57838,7 +57692,7 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "devOptional": true, + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -57849,7 +57703,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "devOptional": true, + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -57858,7 +57712,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "devOptional": true, + "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -57869,7 +57723,7 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "devOptional": true, + "dev": true, "requires": { "safe-buffer": "~5.1.1" } @@ -57878,7 +57732,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "devOptional": true, + "dev": true, "requires": { "ms": "2.1.2" } @@ -57887,7 +57741,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "devOptional": true, + "dev": true, "requires": { "minimist": "^1.2.5" } @@ -57896,13 +57750,13 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true + "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "devOptional": true, + "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -58466,7 +58320,7 @@ "version": "7.12.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz", "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==", - "devOptional": true, + "dev": true, "requires": { "@babel/helper-module-imports": "^7.12.1", "@babel/helper-replace-supers": "^7.12.1", @@ -58483,7 +58337,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "devOptional": true, + "dev": true, "requires": { "@babel/highlight": "^7.10.4" } @@ -58492,7 +58346,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", - "devOptional": true, + "dev": true, "requires": { "@babel/types": "^7.12.11", "jsesc": "^2.5.1", @@ -58503,7 +58357,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", - "devOptional": true, + "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.12.10", "@babel/template": "^7.12.7", @@ -58514,7 +58368,7 @@ "version": "7.12.10", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", - "devOptional": true, + "dev": true, "requires": { "@babel/types": "^7.12.10" } @@ -58523,7 +58377,7 @@ "version": "7.12.7", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.7.tgz", "integrity": "sha512-DCsuPyeWxeHgh1Dus7APn7iza42i/qXqiFPWyBDdOFtvS581JQePsc1F/nD+fHrcswhLlRc2UpYS1NwERxZhHw==", - "devOptional": true, + "dev": true, "requires": { "@babel/types": "^7.12.7" } @@ -58532,7 +58386,7 @@ "version": "7.12.5", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.5.tgz", "integrity": "sha512-SR713Ogqg6++uexFRORf/+nPXMmWIn80TALu0uaFb+iQIUoR7bOC7zBWyzBs5b3tBBJXuyD0cRu1F15GyzjOWA==", - "devOptional": true, + "dev": true, "requires": { "@babel/types": "^7.12.5" } @@ -58541,7 +58395,7 @@ "version": "7.12.10", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.10.tgz", "integrity": "sha512-4tpbU0SrSTjjt65UMWSrUOPZTsgvPgGG4S8QSTNHacKzpS51IVWGDj0yCwyeZND/i+LSN2g/O63jEXEWm49sYQ==", - "devOptional": true, + "dev": true, "requires": { "@babel/types": "^7.12.10" } @@ -58550,7 +58404,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.11.tgz", "integrity": "sha512-q+w1cqmhL7R0FNzth/PLLp2N+scXEK/L2AHbXUyydxp828F4FEa5WcVoqui9vFRiHDQErj9Zof8azP32uGVTRA==", - "devOptional": true, + "dev": true, "requires": { "@babel/helper-member-expression-to-functions": "^7.12.7", "@babel/helper-optimise-call-expression": "^7.12.10", @@ -58562,7 +58416,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", - "devOptional": true, + "dev": true, "requires": { "@babel/types": "^7.12.11" } @@ -58571,13 +58425,13 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "devOptional": true + "dev": true }, "@babel/highlight": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "devOptional": true, + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", @@ -58588,13 +58442,13 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", - "devOptional": true + "dev": true }, "@babel/template": { "version": "7.12.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", - "devOptional": true, + "dev": true, "requires": { "@babel/code-frame": "^7.10.4", "@babel/parser": "^7.12.7", @@ -58605,7 +58459,7 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", - "devOptional": true, + "dev": true, "requires": { "@babel/code-frame": "^7.12.11", "@babel/generator": "^7.12.11", @@ -58622,7 +58476,7 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "devOptional": true, + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -58633,7 +58487,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "devOptional": true, + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -58642,7 +58496,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "devOptional": true, + "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -58653,7 +58507,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "devOptional": true, + "dev": true, "requires": { "ms": "2.1.2" } @@ -58662,13 +58516,13 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true + "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "devOptional": true, + "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -58780,7 +58634,7 @@ "version": "7.12.1", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz", "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==", - "devOptional": true, + "dev": true, "requires": { "@babel/types": "^7.12.1" }, @@ -58789,13 +58643,13 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "devOptional": true + "dev": true }, "@babel/types": { "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "devOptional": true, + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -59039,7 +58893,7 @@ "version": "7.12.5", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz", "integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==", - "devOptional": true, + "dev": true, "requires": { "@babel/template": "^7.10.4", "@babel/traverse": "^7.12.5", @@ -59050,7 +58904,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "devOptional": true, + "dev": true, "requires": { "@babel/highlight": "^7.10.4" } @@ -59059,7 +58913,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz", "integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==", - "devOptional": true, + "dev": true, "requires": { "@babel/types": "^7.12.11", "jsesc": "^2.5.1", @@ -59070,7 +58924,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz", "integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==", - "devOptional": true, + "dev": true, "requires": { "@babel/helper-get-function-arity": "^7.12.10", "@babel/template": "^7.12.7", @@ -59081,7 +58935,7 @@ "version": "7.12.10", "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz", "integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==", - "devOptional": true, + "dev": true, "requires": { "@babel/types": "^7.12.10" } @@ -59090,7 +58944,7 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz", "integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==", - "devOptional": true, + "dev": true, "requires": { "@babel/types": "^7.12.11" } @@ -59099,13 +58953,13 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "devOptional": true + "dev": true }, "@babel/highlight": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "devOptional": true, + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", @@ -59116,13 +58970,13 @@ "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz", "integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==", - "devOptional": true + "dev": true }, "@babel/template": { "version": "7.12.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz", "integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==", - "devOptional": true, + "dev": true, "requires": { "@babel/code-frame": "^7.10.4", "@babel/parser": "^7.12.7", @@ -59133,7 +58987,7 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz", "integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==", - "devOptional": true, + "dev": true, "requires": { "@babel/code-frame": "^7.12.11", "@babel/generator": "^7.12.11", @@ -59150,7 +59004,7 @@ "version": "7.12.12", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz", "integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==", - "devOptional": true, + "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", "lodash": "^4.17.19", @@ -59161,7 +59015,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "devOptional": true, + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -59170,7 +59024,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "devOptional": true, + "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -59181,7 +59035,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "devOptional": true, + "dev": true, "requires": { "ms": "2.1.2" } @@ -59190,13 +59044,13 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "devOptional": true + "dev": true }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "devOptional": true, + "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -66388,11 +66242,6 @@ "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz", "integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==" }, - "@scarf/scarf": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.0.6.tgz", - "integrity": "sha512-y4+DuXrAd1W5UIY3zTcsosi/1GyYT8k5jGnZ/wG7UUHVrU+MHlH4Mp87KK2/lvMW4+H7HVcdB+aJhqywgXksjA==" - }, "@seznam/compose-react-refs": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@seznam/compose-react-refs/-/compose-react-refs-1.0.4.tgz", @@ -66530,12 +66379,6 @@ "refractor": "^3.1.0" } }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -66764,12 +66607,6 @@ "refractor": "^3.1.0" } }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -67035,12 +66872,6 @@ "refractor": "^3.1.0" } }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -67406,12 +67237,6 @@ "refractor": "^3.1.0" } }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -67706,12 +67531,6 @@ "refractor": "^3.1.0" } }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -67820,12 +67639,6 @@ "prismjs": "^1.21.0", "refractor": "^3.1.0" } - }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true } } }, @@ -67854,12 +67667,6 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", "dev": true - }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true } } }, @@ -68117,12 +67924,6 @@ "refractor": "^3.1.0" } }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -68350,12 +68151,6 @@ "refractor": "^3.1.0" } }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -68379,14 +68174,6 @@ "core-js": "^3.0.1", "global": "^4.3.2", "regenerator-runtime": "^0.13.7" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - } } }, "@storybook/api": { @@ -68430,12 +68217,6 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true - }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true } } }, @@ -68504,12 +68285,6 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz", "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", "dev": true - }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true } } }, @@ -70292,12 +70067,6 @@ "refractor": "^3.1.0" } }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -71420,12 +71189,6 @@ "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", "dev": true }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -71774,12 +71537,6 @@ "integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==", "dev": true }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -72080,12 +71837,6 @@ "refractor": "^3.1.0" } }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -72095,19 +71846,19 @@ } }, "@superset-ui/chart-controls": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.17.27.tgz", - "integrity": "sha512-gAldp/Vy5c8lXW8QCoYZ4juOIb9q5M33cwQGIn+bO3z63IAW7fp5Q7/K38hyFS2Xvzemv5zSEU49+MNK2ZuQ2Q==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/chart-controls/-/chart-controls-0.17.32.tgz", + "integrity": "sha512-seHKefTTiT6IHDv/OaS83J0ZVw4Apq/c9kpK97SW/GLrZWQUNRc9gB9b7EBiBUoxeKszw6nQMzW/VJiZTrolaw==", "requires": { - "@superset-ui/core": "0.17.27", + "@superset-ui/core": "0.17.32", "lodash": "^4.17.15", "prop-types": "^15.7.2" } }, "@superset-ui/core": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.17.27.tgz", - "integrity": "sha512-DdwdbpJEQKqe4rD/mcoy8Og+LDJSN6Lvhbg1SP7zHLjAQtYeLgOYoWd+V+engCA1XGbfrigu3+dC8Xn+SjUZsg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/core/-/core-0.17.32.tgz", + "integrity": "sha512-uaSzlZmolqV3XVDVYYy7sVOB76gMkUrKnc+Wng8BDejzK7Cbr5VicbnNNDNvQuh9llfIop2ptNYAtBcdeqPnig==", "requires": { "@babel/runtime": "^7.1.2", "@emotion/core": "^10.0.28", @@ -72176,12 +71927,12 @@ } }, "@superset-ui/legacy-plugin-chart-calendar": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.17.27.tgz", - "integrity": "sha512-Ill80Nv7/8sJqB9l9cO0/5p6Z6Gfsbyw97IkMyQTlgaI2qGWufilmDHCyGT/Qtu+5LxhTKCehIg+Pi9behNzsQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-calendar/-/legacy-plugin-chart-calendar-0.17.32.tgz", + "integrity": "sha512-rFRqw0A+YjGiRwd8c4RFY/SIyC5jY44jdPnJrCi3vOovLRFY+YQ101AW+beA+o7gLBjyLYqZOc8DZK9eEcJIMA==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3-array": "^2.0.3", "d3-selection": "^1.4.0", "d3-tip": "^0.9.1", @@ -72199,24 +71950,24 @@ } }, "@superset-ui/legacy-plugin-chart-chord": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.17.27.tgz", - "integrity": "sha512-wUa4TXumY22bqHXvuuC/5+YU4stwZe2UnFraHx32jGGRjk5DzdrmHaBo4VtgBX26wZl5dK9SBXs1zuwVoMX4Sw==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-chord/-/legacy-plugin-chart-chord-0.17.32.tgz", + "integrity": "sha512-Rc1gdRCu985vJQrJDBQOKl6uWbbqq3Hbt9ZXuLJPkjbu+McTIrsFF9ihxsrLjYlgFtSJWZB3AX/CtkLTXinMKw==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "prop-types": "^15.6.2", "react": "^16.13.1" } }, "@superset-ui/legacy-plugin-chart-country-map": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.17.27.tgz", - "integrity": "sha512-7hX1gqLjXsN7Byx496APLyqOcB4Vn30Wntn9oYpUn0KJkeVvjJYvOnk9GALmMVdEvYtfoqJprk29lESXo7jzqQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-country-map/-/legacy-plugin-chart-country-map-0.17.32.tgz", + "integrity": "sha512-0Vv2xAdGnwL76XuKeAbDeh7fXJbbY1XwY6Jrke1gvlOgpx6d/i0PeQrwHduUPWvCo1cDwPv8sIP7HYPh5XwBCw==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-array": "^2.0.3", "prop-types": "^15.6.2" @@ -72233,34 +71984,34 @@ } }, "@superset-ui/legacy-plugin-chart-event-flow": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.17.27.tgz", - "integrity": "sha512-EDCVOWSofe9um5/cBSNGvpvm+yfSbYY4hNK82j0zy7WvMmLU1xiVjTU3tz6Wn962R+Lg4hXAlsDlyGFh/egx4Q==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-event-flow/-/legacy-plugin-chart-event-flow-0.17.32.tgz", + "integrity": "sha512-s4DrFW4nGZEMCRgs9Ln9OmoTjDC2BlZ3p+XEjp3+QlE+AfcesH0ZxcbgImo03hIBLemby7MkSW3aORA+1+drMA==", "requires": { "@data-ui/event-flow": "^0.0.84", - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-force-directed": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.17.27.tgz", - "integrity": "sha512-K+dqjW9R2fZZHmioQTcdMGTSRaXPc9WgJLWWOZ1EO/VMbInJAjPNldnDAmMBuX8RaipB2dZVcCH16b/zJrZtSA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-force-directed/-/legacy-plugin-chart-force-directed-0.17.32.tgz", + "integrity": "sha512-M6iZM7P80b3iib1oCblLeqO424ZHOeCInTSS8/vMc/BRPjX0savYM4IBQnVJibkTA6CGEsgAqbDYlPQiqF3TwQ==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "prop-types": "^15.7.2" } }, "@superset-ui/legacy-plugin-chart-heatmap": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.17.27.tgz", - "integrity": "sha512-NO2lppl+p9Q0hoRtrJcu2D9SM7TyMcgMn6H0JuIYDL5UKv7f2QmiyfAryLvyLvrI4eejYJsBH71VtkC61m3mVg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-heatmap/-/legacy-plugin-chart-heatmap-0.17.32.tgz", + "integrity": "sha512-kdKD6FEytz5ByPjdmXpjCqqZ9H70HPlCautoK7RGyXVLqqzw4qiz2/8x1tk2aEaqWN3eFWnBKOoHOer7vjw30w==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-svg-legend": "^1.x", "d3-tip": "^0.9.1", @@ -72268,14 +72019,14 @@ } }, "@superset-ui/legacy-plugin-chart-histogram": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.17.27.tgz", - "integrity": "sha512-n+h0owePVmCNZ+fzMm0X3l1BDHEMGmfVhtlhaYJXHd6YInsNvoLQGY7MrLKHRwonagkoYcwLjk3dlDF9jzzOjQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-histogram/-/legacy-plugin-chart-histogram-0.17.32.tgz", + "integrity": "sha512-f/vj/5I3/3ne4RITQEDiJxmuojFoj56XVd/waG+2gz7rWmKoMlQlbW3Oj1AMwpiKMWZYfUQ//twgDRz/skbSPg==", "requires": { "@data-ui/histogram": "^0.0.84", "@data-ui/theme": "^0.0.84", - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@vx/legend": "^0.0.198", "@vx/responsive": "^0.0.199", "@vx/scale": "^0.0.197", @@ -72343,12 +72094,12 @@ } }, "@superset-ui/legacy-plugin-chart-horizon": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.17.27.tgz", - "integrity": "sha512-j4A7Dd9J7bH3pTRvaE7DtysxBEoFWgb0qnowHboVy53Dd2qi8uzs/UZaphpIY+V6APewM9DD9UDg1yQ037Vbzw==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-horizon/-/legacy-plugin-chart-horizon-0.17.32.tgz", + "integrity": "sha512-lfD+a+WPlGxIzqWiAjLVW4wqTUheZ4jF8SneZetP0KT2TT2fdkmoxu3qem87nX+YpyAOJYYVh3zuDx7vqaoV5g==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3-array": "^2.0.3", "d3-scale": "^3.0.1", "prop-types": "^15.6.2" @@ -72377,12 +72128,12 @@ } }, "@superset-ui/legacy-plugin-chart-map-box": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.17.27.tgz", - "integrity": "sha512-Fdc3nWVL2OxLbaouF9BIP/y+0aeD4ZUNm6gehGsHKXBjx6FMWRnsdLoyL/sz5BCR5h9y7y3hpbCb3Th/JOVf2w==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-map-box/-/legacy-plugin-chart-map-box-0.17.32.tgz", + "integrity": "sha512-QhJbNMFUpYPwv6bPTCq5KkYecybkpSju9RhtbSKaqGyA6dNezhPF3WIWyR5oSW5Wm6E7EoTKRi2UDPJsBOO09g==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "immutable": "^3.8.2", "mapbox-gl": "^0.53.0", "prop-types": "^15.6.2", @@ -72399,118 +72150,118 @@ } }, "@superset-ui/legacy-plugin-chart-paired-t-test": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.17.27.tgz", - "integrity": "sha512-vrF5wPH12TMpOahjls/rUYuJNye/cqrM3Y1/84+VPNh+5Y3Vc/2TjxTX9x+ZJY/bUkSL6dcf1U4gIbaqg+LtAg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-paired-t-test/-/legacy-plugin-chart-paired-t-test-0.17.32.tgz", + "integrity": "sha512-O2dwG4/VuS3oHCYE3cpX52sqduDRLTMZIelmI4L0FcDO5Z1hpfxhFDFIfcqpk6KzxJnuwoCJ88Sf42zSS83HCw==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "distributions": "^1.0.0", "prop-types": "^15.6.2", "reactable": "^1.1.0" } }, "@superset-ui/legacy-plugin-chart-parallel-coordinates": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.17.27.tgz", - "integrity": "sha512-W/u4mneqJXAhxK53fSJkjnPCB5NYUcom1uxb7kXbMujuzgAK9OnWL9Mbs9JhDkW/H/benAqSk2vUO/1LMEAefA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-parallel-coordinates/-/legacy-plugin-chart-parallel-coordinates-0.17.32.tgz", + "integrity": "sha512-grHQYcOTzC+FQ3NGs87sPl9abCaradsucE0AUiG9ZM3/WX7M5SrTJ5fQgPmBejUVMzXGeJVcW8CvUYkL4wTsfg==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "prop-types": "^15.7.2" } }, "@superset-ui/legacy-plugin-chart-partition": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.17.27.tgz", - "integrity": "sha512-qqgDsu/HYuPBe4wvM070J2Dsxu+csg4CEi22Zrm5J0pVOk6NVehDNtD3ik+dzbBy0WipoWguV+jojnZtoKu+Gg==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-partition/-/legacy-plugin-chart-partition-0.17.32.tgz", + "integrity": "sha512-mn8gzhggUpeNGMBLOK0tEfalYZdrDUdHxKCrAkIMheS/L+/pZhb7gs2DPY7R746SvsT9ZRr5Q3IBv+Nv5XSDig==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-hierarchy": "^1.1.8", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-pivot-table": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.17.27.tgz", - "integrity": "sha512-chAV2F2BAAlVcZprt5Tcj0TUJc6AxWvxcXhE0QsS5eRDiPip0yBfpUSuLOA+2n+r8rfacuPrPso8YoWxHqP7UQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-pivot-table/-/legacy-plugin-chart-pivot-table-0.17.32.tgz", + "integrity": "sha512-fQ6+Qg3Mj9g3aLvNWn+C9SW1x7kQglotoc1cl0/L5SfVswsW4eqgZSWkLlvYGU3JlL5r+durIqD97gt5cmiB7g==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "datatables.net-bs": "^1.10.15", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-rose": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.17.27.tgz", - "integrity": "sha512-JCFW3UTmghMamLqneiePXJjYB+ovJSRSQJPiRG8DrOGTlXpOGeRSjjy5Chg9Kbm58nSw9KpB2coTD7asehsYqQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-rose/-/legacy-plugin-chart-rose-0.17.32.tgz", + "integrity": "sha512-HjzYOZP++kWVVclNYOtq2To6EAn6j+XZjllgd9EGn7TuF+e1DBHjHTOVyTWyJCMSMR1451kpjQouzZLzTIwQvw==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "nvd3": "1.8.6", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-sankey": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.17.27.tgz", - "integrity": "sha512-bpaVj0j6bi8QsidS50nsGYJeo9xxIg6SGyeXDO0iJKWYqoQVvuYiDGL/wW78kDBRSgrqbpopLTxNfpeUYBYFPA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey/-/legacy-plugin-chart-sankey-0.17.32.tgz", + "integrity": "sha512-xaX/b1aRgh5XoyjFIKOJkj/8WaI9oAP0tbBVLFaxzGhZzQvWwxOluTA1QqeR6ndKeLgbX8LZxc5xXd3kLLsBLA==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-sankey": "^0.4.2", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-sankey-loop": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.17.27.tgz", - "integrity": "sha512-e7cu8tCE+YKpOxdbFr1gBbaqWrdJlrwjC3iZasM61WynIgXPwn8DXwnBYGJ5JhhUmptPIjb69s2JoyouWLsvyw==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sankey-loop/-/legacy-plugin-chart-sankey-loop-0.17.32.tgz", + "integrity": "sha512-7R+1+MJ5Sel8z8dklMmU45/RktOkpmfLcE9zu7S7/vG3aI9ORrp5HFSl6PfzmQ71Q/uHww2m+18LAGMkFsxxTA==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3-sankey-diagram": "^0.7.3", "d3-selection": "^1.4.0", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-sunburst": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.17.27.tgz", - "integrity": "sha512-fwafwAhckYG3EAGyEa1saNuR5in5FIOex6HRv3LThHfaw2CRccKKyQk00kYOF2h1/JfLWpnFpRRNUSrBDYlQ8Q==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-sunburst/-/legacy-plugin-chart-sunburst-0.17.32.tgz", + "integrity": "sha512-zqg3uB/1n+x++PTeiKo2w6qCmTR2sLqWkTR8Towlg1wLNOJHSNbkxDa9CDx6TfKciUCGlJc/FtS4G9lLpwN5Yg==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-treemap": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.17.27.tgz", - "integrity": "sha512-pcGIBkZAzBbpBim7zhuE3LcUD3yq0D1XRmzIOogYMUvZyzJt1psquap53dW0as9nfP3IUwKSWPXohx4WSQ2cyQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-treemap/-/legacy-plugin-chart-treemap-0.17.32.tgz", + "integrity": "sha512-4g3plLNN/QNi2Zs9MkOTSdZtucaCFE5a/nKsuGKa0ZBc8Nm8xAS0u32fA/sBgdoV25uJTLedGfJMzgvqZ43epw==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3-hierarchy": "^1.1.8", "d3-selection": "^1.4.0", "prop-types": "^15.6.2" } }, "@superset-ui/legacy-plugin-chart-world-map": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.17.27.tgz", - "integrity": "sha512-XqtbOOqHryT25H5R++ngOLTRM2NpMRJ8Mu/t8E3cp2iCzUaNIAQfi/F/jko5sc9uU7POgNdTsU/PovC3vhL8KA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-plugin-chart-world-map/-/legacy-plugin-chart-world-map-0.17.32.tgz", + "integrity": "sha512-0/fLJ9kxLD8/R5eNCDLCCDuzwsX6m4r7N6Iqdliqk5HM9mBOJAhB/gpeE1jNoFvCwl4ntxnuToP6QN6xrjw0Qg==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-array": "^2.4.0", "d3-color": "^1.4.1", @@ -72534,13 +72285,13 @@ } }, "@superset-ui/legacy-preset-chart-big-number": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.17.27.tgz", - "integrity": "sha512-fdbDk5WgVTVPszHLR+uoaFQQYVfN5EscD7jzEe2Szv1STbpFHdlcPsa5OER4Yx3CctrCkLgKUNNxcZQ3+3WRCQ==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-big-number/-/legacy-preset-chart-big-number-0.17.32.tgz", + "integrity": "sha512-mSKwdG88WhySuqpmrCHa/R+cDfwqph/Vee4RsOrGzBFS/fPrlCotUbcLaoZnKJtso4cIIynSJRn29CB8bvd82A==", "requires": { "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@types/d3-color": "^1.2.2", "@types/shortid": "^0.0.29", "d3-color": "^1.2.3", @@ -72573,13 +72324,13 @@ } }, "@superset-ui/legacy-preset-chart-nvd3": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.17.27.tgz", - "integrity": "sha512-qP/G9dGTWm3nag8pstyhkmWROiEIMnVJsl0SHQ1foBXPeqYq0HXir0cQ4yzE8EdQiHREd6OECHw/K9TbNnYC8w==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/legacy-preset-chart-nvd3/-/legacy-preset-chart-nvd3-0.17.32.tgz", + "integrity": "sha512-P6HeR8/ik2edSSm+Sxe9TTaCcomb0fAtp7RKOB8lXIGU6oksdvxFcxCB9mbVlnn2ChH9nlUOw5JKo9v3r50w3w==", "requires": { "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "d3": "^3.5.17", "d3-tip": "^0.9.1", "dompurify": "^2.0.6", @@ -72593,34 +72344,44 @@ } }, "@superset-ui/plugin-chart-echarts": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.17.27.tgz", - "integrity": "sha512-CvZ5UrNo+3wwX5MeiWbCWMyciCJPnknTlGVKxjpCUosbikqq0YaBDb4mSg3pfKsuEnhLx7CRqXxYwYY/Pbgx9A==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-echarts/-/plugin-chart-echarts-0.17.32.tgz", + "integrity": "sha512-/ifeCsqh1/iSBMv41JWl4HaNUlLmKCVIQyDMTsdJr4MsvYQG1HHtDUpa3eHnMS7RlrLVZLVmlr59LS6S4Dcoww==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@types/mathjs": "^6.0.7", "d3-array": "^1.2.0", "echarts": "^5.0.2", "mathjs": "^8.0.1" } }, + "@superset-ui/plugin-chart-pivot-table": { + "version": "0.17.33", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-pivot-table/-/plugin-chart-pivot-table-0.17.33.tgz", + "integrity": "sha512-AOJb87OHLEmHZt+bFvt954ZrVvd/VAyxxpjaXSvQiDYpUHuhdUthNX0D3DcD7Gdc5HT2JI1myO2f/eMDTpmrMA==", + "requires": { + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", + "@superset-ui/react-pivottable": "^0.12.5" + } + }, "@superset-ui/plugin-chart-table": { - "version": "0.17.28", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.17.28.tgz", - "integrity": "sha512-E5F4Xgas9crKTPxiA8YwjZXNOXIodfbUXtgfUq4hDeiK+AlJdjE6Yh9FKKHqkko4Ua6laWYmQ3lj+tJ0576fXw==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-table/-/plugin-chart-table-0.17.32.tgz", + "integrity": "sha512-q3REacsT0oDTnM2ZaFy82Gc+ys62fhEBgZl/dkdxK/3To5jZ29++yU+ctYxdYuf0zFqGZltYRh9Ges03MwaS+g==", "requires": { "@emotion/core": "^10.0.28", - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", - "@types/d3-array": "^2.0.0", - "@types/react-table": "^7.0.19", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", + "@types/d3-array": "^2.9.0", + "@types/react-table": "^7.0.29", "d3-array": "^2.4.0", - "match-sorter": "^6.1.0", + "match-sorter": "^6.3.0", "memoize-one": "^5.1.1", - "react-table": "^7.2.1", - "regenerator-runtime": "^0.13.5", - "xss": "^1.0.6" + "react-table": "^7.6.3", + "regenerator-runtime": "^0.13.7", + "xss": "^1.0.8" }, "dependencies": { "d3-array": { @@ -72634,12 +72395,12 @@ } }, "@superset-ui/plugin-chart-word-cloud": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.17.27.tgz", - "integrity": "sha512-2PlHOM3rkBOrCU1MTj7HRifHTcd+2OKmdknNLUsI3cycmUjeWbSY9zhGf6z5cW7KgTBTSfJYXwiqxbrwxiNsdw==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/plugin-chart-word-cloud/-/plugin-chart-word-cloud-0.17.32.tgz", + "integrity": "sha512-G+ghQ5PW3rJMnbxnoo4yfG8HXJ5N23iFXfH/x4AqWgL0RyTJry/dsyvCpKMQNzvsB2M0dQbY5sy4/uG5JIo7Zg==", "requires": { - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@types/d3-cloud": "^1.2.1", "@types/d3-scale": "^2.0.2", "d3-cloud": "^1.2.5", @@ -72671,14 +72432,14 @@ } }, "@superset-ui/preset-chart-xy": { - "version": "0.17.27", - "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.17.27.tgz", - "integrity": "sha512-XlRf3OneDU8WABa2ZsCqkWagpw72PlWnuw1Aa3rnHjMSy8ZkGANv1KyCfld8Bewx/+k7kfYDGl0UC+oGnsGpoA==", + "version": "0.17.32", + "resolved": "https://registry.npmjs.org/@superset-ui/preset-chart-xy/-/preset-chart-xy-0.17.32.tgz", + "integrity": "sha512-tViT1tIBxzhpAbOZI465mF46M5UQ42qA89mGOdzqJLTVe5aggev3oNN8vP35rVsHPz9M96vN3fe8/G1aEqGi/g==", "requires": { "@data-ui/theme": "^0.0.84", "@data-ui/xy-chart": "^0.0.84", - "@superset-ui/chart-controls": "0.17.27", - "@superset-ui/core": "0.17.27", + "@superset-ui/chart-controls": "0.17.32", + "@superset-ui/core": "0.17.32", "@vx/axis": "^0.0.198", "@vx/legend": "^0.0.198", "@vx/scale": "^0.0.197", @@ -72796,6 +72557,18 @@ } } }, + "@superset-ui/react-pivottable": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@superset-ui/react-pivottable/-/react-pivottable-0.12.5.tgz", + "integrity": "sha512-9Nmj/sRdc6U/OUWBqfqHAaV05MCmNLt65I/Ar39brpLcaYKWRIYpBcUT3n/HOhCr8ALilnsLdQzzfjjaNAuv5w==", + "requires": { + "immutability-helper": "^3.1.1", + "prop-types": "^15.7.2", + "react-draggable": "^4.4.3", + "react-sortablejs": "^6.0.0", + "sortablejs": "^1.13.0" + } + }, "@svgr/babel-plugin-add-jsx-attribute": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz", @@ -74274,9 +74047,9 @@ "integrity": "sha1-dvjy6RWa5WKWWy+g5vvuGqZDobw=" }, "@types/d3-array": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-2.8.0.tgz", - "integrity": "sha512-Q0ubcGHAmCRPh90/hoYB4eKWhxYKUxphwSeQrlz2tiabQ8S9zqhaE2CZJtCaLH2cjqKcjr52WPvmOA7ha0O4ZA==" + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-2.9.0.tgz", + "integrity": "sha512-sdBMGfNvLUkBypPMEhOcKcblTQfgHbqbYrUqRE31jOwdDHBJBxz4co2MDAq93S4Cp++phk4UiwoEg/1hK3xXAQ==" }, "@types/d3-cloud": { "version": "1.2.3", @@ -74674,6 +74447,7 @@ "version": "0.32.22", "resolved": "https://registry.npmjs.org/@types/react-bootstrap/-/react-bootstrap-0.32.22.tgz", "integrity": "sha512-pjUVcJzogMxns3lbvMqnnU+I8EOYxl3aI13tS2vvRm0RdAe1rs7Ds/VZA29GI6p8p3Un6NqKUpW3+dgwAjyzxg==", + "dev": true, "requires": { "@types/react": "*" } @@ -74729,6 +74503,7 @@ "version": "5.5.4", "resolved": "https://registry.npmjs.org/@types/react-loadable/-/react-loadable-5.5.4.tgz", "integrity": "sha512-otKcjNCfVUzdBMdwOqFITTmBruIXw6GeoZitTBvJ6BMrif8Utu2JLy42GWukNnYI7ewJdncUCooz5Y/1dBz4+w==", + "dev": true, "requires": { "@types/react": "*", "@types/webpack": "*" @@ -74809,9 +74584,9 @@ } }, "@types/react-table": { - "version": "7.0.19", - "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-7.0.19.tgz", - "integrity": "sha512-RYyEY7Yry6F2JsKhHeFsGdzuFF1hMqBStQrrazDzpBl4m/ECGHJxFVQjLBRzRwK+47ZKNPm79f7qEpHirbiCLA==", + "version": "7.0.29", + "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-7.0.29.tgz", + "integrity": "sha512-RCGVKGlTDv3jbj37WJ5HhN3sPb0W/2rqlvyGUtvawnnyrxgI2BGgASvU93rq2jwanVp5J9l1NYAeiGlNhdaBGw==", "requires": { "@types/react": "*" } @@ -74951,6 +74726,12 @@ "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==", "dev": true }, + "@types/sortablejs": { + "version": "1.10.6", + "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.10.6.tgz", + "integrity": "sha512-QRz8Z+uw2Y4Gwrtxw8hD782zzuxxugdcq8X/FkPsXUa1kfslhGzy13+4HugO9FXNo+jlWVcE6DYmmegniIQ30A==", + "peer": true + }, "@types/source-list-map": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", @@ -74965,7 +74746,8 @@ "@types/tapable": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.4.tgz", - "integrity": "sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ==" + "integrity": "sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ==", + "dev": true }, "@types/testing-library__jest-dom": { "version": "5.9.5", @@ -75000,6 +74782,7 @@ "version": "4.39.1", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.39.1.tgz", "integrity": "sha512-rgO9ihNu/l72Sjx3shqwc9r6gi+tOMsqxhMEZhOEVIZt82GFOeUyEdpTk1BO2HqEHLS/XJW8ldUTIIfIMMyYFQ==", + "dev": true, "requires": { "@types/anymatch": "*", "@types/node": "*", @@ -75012,7 +74795,8 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true } } }, @@ -85418,7 +85202,7 @@ "version": "1.0.0-beta.1", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", - "devOptional": true + "dev": true }, "geojson-vt": { "version": "3.2.1", @@ -85657,7 +85441,7 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "devOptional": true + "dev": true }, "globalthis": { "version": "1.0.2", @@ -86523,6 +86307,11 @@ "resolved": "https://registry.npmjs.org/immer/-/immer-8.0.1.tgz", "integrity": "sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==" }, + "immutability-helper": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-3.1.1.tgz", + "integrity": "sha512-Q0QaXjPjwIju/28TsugCHNEASwoCcJSyJV3uO1sOIQGI0jKgm9f41Lvz0DZj3n46cNCyAZTsEYoY4C2bVRUzyQ==" + }, "immutable": { "version": "4.0.0-rc.12", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0-rc.12.tgz", @@ -92241,7 +92030,7 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "devOptional": true + "dev": true }, "json-bigint": { "version": "1.0.0", @@ -92994,9 +92783,9 @@ } }, "match-sorter": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.1.0.tgz", - "integrity": "sha512-sKPMf4kbF7Dm5Crx0bbfLpokK68PUJ/0STUIOPa1ZmTZEA3lCaPK3gapQR573oLmvdkTfGojzySkIwuq6Z6xRQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.0.tgz", + "integrity": "sha512-efYOf/wUpNb8FgNY+cOD2EIJI1S5I7YPKsw0LBp7wqPh5pmMS6i/wr3ZWwfwrAw1NvqTA2KUReVRWDX84lUcOQ==", "requires": { "@babel/runtime": "^7.12.5", "remove-accents": "0.4.2" @@ -99234,7 +99023,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.3.tgz", "integrity": "sha512-jV4TE59MBuWm7gb6Ns3Q1mxX8Azffb7oTtDtBgFkxRvhDp38YAARmRplrj0+XGkhOJB5XziArX+4HUUABtyZ0w==", - "dev": true, "requires": { "classnames": "^2.2.5", "prop-types": "^15.6.0" @@ -99270,8 +99058,7 @@ "react-error-boundary": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-1.2.5.tgz", - "integrity": "sha512-5CPSeLJA2igJNppAgFRwnTL9aK3ojenk65enNzhVyoxYNbHpIJXnChUO7+4vPhkncRA9wvQMXq6Azp2XeXd+iQ==", - "requires": {} + "integrity": "sha512-5CPSeLJA2igJNppAgFRwnTL9aK3ojenk65enNzhVyoxYNbHpIJXnChUO7+4vPhkncRA9wvQMXq6Azp2XeXd+iQ==" }, "react-error-overlay": { "version": "6.0.9", @@ -99358,8 +99145,7 @@ "react-icons": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.2.0.tgz", - "integrity": "sha512-rmzEDFt+AVXRzD7zDE21gcxyBizD/3NqjbX6cmViAgdqfJ2UiLer8927/QhhrXQV7dEj/1EGuOTPp7JnLYVJKQ==", - "requires": {} + "integrity": "sha512-rmzEDFt+AVXRzD7zDE21gcxyBizD/3NqjbX6cmViAgdqfJ2UiLer8927/QhhrXQV7dEj/1EGuOTPp7JnLYVJKQ==" }, "react-input-autosize": { "version": "2.2.2", @@ -99770,6 +99556,15 @@ "prop-types": "^15.5.7" } }, + "react-sortablejs": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/react-sortablejs/-/react-sortablejs-6.0.0.tgz", + "integrity": "sha512-vzi+TWOnofcYg+dYnC/Iz/ZZkBGG76uM6KaLwuAqBk0349JQxIy3PZizbK0TJdLlK6NnLt4CiEyyQXSSnVYvEw==", + "requires": { + "classnames": "^2.2.6", + "tiny-invariant": "^1.1.0" + } + }, "react-split": { "version": "2.0.9", "resolved": "https://registry.npmjs.org/react-split/-/react-split-2.0.9.tgz", @@ -99871,12 +99666,9 @@ } }, "react-table": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.2.1.tgz", - "integrity": "sha512-cICU3w5yFWTDluz9yFePziEQXGt3PK5R1Va3InP7qMGnZOKm8kv0GiDMli+VOqE1uM1dBYPb9BEad15up1ac6g==", - "requires": { - "@scarf/scarf": "^1.0.4" - } + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.6.3.tgz", + "integrity": "sha512-hfPF13zDLxPMpLKzIKCE8RZud9T/XrRTsaCIf8zXpWZIZ2juCl7qrGpo3AQw9eAetXV5DP7s2GDm+hht7qq5Dw==" }, "react-test-renderer": { "version": "16.9.0", @@ -100337,9 +100129,9 @@ } }, "regenerator-runtime": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", - "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" }, "regenerator-transform": { "version": "0.14.3", @@ -101488,7 +101280,7 @@ "version": "5.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", - "devOptional": true + "dev": true }, "send": { "version": "0.16.2", @@ -102129,6 +101921,11 @@ } } }, + "sortablejs": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.13.0.tgz", + "integrity": "sha512-RBJirPY0spWCrU5yCmWM1eFs/XgX2J5c6b275/YyxFRgnzPhKl/TDeU2hNR8Dt7ITq66NRPM4UlOt+e5O4CFHg==" + }, "source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -102720,12 +102517,6 @@ "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", "dev": true }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -104244,9 +104035,9 @@ "optional": true }, "tiny-invariant": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz", - "integrity": "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", + "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" }, "tiny-warning": { "version": "1.0.3", @@ -105003,9 +104794,9 @@ } }, "urijs": { - "version": "1.19.4", - "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.4.tgz", - "integrity": "sha512-2YF/wdFu02Gsly/wyx+S/f5w/oCF0ihVSgK/Sn8fcY/ZYMYtqxgi03Vi3V7HqyQP8mj8xHMuNFTBIPufmPRdoA==" + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.6.tgz", + "integrity": "sha512-eSXsXZ2jLvGWeLYlQA3Gh36BcjF+0amo92+wHPyN1mdR8Nxf75fuEuYTd9c0a+m/vhCjRK0ESlE9YNLW+E1VEw==" }, "urix": { "version": "0.1.0", diff --git a/superset-frontend/package.json b/superset-frontend/package.json index fda0d5de865bd..fda89d4815d19 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -65,34 +65,35 @@ "@babel/runtime-corejs3": "^7.12.5", "@data-ui/sparkline": "^0.0.84", "@emotion/core": "^10.0.35", - "@superset-ui/chart-controls": "^0.17.27", - "@superset-ui/core": "^0.17.27", - "@superset-ui/legacy-plugin-chart-calendar": "^0.17.27", - "@superset-ui/legacy-plugin-chart-chord": "^0.17.27", - "@superset-ui/legacy-plugin-chart-country-map": "^0.17.27", - "@superset-ui/legacy-plugin-chart-event-flow": "^0.17.27", - "@superset-ui/legacy-plugin-chart-force-directed": "^0.17.27", - "@superset-ui/legacy-plugin-chart-heatmap": "^0.17.27", - "@superset-ui/legacy-plugin-chart-histogram": "^0.17.27", - "@superset-ui/legacy-plugin-chart-horizon": "^0.17.27", - "@superset-ui/legacy-plugin-chart-map-box": "^0.17.27", - "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.17.27", - "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.17.27", - "@superset-ui/legacy-plugin-chart-partition": "^0.17.27", - "@superset-ui/legacy-plugin-chart-pivot-table": "^0.17.27", - "@superset-ui/legacy-plugin-chart-rose": "^0.17.27", - "@superset-ui/legacy-plugin-chart-sankey": "^0.17.27", - "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.17.27", - "@superset-ui/legacy-plugin-chart-sunburst": "^0.17.27", - "@superset-ui/legacy-plugin-chart-treemap": "^0.17.27", - "@superset-ui/legacy-plugin-chart-world-map": "^0.17.27", - "@superset-ui/legacy-preset-chart-big-number": "^0.17.27", + "@superset-ui/chart-controls": "^0.17.32", + "@superset-ui/core": "^0.17.32", + "@superset-ui/legacy-plugin-chart-calendar": "^0.17.32", + "@superset-ui/legacy-plugin-chart-chord": "^0.17.32", + "@superset-ui/legacy-plugin-chart-country-map": "^0.17.32", + "@superset-ui/legacy-plugin-chart-event-flow": "^0.17.32", + "@superset-ui/legacy-plugin-chart-force-directed": "^0.17.32", + "@superset-ui/legacy-plugin-chart-heatmap": "^0.17.32", + "@superset-ui/legacy-plugin-chart-histogram": "^0.17.32", + "@superset-ui/legacy-plugin-chart-horizon": "^0.17.32", + "@superset-ui/legacy-plugin-chart-map-box": "^0.17.32", + "@superset-ui/legacy-plugin-chart-paired-t-test": "^0.17.32", + "@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.17.32", + "@superset-ui/legacy-plugin-chart-partition": "^0.17.32", + "@superset-ui/legacy-plugin-chart-pivot-table": "^0.17.32", + "@superset-ui/legacy-plugin-chart-rose": "^0.17.32", + "@superset-ui/legacy-plugin-chart-sankey": "^0.17.32", + "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.17.32", + "@superset-ui/legacy-plugin-chart-sunburst": "^0.17.32", + "@superset-ui/legacy-plugin-chart-treemap": "^0.17.32", + "@superset-ui/legacy-plugin-chart-world-map": "^0.17.32", + "@superset-ui/legacy-preset-chart-big-number": "^0.17.32", "@superset-ui/legacy-preset-chart-deckgl": "^0.4.6", - "@superset-ui/legacy-preset-chart-nvd3": "^0.17.27", - "@superset-ui/plugin-chart-echarts": "^0.17.27", - "@superset-ui/plugin-chart-table": "^0.17.28", - "@superset-ui/plugin-chart-word-cloud": "^0.17.27", - "@superset-ui/preset-chart-xy": "^0.17.27", + "@superset-ui/legacy-preset-chart-nvd3": "^0.17.32", + "@superset-ui/plugin-chart-echarts": "^0.17.32", + "@superset-ui/plugin-chart-pivot-table": "^0.17.33", + "@superset-ui/plugin-chart-table": "^0.17.32", + "@superset-ui/plugin-chart-word-cloud": "^0.17.32", + "@superset-ui/preset-chart-xy": "^0.17.32", "@vx/responsive": "^0.0.195", "abortcontroller-polyfill": "^1.1.9", "antd": "^4.9.4", @@ -160,7 +161,7 @@ "react-split": "^2.0.9", "react-sticky": "^6.0.3", "react-syntax-highlighter": "^15.3.0", - "react-table": "^7.2.1", + "react-table": "^7.6.3", "react-transition-group": "^2.5.3", "react-ultimate-pagination": "^1.2.0", "react-virtualized": "9.19.1", @@ -174,7 +175,7 @@ "regenerator-runtime": "^0.13.5", "rison": "^0.1.1", "shortid": "^2.2.6", - "urijs": "^1.19.4", + "urijs": "^1.19.6", "use-immer": "^0.4.2", "use-query-params": "^1.1.9" }, diff --git a/superset-frontend/spec/fixtures/mockNativeFilters.ts b/superset-frontend/spec/fixtures/mockNativeFilters.ts index 6d475e3b0955e..d293103556a1a 100644 --- a/superset-frontend/spec/fixtures/mockNativeFilters.ts +++ b/superset-frontend/spec/fixtures/mockNativeFilters.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import { ExtraFormData } from '@superset-ui/core'; import { NativeFiltersState } from 'src/dashboard/reducers/types'; import { DataMaskStateWithId } from '../../src/dataMask/types'; @@ -76,44 +77,38 @@ export const nativeFilters: NativeFiltersState = { }; export const dataMaskWith2Filters: DataMaskStateWithId = { - crossFilters: {}, - ownFilters: {}, - nativeFilters: { - 'NATIVE_FILTER-e7Q8zKixx': { - id: 'NATIVE_FILTER-e7Q8zKixx', - extraFormData: { - append_form_data: { - filters: [ - { - col: 'region', - op: 'IN', - val: ['East Asia & Pacific'], - }, - ], + 'NATIVE_FILTER-e7Q8zKixx': { + id: 'NATIVE_FILTER-e7Q8zKixx', + ownState: {}, + extraFormData: { + filters: [ + { + col: 'region', + op: 'IN', + val: ['East Asia & Pacific'], }, - }, - currentState: { - value: ['East Asia & Pacific'], - }, + ], }, - 'NATIVE_FILTER-x9QPw0so1': { - id: 'NATIVE_FILTER-x9QPw0so1', - extraFormData: {}, - currentState: {}, + filterState: { + value: ['East Asia & Pacific'], }, }, + 'NATIVE_FILTER-x9QPw0so1': { + id: 'NATIVE_FILTER-x9QPw0so1', + ownState: {}, + extraFormData: {}, + filterState: {}, + }, }; -export const extraFormData = { - append_form_data: { - filters: [ - { - col: 'ethnic_minority', - op: 'IN', - val: 'No, not an ethnic minority', - }, - ], - }, +export const extraFormData: ExtraFormData = { + filters: [ + { + col: 'ethnic_minority', + op: 'IN', + val: ['No, not an ethnic minority'], + }, + ], }; export const NATIVE_FILTER_ID = 'NATIVE_FILTER-p4LImrSgA'; @@ -136,14 +131,12 @@ export const singleNativeFiltersState = { }, }; -export const dataMaskWith1Filter = { - nativeFilters: { - [NATIVE_FILTER_ID]: { - id: NATIVE_FILTER_ID, - extraFormData, - currentState: { - value: ['No, not an ethnic minority'], - }, +export const dataMaskWith1Filter: DataMaskStateWithId = { + [NATIVE_FILTER_ID]: { + id: NATIVE_FILTER_ID, + extraFormData, + filterState: { + value: ['No, not an ethnic minority'], }, }, }; diff --git a/superset-frontend/spec/helpers/reducerIndex.ts b/superset-frontend/spec/helpers/reducerIndex.ts index 41b5aff1b2cf4..f1cbfd47ffa49 100644 --- a/superset-frontend/spec/helpers/reducerIndex.ts +++ b/superset-frontend/spec/helpers/reducerIndex.ts @@ -17,6 +17,7 @@ * under the License. */ import charts from 'src/chart/chartReducer'; +import dataMask from 'src/dataMask/reducer'; import dashboardInfo from 'src/dashboard/reducers/dashboardInfo'; import dashboardState from 'src/dashboard/reducers/dashboardState'; import dashboardFilters from 'src/dashboard/reducers/dashboardFilters'; @@ -41,6 +42,7 @@ export default { datasources, dashboardInfo, dashboardFilters, + dataMask, nativeFilters, dashboardState, dashboardLayout, diff --git a/superset-frontend/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx b/superset-frontend/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx deleted file mode 100644 index ea3b65ca28eb8..0000000000000 --- a/superset-frontend/spec/javascripts/dashboard/components/HeaderActionsDropdown_spec.jsx +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import React from 'react'; -import { shallow } from 'enzyme'; -import { Menu, NoAnimationDropdown } from 'src/common/components'; -import RefreshIntervalModal from 'src/dashboard/components/RefreshIntervalModal'; -import HeaderActionsDropdown from 'src/dashboard/components/HeaderActionsDropdown'; -import SaveModal from 'src/dashboard/components/SaveModal'; -import CssEditor from 'src/dashboard/components/CssEditor'; -import fetchMock from 'fetch-mock'; -import ShareMenuItems from 'src/dashboard/components/menu/ShareMenuItems'; - -fetchMock.get('glob:*/csstemplateasyncmodelview/api/read', {}); - -describe('HeaderActionsDropdown', () => { - const props = { - addSuccessToast: () => {}, - addDangerToast: () => {}, - customCss: '', - dashboardId: 1, - dashboardInfo: {}, - dashboardTitle: 'Title', - editMode: false, - expandedSlices: {}, - filters: {}, - forceRefreshAllCharts: () => {}, - hasUnsavedChanges: false, - isLoading: false, - layout: {}, - onChange: () => {}, - onSave: () => {}, - refreshFrequency: 200, - setRefreshFrequency: () => {}, - shouldPersistRefreshFrequency: true, - showPropertiesModal: () => {}, - startPeriodicRender: () => {}, - updateCss: () => {}, - userCanEdit: false, - userCanSave: false, - lastModifiedTime: 0, - }; - - function setup(overrideProps) { - const wrapper = shallow( - , - ); - const menu = shallow( -
{wrapper.find(NoAnimationDropdown).props().overlay}
, - ); - return { wrapper, menu }; - } - - describe('readonly-user', () => { - const overrideProps = { userCanSave: false }; - - it('should render the DropdownButton', () => { - const { wrapper } = setup(overrideProps); - expect(wrapper.find(NoAnimationDropdown)).toExist(); - }); - - it('should not render the SaveModal', () => { - const { menu } = setup(overrideProps); - expect(menu.find(SaveModal)).not.toExist(); - }); - - it('should render available Menu items', () => { - const { menu } = setup(overrideProps); - expect(menu.find(Menu.Item)).toHaveLength(4); - }); - - it('should render the RefreshIntervalModal', () => { - const { menu } = setup(overrideProps); - expect(menu.find(RefreshIntervalModal)).toExist(); - }); - - it('should render the ShareMenuItems', () => { - const { menu } = setup(overrideProps); - expect(menu.find(ShareMenuItems)).toExist(); - }); - - it('should not render the CssEditor', () => { - const { menu } = setup(overrideProps); - expect(menu.find(CssEditor)).not.toExist(); - }); - }); - - describe('write-user', () => { - const overrideProps = { userCanSave: true }; - - it('should render the DropdownButton', () => { - const { wrapper } = setup(overrideProps); - expect(wrapper.find(NoAnimationDropdown)).toExist(); - }); - - it('should render the SaveModal', () => { - const { menu } = setup(overrideProps); - expect(menu.find(SaveModal)).toExist(); - }); - - it('should render available Menu items', () => { - const { menu } = setup(overrideProps); - expect(menu.find(Menu.Item)).toHaveLength(5); - }); - - it('should render the RefreshIntervalModal', () => { - const { menu } = setup(overrideProps); - expect(menu.find(RefreshIntervalModal)).toExist(); - }); - - it('should render the ShareMenuItems', () => { - const { menu } = setup(overrideProps); - expect(menu.find(ShareMenuItems)).toExist(); - }); - - it('should not render the CssEditor', () => { - const { menu } = setup(overrideProps); - expect(menu.find(CssEditor)).not.toExist(); - }); - }); - - describe('write-user-with-edit-mode', () => { - const overrideProps = { userCanSave: true, editMode: true }; - - it('should render the DropdownButton', () => { - const { wrapper } = setup(overrideProps); - expect(wrapper.find(NoAnimationDropdown)).toExist(); - }); - - it('should render the SaveModal', () => { - const { menu } = setup(overrideProps); - expect(menu.find(SaveModal)).toExist(); - }); - - it('should render available MenuItems', () => { - const { menu } = setup(overrideProps); - expect(menu.find(Menu.Item)).toHaveLength(6); - }); - - it('should render the RefreshIntervalModal', () => { - const { menu } = setup(overrideProps); - expect(menu.find(RefreshIntervalModal)).toExist(); - }); - - it('should render the ShareMenuItems', () => { - const { menu } = setup(overrideProps); - expect(menu.find(ShareMenuItems)).toExist(); - }); - - it('should render the CssEditor', () => { - const { menu } = setup(overrideProps); - expect(menu.find(CssEditor)).toExist(); - }); - }); -}); diff --git a/superset-frontend/spec/javascripts/dashboard/components/Header_spec.jsx b/superset-frontend/spec/javascripts/dashboard/components/Header_spec.jsx deleted file mode 100644 index 5e040dd5b6a83..0000000000000 --- a/superset-frontend/spec/javascripts/dashboard/components/Header_spec.jsx +++ /dev/null @@ -1,244 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import React from 'react'; -import { shallow } from 'enzyme'; -import Header from 'src/dashboard/components/Header'; -import EditableTitle from 'src/components/EditableTitle'; -import FaveStar from 'src/components/FaveStar'; -import PublishedStatus from 'src/dashboard/components/PublishedStatus'; -import HeaderActionsDropdown from 'src/dashboard/components/HeaderActionsDropdown'; -import Button from 'src/components/Button'; -import UndoRedoKeylisteners from 'src/dashboard/components/UndoRedoKeylisteners'; - -describe('Header', () => { - const props = { - addSuccessToast: () => {}, - addDangerToast: () => {}, - addWarningToast: () => {}, - dashboardInfo: { - id: 1, - dash_edit_perm: true, - dash_save_perm: true, - userId: 1, - metadata: {}, - common: { - conf: {}, - }, - }, - dashboardTitle: 'title', - charts: {}, - layout: {}, - filters: {}, - expandedSlices: {}, - css: '', - customCss: '', - isStarred: false, - isLoading: false, - lastModifiedTime: 0, - refreshFrequency: 0, - shouldPersistRefreshFrequency: false, - onSave: () => {}, - onChange: () => {}, - fetchFaveStar: () => {}, - fetchCharts: () => {}, - saveFaveStar: () => {}, - savePublished: () => {}, - isPublished: false, - updateDashboardTitle: () => {}, - editMode: false, - setEditMode: () => {}, - showBuilderPane: () => {}, - updateCss: () => {}, - setColorSchemeAndUnsavedChanges: () => {}, - logEvent: () => {}, - setRefreshFrequency: () => {}, - hasUnsavedChanges: false, - maxUndoHistoryExceeded: false, - - // redux - onUndo: () => {}, - onRedo: () => {}, - undoLength: 0, - redoLength: 0, - setMaxUndoHistoryExceeded: () => {}, - maxUndoHistoryToast: () => {}, - dashboardInfoChanged: () => {}, - dashboardTitleChanged: () => {}, - }; - - function setup(overrideProps) { - const wrapper = shallow(
); - return wrapper; - } - - describe('read-only-user', () => { - const overrideProps = { - dashboardInfo: { - ...props.dashboardInfo, - id: 1, - dash_edit_perm: false, - dash_save_perm: false, - userId: 1, - }, - }; - - it('should render the EditableTitle', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(EditableTitle)).toExist(); - }); - - it('should render the PublishedStatus', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(PublishedStatus)).toExist(); - }); - - it('should render the FaveStar', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(FaveStar)).toExist(); - }); - - it('should render the HeaderActionsDropdown', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(HeaderActionsDropdown)).toExist(); - }); - - it('should not set up undo/redo', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(UndoRedoKeylisteners)).not.toExist(); - }); - }); - - describe('write-user', () => { - const overrideProps = { - editMode: false, - dashboardInfo: { - ...props.dashboardInfo, - id: 1, - dash_edit_perm: true, - dash_save_perm: true, - userId: 1, - }, - }; - - it('should render the EditableTitle', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(EditableTitle)).toExist(); - }); - - it('should render the PublishedStatus', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(PublishedStatus)).toExist(); - }); - - it('should render the FaveStar', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(FaveStar)).toExist(); - }); - - it('should render the HeaderActionsDropdown', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(HeaderActionsDropdown)).toExist(); - }); - - it('should not set up undo/redo', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(UndoRedoKeylisteners)).not.toExist(); - }); - }); - - describe('write-user-with-edit-mode', () => { - const overrideProps = { - editMode: true, - dashboardInfo: { - ...props.dashboardInfo, - id: 1, - dash_edit_perm: true, - dash_save_perm: true, - userId: 1, - }, - }; - - it('should render the EditableTitle', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(EditableTitle)).toExist(); - }); - - it('should render the FaveStar', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(FaveStar)).toExist(); - }); - - it('should render the PublishedStatus', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(PublishedStatus)).toExist(); - }); - - it('should render the HeaderActionsDropdown', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(HeaderActionsDropdown)).toExist(); - }); - - it('should render five Buttons', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(Button)).toHaveLength(4); - }); - - it('should set up undo/redo', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(UndoRedoKeylisteners)).toExist(); - }); - }); - - describe('logged-out-user', () => { - const overrideProps = { - dashboardInfo: { - ...props.dashboardInfo, - id: 1, - dash_edit_perm: false, - dash_save_perm: false, - userId: null, - }, - }; - - it('should render the EditableTitle', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(EditableTitle)).toExist(); - }); - - it('should render the PublishedStatus', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(PublishedStatus)).toExist(); - }); - - it('should not render the FaveStar', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(FaveStar)).not.toExist(); - }); - - it('should render the HeaderActionsDropdown', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(HeaderActionsDropdown)).toExist(); - }); - - it('should not set up undo/redo', () => { - const wrapper = setup(overrideProps); - expect(wrapper.find(UndoRedoKeylisteners)).not.toExist(); - }); - }); -}); diff --git a/superset-frontend/spec/javascripts/dashboard/components/nativeFilters/FilterBar_spec.tsx b/superset-frontend/spec/javascripts/dashboard/components/nativeFilters/FilterBar_spec.tsx deleted file mode 100644 index ccd29e198a225..0000000000000 --- a/superset-frontend/spec/javascripts/dashboard/components/nativeFilters/FilterBar_spec.tsx +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import React from 'react'; -import { styledMount as mount } from 'spec/helpers/theming'; -import { Provider } from 'react-redux'; -import FilterBar from 'src/dashboard/components/nativeFilters/FilterBar/FilterBar'; -import Button from 'src/components/Button'; -import { mockStore } from 'spec/fixtures/mockStore'; - -describe('FilterBar', () => { - const props = { - filtersOpen: false, - toggleFiltersBar: jest.fn(), - }; - - const wrapper = mount( - - - , - ); - - it('is a valid', () => { - expect(React.isValidElement()).toBe(true); - }); - it('has filter and collapse icons', () => { - expect(wrapper.find({ name: 'filter' })).toExist(); - expect(wrapper.find({ name: 'collapse' })).toExist(); - }); - it('has apply and clear all buttons', () => { - expect(wrapper.find(Button).length).toBe(2); - expect(wrapper.find(Button).at(0)).toHaveProp('buttonStyle', 'tertiary'); - expect(wrapper.find(Button).at(1)).toHaveProp('buttonStyle', 'primary'); - }); -}); diff --git a/superset-frontend/spec/javascripts/dashboard/fixtures/mockNativeFilters.ts b/superset-frontend/spec/javascripts/dashboard/fixtures/mockNativeFilters.ts index aaef255674f62..6e6e4b8d3902b 100644 --- a/superset-frontend/spec/javascripts/dashboard/fixtures/mockNativeFilters.ts +++ b/superset-frontend/spec/javascripts/dashboard/fixtures/mockNativeFilters.ts @@ -16,11 +16,28 @@ * specific language governing permissions and limitations * under the License. */ -import { DataMaskStateWithId, DataMaskType } from 'src/dataMask/types'; +import { DataMaskStateWithId } from 'src/dataMask/types'; import { NativeFiltersState } from 'src/dashboard/reducers/types'; +export const mockDataMaskInfo: DataMaskStateWithId = { + DefaultsID: { + id: 'DefaultId', + ownState: {}, + filterState: { + value: [], + }, + }, +}; + export const nativeFiltersInfo: NativeFiltersState = { - filterSets: {}, + filterSets: { + 'set-id': { + id: 'DefaultsID', + name: 'Set name', + nativeFilters: {}, + dataMask: mockDataMaskInfo, + }, + }, filters: { DefaultsID: { cascadeParentIds: [], @@ -49,16 +66,3 @@ export const nativeFiltersInfo: NativeFiltersState = { }, }, }; - -export const mockDataMaskInfo: DataMaskStateWithId = { - [DataMaskType.CrossFilters]: {}, - [DataMaskType.OwnFilters]: {}, - [DataMaskType.NativeFilters]: { - DefaultsID: { - id: 'DefaultId', - currentState: { - value: [], - }, - }, - }, -}; diff --git a/superset-frontend/spec/javascripts/dashboard/util/getFormDataWithExtraFilters_spec.ts b/superset-frontend/spec/javascripts/dashboard/util/getFormDataWithExtraFilters_spec.ts index e30ef3f128f7f..38c29e0f5d00c 100644 --- a/superset-frontend/spec/javascripts/dashboard/util/getFormDataWithExtraFilters_spec.ts +++ b/superset-frontend/spec/javascripts/dashboard/util/getFormDataWithExtraFilters_spec.ts @@ -64,14 +64,11 @@ describe('getFormDataWithExtraFilters', () => { }, }, dataMask: { - crossFilters: {}, - ownFilters: {}, - nativeFilters: { - [filterId]: { - id: filterId, - extraFormData: {}, - currentState: {}, - }, + [filterId]: { + id: filterId, + extraFormData: {}, + filterState: {}, + ownState: {}, }, }, layout: (dashboardLayout.present as unknown) as { diff --git a/superset-frontend/spec/javascripts/filters/utils_spec.ts b/superset-frontend/spec/javascripts/filters/utils_spec.ts index dcc02b7053185..d997e6495cb4b 100644 --- a/superset-frontend/spec/javascripts/filters/utils_spec.ts +++ b/superset-frontend/spec/javascripts/filters/utils_spec.ts @@ -35,64 +35,56 @@ describe('Filter utils', () => { describe('getRangeExtraFormData', () => { it('getRangeExtraFormData - col: "testCol", lower: 1, upper: 2', () => { expect(getRangeExtraFormData('testCol', 1, 2)).toEqual({ - append_form_data: { - filters: [ - { - col: 'testCol', - op: '>=', - val: 1, - }, - { - col: 'testCol', - op: '<=', - val: 2, - }, - ], - }, + filters: [ + { + col: 'testCol', + op: '>=', + val: 1, + }, + { + col: 'testCol', + op: '<=', + val: 2, + }, + ], }); }); it('getRangeExtraFormData - col: "testCol", lower: 0, upper: 0', () => { expect(getRangeExtraFormData('testCol', 0, 0)).toEqual({ - append_form_data: { - filters: [ - { - col: 'testCol', - op: '>=', - val: 0, - }, - { - col: 'testCol', - op: '<=', - val: 0, - }, - ], - }, + filters: [ + { + col: 'testCol', + op: '>=', + val: 0, + }, + { + col: 'testCol', + op: '<=', + val: 0, + }, + ], }); }); it('getRangeExtraFormData - col: "testCol", lower: null, upper: 2', () => { expect(getRangeExtraFormData('testCol', null, 2)).toEqual({ - append_form_data: { - filters: [ - { - col: 'testCol', - op: '<=', - val: 2, - }, - ], - }, + filters: [ + { + col: 'testCol', + op: '<=', + val: 2, + }, + ], }); }); it('getRangeExtraFormData - col: "testCol", lower: 1, upper: undefined', () => { expect(getRangeExtraFormData('testCol', 1, undefined)).toEqual({ - append_form_data: { - filters: [ - { - col: 'testCol', - op: '>=', - val: 1, - }, - ], - }, + filters: [ + { + col: 'testCol', + op: '>=', + val: 1, + }, + ], }); }); }); @@ -101,68 +93,56 @@ describe('Filter utils', () => { expect( getSelectExtraFormData('testCol', ['value'], false, false), ).toEqual({ - append_form_data: { - filters: [ - { - col: 'testCol', - op: 'IN', - val: ['value'], - }, - ], - }, + filters: [ + { + col: 'testCol', + op: 'IN', + val: ['value'], + }, + ], }); }); it('getSelectExtraFormData - col: "testCol", value: ["value"], emptyFilter: true, inverseSelection: false', () => { expect(getSelectExtraFormData('testCol', ['value'], true, false)).toEqual( { - append_form_data: { - adhoc_filters: [ - { - clause: 'WHERE', - expressionType: 'SQL', - sqlExpression: '1 = 0', - }, - ], - }, + adhoc_filters: [ + { + clause: 'WHERE', + expressionType: 'SQL', + sqlExpression: '1 = 0', + }, + ], }, ); }); it('getSelectExtraFormData - col: "testCol", value: ["value"], emptyFilter: false, inverseSelection: true', () => { expect(getSelectExtraFormData('testCol', ['value'], false, true)).toEqual( { - append_form_data: { - filters: [ - { - col: 'testCol', - op: 'NOT IN', - val: ['value'], - }, - ], - }, + filters: [ + { + col: 'testCol', + op: 'NOT IN', + val: ['value'], + }, + ], }, ); }); it('getSelectExtraFormData - col: "testCol", value: [], emptyFilter: false, inverseSelection: false', () => { expect(getSelectExtraFormData('testCol', [], false, false)).toEqual({ - append_form_data: { - filters: [], - }, + filters: [], }); }); it('getSelectExtraFormData - col: "testCol", value: undefined, emptyFilter: false, inverseSelection: false', () => { expect( getSelectExtraFormData('testCol', undefined, false, false), ).toEqual({ - append_form_data: { - filters: [], - }, + filters: [], }); }); it('getSelectExtraFormData - col: "testCol", value: null, emptyFilter: false, inverseSelection: false', () => { expect(getSelectExtraFormData('testCol', null, false, false)).toEqual({ - append_form_data: { - filters: [], - }, + filters: [], }); }); }); diff --git a/superset-frontend/spec/javascripts/profile/UserInfo_spec.tsx b/superset-frontend/spec/javascripts/profile/UserInfo_spec.tsx index 57ae89156d4b0..9d8a904cb568b 100644 --- a/superset-frontend/spec/javascripts/profile/UserInfo_spec.tsx +++ b/superset-frontend/spec/javascripts/profile/UserInfo_spec.tsx @@ -18,7 +18,6 @@ */ import React from 'react'; import Gravatar from 'react-gravatar'; -import { Panel } from 'react-bootstrap'; import { mount } from 'enzyme'; import UserInfo from 'src/profile/components/UserInfo'; @@ -37,7 +36,7 @@ describe('UserInfo', () => { }); it('renders a Panel', () => { const wrapper = mount(); - expect(wrapper.find(Panel)).toExist(); + expect(wrapper.find('.panel')).toExist(); }); it('renders 5 icons', () => { const wrapper = mount(); diff --git a/superset-frontend/spec/javascripts/sqllab/ShareSqlLabQuery_spec.jsx b/superset-frontend/spec/javascripts/sqllab/ShareSqlLabQuery_spec.jsx index cb32ab182299c..8a68d386a56a4 100644 --- a/superset-frontend/spec/javascripts/sqllab/ShareSqlLabQuery_spec.jsx +++ b/superset-frontend/spec/javascripts/sqllab/ShareSqlLabQuery_spec.jsx @@ -121,14 +121,12 @@ describe('ShareSqlLabQuery', () => { remoteId: undefined, }, }; - await act(async () => { - render(, { - wrapper: standardProvider, - }); + + render(, { + wrapper: standardProvider, }); - const button = screen.getByRole('button', { name: /copy link/i }); - const style = window.getComputedStyle(button); - expect(style.color).toBe('rgb(102, 102, 102)'); + const button = await screen.findByRole('button', { name: /copy link/i }); + expect(button).toBeDisabled(); }); }); }); diff --git a/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx b/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx index aef614fae9aac..c667560ba2ec7 100644 --- a/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx +++ b/superset-frontend/spec/javascripts/sqllab/TableElement_spec.jsx @@ -24,7 +24,6 @@ import { supersetTheme, ThemeProvider } from '@superset-ui/core'; import Collapse from 'src/common/components/Collapse'; import { IconTooltip } from 'src/components/IconTooltip'; -import Fade from 'src/common/components/Fade'; import TableElement from 'src/SqlLab/components/TableElement'; import ColumnElement from 'src/SqlLab/components/ColumnElement'; import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; @@ -88,11 +87,15 @@ describe('TableElement', () => { }, ); expect(wrapper.find(TableElement).state().hovered).toBe(false); - expect(wrapper.find(Fade).props().hovered).toBe(false); + expect(wrapper.find('[data-test="fade"]').first().props().hovered).toBe( + false, + ); wrapper.find('.header-container').hostNodes().simulate('mouseEnter'); await waitForComponentToPaint(wrapper, 300); expect(wrapper.find(TableElement).state().hovered).toBe(true); - expect(wrapper.find(Fade).props().hovered).toBe(true); + expect(wrapper.find('[data-test="fade"]').first().props().hovered).toBe( + true, + ); }); it('sorts columns', () => { const wrapper = mount( diff --git a/superset-frontend/spec/javascripts/views/CRUD/alert/AlertList_spec.jsx b/superset-frontend/spec/javascripts/views/CRUD/alert/AlertList_spec.jsx index 3a1b0955b3074..b92e36cbefd80 100644 --- a/superset-frontend/spec/javascripts/views/CRUD/alert/AlertList_spec.jsx +++ b/superset-frontend/spec/javascripts/views/CRUD/alert/AlertList_spec.jsx @@ -23,7 +23,7 @@ import { Provider } from 'react-redux'; import thunk from 'redux-thunk'; import { styledMount as mount } from 'spec/helpers/theming'; import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; -import { Switch } from 'src/common/components/Switch'; +import { Switch } from 'src/components/Switch'; import ListView from 'src/components/ListView'; import SubMenu from 'src/components/Menu/SubMenu'; import AlertList from 'src/views/CRUD/alert/AlertList'; diff --git a/superset-frontend/spec/javascripts/views/CRUD/alert/AlertReportModal_spec.jsx b/superset-frontend/spec/javascripts/views/CRUD/alert/AlertReportModal_spec.jsx index b4a0131ae4b07..f494c237aa281 100644 --- a/superset-frontend/spec/javascripts/views/CRUD/alert/AlertReportModal_spec.jsx +++ b/superset-frontend/spec/javascripts/views/CRUD/alert/AlertReportModal_spec.jsx @@ -26,7 +26,7 @@ import AlertReportModal from 'src/views/CRUD/alert/AlertReportModal'; import Modal from 'src/common/components/Modal'; import { AsyncSelect, NativeGraySelect as Select } from 'src/components/Select'; import { Radio } from 'src/common/components/Radio'; -import { Switch } from 'src/common/components/Switch'; +import { Switch } from 'src/components/Switch'; import TextAreaControl from 'src/explore/components/controls/TextAreaControl'; import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; import { styledMount as mount } from 'spec/helpers/theming'; diff --git a/superset-frontend/spec/javascripts/views/CRUD/data/savedquery/SavedQueryList_spec.jsx b/superset-frontend/spec/javascripts/views/CRUD/data/savedquery/SavedQueryList_spec.jsx index a57cf9b54afe8..019c8c3fa8cee 100644 --- a/superset-frontend/spec/javascripts/views/CRUD/data/savedquery/SavedQueryList_spec.jsx +++ b/superset-frontend/spec/javascripts/views/CRUD/data/savedquery/SavedQueryList_spec.jsx @@ -22,6 +22,12 @@ import configureStore from 'redux-mock-store'; import { Provider } from 'react-redux'; import fetchMock from 'fetch-mock'; import { styledMount as mount } from 'spec/helpers/theming'; +import { render, screen, cleanup } from 'spec/helpers/testing-library'; +import userEvent from '@testing-library/user-event'; +import { QueryParamProvider } from 'use-query-params'; +import { act } from 'react-dom/test-utils'; +import { handleBulkSavedQueryExport } from 'src/views/CRUD/utils'; +import * as featureFlags from 'src/featureFlags'; import SavedQueryList from 'src/views/CRUD/data/savedquery/SavedQueryList'; import SubMenu from 'src/components/Menu/SubMenu'; import ListView from 'src/components/ListView'; @@ -31,7 +37,6 @@ import DeleteModal from 'src/components/DeleteModal'; import Button from 'src/components/Button'; import IndeterminateCheckbox from 'src/components/IndeterminateCheckbox'; import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint'; -import { act } from 'react-dom/test-utils'; // store needed for withToasts(DatabaseList) const mockStore = configureStore([thunk]); @@ -70,8 +75,44 @@ const mockqueries = [...new Array(3)].map((_, i) => ({ ], })); +// ---------- For import testing ---------- +// Create an one more mocked query than the original mocked query array +const mockOneMoreQuery = [...new Array(mockqueries.length + 1)].map((_, i) => ({ + created_by: { + id: i, + first_name: `user`, + last_name: `${i}`, + }, + created_on: `${i}-2020`, + database: { + database_name: `db ${i}`, + id: i, + }, + changed_on_delta_humanized: '1 day ago', + db_id: i, + description: `SQL for ${i}`, + id: i, + label: `query ${i}`, + schema: 'public', + sql: `SELECT ${i} FROM table`, + sql_tables: [ + { + catalog: null, + schema: null, + table: `${i}`, + }, + ], +})); +// Grab the last mocked query, to mock import +const mockNewImportQuery = mockOneMoreQuery.pop(); +// Create a new file out of mocked import query to mock upload +const mockImportFile = new File( + [mockNewImportQuery], + 'saved_query_import_mock.json', +); + fetchMock.get(queriesInfoEndpoint, { - permissions: ['can_write'], + permissions: ['can_write', 'can_read'], }); fetchMock.get(queriesEndpoint, { result: mockqueries, @@ -91,6 +132,9 @@ fetchMock.get(queriesDistinctEndpoint, { result: [], }); +// Mock utils module +jest.mock('src/views/CRUD/utils'); + describe('SavedQueryList', () => { const wrapper = mount( @@ -179,3 +223,107 @@ describe('SavedQueryList', () => { ); }); }); + +describe('RTL', () => { + async function renderAndWait() { + const mounted = act(async () => { + render( + + + + + , + ); + }); + + return mounted; + } + + let isFeatureEnabledMock; + beforeEach(async () => { + isFeatureEnabledMock = jest + .spyOn(featureFlags, 'isFeatureEnabled') + .mockImplementation(() => true); + await renderAndWait(); + }); + + afterEach(() => { + cleanup(); + isFeatureEnabledMock.mockRestore(); + }); + it('renders an export button in the bulk actions', () => { + // Grab and click the "Bulk Select" button to expose checkboxes + const bulkSelectButton = screen.getByRole('button', { + name: /bulk select/i, + }); + userEvent.click(bulkSelectButton); + + // Grab and click the "toggle all" checkbox to expose export button + const selectAllCheckbox = screen.getByRole('checkbox', { + name: /toggle all rows selected/i, + }); + userEvent.click(selectAllCheckbox); + + // Grab and assert that export button is visible + const exportButton = screen.getByRole('button', { + name: /export/i, + }); + expect(exportButton).toBeVisible(); + }); + + it('renders an export button in the actions bar', async () => { + // Grab Export action button and mock mouse hovering over it + const exportActionButton = screen.getAllByRole('button')[18]; + userEvent.hover(exportActionButton); + + // Wait for the tooltip to pop up + await screen.findByRole('tooltip'); + + // Grab and assert that "Export Query" tooltip is in the document + const exportTooltip = screen.getByRole('tooltip', { + name: /export query/i, + }); + expect(exportTooltip).toBeInTheDocument(); + }); + + it('runs handleBulkSavedQueryExport when export is clicked', () => { + // Grab Export action button and mock mouse clicking it + const exportActionButton = screen.getAllByRole('button')[18]; + userEvent.click(exportActionButton); + + expect(handleBulkSavedQueryExport).toHaveBeenCalled(); + }); + + it('renders an import button in the submenu', () => { + // Grab and assert that import saved query button is visible + const importSavedQueryButton = screen.getAllByRole('button')[2]; + expect(importSavedQueryButton).toBeVisible(); + }); + + it('renders an import model when import button is clicked', async () => { + // Grab and click import saved query button to reveal modal + const importSavedQueryButton = screen.getAllByRole('button')[2]; + userEvent.click(importSavedQueryButton); + + // Grab and assert that saved query import modal's heading is visible + const importSavedQueryModalHeading = screen.getByRole('heading', { + name: /import saved query/i, + }); + expect(importSavedQueryModalHeading).toBeVisible(); + }); + + it('imports a saved query', () => { + // Grab and click import saved query button to reveal modal + const importSavedQueryButton = screen.getAllByRole('button')[2]; + userEvent.click(importSavedQueryButton); + + // Grab "Choose File" input from import modal + const chooseFileInput = screen.getByLabelText(/file\*/i); + // Upload mocked import file + userEvent.upload(chooseFileInput, mockImportFile); + + expect(chooseFileInput.files[0]).toStrictEqual(mockImportFile); + expect(chooseFileInput.files.item(0)).toStrictEqual(mockImportFile); + expect(chooseFileInput.files).toHaveLength(1); + }); +}); diff --git a/superset-frontend/src/SqlLab/components/QueryTable.jsx b/superset-frontend/src/SqlLab/components/QueryTable.jsx index b761244e297c2..1749c880537ad 100644 --- a/superset-frontend/src/SqlLab/components/QueryTable.jsx +++ b/superset-frontend/src/SqlLab/components/QueryTable.jsx @@ -19,7 +19,7 @@ import React, { useMemo } from 'react'; import PropTypes from 'prop-types'; import moment from 'moment'; -import { Well } from 'react-bootstrap'; +import Card from 'src/common/components/Card'; import ProgressBar from 'src/components/ProgressBar'; import Label from 'src/components/Label'; import { t } from '@superset-ui/core'; @@ -129,14 +129,14 @@ const QueryTable = props => { ); q.sql = ( - + - + ); if (q.resultsKey) { q.output = ( diff --git a/superset-frontend/src/SqlLab/components/RunQueryActionButton.tsx b/superset-frontend/src/SqlLab/components/RunQueryActionButton.tsx index 877eac3e8c422..9d22ce5b7c70b 100644 --- a/superset-frontend/src/SqlLab/components/RunQueryActionButton.tsx +++ b/superset-frontend/src/SqlLab/components/RunQueryActionButton.tsx @@ -25,7 +25,7 @@ import Icon from 'src/components/Icon'; import { DropdownButton, DropdownButtonProps, -} from 'src/common/components/DropdownButton'; +} from 'src/components/DropdownButton'; interface Props { allowAsync: boolean; diff --git a/superset-frontend/src/SqlLab/components/ShareSqlLabQuery.tsx b/superset-frontend/src/SqlLab/components/ShareSqlLabQuery.tsx index 66c709c5d7e78..10e3e43c31c25 100644 --- a/superset-frontend/src/SqlLab/components/ShareSqlLabQuery.tsx +++ b/superset-frontend/src/SqlLab/components/ShareSqlLabQuery.tsx @@ -17,9 +17,7 @@ * under the License. */ import React from 'react'; -import { Tooltip } from 'src/common/components/Tooltip'; -import { t, styled, supersetTheme } from '@superset-ui/core'; -import cx from 'classnames'; +import { t, useTheme } from '@superset-ui/core'; import Button from 'src/components/Button'; import withToasts from 'src/messageToasts/enhancers/withToasts'; @@ -41,24 +39,12 @@ interface ShareSqlLabQueryPropTypes { addDangerToast: (msg: string) => void; } -const Styles = styled.div` - .btn-disabled { - &, - &:hover { - cursor: default; - background-color: ${supersetTheme.colors.grayscale.light2}; - color: ${supersetTheme.colors.grayscale.base}; - } - } - svg { - vertical-align: -${supersetTheme.gridUnit * 1.25}px; - } -`; - function ShareSqlLabQuery({ queryEditor, addDangerToast, }: ShareSqlLabQueryPropTypes) { + const theme = useTheme(); + const getCopyUrlForKvStore = (callback: Function) => { const { dbId, title, schema, autorun, sql } = queryEditor; const sharedQuery = { dbId, title, schema, autorun, sql }; @@ -94,58 +80,41 @@ function ShareSqlLabQuery({ return getCopyUrlForSavedQuery(callback); }; - const buildButton = () => { - const canShare = - queryEditor.remoteId || - isFeatureEnabled(FeatureFlag.SHARE_QUERIES_VIA_KV_STORE); + const buildButton = (canShare: boolean) => { + const tooltip = canShare + ? t('Copy query link to your clipboard') + : t('Save the query to enable this feature'); return ( - - - + ); }; const canShare = - queryEditor.remoteId || + !!queryEditor.remoteId || isFeatureEnabled(FeatureFlag.SHARE_QUERIES_VIA_KV_STORE); return ( - + <> {canShare ? ( ) : ( - buildButton() + buildButton(canShare) )} - + ); } diff --git a/superset-frontend/src/SqlLab/components/TableElement.jsx b/superset-frontend/src/SqlLab/components/TableElement.jsx index 0811d955a1e91..ec7ce38aba801 100644 --- a/superset-frontend/src/SqlLab/components/TableElement.jsx +++ b/superset-frontend/src/SqlLab/components/TableElement.jsx @@ -18,14 +18,13 @@ */ import React from 'react'; import PropTypes from 'prop-types'; -import { Well } from 'react-bootstrap'; +import Card from 'src/common/components/Card'; import Collapse from 'src/common/components/Collapse'; import ButtonGroup from 'src/components/ButtonGroup'; import shortid from 'shortid'; import { t, styled } from '@superset-ui/core'; import { debounce } from 'lodash'; -import Fade from 'src/common/components/Fade'; import { Tooltip } from 'src/common/components/Tooltip'; import CopyToClipboard from '../../components/CopyToClipboard'; import { IconTooltip } from '../../components/IconTooltip'; @@ -52,6 +51,11 @@ const StyledSpan = styled.span` cursor: pointer; `; +const Fade = styled.div` + transition: all ${({ theme }) => theme.transitionTiming}s; + opacity: ${props => (props.hovered ? 1 : 0)}; +`; + class TableElement extends React.PureComponent { constructor(props) { super(props); @@ -111,14 +115,14 @@ class TableElement extends React.PureComponent { ); latest = latest.join('/'); header = ( - +
{t('latest partition:')} {latest} {' '} {partitionClipBoard}
-
+ ); } return header; @@ -215,6 +219,7 @@ class TableElement extends React.PureComponent { ) : ( e.stopPropagation()} > diff --git a/superset-frontend/src/SqlLab/main.less b/superset-frontend/src/SqlLab/main.less index d262ef8fdef59..fa27eefc6c68f 100644 --- a/superset-frontend/src/SqlLab/main.less +++ b/superset-frontend/src/SqlLab/main.less @@ -294,7 +294,8 @@ div.Workspace { .queryPane { flex: 1 1 auto; padding-left: 10px; - overflow: visible; + overflow-y: visible; + overflow-x: scroll; } .schemaPane-enter-done, diff --git a/superset-frontend/src/chart/Chart.jsx b/superset-frontend/src/chart/Chart.jsx index 3032b33a3069d..7ec31b6fb9abc 100644 --- a/superset-frontend/src/chart/Chart.jsx +++ b/superset-frontend/src/chart/Chart.jsx @@ -62,6 +62,7 @@ const propTypes = { onQuery: PropTypes.func, onFilterMenuOpen: PropTypes.func, onFilterMenuClose: PropTypes.func, + ownState: PropTypes.object, }; const BLANK = {}; @@ -127,6 +128,7 @@ class Chart extends React.PureComponent { this.props.timeout, this.props.chartId, this.props.dashboardId, + this.props.ownState, ); } else { // Create chart with POST request @@ -136,6 +138,7 @@ class Chart extends React.PureComponent { this.props.timeout, this.props.chartId, this.props.dashboardId, + this.props.ownState, ); } } diff --git a/superset-frontend/src/chart/ChartRenderer.jsx b/superset-frontend/src/chart/ChartRenderer.jsx index bb9857861346c..f40e46e83cba4 100644 --- a/superset-frontend/src/chart/ChartRenderer.jsx +++ b/superset-frontend/src/chart/ChartRenderer.jsx @@ -45,7 +45,7 @@ const propTypes = { setDataMask: PropTypes.func, onFilterMenuOpen: PropTypes.func, onFilterMenuClose: PropTypes.func, - ownCurrentState: PropTypes.object, + ownState: PropTypes.object, }; const BLANK = {}; @@ -94,7 +94,8 @@ class ChartRenderer extends React.Component { return ( this.hasQueryResponseChange || nextProps.annotationData !== this.props.annotationData || - nextProps.ownCurrentState !== this.props.ownCurrentState || + nextProps.ownState !== this.props.ownState || + nextProps.filterState !== this.props.filterState || nextProps.height !== this.props.height || nextProps.width !== this.props.width || nextProps.triggerRender || @@ -184,7 +185,8 @@ class ChartRenderer extends React.Component { annotationData, datasource, initialValues, - ownCurrentState, + ownState, + filterState, formData, queriesResponse, } = this.props; @@ -224,9 +226,10 @@ class ChartRenderer extends React.Component { datasource={datasource} initialValues={initialValues} formData={formData} - ownCurrentState={ownCurrentState} + ownState={ownState} + filterState={filterState} hooks={this.hooks} - behaviors={[Behavior.CROSS_FILTER]} + behaviors={[Behavior.INTERACTIVE_CHART]} queriesData={queriesResponse} onRenderSuccess={this.handleRenderSuccess} onRenderFailure={this.handleRenderFailure} diff --git a/superset-frontend/src/chart/chartAction.js b/superset-frontend/src/chart/chartAction.js index 906a41c4f36a1..5e5fc4624cf01 100644 --- a/superset-frontend/src/chart/chartAction.js +++ b/superset-frontend/src/chart/chartAction.js @@ -161,6 +161,7 @@ const v1ChartDataRequest = async ( force, requestParams, setDataMask, + ownState, ) => { const payload = buildV1ChartDataPayload({ formData, @@ -168,6 +169,7 @@ const v1ChartDataRequest = async ( resultFormat, force, setDataMask, + ownState, }); // The dashboard id is added to query params for tracking purposes @@ -205,6 +207,7 @@ export async function getChartDataRequest({ force = false, method = 'POST', requestParams = {}, + ownState = {}, }) { let querySettings = { ...requestParams, @@ -235,6 +238,7 @@ export async function getChartDataRequest({ force, querySettings, setDataMask, + ownState, ); } @@ -351,6 +355,7 @@ export function exploreJSON( key, method, dashboardId, + ownState, ) { return async dispatch => { const logStart = Logger.getTimestamp(); @@ -373,6 +378,7 @@ export function exploreJSON( force, method, requestParams, + ownState, }); dispatch(chartUpdateStarted(controller, formData, key)); @@ -470,6 +476,7 @@ export function getSavedChart( timeout = 60, key, dashboardId, + ownState, ) { /* * Perform a GET request to `/explore_json`. @@ -481,7 +488,15 @@ export function getSavedChart( * GET /explore_json?{"chart_id":1,"extra_filters":"..."} * */ - return exploreJSON(formData, force, timeout, key, 'GET', dashboardId); + return exploreJSON( + formData, + force, + timeout, + key, + 'GET', + dashboardId, + ownState, + ); } export const POST_CHART_FORM_DATA = 'POST_CHART_FORM_DATA'; @@ -491,6 +506,7 @@ export function postChartFormData( timeout = 60, key, dashboardId, + ownState, ) { /* * Perform a POST request to `/explore_json`. @@ -498,7 +514,15 @@ export function postChartFormData( * This will post the form data to the endpoint, returning a new chart. * */ - return exploreJSON(formData, force, timeout, key, 'POST', dashboardId); + return exploreJSON( + formData, + force, + timeout, + key, + 'POST', + dashboardId, + ownState, + ); } export function redirectSQLLab(formData) { @@ -537,6 +561,7 @@ export function refreshChart(chartKey, force, dashboardId) { timeout, chart.id, dashboardId, + getState().dataMask[chart.id]?.ownState, ), ); }; diff --git a/superset-frontend/src/chart/chartReducer.ts b/superset-frontend/src/chart/chartReducer.ts index 3f68c043428d7..d6e42dfa87f60 100644 --- a/superset-frontend/src/chart/chartReducer.ts +++ b/superset-frontend/src/chart/chartReducer.ts @@ -18,9 +18,10 @@ */ /* eslint camelcase: 0 */ import { t } from '@superset-ui/core'; +import { HYDRATE_DASHBOARD } from 'src/dashboard/actions/hydrate'; import { ChartState } from 'src/explore/types'; import { getFormDataFromControls } from 'src/explore/controlUtils'; -import { now } from '../modules/dates'; +import { now } from 'src/modules/dates'; import * as actions from './chartAction'; export const chart: ChartState = { @@ -192,7 +193,9 @@ export default function chartReducer( delete charts[key]; return charts; } - + if (action.type === HYDRATE_DASHBOARD) { + return { ...action.data.charts }; + } if (action.type in actionHandlers) { return { ...charts, diff --git a/superset-frontend/src/common/components/Card.tsx b/superset-frontend/src/common/components/Card.tsx new file mode 100644 index 0000000000000..285af246b7e9b --- /dev/null +++ b/superset-frontend/src/common/components/Card.tsx @@ -0,0 +1,39 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { styled } from '@superset-ui/core'; +import AntdCard, { CardProps as AntdCardProps } from 'antd/lib/card'; + +interface CardProps extends AntdCardProps { + padded?: boolean; +} + +const Card = styled(({ padded, ...props }: CardProps) => ( + +))` + background-color: ${({ theme }) => theme.colors.grayscale.light4}; + border-radius: ${({ theme }) => theme.borderRadius}px; + + .ant-card-body { + padding: ${({ padded, theme }) => + padded ? theme.gridUnit * 4 : theme.gridUnit}px; + } +`; + +export default Card; diff --git a/superset-frontend/src/common/components/common.stories.tsx b/superset-frontend/src/common/components/common.stories.tsx index 3b31993a4e065..fa63130599a27 100644 --- a/superset-frontend/src/common/components/common.stories.tsx +++ b/superset-frontend/src/common/components/common.stories.tsx @@ -20,14 +20,13 @@ import React, { useState, useRef, useCallback } from 'react'; import { action } from '@storybook/addon-actions'; import { withKnobs, boolean, select } from '@storybook/addon-knobs'; import Button from 'src/components/Button'; +import { CronPicker, CronError } from 'src/components/CronPicker'; import Modal from './Modal'; import Tabs, { EditableTabs } from './Tabs'; import { Tooltip as AntdTooltip } from './Tooltip'; -import { Switch as AntdSwitch } from './Switch'; import { Menu, Input, Divider } from '.'; import { Dropdown } from './Dropdown'; import InfoTooltip from './InfoTooltip'; -import { CronPicker, CronError } from './CronPicker'; export default { title: 'Common components', @@ -198,14 +197,6 @@ StyledInfoTooltip.argTypes = { }, }; -export const Switch = () => ( - <> - -
- - -); - export function StyledCronPicker() { // @ts-ignore const inputRef = useRef(null); diff --git a/superset-frontend/src/common/components/index.tsx b/superset-frontend/src/common/components/index.tsx index 41a0d1b5159bf..9c336110fa632 100644 --- a/superset-frontend/src/common/components/index.tsx +++ b/superset-frontend/src/common/components/index.tsx @@ -28,7 +28,7 @@ import { DropDownProps } from 'antd/lib/dropdown'; export { AutoComplete, Avatar, - Card, + Button, Checkbox, Col, DatePicker, @@ -51,13 +51,18 @@ export { Tooltip, Input as AntdInput, } from 'antd'; +export { Card as AntdCard } from 'antd'; export { FormInstance } from 'antd/lib/form'; export { RadioChangeEvent } from 'antd/lib/radio'; export { TreeProps } from 'antd/lib/tree'; export { default as Alert, AlertProps } from 'antd/lib/alert'; export { default as Select, SelectProps } from 'antd/lib/select'; +export { default as List, ListItemProps } from 'antd/lib/list'; export { default as Collapse } from './Collapse'; +export { default as Badge } from 'src/components/Badge'; +export { default as Card } from './Card'; +export { default as Progress } from 'src/components/ProgressBar'; export const MenuItem = styled(AntdMenu.Item)` > a { diff --git a/superset-frontend/src/common/hooks/apiResources/dashboards.ts b/superset-frontend/src/common/hooks/apiResources/dashboards.ts new file mode 100644 index 0000000000000..0bb21f16bfb60 --- /dev/null +++ b/superset-frontend/src/common/hooks/apiResources/dashboards.ts @@ -0,0 +1,41 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Dashboard from 'src/types/Dashboard'; +import { useApiV1Resource, useTransformedResource } from './apiResources'; + +export const useDashboard = (idOrSlug: string | number) => + useTransformedResource( + useApiV1Resource(`/api/v1/dashboard/${idOrSlug}`), + dashboard => ({ + ...dashboard, + metadata: JSON.parse(dashboard.json_metadata), + position_data: JSON.parse(dashboard.position_json), + }), + ); + +// gets the chart definitions for a dashboard +export const useDashboardCharts = (idOrSlug: string | number) => + useApiV1Resource(`/api/v1/dashboard/${idOrSlug}/charts`); + +// gets the datasets for a dashboard +// important: this endpoint only returns the fields in the dataset +// that are necessary for rendering the given dashboard +export const useDashboardDatasets = (idOrSlug: string | number) => + useApiV1Resource(`/api/v1/dashboard/${idOrSlug}/datasets`); diff --git a/superset-frontend/src/common/hooks/apiResources/index.ts b/superset-frontend/src/common/hooks/apiResources/index.ts index 8befc73735770..5e63920731144 100644 --- a/superset-frontend/src/common/hooks/apiResources/index.ts +++ b/superset-frontend/src/common/hooks/apiResources/index.ts @@ -26,4 +26,5 @@ export { // A central catalog of API Resource hooks. // Add new API hooks here, organized under // different files for different resource types. -export { useChartOwnerNames } from './charts'; +export * from './charts'; +export * from './dashboards'; diff --git a/superset-frontend/src/components/AlteredSliceTag/AlteredSliceTag.test.jsx b/superset-frontend/src/components/AlteredSliceTag/AlteredSliceTag.test.jsx index 8e76501dd513e..4bec3da0945ae 100644 --- a/superset-frontend/src/components/AlteredSliceTag/AlteredSliceTag.test.jsx +++ b/superset-frontend/src/components/AlteredSliceTag/AlteredSliceTag.test.jsx @@ -148,7 +148,7 @@ describe('AlteredSliceTag', () => { const td = getTableWrapperFromModalBody(modalBody).find('td'); expect(td).toHaveLength(21); ['control', 'before', 'after'].forEach((v, i) => { - expect(td.find('Cell').get(0).props.columns[i].id).toBe(v); + expect(td.find('defaultRenderer').get(0).props.columns[i].id).toBe(v); }); }); }); diff --git a/superset-frontend/src/common/components/CronPicker/CronPicker.test.tsx b/superset-frontend/src/components/CronPicker/CronPicker.test.tsx similarity index 100% rename from superset-frontend/src/common/components/CronPicker/CronPicker.test.tsx rename to superset-frontend/src/components/CronPicker/CronPicker.test.tsx diff --git a/superset-frontend/src/common/components/CronPicker/CronPicker.tsx b/superset-frontend/src/components/CronPicker/CronPicker.tsx similarity index 100% rename from superset-frontend/src/common/components/CronPicker/CronPicker.tsx rename to superset-frontend/src/components/CronPicker/CronPicker.tsx diff --git a/superset-frontend/src/common/components/CronPicker/index.ts b/superset-frontend/src/components/CronPicker/index.ts similarity index 100% rename from superset-frontend/src/common/components/CronPicker/index.ts rename to superset-frontend/src/components/CronPicker/index.ts diff --git a/superset-frontend/src/common/components/DropdownButton/DropdownButton.stories.tsx b/superset-frontend/src/components/DropdownButton/DropdownButton.stories.tsx similarity index 100% rename from superset-frontend/src/common/components/DropdownButton/DropdownButton.stories.tsx rename to superset-frontend/src/components/DropdownButton/DropdownButton.stories.tsx diff --git a/superset-frontend/src/common/components/DropdownButton/index.tsx b/superset-frontend/src/components/DropdownButton/index.tsx similarity index 100% rename from superset-frontend/src/common/components/DropdownButton/index.tsx rename to superset-frontend/src/components/DropdownButton/index.tsx diff --git a/superset-frontend/src/components/ErrorBoundary/index.jsx b/superset-frontend/src/components/ErrorBoundary/index.jsx index 7bc00758afd98..0a1d0c7c46510 100644 --- a/superset-frontend/src/components/ErrorBoundary/index.jsx +++ b/superset-frontend/src/components/ErrorBoundary/index.jsx @@ -38,7 +38,7 @@ export default class ErrorBoundary extends React.Component { } componentDidCatch(error, info) { - this.props.onError(error, info); + if (this.props.onError) this.props.onError(error, info); this.setState({ error, info }); } diff --git a/superset-frontend/src/components/ErrorMessage/types.ts b/superset-frontend/src/components/ErrorMessage/types.ts index 2ffc655580224..5ae5e7f85ddb4 100644 --- a/superset-frontend/src/components/ErrorMessage/types.ts +++ b/superset-frontend/src/components/ErrorMessage/types.ts @@ -28,8 +28,13 @@ export const ErrorTypeEnum = { GENERIC_DB_ENGINE_ERROR: 'GENERIC_DB_ENGINE_ERROR', COLUMN_DOES_NOT_EXIST_ERROR: 'COLUMN_DOES_NOT_EXIST_ERROR', TABLE_DOES_NOT_EXIST_ERROR: 'TABLE_DOES_NOT_EXIST_ERROR', - TEST_CONNECTION_PORT_CLOSED_ERROR: 'TEST_CONNECTION_PORT_CLOSED_ERROR', - TEST_CONNECTION_HOST_DOWN_ERROR: 'TEST_CONNECTION_HOST_DOWN_ERROR', + CONNECTION_INVALID_USERNAME_ERROR: 'CONNECTION_INVALID_USERNAME_ERROR', + CONNECTION_INVALID_PASSWORD_ERROR: 'CONNECTION_INVALID_PASSWORD_ERROR', + CONNECTION_INVALID_HOSTNAME_ERROR: 'CONNECTION_INVALID_HOSTNAME_ERROR', + CONNECTION_PORT_CLOSED_ERROR: 'CONNECTION_PORT_CLOSED_ERROR', + CONNECTION_HOST_DOWN_ERROR: 'CONNECTION_HOST_DOWN_ERROR', + CONNECTION_ACCESS_DENIED_ERROR: 'CONNECTION_ACCESS_DENIED_ERROR', + CONNECTION_UNKNOWN_DATABASE_ERROR: 'CONNECTION_UNKNOWN_DATABASE_ERROR', // Viz errors VIZ_GET_DF_ERROR: 'VIZ_GET_DF_ERROR', @@ -48,8 +53,6 @@ export const ErrorTypeEnum = { // Sqllab error MISSING_TEMPLATE_PARAMS_ERROR: 'MISSING_TEMPLATE_PARAMS_ERROR', - TEST_CONNECTION_INVALID_HOSTNAME_ERROR: - 'TEST_CONNECTION_INVALID_HOSTNAME_ERROR', // Generic errors GENERIC_COMMAND_ERROR: 'GENERIC_COMMAND_ERROR', diff --git a/superset-frontend/src/components/ListViewCard/index.tsx b/superset-frontend/src/components/ListViewCard/index.tsx index 1090d8b880524..b8586ddffff62 100644 --- a/superset-frontend/src/components/ListViewCard/index.tsx +++ b/superset-frontend/src/components/ListViewCard/index.tsx @@ -19,7 +19,7 @@ import React from 'react'; import { styled } from '@superset-ui/core'; import Icon from 'src/components/Icon'; -import { Card, Skeleton, ThinSkeleton } from 'src/common/components'; +import { AntdCard, Skeleton, ThinSkeleton } from 'src/common/components'; import { Tooltip } from 'src/common/components/Tooltip'; import ImageLoader, { BackgroundPosition } from './ImageLoader'; @@ -29,7 +29,7 @@ const ActionsWrapper = styled.div` justify-content: space-between; `; -const StyledCard = styled(Card)` +const StyledCard = styled(AntdCard)` border: 1px solid #d9dbe4; border-radius: ${({ theme }) => theme.gridUnit}px; overflow: hidden; @@ -198,7 +198,7 @@ function ListViewCard({ } > {loading && ( - @@ -221,7 +221,7 @@ function ListViewCard({ /> )} {!loading && ( - diff --git a/superset-frontend/spec/javascripts/explore/components/FixedOrMetricControl_spec.jsx b/superset-frontend/src/components/Slider/Slider.stories.tsx similarity index 54% rename from superset-frontend/spec/javascripts/explore/components/FixedOrMetricControl_spec.jsx rename to superset-frontend/src/components/Slider/Slider.stories.tsx index dae156cd24f8e..e5eea76304aa8 100644 --- a/superset-frontend/spec/javascripts/explore/components/FixedOrMetricControl_spec.jsx +++ b/superset-frontend/src/components/Slider/Slider.stories.tsx @@ -16,28 +16,42 @@ * specific language governing permissions and limitations * under the License. */ -/* eslint-disable no-unused-expressions */ import React from 'react'; -import { shallow } from 'enzyme'; +import Slider, { SliderSingleProps } from '.'; -import TextControl from 'src/explore/components/controls/TextControl'; -import FixedOrMetricControl from 'src/explore/components/controls/FixedOrMetricControl'; -import MetricsControl from 'src/explore/components/controls/MetricControl/MetricsControl'; - -const defaultProps = { - value: {}, - datasource: {}, +export default { + title: 'Slider', + component: Slider, }; -describe('FixedOrMetricControl', () => { - let wrapper; +export const InteractiveSlider = (args: SliderSingleProps) => ( + +); - beforeEach(() => { - wrapper = shallow(); - }); +InteractiveSlider.args = { + min: 0, + max: 100, + defaultValue: 70, + step: 1, +}; - it('renders a TextControl and a SelectControl', () => { - expect(wrapper.find(TextControl)).toExist(); - expect(wrapper.find(MetricsControl)).toExist(); - }); -}); +InteractiveSlider.argTypes = { + onChange: { action: 'onChange' }, + disabled: { + control: { type: 'boolean' }, + }, + reverse: { + control: { type: 'boolean' }, + }, + vertical: { + control: { type: 'boolean' }, + }, +}; + +InteractiveSlider.story = { + parameters: { + knobs: { + disable: true, + }, + }, +}; diff --git a/superset-frontend/src/components/BootstrapSliderWrapper/index.jsx b/superset-frontend/src/components/Slider/index.tsx similarity index 71% rename from superset-frontend/src/components/BootstrapSliderWrapper/index.jsx rename to superset-frontend/src/components/Slider/index.tsx index 5d2a5c8da13a1..904833e174f84 100644 --- a/superset-frontend/src/components/BootstrapSliderWrapper/index.jsx +++ b/superset-frontend/src/components/Slider/index.tsx @@ -17,14 +17,13 @@ * under the License. */ import React from 'react'; -import ReactBootstrapSlider from 'react-bootstrap-slider'; -import 'bootstrap-slider/dist/css/bootstrap-slider.min.css'; -import './BootstrapSliderWrapper.less'; +import AntDSlider, { + SliderSingleProps, + SliderRangeProps, +} from 'antd/lib/slider'; -export default function BootstrapSliderWrapper(props) { - return ( - - - - ); +export { SliderSingleProps, SliderRangeProps }; + +export default function Slider(props: SliderSingleProps | SliderRangeProps) { + return ; } diff --git a/superset-frontend/src/common/components/Switch/Switch.stories.tsx b/superset-frontend/src/components/Switch/Switch.stories.tsx similarity index 100% rename from superset-frontend/src/common/components/Switch/Switch.stories.tsx rename to superset-frontend/src/components/Switch/Switch.stories.tsx diff --git a/superset-frontend/src/common/components/Switch/index.tsx b/superset-frontend/src/components/Switch/index.tsx similarity index 100% rename from superset-frontend/src/common/components/Switch/index.tsx rename to superset-frontend/src/components/Switch/index.tsx diff --git a/superset-frontend/src/dashboard/App.jsx b/superset-frontend/src/dashboard/App.jsx index da06a0130fa31..43d00f5a579c5 100644 --- a/superset-frontend/src/dashboard/App.jsx +++ b/superset-frontend/src/dashboard/App.jsx @@ -25,22 +25,28 @@ import { HTML5Backend } from 'react-dnd-html5-backend'; import { DynamicPluginProvider } from 'src/components/DynamicPlugins'; import setupApp from '../setup/setupApp'; import setupPlugins from '../setup/setupPlugins'; -import DashboardContainer from './containers/Dashboard'; +import DashboardPage from './containers/DashboardPage'; import { theme } from '../preamble'; setupApp(); setupPlugins(); -const App = ({ store }) => ( - - - - - - - - - -); +const App = ({ store }) => { + const dashboardIdOrSlug = window.location.pathname.split('/')[3]; + return ( + + + + + + + + + + ); +}; export default hot(App); diff --git a/superset-frontend/src/dashboard/actions/dashboardState.js b/superset-frontend/src/dashboard/actions/dashboardState.js index 5bfc1dceb5f38..ac2ead0d74330 100644 --- a/superset-frontend/src/dashboard/actions/dashboardState.js +++ b/superset-frontend/src/dashboard/actions/dashboardState.js @@ -41,6 +41,8 @@ import serializeActiveFilterValues from '../util/serializeActiveFilterValues'; import serializeFilterScopes from '../util/serializeFilterScopes'; import { getActiveFilters } from '../util/activeDashboardFilters'; import { safeStringify } from '../../utils/safeStringify'; +import { FeatureFlag, isFeatureEnabled } from '../../featureFlags'; +import { setChartConfiguration } from './dashboardInfo'; export const SET_UNSAVED_CHANGES = 'SET_UNSAVED_CHANGES'; export function setUnsavedChanges(hasUnsavedChanges) { @@ -199,6 +201,28 @@ export function saveDashboardRequest(data, id, saveType) { }, }) .then(response => { + if (isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS)) { + const { + dashboardInfo: { + metadata: { chart_configuration = {} }, + }, + } = getState(); + const chartConfiguration = Object.values(chart_configuration).reduce( + (prev, next) => { + // If chart removed from dashboard - remove it from metadata + if ( + Object.values(layout).find( + layoutItem => layoutItem?.meta?.chartId === next.id, + ) + ) { + return { ...prev, [next.id]: next }; + } + return prev; + }, + {}, + ); + dispatch(setChartConfiguration(chartConfiguration)); + } dispatch(saveDashboardRequestSuccess(response.json.last_modified_time)); dispatch(addSuccessToast(t('This dashboard was saved successfully.'))); return response; diff --git a/superset-frontend/src/dashboard/reducers/getInitialState.js b/superset-frontend/src/dashboard/actions/hydrate.js similarity index 52% rename from superset-frontend/src/dashboard/reducers/getInitialState.js rename to superset-frontend/src/dashboard/actions/hydrate.js index 19ea54e789f1f..d304eda535dd4 100644 --- a/superset-frontend/src/dashboard/reducers/getInitialState.js +++ b/superset-frontend/src/dashboard/actions/hydrate.js @@ -17,46 +17,88 @@ * under the License. */ /* eslint-disable camelcase */ -import { isString } from 'lodash'; +import { isString, keyBy } from 'lodash'; import shortid from 'shortid'; -import { CategoricalColorNamespace } from '@superset-ui/core'; +import { + Behavior, + CategoricalColorNamespace, + getChartMetadataRegistry, +} from '@superset-ui/core'; +import querystring from 'query-string'; +import { chart } from 'src/chart/chartReducer'; import { initSliceEntities } from 'src/dashboard/reducers/sliceEntities'; import { getInitialState as getInitialNativeFilterState } from 'src/dashboard/reducers/nativeFilters'; import { getParam } from 'src/modules/utils'; import { applyDefaultFormData } from 'src/explore/store'; import { buildActiveFilters } from 'src/dashboard/util/activeDashboardFilters'; +import findPermission from 'src/dashboard/util/findPermission'; import { DASHBOARD_FILTER_SCOPE_GLOBAL, dashboardFilter, -} from './dashboardFilters'; -import { chart } from '../../chart/chartReducer'; +} from 'src/dashboard/reducers/dashboardFilters'; import { DASHBOARD_HEADER_ID, GRID_DEFAULT_CHART_WIDTH, GRID_COLUMN_COUNT, -} from '../util/constants'; + DASHBOARD_ROOT_ID, +} from 'src/dashboard/util/constants'; import { DASHBOARD_HEADER_TYPE, CHART_TYPE, ROW_TYPE, -} from '../util/componentTypes'; -import findFirstParentContainerId from '../util/findFirstParentContainer'; -import getEmptyLayout from '../util/getEmptyLayout'; -import getFilterConfigsFromFormdata from '../util/getFilterConfigsFromFormdata'; -import getLocationHash from '../util/getLocationHash'; -import newComponentFactory from '../util/newComponentFactory'; -import { TIME_RANGE } from '../../visualizations/FilterBox/FilterBox'; +} from 'src/dashboard/util/componentTypes'; +import findFirstParentContainerId from 'src/dashboard/util/findFirstParentContainer'; +import getEmptyLayout from 'src/dashboard/util/getEmptyLayout'; +import getFilterConfigsFromFormdata from 'src/dashboard/util/getFilterConfigsFromFormdata'; +import getLocationHash from 'src/dashboard/util/getLocationHash'; +import newComponentFactory from 'src/dashboard/util/newComponentFactory'; +import { TIME_RANGE } from 'src/visualizations/FilterBox/FilterBox'; +import { FeatureFlag, isFeatureEnabled } from '../../featureFlags'; + +const reservedQueryParams = new Set(['standalone', 'edit']); + +/** + * Returns the url params that are used to customize queries + * in datasets built using sql lab. + * We may want to extract this to some kind of util in the future. + */ +const extractUrlParams = queryParams => + Object.entries(queryParams).reduce((acc, [key, value]) => { + if (reservedQueryParams.has(key)) return acc; + // if multiple url params share the same key (?foo=bar&foo=baz), they will appear as an array. + // Only one value can be used for a given query param, so we just take the first one. + if (Array.isArray(value)) { + return { + ...acc, + [key]: value[0], + }; + } + return { ...acc, [key]: value }; + }, {}); -export default function getInitialState(bootstrapData) { - const { user_id, datasources, common, editMode, urlParams } = bootstrapData; +export const HYDRATE_DASHBOARD = 'HYDRATE_DASHBOARD'; + +export const hydrateDashboard = (dashboardData, chartData, datasourcesData) => ( + dispatch, + getState, +) => { + const { user, common } = getState(); + const { metadata } = dashboardData; + const queryParams = querystring.parse(window.location.search); + const urlParams = extractUrlParams(queryParams); + const editMode = queryParams.edit === 'true'; - const dashboard = { ...bootstrapData.dashboard_data }; let preselectFilters = {}; + + chartData.forEach(chart => { + // eslint-disable-next-line no-param-reassign + chart.slice_id = chart.form_data.slice_id; + }); try { // allow request parameter overwrite dashboard metadata preselectFilters = JSON.parse( - getParam('preselect_filters') || dashboard.metadata.default_filters, + getParam('preselect_filters') || metadata.default_filters, ); } catch (e) { // @@ -64,12 +106,12 @@ export default function getInitialState(bootstrapData) { // Priming the color palette with user's label-color mapping provided in // the dashboard's JSON metadata - if (dashboard.metadata && dashboard.metadata.label_colors) { - const scheme = dashboard.metadata.color_scheme; - const namespace = dashboard.metadata.color_namespace; - const colorMap = isString(dashboard.metadata.label_colors) - ? JSON.parse(dashboard.metadata.label_colors) - : dashboard.metadata.label_colors; + if (metadata?.label_colors) { + const scheme = metadata.color_scheme; + const namespace = metadata.color_namespace; + const colorMap = isString(metadata.label_colors) + ? JSON.parse(metadata.label_colors) + : metadata.label_colors; Object.keys(colorMap).forEach(label => { CategoricalColorNamespace.getScale(scheme, namespace).setColor( label, @@ -79,11 +121,11 @@ export default function getInitialState(bootstrapData) { } // dashboard layout - const { position_json: positionJson } = dashboard; - // new dash: positionJson could be {} or null + const { position_data } = dashboardData; + // new dash: position_json could be {} or null const layout = - positionJson && Object.keys(positionJson).length > 0 - ? positionJson + position_data && Object.keys(position_data).length > 0 + ? position_data : getEmptyLayout(); // create a lookup to sync layout names with slice names @@ -100,13 +142,13 @@ export default function getInitialState(bootstrapData) { let newSlicesContainer; let newSlicesContainerWidth = 0; - const filterScopes = dashboard.metadata.filter_scopes || {}; + const filterScopes = metadata?.filter_scopes || {}; const chartQueries = {}; const dashboardFilters = {}; const slices = {}; const sliceIds = new Set(); - dashboard.slices.forEach(slice => { + chartData.forEach(slice => { const key = slice.slice_id; const form_data = { ...slice.form_data, @@ -168,10 +210,7 @@ export default function getInitialState(bootstrapData) { } // build DashboardFilters for interactive filter features - if ( - slice.form_data.viz_type === 'filter_box' || - slice.form_data.viz_type === 'filter_select' - ) { + if (slice.form_data.viz_type === 'filter_box') { const configs = getFilterConfigsFromFormdata(slice.form_data); let { columns } = configs; const { labels } = configs; @@ -240,7 +279,7 @@ export default function getInitialState(bootstrapData) { id: DASHBOARD_HEADER_ID, type: DASHBOARD_HEADER_TYPE, meta: { - text: dashboard.dashboard_title, + text: dashboardData.dashboard_title, }, }; @@ -259,54 +298,100 @@ export default function getInitialState(bootstrapData) { } const nativeFilters = getInitialNativeFilterState({ - filterConfig: dashboard.metadata.native_filter_configuration || [], - filterSetsConfig: dashboard.metadata.filter_sets_configuration || [], + filterConfig: metadata?.native_filter_configuration || [], + filterSetsConfig: metadata?.filter_sets_configuration || [], }); - return { - datasources, - sliceEntities: { ...initSliceEntities, slices, isLoading: false }, - charts: chartQueries, - // read-only data - dashboardInfo: { - id: dashboard.id, - slug: dashboard.slug, - metadata: dashboard.metadata, - userId: user_id, - dash_edit_perm: dashboard.dash_edit_perm, - dash_save_perm: dashboard.dash_save_perm, - superset_can_explore: dashboard.superset_can_explore, - superset_can_csv: dashboard.superset_can_csv, - slice_can_edit: dashboard.slice_can_edit, - common: { - flash_messages: common.flash_messages, - conf: common.conf, + if (isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS)) { + // If user just added cross filter to dashboard it's not saving it scope on server, + // so we tweak it until user will update scope and will save it in server + Object.values(dashboardLayout.present).forEach(layoutItem => { + const chartId = layoutItem.meta?.chartId; + const behaviors = + ( + getChartMetadataRegistry().get( + chartQueries[chartId]?.formData?.viz_type, + ) ?? {} + )?.behaviors ?? []; + + if (!metadata.chart_configuration) { + metadata.chart_configuration = {}; + } + if ( + behaviors.includes(Behavior.INTERACTIVE_CHART) && + !metadata.chart_configuration[chartId] + ) { + metadata.chart_configuration[chartId] = { + id: chartId, + crossFilters: { + scope: { + rootPath: [DASHBOARD_ROOT_ID], + excluded: [chartId], // By default it doesn't affects itself + }, + }, + }; + } + }); + } + + const { roles } = getState().user; + + return dispatch({ + type: HYDRATE_DASHBOARD, + data: { + datasources: keyBy(datasourcesData, 'uid'), + sliceEntities: { ...initSliceEntities, slices, isLoading: false }, + charts: chartQueries, + // read-only data + dashboardInfo: { + ...dashboardData, + metadata, + userId: String(user.userId), // legacy, please use state.user instead + dash_edit_perm: findPermission('can_write', 'Dashboard', roles), + dash_save_perm: findPermission('can_save_dash', 'Superset', roles), + dash_share_perm: findPermission( + 'can_share_dashboard', + 'Superset', + roles, + ), + superset_can_explore: findPermission('can_explore', 'Superset', roles), + superset_can_share: findPermission( + 'can_share_chart', + 'Superset', + roles, + ), + superset_can_csv: findPermission('can_csv', 'Superset', roles), + slice_can_edit: findPermission('can_slice', 'Superset', roles), + common: { + // legacy, please use state.common instead + flash_messages: common.flash_messages, + conf: common.conf, + }, }, - lastModifiedTime: dashboard.last_modified_time, - }, - dashboardFilters, - nativeFilters, - dashboardState: { - sliceIds: Array.from(sliceIds), - directPathToChild, - directPathLastUpdated: Date.now(), - focusedFilterField: null, - expandedSlices: dashboard.metadata.expanded_slices || {}, - refreshFrequency: dashboard.metadata.refresh_frequency || 0, - // dashboard viewers can set refresh frequency for the current visit, - // only persistent refreshFrequency will be saved to backend - shouldPersistRefreshFrequency: false, - css: dashboard.css || '', - colorNamespace: dashboard.metadata.color_namespace, - colorScheme: dashboard.metadata.color_scheme, - editMode: dashboard.dash_edit_perm && editMode, - isPublished: dashboard.published, - hasUnsavedChanges: false, - maxUndoHistoryExceeded: false, - lastModifiedTime: dashboard.last_modified_time, + dashboardFilters, + nativeFilters, + dashboardState: { + sliceIds: Array.from(sliceIds), + directPathToChild, + directPathLastUpdated: Date.now(), + focusedFilterField: null, + expandedSlices: metadata?.expanded_slices || {}, + refreshFrequency: metadata?.refresh_frequency || 0, + // dashboard viewers can set refresh frequency for the current visit, + // only persistent refreshFrequency will be saved to backend + shouldPersistRefreshFrequency: false, + css: dashboardData.css || '', + colorNamespace: metadata?.color_namespace || null, + colorScheme: metadata?.color_scheme || null, + editMode: findPermission('can_write', 'Dashboard', roles) && editMode, + isPublished: dashboardData.published, + hasUnsavedChanges: false, + maxUndoHistoryExceeded: false, + lastModifiedTime: dashboardData.changed_on, + }, + dashboardLayout, + messageToasts: [], + impressionId: shortid.generate(), }, - dashboardLayout, - messageToasts: [], - impressionId: shortid.generate(), - }; -} + }); +}; diff --git a/superset-frontend/src/dashboard/actions/nativeFilters.ts b/superset-frontend/src/dashboard/actions/nativeFilters.ts index fca59f2bafd70..7668184a5b833 100644 --- a/superset-frontend/src/dashboard/actions/nativeFilters.ts +++ b/superset-frontend/src/dashboard/actions/nativeFilters.ts @@ -25,8 +25,14 @@ import { SET_DATA_MASK_FOR_FILTER_CONFIG_COMPLETE, SET_DATA_MASK_FOR_FILTER_CONFIG_FAIL, } from 'src/dataMask/actions'; +import { HYDRATE_DASHBOARD } from './hydrate'; import { dashboardInfoChanged } from './dashboardInfo'; -import { DashboardInfo, FilterSet } from '../reducers/types'; +import { + DashboardInfo, + Filters, + FilterSet, + FilterSets, +} from '../reducers/types'; export const SET_FILTER_CONFIG_BEGIN = 'SET_FILTER_CONFIG_BEGIN'; export interface SetFilterConfigBegin { @@ -105,6 +111,19 @@ export const setFilterConfiguration = ( } }; +type BootstrapData = { + nativeFilters: { + filters: Filters; + filterSets: FilterSets; + filtersState: object; + }; +}; + +export interface SetBooststapData { + type: typeof HYDRATE_DASHBOARD; + data: BootstrapData; +} + export const setFilterSetsConfiguration = ( filterSetsConfig: FilterSet[], ) => async (dispatch: Dispatch, getState: () => any) => { @@ -173,4 +192,5 @@ export type AnyFilterAction = | SetFilterSetsConfigBegin | SetFilterSetsConfigComplete | SetFilterSetsConfigFail - | SaveFilterSets; + | SaveFilterSets + | SetBooststapData; diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx index b532b3b449d0d..463a7700fd2a8 100644 --- a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx +++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx @@ -49,7 +49,7 @@ import { DASHBOARD_ROOT_DEPTH, DashboardStandaloneMode, } from 'src/dashboard/util/constants'; -import FilterBar from '../nativeFilters/FilterBar/FilterBar'; +import FilterBar from 'src/dashboard/components/nativeFilters/FilterBar'; import { StickyVerticalBar } from '../StickyVerticalBar'; import { shouldFocusTabs, getRootLevelTabsComponent } from './utils'; import { useFilters } from '../nativeFilters/FilterBar/state'; diff --git a/superset-frontend/src/dashboard/components/DashboardGrid.jsx b/superset-frontend/src/dashboard/components/DashboardGrid.jsx index 6889c91ab3de2..9fb0fb5fd55e5 100644 --- a/superset-frontend/src/dashboard/components/DashboardGrid.jsx +++ b/superset-frontend/src/dashboard/components/DashboardGrid.jsx @@ -123,7 +123,6 @@ class DashboardGrid extends React.PureComponent { width, isComponentVisible, } = this.props; - const columnPlusGutterWidth = (width + GRID_GUTTER_SIZE) / GRID_COLUMN_COUNT; diff --git a/superset-frontend/src/dashboard/components/FiltersBadge/selectors.ts b/superset-frontend/src/dashboard/components/FiltersBadge/selectors.ts index 2e5e7ac4b343a..8b6bed2e53800 100644 --- a/superset-frontend/src/dashboard/components/FiltersBadge/selectors.ts +++ b/superset-frontend/src/dashboard/components/FiltersBadge/selectors.ts @@ -229,8 +229,7 @@ export const selectNativeIndicatorsForChart = ( layoutItem => dashboardLayout[layoutItem]?.meta?.chartId === chartId, ); const column = nativeFilter.targets[0]?.column?.name; - const dataMaskNativeFilters = dataMask.nativeFilters?.[nativeFilter.id]; - let value = dataMaskNativeFilters?.currentState?.value ?? null; + let value = dataMask[nativeFilter.id]?.filterState?.value ?? null; if (!Array.isArray(value) && value !== null) { value = [value]; } @@ -254,8 +253,7 @@ export const selectNativeIndicatorsForChart = ( layoutItem => dashboardLayout[layoutItem]?.meta?.chartId === chartId, ); - const dataMaskCrossFilters = dataMask.crossFilters?.[chartConfig.id]; - let value = dataMaskCrossFilters?.currentState?.value ?? null; + let value = dataMask[chartConfig.id]?.filterState?.value ?? null; if (!Array.isArray(value) && value !== null) { value = [value]; } diff --git a/superset-frontend/src/dashboard/components/Header/Header.test.tsx b/superset-frontend/src/dashboard/components/Header/Header.test.tsx new file mode 100644 index 0000000000000..d5393b2275597 --- /dev/null +++ b/superset-frontend/src/dashboard/components/Header/Header.test.tsx @@ -0,0 +1,288 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { render, screen, fireEvent } from 'spec/helpers/testing-library'; +import userEvent from '@testing-library/user-event'; +import fetchMock from 'fetch-mock'; +import { HeaderProps } from './types'; +import Header from '.'; + +const createProps = () => ({ + addSuccessToast: jest.fn(), + addDangerToast: jest.fn(), + addWarningToast: jest.fn(), + dashboardInfo: { + id: 1, + dash_edit_perm: false, + dash_save_perm: false, + dash_share_perm: false, + userId: 1, + metadata: {}, + common: { + conf: {}, + }, + }, + dashboardTitle: 'Dashboard Title', + charts: {}, + layout: {}, + expandedSlices: {}, + css: '', + customCss: '', + isStarred: false, + isLoading: false, + lastModifiedTime: 0, + refreshFrequency: 0, + shouldPersistRefreshFrequency: false, + onSave: jest.fn(), + onChange: jest.fn(), + fetchFaveStar: jest.fn(), + fetchCharts: jest.fn(), + saveFaveStar: jest.fn(), + savePublished: jest.fn(), + isPublished: false, + updateDashboardTitle: jest.fn(), + editMode: false, + setEditMode: jest.fn(), + showBuilderPane: jest.fn(), + updateCss: jest.fn(), + setColorSchemeAndUnsavedChanges: jest.fn(), + logEvent: jest.fn(), + setRefreshFrequency: jest.fn(), + hasUnsavedChanges: false, + maxUndoHistoryExceeded: false, + onUndo: jest.fn(), + onRedo: jest.fn(), + undoLength: 0, + redoLength: 0, + setMaxUndoHistoryExceeded: jest.fn(), + maxUndoHistoryToast: jest.fn(), + dashboardInfoChanged: jest.fn(), + dashboardTitleChanged: jest.fn(), +}); +const props = createProps(); +const editableProps = { + ...props, + editMode: true, + dashboardInfo: { + ...props.dashboardInfo, + dash_edit_perm: true, + dash_save_perm: true, + }, +}; +const undoProps = { + ...editableProps, + undoLength: 1, +}; +const redoProps = { + ...editableProps, + redoLength: 1, +}; + +fetchMock.get('glob:*/csstemplateasyncmodelview/api/read', {}); + +function setup(props: HeaderProps) { + return ( +
+
+
+ ); +} + +async function openActionsDropdown() { + const btn = screen.getByRole('img', { name: 'more-horiz' }); + userEvent.click(btn); + expect(await screen.findByRole('menu')).toBeInTheDocument(); +} + +test('should render', () => { + const mockedProps = createProps(); + const { container } = render(setup(mockedProps)); + expect(container).toBeInTheDocument(); +}); + +test('should render the title', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByText('Dashboard Title')).toBeInTheDocument(); +}); + +test('should render the editable title', () => { + render(setup(editableProps)); + expect(screen.getByDisplayValue('Dashboard Title')).toBeInTheDocument(); +}); + +test('should edit the title', () => { + render(setup(editableProps)); + const editableTitle = screen.getByDisplayValue('Dashboard Title'); + expect(editableProps.onChange).not.toHaveBeenCalled(); + userEvent.click(editableTitle); + userEvent.clear(editableTitle); + userEvent.type(editableTitle, 'New Title'); + userEvent.click(document.body); + expect(editableProps.onChange).toHaveBeenCalled(); + expect(screen.getByDisplayValue('New Title')).toBeInTheDocument(); +}); + +test('should render the "Draft" status', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByText('Draft')).toBeInTheDocument(); +}); + +test('should publish', () => { + render(setup(editableProps)); + const draft = screen.getByText('Draft'); + expect(editableProps.savePublished).not.toHaveBeenCalled(); + userEvent.click(draft); + expect(editableProps.savePublished).toHaveBeenCalledTimes(1); +}); + +test('should render the "Undo" action as disabled', () => { + render(setup(editableProps)); + expect(screen.getByTitle('Undo').parentElement).toBeDisabled(); +}); + +test('should undo', () => { + render(setup(undoProps)); + const undo = screen.getByTitle('Undo'); + expect(undoProps.onUndo).not.toHaveBeenCalled(); + userEvent.click(undo); + expect(undoProps.onUndo).toHaveBeenCalledTimes(1); +}); + +test('should undo with key listener', () => { + undoProps.onUndo.mockReset(); + render(setup(undoProps)); + expect(undoProps.onUndo).not.toHaveBeenCalled(); + fireEvent.keyDown(document.body, { key: 'z', code: 'KeyZ', ctrlKey: true }); + expect(undoProps.onUndo).toHaveBeenCalledTimes(1); +}); + +test('should render the "Redo" action as disabled', () => { + render(setup(editableProps)); + expect(screen.getByTitle('Redo').parentElement).toBeDisabled(); +}); + +test('should redo', () => { + render(setup(redoProps)); + const redo = screen.getByTitle('Redo'); + expect(redoProps.onRedo).not.toHaveBeenCalled(); + userEvent.click(redo); + expect(redoProps.onRedo).toHaveBeenCalledTimes(1); +}); + +test('should redo with key listener', () => { + redoProps.onRedo.mockReset(); + render(setup(redoProps)); + expect(redoProps.onRedo).not.toHaveBeenCalled(); + fireEvent.keyDown(document.body, { key: 'y', code: 'KeyY', ctrlKey: true }); + expect(redoProps.onRedo).toHaveBeenCalledTimes(1); +}); + +test('should render the "Discard changes" button', () => { + render(setup(editableProps)); + expect(screen.getByText('Discard changes')).toBeInTheDocument(); +}); + +test('should render the "Save" button as disabled', () => { + render(setup(editableProps)); + expect(screen.getByText('Save').parentElement).toBeDisabled(); +}); + +test('should save', () => { + const unsavedProps = { + ...editableProps, + hasUnsavedChanges: true, + }; + render(setup(unsavedProps)); + const save = screen.getByText('Save'); + expect(unsavedProps.onSave).not.toHaveBeenCalled(); + userEvent.click(save); + expect(unsavedProps.onSave).toHaveBeenCalledTimes(1); +}); + +test('should NOT render the "Draft" status', () => { + const mockedProps = createProps(); + const publishedProps = { + ...mockedProps, + isPublished: true, + }; + render(setup(publishedProps)); + expect(screen.queryByText('Draft')).not.toBeInTheDocument(); +}); + +test('should render the unselected fave icon', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(mockedProps.fetchFaveStar).toHaveBeenCalled(); + expect( + screen.getByRole('img', { name: 'favorite-unselected' }), + ).toBeInTheDocument(); +}); + +test('should render the selected fave icon', () => { + const mockedProps = createProps(); + const favedProps = { + ...mockedProps, + isStarred: true, + }; + render(setup(favedProps)); + expect( + screen.getByRole('img', { name: 'favorite-selected' }), + ).toBeInTheDocument(); +}); + +test('should fave', async () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + const fave = screen.getByRole('img', { name: 'favorite-unselected' }); + expect(mockedProps.saveFaveStar).not.toHaveBeenCalled(); + userEvent.click(fave); + expect(mockedProps.saveFaveStar).toHaveBeenCalledTimes(1); +}); + +test('should toggle the edit mode', () => { + const mockedProps = createProps(); + const canEditProps = { + ...mockedProps, + dashboardInfo: { + ...mockedProps.dashboardInfo, + dash_edit_perm: true, + }, + }; + render(setup(canEditProps)); + const editDashboard = screen.getByTitle('Edit dashboard'); + expect(screen.queryByTitle('Edit dashboard')).toBeInTheDocument(); + userEvent.click(editDashboard); + expect(mockedProps.logEvent).toHaveBeenCalled(); +}); + +test('should render the dropdown icon', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByRole('img', { name: 'more-horiz' })).toBeInTheDocument(); +}); + +test('should refresh the charts', async () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + await openActionsDropdown(); + userEvent.click(screen.getByText('Refresh dashboard')); + expect(mockedProps.fetchCharts).toHaveBeenCalledTimes(1); +}); diff --git a/superset-frontend/src/dashboard/components/Header/HeaderActionsDropdown/HeaderActionsDropdown.test.tsx b/superset-frontend/src/dashboard/components/Header/HeaderActionsDropdown/HeaderActionsDropdown.test.tsx new file mode 100644 index 0000000000000..039c1ef97c6d1 --- /dev/null +++ b/superset-frontend/src/dashboard/components/Header/HeaderActionsDropdown/HeaderActionsDropdown.test.tsx @@ -0,0 +1,200 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { render, screen } from 'spec/helpers/testing-library'; +import userEvent from '@testing-library/user-event'; +import fetchMock from 'fetch-mock'; +import { HeaderDropdownProps } from 'src/dashboard/components/Header/types'; +import HeaderActionsDropdown from '.'; + +const createProps = () => ({ + addSuccessToast: jest.fn(), + addDangerToast: jest.fn(), + customCss: '#save-dash-split-button{margin-left: 100px;}', + dashboardId: 1, + dashboardInfo: { + id: 1, + dash_edit_perm: true, + dash_save_perm: true, + userId: 1, + metadata: {}, + common: { + conf: {}, + }, + }, + dashboardTitle: 'Title', + editMode: false, + expandedSlices: {}, + forceRefreshAllCharts: jest.fn(), + hasUnsavedChanges: false, + isLoading: false, + layout: {}, + onChange: jest.fn(), + onSave: jest.fn(), + refreshFrequency: 200, + setRefreshFrequency: jest.fn(), + shouldPersistRefreshFrequency: false, + showPropertiesModal: jest.fn(), + startPeriodicRender: jest.fn(), + updateCss: jest.fn(), + userCanEdit: false, + userCanSave: false, + userCanShare: false, + lastModifiedTime: 0, +}); +const editModeOnProps = { + ...createProps(), + editMode: true, +}; + +function setup(props: HeaderDropdownProps) { + return ( +
+ +
+ ); +} + +fetchMock.get('glob:*/csstemplateasyncmodelview/api/read', {}); + +async function openDropdown() { + const btn = screen.getByRole('img', { name: 'more-horiz' }); + userEvent.click(btn); + expect(await screen.findByRole('menu')).toBeInTheDocument(); +} + +test('should render', () => { + const mockedProps = createProps(); + const { container } = render(setup(mockedProps)); + expect(container).toBeInTheDocument(); +}); + +test('should render the dropdown button', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByRole('button')).toBeInTheDocument(); +}); + +test('should render the dropdown icon', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByRole('img', { name: 'more-horiz' })).toBeInTheDocument(); +}); + +test('should open the dropdown', async () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + await openDropdown(); + expect(await screen.findByRole('menu')).toBeInTheDocument(); +}); + +test('should render the menu items', async () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + await openDropdown(); + expect(screen.getAllByRole('menuitem')).toHaveLength(4); + expect(screen.getByText('Refresh dashboard')).toBeInTheDocument(); + expect(screen.getByText('Set auto-refresh interval')).toBeInTheDocument(); + expect(screen.getByText('Download as image')).toBeInTheDocument(); + expect(screen.getByText('Toggle fullscreen')).toBeInTheDocument(); +}); + +test('should render the menu items in edit mode', async () => { + render(setup(editModeOnProps)); + await openDropdown(); + expect(screen.getAllByRole('menuitem')).toHaveLength(5); + expect(screen.getByText('Refresh dashboard')).toBeInTheDocument(); + expect(screen.getByText('Set auto-refresh interval')).toBeInTheDocument(); + expect(screen.getByText('Set filter mapping')).toBeInTheDocument(); + expect(screen.getByText('Edit dashboard properties')).toBeInTheDocument(); + expect(screen.getByText('Edit CSS')).toBeInTheDocument(); +}); + +test('should show the share actions', async () => { + const mockedProps = createProps(); + const canShareProps = { + ...mockedProps, + userCanShare: true, + }; + render(setup(canShareProps)); + await openDropdown(); + expect(screen.getByText('Copy dashboard URL')).toBeInTheDocument(); + expect(screen.getByText('Share dashboard by email')).toBeInTheDocument(); +}); + +test('should render the "Save Modal" when user can save', async () => { + const mockedProps = createProps(); + const canSaveProps = { + ...mockedProps, + userCanSave: true, + }; + render(setup(canSaveProps)); + await openDropdown(); + expect(screen.getByText('Save as')).toBeInTheDocument(); +}); + +test('should NOT render the "Save Modal" menu item when user cannot save', async () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + await openDropdown(); + expect(screen.queryByText('Save as')).not.toBeInTheDocument(); +}); + +test('should render the "Refresh dashboard" menu item as disabled when loading', async () => { + const mockedProps = createProps(); + const loadingProps = { + ...mockedProps, + isLoading: true, + }; + render(setup(loadingProps)); + await openDropdown(); + expect(screen.getByText('Refresh dashboard')).toHaveClass( + 'ant-dropdown-menu-item-disabled', + ); +}); + +test('should NOT render the "Refresh dashboard" menu item as disabled', async () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + await openDropdown(); + expect(screen.getByText('Refresh dashboard')).not.toHaveClass( + 'ant-dropdown-menu-item-disabled', + ); +}); + +test('should render with custom css', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByRole('button')).toHaveStyle('margin-left: 100px'); +}); + +test('should refresh the charts', async () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + await openDropdown(); + userEvent.click(screen.getByText('Refresh dashboard')); + expect(mockedProps.forceRefreshAllCharts).toHaveBeenCalledTimes(1); +}); + +test('should show the properties modal', async () => { + render(setup(editModeOnProps)); + await openDropdown(); + userEvent.click(screen.getByText('Edit dashboard properties')); + expect(editModeOnProps.showPropertiesModal).toHaveBeenCalledTimes(1); +}); diff --git a/superset-frontend/src/dashboard/components/HeaderActionsDropdown.jsx b/superset-frontend/src/dashboard/components/Header/HeaderActionsDropdown/index.jsx similarity index 89% rename from superset-frontend/src/dashboard/components/HeaderActionsDropdown.jsx rename to superset-frontend/src/dashboard/components/Header/HeaderActionsDropdown/index.jsx index 5d27f1f7f5917..607fcdfd9d21a 100644 --- a/superset-frontend/src/dashboard/components/HeaderActionsDropdown.jsx +++ b/superset-frontend/src/dashboard/components/Header/HeaderActionsDropdown/index.jsx @@ -25,16 +25,16 @@ import { Menu, NoAnimationDropdown } from 'src/common/components'; import Icon from 'src/components/Icon'; import { URL_PARAMS } from 'src/constants'; import ShareMenuItems from 'src/dashboard/components/menu/ShareMenuItems'; -import CssEditor from './CssEditor'; -import RefreshIntervalModal from './RefreshIntervalModal'; -import SaveModal from './SaveModal'; -import injectCustomCss from '../util/injectCustomCss'; -import { SAVE_TYPE_NEWDASHBOARD } from '../util/constants'; -import FilterScopeModal from './filterscope/FilterScopeModal'; -import downloadAsImage from '../../utils/downloadAsImage'; -import getDashboardUrl from '../util/getDashboardUrl'; -import { getActiveFilters } from '../util/activeDashboardFilters'; -import { getUrlParam } from '../../utils/urlUtils'; +import CssEditor from 'src/dashboard/components/CssEditor'; +import RefreshIntervalModal from 'src/dashboard/components/RefreshIntervalModal'; +import SaveModal from 'src/dashboard/components/SaveModal'; +import injectCustomCss from 'src/dashboard/util/injectCustomCss'; +import { SAVE_TYPE_NEWDASHBOARD } from 'src/dashboard/util/constants'; +import FilterScopeModal from 'src/dashboard/components/filterscope/FilterScopeModal'; +import downloadAsImage from 'src/utils/downloadAsImage'; +import getDashboardUrl from 'src/dashboard/util/getDashboardUrl'; +import { getActiveFilters } from 'src/dashboard/util/activeDashboardFilters'; +import { getUrlParam } from 'src/utils/urlUtils'; const propTypes = { addSuccessToast: PropTypes.func.isRequired, @@ -54,6 +54,7 @@ const propTypes = { startPeriodicRender: PropTypes.func.isRequired, editMode: PropTypes.bool.isRequired, userCanEdit: PropTypes.bool.isRequired, + userCanShare: PropTypes.bool.isRequired, userCanSave: PropTypes.bool.isRequired, isLoading: PropTypes.bool.isRequired, layout: PropTypes.object.isRequired, @@ -192,6 +193,7 @@ class HeaderActionsDropdown extends React.PureComponent { expandedSlices, onSave, userCanEdit, + userCanShare, userCanSave, isLoading, refreshLimit, @@ -241,15 +243,17 @@ class HeaderActionsDropdown extends React.PureComponent { /> )} - + {userCanShare && ( + + )} )} {editMode && ( - @@ -543,6 +542,7 @@ class Header extends React.PureComponent { editMode={editMode} hasUnsavedChanges={hasUnsavedChanges} userCanEdit={userCanEdit} + userCanShare={userCanShare} userCanSave={userCanSaveAs} isLoading={isLoading} showPropertiesModal={this.showPropertiesModal} diff --git a/superset-frontend/src/dashboard/components/Header/types.ts b/superset-frontend/src/dashboard/components/Header/types.ts new file mode 100644 index 0000000000000..5580136cb836e --- /dev/null +++ b/superset-frontend/src/dashboard/components/Header/types.ts @@ -0,0 +1,98 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { Layout } from 'src/dashboard/types'; +import { ChartState } from 'src/explore/types'; + +interface DashboardInfo { + id: number; + userId: number; + dash_edit_perm: boolean; + dash_save_perm: boolean; + metadata?: Record; + common?: { conf: Record }; +} + +export interface HeaderDropdownProps { + addSuccessToast: () => void; + addDangerToast: () => void; + customCss: string; + colorNamespace?: string; + colorScheme?: string; + dashboardId: number; + dashboardInfo: DashboardInfo; + dashboardTitle: string; + editMode: boolean; + expandedSlices: Record; + forceRefreshAllCharts: () => void; + hasUnsavedChanges: boolean; + isLoading: boolean; + layout: Layout; + onChange: () => void; + onSave: () => void; + refreshFrequency: number; + setRefreshFrequency: () => void; + shouldPersistRefreshFrequency: boolean; + showPropertiesModal: () => void; + startPeriodicRender: () => void; + updateCss: () => void; + userCanEdit: boolean; + userCanSave: boolean; + lastModifiedTime: number; +} + +export interface HeaderProps { + addSuccessToast: () => void; + addDangerToast: () => void; + addWarningToast: () => void; + colorNamespace?: string; + charts: ChartState | {}; + colorScheme?: string; + customCss: string; + dashboardInfo: DashboardInfo; + dashboardTitle: string; + setColorSchemeAndUnsavedChanges: () => void; + isStarred: boolean; + isPublished: boolean; + onChange: () => void; + onSave: () => void; + fetchFaveStar: () => void; + saveFaveStar: () => void; + savePublished: () => void; + updateDashboardTitle: () => void; + editMode: boolean; + setEditMode: () => void; + showBuilderPane: () => void; + updateCss: () => void; + logEvent: () => void; + hasUnsavedChanges: boolean; + maxUndoHistoryExceeded: boolean; + lastModifiedTime: number; + onUndo: () => void; + onRedo: () => void; + undoLength: number; + redoLength: number; + setMaxUndoHistoryExceeded: () => void; + maxUndoHistoryToast: () => void; + refreshFrequency: number; + shouldPersistRefreshFrequency: boolean; + setRefreshFrequency: () => void; + dashboardInfoChanged: () => void; + dashboardTitleChanged: () => void; +} diff --git a/superset-frontend/src/dashboard/components/PropertiesModal/index.jsx b/superset-frontend/src/dashboard/components/PropertiesModal/index.jsx index e58c715d51df6..c1114e74d7475 100644 --- a/superset-frontend/src/dashboard/components/PropertiesModal/index.jsx +++ b/superset-frontend/src/dashboard/components/PropertiesModal/index.jsx @@ -399,7 +399,7 @@ class PropertiesModal extends React.PureComponent { />

{t( - 'Roles is a list which defines access to the dashboard. These roles are always applied in addition to restrictions on dataset level access. If no roles defined then the dashboard is available to all roles.', + 'Roles is a list which defines access to the dashboard. Granting a role access to a dashboard will bypass dataset level checks. If no roles defined then the dashboard is available to all roles.', )}

diff --git a/superset-frontend/src/dashboard/components/SaveModal.tsx b/superset-frontend/src/dashboard/components/SaveModal.tsx index 0bbc327767558..1d3141df05f8e 100644 --- a/superset-frontend/src/dashboard/components/SaveModal.tsx +++ b/superset-frontend/src/dashboard/components/SaveModal.tsx @@ -140,7 +140,7 @@ class SaveModal extends React.PureComponent { // check refresh frequency is for current session or persist const refreshFrequency = shouldPersistRefreshFrequency ? currentRefreshFrequency - : dashboardInfo.metadata.refresh_frequency; // eslint-disable camelcase + : dashboardInfo.metadata?.refresh_frequency; // eslint-disable camelcase const data = { positions, diff --git a/superset-frontend/src/dashboard/components/SliceHeader/SliceHeader.test.tsx b/superset-frontend/src/dashboard/components/SliceHeader/SliceHeader.test.tsx new file mode 100644 index 0000000000000..b036ef696c567 --- /dev/null +++ b/superset-frontend/src/dashboard/components/SliceHeader/SliceHeader.test.tsx @@ -0,0 +1,399 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { Slice } from 'src/types/Chart'; +import React from 'react'; +import { render, screen } from 'spec/helpers/testing-library'; +import userEvent from '@testing-library/user-event'; +import SliceHeader from '.'; + +jest.mock('src/dashboard/components/SliceHeaderControls', () => ({ + __esModule: true, + default: (props: any) => ( +
+ + + + + + + + + + + + +
+ ), +})); + +jest.mock('src/dashboard/containers/FiltersBadge', () => ({ + __esModule: true, + default: (props: any) => ( +
+ ), +})); + +const createProps = () => ({ + filters: {}, // is in typing but not being used + editMode: false, + annotationQuery: { param01: 'annotationQuery' } as any, + annotationError: { param01: 'annotationError' } as any, + cachedDttm: [] as string[], + updatedDttm: 1617207718004, + isCached: [false], + isExpanded: false, + sliceName: 'Vaccine Candidates per Phase', + supersetCanExplore: true, + supersetCanCSV: true, + sliceCanEdit: false, + slice: ({ + slice_id: 312, + slice_url: '/superset/explore/?form_data=%7B%22slice_id%22%3A%20312%7D', + slice_name: 'Vaccine Candidates per Phase', + form_data: { + adhoc_filters: [], + bottom_margin: 'auto', + color_scheme: 'SUPERSET_DEFAULT', + columns: [], + datasource: '58__table', + groupby: ['clinical_stage'], + label_colors: {}, + metrics: ['count'], + row_limit: 10000, + show_legend: false, + time_range: 'No filter', + time_range_endpoints: ['inclusive', 'exclusive'], + url_params: {}, + viz_type: 'dist_bar', + x_ticks_layout: 'auto', + y_axis_format: 'SMART_NUMBER', + slice_id: 312, + }, + viz_type: 'dist_bar', + datasource: '58__table', + description: null, + description_markeddown: '', + owners: [], + modified: '20 hours ago', + changed_on: 1617143411366, + } as unknown) as Slice, + componentId: 'CHART-aGfmWtliqA', + dashboardId: 26, + isFullSize: false, + chartStatus: 'rendered', + addSuccessToast: jest.fn(), + addDangerToast: jest.fn(), + handleToggleFullSize: jest.fn(), + updateSliceName: jest.fn(), + toggleExpandSlice: jest.fn(), + forceRefresh: jest.fn(), + exploreChart: jest.fn(), + exportCSV: jest.fn(), +}); + +test('Should render', () => { + const props = createProps(); + render(, { useRedux: true }); + expect(screen.getByTestId('slice-header')).toBeInTheDocument(); +}); + +test('Should render - default props', () => { + const props = createProps(); + + // @ts-ignore + delete props.forceRefresh; + // @ts-ignore + delete props.updateSliceName; + // @ts-ignore + delete props.toggleExpandSlice; + // @ts-ignore + delete props.exploreChart; + // @ts-ignore + delete props.exportCSV; + // @ts-ignore + delete props.innerRef; + // @ts-ignore + delete props.editMode; + // @ts-ignore + delete props.annotationQuery; + // @ts-ignore + delete props.annotationError; + // @ts-ignore + delete props.cachedDttm; + // @ts-ignore + delete props.updatedDttm; + // @ts-ignore + delete props.isCached; + // @ts-ignore + delete props.isExpanded; + // @ts-ignore + delete props.sliceName; + // @ts-ignore + delete props.supersetCanExplore; + // @ts-ignore + delete props.supersetCanCSV; + // @ts-ignore + delete props.sliceCanEdit; + + render(, { useRedux: true }); + expect(screen.getByTestId('slice-header')).toBeInTheDocument(); +}); + +test('Should render default props and "call" actions', () => { + const props = createProps(); + + // @ts-ignore + delete props.forceRefresh; + // @ts-ignore + delete props.updateSliceName; + // @ts-ignore + delete props.toggleExpandSlice; + // @ts-ignore + delete props.exploreChart; + // @ts-ignore + delete props.exportCSV; + // @ts-ignore + delete props.innerRef; + // @ts-ignore + delete props.editMode; + // @ts-ignore + delete props.annotationQuery; + // @ts-ignore + delete props.annotationError; + // @ts-ignore + delete props.cachedDttm; + // @ts-ignore + delete props.updatedDttm; + // @ts-ignore + delete props.isCached; + // @ts-ignore + delete props.isExpanded; + // @ts-ignore + delete props.sliceName; + // @ts-ignore + delete props.supersetCanExplore; + // @ts-ignore + delete props.supersetCanCSV; + // @ts-ignore + delete props.sliceCanEdit; + + render(, { useRedux: true }); + userEvent.click(screen.getByTestId('toggleExpandSlice')); + userEvent.click(screen.getByTestId('forceRefresh')); + userEvent.click(screen.getByTestId('exploreChart')); + userEvent.click(screen.getByTestId('exportCSV')); + userEvent.click(screen.getByTestId('addSuccessToast')); + userEvent.click(screen.getByTestId('addDangerToast')); + userEvent.click(screen.getByTestId('handleToggleFullSize')); + expect(screen.getByTestId('slice-header')).toBeInTheDocument(); +}); + +test('Should render title', () => { + const props = createProps(); + render(, { useRedux: true }); + expect(screen.getByText('Vaccine Candidates per Phase')).toBeInTheDocument(); +}); + +test('Should render "annotationsLoading"', () => { + const props = createProps(); + render(, { useRedux: true }); + expect( + screen.getByRole('img', { + name: 'Annotation layers are still loading.', + }), + ).toBeInTheDocument(); +}); + +test('Should render "annotationsError"', () => { + const props = createProps(); + render(, { useRedux: true }); + expect( + screen.getByRole('img', { + name: 'One ore more annotation layers failed loading.', + }), + ).toBeInTheDocument(); +}); + +test('Should not render "annotationsError" and "annotationsLoading"', () => { + const props = createProps(); + props.annotationQuery = {}; + props.annotationError = {}; + render(, { useRedux: true }); + expect( + screen.queryByRole('img', { + name: 'One ore more annotation layers failed loading.', + }), + ).not.toBeInTheDocument(); + expect( + screen.queryByRole('img', { + name: 'Annotation layers are still loading.', + }), + ).not.toBeInTheDocument(); +}); + +test('Correct props to "FiltersBadge"', () => { + const props = createProps(); + render(, { useRedux: true }); + expect(screen.getByTestId('FiltersBadge')).toHaveAttribute( + 'data-chart-id', + '312', + ); +}); + +test('Correct props to "SliceHeaderControls"', () => { + const props = createProps(); + render(, { useRedux: true }); + expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute( + 'data-cached-dttm', + '', + ); + expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute( + 'data-chart-status', + 'rendered', + ); + expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute( + 'data-component-id', + 'CHART-aGfmWtliqA', + ); + expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute( + 'data-dashboard-id', + '26', + ); + expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute( + 'data-is-cached', + 'false', + ); + expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute( + 'data-is-expanded', + 'false', + ); + expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute( + 'data-is-full-size', + 'false', + ); + expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute( + 'data-slice-can-edit', + 'false', + ); + expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute( + 'data-superset-can-csv', + 'true', + ); + expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute( + 'data-superset-can-explore', + 'true', + ); + expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute( + 'data-test', + 'SliceHeaderControls', + ); + expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute( + 'data-updated-dttm', + '1617207718004', + ); + expect(screen.getByTestId('SliceHeaderControls')).toHaveAttribute( + 'data-slice', + JSON.stringify(props.slice), + ); +}); + +test('Correct actions to "SliceHeaderControls"', () => { + const props = createProps(); + render(, { useRedux: true }); + + expect(props.toggleExpandSlice).toBeCalledTimes(0); + userEvent.click(screen.getByTestId('toggleExpandSlice')); + expect(props.toggleExpandSlice).toBeCalledTimes(1); + + expect(props.forceRefresh).toBeCalledTimes(0); + userEvent.click(screen.getByTestId('forceRefresh')); + expect(props.forceRefresh).toBeCalledTimes(1); + + expect(props.exploreChart).toBeCalledTimes(0); + userEvent.click(screen.getByTestId('exploreChart')); + expect(props.exploreChart).toBeCalledTimes(1); + + expect(props.exportCSV).toBeCalledTimes(0); + userEvent.click(screen.getByTestId('exportCSV')); + expect(props.exportCSV).toBeCalledTimes(1); + + expect(props.addSuccessToast).toBeCalledTimes(0); + userEvent.click(screen.getByTestId('addSuccessToast')); + expect(props.addSuccessToast).toBeCalledTimes(1); + + expect(props.addDangerToast).toBeCalledTimes(0); + userEvent.click(screen.getByTestId('addDangerToast')); + expect(props.addDangerToast).toBeCalledTimes(1); + + expect(props.handleToggleFullSize).toBeCalledTimes(0); + userEvent.click(screen.getByTestId('handleToggleFullSize')); + expect(props.handleToggleFullSize).toBeCalledTimes(1); +}); diff --git a/superset-frontend/src/dashboard/components/SliceHeader.tsx b/superset-frontend/src/dashboard/components/SliceHeader/index.tsx similarity index 83% rename from superset-frontend/src/dashboard/components/SliceHeader.tsx rename to superset-frontend/src/dashboard/components/SliceHeader/index.tsx index 85eaaa05dadb9..f2570eb6ee523 100644 --- a/superset-frontend/src/dashboard/components/SliceHeader.tsx +++ b/superset-frontend/src/dashboard/components/SliceHeader/index.tsx @@ -20,13 +20,13 @@ import React, { FC } from 'react'; import { styled, t } from '@superset-ui/core'; import { Tooltip } from 'src/common/components/Tooltip'; import { useSelector } from 'react-redux'; -import EditableTitle from '../../components/EditableTitle'; -import SliceHeaderControls from './SliceHeaderControls'; -import FiltersBadge from '../containers/FiltersBadge'; -import Icon from '../../components/Icon'; -import { RootState } from '../types'; -import { Slice } from '../../types/Chart'; -import FilterIndicator from './FiltersBadge/FilterIndicator'; +import EditableTitle from 'src/components/EditableTitle'; +import SliceHeaderControls from 'src/dashboard/components/SliceHeaderControls'; +import FiltersBadge from 'src/dashboard/containers/FiltersBadge'; +import Icon from 'src/components/Icon'; +import { RootState } from 'src/dashboard/types'; +import { Slice } from 'src/types/Chart'; +import FilterIndicator from 'src/dashboard/components/FiltersBadge/FilterIndicator'; type SliceHeaderProps = { innerRef?: string; @@ -46,6 +46,7 @@ type SliceHeaderProps = { annotationError?: object; sliceName?: string; supersetCanExplore?: boolean; + supersetCanShare?: boolean; supersetCanCSV?: boolean; sliceCanEdit?: boolean; componentId: string; @@ -83,6 +84,7 @@ const SliceHeader: FC = ({ isExpanded = [], sliceName = '', supersetCanExplore = false, + supersetCanShare = false, supersetCanCSV = false, sliceCanEdit = false, slice, @@ -96,12 +98,11 @@ const SliceHeader: FC = ({ }) => { // TODO: change to indicator field after it will be implemented const crossFilterValue = useSelector( - state => - state.dataMask?.crossFilters?.[slice?.slice_id]?.currentState?.value, + state => state.dataMask[slice?.slice_id]?.filterState?.value, ); return ( -
+
= ({ placement="top" title={annoationsLoading} > - + )} {!!Object.values(annotationError).length && ( @@ -130,7 +135,11 @@ const SliceHeader: FC = ({ placement="top" title={annoationsError} > - + )}
@@ -164,6 +173,7 @@ const SliceHeader: FC = ({ exploreChart={exploreChart} exportCSV={exportCSV} supersetCanExplore={supersetCanExplore} + supersetCanShare={supersetCanShare} supersetCanCSV={supersetCanCSV} sliceCanEdit={sliceCanEdit} componentId={componentId} diff --git a/superset-frontend/src/dashboard/components/SliceHeaderControls/index.jsx b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.jsx index edef021377118..2c66bca1938de 100644 --- a/superset-frontend/src/dashboard/components/SliceHeaderControls/index.jsx +++ b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.jsx @@ -43,6 +43,7 @@ const propTypes = { isExpanded: PropTypes.bool, updatedDttm: PropTypes.number, supersetCanExplore: PropTypes.bool, + supersetCanShare: PropTypes.bool, supersetCanCSV: PropTypes.bool, sliceCanEdit: PropTypes.bool, toggleExpandSlice: PropTypes.func, @@ -61,6 +62,7 @@ const defaultProps = { isCached: [], isExpanded: false, supersetCanExplore: false, + supersetCanShare: false, supersetCanCSV: false, sliceCanEdit: false, }; @@ -72,7 +74,6 @@ const MENU_KEYS = { EXPLORE_CHART: 'explore_chart', EXPORT_CSV: 'export_csv', RESIZE_LABEL: 'resize_label', - SHARE_CHART: 'share_chart', DOWNLOAD_AS_IMAGE: 'download_as_image', }; @@ -188,12 +189,13 @@ class SliceHeaderControls extends React.PureComponent { addSuccessToast, addDangerToast, isFullSize, + supersetCanShare, } = this.props; const crossFilterItems = getChartMetadataRegistry().items; const isCrossFilter = Object.entries(crossFilterItems) // @ts-ignore .filter(([, { value }]) => - value.behaviors?.includes(Behavior.CROSS_FILTER), + value.behaviors?.includes(Behavior.INTERACTIVE_CHART), ) .find(([key]) => key === slice.viz_type); @@ -253,18 +255,20 @@ class SliceHeaderControls extends React.PureComponent { )} - + {supersetCanShare && ( + + )} {resizeLabel} diff --git a/superset-frontend/src/dashboard/components/UndoRedoKeyListeners/UndoRedoKeyListeners.test.tsx b/superset-frontend/src/dashboard/components/UndoRedoKeyListeners/UndoRedoKeyListeners.test.tsx new file mode 100644 index 0000000000000..7d36f66469fba --- /dev/null +++ b/superset-frontend/src/dashboard/components/UndoRedoKeyListeners/UndoRedoKeyListeners.test.tsx @@ -0,0 +1,64 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { render, fireEvent } from 'spec/helpers/testing-library'; +import UndoRedoKeyListeners from '.'; + +const defaultProps = { + onUndo: jest.fn(), + onRedo: jest.fn(), +}; + +test('renders nothing', () => { + const { container } = render(); + expect(container.children).toHaveLength(0); +}); + +test('triggers onUndo', () => { + const onUndo = jest.fn(); + render(); + fireEvent.keyDown(document.body, { key: 'z', keyCode: 90, ctrlKey: true }); + expect(onUndo).toHaveBeenCalledTimes(1); +}); + +test('triggers onRedo', () => { + const onRedo = jest.fn(); + render(); + fireEvent.keyDown(document.body, { key: 'y', keyCode: 89, ctrlKey: true }); + expect(onRedo).toHaveBeenCalledTimes(1); +}); + +test('does not trigger when it is another key', () => { + const onUndo = jest.fn(); + const onRedo = jest.fn(); + render(); + fireEvent.keyDown(document.body, { key: 'x', keyCode: 88, ctrlKey: true }); + expect(onUndo).not.toHaveBeenCalled(); + expect(onRedo).not.toHaveBeenCalled(); +}); + +test('removes the event listener when unmounts', () => { + document.removeEventListener = jest.fn(); + const { unmount } = render(); + unmount(); + expect(document.removeEventListener).toHaveBeenCalledWith( + 'keydown', + expect.anything(), + ); +}); diff --git a/superset-frontend/src/dashboard/components/UndoRedoKeylisteners.jsx b/superset-frontend/src/dashboard/components/UndoRedoKeyListeners/index.jsx similarity index 93% rename from superset-frontend/src/dashboard/components/UndoRedoKeylisteners.jsx rename to superset-frontend/src/dashboard/components/UndoRedoKeyListeners/index.jsx index 82aa58caa2f11..a6160dd295d0d 100644 --- a/superset-frontend/src/dashboard/components/UndoRedoKeylisteners.jsx +++ b/superset-frontend/src/dashboard/components/UndoRedoKeyListeners/index.jsx @@ -24,7 +24,7 @@ const propTypes = { onRedo: PropTypes.func.isRequired, }; -class UndoRedoKeylisteners extends React.PureComponent { +class UndoRedoKeyListeners extends React.PureComponent { constructor(props) { super(props); this.handleKeydown = this.handleKeydown.bind(this); @@ -59,6 +59,6 @@ class UndoRedoKeylisteners extends React.PureComponent { } } -UndoRedoKeylisteners.propTypes = propTypes; +UndoRedoKeyListeners.propTypes = propTypes; -export default UndoRedoKeylisteners; +export default UndoRedoKeyListeners; diff --git a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx b/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx index 18cec534189dd..bc1f78f7df179 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx @@ -65,11 +65,13 @@ const propTypes = { isExpanded: PropTypes.bool.isRequired, isCached: PropTypes.bool, supersetCanExplore: PropTypes.bool.isRequired, + supersetCanShare: PropTypes.bool.isRequired, supersetCanCSV: PropTypes.bool.isRequired, sliceCanEdit: PropTypes.bool.isRequired, addSuccessToast: PropTypes.func.isRequired, addDangerToast: PropTypes.func.isRequired, - ownCurrentState: PropTypes.object, + ownState: PropTypes.object, + filterState: PropTypes.object, }; const defaultProps = { @@ -256,11 +258,13 @@ export default class Chart extends React.Component { toggleExpandSlice, timeout, supersetCanExplore, + supersetCanShare, supersetCanCSV, sliceCanEdit, addSuccessToast, addDangerToast, - ownCurrentState, + ownState, + filterState, handleToggleFullSize, isFullSize, } = this.props; @@ -311,6 +315,7 @@ export default class Chart extends React.Component { updateSliceName={updateSliceName} sliceName={sliceName} supersetCanExplore={supersetCanExplore} + supersetCanShare={supersetCanShare} supersetCanCSV={supersetCanCSV} sliceCanEdit={sliceCanEdit} componentId={componentId} @@ -368,7 +373,8 @@ export default class Chart extends React.Component { dashboardId={dashboardId} initialValues={initialValues} formData={formData} - ownCurrentState={ownCurrentState} + ownState={ownState} + filterState={filterState} queriesResponse={chart.queriesResponse} timeout={timeout} triggerQuery={chart.triggerQuery} diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tab.test.tsx b/superset-frontend/src/dashboard/components/gridComponents/Tab.test.tsx new file mode 100644 index 0000000000000..1e99b6496cff7 --- /dev/null +++ b/superset-frontend/src/dashboard/components/gridComponents/Tab.test.tsx @@ -0,0 +1,235 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import userEvent from '@testing-library/user-event'; +import React from 'react'; +import { render, screen } from 'spec/helpers/testing-library'; +import DashboardComponent from 'src/dashboard/containers/DashboardComponent'; +import EditableTitle from 'src/components/EditableTitle'; +import DragDroppable from 'src/dashboard/components/dnd/DragDroppable'; + +import Tab from './Tab'; + +jest.mock('src/dashboard/containers/DashboardComponent', () => + jest.fn(() =>
), +); +jest.mock('src/components/EditableTitle', () => + jest.fn(props => ( + + )), +); +jest.mock('src/dashboard/components/dnd/DragDroppable', () => + jest.fn(props => { + const childProps = props.editMode + ? { + dragSourceRef: props.dragSourceRef, + dropIndicatorProps: props.dropIndicatorProps, + } + : {}; + return ( +
+ + {props.children(childProps)} +
+ ); + }), +); + +const creteProps = () => ({ + id: 'TAB-YT6eNksV-', + parentId: 'TABS-L-d9eyOE-b', + depth: 2, + index: 1, + renderType: 'RENDER_TAB_CONTENT', + availableColumnCount: 12, + columnWidth: 120, + isFocused: false, + component: { + children: ['ROW-DR80aHJA2c', 'ROW--BIzjz9F0'], + id: 'TAB-YT6eNksV-', + meta: { text: '🚀 Aspiring Developers' }, + parents: ['ROOT_ID', 'GRID_ID', 'TABS-L-d9eyOE-b'], + type: 'TAB', + }, + parentComponent: { + children: ['TAB-AsMaxdYL_t', 'TAB-YT6eNksV-', 'TAB-l_9I0aNYZ'], + id: 'TABS-L-d9eyOE-b', + meta: {}, + parents: ['ROOT_ID', 'GRID_ID'], + type: 'TABS', + }, + editMode: false, + undoLength: 0, + redoLength: 0, + filters: {}, + directPathToChild: ['ROOT_ID', 'GRID_ID', 'TABS-L-d9eyOE-b', 'TAB-YT6eNksV-'], + directPathLastUpdated: 1617374760080, + dashboardId: 23, + focusedFilterScope: null, + isComponentVisible: true, + onDropOnTab: jest.fn(), + handleComponentDrop: jest.fn(), + updateComponents: jest.fn(), + setDirectPathToChild: jest.fn(), +}); + +beforeEach(() => { + jest.clearAllMocks(); +}); + +test('Render tab (no content)', () => { + const props = creteProps(); + props.renderType = 'RENDER_TAB'; + render(, { useRedux: true, useDnd: true }); + expect(screen.getByText('🚀 Aspiring Developers')).toBeInTheDocument(); + expect(EditableTitle).toBeCalledTimes(1); + expect(DragDroppable).toBeCalledTimes(1); +}); + +test('Render tab (no content) editMode:true', () => { + const props = creteProps(); + props.editMode = true; + props.renderType = 'RENDER_TAB'; + render(, { useRedux: true, useDnd: true }); + expect(screen.getByText('🚀 Aspiring Developers')).toBeInTheDocument(); + expect(EditableTitle).toBeCalledTimes(1); + expect(DragDroppable).toBeCalledTimes(1); +}); + +test('Edit table title', () => { + const props = creteProps(); + props.editMode = true; + props.renderType = 'RENDER_TAB'; + render(, { useRedux: true, useDnd: true }); + + expect(EditableTitle).toBeCalledTimes(1); + expect(DragDroppable).toBeCalledTimes(1); + + expect(props.updateComponents).not.toBeCalled(); + userEvent.click(screen.getByText('🚀 Aspiring Developers')); + expect(props.updateComponents).toBeCalled(); +}); + +test('Render tab (with content)', () => { + const props = creteProps(); + props.isFocused = true; + render(, { useRedux: true, useDnd: true }); + expect(DashboardComponent).toBeCalledTimes(2); + expect(DashboardComponent).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + availableColumnCount: 12, + columnWidth: 120, + depth: 2, + id: 'ROW-DR80aHJA2c', + index: 0, + isComponentVisible: true, + onChangeTab: expect.any(Function), + onDrop: expect.any(Function), + onResize: expect.any(Function), + onResizeStart: expect.any(Function), + onResizeStop: expect.any(Function), + parentId: 'TAB-YT6eNksV-', + }), + {}, + ); + expect(DashboardComponent).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + availableColumnCount: 12, + columnWidth: 120, + depth: 2, + id: 'ROW--BIzjz9F0', + index: 1, + isComponentVisible: true, + onChangeTab: expect.any(Function), + onDrop: expect.any(Function), + onResize: expect.any(Function), + onResizeStart: expect.any(Function), + onResizeStop: expect.any(Function), + parentId: 'TAB-YT6eNksV-', + }), + {}, + ); + expect(DragDroppable).toBeCalledTimes(0); +}); + +test('Render tab (with content) editMode:true', () => { + const props = creteProps(); + props.isFocused = true; + props.editMode = true; + render(, { useRedux: true, useDnd: true }); + expect(DashboardComponent).toBeCalledTimes(2); + expect(DashboardComponent).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + availableColumnCount: 12, + columnWidth: 120, + depth: 2, + id: 'ROW-DR80aHJA2c', + index: 0, + isComponentVisible: true, + onChangeTab: expect.any(Function), + onDrop: expect.any(Function), + onResize: expect.any(Function), + onResizeStart: expect.any(Function), + onResizeStop: expect.any(Function), + parentId: 'TAB-YT6eNksV-', + }), + {}, + ); + expect(DashboardComponent).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + availableColumnCount: 12, + columnWidth: 120, + depth: 2, + id: 'ROW--BIzjz9F0', + index: 1, + isComponentVisible: true, + onChangeTab: expect.any(Function), + onDrop: expect.any(Function), + onResize: expect.any(Function), + onResizeStart: expect.any(Function), + onResizeStop: expect.any(Function), + parentId: 'TAB-YT6eNksV-', + }), + {}, + ); + expect(DragDroppable).toBeCalledTimes(2); +}); + +test('Should call "handleDrop" and "handleTopDropTargetDrop"', () => { + const props = creteProps(); + props.isFocused = true; + props.editMode = true; + render(, { useRedux: true, useDnd: true }); + + expect(props.handleComponentDrop).not.toBeCalled(); + userEvent.click(screen.getAllByRole('button')[0]); + expect(props.handleComponentDrop).toBeCalledTimes(1); + expect(props.onDropOnTab).not.toBeCalled(); + userEvent.click(screen.getAllByRole('button')[1]); + expect(props.onDropOnTab).toBeCalledTimes(1); + expect(props.handleComponentDrop).toBeCalledTimes(2); +}); diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tabs.test.tsx b/superset-frontend/src/dashboard/components/gridComponents/Tabs.test.tsx new file mode 100644 index 0000000000000..f35dcd4df10b3 --- /dev/null +++ b/superset-frontend/src/dashboard/components/gridComponents/Tabs.test.tsx @@ -0,0 +1,233 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import userEvent from '@testing-library/user-event'; +import React from 'react'; +import { render, screen, waitFor } from 'spec/helpers/testing-library'; +import DashboardComponent from 'src/dashboard/containers/DashboardComponent'; +import DragDroppable from 'src/dashboard/components/dnd/DragDroppable'; +import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton'; +import getLeafComponentIdFromPath from 'src/dashboard/util/getLeafComponentIdFromPath'; + +import Tabs from './Tabs'; + +jest.mock('src/dashboard/containers/DashboardComponent', () => + jest.fn(props => ( + + )), +); + +jest.mock('src/dashboard/components/DeleteComponentButton', () => + jest.fn(props => ( + + )), +); +jest.mock('src/dashboard/util/getLeafComponentIdFromPath', () => jest.fn()); + +jest.mock('src/dashboard/components/dnd/DragDroppable', () => + jest.fn(props => { + const childProps = props.editMode + ? { + dragSourceRef: props.dragSourceRef, + dropIndicatorProps: props.dropIndicatorProps, + } + : {}; + return ( +
+ + {props.children(childProps)} +
+ ); + }), +); + +const createProps = () => ({ + id: 'TABS-L-d9eyOE-b', + parentId: 'GRID_ID', + depth: 2, + index: 0, + availableColumnCount: 12, + columnWidth: 120, + isComponentVisible: true, + component: { + children: ['TAB-AsMaxdYL_t', 'TAB-YT6eNksV-', 'TAB-l_9I0aNYZ'], + id: 'TABS-L-d9eyOE-b', + meta: {}, + parents: ['ROOT_ID', 'GRID_ID'], + type: 'TABS', + }, + parentComponent: { + children: ['TABS-L-d9eyOE-b'], + id: 'GRID_ID', + parents: ['ROOT_ID'], + type: 'GRID', + }, + editMode: true, + undoLength: 0, + redoLength: 0, + filters: {}, + directPathToChild: [], + directPathLastUpdated: 1617395480760, + dashboardId: 23, + focusedFilterScope: null, + renderTabContent: true, + renderHoverMenu: true, + logEvent: jest.fn(), + createComponent: jest.fn(), + handleComponentDrop: jest.fn(), + onChangeTab: jest.fn(), + deleteComponent: jest.fn(), + updateComponents: jest.fn(), +}); + +beforeEach(() => { + jest.clearAllMocks(); +}); + +test('Should render editMode:true', () => { + const props = createProps(); + render(, { useRedux: true, useDnd: true }); + expect(screen.getAllByRole('tab')).toHaveLength(3); + expect(DragDroppable).toBeCalledTimes(1); + expect(DashboardComponent).toBeCalledTimes(4); + expect(DeleteComponentButton).toBeCalledTimes(1); + expect(screen.getAllByRole('button', { name: 'remove' })).toHaveLength(3); + expect(screen.getAllByRole('button', { name: 'Add tab' })).toHaveLength(2); +}); + +test('Should render editMode:false', () => { + const props = createProps(); + props.editMode = false; + render(, { useRedux: true, useDnd: true }); + expect(screen.getAllByRole('tab')).toHaveLength(3); + expect(DragDroppable).toBeCalledTimes(1); + expect(DashboardComponent).toBeCalledTimes(4); + expect(DeleteComponentButton).not.toBeCalled(); + expect( + screen.queryByRole('button', { name: 'remove' }), + ).not.toBeInTheDocument(); + expect( + screen.queryByRole('button', { name: 'Add tab' }), + ).not.toBeInTheDocument(); +}); + +test('Update component props', () => { + const props = createProps(); + (getLeafComponentIdFromPath as jest.Mock).mockResolvedValueOnce('none'); + props.editMode = false; + const { rerender } = render(, { + useRedux: true, + useDnd: true, + }); + expect(DeleteComponentButton).not.toBeCalled(); + + props.editMode = true; + rerender(); + expect(DeleteComponentButton).toBeCalledTimes(1); +}); + +test('Clicking on "DeleteComponentButton"', () => { + const props = createProps(); + render(, { + useRedux: true, + useDnd: true, + }); + + expect(props.deleteComponent).not.toBeCalled(); + userEvent.click(screen.getByTestId('DeleteComponentButton')); + expect(props.deleteComponent).toBeCalledWith('TABS-L-d9eyOE-b', 'GRID_ID'); +}); + +test('Add new tab', () => { + const props = createProps(); + render(, { + useRedux: true, + useDnd: true, + }); + + expect(props.createComponent).not.toBeCalled(); + userEvent.click(screen.getAllByRole('button', { name: 'Add tab' })[0]); + expect(props.createComponent).toBeCalled(); +}); + +test('Removing a tab', async () => { + const props = createProps(); + render(, { + useRedux: true, + useDnd: true, + }); + + expect(props.deleteComponent).not.toBeCalled(); + expect(screen.queryByText('Delete dashboard tab?')).not.toBeInTheDocument(); + userEvent.click(screen.getAllByRole('button', { name: 'remove' })[0]); + expect(props.deleteComponent).not.toBeCalled(); + + expect(await screen.findByText('Delete dashboard tab?')).toBeInTheDocument(); + + expect(props.deleteComponent).not.toBeCalled(); + userEvent.click(screen.getByRole('button', { name: 'DELETE' })); + expect(props.deleteComponent).toBeCalled(); +}); + +test('Switching tabs', () => { + const props = createProps(); + render(, { + useRedux: true, + useDnd: true, + }); + + expect(props.logEvent).not.toBeCalled(); + expect(props.onChangeTab).not.toBeCalled(); + userEvent.click(screen.getAllByRole('tab')[2]); + expect(props.logEvent).toBeCalled(); + expect(props.onChangeTab).toBeCalled(); +}); + +test('Call "DashboardComponent.onDropOnTab"', async () => { + const props = createProps(); + render(, { + useRedux: true, + useDnd: true, + }); + + expect(props.logEvent).not.toBeCalled(); + expect(props.onChangeTab).not.toBeCalled(); + userEvent.click(screen.getAllByText('DashboardComponent')[0]); + + await waitFor(() => { + expect(props.logEvent).toBeCalled(); + expect(props.onChangeTab).toBeCalled(); + }); +}); diff --git a/superset-frontend/src/dashboard/components/menu/ShareMenuItems/ShareMenuItems.test.tsx b/superset-frontend/src/dashboard/components/menu/ShareMenuItems/ShareMenuItems.test.tsx new file mode 100644 index 0000000000000..f4c65ce67dfa9 --- /dev/null +++ b/superset-frontend/src/dashboard/components/menu/ShareMenuItems/ShareMenuItems.test.tsx @@ -0,0 +1,194 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { Menu } from 'src/common/components'; +import { render, screen, waitFor } from 'spec/helpers/testing-library'; +import userEvent from '@testing-library/user-event'; +import * as copyTextToClipboard from 'src/utils/copy'; +import fetchMock from 'fetch-mock'; +import ShareMenuItems from '.'; + +const spy = jest.spyOn(copyTextToClipboard, 'default'); + +const createProps = () => ({ + addDangerToast: jest.fn(), + addSuccessToast: jest.fn(), + url: '/superset/dashboard/26/?preselect_filters=%7B%7D', + copyMenuItemTitle: 'Copy dashboard URL', + emailMenuItemTitle: 'Share dashboard by email', + emailSubject: 'Superset dashboard COVID Vaccine Dashboard', + emailBody: 'Check out this dashboard: ', +}); + +const { location } = window; + +beforeAll((): void => { + // @ts-ignore + delete window.location; + fetchMock.post( + 'http://localhost/r/shortner/', + { body: 'http://localhost:8088/r/3' }, + { + sendAsJson: false, + }, + ); +}); + +beforeEach(() => { + jest.clearAllMocks(); + window.location = { + href: '', + } as any; +}); + +afterAll((): void => { + window.location = location; +}); + +test('Should render menu items', () => { + const props = createProps(); + render( + + + , + ); + expect( + screen.getByRole('menuitem', { name: 'Copy dashboard URL' }), + ).toBeInTheDocument(); + expect( + screen.getByRole('menuitem', { name: 'Share dashboard by email' }), + ).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'Copy dashboard URL' }), + ).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: 'Share dashboard by email' }), + ).toBeInTheDocument(); +}); + +test('Click on "Copy dashboard URL" and succeed', async () => { + spy.mockResolvedValue(undefined); + const props = createProps(); + render( + + + , + ); + + await waitFor(() => { + expect(spy).toBeCalledTimes(0); + expect(props.addSuccessToast).toBeCalledTimes(0); + expect(props.addDangerToast).toBeCalledTimes(0); + }); + + userEvent.click(screen.getByRole('button', { name: 'Copy dashboard URL' })); + + await waitFor(() => { + expect(spy).toBeCalledTimes(1); + expect(spy).toBeCalledWith('http://localhost:8088/r/3'); + expect(props.addSuccessToast).toBeCalledTimes(1); + expect(props.addSuccessToast).toBeCalledWith('Copied to clipboard!'); + expect(props.addDangerToast).toBeCalledTimes(0); + }); +}); + +test('Click on "Copy dashboard URL" and fail', async () => { + spy.mockRejectedValue(undefined); + const props = createProps(); + render( + + + , + ); + + await waitFor(() => { + expect(spy).toBeCalledTimes(0); + expect(props.addSuccessToast).toBeCalledTimes(0); + expect(props.addDangerToast).toBeCalledTimes(0); + }); + + userEvent.click(screen.getByRole('button', { name: 'Copy dashboard URL' })); + + await waitFor(() => { + expect(spy).toBeCalledTimes(1); + expect(spy).toBeCalledWith('http://localhost:8088/r/3'); + expect(props.addSuccessToast).toBeCalledTimes(0); + expect(props.addDangerToast).toBeCalledTimes(1); + expect(props.addDangerToast).toBeCalledWith( + 'Sorry, your browser does not support copying.', + ); + }); +}); + +test('Click on "Share dashboard by email" and succeed', async () => { + const props = createProps(); + render( + + + , + ); + + await waitFor(() => { + expect(props.addDangerToast).toBeCalledTimes(0); + expect(window.location.href).toBe(''); + }); + + userEvent.click( + screen.getByRole('button', { name: 'Share dashboard by email' }), + ); + + await waitFor(() => { + expect(props.addDangerToast).toBeCalledTimes(0); + expect(window.location.href).toBe( + 'mailto:?Subject=Superset dashboard COVID Vaccine Dashboard%20&Body=Check out this dashboard: http://localhost:8088/r/3', + ); + }); +}); + +test('Click on "Share dashboard by email" and fail', async () => { + fetchMock.post( + 'http://localhost/r/shortner/', + { status: 404 }, + { overwriteRoutes: true }, + ); + const props = createProps(); + render( + + + , + ); + + await waitFor(() => { + expect(props.addDangerToast).toBeCalledTimes(0); + expect(window.location.href).toBe(''); + }); + + userEvent.click( + screen.getByRole('button', { name: 'Share dashboard by email' }), + ); + + await waitFor(() => { + expect(window.location.href).toBe(''); + expect(props.addDangerToast).toBeCalledTimes(1); + expect(props.addDangerToast).toBeCalledWith( + 'Sorry, something went wrong. Try again later.', + ); + }); +}); diff --git a/superset-frontend/src/dashboard/components/menu/ShareMenuItems.tsx b/superset-frontend/src/dashboard/components/menu/ShareMenuItems/index.tsx similarity index 100% rename from superset-frontend/src/dashboard/components/menu/ShareMenuItems.tsx rename to superset-frontend/src/dashboard/components/menu/ShareMenuItems/index.tsx diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/CascadePopover/index.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/CascadePopover/index.tsx index 76be1409fb244..60396caea1b0a 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/CascadePopover/index.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/CascadePopover/index.tsx @@ -22,8 +22,8 @@ import Popover from 'src/components/Popover'; import Icon from 'src/components/Icon'; import { Pill } from 'src/dashboard/components/FiltersBadge/Styles'; import { useSelector } from 'react-redux'; -import { getInitialMask } from 'src/dataMask/reducer'; -import { MaskWithId } from 'src/dataMask/types'; +import { getInitialDataMask } from 'src/dataMask/reducer'; +import { DataMaskWithId } from 'src/dataMask/types'; import FilterControl from 'src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControl'; import CascadeFilterControl from 'src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/CascadeFilterControl'; import { CascadeFilter } from 'src/dashboard/components/nativeFilters/FilterBar/CascadeFilters/types'; @@ -82,9 +82,8 @@ const CascadePopover: React.FC = ({ directPathToChild, }) => { const [currentPathToChild, setCurrentPathToChild] = useState(); - const dataMask = useSelector( - state => - state.dataMask.nativeFilters[filter.id] ?? getInitialMask(filter.id), + const dataMask = useSelector( + state => state.dataMask[filter.id] ?? getInitialDataMask(filter.id), ); useEffect(() => { @@ -98,7 +97,7 @@ const CascadePopover: React.FC = ({ const getActiveChildren = useCallback( (filter: CascadeFilter): CascadeFilter[] | null => { const children = filter.cascadeChildren || []; - const currentValue = dataMask.currentState?.value; + const currentValue = dataMask.filterState?.value; const activeChildren = children.flatMap( childFilter => getActiveChildren(childFilter) || [], diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.test.tsx new file mode 100644 index 0000000000000..a942e2516af2d --- /dev/null +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.test.tsx @@ -0,0 +1,86 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { render, screen } from 'spec/helpers/testing-library'; +import userEvent from '@testing-library/user-event'; +import { mockStore } from 'spec/fixtures/mockStore'; +import { Provider } from 'react-redux'; +import FilterBar, { FiltersBarProps } from '.'; + +const createProps = () => ({ + filtersOpen: false, + toggleFiltersBar: jest.fn(), +}); + +const setup = (props: FiltersBarProps) => ( + + + +); + +test('should render', () => { + const mockedProps = createProps(); + const { container } = render(setup(mockedProps)); + expect(container).toBeInTheDocument(); +}); + +test('should render the "Filters" heading', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByText('Filters')).toBeInTheDocument(); +}); + +test('should render the "Clear all" option', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByText('Clear all')).toBeInTheDocument(); +}); + +test('should render the "Apply" option', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByText('Apply')).toBeInTheDocument(); +}); + +test('should render the collapse icon', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByRole('img', { name: 'collapse' })).toBeInTheDocument(); +}); + +test('should render the filter icon', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByRole('img', { name: 'filter' })).toBeInTheDocument(); +}); + +test('should render the filter control name', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByText('test')).toBeInTheDocument(); +}); + +test('should toggle', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + const collapse = screen.getByRole('img', { name: 'collapse' }); + expect(mockedProps.toggleFiltersBar).not.toHaveBeenCalled(); + userEvent.click(collapse); + expect(mockedProps.toggleFiltersBar).toHaveBeenCalled(); +}); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/FilterConfigurationLink.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/FilterConfigurationLink.test.tsx new file mode 100644 index 0000000000000..363a30f423f31 --- /dev/null +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/FilterConfigurationLink.test.tsx @@ -0,0 +1,49 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { render, screen } from 'spec/helpers/testing-library'; +import userEvent from '@testing-library/user-event'; +import FilterConfigurationLink from '.'; + +test('should render', () => { + const { container } = render( + Config link, + { + useRedux: true, + }, + ); + expect(container).toBeInTheDocument(); +}); + +test('should render the config link text', () => { + render(Config link, { + useRedux: true, + }); + expect(screen.getByText('Config link')).toBeInTheDocument(); +}); + +test('should render the modal on click', () => { + render(Config link, { + useRedux: true, + }); + const configLink = screen.getByText('Config link'); + expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); + userEvent.click(configLink); + expect(screen.getByRole('dialog')).toBeInTheDocument(); +}); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/index.tsx similarity index 89% rename from superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink.tsx rename to superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/index.tsx index 875cbfc1aee77..db125d7960929 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/index.tsx @@ -19,8 +19,8 @@ import React, { useState } from 'react'; import { useDispatch } from 'react-redux'; import { setFilterConfiguration } from 'src/dashboard/actions/nativeFilters'; -import { FilterConfiguration } from '../types'; -import { FiltersConfigModal } from '../FiltersConfigModal/FiltersConfigModal'; +import { FilterConfiguration } from 'src/dashboard/components/nativeFilters/types'; +import { FiltersConfigModal } from 'src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal'; export interface FCBProps { createNewOnOpen?: boolean; diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx index 424e587217509..7b630f8ff6ff6 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx @@ -17,7 +17,7 @@ * under the License. */ import React, { FC, useMemo, useState } from 'react'; -import { DataMaskUnit } from 'src/dataMask/types'; +import { DataMaskState } from 'src/dataMask/types'; import { DataMask, styled } from '@superset-ui/core'; import CascadePopover from '../CascadeFilters/CascadePopover'; import { buildCascadeFiltersTree } from './utils'; @@ -33,7 +33,7 @@ const Wrapper = styled.div` type FilterControlsProps = { directPathToChild?: string[]; - dataMaskSelected: DataMaskUnit; + dataMaskSelected: DataMaskState; onFilterSelectionChange: (filter: Filter, dataMask: DataMask) => void; }; @@ -49,7 +49,7 @@ const FilterControls: FC = ({ const cascadeFilters = useMemo(() => { const filtersWithValue = filterValues.map(filter => ({ ...filter, - currentValue: dataMaskSelected[filter.id]?.currentState?.value, + dataMask: dataMaskSelected[filter.id], })); return buildCascadeFiltersTree(filtersWithValue); }, [filterValues, dataMaskSelected]); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx index 06241e944ef35..f8b97cfc34251 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx @@ -78,6 +78,7 @@ const FilterValue: React.FC = ({ formData: newFormData, force: false, requestParams: { dashboardId: 0 }, + ownState: filter.dataMask?.ownState, }) .then(response => { if (isFeatureEnabled(FeatureFlag.GLOBAL_ASYNC_QUERIES)) { @@ -150,6 +151,8 @@ const FilterValue: React.FC = ({ queriesData={hasDataSource ? state : [{ data: [{}] }]} chartType={filterType} behaviors={[Behavior.NATIVE_FILTER]} + filterState={filter.dataMask?.filterState} + ownState={filter.dataMask?.ownState} hooks={{ setDataMask }} /> )} diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/state.ts b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/state.ts index 0a60c2c02239f..802a12d1953f6 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/state.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/state.ts @@ -29,9 +29,9 @@ export function useCascadingFilters(id: string) { const filter = filters[id]; const cascadeParentIds: string[] = filter?.cascadeParentIds ?? []; let cascadedFilters = {}; - const nativeFilters = useDataMask(); + const nativeFiltersDataMask = useDataMask(); cascadeParentIds.forEach(parentId => { - const parentState = nativeFilters[parentId] || {}; + const parentState = nativeFiltersDataMask[parentId] || {}; const { extraFormData: parentExtra = {} } = parentState; cascadedFilters = mergeExtraFormData(cascadedFilters, parentExtra); }); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/EditSection.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/EditSection.test.tsx new file mode 100644 index 0000000000000..812b0af270f66 --- /dev/null +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/EditSection.test.tsx @@ -0,0 +1,113 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { render, screen } from 'spec/helpers/testing-library'; +import userEvent from '@testing-library/user-event'; +import { mockStore } from 'spec/fixtures/mockStore'; +import { Provider } from 'react-redux'; +import EditSection, { EditSectionProps } from './EditSection'; + +const createProps = () => ({ + filterSetId: 'set-id', + dataMaskSelected: { + DefaultsID: { + filterState: { + value: 'value', + }, + }, + }, + onCancel: jest.fn(), + disabled: false, +}); + +const setup = (props: EditSectionProps) => ( + + + +); + +test('should render', () => { + const mockedProps = createProps(); + const { container } = render(setup(mockedProps)); + expect(container).toBeInTheDocument(); +}); + +test('should render the title', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByText('Editing filter set:')).toBeInTheDocument(); +}); + +test('should render the set name', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByText('Set name')).toBeInTheDocument(); +}); + +test('should render a textbox', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByRole('textbox')).toBeInTheDocument(); +}); + +test('should change the set name', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + const textbox = screen.getByRole('textbox'); + userEvent.clear(textbox); + userEvent.type(textbox, 'New name'); + expect(textbox).toHaveValue('New name'); +}); + +test('should render the enter icon', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByRole('img', { name: 'enter' })).toBeInTheDocument(); +}); + +test('should render the Cancel button', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByText('Cancel')).toBeInTheDocument(); +}); + +test('should cancel', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + const cancelBtn = screen.getByText('Cancel'); + expect(mockedProps.onCancel).not.toHaveBeenCalled(); + userEvent.click(cancelBtn); + expect(mockedProps.onCancel).toHaveBeenCalled(); +}); + +test('should render the Save button', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByText('Save')).toBeInTheDocument(); +}); + +test('should render the Save button as disabled', () => { + const mockedProps = createProps(); + const saveDisabledProps = { + ...mockedProps, + disabled: true, + }; + render(setup(saveDisabledProps)); + expect(screen.getByText('Save').parentElement).toBeDisabled(); +}); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/EditSection.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/EditSection.tsx index 60f42a203d336..c77ea56c8fb30 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/EditSection.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/EditSection.tsx @@ -22,7 +22,7 @@ import { Typography, Tooltip } from 'src/common/components'; import { useDispatch } from 'react-redux'; import Button from 'src/components/Button'; import { setFilterSetsConfiguration } from 'src/dashboard/actions/nativeFilters'; -import { DataMaskUnit } from 'src/dataMask/types'; +import { DataMaskState } from 'src/dataMask/types'; import { WarningOutlined } from '@ant-design/icons'; import { ActionButtons } from './Footer'; import { useDataMask, useFilters, useFilterSets } from '../state'; @@ -58,9 +58,9 @@ const ActionButton = styled.div<{ disabled?: boolean }>` } `; -type EditSectionProps = { +export type EditSectionProps = { filterSetId: string; - dataMaskSelected: DataMaskUnit; + dataMaskSelected: DataMaskState; onCancel: HandlerFunction; disabled: boolean; }; @@ -94,7 +94,7 @@ const EditSection: FC = ({ ...filterSet, name: filterSetName, nativeFilters: filters, - dataMask: { nativeFilters: { ...dataMaskApplied } }, + dataMask: { ...dataMaskApplied }, }; return filterSetId === filterSet.id ? newFilterSet : filterSet; }), diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSetUnit.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSetUnit.test.tsx new file mode 100644 index 0000000000000..a5e34432cd7f1 --- /dev/null +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSetUnit.test.tsx @@ -0,0 +1,100 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { render, screen } from 'spec/helpers/testing-library'; +import { mockStore } from 'spec/fixtures/mockStore'; +import { Provider } from 'react-redux'; +import userEvent from '@testing-library/user-event'; +import FilterSetUnit, { FilterSetUnitProps } from './FilterSetUnit'; + +const createProps = () => ({ + editMode: true, + setFilterSetName: jest.fn(), + onDelete: jest.fn(), + onEdit: jest.fn(), + onRebuild: jest.fn(), +}); + +function openDropdown() { + const dropdownIcon = screen.getByRole('img', { name: 'ellipsis' }); + userEvent.click(dropdownIcon); +} + +const setup = (props: FilterSetUnitProps) => ( + + + +); + +test('should render', () => { + const mockedProps = createProps(); + const { container } = render(setup(mockedProps)); + expect(container).toBeInTheDocument(); +}); + +test('should render the edit button', () => { + const mockedProps = createProps(); + const editModeOffProps = { + ...mockedProps, + editMode: false, + }; + render(setup(editModeOffProps)); + expect(screen.getByRole('button', { name: 'Edit' })).toBeInTheDocument(); +}); + +test('should render the menu', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + openDropdown(); + expect(screen.getByRole('menu')).toBeInTheDocument(); + expect(screen.getAllByRole('menuitem')).toHaveLength(3); + expect(screen.getByText('Edit')).toBeInTheDocument(); + expect(screen.getByText('Rebuild')).toBeInTheDocument(); + expect(screen.getByText('Delete')).toBeInTheDocument(); +}); + +test('should edit', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + openDropdown(); + const editBtn = screen.getByText('Edit'); + expect(mockedProps.onEdit).not.toHaveBeenCalled(); + userEvent.click(editBtn); + expect(mockedProps.onEdit).toHaveBeenCalled(); +}); + +test('should delete', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + openDropdown(); + const deleteBtn = screen.getByText('Delete'); + expect(mockedProps.onDelete).not.toHaveBeenCalled(); + userEvent.click(deleteBtn); + expect(mockedProps.onDelete).toHaveBeenCalled(); +}); + +test('should rebuild', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + openDropdown(); + const rebuildBtn = screen.getByText('Rebuild'); + expect(mockedProps.onRebuild).not.toHaveBeenCalled(); + userEvent.click(rebuildBtn); + expect(mockedProps.onRebuild).toHaveBeenCalled(); +}); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSetUnit.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSetUnit.tsx index 344cef091f367..2f628933e45c4 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSetUnit.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSetUnit.tsx @@ -19,7 +19,7 @@ import { Typography, Dropdown, Menu } from 'src/common/components'; import React, { FC } from 'react'; import { FilterSet } from 'src/dashboard/reducers/types'; -import { DataMaskUnit } from 'src/dataMask/types'; +import { DataMaskState } from 'src/dataMask/types'; import { CheckOutlined, EllipsisOutlined } from '@ant-design/icons'; import { HandlerFunction, styled, supersetTheme, t } from '@superset-ui/core'; import { Tooltip } from 'src/common/components/Tooltip'; @@ -40,12 +40,12 @@ const IconsBlock = styled.div` } `; -type FilterSetUnitProps = { +export type FilterSetUnitProps = { editMode?: boolean; isApplied?: boolean; filterSet?: FilterSet; filterSetName?: string; - dataMaskSelected?: DataMaskUnit; + dataMaskSelected?: DataMaskState; setFilterSetName?: (name: string) => void; onDelete?: HandlerFunction; onEdit?: HandlerFunction; @@ -114,7 +114,7 @@ const FilterSetUnit: FC = ({ ); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSets.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSets.test.tsx new file mode 100644 index 0000000000000..ef8f859274290 --- /dev/null +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FilterSets.test.tsx @@ -0,0 +1,67 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { render, screen } from 'spec/helpers/testing-library'; +import { mockStore } from 'spec/fixtures/mockStore'; +import { Provider } from 'react-redux'; +import FilterSets, { FilterSetsProps } from '.'; + +const createProps = () => ({ + disabled: false, + isFilterSetChanged: false, + dataMaskSelected: { + DefaultsID: { + filterState: { + value: 'value', + }, + }, + }, + onEditFilterSet: jest.fn(), + onFilterSelectionChange: jest.fn(), +}); + +const setup = (props: FilterSetsProps) => ( + + + +); + +test('should render', () => { + const mockedProps = createProps(); + const { container } = render(setup(mockedProps)); + expect(container).toBeInTheDocument(); +}); + +test('should render the default title', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByText('New filter set')).toBeInTheDocument(); +}); + +test('should render the right number of filters', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByText('Filters (1)')).toBeInTheDocument(); +}); + +test('should render the filters', () => { + const mockedProps = createProps(); + render(setup(mockedProps)); + expect(screen.getByText('Set name')).toBeInTheDocument(); +}); diff --git a/superset-frontend/spec/javascripts/dashboard/components/nativeFilters/FilterConfigurationLink_spec.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.test.tsx similarity index 52% rename from superset-frontend/spec/javascripts/dashboard/components/nativeFilters/FilterConfigurationLink_spec.tsx rename to superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.test.tsx index 5382ec5cf2d0e..1879b7922b48d 100644 --- a/superset-frontend/spec/javascripts/dashboard/components/nativeFilters/FilterConfigurationLink_spec.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.test.tsx @@ -17,29 +17,38 @@ * under the License. */ import React from 'react'; -import { styledMount as mount } from 'spec/helpers/theming'; -import { Provider } from 'react-redux'; -import FilterConfigurationLink from 'src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink'; +import { render, screen } from 'spec/helpers/testing-library'; import { mockStore } from 'spec/fixtures/mockStore'; +import { Provider } from 'react-redux'; +import FiltersHeader, { FiltersHeaderProps } from './FiltersHeader'; + +const mockedProps = { + dataMask: { + DefaultsID: { + filterState: { + value: 'value', + }, + }, + }, +}; +const setup = (props: FiltersHeaderProps) => ( + + + +); + +test('should render', () => { + const { container } = render(setup(mockedProps)); + expect(container).toBeInTheDocument(); +}); + +test('should render the right number of filters', () => { + render(setup(mockedProps)); + expect(screen.getByText('Filters (1)')).toBeInTheDocument(); +}); -describe('FilterConfigurationButton', () => { - const mockedProps = { - createNewOnOpen: false, - }; - it('is valid', () => { - expect( - React.isValidElement(), - ).toBe(true); - }); - it('takes in children', () => { - const wrapper = mount( - - - {' '} - Test - - , - ); - expect(wrapper.find('span')).toHaveLength(1); - }); +test('should render the name and value', () => { + render(setup(mockedProps)); + expect(screen.getByText('test:')).toBeInTheDocument(); + expect(screen.getByText('value')).toBeInTheDocument(); }); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.tsx index fb5a66d51878c..3b4b91a868c3e 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/FiltersHeader.tsx @@ -19,7 +19,7 @@ import React, { FC } from 'react'; import { styled, t } from '@superset-ui/core'; import { Collapse, Typography, Tooltip } from 'src/common/components'; -import { DataMaskUnit } from 'src/dataMask/types'; +import { DataMaskState } from 'src/dataMask/types'; import { CaretDownOutlined } from '@ant-design/icons'; import { areObjectsEqual } from 'src/reduxUtils'; import { FilterSet } from 'src/dashboard/reducers/types'; @@ -54,8 +54,8 @@ const StyledCollapse = styled(Collapse)` } `; -type FiltersHeaderProps = { - dataMask?: DataMaskUnit; +export type FiltersHeaderProps = { + dataMask?: DataMaskState; filterSet?: FilterSet; }; @@ -93,13 +93,14 @@ const FiltersHeader: FC = ({ dataMask, filterSet }) => { t('Filter metadata changed in dashboard. It will not be applied.')) } placement="bottomLeft" + key={id} > -
+
{name}:  - {getFilterValueForDisplay(dataMask?.[id]?.currentState?.value) || ( + {getFilterValueForDisplay(dataMask?.[id]?.filterState?.value) || ( {t('None')} )} diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/Footer.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/Footer.test.tsx new file mode 100644 index 0000000000000..b1a2638e7e004 --- /dev/null +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterSets/Footer.test.tsx @@ -0,0 +1,94 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import userEvent from '@testing-library/user-event'; +import { render, screen } from 'spec/helpers/testing-library'; +import Footer from './Footer'; + +const createProps = () => ({ + filterSetName: 'Set name', + disabled: false, + editMode: false, + onCancel: jest.fn(), + onEdit: jest.fn(), + onCreate: jest.fn(), +}); + +const editModeProps = { + ...createProps(), + editMode: true, +}; + +test('should render', () => { + const mockedProps = createProps(); + const { container } = render(