Compare commits

...

91 Commits

Author SHA1 Message Date
Elizabeth Thompson
e7000c72d5 update changelog 2022-10-20 11:58:07 -07:00
dependabot[bot]
f76755e72b chore(deps): bump moment from 2.29.2 to 2.29.4 in /superset-frontend (#20644)
Bumps [moment](https://github.com/moment/moment) from 2.29.2 to 2.29.4.
- [Release notes](https://github.com/moment/moment/releases)
- [Changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/moment/moment/compare/2.29.2...2.29.4)

---
updated-dependencies:
- dependency-name: moment
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-20 09:17:28 -07:00
Elizabeth Thompson
42c9eacaeb fix test 2022-10-19 14:15:16 -07:00
Elizabeth Thompson
af6887a84f update changelog 2022-10-18 16:28:48 -07:00
Elizabeth Thompson
2fbda195e8 xit cypress test 2022-10-18 14:17:48 -07:00
Mayur
8432c64b60 fix: allow adhoc columns in non-aggregate query (#21729) 2022-10-18 14:06:51 -07:00
Stephen Liu
cea8e4daf2 fix(dashboard): dashboard doesn't load properly if it has tabs (#21576)
Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com>
2022-10-18 14:02:58 -07:00
Mayur
930148dec6 fix(dashboard): show correct roles for dashboard access dropdown (#21549) 2022-10-18 14:00:56 -07:00
Elizabeth Thompson
f7e664bd68 fix: remove deprecated ETagResponseMixin (#21773)
(cherry picked from commit 75e6a04269)
2022-10-18 13:55:45 -07:00
Rui Zhao
b1c9adb52b fix(report): Fix permission check for set up email report on charts/dashboards. Fixes #21559 (#21561)
Co-authored-by: Rui Zhao <zhaorui@dropbox.com>
(cherry picked from commit 7f971b4103)
2022-10-18 13:55:41 -07:00
Yongjie Zhao
9e907be7a8 fix: annotation broken (#20651)
* fix: annotation broken

* fix UT

* add annotation data to mixed timeseries chart

(cherry picked from commit 7f918a4ec0)
2022-10-18 13:55:37 -07:00
Elizabeth Thompson
61b64492a2 bump package-lock version 2022-10-06 15:45:55 -07:00
AAfghahi
2c65ef967e build: changelog for 2.0.1 (#21721) 2022-10-06 14:32:46 -07:00
AAfghahi
ad52469a04 test fix 2022-10-06 14:45:06 -04:00
AAfghahi
548311c424 linting issues 2022-10-06 14:45:06 -04:00
Hugh A. Miles II
40c5b2a688 remove eleement reference (#20830)
(cherry picked from commit 2263a76f4d)
2022-10-06 14:45:06 -04:00
aehanno
511cafa790 fix: Add locale for DatePicker component (#20063)
Co-authored-by: Kevin Dethelot <kevin.dethelot@kosmos.fr>
Co-authored-by: Yongjie Zhao <yongjie.zhao@gmail.com>
2022-10-06 14:45:06 -04:00
Daniel Vaz Gaspar
dd919bc176 fix: disallow users from viewing other user's profile on config (#21302) 2022-10-06 14:45:06 -04:00
Cody Leff
143c5f1ecc fix(explore): Prevent unnecessary series limit subquery (#21154)
* Prevent series limit when no series limit columns specified.

* Add timeseries check for legacy charts.

* Apply fix to helpers.py.

* Skip Cypress color consistency tests.
2022-10-06 14:45:06 -04:00
Mayur
dfcb66a098 fix: set correct favicon from config for login and FAB list views (#21498)
(cherry picked from commit b29e7e7d9e)
2022-10-06 14:45:06 -04:00
MichaelHintz
440ab64c53 fix(sqllab): Fix cursor alignment in SQL lab editor by avoiding Lucida Console font on Windows (#21380)
(cherry picked from commit 3098e657e5)
2022-10-06 14:45:06 -04:00
aehanno
5b662f7874 fix: Add french translation missing (#20061)
(cherry picked from commit 944808a0ce)
2022-10-06 14:45:06 -04:00
JUST.in DO IT
08052a7db3 fix(plugin-chart-echarts): missing value format in mixed timeseries (#21044) 2022-10-06 14:45:06 -04:00
Daniel Vaz Gaspar
2583f7fde3 fix: cached common bootstrap Revert (#21018) (#21419)
(cherry picked from commit 094400c308)
2022-10-06 14:45:05 -04:00
Ville Brofeldt
db661ec17f fix(plugin-chart-echarts): show zero value in tooltip (#21296)
Co-authored-by: Ville Brofeldt <ville.brofeldt@apple.com>
(cherry picked from commit 1aeb8fd6b7)
2022-10-06 14:45:05 -04:00
Kamil Gabryjelski
7f8d6b3400 fix(explore): Time column label not formatted when GENERIC_X_AXES enabled (#21294)
(cherry picked from commit c3a00d43d0)
2022-10-06 14:45:05 -04:00
Daniel Vaz Gaspar
2b760d0775 feat: adds TLS certificate validation option for SMTP (#21272)
(cherry picked from commit 9fd752057e)
2022-09-14 11:38:28 -07:00
ʈᵃᵢ
4298690e16 fix(celery cache warmup): add auth and use warm_up_cache endpoint (#21076)
(cherry picked from commit 04dd8d414d)
2022-09-14 11:38:27 -07:00
Stephen Liu
32736680da fix(database-list): hidden upload file button if no permission (#21216)
(cherry picked from commit 0c43190e04)
2022-09-14 11:38:27 -07:00
JUST.in DO IT
9337dec038 fix(sqllab): missing zero values while copy-to-clipboard (#21153)
(cherry picked from commit 4e23d62d4f)
2022-09-14 11:38:26 -07:00
stevetracvc
05d7c3d74d fix(native filters): groupby filter issue (#21084)
(cherry picked from commit d79b0bfc74)
2022-09-14 11:38:25 -07:00
Stephen Liu
c3e04d7ebf fix(plugin-chart-handlebars): order by control not work (#21005)
(cherry picked from commit e70699fb43)
2022-09-14 11:38:25 -07:00
EugeneTorap
47c3cd10bd fix(dashboard): Fix scroll behaviour in DashboardBuilderSidepane (#20969)
(cherry picked from commit 6f3a555e58)
2022-09-14 11:38:24 -07:00
Bogdan
b4df82591e Memoize the common_bootstrap_payload (#21018)
Try patch

Co-authored-by: Bogdan Kyryliuk <bogdankyryliuk@dropbox.com>
(cherry picked from commit 495a205dec)
2022-09-14 11:38:24 -07:00
Stephen Liu
884e2f1ca7 fix(plugin-chart-echarts): gauge chart enhancements and fixes (#21007)
* fix(plugin-chart-echarts): gauge chart enhancements and fixes

* fix lint

(cherry picked from commit b303d1e156)
2022-09-14 11:38:24 -07:00
Erik Cederstrand
e91222eb65 chore(deps): unpin holidays dependency version (#21091)
The blocking issue has been fixed upstream

Co-authored-by: Erik Cederstrand <erik@adamatics.com>
(cherry picked from commit d817a1dc87)
2022-09-14 11:38:23 -07:00
JUST.in DO IT
2db82d578c fix(plugin-chart-echarts): invalid total label location for negative values in stacked bar chart (#21032)
(cherry picked from commit a8ba544e60)
2022-09-14 11:38:23 -07:00
Kamil Gabryjelski
346c035690 fix: Explore scrolled down when navigating from dashboard (#20962)
(cherry picked from commit e4fc5564ce)
2022-09-14 11:38:22 -07:00
Antonio Rivero Martinez
6a5b12ec8c Big Number Viz: (#20946)
- When the value is zero we still render the percent change and suffix if present

(cherry picked from commit aa53c10312)
2022-09-14 11:38:22 -07:00
Diego Medina
8c2ca2d8d8 Temporal X Axis values are not properly displayed if the time column has a custom label defined (#20819)
(cherry picked from commit 51869f32ac)
2022-09-14 11:38:21 -07:00
Yongjie Zhao
43b8f18a21 fix: getting default value in run-server.sh (#20736)
(cherry picked from commit 5990ea639e)
2022-09-14 11:38:21 -07:00
Multazim Deshmukh
5efee17def fix: make max-requests and max-requests-jitter adjustable (#20733)
Co-authored-by: Multazim Deshmukh <multazim.deshmukh@morningstar.com>
(cherry picked from commit 883241070f)
2022-09-14 11:38:21 -07:00
Beto Dealmeida
56137ebbe5 fix: logger message (#20714)
(cherry picked from commit c70d102b73)
2022-09-14 11:38:21 -07:00
Michael S. Molina
067495d954 Updates CHANGELOG.md 2022-07-06 13:48:18 -03:00
Reese
bba486b08c fix: Allow dataset owners to explore their datasets (#20382)
* fix: Allow dataset owners to explore their datasets

* Re-order imports

* Give owners security manager permissions to their datasets

* Update test suite

* Add SqlaTable to is_owner types

* Add owners to datasource mock

* Fix VSCode import error

* Fix merge error
2022-07-06 11:43:19 -03:00
Daniel Vaz Gaspar
6e74f3e82c docs: fix link for Apache Superset source code (#20620)
(cherry picked from commit b39a3d8f78)
2022-07-06 11:40:43 -03:00
Daniel Vaz Gaspar
3e97c60c8d chore: bump FAB to 4.1.3 (#20621)
(cherry picked from commit 183871b002)
2022-07-06 11:40:43 -03:00
Rui Zhao
747b011bb7 fix(embedded): Retry when executing alert queries to avoid sending transient errors to users as alert failure notifications (#20419)
Co-authored-by: Rui Zhao <zhaorui@dropbox.com>
(cherry picked from commit 818962cc89)
2022-07-06 11:40:43 -03:00
Evan Rusackas
dc71454416 fix: Respecting max/min opacities, and adding tests. (#20555)
* Respecting max/min opacities, and adding tests.

* revising tests

* Adding missing test case for maximum coverage :)

* removing unnecessary logic and test

* adding another unit test for (hopefully) full coverage.

* no more ternary operator

* New approach with Math.min  - take THAT codecov.

* one more stab at making codecov happy... ignoring the file next.

* lint fixes

(cherry picked from commit ac8e502228)
2022-07-06 11:38:33 -03:00
Evan Rusackas
4dcc805dee Revert "feat(plugin-chart-echarts): Support stacking negative and positive values (#20408)" (#20571)
This reverts commit c959d92dd1.

(cherry picked from commit f5f8ddec3e)
2022-07-06 11:38:33 -03:00
Stephen Liu
3b5513be36 fix(database-modal): forms in database modal will be effected by external form values (#20487)
(cherry picked from commit 932e304ffb)
2022-07-06 11:38:33 -03:00
Stephen Liu
4b2397f0c7 fix(big-number): big number gets cut off on a Dashboard (#20488)
(cherry picked from commit 24a53c38c6)
2022-07-06 11:38:32 -03:00
yourssvk
9a26a211d4 fix: SQL Lab cancel query in Redshift database connection does not wo… (#16326)
* fix: SQL Lab cancel query in Redshift database connection does not work #16325

Co-authored-by: Venkata Krishnan Somasundaram <venkata_cred@Venkatas-MacBook-Pro.local>
Co-authored-by: Elizabeth Thompson <eschutho@gmail.com>
(cherry picked from commit 90d486a643)
2022-07-06 11:38:32 -03:00
Diego Medina
eedcefc64a fix: Unable to download the Dashboard as image in case there's an image added through Markdown (#20362)
* fix: Unable to download the Dashboard as image in case there's an image added through Markdown

* licence

(cherry picked from commit c5d3678a31)
2022-07-06 11:38:32 -03:00
Alex Lauderbaugh
eba63b4add Updated copy in chart drop down to "View as table" (#20486)
(cherry picked from commit 93fbfe9d28)
2022-07-06 11:38:32 -03:00
Michael S. Molina
edbbf886af Updates CHANGELOG.md 2022-06-29 09:27:48 -03:00
Michael S. Molina
80e2f1abe7 fix: Removes psycopg2 as a required dependency (#20543)
* fix: Removes psycopg2 as a required dependency

* Disables lint warning

(cherry picked from commit cb3cd41dcd)
2022-06-29 09:25:30 -03:00
Michael S. Molina
b5a4c06d82 Updates CHANGELOG.md, UPDATING.md and package.json 2022-06-28 15:55:58 -03:00
smileydev
789f99341b fix(db): Show the only db install guide when the db is already installed and error is existed while importing file. (#20442)
* fix(db): make to show the db error msg when db installed and error is exist

* fix(db): make to replace dbinstall str into showDbInstallInstructions

(cherry picked from commit 23e62d3782)
2022-06-28 14:22:22 -03:00
Daniel Vaz Gaspar
63229dcf56 fix: bump FAB to 4.1.2 (#20483) 2022-06-28 14:22:12 -03:00
Multazim Deshmukh
92038db579 fix: correction from mmsql to mssql in setup.py (#20493)
Co-authored-by: Multazim Deshmukh <multazim.deshmukh@morningstar.com>
(cherry picked from commit 5a2abfab65)
2022-06-28 14:21:25 -03:00
Yongjie Zhao
b8d9208b6e feat(standardized form data): keep all columns and metrics (#20377)
(cherry picked from commit bbbe102887)
2022-06-28 14:21:25 -03:00
Elizabeth Thompson
885bbdde95 remove autoflush for queries during dual write (#20460)
(cherry picked from commit 44f0b511dd)
2022-06-28 14:21:25 -03:00
John Bodley
25a6f02cd6 fix: Re-add filter-box time granularity/column (#20485)
Co-authored-by: John Bodley <john.bodley@airbnb.com>
(cherry picked from commit 661ab35bd0)
2022-06-28 14:21:25 -03:00
Stephen Liu
bbca109ea3 fix(docs): prevent some symbols from being copied with (#20480)
(cherry picked from commit aa4068048a)
2022-06-28 14:19:23 -03:00
stevetracvc
bcc23bbacd fix: issue with sorting by multiple columns in a table (#19920)
Recent commit to sort alphanumeric columns via case insensitive
comparison broke the multi-column sort option. React-table only sorts
by the second (or third...) column if the first column matches.
Since the alphanumeric sort only returned -1 or 1, it never would move
to the subsequent columns when the earlier column values matched.

(cherry picked from commit a45d011e74)
2022-06-28 14:19:23 -03:00
John Bodley
23061d6822 fix(migration): Ensure key_value LargeBinary is encoded as a MEDIUMBLOB as opposed to BLOB for MySQL (#20385)
* fix(migration): Ensure key_value LargeBinary is encoded as a MEDIUMBLOB as opposed to BLOB for MySQL

* Update 2022-06-14_15-28_e09b4ae78457_resize_key_value_blob.py

Co-authored-by: John Bodley <john.bodley@airbnb.com>
(cherry picked from commit f5cb23e0a3)
2022-06-28 14:19:23 -03:00
Diego Medina
40a9257311 fix: alert & reports active toggle optimistic update (#20402)
(cherry picked from commit 4dc30441b7)
2022-06-28 14:19:23 -03:00
Michael S. Molina
ca0544a573 fix: Changes the return type of get_permissions to be JSON friendly (#20472)
* fix: Changes the return type of get_permissions to be JSON friendly

* Removes dangling comma

* Removes unused import

* Fixes typing errors

(cherry picked from commit a169b60712)
2022-06-28 14:19:23 -03:00
AAfghahi
d789f376b3 async queries limit bug (#20468)
(cherry picked from commit 2c16be42e1)
2022-06-28 14:19:23 -03:00
smileydev
3d850426ff fix(home): Show home page tabs as pills instead of links (#20257)
* fix(home): make to update the css style of links

* fix(home): make to fix lint issue

* fix(home): make to remove underline when tab is active

* fix(homes): make to fix the issue of tab

* fix(home): make to move styles to a tag

(cherry picked from commit a833674a8d)
2022-06-28 14:19:23 -03:00
Beto Dealmeida
4728d8f49b fix: ensure column name in description is string (#20340)
* fix: ensure column name in description is string

* Add unit test

(cherry picked from commit f3b289d3c3)
2022-06-28 14:19:23 -03:00
Diego Medina
9e6a3e1a4e fix(viz): BigQuery time grain 'minute'/'second' throws an error (#20350)
(cherry picked from commit 5afeba34bd)
2022-06-28 14:19:22 -03:00
smileydev
2d551faaf4 fix(chart & table): make to prevent dates from wrapping (#20384)
(cherry picked from commit 1ae935379f)
2022-06-28 14:19:22 -03:00
Yongjie Zhao
f58ad259ac fix: suppress translation warning in jest (#20404)
(cherry picked from commit 9fad26fa19)
2022-06-28 14:19:22 -03:00
Yongjie Zhao
4e93690e19 fix: should raise exception when apply a categorical axis (#20451)
(cherry picked from commit 8bbbd6f03f)
2022-06-28 14:19:22 -03:00
Diego Medina
d1ac6e5db4 fix: table viz sort icon bottom aligned (#20447)
(cherry picked from commit 93774d1860)
2022-06-28 14:19:22 -03:00
Samira El Aabidi
59fbf2a202 feat(chart): Enable caching per user when user impersonation is enabled (#20114)
* add username to extra cache keys when impersonation is enabled.

* don't put effective_user in extra_cache_key

* get_impersonation_key method in engine_spec class  to construct an impersonation key

* pass datasource when creating query objects

* adding an impersonation key when construction cache key

* add feature flag to control caching per user

* revert changes

* make precommit and pylint happy

* pass a User instance

* remove unnecessary import

(cherry picked from commit 68af5980ea)
2022-06-23 09:12:14 -03:00
Sam Firke
4841e8fb9c style(typo): occured -> occurred (#20116)
* Occured -> Occurred

* Occured -> Occurred

* Occured -> Occurred

* Occured - > Occurred

* Update FallbackComponent.tsx

* Update FallbackComponent.tsx

Co-authored-by: John Bodley <4567245+john-bodley@users.noreply.github.com>
(cherry picked from commit b7eb235440)
2022-06-23 09:12:14 -03:00
John Bodley
06592180ea [fbprophet] Fix frequencies (#20326)
(cherry picked from commit 8b0bee5e8b)
2022-06-23 09:12:14 -03:00
Simon Thelin
cb270034f3 fix(20428): Address-Presto/Trino-Poll-Issue-Refactor (#20434)
* fix(20428)-Address-Presto/Trino-Poll-Issue-Refacto
r

Update linter

* Update to only use BaseEngineSpec handle_cursor

* Fix CI

Co-authored-by: John Bodley <4567245+john-bodley@users.noreply.github.com>
(cherry picked from commit 8b7262fa90)
2022-06-23 09:12:14 -03:00
Diego Medina
7e48de484a fix(dashboard): new created chart did not have high lighted effect when using the permalink of chart share in dashboard (#20411)
(cherry picked from commit c2f01a676c)
2022-06-23 09:12:14 -03:00
Lily Kuang
a08499d88d fix(embedded): CSV download for chart (#20261)
* move postForm to superset client

* lint

* fix lint

* fix type

* update tests

* add tests

* add test for form submit

* add test for request form

* lint

* fix test

* fix tests

* more tests

* more tests

* test

* lint

* more test for postForm

* lint

* Update superset-frontend/packages/superset-ui-core/test/connection/SupersetClientClass.test.ts

Co-authored-by: David Aaron Suddjian <1858430+suddjian@users.noreply.github.com>

* update tests

* remove useless test

* make test cover happy

* make test cover happy

* make test cover happy

* make codecov happy

* make codecov happy

Co-authored-by: David Aaron Suddjian <1858430+suddjian@users.noreply.github.com>
(cherry picked from commit ab9f72f1a1)
2022-06-23 09:12:14 -03:00
jiAng
bd721cd86c fix(cosmetic): cannot find m-r-10 class in superset.less (#20276)
* fix(cosmetic): cannot find m-r-10 class in superset.less

* fix: remove .m-r-10 class and use emotion instead

* Update superset-frontend/src/components/Datasource/CollectionTable.tsx

Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com>

* Update superset-frontend/src/components/Datasource/DatasourceEditor.jsx

Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com>

Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com>
(cherry picked from commit f6f93aad37)
2022-06-23 09:12:14 -03:00
Stephen Liu
67c853790d fix: rm eslint-plugin-translation-vars engine requirement (#20420)
(cherry picked from commit fa7f144a68)
2022-06-23 09:12:14 -03:00
Stephen Liu
3b3c3be9b2 fix(bar-chart-v2): remove marker from bar chart V2 (#20409)
(cherry picked from commit b32288fddf)
2022-06-23 09:12:13 -03:00
mohittt8
2f07a88c32 fix(presto): use correct timespec for presto (#20333)
(cherry picked from commit 41bbf62e58)
2022-06-23 09:12:13 -03:00
Elizabeth Thompson
128722085c fix key error on permalink fetch for old permalinks (#20414)
(cherry picked from commit 12436e47c9)
2022-06-23 09:12:13 -03:00
Smart-Codi
78c577b515 adding extra metrics after chart configuration (#20410)
(cherry picked from commit a8a6b732e9)
2022-06-23 09:12:13 -03:00
chuancy
9aa047cf12 Chinese translation and English translation do not match (#20405)
(cherry picked from commit 11d94ce56c)
2022-06-23 09:12:13 -03:00
Kamil Gabryjelski
ce9807941b feat(plugin-chart-echarts): Support stacking negative and positive values (#20408)
(cherry picked from commit c959d92dd1)
2022-06-16 09:03:57 -03:00
170 changed files with 9993 additions and 20867 deletions

View File

@@ -19,6 +19,544 @@ under the License.
## Change Log
### 2.0.1 (Mon Oct 10 09:38:33 2022 +0530)
**Database Migrations**
**Features**
**Fixes**
- [#21729](https://github.com/apache/superset/pull/21729) fix: allow adhoc columns in non-aggregate query (@mayurnewase)
- [#21576](https://github.com/apache/superset/pull/21576) fix(dashboard): dashboard doesn't load properly if it has tabs (@stephenLYZ)
- [#21549](https://github.com/apache/superset/pull/21549) fix(dashboard): show correct roles for dashboard access dropdown (@mayurnewase)
- [#21773](https://github.com/apache/superset/pull/21773) fix: remove deprecated ETagResponseMixin (@eschutho)
- [#21561](https://github.com/apache/superset/pull/21561) fix(report): Fix permission check for set up email report on charts/dashboards. Fixes #21559 (@zhaorui2022)
- [#20651](https://github.com/apache/superset/pull/20651) fix: annotation broken (@zhaoyongjie)
- [#20830](https://github.com/apache/superset/pull/20830) fix: remove element reference in alerts report fetchs (@hughhhh)
- [#20063](https://github.com/apache/superset/pull/20063) fix: Add locale for DatePicker component (@aehanno)
- [#21302](https://github.com/apache/superset/pull/21302) fix: disallow users from viewing other user's profile on config (@dpgaspar)
- [#21154](https://github.com/apache/superset/pull/21154) fix(explore): Prevent unnecessary series limit subquery (@codyml)
- [#21498](https://github.com/apache/superset/pull/21498) fix: set correct favicon from config for login and FAB list views (@mayurnewase)
- [#21380](https://github.com/apache/superset/pull/21380) fix(sqllab): Fix cursor alignment in SQL lab editor by avoiding Lucida Console font on Windows (@MichaelHintz)
- [#20061](https://github.com/apache/superset/pull/20061) fix: Add french translation missing (@aehanno)
- [#21044](https://github.com/apache/superset/pull/21044) fix(plugin-chart-echarts): missing value format in mixed timeseries (@justinpark)
- [#21419](https://github.com/apache/superset/pull/21419) fix: cached common bootstrap Revert (#21018) (@dpgaspar)
- [#21296](https://github.com/apache/superset/pull/21296) fix(plugin-chart-echarts): show zero value in tooltip (@villebro)
- [#21294](https://github.com/apache/superset/pull/21294) fix(explore): Time column label not formatted when GENERIC_X_AXES enabled (@kgabryje)
- [#21272](https://github.com/apache/superset/pull/21272) fix: adds TLS certificate validation option for SMTP (@dpgaspar)
- [#21076](https://github.com/apache/superset/pull/21076) fix(celery cache warmup): add auth and use warm_up_cache endpoint (@nytai)
- [#21216](https://github.com/apache/superset/pull/21216) fix(database-list): hide upload file button if no permission (@stephenLYZ)
- [#21153](https://github.com/apache/superset/pull/21153) fix(sqllab): missing zero values while copy-to-clipboard (@justinpark)
- [#21084](https://github.com/apache/superset/pull/21084) fix(native filters): groupby filter issue (@stevetracvc)
- [#21005](https://github.com/apache/superset/pull/21005) fix(plugin-chart-handlebars): Sort-By and Sort-By-Descending control not work (@stephenLYZ)
- [#20969](https://github.com/apache/superset/pull/20969) fix(dashboard): Fix scroll behaviour in DashboardBuilderSidepane (@EugeneTorap)
- [#21007](https://github.com/apache/superset/pull/21007) fix(plugin-chart-echarts): gauge chart enhancements and fixes (@stephenLYZ)
- [#21032](https://github.com/apache/superset/pull/21032) fix(plugin-chart-echarts): invalid total label location for negative values in stacked bar chart (@justinpark)
- [#20962](https://github.com/apache/superset/pull/20962) fix: Explore scrolled down when navigating from dashboard (@kgabryje)
- [#20946](https://github.com/apache/superset/pull/20946) fix(viz): Show zero percent changes in Big Number Viz (@Antonio-RiveroMartnez)
- [#20819](https://github.com/apache/superset/pull/20819) fix: Temporal X Axis values are not properly displayed if the time column has a custom label defined (@diegomedina248)
- [#20736](https://github.com/apache/superset/pull/20736) fix: getting default value in run-server.sh (@zhaoyongjie)
- [#20733](https://github.com/apache/superset/pull/20733) fix(docker): Make Gunicorn max_requests and max_requests_jitter adjustable (@mdeshmu)
- [#20714](https://github.com/apache/superset/pull/20714) fix: logger message (@betodealmeida)
**Others**
- [#21721](https://github.com/apache/superset/pull/21721) build: changelog for 2.0.1 (@AAfghahi)
- [#21018](https://github.com/apache/superset/pull/21018) perf: Memoize the common_bootstrap_payload (@bkyryliuk)
- [#21091](https://github.com/apache/superset/pull/21091) chore(deps): unpin holidays dependency version (@ecederstrand)
### 2.0 (Tue Jun 28 08:53:02 2022 -0400)
**Database Migrations**
- [#20385](https://github.com/apache/superset/pull/20385) fix(migration): Ensure key_value LargeBinary is encoded as a MEDIUMBLOB as opposed to BLOB for MySQL (@john-bodley)
- [#20284](https://github.com/apache/superset/pull/20284) chore(migrations): Renaming migration files so that they're easier to keep track of (@craig-rueda)
- [#20108](https://github.com/apache/superset/pull/20108) fix: None dataset and schema permissions (@dpgaspar)
- [#18794](https://github.com/apache/superset/pull/18794) feat(business-types): initial implementation of SIP-78 (@cccs-RyanS)
- [#20073](https://github.com/apache/superset/pull/20073) fix(dataset): handle missing sqla uri in migration (@villebro)
- [#19941](https://github.com/apache/superset/pull/19941) fix(reports): Clear last value when state is WORKING (@john-bodley)
- [#19675](https://github.com/apache/superset/pull/19675) chore(docs): Spelling (@jsoref)
- [#19793](https://github.com/apache/superset/pull/19793) fix(SIP-68): handle empty table name during migration (@ktmud)
- [#19786](https://github.com/apache/superset/pull/19786) fix(migrations): coalesce is_temporal when inserting into sl_columns (@cemremengu)
- [#19421](https://github.com/apache/superset/pull/19421) perf: refactor SIP-68 db migrations with INSERT SELECT FROM (@ktmud)
- [#19767](https://github.com/apache/superset/pull/19767) fix: Fix migration for removing time_range_endpoints 3 (@hughhhh)
- [#19728](https://github.com/apache/superset/pull/19728) fix: Removetime_range_endpoints from query context object pt 2 (@hughhhh)
- [#19630](https://github.com/apache/superset/pull/19630) chore: clean up unused imports in db migration scripts (@ktmud)
- [#19577](https://github.com/apache/superset/pull/19577) fix: merge multiple db heads (@eschutho)
- [#19243](https://github.com/apache/superset/pull/19243) fix: cannot delete a database if team member has SQL editor tab that uses that db (@diegomedina248)
- [#19537](https://github.com/apache/superset/pull/19537) chore: block unsafe functions (@betodealmeida)
- [#19513](https://github.com/apache/superset/pull/19513) chore: postpone timerange endpoint removal (@villebro)
- [#19495](https://github.com/apache/superset/pull/19495) perf: speed up db migration for deprecating time_range_endpoints (@ktmud)
- [#19474](https://github.com/apache/superset/pull/19474) fix: handle null params in #18936 migration (@serenajiang)
- [#19423](https://github.com/apache/superset/pull/19423) fix: Remove`time_range_endpoints` from query context object (@hughhhh)
- [#18936](https://github.com/apache/superset/pull/18936) chore: Remove legacy SIP-15 interim logic/flags (@john-bodley)
**Features**
- [#20377](https://github.com/apache/superset/pull/20377) feat(standardized form data): keep all columns and metrics (@zhaoyongjie)
- [#20114](https://github.com/apache/superset/pull/20114) feat(chart): Enable caching per user when user impersonation is enabled (@Samira-El)
- [#20408](https://github.com/apache/superset/pull/20408) feat(plugin-chart-echarts): Support stacking negative and positive values (@kgabryje)
- [#20278](https://github.com/apache/superset/pull/20278) feat: Prevent dataset edit modal closing on click-away in edit mode (@reesercollins)
- [#20392](https://github.com/apache/superset/pull/20392) feat: setting limit value when Pie chart switches (@zhaoyongjie)
- [#20373](https://github.com/apache/superset/pull/20373) feat: adding truncate metric control on timeseries charts (@zhaoyongjie)
- [#20248](https://github.com/apache/superset/pull/20248) feat(explore): Implement viz switcher redesign (@kgabryje)
- [#20113](https://github.com/apache/superset/pull/20113) feat(api): Added "kind" to dataset/<pk> endpoint (@reesercollins)
- [#20299](https://github.com/apache/superset/pull/20299) feat(explore): Dataset Panel Options when Source = Query II (@lyndsiWilliams)
- [#20320](https://github.com/apache/superset/pull/20320) feat: Databricks native driver (@betodealmeida)
- [#20313](https://github.com/apache/superset/pull/20313) feat(explore): Denormalize form data in echarts, world map and nvd3 bar and line charts (@kgabryje)
- [#20277](https://github.com/apache/superset/pull/20277) feat: multiple results pane on explore and dashboard (@zhaoyongjie)
- [#19898](https://github.com/apache/superset/pull/19898) feat: When editing the label/title in the Metrics popover, hitting Enter should save what you've typed (@diegomedina248)
- [#16493](https://github.com/apache/superset/pull/16493) feat(plugin-chart-echarts): [feature-parity] support extra control for the area chart V2 (@stephenLYZ)
- [#19855](https://github.com/apache/superset/pull/19855) feat(explore): Frontend implementation of dataset creation from infobox (@lyndsiWilliams)
- [#20165](https://github.com/apache/superset/pull/20165) feat: add modfied col and timezone info to schedule col (@pkdotson)
- [#20144](https://github.com/apache/superset/pull/20144) feat: showing results pane in dashboard (@zhaoyongjie)
- [#20242](https://github.com/apache/superset/pull/20242) feat: derived metrics use different line style (@zhaoyongjie)
- [#20010](https://github.com/apache/superset/pull/20010) feat: standardized form_data (@zhaoyongjie)
- [#19987](https://github.com/apache/superset/pull/19987) feat(superset-ui-core): add feature flag for the analogous colors (@stephenLYZ)
- [#19881](https://github.com/apache/superset/pull/19881) feat(world-map): support color by metric or country column (@stephenLYZ)
- [#19981](https://github.com/apache/superset/pull/19981) feat!: pass datasource_type and datasource_id to form_data (@eschutho)
- [#15241](https://github.com/apache/superset/pull/15241) feat: query datasets from SQL Lab (@betodealmeida)
- [#20129](https://github.com/apache/superset/pull/20129) feat(explore): Fill dashboard name when adding new chart from dashboard view (@kgabryje)
- [#20160](https://github.com/apache/superset/pull/20160) feat(explore): Add empty state to annotations (@kgabryje)
- [#20134](https://github.com/apache/superset/pull/20134) feat: add Query.columns for bootstrap_data (@hughhhh)
- [#20158](https://github.com/apache/superset/pull/20158) feat: add statsd metrics for notifications (@dpgaspar)
- [#20052](https://github.com/apache/superset/pull/20052) feat(Helm Chart): Support resource limits and requests for each component (@rathberm)
- [#20170](https://github.com/apache/superset/pull/20170) feat: add samples endpoint (@zhaoyongjie)
- [#19381](https://github.com/apache/superset/pull/19381) feat: add drag and drop column rearrangement for table viz (@stevetracvc)
- [#20136](https://github.com/apache/superset/pull/20136) feat: Add Certified filter to Datasets (@hughhhh)
- [#20111](https://github.com/apache/superset/pull/20111) feat(dashboard): Chart title click redirects to Explore in new tab (@kgabryje)
- [#20097](https://github.com/apache/superset/pull/20097) feat(plugin-chart-echarts): add support for generic axis to mixed chart (@villebro)
- [#20126](https://github.com/apache/superset/pull/20126) feat(dashboard): Add create chart button in dashboard edit mode (@kgabryje)
- [#20059](https://github.com/apache/superset/pull/20059) feat: Save column data into json_metadata for all Query executions (@hughhhh)
- [#19918](https://github.com/apache/superset/pull/19918) feat(plugin-chart-echarts): support horizontal bar chart (@stephenLYZ)
- [#19902](https://github.com/apache/superset/pull/19902) feat: Explore popovers should close on escape (@diegomedina248)
- [#20049](https://github.com/apache/superset/pull/20049) feat(dashboard): Rearrange items in chart header controls dropdown (@kgabryje)
- [#20030](https://github.com/apache/superset/pull/20030) feat(sip-68): Add DatasourceDAO class to manage querying different datasources easier (@hughhhh)
- [#19581](https://github.com/apache/superset/pull/19581) feat(viz-gallery): add search weight for viz-name (@stephenLYZ)
- [#19999](https://github.com/apache/superset/pull/19999) feat: RLS for SQL Lab (@betodealmeida)
- [#19993](https://github.com/apache/superset/pull/19993) feat(explore): Show confirmation modal if user exits Explore without saving changes (@kgabryje)
- [#19873](https://github.com/apache/superset/pull/19873) feat(css): adds `chartId`-based class to dashboard chart holder (@rusackas)
- [#20002](https://github.com/apache/superset/pull/20002) feat: deprecate /superset/testconn and migrate to api v1 (@zephyring)
- [#19935](https://github.com/apache/superset/pull/19935) feat: deprecate /superset/validate_sql_json migrate to api v1 (@dpgaspar)
- [#20015](https://github.com/apache/superset/pull/20015) feat: add new enums for datasource types (@hughhhh)
- [#19956](https://github.com/apache/superset/pull/19956) feat: Applitools Cypress workflow (@geido)
- [#19852](https://github.com/apache/superset/pull/19852) feat: Run Applitools on public Storybook (@geido)
- [#19963](https://github.com/apache/superset/pull/19963) feat: Add cypress test for downloading chart as image (@codemaster08240328)
- [#19957](https://github.com/apache/superset/pull/19957) feat: switch from `sqlalchemy-trino` to `trino-python-client` (@dungdm93)
- [#19921](https://github.com/apache/superset/pull/19921) feat: deprecate /superset/extra_table_metadata migrate to api v1 (@dpgaspar)
- [#19745](https://github.com/apache/superset/pull/19745) feat: simplify SQLite time grain (@betodealmeida)
- [#19927](https://github.com/apache/superset/pull/19927) feat(chart & legend): make to enable show legend by default (@prosdev0107)
- [#19754](https://github.com/apache/superset/pull/19754) feat: deprecate old API on core superset fave_dashboards (@dpgaspar)
- [#19905](https://github.com/apache/superset/pull/19905) feat: simplify `memoized_func` (@betodealmeida)
- [#19871](https://github.com/apache/superset/pull/19871) feat(filter): make to hide sort filter when time range (@prosdev0107)
- [#19851](https://github.com/apache/superset/pull/19851) feat: add Advanced Analytics into mixed time series chart (@zhaoyongjie)
- [#19692](https://github.com/apache/superset/pull/19692) feat: Update ShortKey for stop query running in SqlLab editor (@codemaster08240328)
- [#17903](https://github.com/apache/superset/pull/17903) feat: Adds plugin-chart-handlebars (@jdbranham)
- [#19748](https://github.com/apache/superset/pull/19748) feat(explore): improve UI in the control panel (@stephenLYZ)
- [#19724](https://github.com/apache/superset/pull/19724) feat: 10/15/30 min grain to Pinot (@hughhhh)
- [#19696](https://github.com/apache/superset/pull/19696) feat(explore): Replace overlay with alert banner when chart controls change (@kgabryje)
- [#19751](https://github.com/apache/superset/pull/19751) feat(explore): Implement data panel redesign (@kgabryje)
- [#19598](https://github.com/apache/superset/pull/19598) feat: add empty states to sqlab editor and select (@pkdotson)
- [#19450](https://github.com/apache/superset/pull/19450) feat: Remove legacy sql alchemy db connection link from G Sheet connection (@codemaster08240328)
- [#19710](https://github.com/apache/superset/pull/19710) feat: Enabling source maps full time (@rusackas)
- [#19671](https://github.com/apache/superset/pull/19671) feat: UI override registry (@suddjian)
- [#19691](https://github.com/apache/superset/pull/19691) feat(explore): More explicit labels of adhoc filter operators (@kgabryje)
- [#19558](https://github.com/apache/superset/pull/19558) feat(explore): Redesign of Run/Save buttons (@kgabryje)
- [#19650](https://github.com/apache/superset/pull/19650) feat(embedded): API get embedded dashboard config by uuid (@lilykuang)
- [#19310](https://github.com/apache/superset/pull/19310) feat(CRUD): add new empty state (@stephenLYZ)
- [#19622](https://github.com/apache/superset/pull/19622) feat(plugin-chart-echarts): add aggregate total for the Pie/Donuct chart (@stephenLYZ)
- [#19314](https://github.com/apache/superset/pull/19314) feat: Move Database Import option into DB Connection modal (@lyndsiWilliams)
- [#19434](https://github.com/apache/superset/pull/19434) feat: deprecate old API and create new API for dashes created by me (@dpgaspar)
- [#19482](https://github.com/apache/superset/pull/19482) feat: add success toast to alerts and reports (@pkdotson)
- [#19574](https://github.com/apache/superset/pull/19574) feat: add a `where_in` filter for Jinja2 (@betodealmeida)
- [#19458](https://github.com/apache/superset/pull/19458) feat(explore): Move timer, row counter and cached pills to chart container (@kgabryje)
- [#19529](https://github.com/apache/superset/pull/19529) feat(explore): Move chart header to top of the page (@kgabryje)
- [#19489](https://github.com/apache/superset/pull/19489) feat(CI): clean up Python tests output (@ktmud)
- [#19308](https://github.com/apache/superset/pull/19308) feat(explore): SQL popover in datasource panel (@kgabryje)
- [#19325](https://github.com/apache/superset/pull/19325) feat(color): support analogous colors to prevent color conflict (@stephenLYZ)
- [#19408](https://github.com/apache/superset/pull/19408) feat(dashboard): Implement empty states for empty tabs (@kgabryje)
- [#19446](https://github.com/apache/superset/pull/19446) feat(explore): Move chart actions into dropdown (@kgabryje)
- [#19394](https://github.com/apache/superset/pull/19394) feat(explore): UI changes in dataset panel on Explore page (@kgabryje)
**Fixes**
- [#20382](https://github.com/apache/superset/pull/20382) fix: Allow dataset owners to explore their datasets (@reesercollins)
- [#20419](https://github.com/apache/superset/pull/20419) fix(embedded): Retry when executing alert queries to avoid sending transient errors to users as alert failure notifications (@zhaorui2022)
- [#20555](https://github.com/apache/superset/pull/20555) fix: Respecting max/min opacities, and adding tests. (@rusackas)
- [#20571](https://github.com/apache/superset/pull/20571) fix: Revert #20408 (stacking negative values in echarts bar chart) (@rusackas)
- [#20487](https://github.com/apache/superset/pull/20487) fix(database-modal): form in database model effects results of the database list (@stephenLYZ)
- [#20488](https://github.com/apache/superset/pull/20488) fix(big-number): big number gets cut off on a Dashboard (@stephenLYZ)
- [#16326](https://github.com/apache/superset/pull/16326) fix: SQL Lab cancel query in Redshift database connection does not wo… (@yourssvk)
- [#20362](https://github.com/apache/superset/pull/20362) fix: Unable to download the Dashboard as image in case there's an image added through Markdown (@diegomedina248)
- [#20543](https://github.com/apache/superset/pull/20543) fix: Removes psycopg2 as a required dependency
- [#20442](https://github.com/apache/superset/pull/20442) fix(db): Show the only db install guide when the db is already installed and error is existed while importing file. (@prosdev0107)
- [#20483](https://github.com/apache/superset/pull/20483) fix: bump FAB to 4.1.2 (@dpgaspar)
- [#20493](https://github.com/apache/superset/pull/20493) fix: correction from mmsql to mssql in setup.py (@mdeshmu)
- [#20460](https://github.com/apache/superset/pull/20460) fix: new column UUID conflicts in dual write (@eschutho)
- [#20485](https://github.com/apache/superset/pull/20485) fix: Re-add filter-box time granularity/column (@john-bodley)
- [#20480](https://github.com/apache/superset/pull/20480) fix(docs): prevent some symbols from being copied in docs (@stephenLYZ)
- [#19920](https://github.com/apache/superset/pull/19920) fix(table viz): correctly sort by multiple columns in a table (@stevetracvc)
- [#20402](https://github.com/apache/superset/pull/20402) fix: alert & reports active toggle optimistic update (@diegomedina248)
- [#20472](https://github.com/apache/superset/pull/20472) fix: Changes the return type of get_permissions to be JSON friendly (@michael-s-molina)
- [#20468](https://github.com/apache/superset/pull/20468) fix: async queries limit bug (@AAfghahi)
- [#20257](https://github.com/apache/superset/pull/20257) fix(home): Show home page tabs as pills instead of links (@prosdev0107)
- [#20340](https://github.com/apache/superset/pull/20340) fix: ensure column name in description is string (@betodealmeida)
- [#20350](https://github.com/apache/superset/pull/20350) fix(viz): BigQuery time grain 'minute'/'second' throws an error (@diegomedina248)
- [#20384](https://github.com/apache/superset/pull/20384) fix(chart & table): Prevent the dates from wrapping in table chart (@prosdev0107)
- [#20404](https://github.com/apache/superset/pull/20404) fix: suppress translation warning in jest (@zhaoyongjie)
- [#20451](https://github.com/apache/superset/pull/20451) fix: should raise exception when apply a categorical axis (@zhaoyongjie)
- [#20447](https://github.com/apache/superset/pull/20447) fix: table viz sort icon bottom aligned (@diegomedina248)
- [#20326](https://github.com/apache/superset/pull/20326) fix(fbprophet): Fix weekly frequencies (@john-bodley)
- [#20434](https://github.com/apache/superset/pull/20434) fix(20428): Address-Presto/Trino-Poll-Issue-Refactor (@Thelin90)
- [#20411](https://github.com/apache/superset/pull/20411) fix(dashboard): new created chart did not have high lighted effect when using the permalink of chart share in dashboard (@diegomedina248)
- [#20261](https://github.com/apache/superset/pull/20261) fix(embedded): CSV download for chart (@lilykuang)
- [#20276](https://github.com/apache/superset/pull/20276) fix(cosmetic): cannot find m-r-10 class in superset.less (@Renderz)
- [#20420](https://github.com/apache/superset/pull/20420) fix: rm eslint-plugin-translation-vars engines requirement (@stephenLYZ)
- [#20409](https://github.com/apache/superset/pull/20409) fix(bar-chart-v2): remove marker control from bar chart V2 (@stephenLYZ)
- [#20333](https://github.com/apache/superset/pull/20333) fix(presto): use milliseconds timespec for presto (@mohittt8)
- [#20414](https://github.com/apache/superset/pull/20414) fix: key error on permalink fetch for old permalinks (@eschutho)
- [#20410](https://github.com/apache/superset/pull/20410) fix: Adding extra metrics issue after chart configuration (@codemaster08240328)
- [#20405](https://github.com/apache/superset/pull/20405) fix: Incorrect translations in Chinese in messages.po (@chuancyzhang)
- [#20396](https://github.com/apache/superset/pull/20396) fix(plugin-chart-pivot-table): color weight of Conditional formatting metrics not work (@stephenLYZ)
- [#20361](https://github.com/apache/superset/pull/20361) fix(fonts): Show the all the A's in our workspace correctly, not funky (@prosdev0107)
- [#20383](https://github.com/apache/superset/pull/20383) fix: Unable to export multiple Dashboards with the same name (@diegomedina248)
- [#20363](https://github.com/apache/superset/pull/20363) fix: A newly connected database doesn't appear in the databases list if user connected database using the 'plus' button (@diegomedina248)
- [#20372](https://github.com/apache/superset/pull/20372) fix: update connection modal to use existing catalog (@pkdotson)
- [#20368](https://github.com/apache/superset/pull/20368) fix(VERSIONED_EXPORTS): Ensure dashboards and charts adhere to the VERSIONED_EXPORTS feature flag (@john-bodley)
- [#20351](https://github.com/apache/superset/pull/20351) fix: catch some potential errors on dual write (@eschutho)
- [#20364](https://github.com/apache/superset/pull/20364) fix: query execution time is not fully displayed in bubble icon (@diegomedina248)
- [#20365](https://github.com/apache/superset/pull/20365) fix: Fix typo in Error handling message (@codemaster08240328)
- [#19967](https://github.com/apache/superset/pull/19967) fix: A newly connected database doesn't appear in the databases list if user connected database using the 'plus' button (@diegomedina248)
- [#20348](https://github.com/apache/superset/pull/20348) fix(docker): Make Gunicorn Keepalive Adjustable (@mdeshmu)
- [#19670](https://github.com/apache/superset/pull/19670) fix: Add serviceAccountName to celerybeat pods (@paulinjo)
- [#20315](https://github.com/apache/superset/pull/20315) fix(chart): chart gets cut off on the dashboard (@stephenLYZ)
- [#20324](https://github.com/apache/superset/pull/20324) fix: superset-ui/core coverage (@zhaoyongjie)
- [#20282](https://github.com/apache/superset/pull/20282) fix(explore): Make that see more/see less works correctly with scrolling when error msg is long text. (@prosdev0107)
- [#20296](https://github.com/apache/superset/pull/20296) fix: Alpha are unable to perform a second modification to a Dataset when in Explore (@hughhhh)
- [#20290](https://github.com/apache/superset/pull/20290) fix: Faulty datetime parser regex (@reesercollins)
- [#19761](https://github.com/apache/superset/pull/19761) fix(plugin-chart-echarts): [feature-parity] apply button of annotation layer doesn't work as expected (@stephenLYZ)
- [#20263](https://github.com/apache/superset/pull/20263) fix(embedded): accessing variable response before initialization (@zhaorui2022)
- [#20274](https://github.com/apache/superset/pull/20274) fix(codecov): improve core code coverage (@stephenLYZ)
- [#20187](https://github.com/apache/superset/pull/20187) fix: Database import with cancel_query.. extra field (@codemaster08240328)
- [#20237](https://github.com/apache/superset/pull/20237) fix(cosmetic): Fix Datasource Modal Out Of Box (@Renderz)
- [#20058](https://github.com/apache/superset/pull/20058) fix: Support the Clipboard API in modern browsers (@diegomedina248)
- [#20164](https://github.com/apache/superset/pull/20164) fix(sql lab): View result button is not showing consistently (@diegomedina248)
- [#20171](https://github.com/apache/superset/pull/20171) fix(charts list): do not trigger ListViewError exception for anonymous user (@trepmag)
- [#20178](https://github.com/apache/superset/pull/20178) fix: While exporting CSV , only the entries on first page are getting downloaded even when user is on other pages #17861 (@LahmerIlyas)
- [#20204](https://github.com/apache/superset/pull/20204) fix: Fixes issue where results panel height was incorrect [sc-49045] (@eric-briscoe)
- [#20235](https://github.com/apache/superset/pull/20235) fix: Box Plot Chart throws an error when the average (AVG) / SUM is being calculated on the Metrics (@diegomedina248)
- [#20088](https://github.com/apache/superset/pull/20088) fix: datatype tracking issue on virtual dataset (@codemaster08240328)
- [#20220](https://github.com/apache/superset/pull/20220) fix: dashbaord unable to refresh (@zhaoyongjie)
- [#20228](https://github.com/apache/superset/pull/20228) fix: failed samples should throw exception (@zhaoyongjie)
- [#20203](https://github.com/apache/superset/pull/20203) fix: move columns to datasource object for bootstrap data (@hughhhh)
- [#20151](https://github.com/apache/superset/pull/20151) fix(csv): Ensure df_to_escaped_csv does not coerce integer columns to float (@john-bodley)
- [#20221](https://github.com/apache/superset/pull/20221) fix(legacy-plugin-chart-sunburst): linear color scheme not work when secondary metric is provided (@stephenLYZ)
- [#20223](https://github.com/apache/superset/pull/20223) fix(legacy-plugin-chart-sunburst): chart broken when secondary metric is removed (@stephenLYZ)
- [#20147](https://github.com/apache/superset/pull/20147) fix(cosmetic): Limiting modal height (@rusackas)
- [#20206](https://github.com/apache/superset/pull/20206) fix(sql lab): SQL Lab Compile Query Delay (@diegomedina248)
- [#20201](https://github.com/apache/superset/pull/20201) fix: unable to set destroyOnClose on ModalTrigger (@zhaoyongjie)
- [#20186](https://github.com/apache/superset/pull/20186) fix(db): make to allow to show/hide the password when only creating (@prosdev0107)
- [#20127](https://github.com/apache/superset/pull/20127) fix(database): retrival of tables and views from schema for exasol backend (@Nicoretti)
- [#19899](https://github.com/apache/superset/pull/19899) fix: always create parameter json field (@pkdotson)
- [#20173](https://github.com/apache/superset/pull/20173) fix: avoid while cycle in computeMaxFontSize for big Number run forever when css rule applied (@diegomedina248)
- [#20086](https://github.com/apache/superset/pull/20086) fix(css): transparent linear gradient not working in safari (@stephenLYZ)
- [#19102](https://github.com/apache/superset/pull/19102) fix: string aggregation is incorrect in PivotTableV2 (@diegomedina248)
- [#20011](https://github.com/apache/superset/pull/20011) fix(chart & heatmap): make to fix that y label is rendering out of bounds (@prosdev0107)
- [#20142](https://github.com/apache/superset/pull/20142) fix(explore): handle null control sections (@villebro)
- [#20128](https://github.com/apache/superset/pull/20128) fix: advanced data type API spec and permission name (@dpgaspar)
- [#20107](https://github.com/apache/superset/pull/20107) fix(generic-chart-axes): set x-axis if unset and ff is enabled (@villebro)
- [#20018](https://github.com/apache/superset/pull/20018) fix(modal): add primary button loading state to modals (@kgopal492)
- [#20099](https://github.com/apache/superset/pull/20099) fix: Add cypress test for report page direct link issue (@codemaster08240328)
- [#20068](https://github.com/apache/superset/pull/20068) fix: dbmodal test connection error timeout (@pkdotson)
- [#20092](https://github.com/apache/superset/pull/20092) fix: Revert "feat(explore): Show confirmation modal if user exits Explore without saving changes (#19993) (@kgabryje)
- [#19939](https://github.com/apache/superset/pull/19939) fix(chart & alert): make to show metrics properly (@prosdev0107)
- [#20085](https://github.com/apache/superset/pull/20085) fix: typo in `importexport/api.py` OpenAPI (@betodealmeida)
- [#20051](https://github.com/apache/superset/pull/20051) fix(CRUD): make to fix the dancing when crud view is on hover (@prosdev0107)
- [#20064](https://github.com/apache/superset/pull/20064) fix(chart & gallery): make to add mixed time-series into recommended charts (@prosdev0107)
- [#20013](https://github.com/apache/superset/pull/20013) fix: The dynamic form to connect to Snowflake DB is not returning any errors (@diegomedina248)
- [#20029](https://github.com/apache/superset/pull/20029) fix(plugin-chart-echarts): tooltip of big number truncated at then bottom (@stephenLYZ)
- [#19914](https://github.com/apache/superset/pull/19914) fix: Refactor SQL engine username logic (@john-bodley)
- [#20050](https://github.com/apache/superset/pull/20050) fix: Fixes Tabs style (@michael-s-molina)
- [#20048](https://github.com/apache/superset/pull/20048) fix(homepage): make to show indicator when tab is chosen (@prosdev0107)
- [#20026](https://github.com/apache/superset/pull/20026) fix(chart & filters): make to padding between textarea and buttons (@prosdev0107)
- [#20019](https://github.com/apache/superset/pull/20019) fix(embedded): third party cookies (@lilykuang)
- [#20033](https://github.com/apache/superset/pull/20033) fix: Direct Linking issue on report list: 404 status code. (@codemaster08240328)
- [#19977](https://github.com/apache/superset/pull/19977) fix(word-cloud): fix randomness of each word's rotation (@ebaratte)
- [#20021](https://github.com/apache/superset/pull/20021) fix: native filter truncation rerendering loop on hover (@diegomedina248)
- [#20004](https://github.com/apache/superset/pull/20004) fix: URI form is blank when trying to connect from sql lab (@diegomedina248)
- [#19841](https://github.com/apache/superset/pull/19841) fix: Table chart column config issue (@codemaster08240328)
- [#19877](https://github.com/apache/superset/pull/19877) fix: Making chart update more truthful (@Gwitchr)
- [#19996](https://github.com/apache/superset/pull/19996) fix: Use pull_request_target in Cypress Applitools workflow (@geido)
- [#19972](https://github.com/apache/superset/pull/19972) fix: revert chore(deps): bump d3-svg-legend in /superset-frontend (#19846) (@villebro)
- [#19889](https://github.com/apache/superset/pull/19889) fix: Fix auto-reversion of label/title in the Metrics popover (@diegomedina248)
- [#19903](https://github.com/apache/superset/pull/19903) fix(explore): Explore data table tooltip (@Gwitchr)
- [#19938](https://github.com/apache/superset/pull/19938) fix(chart & table): make to allow highlight in case of numeric column (@prosdev0107)
- [#19839](https://github.com/apache/superset/pull/19839) fix(dashboard): allow users to resize the markdown widget easier (@cccs-Dustin)
- [#19887](https://github.com/apache/superset/pull/19887) fix(hive): Workaround for Python 3.9 s3 transfer issue (@john-bodley)
- [#19936](https://github.com/apache/superset/pull/19936) fix: OpenAPI docs small fixes (@dpgaspar)
- [#19932](https://github.com/apache/superset/pull/19932) fix: can not correctly set force in store (@zhaoyongjie)
- [#19930](https://github.com/apache/superset/pull/19930) fix: memoize primitives (@betodealmeida)
- [#19926](https://github.com/apache/superset/pull/19926) fix(dataset): DAO update (@betodealmeida)
- [#19826](https://github.com/apache/superset/pull/19826) fix: Missing `f` prefix on f-strings (@code-review-doctor)
- [#18988](https://github.com/apache/superset/pull/18988) fix(column-header-tooltip): make that hide the tooltip when the cloum… (@prosdev0107)
- [#19782](https://github.com/apache/superset/pull/19782) fix: chart import error with virtual dataset (@codemaster08240328)
- [#19485](https://github.com/apache/superset/pull/19485) fix: Set fixed maxWidth of the cron schedule modal (@codemaster08240328)
- [#19885](https://github.com/apache/superset/pull/19885) fix: Chart download as image issue (@codemaster08240328)
- [#19883](https://github.com/apache/superset/pull/19883) fix(allow-db-explore): make to check the allow virtual table explore option by default (@prosdev0107)
- [#19835](https://github.com/apache/superset/pull/19835) fix(helm): fix postgresql values (@benjamin-texier)
- [#19758](https://github.com/apache/superset/pull/19758) fix(plugin-chart-echarts): [feature parity] annotation line chart color does not work (@stephenLYZ)
- [#19879](https://github.com/apache/superset/pull/19879) fix(plugin-chart-handlebars): fix overflow, debounce and control reset (@villebro)
- [#19668](https://github.com/apache/superset/pull/19668) fix: Dates alignment in Table viz (@geido)
- [#19876](https://github.com/apache/superset/pull/19876) fix: Cannot re-order metrics by drag and drop (@diegomedina248)
- [#19840](https://github.com/apache/superset/pull/19840) fix(dashboard-css): make to load saved css template (@prosdev0107)
- [#19859](https://github.com/apache/superset/pull/19859) fix: Dashboard report creation error handling (@etr2460)
- [#19857](https://github.com/apache/superset/pull/19857) fix: Update eslint error message to reflect location of antd components (@etr2460)
- [#19605](https://github.com/apache/superset/pull/19605) fix: Query execution time is displayed as invalid date (@diegomedina248)
- [#19694](https://github.com/apache/superset/pull/19694) fix(db & connection): make to show/hide the password when only creating db connection (@prosdev0107)
- [#19778](https://github.com/apache/superset/pull/19778) fix: deck.gl GeoJsonLayer Autozoom & fill/stroke options (@diegomedina248)
- [#19850](https://github.com/apache/superset/pull/19850) fix: Regression on Data and Alerts & Reports Headers (@diegomedina248)
- [#19842](https://github.com/apache/superset/pull/19842) fix: count(distinct column_name) in metrics (@zhaoyongjie)
- [#19843](https://github.com/apache/superset/pull/19843) fix(explore): ignore temporary controls in altered pill (@villebro)
- [#19800](https://github.com/apache/superset/pull/19800) fix: Cypress tests reliability improvements (@diegomedina248)
- [#19575](https://github.com/apache/superset/pull/19575) fix: Show full long number in text email report for table chart. (@codemaster08240328)
- [#19429](https://github.com/apache/superset/pull/19429) fix(dashboard): make to filter the correct certified or non-certified… (@prosdev0107)
- [#13082](https://github.com/apache/superset/pull/13082) fix(sql_lab): Add custom timestamp type for literal casting for presto timestamps (@kekwan)
- [#19797](https://github.com/apache/superset/pull/19797) fix: add missing init files (@suddjian)
- [#19672](https://github.com/apache/superset/pull/19672) fix: trap SQLAlchemy common exceptions & throw 422 error instead (@diegomedina248)
- [#19288](https://github.com/apache/superset/pull/19288) fix: AlertReportCronScheduler tests (@diegomedina248)
- [#19781](https://github.com/apache/superset/pull/19781) fix(world-map): remove categorical color control (@serenajiang)
- [#19792](https://github.com/apache/superset/pull/19792) fix(plugin-chart-table): Resetting controls when switching query mode (@kgabryje)
- [#19755](https://github.com/apache/superset/pull/19755) fix: small cleanup for created by me dashboards API (@dpgaspar)
- [#19784](https://github.com/apache/superset/pull/19784) fix(readme): Remove broken link to legacy gallery (@drluckyspin)
- [#19722](https://github.com/apache/superset/pull/19722) fix: dashboard top level tabs edit (@diegomedina248)
- [#19777](https://github.com/apache/superset/pull/19777) fix(explore): Double divider if no permissions for adding reports (@kgabryje)
- [#19673](https://github.com/apache/superset/pull/19673) fix(import): Add the error alert on failed database import (@prosdev0107)
- [#19518](https://github.com/apache/superset/pull/19518) fix: alert/report created by filter inconsistency with table display (@diegomedina248)
- [#19700](https://github.com/apache/superset/pull/19700) fix: remove expose (@AAfghahi)
- [#19626](https://github.com/apache/superset/pull/19626) fix: deactivate embedding on a dashboard (@suddjian)
- [#19472](https://github.com/apache/superset/pull/19472) fix: Dashboard Edit View Tab Headers Hidden when Dashboard Name is Long (@diegomedina248)
- [#19311](https://github.com/apache/superset/pull/19311) fix(sql lab): add quotes when autocompleting table names with spaces in the editor (@diegomedina248)
- [#19290](https://github.com/apache/superset/pull/19290) fix(sql lab): select edit on query from history doesn't upload editor properly (@diegomedina248)
- [#19420](https://github.com/apache/superset/pull/19420) fix: sql lab ctrl t behaved differently from clicking (@Gwitchr)
- [#19357](https://github.com/apache/superset/pull/19357) fix: Redirect to full url on 401 (@geido)
- [#19001](https://github.com/apache/superset/pull/19001) fix: Line Chart Annotation Info Update (@codemaster08240328)
- [#19714](https://github.com/apache/superset/pull/19714) fix: create virtual table with exotic type (@villebro)
- [#19708](https://github.com/apache/superset/pull/19708) fix(nav): infinite redirect and upload dataset nav permissions (@ktmud)
- [#19430](https://github.com/apache/superset/pull/19430) fix(data-upload): make to change err message (@prosdev0107)
- [#19419](https://github.com/apache/superset/pull/19419) fix(alert & report): make to fix the issue when recreate report (@prosdev0107)
- [#19371](https://github.com/apache/superset/pull/19371) fix: Reset sorting bar issue in Barchart (@codemaster08240328)
- [#19362](https://github.com/apache/superset/pull/19362) fix(sql lab): display the 'View Results' button consistently in the history tab on sync mode (@diegomedina248)
- [#19294](https://github.com/apache/superset/pull/19294) fix: improve alerts & reports modal on small devices (@diegomedina248)
- [#19257](https://github.com/apache/superset/pull/19257) fix(sql lab): table selector should display all the selected tables (@diegomedina248)
- [#19686](https://github.com/apache/superset/pull/19686) fix(plugin-chart-echarts): xAxis scale is not correct when time grain is quarter (@stephenLYZ)
- [#19646](https://github.com/apache/superset/pull/19646) fix(explore): Change copy of cross filters checkbox (@kgabryje)
- [#19586](https://github.com/apache/superset/pull/19586) fix: Navbar styles and Welcome page text (@geido)
- [#19662](https://github.com/apache/superset/pull/19662) fix(database-api): allow search for all columns (@villebro)
- [#19656](https://github.com/apache/superset/pull/19656) fix: allow_browser_login in import/export API (@betodealmeida)
- [#19628](https://github.com/apache/superset/pull/19628) fix: Table Autosizing Has Unnecessary Horizontal Scroll Bars (@diegomedina248)
- [#19573](https://github.com/apache/superset/pull/19573) fix(chart & polygon): make to fix the issue the polygon chart (@prosdev0107)
- [#19051](https://github.com/apache/superset/pull/19051) fix: update Permissions for right nav (@AAfghahi)
- [#19625](https://github.com/apache/superset/pull/19625) fix(test): make test_clean_requests_after_schema_grant more idempotent (@ktmud)
- [#19571](https://github.com/apache/superset/pull/19571) fix: Catch literal colors when theme top level (@geido)
- [#19594](https://github.com/apache/superset/pull/19594) fix: spelling of following (@lzm0)
- [#19569](https://github.com/apache/superset/pull/19569) fix: check type of url before performing string actions (@eschutho)
- [#19570](https://github.com/apache/superset/pull/19570) fix: sqloxide optional (@betodealmeida)
- [#19397](https://github.com/apache/superset/pull/19397) fix: weight tooltip issue (@codemaster08240328)
- [#19313](https://github.com/apache/superset/pull/19313) fix(sql lab): increase the size of the action icons in the history tab (@diegomedina248)
- [#19039](https://github.com/apache/superset/pull/19039) fix(explore): clean data when hidding control (@stephenLYZ)
- [#19444](https://github.com/apache/superset/pull/19444) fix: Error Message is cut off in alerts & reports log page (@codemaster08240328)
- [#19312](https://github.com/apache/superset/pull/19312) fix: adaptive formatting typo in explore dropdowns (@diegomedina248)
- [#19534](https://github.com/apache/superset/pull/19534) fix(explore): Chart header icon paddings (@kgabryje)
- [#19399](https://github.com/apache/superset/pull/19399) fix: native filter dropdown not attached to parent node (@diegomedina248)
- [#19112](https://github.com/apache/superset/pull/19112) fix: Dashboard import holding issue (@codemaster08240328)
- [#19342](https://github.com/apache/superset/pull/19342) fix: Clean up custom css when dashboard unmounted (@codemaster08240328)
- [#19491](https://github.com/apache/superset/pull/19491) fix: Dynamic form to connect to Snowflake DB is not displaying authentication errors (@diegomedina248)
- [#19528](https://github.com/apache/superset/pull/19528) fix: Correct Ukraine map (@wacken89)
- [#19522](https://github.com/apache/superset/pull/19522) fix: add back view for report reload error (@pkdotson)
- [#19519](https://github.com/apache/superset/pull/19519) fix: GSheets rendering from global nav (@hughhhh)
- [#19358](https://github.com/apache/superset/pull/19358) fix(sqllab): make to hide the delete button of most recent query history (@prosdev0107)
- [#19307](https://github.com/apache/superset/pull/19307) fix: Logo resizing on page load (@geido)
- [#19166](https://github.com/apache/superset/pull/19166) fix: time filter should be [start, end) (@zhaoyongjie)
**Others**
- [#20620](https://github.com/apache/superset/pull/20620) docs: fix link for Apache Superset source code (@dpgaspar)
- [#20621](https://github.com/apache/superset/pull/20621) chore: bump FAB to 4.1.3 (@dpgaspar)
- [#20486](https://github.com/apache/superset/pull/20486) chore: Updated copy in chart drop down to "View as table" (@lauderbaugh)
- [#20116](https://github.com/apache/superset/pull/20116) style(typo): occured -> occurred (@sfirke)
- [#20401](https://github.com/apache/superset/pull/20401) chore: add action to welcome new users (@eschutho)
- [#20269](https://github.com/apache/superset/pull/20269) chore(docs): Remove cache warming documentation (@ajwhite)
- [#20194](https://github.com/apache/superset/pull/20194) chore: Removes unused vars (@michael-s-molina)
- [#20321](https://github.com/apache/superset/pull/20321) chore: add breaking change information about form_data datasource_type (@eschutho)
- [#20298](https://github.com/apache/superset/pull/20298) chore: Removes no-use-before-define warnings (@michael-s-molina)
- [#20337](https://github.com/apache/superset/pull/20337) chore(dashboard): update Edit Dashboard side panel tabs (@codyml)
- [#20318](https://github.com/apache/superset/pull/20318) chore: Updates the final steps of the release README (@michael-s-molina)
- [#20307](https://github.com/apache/superset/pull/20307) docs: Updates CHANGELOG.md with 1.5.1 fixes (@michael-s-molina)
- [#20308](https://github.com/apache/superset/pull/20308) docs(jinja): Detail how to use Jinja parameters (@EBoisseauSierra)
- [#20304](https://github.com/apache/superset/pull/20304) chore: superset-ui/core code coverage (@zhaoyongjie)
- [#20297](https://github.com/apache/superset/pull/20297) chore(deps): pinning pyjwt to 2.4.0 (@sadpandajoe)
- [#20287](https://github.com/apache/superset/pull/20287) chore(deps): bump numpy 1.22.1 and PyJWT to 2.4.0 (@sadpandajoe)
- [#20272](https://github.com/apache/superset/pull/20272) chore: remove unused codes for samples (@zhaoyongjie)
- [#20289](https://github.com/apache/superset/pull/20289) chore: Adjusts release emails (@michael-s-molina)
- [#20180](https://github.com/apache/superset/pull/20180) docs: facelift the docs (@mistercrunch)
- [#20249](https://github.com/apache/superset/pull/20249) chore: add event logger to reports/alerts CRUD (@AAfghahi)
- [#20273](https://github.com/apache/superset/pull/20273) chore: adjust the psycopg2 version of k8s installation guide (@ensky)
- [#20152](https://github.com/apache/superset/pull/20152) refactor(trino): Handful of updates for the Trino engine (@john-bodley)
- [#20252](https://github.com/apache/superset/pull/20252) chore: use exc_info to pass errors to log warnings (@eschutho)
- [#20154](https://github.com/apache/superset/pull/20154) chore(requirements): Cleanup of Python requirements (@john-bodley)
- [#20226](https://github.com/apache/superset/pull/20226) refactor: decouple DataTableControl (@zhaoyongjie)
- [#20243](https://github.com/apache/superset/pull/20243) docs: Add beans to users list (@kakoni)
- [#20231](https://github.com/apache/superset/pull/20231) docs: Updates release scripts and docs (@michael-s-molina)
- [#20196](https://github.com/apache/superset/pull/20196) chore: bumping min version of shillelagh (@AAfghahi)
- [#20192](https://github.com/apache/superset/pull/20192) chore: Moves date utils to utils folder (@michael-s-molina)
- [#20210](https://github.com/apache/superset/pull/20210) docs: update release instructions (@villebro)
- [#20205](https://github.com/apache/superset/pull/20205) chore(deps): bump swagger-ui-react from 4.1.2 to 4.1.3 in /docs (@dependabot[bot])
- [#20195](https://github.com/apache/superset/pull/20195) docs: correct case of ClickHouse (@DanRoscigno)
- [#20109](https://github.com/apache/superset/pull/20109) refactor: decouple DataTablesPane (@zhaoyongjie)
- [#20193](https://github.com/apache/superset/pull/20193) refactor: Removes embedded/index.tsx warnings (@michael-s-molina)
- [#20185](https://github.com/apache/superset/pull/20185) docs(security): a typo: Gamma should be in quotes (@jimmytheneutrino)
- [#20146](https://github.com/apache/superset/pull/20146) chore: Implement global header in Dashboard (@geido)
- [#20174](https://github.com/apache/superset/pull/20174) chore: Disable flaky assert in reports cypress test (@kgabryje)
- [#20163](https://github.com/apache/superset/pull/20163) chore: change button name in Sql Lab (@AAfghahi)
- [#20157](https://github.com/apache/superset/pull/20157) chore: filter undefined operators (@zhaoyongjie)
- [#20140](https://github.com/apache/superset/pull/20140) chore(data-table): make formatted dttm the default (@villebro)
- [#20104](https://github.com/apache/superset/pull/20104) chore: fix INTHEWILD sort order and indentation (@villebro)
- [#20093](https://github.com/apache/superset/pull/20093) chore: Add the tnum font property to Table components (@geido)
- [#20103](https://github.com/apache/superset/pull/20103) docs: Update INTHEWILD.md (@fccoelho)
- [#20102](https://github.com/apache/superset/pull/20102) chore: Update aiohttp to 3.8.1 (@diegomedina248)
- [#20066](https://github.com/apache/superset/pull/20066) chore: Set limit for a query in execute_sql_statement (@AAfghahi)
- [#20032](https://github.com/apache/superset/pull/20032) chore: Change copy to Edit chart in Dashboard dropdown (@geido)
- [#20071](https://github.com/apache/superset/pull/20071) chore: Fix and enhance Applitools workflows (@geido)
- [#19966](https://github.com/apache/superset/pull/19966) test: make tabbed dashboard a little more complex (@ktmud)
- [#19976](https://github.com/apache/superset/pull/19976) perf(plugin-chart-table): Add memoization to avoid rerenders (@kgabryje)
- [#20044](https://github.com/apache/superset/pull/20044) chore: Create a generic header component for Explore and Dashboard (@kgabryje)
- [#20046](https://github.com/apache/superset/pull/20046) docs: add changelog and updating entries for 1.5.0 (@villebro)
- [#19962](https://github.com/apache/superset/pull/19962) chore: add doc link for db migration conflict warning (@ktmud)
- [#20034](https://github.com/apache/superset/pull/20034) chore: Changes the no-literal-colors lint rule to throw errors instead of warnings (@michael-s-molina)
- [#20031](https://github.com/apache/superset/pull/20031) chore: Run Applitools + Cypress nightly (@geido)
- [#20006](https://github.com/apache/superset/pull/20006) chore: Removes hard-coded colors from the plugins - iteration 2 (@michael-s-molina)
- [#19130](https://github.com/apache/superset/pull/19130) refactor: Refactor reports for Charts and Dashboards (@AAfghahi)
- [#20016](https://github.com/apache/superset/pull/20016) chore: Removes hard-coded colors - iteration 3 (@michael-s-molina)
- [#19870](https://github.com/apache/superset/pull/19870) docs: Detail front-end development instructions (@EBoisseauSierra)
- [#19971](https://github.com/apache/superset/pull/19971) docs: Add config for running on a WSGI HTTP server (@thinhnd2104)
- [#20008](https://github.com/apache/superset/pull/20008) chore: Upgrades Storybook from 6.4.19 to 6.4.22 (@michael-s-molina)
- [#20009](https://github.com/apache/superset/pull/20009) docs: typo in chart-params markdown file (@JakobMiksch)
- [#19923](https://github.com/apache/superset/pull/19923) chore: Removes hard-coded colors from the plugins - iteration 1 (@michael-s-molina)
- [#19954](https://github.com/apache/superset/pull/19954) chore: convert URLShortLinkButton to typescript (@ktmud)
- [#19929](https://github.com/apache/superset/pull/19929) chore: change subject name from no_name to named for PNGs in (@AAfghahi)
- [#19942](https://github.com/apache/superset/pull/19942) refactor(ReportModal): simplify state reducer and improve error handling (@ktmud)
- [#19770](https://github.com/apache/superset/pull/19770) chore: remove druid datasource from the config (@eschutho)
- [#19911](https://github.com/apache/superset/pull/19911) chore: Fix broken link for DouroECI (@mavimo)
- [#19951](https://github.com/apache/superset/pull/19951) chore: Adds the theme object to chart properties (@michael-s-molina)
- [#19813](https://github.com/apache/superset/pull/19813) chore: get embedded user with roles and permissions (@suddjian)
- [#19897](https://github.com/apache/superset/pull/19897) chore: Adds a storybook to FilterableTable (@michael-s-molina)
- [#19924](https://github.com/apache/superset/pull/19924) chore(reports): Improving logging around failed scheduled reports (@craig-rueda)
- [#19906](https://github.com/apache/superset/pull/19906) revert: "fix(sql lab): display the 'View Results' button consistently in the history tab on sync mode" (@Gwitchr)
- [#19916](https://github.com/apache/superset/pull/19916) chore(deps): bump react-virtualized-auto-sizer from 1.0.2 to 1.0.6 in /superset-frontend (@dependabot[bot])
- [#19888](https://github.com/apache/superset/pull/19888) chore(deps): bump cross-fetch from 3.1.4 to 3.1.5 in /docs (@dependabot[bot])
- [#19894](https://github.com/apache/superset/pull/19894) chore(deps-dev): bump eslint-plugin-prettier from 3.3.1 to 4.0.0 in /superset-frontend (@dependabot[bot])
- [#19602](https://github.com/apache/superset/pull/19602) docs: Added gtag to docusaurus (@AAfghahi)
- [#19878](https://github.com/apache/superset/pull/19878) chore(deps-dev): bump @storybook/client-api from 6.4.19 to 6.4.22 in /superset-frontend (@dependabot[bot])
- [#19821](https://github.com/apache/superset/pull/19821) test(native filter): refactor and add new test (@jinghua-qa)
- [#19613](https://github.com/apache/superset/pull/19613) chore: Update line-height in SliceHeaderControl (@geido)
- [#19616](https://github.com/apache/superset/pull/19616) chore: Update font-sizes in DatabaseModal (@geido)
- [#19866](https://github.com/apache/superset/pull/19866) chore: fix explore pills (@villebro)
- [#19872](https://github.com/apache/superset/pull/19872) chore: Update aiohttp>=3.7.4 in requirements (@hughhhh)
- [#19874](https://github.com/apache/superset/pull/19874) chore: bump rockset>=0.8.10, <0.9 (@hughhhh)
- [#19864](https://github.com/apache/superset/pull/19864) chore(deps): bump react-syntax-highlighter from 15.4.5 to 15.5.0 in /superset-frontend (@dependabot[bot])
- [#19828](https://github.com/apache/superset/pull/19828) chore: add custom eslint plugin to prevent translation variables (@stephenLYZ)
- [#19845](https://github.com/apache/superset/pull/19845) chore(deps): bump react-split from 2.0.9 to 2.0.14 in /superset-frontend (@dependabot[bot])
- [#19846](https://github.com/apache/superset/pull/19846) chore(deps): bump d3-svg-legend from 1.13.0 to 2.25.6 in /superset-frontend (@dependabot[bot])
- [#19847](https://github.com/apache/superset/pull/19847) chore(deps-dev): bump eslint-plugin-jsx-a11y from 6.4.1 to 6.5.1 in /superset-frontend (@dependabot[bot])
- [#19853](https://github.com/apache/superset/pull/19853) chore(frontend-tests): Spelling (@jsoref)
- [#19823](https://github.com/apache/superset/pull/19823) docs: updated links for country map scripts (@ktmud)
- [#19829](https://github.com/apache/superset/pull/19829) chore(deps-dev): bump babel-loader from 8.2.4 to 8.2.5 in /superset-frontend (@dependabot[bot])
- [#19830](https://github.com/apache/superset/pull/19830) chore(deps): bump react-hot-loader from 4.12.20 to 4.13.0 in /superset-frontend (@dependabot[bot])
- [#19403](https://github.com/apache/superset/pull/19403) chore(deps-dev): bump babel-loader from 8.2.2 to 8.2.4 in /superset-frontend (@dependabot[bot])
- [#19637](https://github.com/apache/superset/pull/19637) chore(deps): bump moment from 2.29.1 to 2.29.2 in /superset-frontend (@dependabot[bot])
- [#19681](https://github.com/apache/superset/pull/19681) chore(deps): bump async from 3.2.0 to 3.2.3 in /superset-frontend/cypress-base (@dependabot[bot])
- [#19680](https://github.com/apache/superset/pull/19680) chore(deps): bump async from 3.2.0 to 3.2.3 in /superset-websocket (@dependabot[bot])
- [#19020](https://github.com/apache/superset/pull/19020) chore(deps): bump url-parse from 1.5.7 to 1.5.10 in /superset-frontend (@dependabot[bot])
- [#17978](https://github.com/apache/superset/pull/17978) chore(deps): bump @types/d3-time from 1.1.1 to 3.0.0 in /superset-frontend (@dependabot[bot])
- [#19727](https://github.com/apache/superset/pull/19727) chore(deps): bump async from 2.6.3 to 2.6.4 in /docs (@dependabot[bot])
- [#19551](https://github.com/apache/superset/pull/19551) chore(deps): bump minimist from 1.2.5 to 1.2.6 in /superset-websocket (@dependabot[bot])
- [#19165](https://github.com/apache/superset/pull/19165) chore: simplify error messaging in database modal (@pkdotson)
- [#19790](https://github.com/apache/superset/pull/19790) chore: bump postgres from 10 to 14 (@dpgaspar)
- [#19480](https://github.com/apache/superset/pull/19480) chore: Update UPDATING.md (@john-bodley)
- [#19740](https://github.com/apache/superset/pull/19740) chore: fix grammar error (@zhaoyongjie)
- [#19703](https://github.com/apache/superset/pull/19703) chore(build): upgrade less-loader (@ktmud)
- [#19736](https://github.com/apache/superset/pull/19736) chore: Updates the Select code owners (@michael-s-molina)
- [#19715](https://github.com/apache/superset/pull/19715) docs(install): ubuntu default-libmysqlclient-dev (@cemremengu)
- [#19726](https://github.com/apache/superset/pull/19726) chore: bumping shillelagh (@AAfghahi)
- [#19699](https://github.com/apache/superset/pull/19699) chore: fix typo (@betodealmeida)
- [#19674](https://github.com/apache/superset/pull/19674) chore: upgrade Pillow (@betodealmeida)
- [#19647](https://github.com/apache/superset/pull/19647) chore(explore): Change labels "Group by"/"Series" to "Dimensions" (@kgabryje)
- [#19679](https://github.com/apache/superset/pull/19679) chore(deps): bump urijs from 1.19.8 to 1.19.11 in /superset-frontend (@dependabot[bot])
- [#19638](https://github.com/apache/superset/pull/19638) chore(deps): bump moment from 2.29.1 to 2.29.2 in /docs (@dependabot[bot])
- [#19617](https://github.com/apache/superset/pull/19617) chore: updated two github issue templates (@srinify)
- [#19666](https://github.com/apache/superset/pull/19666) chore: Remove TwoTone icons (@geido)
- [#19614](https://github.com/apache/superset/pull/19614) chore: Remove wrong usage of font-size in ExploreViewContainer (@geido)
- [#19593](https://github.com/apache/superset/pull/19593) chore: Update font-sizes in ReportModal (@geido)
- [#19611](https://github.com/apache/superset/pull/19611) chore: Update font-sizes in ImportModal (@geido)
- [#19615](https://github.com/apache/superset/pull/19615) chore: Update font-sizes in AlertReportModal (@geido)
- [#19620](https://github.com/apache/superset/pull/19620) chore: Update font-sizes in QueryPreviewModal (@geido)
- [#19641](https://github.com/apache/superset/pull/19641) chore: clean up dynamic translation strings (@villebro)
- [#19635](https://github.com/apache/superset/pull/19635) refactor: consistent migration tests organization (@ktmud)
- [#19634](https://github.com/apache/superset/pull/19634) test: freeze time for dashboard export test (@ktmud)
- [#19606](https://github.com/apache/superset/pull/19606) test(jinja): refactor to functional tests (@villebro)
- [#19587](https://github.com/apache/superset/pull/19587) chore: cleanup as unknown conversion (@zhaoyongjie)
- [#19562](https://github.com/apache/superset/pull/19562) refactor: Removes the CSS files from the Horizon plugin (@michael-s-molina)
- [#19563](https://github.com/apache/superset/pull/19563) refactor: Removes the CSS files from the Paired T-Test plugin (@michael-s-molina)
- [#19539](https://github.com/apache/superset/pull/19539) refactor: Removes the CSS files from the Parallel Coordinates plugin (@michael-s-molina)
- [#19521](https://github.com/apache/superset/pull/19521) refactor: Removes the CSS files from the Partition plugin (@michael-s-molina)
- [#19493](https://github.com/apache/superset/pull/19493) chore: Removes hard-coded colors from legacy-plugin-chart-sankey (@michael-s-molina)
- [#19462](https://github.com/apache/superset/pull/19462) chore: Remove FilterBox.less (@geido)
- [#19438](https://github.com/apache/superset/pull/19438) chore: Remove crud.less from Datasource (@geido)
- [#19517](https://github.com/apache/superset/pull/19517) chore: Enhance ReactChord style with theme vars (@geido)
- [#19463](https://github.com/apache/superset/pull/19463) chore: Remove TimeTable.less (@geido)
- [#19550](https://github.com/apache/superset/pull/19550) chore(deps): bump minimist from 1.2.5 to 1.2.6 in /superset-embedded-sdk (@dependabot[bot])
- [#19566](https://github.com/apache/superset/pull/19566) chore(deps): bump node-forge from 1.2.1 to 1.3.1 in /docs (@dependabot[bot])
- [#19552](https://github.com/apache/superset/pull/19552) chore(deps): bump minimist from 1.2.5 to 1.2.6 in /docs (@dependabot[bot])
- [#19549](https://github.com/apache/superset/pull/19549) chore(deps): bump minimist from 1.2.5 to 1.2.6 in /superset-frontend/cypress-base (@dependabot[bot])
- [#19559](https://github.com/apache/superset/pull/19559) docs: update the typo in the documentation (@fatosmorina)
- [#19538](https://github.com/apache/superset/pull/19538) refactor: Removes the CSS files from the Country Map plugin (@michael-s-molina)
- [#19536](https://github.com/apache/superset/pull/19536) chore: Removes hard-coded opacity and spacing from the BigNumber plugin (@michael-s-molina)
- [#19494](https://github.com/apache/superset/pull/19494) refactor: Removes the CSS files from the Sankey Loop plugin (@michael-s-molina)
- [#19492](https://github.com/apache/superset/pull/19492) chore: Remove Legacy Force Directed viz plugin (@geido)
- [#19524](https://github.com/apache/superset/pull/19524) chore: Deprecating /my_queries endpoint (@AAfghahi)
- [#19467](https://github.com/apache/superset/pull/19467) chore(Explore): Change text when saving a chart in a new dashboard (@geido)
- [#19526](https://github.com/apache/superset/pull/19526) chore(database): Creating helper make_url_safe to wrap potential errors (@craig-rueda)
- [#19415](https://github.com/apache/superset/pull/19415) chore: Remove Control.less in Explore (@geido)
- [#19413](https://github.com/apache/superset/pull/19413) chore: Remove unused less file from profile (@geido)
- [#19460](https://github.com/apache/superset/pull/19460) chore: Switch to gender neutral terms (@inclusive-coding-bot)
- [#19486](https://github.com/apache/superset/pull/19486) refactor: Removes the CSS files from the Treemap plugin (@michael-s-molina)
- [#19488](https://github.com/apache/superset/pull/19488) refactor: Removes the CSS files from the Sunburst plugin (@michael-s-molina)
- [#19490](https://github.com/apache/superset/pull/19490) chore: Add theme color to ParallelCoordinates (@geido)
- [#19442](https://github.com/apache/superset/pull/19442) chore: Remove FilterbaleTableStyles.less (@geido)
- [#19441](https://github.com/apache/superset/pull/19441) chore: Remove StyledQueryButton.less (@geido)
- [#19473](https://github.com/apache/superset/pull/19473) refactor: Removes the CSS files from the Rose plugin (@michael-s-molina)
- [#19466](https://github.com/apache/superset/pull/19466) chore: Removes hard-coded colors from legacy-plugin-chart-world-map (@michael-s-molina)
- [#19465](https://github.com/apache/superset/pull/19465) refactor: Removes the CSS files from the DeckGL plugin (@michael-s-molina)
- [#19440](https://github.com/apache/superset/pull/19440) chore: Remove index.less from showSavedQuery (@geido)
- [#19230](https://github.com/apache/superset/pull/19230) chore!: remove `ROW_LEVEL_SECURITY` feature flag (permanently enable) (@suddjian)
- [#19361](https://github.com/apache/superset/pull/19361) chore: remove deprecated config keys and endpoints code 2.0 (@pkdotson)
- [#19261](https://github.com/apache/superset/pull/19261) chore: remove old alerts and configs keys (@pkdotson)
- [#19168](https://github.com/apache/superset/pull/19168) chore: bump celery and Flask (@dpgaspar)
- [#19049](https://github.com/apache/superset/pull/19049) chore: Remove logo forced width (@geido)
- [#19274](https://github.com/apache/superset/pull/19274) chore: remove PUBLIC_ROLE_LIKE_GAMMA deprecated config key (@dpgaspar)
- [#19273](https://github.com/apache/superset/pull/19273) chore: remove deprecated celery cli (@dpgaspar)
- [#19262](https://github.com/apache/superset/pull/19262) chore: update updating with druid no sql deprecation (@eschutho)
- [#19083](https://github.com/apache/superset/pull/19083) chore!: update mutator to take kwargs (@eschutho)
- [#19231](https://github.com/apache/superset/pull/19231) chore!: remove `ENABLE_REACT_CRUD_VIEWS` feature flag (permanently enable) (@suddjian)
- [#19241](https://github.com/apache/superset/pull/19241) chore(superset 2.0): remove front-end deprecated code (@graceguo-supercat)
- [#19107](https://github.com/apache/superset/pull/19107) chore: turn on SQLLAB_BACKEND_PERSISTENCE by default (@ktmud)
- [#19142](https://github.com/apache/superset/pull/19142) chore!: turn on Versioned Export in config.py (@AAfghahi)
- [#19108](https://github.com/apache/superset/pull/19108) chore: Update UPDATING.md with info about flipping dnd feature flag (@kgabryje)
- [#19146](https://github.com/apache/superset/pull/19146) chore!: Remove remove SQLALCHEMY_DOCS_URL and SQLALCHEMY_DISPLAY_TEXT from the config from the config (@hughhhh)
- [#19017](https://github.com/apache/superset/pull/19017) chore: Deprecate Python 3.7 (@john-bodley)
- [#19113](https://github.com/apache/superset/pull/19113) chore(config): Migrating `ENABLE_JAVASCRIPT_CONTROLS` from app config to a feature flag (@rusackas)
- [#19046](https://github.com/apache/superset/pull/19046) chore(explore): Set Drag&Drop feature flags to True by default (@kgabryje)
- [#19016](https://github.com/apache/superset/pull/19016) chore: Adding PR to Updating.md (@AAfghahi)
- [#18970](https://github.com/apache/superset/pull/18970) chore: Change Dataset legacy editor flag to true (@AAfghahi)
### 1.5.1 (Thu May 26 14:45:20 2022 +0300)
**Fixes**

View File

@@ -22,7 +22,7 @@ under the License.
This file documents any backwards-incompatible changes in Superset and
assists people when migrating to a new version.
## Next
## 2.0.0
- [19046](https://github.com/apache/superset/pull/19046): Enables the drag and drop interface in Explore control panel by default. Flips `ENABLE_EXPLORE_DRAG_AND_DROP` and `ENABLE_DND_WITH_CLICK_UX` feature flags to `True`.
- [18936](https://github.com/apache/superset/pull/18936): Removes legacy SIP-15 interim logic/flags—specifically the `SIP_15_ENABLED`, `SIP_15_GRACE_PERIOD_END`, `SIP_15_DEFAULT_TIME_RANGE_ENDPOINTS`, and `SIP_15_TOAST_MESSAGE` flags. Time range endpoints are no longer configurable and strictly adhere to the `[start, end)` paradigm, i.e., inclusive of the start and exclusive of the end. Additionally this change removes the now obsolete `time_range_endpoints` from the form-data and resulting in the cache being busted.
@@ -46,10 +46,6 @@ assists people when migrating to a new version.
- [19017](https://github.com/apache/superset/pull/19017): Removes Python 3.7 support.
- [18970](https://github.com/apache/superset/pull/18970): The `DISABLE_LEGACY_DATASOURCE_EDITOR` feature flag is now `True` by default which disables the legacy datasource editor from being shown in the client.
### Potential Downtime
### Other
## 1.5.0
### Breaking Changes
@@ -82,6 +78,7 @@ assists people when migrating to a new version.
## 1.4.1
### Breaking Changes
- [17984](https://github.com/apache/superset/pull/17984): Default Flask SECRET_KEY has changed for security reasons. You should always override with your own secret. Set `PREVIOUS_SECRET_KEY` (ex: PREVIOUS_SECRET_KEY = "\2\1thisismyscretkey\1\2\\e\\y\\y\\h") with your previous key and use `superset re-encrypt-secrets` to rotate you current secrets
### Potential Downtime

View File

@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
x-superset-image: &superset-image apache/superset:${TAG:-latest-dev}
x-superset-image: &superset-image apache/superset:2.0.0
x-superset-user: &superset-user root
x-superset-depends-on: &superset-depends-on
- db

View File

@@ -69,6 +69,16 @@ REDIS_RESULTS_DB = get_env_variable("REDIS_RESULTS_DB", "1")
RESULTS_BACKEND = FileSystemCache("/app/superset_home/sqllab")
CACHE_CONFIG = {
"CACHE_TYPE": "redis",
"CACHE_DEFAULT_TIMEOUT": 300,
"CACHE_KEY_PREFIX": "superset_",
"CACHE_REDIS_HOST": REDIS_HOST,
"CACHE_REDIS_PORT": REDIS_PORT,
"CACHE_REDIS_DB": REDIS_RESULTS_DB,
}
DATA_CACHE_CONFIG = CACHE_CONFIG
class CeleryConfig(object):
BROKER_URL = f"redis://{REDIS_HOST}:{REDIS_PORT}/{REDIS_CELERY_DB}"

View File

@@ -28,6 +28,8 @@ gunicorn \
--threads ${SERVER_THREADS_AMOUNT:-20} \
--timeout ${GUNICORN_TIMEOUT:-60} \
--keep-alive ${GUNICORN_KEEPALIVE:-2} \
--max-requests ${WORKER_MAX_REQUESTS:-0} \
--max-requests-jitter ${WORKER_MAX_REQUESTS_JITTER:-0} \
--limit-request-line ${SERVER_LIMIT_REQUEST_LINE:-0} \
--limit-request-field_size ${SERVER_LIMIT_REQUEST_FIELD_SIZE:-0} \
"${FLASK_APP}"

View File

@@ -14,7 +14,7 @@ You also need to install MySQL or [MariaDB](https://mariadb.com/downloads).
Ensure that you are using Python version 3.8 or 3.9, then proceed with:
````bash
```bash
# Create a virtual environment and activate it (recommended)
python3 -m venv venv # setup a python3 virtualenv
source venv/bin/activate
@@ -47,18 +47,18 @@ Or you can install via our Makefile
```bash
# Create a virtual environment and activate it (recommended)
$ python3 -m venv venv # setup a python3 virtualenv
$ source venv/bin/activate
python3 -m venv venv # setup a python3 virtualenv
source venv/bin/activate
# install pip packages + pre-commit
$ make install
make install
# Install superset pip packages and setup env only
$ make superset
make superset
# Setup pre-commit only
$ make pre-commit
````
make pre-commit
```
**Note: the FLASK_APP env var should not need to be set, as it's currently controlled
via `.flaskenv`, however if needed, it should be set to `superset.app:create_app()`**
@@ -103,4 +103,5 @@ app.logger.info(form_data)
```
### Frontend Assets
See [Building Frontend Assets Locally](https://github.com/apache/superset/blob/master/CONTRIBUTING.md#frontend)

View File

@@ -126,6 +126,7 @@ SLACK_API_TOKEN = "xoxb-"
# Email configuration
SMTP_HOST = "smtp.sendgrid.net" #change to your host
SMTP_STARTTLS = True
SMTP_SSL_SERVER_AUTH = True # If your using an SMTP server with a valid certificate
SMTP_SSL = False
SMTP_USER = "your_user"
SMTP_PORT = 2525 # your port eg. 587

View File

@@ -129,7 +129,7 @@ Finish installing by running through the following commands:
```
# Create an admin user in your metadata database (use `admin` as username to be able to load the examples)
$ export FLASK_APP=superset
export FLASK_APP=superset
superset fab create-admin
# Load some data to play with

View File

@@ -44,7 +44,7 @@ all of the required dependencies. Docker Desktop [recently added support for Win
following command:
```bash
$ git clone https://github.com/apache/superset.git
git clone https://github.com/apache/superset.git
```
Once that command completes successfully, you should see a new `superset` folder in your
@@ -55,14 +55,14 @@ current directory.
Navigate to the folder you created in step 1:
```bash
$ cd superset
cd superset
```
When working on master branch, run the following commands:
```bash
$ docker-compose -f docker-compose-non-dev.yml pull
$ docker-compose -f docker-compose-non-dev.yml up
docker-compose -f docker-compose-non-dev.yml pull
docker-compose -f docker-compose-non-dev.yml up
```
Alternatively, you can also run a specific version of Superset by first checking out
@@ -70,9 +70,9 @@ the branch/tag, and then starting `docker-compose` with the `TAG` variable.
For example, to run the 1.4.0 version, run the following commands:
```bash
% git checkout 1.4.0
$ TAG=1.4.0 docker-compose -f docker-compose-non-dev.yml pull
$ TAG=1.4.0 docker-compose -f docker-compose-non-dev.yml up
git checkout 1.4.0
TAG=1.4.0 docker-compose -f docker-compose-non-dev.yml pull
TAG=1.4.0 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

View File

@@ -13,7 +13,7 @@ geospatial charts.
Here are a **few different ways you can get started with Superset**:
- Download the [source from Apache Foundation's website](https://dist.apache.org/repos/dist/release/superset/1.4.1/)
- Download the [source from Apache Foundation's website](https://dist.apache.org/repos/dist/release/superset/)
- Download the latest Superset version from [Pypi here](https://pypi.org/project/apache-superset/)
- Setup Superset locally with one command
using [Docker Compose](installation/installing-superset-using-docker-compose)

View File

@@ -60,7 +60,7 @@ colorama==0.4.4
# via
# apache-superset
# flask-appbuilder
convertdate==2.3.2
convertdate==2.4.0
# via holidays
cron-descriptor==1.2.24
# via apache-superset
@@ -86,7 +86,7 @@ flask==2.0.3
# flask-migrate
# flask-sqlalchemy
# flask-wtf
flask-appbuilder==4.0.0
flask-appbuilder==4.1.3
# via apache-superset
flask-babel==1.0.0
# via flask-appbuilder
@@ -126,7 +126,7 @@ gunicorn==20.1.0
# via apache-superset
hashids==1.3.1
# via apache-superset
holidays==0.10.3
holidays==0.14.2
# via apache-superset
humanize==3.11.0
# via apache-superset

View File

@@ -77,7 +77,7 @@ setup(
"cryptography>=3.3.2",
"deprecation>=2.1.0, <2.2.0",
"flask>=2.0.0, <3.0.0",
"flask-appbuilder>=4.0.0, <5.0.0",
"flask-appbuilder>=4.1.3, <5.0.0",
"flask-caching>=1.10.0",
"flask-compress",
"flask-talisman",
@@ -88,7 +88,7 @@ setup(
"graphlib-backport",
"gunicorn>=20.1.0",
"hashids>=1.3.1, <2",
"holidays==0.10.3", # PINNED! https://github.com/dr-prodigy/python-holidays/issues/406
"holidays==0.14.2",
"humanize",
"isodate",
"markdown>=3.0",
@@ -149,7 +149,7 @@ setup(
"impala": ["impyla>0.16.2, <0.17"],
"kusto": ["sqlalchemy-kusto>=1.0.1, <2"],
"kylin": ["kylinpy>=2.8.1, <2.9"],
"mmsql": ["pymssql>=2.1.4, <2.2"],
"mssql": ["pymssql>=2.1.4, <2.2"],
"mysql": ["mysqlclient>=2.1.0, <3"],
"oracle": ["cx-Oracle>8.0.0, <8.1"],
"pinot": ["pinotdb>=0.3.3, <0.4"],

View File

@@ -204,7 +204,7 @@ describe('Time range filter', () => {
});
});
it('Custom time_range params', () => {
xit('Custom time_range params', () => {
const formData = {
...FORM_DATA_DEFAULTS,
viz_type: 'line',

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "superset",
"version": "0.0.0dev",
"version": "2.0.1",
"description": "Superset is a data exploration platform designed to be visual, intuitive, and interactive.",
"keywords": [
"big",
@@ -126,7 +126,7 @@
"d3-array": "^1.2.4",
"d3-color": "^1.2.0",
"d3-scale": "^2.1.2",
"dom-to-image": "^2.6.0",
"dom-to-image-more": "^2.10.1",
"emotion-rgba": "0.0.9",
"fast-glob": "^3.2.7",
"fontsource-fira-code": "^4.0.0",
@@ -237,7 +237,6 @@
"@testing-library/react-hooks": "^5.0.3",
"@testing-library/user-event": "^12.7.0",
"@types/classnames": "^2.2.10",
"@types/dom-to-image": "^2.6.0",
"@types/enzyme": "^3.10.5",
"@types/enzyme-adapter-react-16": "^1.0.6",
"@types/fetch-mock": "^7.3.2",

View File

@@ -34,7 +34,7 @@ export const annotationsAndLayersControls: ControlPanelSectionConfig = {
label: '',
default: annotationLayers,
description: t('Annotation Layers'),
renderTrigger: true,
renderTrigger: false,
},
},
],

View File

@@ -363,6 +363,10 @@ export interface ControlPanelConfig {
standardizedFormData: StandardizedFormDataInterface;
},
) => QueryFormData;
updateStandardizedState?: (
prevState: StandardizedState,
currState: StandardizedState,
) => StandardizedState;
}
export type ControlOverrides = {

View File

@@ -37,16 +37,21 @@ export const getOpacity = (
extremeValue: number,
minOpacity = MIN_OPACITY_BOUNDED,
maxOpacity = MAX_OPACITY,
) =>
extremeValue === cutoffPoint
? maxOpacity
: round(
Math.abs(
((maxOpacity - minOpacity) / (extremeValue - cutoffPoint)) *
(value - cutoffPoint),
) + minOpacity,
2,
);
) => {
if (extremeValue === cutoffPoint) {
return maxOpacity;
}
return Math.min(
maxOpacity,
round(
Math.abs(
((maxOpacity - minOpacity) / (extremeValue - cutoffPoint)) *
(value - cutoffPoint),
) + minOpacity,
2,
),
);
};
export const getColorFunction = (
{

View File

@@ -50,6 +50,9 @@ describe('getOpacity', () => {
expect(getOpacity(100, 100, 50)).toEqual(0.05);
expect(getOpacity(100, 100, 100, 0, 0.8)).toEqual(0.8);
expect(getOpacity(100, 100, 50, 0, 1)).toEqual(0);
expect(getOpacity(999, 100, 50, 0, 1)).toEqual(1);
expect(getOpacity(100, 100, 50, 0.99, 1)).toEqual(0.99);
expect(getOpacity(99, 100, 50, 0, 1)).toEqual(0.02);
});
});

View File

@@ -40,6 +40,7 @@
"@types/d3-time-format": "^2.1.0",
"@types/lodash": "^4.14.149",
"@types/math-expression-evaluator": "^1.2.1",
"@types/node": "^18.0.0",
"@types/rison": "0.0.6",
"@types/seedrandom": "^2.4.28",
"@types/fetch-mock": "^7.3.3",

View File

@@ -41,7 +41,7 @@ export default function FallbackComponent({
>
<div>
<div>
<b>Oops! An error occured!</b>
<b>Oops! An error occurred!</b>
</div>
<code>{error ? error.toString() : 'Unknown Error'}</code>
</div>

View File

@@ -45,6 +45,7 @@ const SupersetClient: SupersetClientInterface = {
init: force => getInstance().init(force),
isAuthenticated: () => getInstance().isAuthenticated(),
post: request => getInstance().post(request),
postForm: (...args) => getInstance().postForm(...args),
put: request => getInstance().put(request),
reAuthenticate: () => getInstance().reAuthenticate(),
request: request => getInstance().request(request),

View File

@@ -119,6 +119,36 @@ export default class SupersetClientClass {
return this.getCSRFToken();
}
async postForm(url: string, payload: Record<string, any>, target = '_blank') {
if (url) {
await this.ensureAuth();
const hiddenForm = document.createElement('form');
hiddenForm.action = url;
hiddenForm.method = 'POST';
hiddenForm.target = target;
const payloadWithToken: Record<string, any> = {
...payload,
csrf_token: this.csrfToken!,
};
if (this.guestToken) {
payloadWithToken.guest_token = this.guestToken;
}
Object.entries(payloadWithToken).forEach(([key, value]) => {
const data = document.createElement('input');
data.type = 'hidden';
data.name = key;
data.value = value;
hiddenForm.appendChild(data);
});
document.body.appendChild(hiddenForm);
hiddenForm.submit();
document.body.removeChild(hiddenForm);
}
}
async reAuthenticate() {
return this.init(true);
}

View File

@@ -146,6 +146,7 @@ export interface SupersetClientInterface
| 'delete'
| 'get'
| 'post'
| 'postForm'
| 'put'
| 'request'
| 'init'

View File

@@ -178,9 +178,9 @@ export function isTimeseriesAnnotationResult(
}
export function isRecordAnnotationResult(
result: AnnotationResult,
result: any,
): result is RecordAnnotationResult {
return 'columns' in result && 'records' in result;
return Array.isArray(result?.columns) && Array.isArray(result?.records);
}
export type AnnotationData = { [key: string]: AnnotationResult };

View File

@@ -47,7 +47,7 @@ export interface ChartDataResponseResult {
/**
* Data for the annotation layer.
*/
annotation_data: AnnotationData[] | null;
annotation_data: AnnotationData | null;
cache_key: string | null;
cache_timeout: number | null;
cached_dttm: string | null;

View File

@@ -56,7 +56,7 @@ export default class Translator {
*/
addTranslation(key: string, texts: ReadonlyArray<string>) {
const translations = this.i18n.options.locale_data.superset;
if (key in translations) {
if (process.env.WEBPACK_MODE !== 'test' && key in translations) {
logging.warn(`Duplicate translation key "${key}", will override.`);
}
translations[key] = texts;

View File

@@ -121,7 +121,7 @@ describe('SuperChart', () => {
);
await new Promise(resolve => setImmediate(resolve));
wrapper.update();
expect(wrapper.text()).toContain('Oops! An error occured!');
expect(wrapper.text()).toContain('Oops! An error occurred!');
});
it('renders custom FallbackComponent', () => {
expectedErrors = 1;

View File

@@ -30,21 +30,23 @@ describe('SupersetClient', () => {
afterEach(SupersetClient.reset);
it('exposes reset, configure, init, get, post, isAuthenticated, and reAuthenticate methods', () => {
it('exposes reset, configure, init, get, post, postForm, isAuthenticated, and reAuthenticate methods', () => {
expect(typeof SupersetClient.configure).toBe('function');
expect(typeof SupersetClient.init).toBe('function');
expect(typeof SupersetClient.get).toBe('function');
expect(typeof SupersetClient.post).toBe('function');
expect(typeof SupersetClient.postForm).toBe('function');
expect(typeof SupersetClient.isAuthenticated).toBe('function');
expect(typeof SupersetClient.reAuthenticate).toBe('function');
expect(typeof SupersetClient.request).toBe('function');
expect(typeof SupersetClient.reset).toBe('function');
});
it('throws if you call init, get, post, isAuthenticated, or reAuthenticate before configure', () => {
it('throws if you call init, get, post, postForm, isAuthenticated, or reAuthenticate before configure', () => {
expect(SupersetClient.init).toThrow();
expect(SupersetClient.get).toThrow();
expect(SupersetClient.post).toThrow();
expect(SupersetClient.postForm).toThrow();
expect(SupersetClient.isAuthenticated).toThrow();
expect(SupersetClient.reAuthenticate).toThrow();
expect(SupersetClient.request).toThrow();

View File

@@ -605,4 +605,107 @@ describe('SupersetClientClass', () => {
}
});
});
describe('.postForm()', () => {
const protocol = 'https:';
const host = 'host';
const mockPostFormEndpoint = '/post_form/url';
const mockPostFormUrl = `${protocol}//${host}${mockPostFormEndpoint}`;
const guestToken = 'test-guest-token';
const postFormPayload = { number: 123, array: [1, 2, 3] };
let authSpy: jest.SpyInstance;
let client: SupersetClientClass;
let appendChild: any;
let removeChild: any;
let submit: any;
let createElement: any;
beforeEach(async () => {
client = new SupersetClientClass({ protocol, host });
authSpy = jest.spyOn(SupersetClientClass.prototype, 'ensureAuth');
await client.init();
appendChild = jest.fn();
removeChild = jest.fn();
submit = jest.fn();
createElement = jest.fn(() => ({
appendChild: jest.fn(),
submit,
}));
document.createElement = createElement as any;
document.body.appendChild = appendChild;
document.body.removeChild = removeChild;
});
afterEach(() => {
jest.restoreAllMocks();
});
it('makes postForm request', async () => {
await client.postForm(mockPostFormUrl, {});
const hiddenForm = createElement.mock.results[0].value;
const csrfTokenInput = createElement.mock.results[1].value;
expect(createElement.mock.calls).toHaveLength(2);
expect(hiddenForm.action).toBe(mockPostFormUrl);
expect(hiddenForm.method).toBe('POST');
expect(hiddenForm.target).toBe('_blank');
expect(csrfTokenInput.type).toBe('hidden');
expect(csrfTokenInput.name).toBe('csrf_token');
expect(csrfTokenInput.value).toBe(1234);
expect(appendChild.mock.calls).toHaveLength(1);
expect(removeChild.mock.calls).toHaveLength(1);
expect(authSpy).toHaveBeenCalledTimes(1);
});
it('makes postForm request with guest token', async () => {
client = new SupersetClientClass({ protocol, host, guestToken });
await client.init();
await client.postForm(mockPostFormUrl, {});
const guestTokenInput = createElement.mock.results[2].value;
expect(createElement.mock.calls).toHaveLength(3);
expect(guestTokenInput.type).toBe('hidden');
expect(guestTokenInput.name).toBe('guest_token');
expect(guestTokenInput.value).toBe(guestToken);
expect(appendChild.mock.calls).toHaveLength(1);
expect(removeChild.mock.calls).toHaveLength(1);
expect(authSpy).toHaveBeenCalledTimes(1);
});
it('makes postForm request with payload', async () => {
await client.postForm(mockPostFormUrl, { form_data: postFormPayload });
const postFormPayloadInput = createElement.mock.results[1].value;
expect(createElement.mock.calls).toHaveLength(3);
expect(postFormPayloadInput.type).toBe('hidden');
expect(postFormPayloadInput.name).toBe('form_data');
expect(postFormPayloadInput.value).toBe(postFormPayload);
expect(appendChild.mock.calls).toHaveLength(1);
expect(removeChild.mock.calls).toHaveLength(1);
expect(submit.mock.calls).toHaveLength(1);
expect(authSpy).toHaveBeenCalledTimes(1);
});
it('should do nothing when url is empty string', async () => {
const result = await client.postForm('', {});
expect(result).toBeUndefined();
expect(createElement.mock.calls).toHaveLength(0);
expect(appendChild.mock.calls).toHaveLength(0);
expect(removeChild.mock.calls).toHaveLength(0);
expect(authSpy).toHaveBeenCalledTimes(0);
});
});
});

View File

@@ -41,10 +41,12 @@ describe('Translator', () => {
spy.mockImplementation((info: any) => {
throw new Error(info);
});
process.env.WEBPACK_MODE = 'production';
});
afterAll(() => {
spy.mockRestore();
process.env.WEBPACK_MODE = 'test';
});
describe('new Translator(config)', () => {

View File

@@ -31,6 +31,7 @@ import {
MixedTimeseriesTransformProps,
} from '@superset-ui/plugin-chart-echarts';
import data from '../Timeseries/data';
import negativeNumData from './negativeData';
import { withResizableChartDemo } from '../../../../shared/components/ResizableChartDemo';
new EchartsTimeseriesChartPlugin()
@@ -57,6 +58,8 @@ export const Timeseries = ({ width, height }) => {
Boston: row.Boston,
}))
.filter(row => !!row.Boston),
colnames: ['__timestamp'],
coltypes: [2],
},
{
data: data
@@ -82,8 +85,13 @@ export const Timeseries = ({ width, height }) => {
logAxis: boolean('Log axis', false),
xAxisTimeFormat: 'smart_date',
tooltipTimeFormat: 'smart_date',
yAxisFormat: 'SMART_NUMBER',
yAxisFormat: select(
'y-axis format',
['$,.2f', 'SMART_NUMBER'],
'$,.2f',
),
yAxisTitle: text('Y Axis title', ''),
yAxisIndexB: select('yAxisIndexB', [0, 1], 1),
minorSplitLine: boolean('Query 1: Minor splitline', false),
seriesType: select(
'Query 1: Line type',
@@ -105,7 +113,61 @@ export const Timeseries = ({ width, height }) => {
markerEnabledB: boolean('Query 2: Enable markers', false),
markerSizeB: number('Query 2: Marker Size', 6),
opacityB: number('Query 2: Opacity', 0.2),
showValue: true,
}}
/>
);
};
export const WithNegativeNumbers = ({ width, height }) => (
<SuperChart
chartType="mixed-timeseries"
width={width}
height={height}
queriesData={[
{
data: negativeNumData,
colnames: ['__timestamp'],
coltypes: [2],
},
{
data: negativeNumData.map(({ __timestamp, Boston }) => ({
__timestamp,
avgRate: Boston / 100,
})),
},
]}
formData={{
contributionMode: undefined,
colorScheme: 'supersetColors',
seriesType: select(
'Line type',
['line', 'scatter', 'smooth', 'bar', 'start', 'middle', 'end'],
'line',
),
xAxisTimeFormat: 'smart_date',
yAxisFormat: select(
'y-axis format',
{
'Original value': '~g',
'Smart number': 'SMART_NUMBER',
'(12345.432 => $12,345.43)': '$,.2f',
},
'$,.2f',
),
stack: true,
showValue: boolean('Query 1: Show Value', true),
showValueB: boolean('Query 2: Show Value', false),
showLegend: true,
markerEnabledB: true,
yAxisIndexB: select(
'Query 2: Y Axis',
{
Primary: 0,
Secondary: 1,
},
1,
),
}}
/>
);

View File

@@ -0,0 +1,45 @@
/*
* 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.
*/
export default [
{
__timestamp: 1619827200000,
Boston: 10.8812312312,
Washington: -45.3089432023,
JerseyCity: -23.0509234029834,
},
{
__timestamp: 1622505600000,
Boston: 80.81029340234,
Washington: -10.299023489023,
JerseyCity: 53.54239402349,
},
{
__timestamp: 1625097600000,
Boston: 30.9129034924,
Washington: 100.25234902349,
JerseyCity: 27.17239402394,
},
{
__timestamp: 1627776000000,
Boston: 42.6129034924,
Washington: 90.23234902349,
JerseyCity: -32.23239402394,
},
];

View File

@@ -25,6 +25,7 @@ import {
TimeseriesTransformProps,
} from '@superset-ui/plugin-chart-echarts';
import data from './data';
import negativeNumData from './negativeNumData';
import { withResizableChartDemo } from '../../../../shared/components/ResizableChartDemo';
new EchartsTimeseriesChartPlugin()
@@ -61,7 +62,9 @@ export const Timeseries = ({ width, height }) => {
chartType="echarts-timeseries"
width={width}
height={height}
queriesData={[{ data: queryData }]}
queriesData={[
{ data: queryData, colnames: ['__timestamp'], coltypes: [2] },
]}
formData={{
contributionMode: undefined,
forecastEnabled,
@@ -87,3 +90,33 @@ export const Timeseries = ({ width, height }) => {
/>
);
};
export const WithNegativeNumbers = ({ width, height }) => (
<SuperChart
chartType="echarts-timeseries"
width={width}
height={height}
queriesData={[
{ data: negativeNumData, colnames: ['__timestamp'], coltypes: [2] },
]}
formData={{
contributionMode: undefined,
colorScheme: 'supersetColors',
seriesType: select(
'Line type',
['line', 'scatter', 'smooth', 'bar', 'start', 'middle', 'end'],
'line',
),
yAxisFormat: '$,.2f',
stack: boolean('Stack', true),
showValue: true,
showLegend: true,
onlyTotal: boolean('Only Total', true),
orientation: select(
'Orientation',
['vertical', 'horizontal'],
'vertical',
),
}}
/>
);

View File

@@ -0,0 +1,111 @@
/*
* 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.
*/
export default [
{
__timestamp: 1619827200000,
Boston: -0.88,
NewYork: null,
Washington: -0.3,
JerseyCity: -3.05,
Denver: -8.25,
SF: -0.13,
},
{
__timestamp: 1622505600000,
Boston: -0.81,
NewYork: null,
Washington: -0.29,
JerseyCity: -3.54,
Denver: -13.4,
SF: -0.12,
},
{
__timestamp: 1625097600000,
Boston: 0.91,
NewYork: null,
Washington: 0.25,
JerseyCity: 7.17,
Denver: 7.69,
SF: 0.05,
},
{
__timestamp: 1627776000000,
Boston: -1.05,
NewYork: -1.04,
Washington: -0.19,
JerseyCity: -8.99,
Denver: -7.99,
SF: -0.01,
},
{
__timestamp: 1630454400000,
Boston: -0.92,
NewYork: -1.09,
Washington: -0.17,
JerseyCity: -8.75,
Denver: -7.55,
SF: -0.01,
},
{
__timestamp: 1633046400000,
Boston: 0.79,
NewYork: -0.85,
Washington: 0.13,
JerseyCity: 12.59,
Denver: 3.34,
SF: -0.05,
},
{
__timestamp: 1635724800000,
Boston: 0.72,
NewYork: 0.54,
Washington: 0.15,
JerseyCity: 11.03,
Denver: 7.24,
SF: -0.14,
},
{
__timestamp: 1638316800000,
Boston: 0.61,
NewYork: 0.73,
Washington: 0.15,
JerseyCity: 13.45,
Denver: 5.98,
SF: -0.22,
},
{
__timestamp: 1640995200000,
Boston: 0.51,
NewYork: 1.8,
Washington: 0.15,
JerseyCity: 12.96,
Denver: 3.22,
SF: -0.02,
},
{
__timestamp: 1643673600000,
Boston: -0.47,
NewYork: null,
Washington: -0.18,
JerseyCity: -14.27,
Denver: -6.24,
SF: -0.04,
},
];

View File

@@ -100,4 +100,8 @@ export default {
...formData,
metric: formData.standardizedFormData.standardizedState.metrics[0],
}),
updateStandardizedState: (prevState, currState) => ({
...currState,
metrics: [currState.metrics[0], ...prevState.metrics.slice(1)],
}),
} as ControlPanelConfig;

View File

@@ -152,7 +152,7 @@ class BigNumberVis extends React.PureComponent<BigNumberVisProps> {
document.body.append(container);
const fontSize = computeMaxFontSize({
text,
maxWidth: width,
maxWidth: width - 8, // Decrease 8px for more precise font size
maxHeight,
className: 'header-line',
container,

View File

@@ -274,6 +274,10 @@ const config: ControlPanelConfig = {
...formData,
metric: formData.standardizedFormData.standardizedState.metrics[0],
}),
updateStandardizedState: (prevState, currState) => ({
...currState,
metrics: [currState.metrics[0], ...prevState.metrics.slice(1)],
}),
};
export default config;

View File

@@ -125,8 +125,10 @@ export default function transformProps(
if (compareIndex < sortedData.length) {
const compareValue = sortedData[compareIndex][1];
// compare values must both be non-nulls
if (bigNumber !== null && compareValue !== null && compareValue !== 0) {
percentChange = (bigNumber - compareValue) / Math.abs(compareValue);
if (bigNumber !== null && compareValue !== null) {
percentChange = compareValue
? (bigNumber - compareValue) / Math.abs(compareValue)
: 0;
formattedSubheader = `${formatPercentChange(
percentChange,
)} ${compareSuffix}`;

View File

@@ -148,6 +148,10 @@ const config: ControlPanelConfig = {
metric: formData.standardizedFormData.standardizedState.metrics[0],
groupby: formData.standardizedFormData.standardizedState.columns,
}),
updateStandardizedState: (prevState, currState) => ({
...currState,
metrics: [currState.metrics[0], ...prevState.metrics.slice(1)],
}),
};
export default config;

View File

@@ -17,7 +17,7 @@
* under the License.
*/
import React from 'react';
import { t, validateNonEmpty, validateInteger } from '@superset-ui/core';
import { t } from '@superset-ui/core';
import {
sharedControls,
ControlPanelConfig,
@@ -81,8 +81,7 @@ const config: ControlPanelConfig = {
config: {
type: 'TextControl',
isInt: true,
default: String(DEFAULT_FORM_DATA.minVal),
validators: [validateNonEmpty, validateInteger],
default: DEFAULT_FORM_DATA.minVal,
renderTrigger: true,
label: t('Min'),
description: t('Minimum value on the gauge axis'),
@@ -94,7 +93,6 @@ const config: ControlPanelConfig = {
type: 'TextControl',
isInt: true,
default: DEFAULT_FORM_DATA.maxVal,
validators: [validateNonEmpty, validateInteger],
renderTrigger: true,
label: t('Max'),
description: t('Maximum value on the gauge axis'),
@@ -313,6 +311,10 @@ const config: ControlPanelConfig = {
metric: formData.standardizedFormData.standardizedState.metrics[0],
groupby: formData.standardizedFormData.standardizedState.columns,
}),
updateStandardizedState: (prevState, currState) => ({
...currState,
metrics: [currState.metrics[0], ...prevState.metrics.slice(1)],
}),
};
export default config;

View File

@@ -28,6 +28,7 @@ import {
} from '@superset-ui/core';
import { EChartsCoreOption, GaugeSeriesOption } from 'echarts';
import { GaugeDataItemOption } from 'echarts/types/src/chart/gauge/GaugeSeries';
import { CallbackDataParams } from 'echarts/types/src/util/types';
import range from 'lodash/range';
import { parseNumbersList } from '../utils/controls';
import {
@@ -80,6 +81,12 @@ const calculateAxisLineWidth = (
overlap: boolean,
): number => (overlap ? fontSize : data.length * fontSize);
const calculateMin = (data: GaugeDataItemOption[]) =>
2 * Math.min(...data.map(d => d.value as number).concat([0]));
const calculateMax = (data: GaugeDataItemOption[]) =>
2 * Math.max(...data.map(d => d.value as number).concat([0]));
export default function transformProps(
chartProps: EchartsGaugeChartProps,
): GaugeChartTransformedProps {
@@ -115,12 +122,7 @@ export default function transformProps(
const data = (queriesData[0]?.data || []) as DataRecord[];
const numberFormatter = getNumberFormatter(numberFormat);
const colorFn = CategoricalColorNamespace.getScale(colorScheme as string);
const normalizer = maxVal;
const axisLineWidth = calculateAxisLineWidth(data, fontSize, overlap);
const axisLabels = range(minVal, maxVal, (maxVal - minVal) / splitNumber);
const axisLabelLength = Math.max(
...axisLabels.map(label => numberFormatter(label).length).concat([1]),
);
const groupbyLabels = groupby.map(getColumnLabel);
const formatValue = (value: number) =>
valueFormatter.replace('{value}', numberFormatter(value));
@@ -130,12 +132,6 @@ export default function transformProps(
FONT_SIZE_MULTIPLIERS.titleOffsetFromTitle * fontSize;
const detailOffsetFromTitle =
FONT_SIZE_MULTIPLIERS.detailOffsetFromTitle * fontSize;
const intervalBoundsAndColors = setIntervalBoundsAndColors(
intervals,
intervalColorIndices,
colorFn,
normalizer,
);
const columnsLabelMap = new Map<string, DataRecordValue[]>();
const transformedData: GaugeDataItemOption[] = data.map(
@@ -196,6 +192,33 @@ export default function transformProps(
const { setDataMask = () => {} } = hooks;
const min = minVal ?? calculateMin(transformedData);
const max = maxVal ?? calculateMax(transformedData);
const axisLabels = range(min, max, (max - min) / splitNumber);
const axisLabelLength = Math.max(
...axisLabels.map(label => numberFormatter(label).length).concat([1]),
);
const normalizer = max;
const intervalBoundsAndColors = setIntervalBoundsAndColors(
intervals,
intervalColorIndices,
colorFn,
normalizer,
);
const splitLineDistance =
axisLineWidth + splitLineLength + OFFSETS.ticksFromLine;
const axisLabelDistance =
FONT_SIZE_MULTIPLIERS.axisLabelDistance *
fontSize *
FONT_SIZE_MULTIPLIERS.axisLabelLength *
axisLabelLength +
(showSplitLine ? splitLineLength : 0) +
(showAxisTick ? axisTickLength : 0) +
OFFSETS.ticksFromLine -
axisLineWidth;
const axisTickDistance =
axisLineWidth + axisTickLength + OFFSETS.ticksFromLine;
const progress = {
show: showProgress,
overlap,
@@ -204,7 +227,7 @@ export default function transformProps(
};
const splitLine = {
show: showSplitLine,
distance: -axisLineWidth - splitLineLength - OFFSETS.ticksFromLine,
distance: -splitLineDistance,
length: splitLineLength,
lineStyle: {
width: FONT_SIZE_MULTIPLIERS.splitLineWidth * fontSize,
@@ -219,22 +242,14 @@ export default function transformProps(
},
};
const axisLabel = {
distance:
axisLineWidth -
FONT_SIZE_MULTIPLIERS.axisLabelDistance *
fontSize *
FONT_SIZE_MULTIPLIERS.axisLabelLength *
axisLabelLength -
(showSplitLine ? splitLineLength : 0) -
(showAxisTick ? axisTickLength : 0) -
OFFSETS.ticksFromLine,
distance: -axisLabelDistance,
fontSize,
formatter: numberFormatter,
color: gaugeSeriesOptions.axisLabel?.color,
};
const axisTick = {
show: showAxisTick,
distance: -axisLineWidth - axisTickLength - OFFSETS.ticksFromLine,
distance: -axisTickDistance,
length: axisTickLength,
lineStyle: gaugeSeriesOptions.axisTick?.lineStyle as AxisTickLineStyle,
};
@@ -243,8 +258,14 @@ export default function transformProps(
formatter: (value: number) => formatValue(value),
color: gaugeSeriesOptions.detail?.color,
};
let pointer;
const tooltip = {
formatter: (params: CallbackDataParams) => {
const { name, value } = params;
return `${name} : ${formatValue(value as number)}`;
},
};
let pointer;
if (intervalBoundsAndColors.length) {
splitLine.lineStyle.color =
INTERVAL_GAUGE_SERIES_OPTION.splitLine?.lineStyle?.color;
@@ -269,8 +290,8 @@ export default function transformProps(
type: 'gauge',
startAngle,
endAngle,
min: minVal,
max: maxVal,
min,
max,
progress,
animation,
axisLine: axisLine as GaugeSeriesOption['axisLine'],
@@ -280,11 +301,19 @@ export default function transformProps(
axisTick,
pointer,
detail,
tooltip,
radius:
Math.min(width, height) / 2 - axisLabelDistance - axisTickDistance,
center: ['50%', '55%'],
data: transformedData,
},
];
const echartOptions: EChartsCoreOption = {
tooltip: {
appendToBody: true,
trigger: 'item',
},
series,
};

View File

@@ -34,8 +34,8 @@ export type EchartsGaugeFormData = QueryFormData & {
groupby: QueryFormColumn[];
metric?: string;
rowLimit: number;
minVal: number;
maxVal: number;
minVal: number | null;
maxVal: number | null;
fontSize: number;
numberFormat: string;
animation: boolean;
@@ -58,8 +58,8 @@ export const DEFAULT_FORM_DATA: Partial<EchartsGaugeFormData> = {
...DEFAULT_LEGEND_FORM_DATA,
groupby: [],
rowLimit: 10,
minVal: 0,
maxVal: 100,
minVal: null,
maxVal: null,
fontSize: 15,
numberFormat: 'SMART_NUMBER',
animation: true,

View File

@@ -324,6 +324,10 @@ const controlPanel: ControlPanelConfig = {
...formData,
metric: formData.standardizedFormData.standardizedState.metrics[0],
}),
updateStandardizedState: (prevState, currState) => ({
...currState,
metrics: [currState.metrics[0], ...prevState.metrics.slice(1)],
}),
};
export default controlPanel;

View File

@@ -47,8 +47,13 @@ import {
getAxisType,
getColtypesMapping,
getLegendProps,
extractDataTotalValues,
extractShowValueIndexes,
} from '../utils/series';
import { extractAnnotationLabels } from '../utils/annotation';
import {
extractAnnotationLabels,
getAnnotationData,
} from '../utils/annotation';
import {
extractForecastSeriesContext,
extractForecastValuesFromTooltipParams,
@@ -81,11 +86,11 @@ export default function transformProps(
filterState,
datasource,
theme,
annotationData = {},
} = chartProps;
const { verboseMap = {} } = datasource;
const data1 = (queriesData[0].data || []) as TimeseriesDataRecord[];
const data2 = (queriesData[1].data || []) as TimeseriesDataRecord[];
const annotationData = getAnnotationData(chartProps);
const {
area,
@@ -136,6 +141,7 @@ export default function transformProps(
yAxisTitlePosition,
sliceId,
timeGrainSqla,
percentageThreshold,
}: EchartsMixedTimeseriesFormData = { ...DEFAULT_FORM_DATA, ...formData };
const colorScale = CategoricalColorNamespace.getScale(colorScheme as string);
@@ -155,7 +161,7 @@ export default function transformProps(
});
const dataTypes = getColtypesMapping(queriesData[0]);
const xAxisDataType = dataTypes?.[xAxisCol];
const xAxisDataType = dataTypes?.[xAxisCol] ?? dataTypes?.[xAxisOrig];
const xAxisType = getAxisType(xAxisDataType);
const series: SeriesOption[] = [];
const formatter = getNumberFormatter(contributionMode ? ',.0%' : yAxisFormat);
@@ -181,7 +187,28 @@ export default function transformProps(
rawSeriesB.forEach(seriesOption =>
mapSeriesIdToAxis(seriesOption, yAxisIndexB),
);
const showValueIndexesA = extractShowValueIndexes(rawSeriesA, {
stack,
});
const showValueIndexesB = extractShowValueIndexes(rawSeriesB, {
stack,
});
const { totalStackedValues, thresholdValues } = extractDataTotalValues(
rebasedDataA,
{
stack,
percentageThreshold,
xAxisCol,
},
);
const {
totalStackedValues: totalStackedValuesB,
thresholdValues: thresholdValuesB,
} = extractDataTotalValues(rebasedDataB, {
stack: Boolean(stackB),
percentageThreshold,
xAxisCol,
});
rawSeriesA.forEach(entry => {
const transformedSeries = transformSeries(entry, colorScale, {
area,
@@ -195,6 +222,10 @@ export default function transformProps(
filterState,
seriesKey: entry.name,
sliceId,
formatter,
showValueIndexes: showValueIndexesA,
totalStackedValues,
thresholdValues,
});
if (transformedSeries) series.push(transformedSeries);
});
@@ -214,6 +245,10 @@ export default function transformProps(
? `${entry.name} (1)`
: entry.name,
sliceId,
formatter: formatterSecondary,
showValueIndexes: showValueIndexesB,
totalStackedValues: totalStackedValuesB,
thresholdValues: thresholdValuesB,
});
if (transformedSeries) series.push(transformedSeries);
});

View File

@@ -260,6 +260,10 @@ const config: ControlPanelConfig = {
row_limit:
ensureIsInt(formData.row_limit, 100) >= 100 ? 100 : formData.row_limit,
}),
updateStandardizedState: (prevState, currState) => ({
...currState,
metrics: [currState.metrics[0], ...prevState.metrics.slice(1)],
}),
};
export default config;

View File

@@ -45,8 +45,6 @@ import {
const {
contributionMode,
logAxis,
markerEnabled,
markerSize,
minorSplitLine,
rowLimit,
truncateYAxis,
@@ -341,38 +339,6 @@ const config: ControlPanelConfig = {
controlSetRows: [
['color_scheme'],
...showValueSection,
[
{
name: 'markerEnabled',
config: {
type: 'CheckboxControl',
label: t('Marker'),
renderTrigger: true,
default: markerEnabled,
description: t(
'Draw a marker on data points. Only applicable for line types.',
),
},
},
],
[
{
name: 'markerSize',
config: {
type: 'SliderControl',
label: t('Marker Size'),
renderTrigger: true,
min: 0,
max: 20,
default: markerSize,
description: t(
'Size of marker. Also applies to forecast observations.',
),
visibility: ({ controls }: ControlPanelsContainerProps) =>
Boolean(controls?.markerEnabled?.value),
},
},
],
[
{
name: 'zoomable',

View File

@@ -55,7 +55,10 @@ import {
extractDataTotalValues,
extractShowValueIndexes,
} from '../utils/series';
import { extractAnnotationLabels } from '../utils/annotation';
import {
extractAnnotationLabels,
getAnnotationData,
} from '../utils/annotation';
import {
extractForecastSeriesContext,
extractForecastSeriesContexts,
@@ -93,12 +96,12 @@ export default function transformProps(
queriesData,
datasource,
theme,
annotationData = {},
} = chartProps;
const { verboseMap = {} } = datasource;
const [queryData] = queriesData;
const { data = [] } = queryData as TimeseriesChartDataResponseResult;
const dataTypes = getColtypesMapping(queryData);
const annotationData = getAnnotationData(chartProps);
const {
area,
@@ -165,12 +168,15 @@ export default function transformProps(
});
const showValueIndexes = extractShowValueIndexes(rawSeries, {
stack,
onlyTotal,
isHorizontal,
});
const seriesContexts = extractForecastSeriesContexts(
Object.values(rawSeries).map(series => series.name as string),
);
const isAreaExpand = stack === AreaChartExtraControlsValue.Expand;
const xAxisDataType = dataTypes?.[xAxisCol];
const xAxisDataType = dataTypes?.[xAxisCol] ?? dataTypes?.[xAxisOrig];
const xAxisType = getAxisType(xAxisDataType);
const series: SeriesOption[] = [];
const formatter = getNumberFormatter(

View File

@@ -233,7 +233,10 @@ export function transformSeries(
if (!formatter) return numericValue;
if (!stack || isSelectedLegend) return formatter(numericValue);
if (!onlyTotal) {
if (numericValue >= thresholdValues[dataIndex]) {
if (
numericValue >=
(thresholdValues[dataIndex] || Number.MIN_SAFE_INTEGER)
) {
return formatter(numericValue);
}
return '';

View File

@@ -289,6 +289,10 @@ const controlPanel: ControlPanelConfig = {
...formData,
metric: formData.standardizedFormData.standardizedState.metrics[0],
}),
updateStandardizedState: (prevState, currState) => ({
...currState,
metrics: [currState.metrics[0], ...prevState.metrics.slice(1)],
}),
};
export default controlPanel;

View File

@@ -142,6 +142,10 @@ const config: ControlPanelConfig = {
metric: formData.standardizedFormData.standardizedState.metrics[0],
groupby: formData.standardizedFormData.standardizedState.columns,
}),
updateStandardizedState: (prevState, currState) => ({
...currState,
metrics: [currState.metrics[0], ...prevState.metrics.slice(1)],
}),
};
export default config;

View File

@@ -17,6 +17,8 @@
* specific language governing permissions and limitations
* under the License.
*/
import { isEmpty } from 'lodash';
import {
Annotation,
AnnotationData,
@@ -30,6 +32,8 @@ import {
isTimeseriesAnnotationResult,
TimeseriesDataRecord,
} from '@superset-ui/core';
import { EchartsTimeseriesChartProps } from '../types';
import { EchartsMixedTimeseriesProps } from '../MixedTimeseries/types';
export function evalFormula(
formula: FormulaAnnotationLayer,
@@ -130,3 +134,13 @@ export function extractAnnotationLabels(
return formulaAnnotationLabels.concat(timeseriesAnnotationLabels);
}
export function getAnnotationData(
chartProps: EchartsTimeseriesChartProps | EchartsMixedTimeseriesProps,
): AnnotationData {
const data = chartProps?.queriesData[0]?.annotation_data as AnnotationData;
if (!isEmpty(data)) {
return data;
}
return {};
}

View File

@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { isNumber } from 'lodash';
import { DataRecord, DTTM_ALIAS, NumberFormatter } from '@superset-ui/core';
import { OptionName } from 'echarts/types/src/util/types';
import { TooltipMarker } from 'echarts/types/src/util/format';
@@ -60,7 +61,7 @@ export const extractForecastValuesFromTooltipParams = (
const { marker, seriesId, value } = param;
const context = extractForecastSeriesContext(seriesId);
const numericValue = isHorizontal ? value[0] : value[1];
if (numericValue) {
if (isNumber(numericValue)) {
if (!(context.name in values))
values[context.name] = {
marker: marker || '',
@@ -94,7 +95,7 @@ export const formatForecastTooltipSeries = ({
}): string => {
let row = `${marker}${sanitizeHtml(seriesName)}: `;
let isObservation = false;
if (observation) {
if (isNumber(observation)) {
isObservation = true;
row += `${formatter(observation)}`;
}

View File

@@ -77,6 +77,8 @@ export function extractShowValueIndexes(
series: SeriesOption[],
opts: {
stack: StackType;
onlyTotal?: boolean;
isHorizontal?: boolean;
},
): number[] {
const showValueIndexes: number[] = [];
@@ -84,9 +86,20 @@ export function extractShowValueIndexes(
series.forEach((entry, seriesIndex) => {
const { data = [] } = entry;
(data as [any, number][]).forEach((datum, dataIndex) => {
if (datum[1] !== null) {
if (!opts.onlyTotal && datum[opts.isHorizontal ? 0 : 1] !== null) {
showValueIndexes[dataIndex] = seriesIndex;
}
if (opts.onlyTotal) {
if (datum[opts.isHorizontal ? 0 : 1] > 0) {
showValueIndexes[dataIndex] = seriesIndex;
}
if (
!showValueIndexes[dataIndex] &&
datum[opts.isHorizontal ? 0 : 1] !== null
) {
showValueIndexes[dataIndex] = seriesIndex;
}
}
});
});
}

View File

@@ -174,60 +174,62 @@ describe('EchartsTimeseries transformProps', () => {
titleColumn: '',
value: 3,
};
const annotationData = {
'My Event': {
columns: [
'start_dttm',
'end_dttm',
'short_descr',
'long_descr',
'json_metadata',
],
records: [
{
start_dttm: 0,
end_dttm: 1000,
short_descr: '',
long_descr: '',
json_metadata: null,
},
],
},
'My Interval': {
columns: ['start', 'end', 'title'],
records: [
{
start: 2000,
end: 3000,
title: 'My Title',
},
],
},
'My Timeseries': [
{
key: 'My Line',
values: [
{
x: 10000,
y: 11000,
},
{
x: 20000,
y: 21000,
},
],
},
],
};
const chartProps = new ChartProps({
...chartPropsConfig,
formData: {
...formData,
annotationLayers: [event, interval, timeseries],
},
annotationData: {
'My Event': {
columns: [
'start_dttm',
'end_dttm',
'short_descr',
'long_descr',
'json_metadata',
],
records: [
{
start_dttm: 0,
end_dttm: 1000,
short_descr: '',
long_descr: '',
json_metadata: null,
},
],
},
'My Interval': {
columns: ['start', 'end', 'title'],
records: [
{
start: 2000,
end: 3000,
title: 'My Title',
},
],
},
'My Timeseries': [
{
key: 'My Line',
values: [
{
x: 10000,
y: 11000,
},
{
x: 20000,
y: 21000,
},
],
},
],
},
annotationData,
queriesData: [
{
...queriesData[0],
annotation_data: annotationData,
},
],
});

View File

@@ -154,103 +154,148 @@ describe('rebaseForecastDatum', () => {
});
});
describe('extractForecastValuesFromTooltipParams', () => {
it('should extract the proper data from tooltip params', () => {
expect(
extractForecastValuesFromTooltipParams([
{
marker: '<img>',
seriesId: 'abc',
value: [new Date(0), 10],
},
{
marker: '<img>',
seriesId: 'abc__yhat',
value: [new Date(0), 1],
},
{
marker: '<img>',
seriesId: 'abc__yhat_lower',
value: [new Date(0), 5],
},
{
marker: '<img>',
seriesId: 'abc__yhat_upper',
value: [new Date(0), 6],
},
{
marker: '<img>',
seriesId: 'qwerty',
value: [new Date(0), 2],
},
]),
).toEqual({
abc: {
test('extractForecastValuesFromTooltipParams should extract the proper data from tooltip params', () => {
expect(
extractForecastValuesFromTooltipParams([
{
marker: '<img>',
observation: 10,
forecastTrend: 1,
forecastLower: 5,
forecastUpper: 6,
seriesId: 'abc',
value: [new Date(0), 10],
},
qwerty: {
{
marker: '<img>',
observation: 2,
seriesId: 'abc__yhat',
value: [new Date(0), 1],
},
});
{
marker: '<img>',
seriesId: 'abc__yhat_lower',
value: [new Date(0), 5],
},
{
marker: '<img>',
seriesId: 'abc__yhat_upper',
value: [new Date(0), 6],
},
{
marker: '<img>',
seriesId: 'qwerty',
value: [new Date(0), 2],
},
]),
).toEqual({
abc: {
marker: '<img>',
observation: 10,
forecastTrend: 1,
forecastLower: 5,
forecastUpper: 6,
},
qwerty: {
marker: '<img>',
observation: 2,
},
});
});
test('extractForecastValuesFromTooltipParams should extract valid values', () => {
expect(
extractForecastValuesFromTooltipParams([
{
marker: '<img>',
seriesId: 'foo',
value: [0, 10],
},
{
marker: '<img>',
seriesId: 'bar',
value: [100, 0],
},
]),
).toEqual({
foo: {
marker: '<img>',
observation: 10,
},
bar: {
marker: '<img>',
observation: 0,
},
});
});
const formatter = getNumberFormatter(NumberFormats.INTEGER);
describe('formatForecastTooltipSeries', () => {
it('should generate a proper series tooltip', () => {
expect(
formatForecastTooltipSeries({
seriesName: 'abc',
marker: '<img>',
observation: 10.1,
formatter,
}),
).toEqual('<img>abc: 10');
expect(
formatForecastTooltipSeries({
seriesName: 'qwerty',
marker: '<img>',
observation: 10.1,
forecastTrend: 20.1,
forecastLower: 5.1,
forecastUpper: 7.1,
formatter,
}),
).toEqual('<img>qwerty: 10, ŷ = 20 (5, 12)');
expect(
formatForecastTooltipSeries({
seriesName: 'qwerty',
marker: '<img>',
forecastTrend: 20,
forecastLower: 5,
forecastUpper: 7,
formatter,
}),
).toEqual('<img>qwerty: ŷ = 20 (5, 12)');
expect(
formatForecastTooltipSeries({
seriesName: 'qwerty',
marker: '<img>',
observation: 10.1,
forecastLower: 6,
forecastUpper: 7,
formatter,
}),
).toEqual('<img>qwerty: 10 (6, 13)');
expect(
formatForecastTooltipSeries({
seriesName: 'qwerty',
marker: '<img>',
forecastLower: 7,
forecastUpper: 8,
formatter,
}),
).toEqual('<img>qwerty: (7, 15)');
});
test('formatForecastTooltipSeries should apply format to value', () => {
expect(
formatForecastTooltipSeries({
seriesName: 'abc',
marker: '<img>',
observation: 10.1,
formatter,
}),
).toEqual('<img>abc: 10');
});
test('formatForecastTooltipSeries should show falsy value', () => {
expect(
formatForecastTooltipSeries({
seriesName: 'abc',
marker: '<img>',
observation: 0,
formatter,
}),
).toEqual('<img>abc: 0');
});
test('formatForecastTooltipSeries should format full forecast', () => {
expect(
formatForecastTooltipSeries({
seriesName: 'qwerty',
marker: '<img>',
observation: 10.1,
forecastTrend: 20.1,
forecastLower: 5.1,
forecastUpper: 7.1,
formatter,
}),
).toEqual('<img>qwerty: 10, ŷ = 20 (5, 12)');
});
test('formatForecastTooltipSeries should format forecast without observation', () => {
expect(
formatForecastTooltipSeries({
seriesName: 'qwerty',
marker: '<img>',
forecastTrend: 20,
forecastLower: 5,
forecastUpper: 7,
formatter,
}),
).toEqual('<img>qwerty: ŷ = 20 (5, 12)');
});
test('formatForecastTooltipSeries should format forecast without point estimate', () => {
expect(
formatForecastTooltipSeries({
seriesName: 'qwerty',
marker: '<img>',
observation: 10.1,
forecastLower: 6,
forecastUpper: 7,
formatter,
}),
).toEqual('<img>qwerty: 10 (6, 13)');
});
test('formatForecastTooltipSeries should format forecast with only confidence band', () => {
expect(
formatForecastTooltipSeries({
seriesName: 'qwerty',
marker: '<img>',
forecastLower: 7,
forecastUpper: 8,
formatter,
}),
).toEqual('<img>qwerty: (7, 15)');
});

View File

@@ -25,6 +25,7 @@ import {
getChartPadding,
getLegendProps,
sanitizeHtml,
extractShowValueIndexes,
} from '../../src/utils/series';
import { LegendOrientation, LegendType } from '../../src/types';
import { defaultLegendPadding } from '../../src/defaults';
@@ -206,6 +207,124 @@ describe('extractGroupbyLabel', () => {
});
});
describe('extractShowValueIndexes', () => {
it('should return the latest index for stack', () => {
expect(
extractShowValueIndexes(
[
{
id: 'abc',
name: 'abc',
data: [
['2000-01-01', null],
['2000-02-01', 0],
['2000-03-01', 1],
['2000-04-01', 0],
['2000-05-01', null],
['2000-06-01', 0],
['2000-07-01', 2],
['2000-08-01', 3],
['2000-09-01', null],
['2000-10-01', null],
],
},
{
id: 'def',
name: 'def',
data: [
['2000-01-01', null],
['2000-02-01', 0],
['2000-03-01', null],
['2000-04-01', 0],
['2000-05-01', null],
['2000-06-01', 0],
['2000-07-01', 2],
['2000-08-01', 3],
['2000-09-01', null],
['2000-10-01', 0],
],
},
{
id: 'def',
name: 'def',
data: [
['2000-01-01', null],
['2000-02-01', null],
['2000-03-01', null],
['2000-04-01', null],
['2000-05-01', null],
['2000-06-01', 3],
['2000-07-01', null],
['2000-08-01', null],
['2000-09-01', null],
['2000-10-01', null],
],
},
],
{ stack: true, onlyTotal: false, isHorizontal: false },
),
).toEqual([undefined, 1, 0, 1, undefined, 2, 1, 1, undefined, 1]);
});
it('should handle the negative numbers for total only', () => {
expect(
extractShowValueIndexes(
[
{
id: 'abc',
name: 'abc',
data: [
['2000-01-01', null],
['2000-02-01', 0],
['2000-03-01', -1],
['2000-04-01', 0],
['2000-05-01', null],
['2000-06-01', 0],
['2000-07-01', -2],
['2000-08-01', -3],
['2000-09-01', null],
['2000-10-01', null],
],
},
{
id: 'def',
name: 'def',
data: [
['2000-01-01', null],
['2000-02-01', 0],
['2000-03-01', null],
['2000-04-01', 0],
['2000-05-01', null],
['2000-06-01', 0],
['2000-07-01', 2],
['2000-08-01', -3],
['2000-09-01', null],
['2000-10-01', 0],
],
},
{
id: 'def',
name: 'def',
data: [
['2000-01-01', null],
['2000-02-01', 0],
['2000-03-01', null],
['2000-04-01', 1],
['2000-05-01', null],
['2000-06-01', 0],
['2000-07-01', -2],
['2000-08-01', 3],
['2000-09-01', null],
['2000-10-01', 0],
],
},
],
{ stack: true, onlyTotal: true, isHorizontal: false },
),
).toEqual([undefined, 1, 0, 2, undefined, 1, 1, 2, undefined, 1]);
});
});
describe('formatSeriesName', () => {
const numberFormatter = getNumberFormatter();
const timeFormatter = getTimeFormatter();

View File

@@ -16,15 +16,19 @@
* specific language governing permissions and limitations
* under the License.
*/
import { buildQueryContext, QueryFormData } from '@superset-ui/core';
import {
buildQueryContext,
normalizeOrderBy,
QueryFormData,
} from '@superset-ui/core';
export default function buildQuery(formData: QueryFormData) {
const { metric, sort_by_metric, groupby } = formData;
const { groupby } = formData;
return buildQueryContext(formData, baseQueryObject => [
{
...baseQueryObject,
...(sort_by_metric && { orderby: [[metric, false]] }),
orderby: normalizeOrderBy(baseQueryObject).orderby,
...(groupby && { groupby }),
},
]);

View File

@@ -61,9 +61,10 @@ const config: ControlPanelConfig = {
[metricsControlSetItem, allColumnsControlSetItem],
[percentMetricsControlSetItem],
[timeSeriesLimitMetricControlSetItem, orderByControlSetItem],
[orderDescendingControlSetItem],
serverPaginationControlSetRow,
[rowLimitControlSetItem, serverPageLengthControlSetItem],
[includeTimeControlSetItem, orderDescendingControlSetItem],
[includeTimeControlSetItem],
[showTotalsControlSetItem],
['adhoc_filters'],
emitFilterControl,

View File

@@ -18,6 +18,7 @@
*/
import { ControlSetItem, Dataset } from '@superset-ui/chart-controls';
import { t } from '@superset-ui/core';
import { isEmpty } from 'lodash';
import { isAggMode, isRawMode } from './shared';
export const orderByControlSetItem: ControlSetItem = {
@@ -45,7 +46,12 @@ export const orderDescendingControlSetItem: ControlSetItem = {
label: t('Sort descending'),
default: true,
description: t('Whether to sort descending or ascending'),
visibility: isAggMode,
visibility: ({ controls }) =>
!!(
isAggMode({ controls }) &&
controls?.timeseries_limit_metric.value &&
!isEmpty(controls?.timeseries_limit_metric.value)
),
resetOnHide: false,
},
};

View File

@@ -33,5 +33,5 @@ export const sortAlphanumericCaseInsensitive = <D extends {}>(
if (!valueB || typeof valueB !== 'string') {
return 1;
}
return valueA.localeCompare(valueB) > 0 ? 1 : -1;
return valueA.localeCompare(valueB);
};

View File

@@ -394,6 +394,7 @@ export default function TableChart<D extends DataRecord = DataRecord>(
colorPositiveNegative,
})
: undefined)};
white-space: ${value instanceof Date ? 'nowrap' : undefined};
`;
const cellProps = {
@@ -449,7 +450,7 @@ export default function TableChart<D extends DataRecord = DataRecord>(
data-column-name={col.id}
css={{
display: 'inline-flex',
alignItems: 'center',
alignItems: 'flex-end',
}}
>
<span data-column-name={col.id}>{label}</span>

View File

@@ -17,8 +17,13 @@
* under the License.
*/
import { defaultOrderByFn, Row } from 'react-table';
import { sortAlphanumericCaseInsensitive } from '../src/DataTable/utils/sortAlphanumericCaseInsensitive';
type RecursivePartial<T> = {
[P in keyof T]?: T[P] | RecursivePartial<T[P]>;
};
const testData = [
{
values: {
@@ -133,3 +138,106 @@ describe('sortAlphanumericCaseInsensitive', () => {
]);
});
});
const testDataMulti: Array<RecursivePartial<Row<object>>> = [
{
values: {
colA: 'group 1',
colB: '10',
},
},
{
values: {
colA: 'group 1',
colB: '15',
},
},
{
values: {
colA: 'group 1',
colB: '20',
},
},
{
values: {
colA: 'group 2',
colB: '10',
},
},
{
values: {
colA: 'group 3',
colB: '10',
},
},
{
values: {
colA: 'group 3',
colB: '15',
},
},
{
values: {
colA: 'group 3',
colB: '10',
},
},
];
describe('sortAlphanumericCaseInsensitiveMulti', () => {
it('Sort rows', () => {
const sorted = defaultOrderByFn(
[...testDataMulti] as Array<Row<object>>,
[
(a, b) => sortAlphanumericCaseInsensitive(a, b, 'colA'),
(a, b) => sortAlphanumericCaseInsensitive(a, b, 'colB'),
],
[true, false],
);
expect(sorted).toEqual([
{
values: {
colA: 'group 1',
colB: '20',
},
},
{
values: {
colA: 'group 1',
colB: '15',
},
},
{
values: {
colA: 'group 1',
colB: '10',
},
},
{
values: {
colA: 'group 2',
colB: '10',
},
},
{
values: {
colA: 'group 3',
colB: '15',
},
},
{
values: {
colA: 'group 3',
colB: '10',
},
},
{
values: {
colA: 'group 3',
colB: '10',
},
},
]);
});
});

View File

@@ -81,3 +81,5 @@ setupSupersetClient();
jest.mock('src/hooks/useTabId', () => ({
useTabId: () => 1,
}));
process.env.WEBPACK_MODE = 'test';

View File

@@ -370,8 +370,8 @@ div.tablePopover {
border: 1px solid @gray-light;
font-feature-settings: @font-feature-settings;
// Fira Code causes problem with Ace under Firefox
font-family: 'Menlo', 'Lucida Console', 'Courier New', 'Ubuntu Mono',
'Consolas', 'source-code-pro', monospace;
font-family: 'Menlo', 'Consolas', 'Courier New', 'Ubuntu Mono',
'source-code-pro', 'Lucida Console', monospace;
&.ace_autocomplete {
// Use !important because Ace Editor applies extra CSS at the last second

View File

@@ -27,7 +27,6 @@ import {
getExploreUrl,
getLegacyEndpointType,
buildV1ChartDataPayload,
postForm,
shouldUseLegacyApi,
getChartDataUri,
} from 'src/explore/exploreUtils';
@@ -40,6 +39,7 @@ import { addDangerToast } from 'src/components/MessageToasts/actions';
import { logEvent } from 'src/logger/actions';
import { Logger, LOG_ACTIONS_LOAD_CHART } from 'src/logger/LogUtils';
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
import { safeStringify } from 'src/utils/safeStringify';
import { allowCrossDomain as domainShardingEnabled } from 'src/utils/hostNamesConfig';
import { updateDataMask } from 'src/dataMask/actions';
import { waitForAsyncData } from 'src/middleware/asyncEvent';
@@ -563,7 +563,9 @@ export function redirectSQLLab(formData) {
datasourceKey: formData.datasource,
sql: json.result[0].query,
};
postForm(redirectUrl, payload);
SupersetClient.postForm(redirectUrl, {
form_data: safeStringify(payload),
});
})
.catch(() =>
dispatch(addDangerToast(t('An error occurred while loading the SQL'))),

View File

@@ -130,6 +130,13 @@ const CrudButtonWrapper = styled.div`
${({ theme }) => `margin-bottom: ${theme.gridUnit * 2}px`}
`;
const StyledButtonWrapper = styled.span`
${({ theme }) => `
margin-top: ${theme.gridUnit * 3}px;
margin-left: ${theme.gridUnit * 3}px;
`}
`;
export default class CRUDCollection extends React.PureComponent<
CRUDCollectionProps,
CRUDCollectionState
@@ -424,7 +431,7 @@ export default class CRUDCollection extends React.PureComponent<
<>
<CrudButtonWrapper>
{this.props.allowAddItem && (
<span className="m-t-10 m-r-10">
<StyledButtonWrapper>
<Button
buttonSize="small"
buttonStyle="tertiary"
@@ -434,7 +441,7 @@ export default class CRUDCollection extends React.PureComponent<
<i data-test="crud-add-table-item" className="fa fa-plus" />{' '}
{t('Add item')}
</Button>
</span>
</StyledButtonWrapper>
)}
</CrudButtonWrapper>
<CrudTableWrapper

View File

@@ -123,6 +123,13 @@ const StyledColumnsTabWrapper = styled.div`
}
`;
const StyledButtonWrapper = styled.span`
${({ theme }) => `
margin-top: ${theme.gridUnit * 3}px;
margin-left: ${theme.gridUnit * 3}px;
`}
`;
const checkboxGenerator = (d, onChange) => (
<CheckboxControl value={d} onChange={onChange} />
);
@@ -1361,7 +1368,7 @@ class DatasourceEditor extends React.PureComponent {
>
<StyledColumnsTabWrapper>
<ColumnButtonWrapper>
<span className="m-t-10 m-r-10">
<StyledButtonWrapper>
<Button
buttonSize="small"
buttonStyle="tertiary"
@@ -1372,7 +1379,7 @@ class DatasourceEditor extends React.PureComponent {
<i className="fa fa-database" />{' '}
{t('Sync columns from source')}
</Button>
</span>
</StyledButtonWrapper>
</ColumnButtonWrapper>
<ColumnCollectionTable
className="columns-table"

View File

@@ -31,9 +31,13 @@ export const DOCUMENTATION_LINK = supersetTextDocs
export interface IProps {
errorMessage: string;
showDbInstallInstructions: boolean;
}
const ErrorAlert: FunctionComponent<IProps> = ({ errorMessage }) => (
const ErrorAlert: FunctionComponent<IProps> = ({
errorMessage,
showDbInstallInstructions,
}) => (
<Alert
closable={false}
css={(theme: SupersetTheme) => antdWarningAlertStyles(theme)}
@@ -41,21 +45,25 @@ const ErrorAlert: FunctionComponent<IProps> = ({ errorMessage }) => (
showIcon
message={errorMessage}
description={
<>
<br />
{t(
'Database driver for importing maybe not installed. Visit the Superset documentation page for installation instructions:',
)}
<a
href={DOCUMENTATION_LINK}
target="_blank"
rel="noopener noreferrer"
className="additional-fields-alert-description"
>
{t('here')}
</a>
.
</>
showDbInstallInstructions ? (
<>
<br />
{t(
'Database driver for importing maybe not installed. Visit the Superset documentation page for installation instructions:',
)}
<a
href={DOCUMENTATION_LINK}
target="_blank"
rel="noopener noreferrer"
className="additional-fields-alert-description"
>
{t('here')}
</a>
.
</>
) : (
''
)
}
/>
);

View File

@@ -300,7 +300,12 @@ const ImportModelsModal: FunctionComponent<ImportModelsModalProps> = ({
<Button loading={importingModel}>Select file</Button>
</Upload>
</StyledInputContainer>
{errorMessage && <ErrorAlert errorMessage={errorMessage} />}
{errorMessage && (
<ErrorAlert
errorMessage={errorMessage}
showDbInstallInstructions={passwordFields.length > 0}
/>
)}
{renderPasswordFields()}
{renderOverwriteConfirmation()}
</Modal>

View File

@@ -25,8 +25,8 @@ const MenuItem = styled(AntdMenu.Item)`
}
&.ant-menu-item {
height: ${({ theme }) => theme.gridUnit * 7}px;
line-height: ${({ theme }) => theme.gridUnit * 7}px;
height: ${({ theme }) => theme.gridUnit * 8}px;
line-height: ${({ theme }) => theme.gridUnit * 8}px;
a {
border-bottom: none;
transition: background-color ${({ theme }) => theme.transitionTiming}s;

View File

@@ -49,6 +49,43 @@ const stateWithOnlyUser = {
reports: {},
};
const stateWithNonAdminUser = {
explore: {
user: {
email: 'nonadmin@test.com',
firstName: 'nonadmin',
isActive: true,
lastName: 'nonadmin',
permissions: {},
createdOn: '2022-01-12T10:17:37.801361',
roles: {
Gamme: [['no_menu_access', 'Manage']],
OtherRole: [['menu_access', 'Manage']],
},
userId: 1,
username: 'nonadmin',
},
},
reports: {},
};
const stateWithNonMenuAccessOnManage = {
explore: {
user: {
email: 'nonaccess@test.com',
firstName: 'nonaccess',
isActive: true,
lastName: 'nonaccess',
permissions: {},
createdOn: '2022-01-12T10:17:37.801361',
roles: { Gamma: [['no_menu_access', 'Manage']] },
userId: 1,
username: 'nonaccess',
},
},
reports: {},
};
const stateWithUserAndReport = {
explore: {
user: {
@@ -195,4 +232,32 @@ describe('Header Report Dropdown', () => {
});
expect(screen.getByText('Set up an email report')).toBeInTheDocument();
});
it('renders Schedule Email Reports as long as user has permission through any role', () => {
let mockedProps = createProps();
mockedProps = {
...mockedProps,
useTextMenu: true,
isDropdownVisible: true,
};
act(() => {
setup(mockedProps, stateWithNonAdminUser);
});
expect(screen.getByText('Set up an email report')).toBeInTheDocument();
});
it('do not render Schedule Email Reports if user no permission', () => {
let mockedProps = createProps();
mockedProps = {
...mockedProps,
useTextMenu: true,
isDropdownVisible: true,
};
act(() => {
setup(mockedProps, stateWithNonMenuAccessOnManage);
});
expect(
screen.queryByText('Set up an email report'),
).not.toBeInTheDocument();
});
});

View File

@@ -81,7 +81,6 @@ export interface HeaderReportProps {
setShowReportSubMenu?: (show: boolean) => void;
setIsDropdownVisible?: (visible: boolean) => void;
isDropdownVisible?: boolean;
showReportSubMenu?: boolean;
}
export default function HeaderReportDropDown({
@@ -120,7 +119,7 @@ export default function HeaderReportDropDown({
perms => perms[0] === 'menu_access' && perms[1] === 'Manage',
),
);
return permissions[0].length > 0;
return permissions.some(permission => permission.length > 0);
};
const [currentReportDeleting, setCurrentReportDeleting] =
@@ -156,10 +155,8 @@ export default function HeaderReportDropDown({
}
}, []);
const showReportSubMenu = report && setShowReportSubMenu && canAddReports();
useEffect(() => {
if (showReportSubMenu) {
if (report && setShowReportSubMenu && canAddReports()) {
setShowReportSubMenu(true);
} else if (!report && setShowReportSubMenu) {
setShowReportSubMenu(false);

View File

@@ -325,7 +325,7 @@ export function saveDashboardRequest(data, id, saveType) {
const onError = async response => {
const { error, message } = await getClientErrorObject(response);
let errorText = t('Sorry, an unknown error occured');
let errorText = t('Sorry, an unknown error occurred');
if (error) {
errorText = t(

View File

@@ -127,6 +127,8 @@ export const hydrateDashboard =
const dashboardFilters = {};
const slices = {};
const sliceIds = new Set();
const slicesFromExploreCount = new Map();
chartData.forEach(slice => {
const key = slice.slice_id;
const form_data = {
@@ -182,6 +184,10 @@ export const hydrateDashboard =
(newSlicesContainer.parents || []).slice(),
);
const count = (slicesFromExploreCount.get(slice.slice_id) ?? 0) + 1;
chartHolder.id = `${CHART_TYPE}-explore-${slice.slice_id}-${count}`;
slicesFromExploreCount.set(slice.slice_id, count);
layout[chartHolder.id] = chartHolder;
newSlicesContainer.children.push(chartHolder.id);
chartIdToLayoutId[chartHolder.meta.chartId] = chartHolder.id;

View File

@@ -41,8 +41,7 @@ export interface BCPProps {
const SUPERSET_HEADER_HEIGHT = 59;
const SIDEPANE_ADJUST_OFFSET = 4;
const SIDEPANE_HEADER_HEIGHT = 64; // including margins
const SIDEPANE_FILTERBAR_HEIGHT = 56;
const TOP_PANEL_OFFSET = 210;
const BuilderComponentPaneTabs = styled(Tabs)`
line-height: inherit;
@@ -52,20 +51,10 @@ const BuilderComponentPaneTabs = styled(Tabs)`
const DashboardBuilderSidepane = styled.div<{
topOffset: number;
}>`
height: 100%;
height: calc(100% - ${TOP_PANEL_OFFSET}px);
position: fixed;
right: 0;
top: 0;
.ReactVirtualized__List {
padding-bottom: ${({ topOffset }) =>
`${
SIDEPANE_HEADER_HEIGHT +
SIDEPANE_FILTERBAR_HEIGHT +
SIDEPANE_ADJUST_OFFSET +
topOffset
}px`};
}
`;
const BuilderComponentPane: React.FC<BCPProps> = ({

View File

@@ -174,7 +174,6 @@ class HeaderActionsDropdown extends React.PureComponent {
downloadAsImage(
SCREENSHOT_NODE_SELECTOR,
this.props.dashboardTitle,
{},
true,
)(domEvent).then(() => {
menu.style.visibility = 'visible';

File diff suppressed because one or more lines are too long

View File

@@ -129,12 +129,14 @@ const PropertiesModal = ({
return SupersetClient.get({
endpoint: `/api/v1/dashboard/related/${accessType}?q=${query}`,
}).then(response => ({
data: response.json.result.map(
(item: { value: number; text: string }) => ({
data: response.json.result
.filter((item: { extra: { active: boolean } }) =>
item.extra.active !== undefined ? item.extra.active : true,
)
.map((item: { value: number; text: string }) => ({
value: item.value,
label: item.text,
}),
),
})),
totalCount: response.json.count,
}));
},

View File

@@ -334,7 +334,7 @@ class SliceHeaderControls extends React.PureComponent<
<ModalTrigger
triggerNode={
<span data-test="view-query-menu-item">
{t('Drill to detail')}
{t('View as table')}
</span>
}
modalTitle={t('Chart Data: %s', slice.slice_name)}

View File

@@ -112,15 +112,7 @@ const StyledTabsContainer = styled.div`
export class Tabs extends React.PureComponent {
constructor(props) {
super(props);
const tabIndex = Math.max(
0,
findTabIndexByComponentId({
currentComponent: props.component,
directPathToChild: props.directPathToChild,
}),
);
const { children: tabIds } = props.component;
const activeKey = tabIds[tabIndex];
const { tabIndex, activeKey } = this.getTabInfo(props);
this.state = {
tabIndex,
@@ -155,6 +147,15 @@ export class Tabs extends React.PureComponent {
this.setState(() => ({ tabIndex: maxIndex }));
}
// reset tab index if dashboard was changed
if (nextProps.dashboardId !== this.props.dashboardId) {
const { tabIndex, activeKey } = this.getTabInfo(nextProps);
this.setState(() => ({
tabIndex,
activeKey,
}));
}
if (nextProps.isComponentVisible) {
const nextFocusComponent = getLeafComponentIdFromPath(
nextProps.directPathToChild,
@@ -187,6 +188,30 @@ export class Tabs extends React.PureComponent {
}
}
getTabInfo = props => {
let tabIndex = Math.max(
0,
findTabIndexByComponentId({
currentComponent: props.component,
directPathToChild: props.directPathToChild,
}),
);
if (tabIndex === 0 && props.activeTabs?.length) {
props.component.children.forEach((tabId, index) => {
if (tabIndex === 0 && props.activeTabs.includes(tabId)) {
tabIndex = index;
}
});
}
const { children: tabIds } = props.component;
const activeKey = tabIds[tabIndex];
return {
tabIndex,
activeKey,
};
};
showDeleteConfirmModal = key => {
const { component, deleteComponent } = this.props;
AntdModal.confirm({

View File

@@ -53,6 +53,7 @@ describe('Tabs', () => {
editMode: false,
availableColumnCount: 12,
columnWidth: 50,
dashboardId: 1,
onResizeStart() {},
onResize() {},
onResizeStop() {},
@@ -202,4 +203,15 @@ describe('Tabs', () => {
expect(modalMock.mock.calls).toHaveLength(1);
expect(deleteComponent.callCount).toBe(0);
});
it('should set new tab key if dashboardId was changed', () => {
const wrapper = shallow(<Tabs {...props} />);
expect(wrapper.state('activeKey')).toBe('TAB_ID');
wrapper.setProps({
...props,
dashboardId: 2,
component: dashboardLayoutWithTabs.present.TAB_ID,
});
expect(wrapper.state('activeKey')).toBe('ROW_ID');
});
});

View File

@@ -21,7 +21,6 @@ import React from 'react';
import { render, screen, act } from 'spec/helpers/testing-library';
import userEvent from '@testing-library/user-event';
import { SupersetClient, DatasourceType } from '@superset-ui/core';
import * as Utils from 'src/explore/exploreUtils';
import DatasourceControl from '.';
const SupersetClientGet = jest.spyOn(SupersetClient, 'get');
@@ -142,7 +141,7 @@ test('Click on Edit dataset', async () => {
test('Click on View in SQL Lab', async () => {
const props = createProps();
const postFormSpy = jest.spyOn(Utils, 'postForm');
const postFormSpy = jest.spyOn(SupersetClient, 'postForm');
postFormSpy.mockImplementation(jest.fn());
render(<DatasourceControl {...props} />, {

View File

@@ -19,7 +19,13 @@
*/
import React from 'react';
import PropTypes from 'prop-types';
import { t, styled, withTheme, DatasourceType } from '@superset-ui/core';
import {
DatasourceType,
SupersetClient,
styled,
t,
withTheme,
} from '@superset-ui/core';
import { getUrlParam } from 'src/utils/urlUtils';
import { AntdDropdown } from 'src/components';
@@ -30,13 +36,13 @@ import {
ChangeDatasourceModal,
DatasourceModal,
} from 'src/components/Datasource';
import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal';
import { postForm } from 'src/explore/exploreUtils';
import Button from 'src/components/Button';
import ErrorAlert from 'src/components/ErrorMessage/ErrorAlert';
import WarningIconWithTooltip from 'src/components/WarningIconWithTooltip';
import { URL_PARAMS } from 'src/constants';
import { isUserAdmin } from 'src/dashboard/util/findPermission';
import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal';
import { safeStringify } from 'src/utils/safeStringify';
const propTypes = {
actions: PropTypes.object.isRequired,
@@ -193,7 +199,9 @@ class DatasourceControl extends React.PureComponent {
datasourceKey: `${datasource.id}__${datasource.type}`,
sql: datasource.sql,
};
postForm('/superset/sqllab/', payload);
SupersetClient.postForm('/superset/sqllab/', {
form_data: safeStringify(payload),
});
}
break;

View File

@@ -17,6 +17,9 @@
* under the License.
*/
import React from 'react';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import { render, screen } from 'spec/helpers/testing-library';
import userEvent from '@testing-library/user-event';
import { CustomFrame } from '.';
@@ -29,8 +32,17 @@ const specificValue = '2021-03-16T00:00:00 : 2021-03-17T00:00:00';
const relativeNowValue = `DATEADD(DATETIME("now"), -7, day) : DATEADD(DATETIME("now"), 7, day)`;
const relativeTodayValue = `DATEADD(DATETIME("today"), -7, day) : DATEADD(DATETIME("today"), 7, day)`;
const mockStore = configureStore([thunk]);
const store = mockStore({
common: { locale: 'en' },
});
test('renders with default props', () => {
render(<CustomFrame onChange={jest.fn()} value={emptyValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={jest.fn()} value={emptyValue} />
</Provider>,
);
expect(screen.getByText('Configure custom time range')).toBeInTheDocument();
expect(screen.getByText('Relative Date/Time')).toBeInTheDocument();
expect(screen.getByRole('spinbutton')).toBeInTheDocument();
@@ -40,13 +52,21 @@ test('renders with default props', () => {
});
test('renders since and until with specific date/time', () => {
render(<CustomFrame onChange={jest.fn()} value={specificValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={jest.fn()} value={specificValue} />
</Provider>,
);
expect(screen.getAllByText('Specific Date/Time').length).toBe(2);
expect(screen.getAllByRole('img', { name: 'calendar' }).length).toBe(2);
});
test('renders since and until with relative date/time', () => {
render(<CustomFrame onChange={jest.fn()} value={relativeNowValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={jest.fn()} value={relativeNowValue} />
</Provider>,
);
expect(screen.getAllByText('Relative Date/Time').length).toBe(2);
expect(screen.getAllByRole('spinbutton').length).toBe(2);
expect(screen.getByText('Days Before')).toBeInTheDocument();
@@ -54,17 +74,29 @@ test('renders since and until with relative date/time', () => {
});
test('renders since and until with Now option', () => {
render(<CustomFrame onChange={jest.fn()} value={nowValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={jest.fn()} value={nowValue} />
</Provider>,
);
expect(screen.getAllByText('Now').length).toBe(2);
});
test('renders since and until with Midnight option', () => {
render(<CustomFrame onChange={jest.fn()} value={todayValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={jest.fn()} value={todayValue} />
</Provider>,
);
expect(screen.getAllByText('Midnight').length).toBe(2);
});
test('renders anchor with now option', () => {
render(<CustomFrame onChange={jest.fn()} value={relativeNowValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={jest.fn()} value={relativeNowValue} />
</Provider>,
);
expect(screen.getByText('Anchor to')).toBeInTheDocument();
expect(screen.getByRole('radio', { name: 'NOW' })).toBeInTheDocument();
expect(screen.getByRole('radio', { name: 'Date/Time' })).toBeInTheDocument();
@@ -72,7 +104,11 @@ test('renders anchor with now option', () => {
});
test('renders anchor with date/time option', () => {
render(<CustomFrame onChange={jest.fn()} value={relativeTodayValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={jest.fn()} value={relativeTodayValue} />
</Provider>,
);
expect(screen.getByText('Anchor to')).toBeInTheDocument();
expect(screen.getByRole('radio', { name: 'NOW' })).toBeInTheDocument();
expect(screen.getByRole('radio', { name: 'Date/Time' })).toBeInTheDocument();
@@ -81,21 +117,33 @@ test('renders anchor with date/time option', () => {
test('triggers onChange when the anchor changes', () => {
const onChange = jest.fn();
render(<CustomFrame onChange={onChange} value={relativeNowValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={onChange} value={relativeNowValue} />
</Provider>,
);
userEvent.click(screen.getByRole('radio', { name: 'Date/Time' }));
expect(onChange).toHaveBeenCalled();
});
test('triggers onChange when the value changes', () => {
const onChange = jest.fn();
render(<CustomFrame onChange={onChange} value={emptyValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={onChange} value={emptyValue} />
</Provider>,
);
userEvent.click(screen.getByRole('img', { name: 'up' }));
expect(onChange).toHaveBeenCalled();
});
test('triggers onChange when the mode changes', () => {
const onChange = jest.fn();
render(<CustomFrame onChange={onChange} value={todayNowValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={onChange} value={todayNowValue} />
</Provider>,
);
userEvent.click(screen.getByTitle('Midnight'));
userEvent.click(screen.getByTitle('Relative Date/Time'));
userEvent.click(screen.getAllByTitle('Now')[1]);
@@ -105,7 +153,11 @@ test('triggers onChange when the mode changes', () => {
test('triggers onChange when the grain changes', async () => {
const onChange = jest.fn();
render(<CustomFrame onChange={onChange} value={relativeNowValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={onChange} value={relativeNowValue} />
</Provider>,
);
userEvent.click(screen.getByText('Days Before'));
userEvent.click(screen.getByText('Weeks Before'));
userEvent.click(screen.getByText('Days After'));
@@ -115,7 +167,11 @@ test('triggers onChange when the grain changes', async () => {
test('triggers onChange when the date changes', async () => {
const onChange = jest.fn();
render(<CustomFrame onChange={onChange} value={specificValue} />);
render(
<Provider store={store}>
<CustomFrame onChange={onChange} value={specificValue} />
</Provider>,
);
const inputs = screen.getAllByPlaceholderText('Select date');
userEvent.click(inputs[0]);
userEvent.click(screen.getAllByText('Now')[0]);
@@ -123,3 +179,24 @@ test('triggers onChange when the date changes', async () => {
userEvent.click(screen.getAllByText('Now')[1]);
expect(onChange).toHaveBeenCalledTimes(2);
});
test('should translate Date Picker', () => {
const onChange = jest.fn();
const store = mockStore({
common: { locale: 'fr' },
});
render(
<Provider store={store}>
<CustomFrame onChange={onChange} value={specificValue} />
</Provider>,
);
userEvent.click(screen.getAllByRole('img', { name: 'calendar' })[0]);
expect(screen.getByText('2021')).toBeInTheDocument();
expect(screen.getByText('lu')).toBeInTheDocument();
expect(screen.getByText('ma')).toBeInTheDocument();
expect(screen.getByText('me')).toBeInTheDocument();
expect(screen.getByText('je')).toBeInTheDocument();
expect(screen.getByText('ve')).toBeInTheDocument();
expect(screen.getByText('sa')).toBeInTheDocument();
expect(screen.getByText('di')).toBeInTheDocument();
});

View File

@@ -17,9 +17,12 @@
* under the License.
*/
import React from 'react';
import { useSelector } from 'react-redux';
import { t } from '@superset-ui/core';
import { Moment } from 'moment';
import { isInteger } from 'lodash';
// @ts-ignore
import { locales } from 'antd/dist/antd-with-locales';
import { Col, Row } from 'src/components';
import { InputNumber } from 'src/components/Input';
import { DatePicker } from 'src/components/DatePicker';
@@ -36,11 +39,13 @@ import {
customTimeRangeDecode,
customTimeRangeEncode,
dttmToMoment,
LOCALE_MAPPING,
} from 'src/explore/components/controls/DateFilterControl/utils';
import {
CustomRangeKey,
FrameComponentProps,
} from 'src/explore/components/controls/DateFilterControl/types';
import { ExplorePageState } from 'src/explore/types';
export function CustomFrame(props: FrameComponentProps) {
const { customRange, matchedFlag } = customTimeRangeDecode(props.value);
@@ -105,6 +110,10 @@ export function CustomFrame(props: FrameComponentProps) {
}
}
const localFromFlaskBabel =
useSelector((state: ExplorePageState) => state.common.locale) || 'en';
const currentLocale = locales[LOCALE_MAPPING[localFromFlaskBabel]].DatePicker;
return (
<div data-test="custom-frame">
<div className="section-title">{t('Configure custom time range')}</div>
@@ -132,6 +141,7 @@ export function CustomFrame(props: FrameComponentProps) {
onChange('sinceDatetime', datetime.format(MOMENT_FORMAT))
}
allowClear={false}
locale={currentLocale}
/>
</Row>
)}
@@ -184,6 +194,7 @@ export function CustomFrame(props: FrameComponentProps) {
onChange('untilDatetime', datetime.format(MOMENT_FORMAT))
}
allowClear={false}
locale={currentLocale}
/>
</Row>
)}
@@ -241,6 +252,7 @@ export function CustomFrame(props: FrameComponentProps) {
}
allowClear={false}
className="control-anchor-to-datetime"
locale={currentLocale}
/>
</Col>
)}

View File

@@ -114,3 +114,20 @@ export const SEVEN_DAYS_AGO = moment()
.subtract(7, 'days')
.format(MOMENT_FORMAT);
export const MIDNIGHT = moment().utc().startOf('day').format(MOMENT_FORMAT);
export const LOCALE_MAPPING = {
en: 'en_US',
fr: 'fr_FR',
es: 'es_ES',
it: 'it_IT',
zh: 'zh_CN',
ja: 'ja_JP',
de: 'de_DE',
pt: 'pt_PT',
pt_BR: 'pt_BR',
ru: 'ru_RU',
ko: 'ko_KR',
sk: 'sk_SK',
sl: 'sl_SI',
nl: 'nl_NL',
};

View File

@@ -203,7 +203,6 @@ export const useExploreAdditionalActionsMenu = (
'.panel-body .chart-container',
// eslint-disable-next-line camelcase
slice?.slice_name ?? t('New chart'),
{},
true,
)(domEvent);
setIsDropdownVisible(false);

View File

@@ -28,15 +28,46 @@ import {
import { xAxisControl } from '../../../plugins/plugin-chart-echarts/src/controls';
describe('should collect control values and create SFD', () => {
const sharedControlsFormData = {};
Object.entries(sharedControls).forEach(([, names]) => {
names.forEach(name => {
sharedControlsFormData[name] = name;
});
});
const publicControlsFormData = Object.fromEntries(
publicControls.map((name, idx) => [[name], idx]),
);
const sharedControlsFormData = {
// metrics
metric: 'm1',
metrics: ['m2'],
metric_2: 'm3',
// columns
groupby: ['c1'],
columns: ['c2'],
groupbyColumns: ['c3'],
groupbyRows: ['c4'],
};
const publicControlsFormData = {
// time section
granularity_sqla: 'time_column',
time_grain_sqla: 'P1D',
time_range: '2000 : today',
// filters
adhoc_filters: [],
// subquery limit(series limit)
limit: 5,
// order by clause
timeseries_limit_metric: 'orderby_metric',
series_limit_metric: 'orderby_metric',
// desc or asc in order by clause
order_desc: true,
// outer query limit
row_limit: 100,
// x asxs column
x_axis: 'x_axis_column',
// advanced analytics - rolling window
rolling_type: 'sum',
rolling_periods: 1,
min_periods: 0,
// advanced analytics - time comparison
time_compare: '1 year ago',
comparison_type: 'values',
// advanced analytics - resample
resample_rule: '1D',
resample_method: 'zerofill',
};
const sourceMockFormData: QueryFormData = {
...sharedControlsFormData,
...publicControlsFormData,
@@ -90,26 +121,45 @@ describe('should collect control values and create SFD', () => {
});
});
test('collect sharedControls', () => {
const sfd = new StandardizedFormData(sourceMockFormData);
expect(sfd.dumpSFD().standardizedState.metrics).toEqual(
sharedControls.metrics.map(controlName => controlName),
);
expect(sfd.dumpSFD().standardizedState.columns).toEqual(
sharedControls.columns.map(controlName => controlName),
);
test('should avoid to overlap', () => {
const sharedControlsSet = new Set(Object.keys(sharedControls));
const publicControlsSet = new Set(publicControls);
expect(
[...sharedControlsSet].filter((x: string) => publicControlsSet.has(x)),
).toEqual([]);
});
test('should transform all publicControls', () => {
test('should collect all sharedControls', () => {
expect(Object.entries(sharedControlsFormData).length).toBe(
Object.entries(sharedControls).length,
);
const sfd = new StandardizedFormData(sourceMockFormData);
expect(sfd.serialize().standardizedState.metrics).toEqual([
'm1',
'm2',
'm3',
]);
expect(sfd.serialize().standardizedState.columns).toEqual([
'c1',
'c2',
'c3',
'c4',
]);
});
test('should transform all publicControls and sharedControls', () => {
expect(Object.entries(publicControlsFormData).length).toBe(
publicControls.length,
);
const sfd = new StandardizedFormData(sourceMockFormData);
const { formData } = sfd.transform('target_viz', sourceMockStore);
Object.entries(publicControlsFormData).forEach(([key]) => {
Object.entries(publicControlsFormData).forEach(([key, value]) => {
expect(formData).toHaveProperty(key);
expect(value).toEqual(publicControlsFormData[key]);
});
Object.entries(sharedControls).forEach(([key, value]) => {
expect(formData[key]).toEqual(value);
});
expect(formData.columns).toEqual(['c1', 'c2', 'c3', 'c4']);
expect(formData.metrics).toEqual(['m1', 'm2', 'm3']);
});
test('should inherit standardizedFormData and memorizedFormData is LIFO', () => {
@@ -157,6 +207,7 @@ describe('should transform form_data between table and bigNumberTotal', () => {
const tableVizFormData = {
datasource: '30__table',
viz_type: 'table',
granularity_sqla: 'ds',
time_grain_sqla: 'P1D',
time_range: 'No filter',
query_mode: 'aggregate',
@@ -172,7 +223,6 @@ describe('should transform form_data between table and bigNumberTotal', () => {
table_timestamp_format: 'smart_date',
show_cell_bars: true,
color_pn: true,
applied_time_extras: {},
url_params: {
form_data_key:
'p3No_sqDW7k-kMTzlBPAPd9vwp1IXTf6stbyzjlrPPa0ninvdYUUiMC6F1iKit3Y',
@@ -197,7 +247,9 @@ describe('should transform form_data between table and bigNumberTotal', () => {
dataset_id: '30',
},
},
granularity_sqla: {},
granularity_sqla: {
value: 'ds',
},
time_grain_sqla: {
value: 'P1D',
},
@@ -271,6 +323,22 @@ describe('should transform form_data between table and bigNumberTotal', () => {
);
});
test('get and has', () => {
// table -> bigNumberTotal
const sfd = new StandardizedFormData(tableVizFormData);
const { formData: bntFormData } = sfd.transform(
'big_number_total',
tableVizStore,
);
// bigNumberTotal -> table
const sfd2 = new StandardizedFormData(bntFormData);
expect(sfd2.has('big_number_total')).toBeTruthy();
expect(sfd2.has('table')).toBeTruthy();
expect(sfd2.get('big_number_total').viz_type).toBe('big_number_total');
expect(sfd2.get('table').viz_type).toBe('table');
});
test('transform', () => {
// table -> bigNumberTotal
const sfd = new StandardizedFormData(tableVizFormData);
@@ -301,7 +369,7 @@ describe('should transform form_data between table and bigNumberTotal', () => {
);
expect(tblFormData.viz_type).toBe('table');
expect(tblFormData.metrics).toEqual(['sum(sales)']);
expect(tblFormData.groupby).toEqual([]);
expect(tblFormData.groupby).toEqual(['name']);
expect(tblFormData.time_range).toBe('2021 : 2022');
});
});

View File

@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { isEmpty, intersection } from 'lodash';
import {
ensureIsArray,
getChartControlPanelRegistry,
@@ -29,38 +30,52 @@ import {
import { getControlsState } from 'src/explore/store';
import { getFormDataFromControls } from './getFormDataFromControls';
export const sharedControls: Record<keyof StandardizedState, string[]> = {
metrics: ['metric', 'metrics', 'metric_2'],
columns: ['groupby', 'columns', 'groupbyColumns', 'groupbyRows'],
export const sharedControls: Record<string, keyof StandardizedState> = {
// metrics
metric: 'metrics', // via sharedControls, scalar
metrics: 'metrics', // via sharedControls, array
metric_2: 'metrics', // via sharedControls, scalar
// columns
groupby: 'columns', // via sharedControls, array
columns: 'columns', // via sharedControls, array
groupbyColumns: 'columns', // via pivot table v2, array
groupbyRows: 'columns', // via pivot table v2, array
};
const sharedControlsMap: Record<keyof StandardizedState, string[]> = {
metrics: [],
columns: [],
};
Object.entries(sharedControls).forEach(([key, value]) =>
sharedControlsMap[value].push(key),
);
export const publicControls = [
// time section
'granularity_sqla',
'time_grain_sqla',
'time_range',
'granularity_sqla', // via sharedControls
'time_grain_sqla', // via sharedControls
'time_range', // via sharedControls
// filters
'adhoc_filters',
'adhoc_filters', // via sharedControls
// subquery limit(series limit)
'limit',
'limit', // via sharedControls
// order by clause
'timeseries_limit_metric',
'series_limit_metric',
'timeseries_limit_metric', // via sharedControls
'series_limit_metric', // via sharedControls
// desc or asc in order by clause
'order_desc',
'order_desc', // via sharedControls
// outer query limit
'row_limit',
'row_limit', // via sharedControls
// x asxs column
'x_axis',
'x_axis', // via sharedControls
// advanced analytics - rolling window
'rolling_type',
'rolling_periods',
'min_periods',
'rolling_type', // via sections.advancedAnalytics
'rolling_periods', // via sections.advancedAnalytics
'min_periods', // via sections.advancedAnalytics
// advanced analytics - time comparison
'time_compare',
'comparison_type',
'time_compare', // via sections.advancedAnalytics
'comparison_type', // via sections.advancedAnalytics
// advanced analytics - resample
'resample_rule',
'resample_method',
'resample_rule', // via sections.advancedAnalytics
'resample_method', // via sections.advancedAnalytics
];
export class StandardizedFormData {
@@ -70,20 +85,10 @@ export class StandardizedFormData {
/*
* Support form_data for smooth switching between different viz
* */
const standardizedState = {
metrics: [],
columns: [],
};
const formData = Object.freeze(sourceFormData);
const reversedMap = StandardizedFormData.getReversedMap();
Object.entries(formData).forEach(([key, value]) => {
if (reversedMap.has(key)) {
standardizedState[reversedMap.get(key)].push(...ensureIsArray(value));
}
});
const memorizedFormData = Array.isArray(
// generates an ordered map, the key is viz_type and the value is form_data. the last item is current viz
const memorizedFormData: Map<string, QueryFormData> = Array.isArray(
formData?.standardizedFormData?.memorizedFormData,
)
? new Map(formData.standardizedFormData.memorizedFormData)
@@ -93,25 +98,72 @@ export class StandardizedFormData {
memorizedFormData.delete(vizType);
}
memorizedFormData.set(vizType, formData);
// calculate sharedControls
const standardizedState =
StandardizedFormData.getStandardizedState(formData);
this.sfd = {
standardizedState,
memorizedFormData,
};
}
static getReversedMap() {
const reversedMap = new Map();
Object.entries(sharedControls).forEach(([key, names]) => {
names.forEach(name => {
reversedMap.set(name, key);
});
static getStandardizedState(formData: QueryFormData): StandardizedState {
// 1. collect current sharedControls
let currState: StandardizedState = {
metrics: [],
columns: [],
};
Object.entries(formData).forEach(([key, value]) => {
if (key in sharedControls) {
currState[sharedControls[key]].push(...ensureIsArray(value));
}
});
return reversedMap;
// 2. get previous StandardizedState
let prevState: StandardizedState = {
metrics: [],
columns: [],
};
if (
formData?.standardizedFormData?.standardizedState &&
Array.isArray(formData.standardizedFormData.standardizedState.metrics) &&
Array.isArray(formData.standardizedFormData.standardizedState.columns)
) {
prevState = formData.standardizedFormData.standardizedState;
}
// the initial prevState should equal to currentState
if (isEmpty(prevState.metrics) && isEmpty(prevState.columns)) {
prevState = currState;
}
// 3. inherit SS from previous state if current viz hasn't columns-like controls or metrics-like controls
Object.keys(sharedControlsMap).forEach(key => {
if (
isEmpty(intersection(Object.keys(formData), sharedControlsMap[key]))
) {
currState[key] = prevState[key];
}
});
// 4. update hook
const controlPanel = getChartControlPanelRegistry().get(formData.viz_type);
if (controlPanel?.updateStandardizedState) {
currState = controlPanel.updateStandardizedState(prevState, currState);
}
// 5. clear up
Object.entries(currState).forEach(([key, value]) => {
currState[key] = value.filter(Boolean);
});
return currState;
}
private getLatestFormData(vizType: string): QueryFormData {
if (this.sfd.memorizedFormData.has(vizType)) {
return this.sfd.memorizedFormData.get(vizType) as QueryFormData;
if (this.has(vizType)) {
return this.get(vizType);
}
return this.memorizedFormData.slice(-1)[0][1];
@@ -125,13 +177,21 @@ export class StandardizedFormData {
return Array.from(this.sfd.memorizedFormData.entries());
}
dumpSFD() {
serialize() {
return {
standardizedState: this.standardizedState,
memorizedFormData: this.memorizedFormData,
};
}
has(vizType: string): boolean {
return this.sfd.memorizedFormData.has(vizType);
}
get(vizType: string): QueryFormData {
return this.sfd.memorizedFormData.get(vizType) as QueryFormData;
}
transform(
targetVizType: string,
exploreState: Record<string, any>,
@@ -162,7 +222,7 @@ export class StandardizedFormData {
});
const targetFormData = {
...getFormDataFromControls(targetControlsState),
standardizedFormData: this.dumpSFD(),
standardizedFormData: this.serialize(),
};
const controlPanel = getChartControlPanelRegistry().get(targetVizType);

View File

@@ -21,13 +21,14 @@ import sinon from 'sinon';
import URI from 'urijs';
import {
buildV1ChartDataPayload,
exploreChart,
getExploreUrl,
shouldUseLegacyApi,
getSimpleSQLExpression,
shouldUseLegacyApi,
} from 'src/explore/exploreUtils';
import { DashboardStandaloneMode } from 'src/dashboard/util/constants';
import * as hostNamesConfig from 'src/utils/hostNamesConfig';
import { getChartMetadataRegistry } from '@superset-ui/core';
import { getChartMetadataRegistry, SupersetClient } from '@superset-ui/core';
describe('exploreUtils', () => {
const { location } = window;
@@ -275,4 +276,16 @@ describe('exploreUtils', () => {
);
});
});
describe('.exploreChart()', () => {
it('postForm', () => {
const postFormSpy = jest.spyOn(SupersetClient, 'postForm');
postFormSpy.mockImplementation(jest.fn());
exploreChart({
formData: { ...formData, viz_type: 'my_custom_viz' },
});
expect(postFormSpy).toBeCalledTimes(1);
});
});
});

View File

@@ -25,6 +25,7 @@ import {
ensureIsArray,
getChartBuildQueryRegistry,
getChartMetadataRegistry,
SupersetClient,
} from '@superset-ui/core';
import { availableDomains } from 'src/utils/hostNamesConfig';
import { safeStringify } from 'src/utils/safeStringify';
@@ -234,31 +235,6 @@ export const buildV1ChartDataPayload = ({
export const getLegacyEndpointType = ({ resultType, resultFormat }) =>
resultFormat === 'csv' ? resultFormat : resultType;
export function postForm(url, payload, target = '_blank') {
if (!url) {
return;
}
const hiddenForm = document.createElement('form');
hiddenForm.action = url;
hiddenForm.method = 'POST';
hiddenForm.target = target;
const token = document.createElement('input');
token.type = 'hidden';
token.name = 'csrf_token';
token.value = (document.getElementById('csrf_token') || {}).value;
hiddenForm.appendChild(token);
const data = document.createElement('input');
data.type = 'hidden';
data.name = 'form_data';
data.value = safeStringify(payload);
hiddenForm.appendChild(data);
document.body.appendChild(hiddenForm);
hiddenForm.submit();
document.body.removeChild(hiddenForm);
}
export const exportChart = ({
formData,
resultFormat = 'json',
@@ -286,7 +262,8 @@ export const exportChart = ({
ownState,
});
}
postForm(url, payload);
SupersetClient.postForm(url, { form_data: safeStringify(payload) });
};
export const exploreChart = formData => {
@@ -295,7 +272,7 @@ export const exploreChart = formData => {
endpointType: 'base',
allowDomainSharding: false,
});
postForm(url, formData);
SupersetClient.postForm(url, { form_data: safeStringify(formData) });
};
export const useDebouncedEffect = (effect, delay, deps) => {

View File

@@ -141,8 +141,8 @@ export default function exploreReducer(state = {}, action) {
if (controlName === 'metrics' && old_metrics_data && new_column_config) {
value.forEach((item, index) => {
if (
item.label !== old_metrics_data[index].label &&
!!new_column_config[old_metrics_data[index].label]
item?.label !== old_metrics_data[index]?.label &&
!!new_column_config[old_metrics_data[index]?.label]
) {
new_column_config[item.label] =
new_column_config[old_metrics_data[index].label];

View File

@@ -21,11 +21,17 @@ import {
QueryFormData,
AnnotationData,
AdhocMetric,
JsonObject,
} from '@superset-ui/core';
import { ColumnMeta, Dataset } from '@superset-ui/chart-controls';
import {
ColumnMeta,
Dataset,
ControlStateMapping,
} from '@superset-ui/chart-controls';
import { DatabaseObject } from 'src/views/CRUD/types';
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
import { toastState } from 'src/SqlLab/types';
import { Slice } from 'src/types/Chart';
export { Slice, Chart } from 'src/types/Chart';
@@ -90,3 +96,41 @@ export type ExploreRootState = {
messageToasts: toastState[];
common: {};
};
export interface ExplorePageInitialData {
dataset: Dataset;
form_data: QueryFormData;
slice: Slice | null;
}
export interface ExploreResponsePayload {
result: ExplorePageInitialData & { message: string };
}
export interface ExplorePageState {
user: UserWithPermissionsAndRoles;
common: {
flash_messages: string[];
conf: JsonObject;
locale: string;
};
charts: { [key: number]: ChartState };
datasources: { [key: string]: Dataset };
explore: {
can_add: boolean;
can_download: boolean;
can_overwrite: boolean;
isDatasourceMetaLoading: boolean;
isStarred: boolean;
triggerRender: boolean;
// duplicate datasource in exploreState - it's needed by getControlsState
datasource: Dataset;
controls: ControlStateMapping;
form_data: QueryFormData;
slice: Slice;
controlsTransferred: string[];
standalone: boolean;
force: boolean;
};
sliceEntities?: JsonObject; // propagated from Dashboard view
}

View File

@@ -71,7 +71,7 @@ export default function PluginFilterGroupBy(props: PluginFilterGroupByProps) {
}, [JSON.stringify(defaultValue), multiSelect]);
const groupbys = ensureIsArray(formData.groupby).map(getColumnLabel);
const groupby = groupbys[0].length ? groupbys[0] : null;
const groupby = groupbys[0]?.length ? groupbys[0] : null;
const withData = groupby
? data.filter(row => groupby.includes(row.column_name as string))

View File

@@ -0,0 +1,37 @@
/**
* 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.
*/
declare module 'dom-to-image-more' {
export interface Options {
filter?: ((node: Node) => boolean) | undefined;
bgcolor?: string | undefined;
width?: number | undefined;
height?: number | undefined;
style?: {} | undefined;
quality?: number | undefined;
imagePlaceholder?: string | undefined;
cacheBust?: boolean | undefined;
}
class DomToImageMore {
static toJpeg(node: Node, options?: Options): Promise<string>;
}
export default DomToImageMore;
}

View File

@@ -97,7 +97,7 @@ export function prepareCopyToClipboardTabularData(data, columns) {
// JavaScript does not maintain the order of a mixed set of keys (i.e integers and strings)
// the below function orders the keys based on the column names.
const key = columns[j].name || columns[j];
if (data[i][key]) {
if (key in data[i]) {
row[j] = data[i][key];
} else {
row[j] = data[i][parseFloat(key)];

View File

@@ -59,6 +59,16 @@ describe('utils/common', () => {
'lorem\tipsum\t\ndolor\tsit\tamet\n',
);
});
it('includes 0 values', () => {
const array = [
{ column1: 0, column2: 0 },
{ column1: 1, column2: -1, 0: 0 },
];
const column = ['column1', 'column2', '0'];
expect(prepareCopyToClipboardTabularData(array, column)).toEqual(
'0\t0\t\n1\t-1\t0\n',
);
});
});
describe('applyFormattingToTabularData', () => {
it('does not mutate empty array', () => {

View File

@@ -17,7 +17,7 @@
* under the License.
*/
import { SyntheticEvent } from 'react';
import domToImage from 'dom-to-image';
import domToImage from 'dom-to-image-more';
import kebabCase from 'lodash/kebabCase';
import { t } from '@superset-ui/core';
import { addWarningToast } from 'src/components/MessageToasts/actions';
@@ -43,7 +43,6 @@ const generateFileStem = (description: string, date = new Date()) =>
* @param selector css selector of the parent element which should be turned into image
* @param description name or a short description of what is being printed.
* Value will be normalized, and a date as well as a file extension will be added.
* @param domToImageOptions dom-to-image Options object.
* @param isExactSelector if false, searches for the closest ancestor that matches selector.
* @returns event handler
*/

View File

@@ -35,6 +35,7 @@ import setupApp from 'src/setup/setupApp';
import { routes, isFrontendRoute } from 'src/views/routes';
import { Logger } from 'src/logger/LogUtils';
import { RootContextProviders } from './RootContextProviders';
import { ScrollToTop } from './ScrollToTop';
setupApp();
@@ -60,6 +61,7 @@ const LocationPathnameLogger = () => {
const App = () => (
<Router>
<ScrollToTop />
<LocationPathnameLogger />
<RootContextProviders>
<GlobalStyles />

Some files were not shown because too many files have changed in this diff Show More