Compare commits

...

104 Commits

Author SHA1 Message Date
Ville Brofeldt
286ba5d37d update changelog and version 2021-10-11 15:32:25 +03:00
Youkyoung Cha
457a9e7d21 fix: handle mixed time-series error (#16928)
* rebase to master

* Fix line 402

Co-authored-by: yougyoung <yougyoung@pubg.com>
(cherry picked from commit 93ebe3d963)
2021-10-11 15:30:21 +03:00
Daniel Vaz Gaspar
768a1dcc12 fix: don't log invalid redirect URL send by user (#16955)
(cherry picked from commit 6d1fbb0668)
2021-10-04 18:26:56 +03:00
Ville Brofeldt
acda8a68bc fix(examples): incorrect covid row component id (#16953)
(cherry picked from commit 3dee22adfd)
2021-10-04 18:26:55 +03:00
Craig Rueda
c53eaaec57 fix(explore): Ensuring parameters key is included (#16949)
* Ensuring parameters key is included

* Fixing linter
2021-10-04 18:25:41 +03:00
Ville Brofeldt
b04a9f8ca1 fix(dashboard): recursive parent on dashboard components (#16933)
* fix(dashboard): don't add recursive parents on wrapper component

* add test

* refine logic and add new test

(cherry picked from commit a07c9d591b)
2021-10-04 16:42:49 +03:00
simcha90
ddc01a9b76 fix: Clear native filters state (#16893)
* fix:fix get permission function

* fix: clear native filters state

(cherry picked from commit 3d8cc15cba)
2021-10-04 16:42:48 +03:00
Ville Brofeldt
296b925112 update changelog and updating 2021-09-22 14:35:38 +03:00
Ville Brofeldt
73a7f6b3df fix(dataset): retain is_dttm if set on metadata sync (#16776)
(cherry picked from commit 1d5100daa2)
2021-09-22 13:31:07 +03:00
Ville Brofeldt
8e9b16d7f8 fix(pandas-postprocessing): percentage compare to use correct column (#16716)
(cherry picked from commit a839649e5c)
2021-09-22 13:31:06 +03:00
Yongjie Zhao
b95e697ef9 fix: catch exception when create connection (#16692)
* fix: catch exception when create connection

* fix lint

* added UT

(cherry picked from commit 21f98ddc21)
2021-09-22 13:25:43 +03:00
Ville Brofeldt
523c97b04f feat(jinja): improve url parameter formatting (#16711)
* feat(jinja): improve url parameter formatting

* add UPDATING.md

* fix test
2021-09-22 13:21:51 +03:00
Stephen Liu
555ba7d04b perf(dashboard): fix filter-box performance bug (#16702)
(cherry picked from commit b0ac5d1625)
2021-09-22 13:19:48 +03:00
Ville Brofeldt
693b428228 fix(explore): only refresh data panel on relevant changes (#16699)
* fix(explore): only refresh data panel on relevant changes

* add comment and supress warning

(cherry picked from commit c99cacbdd2)
2021-09-22 13:19:48 +03:00
Daniel Vaz Gaspar
711975a39f fix: don't send invalid URLs back to the user (#16687)
* fix: don't send bogus URLs back to the user

* lint, remove f string

(cherry picked from commit 5ac8ac0e6c)
2021-09-22 13:19:48 +03:00
cccs-joel
6653954b92 feat: show build number value in the About if present in the config (#14955)
(cherry picked from commit c6ac10716a)
2021-09-22 13:19:48 +03:00
天河
6994d0012e fix: fix assignment in FilterBoxViz (#16662)
* Fixing assignment.

Signed-off-by: tianhe1986 <w1s2j3229@163.com>

* Adding unit test for FilterBoxViz.

Signed-off-by: tianhe1986 <w1s2j3229@163.com>

* Reformatting with black.

Signed-off-by: tianhe1986 <w1s2j3229@163.com>

* Revert format change in other test.

Signed-off-by: tianhe1986 <w1s2j3229@163.com>

* Reformatting with the same black version with pre-commit config.

Signed-off-by: tianhe1986 <w1s2j3229@163.com>
(cherry picked from commit bb014b5131)
2021-09-22 13:19:47 +03:00
Daniel Vaz Gaspar
dd99bf2ea4 chore: Bump Flask-OpenID to 1.3.0 (#16648)
(cherry picked from commit 3d0ebd5abc)
2021-09-22 13:19:47 +03:00
Ville Brofeldt
d8cae30f8c fix(sqla): support for date adhoc filter (#16634)
(cherry picked from commit 6465ee7dbd)
2021-09-22 13:19:47 +03:00
Elizabeth Thompson
c621deeb8f fix: params in sql lab are jumpy in the ace editor (#16536)
* fix jumpy params

* remove cypress tests

(cherry picked from commit 519baa6f00)
2021-09-22 13:12:29 +03:00
Ville Brofeldt
72fe94842d fix: TemporalWrapperType string representation (#16614)
* fix: TemporalWrapperType string representation

* fix tests

(cherry picked from commit 9de2196b7f)
2021-09-22 13:12:29 +03:00
AAfghahi
4a92ba0b12 fix: queryEditor bug (#16452)
* queryEditor bug

* update tests

Co-authored-by: Elizabeth Thompson <eschutho@gmail.com>
(cherry picked from commit ee2eccdb67)
2021-09-22 13:12:28 +03:00
Beto Dealmeida
f78928eba1 fix: update table ID in query context on chart import (#16374)
* fix: update table ID in query context on chart import

* Fix test

(cherry picked from commit adebc0997b)
2021-09-22 13:12:28 +03:00
Beto Dealmeida
f96189421b fix: improve pivot post-processing (#16289)
* fix: improve pivot post-processing

* Add tests

* Trim space from column name

(cherry picked from commit ac8e54d909)
2021-09-22 13:12:28 +03:00
Beto Dealmeida
b932e5e458 fix: pivot col names in post_process (#16262)
(cherry picked from commit 542b864e61)
2021-09-22 13:12:28 +03:00
Ville Brofeldt
0f81b04235 update changelog and version 2021-09-06 14:55:01 +03:00
Duy Nguyen Hoang
acb271e5ee Remove export CSV in old filter box (#16592)
(cherry picked from commit 3fe2e6ec73)
2021-09-06 14:51:52 +03:00
simcha90
69143b60f5 feat: Experimental cross-filter plugins (#16594)
* fix:fix get permission function

* feat: add cross filter chart in charts gallery under FF

(cherry picked from commit df5c0fbce9)
2021-09-06 14:51:52 +03:00
Beto Dealmeida
241aef8617 fix: impersonate user label/tooltip (#16573)
(cherry picked from commit 9bb890ebed)
2021-09-06 14:51:52 +03:00
Rob DiCiuccio
3097f86bf8 fix: Support Jinja template functions in global async queries (#16412)
* Support Jinja template functions in async queries

* Pylint

* Add tests for async tasks

* Remove redundant has_request_context check

(cherry picked from commit 4e380db3fd)
2021-09-06 14:51:51 +03:00
Yongjie Zhao
b8c3f61097 fix: can't drop column when name overlap (#16482)
(cherry picked from commit 80c39daa85)
2021-09-06 14:51:51 +03:00
Daniel Wood
b7b35ab5ba fix: Set correct comparison operator for snowflake-sqlalchemy pinning (#16526)
(cherry picked from commit e024f8c7d6)
2021-09-06 14:51:51 +03:00
grumpy-miner
97ef416381 fix: ensure setting operator to None (#16371) (#16372)
(cherry picked from commit 1f1e2dd29a)
2021-09-06 14:51:51 +03:00
Daniel Wood
13de2422d5 fix: Pin snowflake-sqlalchemy to 1.2.4 (#16515)
* Pin snowflake-sqlalchemy to 1.2.4

* lint files

(cherry picked from commit ad8336a5b4)
2021-09-06 14:51:51 +03:00
Ville Brofeldt
9e78db81c3 fix(native-filters): add handle undefined control value gracefully (#16468)
(cherry picked from commit 147637a02d)
2021-09-06 14:51:50 +03:00
Yongjie Zhao
df5951eedf fix: prevent page crash when chart can't render (#16464)
(cherry picked from commit 577ede4b12)
2021-09-06 14:51:50 +03:00
Ville Brofeldt
ad9b0f973b fix(native-filters): handle null values in value filter (#16460)
* fix(native-filters): handle null values in value filter

* lint

(cherry picked from commit 1c71eda70f)
2021-09-06 14:51:50 +03:00
Beto Dealmeida
e9ac57e3c2 feat: add Shillelagh DB engine spec (#16416)
(cherry picked from commit 5e472980a6)
2021-09-06 14:51:50 +03:00
AAfghahi
97f86ffe1e fix: copy to Clipboard order (#16299)
* copy to Clipboard order

* centralized copyToClipboard

* fixed table order

* fixed tests

* added colnames to all viz types

* added colnames to all viz types

* added colnames to all viz types

(cherry picked from commit 631ad02a76)
2021-09-06 14:51:49 +03:00
Ville Brofeldt
cd4583331d fix: call external metadata endpoint with correct rison object (#16369)
(cherry picked from commit 575ee24a99)
2021-09-06 14:51:49 +03:00
Srini Kadamati
745325beb2 feat: Adding Rockset db engine spec (#16167)
* rockset prototype

* rockset

* t

* removed print / logging

* set a more specific version range for Rockset library

(cherry picked from commit 5f060a2227)
2021-09-06 14:51:49 +03:00
Ville Brofeldt
c92c6761b6 fix(sqlite): week grain refer to day of week (#16293)
(cherry picked from commit 5e75baf0cc)
2021-09-06 14:51:49 +03:00
Yongjie Zhao
7f799ed00a refactor: external metadata fetch API (#16193)
* refactor: external metadata api

* fix comments

* fix ut

* fix fe lint

* fix UT

* fix UT

(cherry picked from commit 6cd15d54a0)
2021-09-06 14:51:49 +03:00
Ville Brofeldt
6d19de8f66 update changelog and updating 2021-08-16 09:36:28 +03:00
Elizabeth Thompson
0e7b220917 check roles before fetching reports (#16260)
(cherry picked from commit 3709131089)
2021-08-16 08:45:36 +03:00
Beto Dealmeida
3bfb4d689a fix: pivot columns with ints for name (#16259)
(cherry picked from commit 9b2dffeb1d)
2021-08-16 08:45:36 +03:00
Phillip Kelley-Dotson
6599262cfe fix examples tab for dashboard (#16253)
(cherry picked from commit a5dbe6a14d)
2021-08-16 08:45:36 +03:00
Phillip Kelley-Dotson
9ca7e0b752 chore: bump superset-ui packages to 0.17.84 (#16251)
* initial bump

* commit pack-lock file

(cherry picked from commit f94695480a)
2021-08-16 08:45:36 +03:00
Kamil Gabryjelski
90ad28bbe6 fix(dashboard): cross filter chart highlight when filters badge icon clicked (#16233)
* fix(dashboard): cross filter chart highlight when filters badge icon pressed

* Fix tests

* Fix tests

* break out label logic

Co-authored-by: Ville Brofeldt <ville.v.brofeldt@gmail.com>
(cherry picked from commit 517a678cd7)
2021-08-16 08:45:35 +03:00
Beto Dealmeida
52f747b3fc fix: validate_parameters and query (#16241)
* fix: validate_parameters and query

* add onQueryChange

(cherry picked from commit 5d3d6b6eae)
2021-08-16 08:45:35 +03:00
Evan Rusackas
56897ab14c fix: Remove Advanced Analytics tag for 2 charts (#16240)
* removing AA tag from TimeTableChartPlugin

* package bump for echarts (removes AA tag there)

* package-lock bump for new echarts plugin

(cherry picked from commit cdcc161846)
2021-08-16 08:45:35 +03:00
Elizabeth Thompson
f36a7f0d2c fix: remove encryption from db params (#16214)
* remove encryption from db params

* Update superset/db_engine_specs/base.py

Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>

Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>
(cherry picked from commit 67c4c0116e)
2021-08-16 08:45:34 +03:00
Kamil Gabryjelski
90f432e18c fix(explore): conditional formatting value validators (#16230)
* fix(explore): conditional formatting value validators

* Fix typing, make validator more generic

* Remove commented code

(cherry picked from commit a16e290765)
2021-08-16 08:45:34 +03:00
Phillip Kelley-Dotson
f749834a1a initial fix (#16212)
(cherry picked from commit c79de7abd7)
2021-08-16 08:45:34 +03:00
Phillip Kelley-Dotson
31c57db0aa fix: sorting on "Modified By" in chart table (#16208)
* initial fix

* Update ChartList.tsx

change sort to first name

(cherry picked from commit b4555dfa4f)
2021-08-16 08:45:34 +03:00
Kamil Gabryjelski
f56a25d2d9 fix(explore): adhoc metrics popover resets label after hovering outside (#16196)
* fix(explore): adhoc metrics popover resets label after hovering outside

* Remove irrelevant tests and skip rest

* Use ensureIsArray

(cherry picked from commit ccfc95fbe6)
2021-08-16 08:45:34 +03:00
AAfghahi
185eaf7636 feat: Changing Dataset names (#16199)
* added google alert

* changing Dataset Names

(cherry picked from commit 6c304b83a9)
2021-08-16 08:45:33 +03:00
Kamil Gabryjelski
4c6a2597b4 fix(explore): metric label disappearing in some scenarios (#16190)
(cherry picked from commit 98fc29cbbb)
2021-08-16 08:45:33 +03:00
Ville Brofeldt
bc5e05b1df chore: bump superset-ui to 0.17.82 (#16186)
(cherry picked from commit 4df3672baa)
2021-08-16 08:45:33 +03:00
Elizabeth Thompson
52ae86de05 update covid dashboard (#16183)
(cherry picked from commit 3aefa6925b)
2021-08-16 08:45:33 +03:00
Phillip Kelley-Dotson
9dc44a443e fix: change listivew card layouts to the new homepage card layout (#16171)
* initial commit

* removing CardStylesOverrides (unused)

Co-authored-by: Evan Rusackas <evan@preset.io>
(cherry picked from commit a30d884cfc)
2021-08-16 08:45:32 +03:00
Junlin Chen
79ad7e8529 chore: switch back tag name to popular from highly-used (#16174)
* chore: switch back tag name to popular from highly-used

* new package lock

* new package lock with npm 7

* fix lint

* remove package changes

Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>
(cherry picked from commit 9841c78967)
2021-08-16 08:45:32 +03:00
Phillip Kelley-Dotson
a9298ecb9e fix: ensure created user entities do not show inside examples (#16176)
* initial commit

* fix lint

* Update superset-frontend/src/views/CRUD/utils.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update superset-frontend/src/views/CRUD/utils.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update superset-frontend/src/views/CRUD/utils.tsx

Co-authored-by: Evan Rusackas <evan@preset.io>

Co-authored-by: Evan Rusackas <evan@preset.io>
(cherry picked from commit a0c9b9d9c2)
2021-08-16 08:45:32 +03:00
AAfghahi
61a6795961 feat: CLI cleanup (#16178)
* added google alert

* removing datasets from cli

(cherry picked from commit 6df16c4b1f)
2021-08-16 08:45:32 +03:00
Elizabeth Thompson
93de225f19 feat: change query predicate to text (#16160)
* change query predicate to text

* make input multiline

* remove value that is too long for the downgrade

* keep logging lint rule

(cherry picked from commit 628169a171)
2021-08-16 08:45:32 +03:00
AAfghahi
2b6a8652ec feat: Added multi-regional IPs to Database Connections (#16170)
* added google alert

* multi-regional IPs

* beto revisions

* Update superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx

Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>

* Update superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx

Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>

* Update superset-frontend/src/views/CRUD/data/database/DatabaseModal/index.tsx

Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>

Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>
(cherry picked from commit 2dc0bdda5d)
2021-08-16 08:45:32 +03:00
Beto Dealmeida
077c6378ed fix: isDynamic function (#16175)
* fix: isDynamic function

* trigger tests

(cherry picked from commit 9f52c103ac)
2021-08-16 08:45:31 +03:00
Elizabeth Thompson
bab7e7a842 feat: add chart image info to reports from charts (#16158)
* refetch reports on props update

* add chart types to reports

(cherry picked from commit a3102488a1)
2021-08-16 08:45:31 +03:00
Beto Dealmeida
77415685ce fix: revert data endpoint name (#16162)
(cherry picked from commit 7b3fce7e81)
2021-08-16 08:45:31 +03:00
Hugh A. Miles II
3bf5852d67 fix: turn on SSL in database edit form show 500 error (#16151)
* fix error for query.update

* converrt before making request

* fix query params

* remove unchanged files

* this

* update tsconfig

(cherry picked from commit 3f86a54ac1)
2021-08-16 08:45:31 +03:00
Phillip Kelley-Dotson
cf922e1dcf fix: ensure that users viewing chart does not automatically save edit data (#16077)
* add last_change_at migration

* add last_saved_by db migration

* finish rest of api migration

* run precommit

* fix name

* run precommitt

* remove unused mods

* merge migrations

* Update superset/migrations/versions/6d20ba9ecb33_add_last_saved_at_to_slice_model.py

Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>

* Update superset/migrations/versions/6d20ba9ecb33_add_last_saved_at_to_slice_model.py

Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>

* Update superset/migrations/versions/f6196627326f_update_chart_permissions.py

Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>

* fix test

* precommit

* remove print

* fix test

* change test

* test commit

* test 2

* test 3

* third time the charm

* fix put req

Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>
(cherry picked from commit f0e3b68cc2)
2021-08-16 08:45:31 +03:00
Ville Brofeldt
630b49f4a3 feat(cross-filters): add support for temporal filters (#16139)
* feat(cross-filters): add support for temporal filters

* fix test

* make filter optional

* remove mocks

* fix more tests

* remove unnecessary optionality

* fix even more tests

* bump superset-ui

* add isExtra to schema

* address comments

* fix presto test

(cherry picked from commit 63ace7b288)
2021-08-16 08:45:30 +03:00
Elizabeth Thompson
a38115d517 add config to hide some user menu items (#16156)
(cherry picked from commit 5488a8a948)
2021-08-16 08:45:30 +03:00
Phillip Kelley-Dotson
74b37c3631 feat: add sticky state to tables and loadingcards state. (#16102)
* initial feat commit

* fix chart and dash rendering onload

* Update superset-frontend/src/views/CRUD/welcome/Welcome.tsx

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

* fix jumpyness and add const

Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com>
(cherry picked from commit a70248736f)
2021-08-16 08:45:30 +03:00
Michael S. Molina
2d59be3fd3 fix: Safari is not showing scroll bars in Explore (#16089)
(cherry picked from commit 273ab3d257)
2021-08-16 08:45:29 +03:00
Michael S. Molina
cfeb5580b8 fix: Multiple dashboard refresh triggers for the same session (#16094)
(cherry picked from commit 07f33998ac)
2021-08-16 08:45:29 +03:00
Yongjie Zhao
33b56bb31d fix: boolean type into SQL 'in' operator (#16107)
* fix: boolean type into SQL 'in' operator

* fix ut

* fix ut again

* update url

* remove blank line

(cherry picked from commit bb1d8fe4ef)
2021-08-16 08:45:29 +03:00
Jesse Yang
8146a5cad3 fix(dashboard): 500 error caused by data_for_slices API (#16053)
(cherry picked from commit 490890de23)
2021-08-16 08:45:29 +03:00
Ville Brofeldt
7fa5a26147 update version and changelog 2021-08-10 08:35:35 +03:00
Lyndsi Kay Williams
762945b3f3 additional params field fixed (#16161)
(cherry picked from commit 3712ee02fa)
2021-08-10 07:53:24 +03:00
AAfghahi
80d09e63b2 fix: change Alert Permissions (#16118)
* added google alert

* reworked permissions

(cherry picked from commit 606a7bf429)
2021-08-10 07:53:24 +03:00
AAfghahi
f59fe808f8 feat: better errors for report in charts and dashboard (#16131)
* added google alert

* better errors and report actions

(cherry picked from commit 5ce38839e7)
2021-08-10 07:53:24 +03:00
AAfghahi
8383b2c03d feat: added google alert to DB Connection Form (#16095)
* added google alert

* using superset_text

* made google alert public and others private

* Hugh revisions

(cherry picked from commit a51851308b)
2021-08-10 07:53:24 +03:00
Kamil Gabryjelski
951bbcf276 fix(explore): dnd error when dragging metric if multi: false (#16088)
* fix(explore): dnd error when dragging metric if multi: false

* Fix error for non-dnd controls

(cherry picked from commit b7cc89c6d4)
2021-08-10 07:53:23 +03:00
Kamil Gabryjelski
60ad321dcd chore(explore): change dnd placeholders (#16116)
* chore(explore): change dnd placeholders

* Fix tests and lint

(cherry picked from commit 6ac4f4ef2f)
2021-08-10 07:53:23 +03:00
Maxime Beauchemin
3e98e9cd79 chore: add stats logging to thumbnail api (#16133)
(cherry picked from commit df50a47777)
2021-08-10 07:53:23 +03:00
Geido
1273cfb6b5 Adjust width (#16092)
(cherry picked from commit b07c80a839)
2021-08-10 07:53:23 +03:00
Yongjie Zhao
5b04835d6e fix: virtual dataset wont work (#16132)
(cherry picked from commit 3bbcc30d69)
2021-08-10 07:53:22 +03:00
Beto Dealmeida
eb2d8882cf fix: migrate_roles (#16098)
(cherry picked from commit 28c383af68)
2021-08-10 07:53:22 +03:00
Kamil Gabryjelski
d5602a6017 fix(explore): revert dnd column dependency array change to fix infinite rerenders (#16115)
* fix(explore): revert dnd column dependency array change to fix infinite rerenders

* Remove console.log

* Remove comment

(cherry picked from commit 772da8de63)
2021-08-10 07:53:22 +03:00
ʈᵃᵢ
95679d7594 fix: move watermark to about section (#16097)
(cherry picked from commit b80f018691)
2021-08-10 07:53:22 +03:00
David Aaron Suddjian
0771475383 fix(explore): drag & drop column select component triggering onChange unnecessarily (#16073)
* fix(explore): avoid sync until after first render

* fix example

(cherry picked from commit e6292a89bb)
2021-08-10 07:53:22 +03:00
David Aaron Suddjian
a688da5aa7 fix(dashboard): user id can be null when there is an anonymous user (#15592)
(cherry picked from commit 23072161e2)
2021-08-10 07:53:21 +03:00
Beto Dealmeida
0bacb2780b fix: load tabbed dash only for tests (#16091)
(cherry picked from commit b72fd7b9f4)
2021-08-10 07:53:21 +03:00
AAfghahi
dec3ff770f change button color (#16093)
(cherry picked from commit e6274e0764)
2021-08-10 07:53:21 +03:00
Kamil Gabryjelski
4caecd7202 chore(explore): bump deckgl to 0.4.9 (#16086)
(cherry picked from commit af204ff449)
2021-08-10 07:53:21 +03:00
Beto Dealmeida
8506e4f555 chore: simplify chart permissions (#16078)
(cherry picked from commit 1dbd1e9f02)
2021-08-10 07:53:20 +03:00
Kamil Gabryjelski
b6e4b0d6c1 chore(explore): Create new entrypoints for Echarts Timeseries (#15942)
* feat(explore): Create new entrypoints for Echarts Timeseries

* Change order of some charts

* bump superset-ui

* also bump echarts package

* fix UT

Co-authored-by: Ville Brofeldt <ville.v.brofeldt@gmail.com>
(cherry picked from commit a59d458e41)
2021-08-10 07:53:20 +03:00
Hugh A. Miles II
88ee092fb2 fix: Remove grey bar for TableElement component when metadata is empty (#16054)
* create serialize json function

* remove grey space with no metadata

* remove console log

(cherry picked from commit 11a2d4dfdd)
2021-08-10 07:53:20 +03:00
Ville Brofeldt
7cfa0e7b8c feat(explore): add automatic conditional formatter to pivot table v2 (#16045)
(cherry picked from commit 7ef97a54e2)
2021-08-10 07:53:20 +03:00
AAfghahi
d320a97ae6 fix: Adding report bug (#16065)
* report add fix

* added theme

(cherry picked from commit 4359650b7d)
2021-08-10 07:53:20 +03:00
Ville Brofeldt
5c2835c266 fix(native-filters): add support for boolean cols to select (#16061)
(cherry picked from commit 86cecaeec5)
2021-08-10 07:53:19 +03:00
Michael S. Molina
aec507d35c fix: Fix the Select unselect for object values (#16062)
(cherry picked from commit 1917464d2b)
2021-08-10 07:53:19 +03:00
Ville Brofeldt
3c4f4dbf06 chore: bump superset-ui to 0.17.78 (#16058)
(cherry picked from commit 7332055ff6)
2021-08-10 07:53:19 +03:00
186 changed files with 6911 additions and 2786 deletions

View File

@@ -17,7 +17,797 @@ specific language governing permissions and limitations
under the License.
-->
## Change Log
### 1.2 (Date TBD)
### 1.3.2
**Fixes**
- [#16928](https://github.com/apache/superset/pull/16928) fix: handle mixed time-series error (@yougyoung94)
- [#16955](https://github.com/apache/superset/pull/16955) fix: don't log invalid redirect URL sent by user (@dpgaspar)
- [#16953](https://github.com/apache/superset/pull/16953) fix(examples): incorrect covid row component id (@villebro)
- [#16949](https://github.com/apache/superset/pull/16949) fix(explore): Ensuring parameters key is included (@craig-rueda)
- [#16933](https://github.com/apache/superset/pull/16933) fix(dashboard): recursive parent on dashboard components (@villebro)
- [#16893](https://github.com/apache/superset/pull/16893) fix: Clear native filters state (@simcha90)
### 1.3.1
**Features**
- [#16711](https://github.com/apache/superset/pull/16711) feat(jinja): improve url parameter formatting (@villebro)
- [#14955](https://github.com/apache/superset/pull/14955) feat: show build number value in the About if present in the config (@cccs-joel)
- [#16594](https://github.com/apache/superset/pull/16594) feat: Experimental cross-filter plugins (@simcha90)
- [#16416](https://github.com/apache/superset/pull/16416) feat: add Shillelagh DB engine spec (@betodealmeida)
- [#16167](https://github.com/apache/superset/pull/16167) feat: Adding Rockset db engine spec (@srinify)
**Fixes**
- [#16776](https://github.com/apache/superset/pull/16776) fix(dataset): retain is_dttm if set on metadata sync (@villebro)
- [#16716](https://github.com/apache/superset/pull/16716) fix(pandas-postprocessing): percentage compare to use correct column (@villebro)
- [#16692](https://github.com/apache/superset/pull/16692) fix: catch exception when create connection (@zhaoyongjie)
- [#16699](https://github.com/apache/superset/pull/16699) fix(explore): only refresh data panel on relevant changes (@villebro)
- [#16687](https://github.com/apache/superset/pull/16687) fix: don't send invalid URLs back to the user (@dpgaspar)
- [#16662](https://github.com/apache/superset/pull/16662) fix: fix assignment in FilterBoxViz (@tianhe1986)
- [#16634](https://github.com/apache/superset/pull/16634) fix(sqla): support for date adhoc filter (@villebro)
- [#16536](https://github.com/apache/superset/pull/16536) fix: params in sql lab are jumpy in the ace editor (@eschutho)
- [#16614](https://github.com/apache/superset/pull/16614) fix: TemporalWrapperType string representation (@villebro)
- [#16452](https://github.com/apache/superset/pull/16452) fix: queryEditor bug (@AAfghahi)
- [#16374](https://github.com/apache/superset/pull/16374) fix: update table ID in query context on chart import (@betodealmeida)
- [#16289](https://github.com/apache/superset/pull/16289) fix: improve pivot post-processing (@betodealmeida)
- [#16262](https://github.com/apache/superset/pull/16262) fix: pivot col names in post_process (@betodealmeida)
- [#16592](https://github.com/apache/superset/pull/16592) fix: Remove export CSV in old filter box (@duynguyenhoang)
- [#16573](https://github.com/apache/superset/pull/16573) fix: impersonate user label/tooltip (@betodealmeida)
- [#16412](https://github.com/apache/superset/pull/16412) fix: Support Jinja template functions in global async queries (@robdiciuccio)
- [#16482](https://github.com/apache/superset/pull/16482) fix: can't drop column when name overlap (@zhaoyongjie)
- [#16526](https://github.com/apache/superset/pull/16526) fix: Set correct comparison operator for snowflake-sqlalchemy pinning (@danielewood)
- [#16372](https://github.com/apache/superset/pull/16372) fix: ensure setting operator to `None` (#16371) (@grumpy-miner)
- [#16515](https://github.com/apache/superset/pull/16515) fix: Pin snowflake-sqlalchemy to 1.2.4 (@danielewood)
- [#16468](https://github.com/apache/superset/pull/16468) fix(native-filters): handle undefined control value gracefully (@villebro)
- [#16464](https://github.com/apache/superset/pull/16464) fix: prevent page crash when chart can't render (@zhaoyongjie)
- [#16460](https://github.com/apache/superset/pull/16460) fix(native-filters): handle null values in value filter (@villebro)
- [#16299](https://github.com/apache/superset/pull/16299) fix: copy to Clipboard order (@AAfghahi)
- [#16369](https://github.com/apache/superset/pull/16369) fix: call external metadata endpoint with correct rison object (@villebro)
- [#16293](https://github.com/apache/superset/pull/16293) fix(sqlite): week grain refer to day of week (@villebro)
**Others**
- [#16702](https://github.com/apache/superset/pull/16702) perf(dashboard): native filter select will be stuck if there has a filter box. (@stephenLYZ)
- [#16648](https://github.com/apache/superset/pull/16648) chore: Bump Flask-OpenID to 1.3.0 (@dpgaspar)
- [#16193](https://github.com/apache/superset/pull/16193) refactor: external metadata fetch API (@zhaoyongjie)
### 1.3.0 (Fri Aug 13 20:41:03 2021 -0700)
**Database Migrations**
- [#16160](https://github.com/apache/superset/pull/16160) feat: change query predicate to text (@eschutho)
- [#16077](https://github.com/apache/superset/pull/16077) fix: ensure that users viewing chart does not automatically save edit data (@pkdotson)
- [#16098](https://github.com/apache/superset/pull/16098) fix: migrate_roles (@betodealmeida)
- [#16078](https://github.com/apache/superset/pull/16078) chore: simplify chart permissions (@betodealmeida)
- [#16045](https://github.com/apache/superset/pull/16045) feat(explore): add automatic conditional formatter to pivot table v2 (@villebro)
- [#16038](https://github.com/apache/superset/pull/16038) fix: handle schemas_allowed_for_csv_upload serde (@betodealmeida)
- [#15909](https://github.com/apache/superset/pull/15909) fix: Ensure table uniqueness on update (@john-bodley)
- [#15747](https://github.com/apache/superset/pull/15747) feat: migration to add timezone to report schedule (@eschutho)
- [#15824](https://github.com/apache/superset/pull/15824) feat: store query context when saving charts (@betodealmeida)
- [#15822](https://github.com/apache/superset/pull/15822) fix: benchmark_migration.py needs to close sssion (@betodealmeida)
- [#15807](https://github.com/apache/superset/pull/15807) fix: migration script can't drop constraint (@betodealmeida)
- [#15791](https://github.com/apache/superset/pull/15791) fix: migration downgrade references wrong column (@betodealmeida)
- [#15725](https://github.com/apache/superset/pull/15725) fix: change to alerts_reports (@AAfghahi)
- [#15683](https://github.com/apache/superset/pull/15683) feat: add Column to reports model (@AAfghahi)
- [#15507](https://github.com/apache/superset/pull/15507) chore(python-testing): move memoized tests to unit tests (@amitmiran137)
- [#15032](https://github.com/apache/superset/pull/15032) fix: benchmark migration script (@betodealmeida)
- [#14433](https://github.com/apache/superset/pull/14433) feat: Adding configuration_method column to Database Model (@AAfghahi)
**Features**
- [#16199](https://github.com/apache/superset/pull/16199) feat: Changing Dataset names (@AAfghahi)
- [#16183](https://github.com/apache/superset/pull/16183) feat: update covid dashboard (@eschutho)
- [#16178](https://github.com/apache/superset/pull/16178) feat: CLI cleanup (@AAfghahi)
- [#16170](https://github.com/apache/superset/pull/16170) feat: Added multi-regional IPs to Database Connections (@AAfghahi)
- [#16158](https://github.com/apache/superset/pull/16158) feat: add chart image info to reports from charts (@eschutho)
- [#16139](https://github.com/apache/superset/pull/16139) feat(cross-filters): add support for temporal filters (@villebro)
- [#16156](https://github.com/apache/superset/pull/16156) feat: add config to hide some user menu items (@eschutho)
- [#16102](https://github.com/apache/superset/pull/16102) feat: add sticky state to tables and loadingcards state. (@pkdotson)
- [#16131](https://github.com/apache/superset/pull/16131) feat: better errors for report in charts and dashboard (@AAfghahi)
- [#16095](https://github.com/apache/superset/pull/16095) feat: added google alert to DB Connection Form (@AAfghahi)
- [#16052](https://github.com/apache/superset/pull/16052) feat: handle subtle bug with load-examples (@betodealmeida)
- [#16027](https://github.com/apache/superset/pull/16027) feat: Self subscribe reports (@eschutho)
- [#15887](https://github.com/apache/superset/pull/15887) feat: auto sync table columns when change dataset (@zhaoyongjie)
- [#15953](https://github.com/apache/superset/pull/15953) feat: send post-processed data in reports (@betodealmeida)
- [#15853](https://github.com/apache/superset/pull/15853) feat(homepage): add more cards and new layout (@pkdotson)
- [#15879](https://github.com/apache/superset/pull/15879) feat: post-processing for pivot table v2 (@betodealmeida)
- [#15806](https://github.com/apache/superset/pull/15806) feat: send report data to Slack (@betodealmeida)
- [#15801](https://github.com/apache/superset/pull/15801) feat(dbc ui): Adding Google Sheets Dynamic Form (@hughhhh)
- [#15920](https://github.com/apache/superset/pull/15920) feat: add timezone selector to alerts and reports (@eschutho)
- [#15805](https://github.com/apache/superset/pull/15805) feat: send data embedded in report email (@betodealmeida)
- [#15279](https://github.com/apache/superset/pull/15279) feat: run extra query on QueryObject and add compare operator for post_processing (@zhaoyongjie)
- [#15849](https://github.com/apache/superset/pull/15849) feat: add timezones to report cron (@eschutho)
- [#15846](https://github.com/apache/superset/pull/15846) feat: call screenshot to store `query_context` (@betodealmeida)
- [#15864](https://github.com/apache/superset/pull/15864) feat(explore): new datasets have autocomplete filters enabled if UX_BETA is set (@kgabryje)
- [#15843](https://github.com/apache/superset/pull/15843) feat: apply post processing to chart data (@betodealmeida)
- [#15880](https://github.com/apache/superset/pull/15880) feat: add timezone selector component (@eschutho)
- [#15882](https://github.com/apache/superset/pull/15882) feat: deprecate plugins by their metadata (@suddjian)
- [#15792](https://github.com/apache/superset/pull/15792) feat(homepage): conditionally render viewed tab and move examples to chart and dashboard table (@pkdotson)
- [#15798](https://github.com/apache/superset/pull/15798) feat(explore): default aggregate for string/numeric columns when creating metric (@kgabryje)
- [#15830](https://github.com/apache/superset/pull/15830) feat: use new API endpoint to build CSV reports (@betodealmeida)
- [#15827](https://github.com/apache/superset/pull/15827) feat: add `GET /api/v1/chart/{chart_id}/data/?format{format}` API (@betodealmeida)
- [#15719](https://github.com/apache/superset/pull/15719) feat: adding Progress Bar to Benchmark script (@AAfghahi)
- [#15712](https://github.com/apache/superset/pull/15712) feat: add show columns to Reports model (@AAfghahi)
- [#15740](https://github.com/apache/superset/pull/15740) feat(explore): UX improvements for drag'n'dropping time column (@kgabryje)
- [#15685](https://github.com/apache/superset/pull/15685) feat: add logic to creation_method for reports schedule (@AAfghahi)
- [#15711](https://github.com/apache/superset/pull/15711) feat(homepage): move savequeries table and render open conditionally (@pkdotson)
- [#15628](https://github.com/apache/superset/pull/15628) feat(menu): expand support for custom branding (@nytai)
- [#15403](https://github.com/apache/superset/pull/15403) feat: cancel db query on stop (@koszti)
- [#15651](https://github.com/apache/superset/pull/15651) feat(explore): Implement conditional formatting component (@kgabryje)
- [#15303](https://github.com/apache/superset/pull/15303) feat(explore): Upgraded viz select gallery (@suddjian)
- [#15578](https://github.com/apache/superset/pull/15578) feat: validate_parameters for GSheets (@betodealmeida)
- [#15502](https://github.com/apache/superset/pull/15502) feat: supporting jinja templating in saved metrics (@guydou)
- [#15500](https://github.com/apache/superset/pull/15500) feat(cross-filters): add option to clear set cross filters (@villebro)
- [#14775](https://github.com/apache/superset/pull/14775) feat: extra table metadata for Google Sheets (@betodealmeida)
- [#14881](https://github.com/apache/superset/pull/14881) feat: Database Connection UI (@hughhhh)
- [#15419](https://github.com/apache/superset/pull/15419) feat(native-filters): add null option to value filter (@mironovmeow)
- [#15273](https://github.com/apache/superset/pull/15273) feat: update ingress api version to v1 (@mvoitko)
- [#15454](https://github.com/apache/superset/pull/15454) feat(dashboard-groupby): group by - add ability to exclude columns (@einatbar)
- [#15482](https://github.com/apache/superset/pull/15482) feat: more SIP-40 errors (@betodealmeida)
- [#15475](https://github.com/apache/superset/pull/15475) feat(add IBM Netezza support): documentation changes for Netezza (@abhishekjog)
- [#15450](https://github.com/apache/superset/pull/15450) feat(add Netezza database): Add IBM Netezza support (@abhishekjog)
- [#15177](https://github.com/apache/superset/pull/15177) feat(trino): add support for query cost estimate #15166 (@rijojoseph07)
- [#15426](https://github.com/apache/superset/pull/15426) feat: add env vars from multiple secrets in Helm chart (@mvoitko)
- [#15436](https://github.com/apache/superset/pull/15436) feat: add more SIP-40 errors to SQL Lab (@betodealmeida)
- [#15432](https://github.com/apache/superset/pull/15432) feat: Better Errors in SQL Lab (@AAfghahi)
- [#15427](https://github.com/apache/superset/pull/15427) feat(native-filters): add support for preselect filters (@villebro)
- [#15409](https://github.com/apache/superset/pull/15409) feat: more SIP-40 error messages for SQL Lab (@betodealmeida)
- [#15153](https://github.com/apache/superset/pull/15153) feat: Adding a show all button to the column/metrics list in the explore view (Allow more than 50 columns to be shown) (@cccs-RyanS)
- [#15385](https://github.com/apache/superset/pull/15385) feat(native-filters): Hide non-numeric columns in numeric range filter (@kgabryje)
- [#15340](https://github.com/apache/superset/pull/15340) feat: add possibility to specify Service Account name for the Deployment in the Helm chart (@mvoitko)
- [#15342](https://github.com/apache/superset/pull/15342) feat: custom error SQL Lab timeout (@betodealmeida)
- [#15302](https://github.com/apache/superset/pull/15302) feat(native-filters): Set default scope by filters' and charts' datasets (@kgabryje)
- [#15206](https://github.com/apache/superset/pull/15206) feat: implement specific errors for SQL Lab (@betodealmeida)
- [#15270](https://github.com/apache/superset/pull/15270) feat(editable-title): move cursor and scroll to the end (@stephenLYZ)
- [#15157](https://github.com/apache/superset/pull/15157) feat: Synchronously return cached charts (@benjreinhart)
- [#15276](https://github.com/apache/superset/pull/15276) feat(native-filters): Show/Highlight errored/focused status (@simcha90)
- [#15253](https://github.com/apache/superset/pull/15253) feat(native-filters): add support for import/export dashboard (@villebro)
- [#15247](https://github.com/apache/superset/pull/15247) feat(sql): add jinja support to metrics and expressions (@villebro)
- [#14682](https://github.com/apache/superset/pull/14682) feat(db_engine_specs): Add quirks to support Ascend.io/HiveServer2 with Impala driver. (@danielewood)
- [#15225](https://github.com/apache/superset/pull/15225) feat(native-filters): Hide time filters if loaded datasets don't have temporal columns (@kgabryje)
- [#15222](https://github.com/apache/superset/pull/15222) feat(native-filters): Disable Apply button if filter required (@simcha90)
- [#15158](https://github.com/apache/superset/pull/15158) feat: show rich error messages on past failed queries (@betodealmeida)
- [#15121](https://github.com/apache/superset/pull/15121) feat: Select component (Iteration 1) (@geido)
- [#15188](https://github.com/apache/superset/pull/15188) feat(api): add featured datatypes to dashboard dataset ep (@villebro)
- [#14703](https://github.com/apache/superset/pull/14703) feat(helm): Make local admin optional (@danielewood)
- [#15117](https://github.com/apache/superset/pull/15117) feat(native-filters): add optional time col to time range (@villebro)
- [#15105](https://github.com/apache/superset/pull/15105) feat(webpack): configure publicPath via ASSET_BASE_URL env var (@nytai)
- [#14872](https://github.com/apache/superset/pull/14872) feat(Explore): add sort to edit dataset modal (@pkdotson)
- [#15046](https://github.com/apache/superset/pull/15046) feat(dashboard): Let users download full CSV of a table (@m-ajay)
- [#15107](https://github.com/apache/superset/pull/15107) feat: show spinner on exports (@betodealmeida)
- [#15120](https://github.com/apache/superset/pull/15120) feat(native-filters): Defer loading filters data until filter is visible (@kgabryje)
- [#15063](https://github.com/apache/superset/pull/15063) feat(native-filters): Hide filters which don't affect any visible charts (@kgabryje)
- [#15055](https://github.com/apache/superset/pull/15055) feat: spinner for imports (@betodealmeida)
- [#15057](https://github.com/apache/superset/pull/15057) feat: style import button (@betodealmeida)
- [#14868](https://github.com/apache/superset/pull/14868) feat: add more timeout configuration on screenshots (@dpgaspar)
- [#14921](https://github.com/apache/superset/pull/14921) feat(filter-box): hide druid options if druid not enabled (@villebro)
- [#14869](https://github.com/apache/superset/pull/14869) feat(native-filters): Support default to first value in select filter (@simcha90)
- [#14981](https://github.com/apache/superset/pull/14981) feat(native-filters): add markers and number formatter to range filter (@villebro)
- [#14966](https://github.com/apache/superset/pull/14966) feat(native-filters): apply cascading without instant filtering (@villebro)
- [#14863](https://github.com/apache/superset/pull/14863) feat: add type_generic and is_dttm to table metadata (@zhaoyongjie)
- [#14934](https://github.com/apache/superset/pull/14934) feat: Adding FORCE_SSL as feature flag in config.py (@AAfghahi)
- [#14933](https://github.com/apache/superset/pull/14933) feat(dashboard/native-filters): Hide filters out of scope of current tab (@kgabryje)
- [#14818](https://github.com/apache/superset/pull/14818) feat: Icon Button (@lyndsiWilliams)
- [#14832](https://github.com/apache/superset/pull/14832) feat: validation db modal (@eschutho)
- [#14765](https://github.com/apache/superset/pull/14765) feat: add support for filters in sqlLab (@cccs-jc)
- [#14784](https://github.com/apache/superset/pull/14784) feat(native-filter): Hide native filters (@simcha90)
- [#14843](https://github.com/apache/superset/pull/14843) feat(trino): add support for user impersonation (@rijojoseph07)
- [#14865](https://github.com/apache/superset/pull/14865) feat(dashboard): Highlight tabs that contain a chart in scope of focused native filter (@kgabryje)
- [#14873](https://github.com/apache/superset/pull/14873) feat(native-filters): improve inverse selection indicators (@villebro)
- [#14883](https://github.com/apache/superset/pull/14883) feat: validate database parameters (@betodealmeida)
- [#14842](https://github.com/apache/superset/pull/14842) feat(native-filters): sort selected values on blur (@villebro)
- [#14486](https://github.com/apache/superset/pull/14486) feat(native-filter): limit max tag count for selected filter values (@einatbar)
- [#14686](https://github.com/apache/superset/pull/14686) feat: ability to pull from your own docker registry using a secret (@jmistry)
- [#14803](https://github.com/apache/superset/pull/14803) feat: return parameters only for DB with default driver (@betodealmeida)
- [#14484](https://github.com/apache/superset/pull/14484) feat: chart gallery search improvement (@einatbar)
- [#14661](https://github.com/apache/superset/pull/14661) feat(explore): Remove default for time range filter and Metrics (@kgabryje)
- [#14710](https://github.com/apache/superset/pull/14710) feat(native-filters): add search all filter options (@villebro)
- [#14721](https://github.com/apache/superset/pull/14721) feat: Create BigQuery Parameters for DatabaseModal (@hughhhh)
- [#14767](https://github.com/apache/superset/pull/14767) feat: enable user impersonation in GSheets (@betodealmeida)
- [#14583](https://github.com/apache/superset/pull/14583) feat: save database with new dynamic form (@eschutho)
- [#14695](https://github.com/apache/superset/pull/14695) feat: make tabs sticky in homepage (@pkdotson)
- [#14507](https://github.com/apache/superset/pull/14507) feat: Add a remove_filter flag to the jinja filter_values function and add a new get_filters function (see issue 13943 for more details) (@cccs-jc)
- [#14693](https://github.com/apache/superset/pull/14693) feat(native-filters): Highlight charts affected by focused native filter (@kgabryje)
- [#14530](https://github.com/apache/superset/pull/14530) feat: Labeled Error-bound Input (@lyndsiWilliams)
- [#14652](https://github.com/apache/superset/pull/14652) feat: Add `make update` cmd (@hughhhh)
- [#14724](https://github.com/apache/superset/pull/14724) feat: do not redirect on 404/500 (@betodealmeida)
- [#14647](https://github.com/apache/superset/pull/14647) feat: Add headers for DatabaseModal (@hughhhh)
- [#14680](https://github.com/apache/superset/pull/14680) feat: Expanded Parameters for Mysql (@AAfghahi)
- [#14653](https://github.com/apache/superset/pull/14653) feat: Add Parameters fields to GET Database (@hughhhh)
- [#14677](https://github.com/apache/superset/pull/14677) feat: redirect 404/500 to static pages (@betodealmeida)
- [#14675](https://github.com/apache/superset/pull/14675) feat: redirect to /login when CSRF expired (@betodealmeida)
- [#14667](https://github.com/apache/superset/pull/14667) feat(dashboard): View query of the chart in dashboard (@m-ajay)
- [#14381](https://github.com/apache/superset/pull/14381) feat: Better return messages in SQL Editor (@AAfghahi)
- [#14673](https://github.com/apache/superset/pull/14673) feat: add SSL to new DB parameters (@betodealmeida)
- [#14668](https://github.com/apache/superset/pull/14668) feat: make config method optional (@AAfghahi)
- [#14451](https://github.com/apache/superset/pull/14451) feat: Configuration Method and expanded parameters for Database Model (@AAfghahi)
- [#14560](https://github.com/apache/superset/pull/14560) feat: bumping echarts plugin, adding new treemap plugin (@rusackas)
- [#14547](https://github.com/apache/superset/pull/14547) feat: add generic type to column payload (@villebro)
- [#14420](https://github.com/apache/superset/pull/14420) feat: API endpoint to validate databases using separate parameters (@betodealmeida)
- [#14527](https://github.com/apache/superset/pull/14527) feat(alert&report): set image width for email (@lilykuang)
- [#14514](https://github.com/apache/superset/pull/14514) feat: Containerize WebSocket server (@benjreinhart)
- [#14493](https://github.com/apache/superset/pull/14493) feat(explore): collapse time section if no ts columns (@villebro)
- [#14516](https://github.com/apache/superset/pull/14516) feat(viz): new tree chart (@mayurnewase)
- [#14491](https://github.com/apache/superset/pull/14491) feat: add `make format` command (@hughhhh)
- [#14470](https://github.com/apache/superset/pull/14470) feat: db modal split (@eschutho)
- [#14480](https://github.com/apache/superset/pull/14480) feat(viz): add funnel chart (@villebro)
**Fixes**
- [#16260](https://github.com/apache/superset/pull/16260) fix: check roles before fetching reports (@eschutho)
- [#16259](https://github.com/apache/superset/pull/16259) fix: pivot columns with ints for name (@betodealmeida)
- [#16253](https://github.com/apache/superset/pull/16253) fix: Homepage dashboard examples tab does not show user created objects (@pkdotson)
- [#16233](https://github.com/apache/superset/pull/16233) fix(dashboard): cross filter chart highlight when filters badge icon clicked (@kgabryje)
- [#16241](https://github.com/apache/superset/pull/16241) fix: validate_parameters and query (@betodealmeida)
- [#16240](https://github.com/apache/superset/pull/16240) fix: Remove Advanced Analytics tag for 2 charts (@rusackas)
- [#16214](https://github.com/apache/superset/pull/16214) fix: remove encryption from db params (@eschutho)
- [#16230](https://github.com/apache/superset/pull/16230) fix(explore): conditional formatting value validators (@kgabryje)
- [#16212](https://github.com/apache/superset/pull/16212) fix: example tabs filter (@pkdotson)
- [#16208](https://github.com/apache/superset/pull/16208) fix: sorting on "Modified By" in chart table (@pkdotson)
- [#16196](https://github.com/apache/superset/pull/16196) fix(explore): adhoc metrics popover resets label after hovering outside (@kgabryje)
- [#16190](https://github.com/apache/superset/pull/16190) fix(explore): metric label disappearing in some scenarios (@kgabryje)
- [#16171](https://github.com/apache/superset/pull/16171) fix: change listivew card layouts to the new homepage card layout (@pkdotson)
- [#16176](https://github.com/apache/superset/pull/16176) fix: ensure created user entities do not show inside examples (@pkdotson)
- [#16175](https://github.com/apache/superset/pull/16175) fix: isDynamic function (@betodealmeida)
- [#16162](https://github.com/apache/superset/pull/16162) fix: revert data endpoint name (@betodealmeida)
- [#16151](https://github.com/apache/superset/pull/16151) fix: turn on SSL in database edit form show 500 error (@hughhhh)
- [#16089](https://github.com/apache/superset/pull/16089) fix: Safari is not showing scroll bars in Explore (@michael-s-molina)
- [#16094](https://github.com/apache/superset/pull/16094) fix: Multiple dashboard refresh triggers for the same session (@michael-s-molina)
- [#16107](https://github.com/apache/superset/pull/16107) fix: boolean type into SQL 'in' operator (@zhaoyongjie)
- [#16053](https://github.com/apache/superset/pull/16053) fix(dashboard): 500 error caused by data_for_slices API (@ktmud)
- [#16161](https://github.com/apache/superset/pull/16161) fix: Big Query additional parameters field doesn't keep value (@lyndsiWilliams)
- [#16118](https://github.com/apache/superset/pull/16118) fix: change Alert Permissions (@AAfghahi)
- [#16088](https://github.com/apache/superset/pull/16088) fix(explore): dnd error when dragging metric if multi: false (@kgabryje)
- [#16092](https://github.com/apache/superset/pull/16092) fix(Explore): Adjust width of cell in Time-Series Table chart (@geido)
- [#16132](https://github.com/apache/superset/pull/16132) fix: virtual dataset wont work (@zhaoyongjie)
- [#16115](https://github.com/apache/superset/pull/16115) fix(explore): revert dnd column dependency array change to fix infinite rerenders (@kgabryje)
- [#16097](https://github.com/apache/superset/pull/16097) fix: move watermark to about section (@nytai)
- [#16073](https://github.com/apache/superset/pull/16073) fix(explore): drag & drop column select component triggering onChange unnecessarily (@suddjian)
- [#15592](https://github.com/apache/superset/pull/15592) fix(dashboard): user id can be null when there is an anonymous user (@suddjian)
- [#16091](https://github.com/apache/superset/pull/16091) fix: load tabbed dash only for tests (@betodealmeida)
- [#16093](https://github.com/apache/superset/pull/16093) fix: Calendar color change (@AAfghahi)
- [#16054](https://github.com/apache/superset/pull/16054) fix: Remove grey bar for TableElement component when `metadata` is empty (@hughhhh)
- [#16065](https://github.com/apache/superset/pull/16065) fix: Adding report bug (@AAfghahi)
- [#16061](https://github.com/apache/superset/pull/16061) fix(native-filters): add support for boolean cols to select (@villebro)
- [#16062](https://github.com/apache/superset/pull/16062) fix: Fixes the Select unselect for object values (@michael-s-molina)
- [#16042](https://github.com/apache/superset/pull/16042) fix: sync columns in explore page (@zhaoyongjie)
- [#16044](https://github.com/apache/superset/pull/16044) fix: make dataset update methods static instead of global (@etr2460)
- [#16037](https://github.com/apache/superset/pull/16037) fix: DB exported with incorrect type (@betodealmeida)
- [#15954](https://github.com/apache/superset/pull/15954) fix: Fix long dashboards screenshot emails (@hughhhh)
- [#16024](https://github.com/apache/superset/pull/16024) fix(explore): filter popover opening after removing a filter (@kgabryje)
- [#16035](https://github.com/apache/superset/pull/16035) fix: Modal is blinking when opening (@michael-s-molina)
- [#16022](https://github.com/apache/superset/pull/16022) fix: Select the right section when categories and tags have the same name in Viz Gallery (@geido)
- [#16026](https://github.com/apache/superset/pull/16026) fix: missing mulitiple metrics on pivot operator (@zhaoyongjie)
- [#16031](https://github.com/apache/superset/pull/16031) fix: add feature flag to header bar (@eschutho)
- [#16020](https://github.com/apache/superset/pull/16020) fix(explore): fix undefined error when using dnd (@kgabryje)
- [#16017](https://github.com/apache/superset/pull/16017) fix(native-filters): add support for versioned import/export (@villebro)
- [#15971](https://github.com/apache/superset/pull/15971) fix: Sort Metrics by ID DESC (order of creation) in the Datasource Editor (@geido)
- [#15994](https://github.com/apache/superset/pull/15994) fix(explore): highlight Run button correctly when query is stale (@suddjian)
- [#15991](https://github.com/apache/superset/pull/15991) fix: save DB with with query (@betodealmeida)
- [#15975](https://github.com/apache/superset/pull/15975) fix: eliminate cartesian product columns in pivot operator (@zhaoyongjie)
- [#15993](https://github.com/apache/superset/pull/15993) fix(dashboard): FilterBox JS error when datasets API is slow (@ktmud)
- [#15978](https://github.com/apache/superset/pull/15978) fix: three button styles to tertiary (@stellalc7)
- [#15992](https://github.com/apache/superset/pull/15992) fix(sqllab): revert #15891 to fix sqllab delay (@serenajiang)
- [#15986](https://github.com/apache/superset/pull/15986) fix: remove select width (@eschutho)
- [#15985](https://github.com/apache/superset/pull/15985) fix: Dataset field required 2 clicks to select when dashboard was empty (@michael-s-molina)
- [#15981](https://github.com/apache/superset/pull/15981) fix: DB add modal (@betodealmeida)
- [#15925](https://github.com/apache/superset/pull/15925) fix: Cancel alert is not appearing to all native filters modal fields (@michael-s-molina)
- [#15952](https://github.com/apache/superset/pull/15952) fix: Name change is not lost in left side of modal when return to edition after aborting changes (@michael-s-molina)
- [#15946](https://github.com/apache/superset/pull/15946) fix: Drag inner tab to outer tab while editing a dashboard will show an error (@michael-s-molina)
- [#15941](https://github.com/apache/superset/pull/15941) fix(dashboard): Add required message in the tooltip for the time range filter (@geido)
- [#15949](https://github.com/apache/superset/pull/15949) fix: Update gsheets mapping for documentation (@hughhhh)
- [#15933](https://github.com/apache/superset/pull/15933) fix(dashboard): Show the filters popover behind the dashboard header when scrolling (@geido)
- [#15921](https://github.com/apache/superset/pull/15921) fix(dashboard): check dashboard id before calling redux methods (@suddjian)
- [#15918](https://github.com/apache/superset/pull/15918) fix: Keep chosen columns sort option when changing a column (@geido)
- [#14969](https://github.com/apache/superset/pull/14969) fix: remove unused time column when update dataset (@zhaoyongjie)
- [#15891](https://github.com/apache/superset/pull/15891) fix: Select a query from History and Run (@graceguo-supercat)
- [#15585](https://github.com/apache/superset/pull/15585) fix: no roles being returned for anonymous user (@aspedrosa)
- [#15893](https://github.com/apache/superset/pull/15893) fix: dashboard url error when edit slug (@zhaoyongjie)
- [#15813](https://github.com/apache/superset/pull/15813) fix: Incorrect translations in Chinese in messages.po (@chuancyzhang)
- [#15841](https://github.com/apache/superset/pull/15841) fix(Explore): "Customize" tab rendering behavior (@geido)
- [#15890](https://github.com/apache/superset/pull/15890) fix(dashboard): Refresh Native Filters when Dashboard refreshes (@geido)
- [#15865](https://github.com/apache/superset/pull/15865) fix: Update Query Context on Explore loading (@hughhhh)
- [#15896](https://github.com/apache/superset/pull/15896) fix: Charts sort by in edit mode gets cut off (@michael-s-molina)
- [#15897](https://github.com/apache/superset/pull/15897) fix: Download as image of dashboard chart did not work (@michael-s-molina)
- [#15888](https://github.com/apache/superset/pull/15888) fix: Side menu of the dashboard component will scroll out of dashboard (@michael-s-molina)
- [#15889](https://github.com/apache/superset/pull/15889) fix: New time range filter initially show advance section (@michael-s-molina)
- [#15878](https://github.com/apache/superset/pull/15878) fix(15403): Re-enable canceling query for Hive and Presto (@john-bodley)
- [#15874](https://github.com/apache/superset/pull/15874) fix(15482): Propagate SupersetSecurityException error (@john-bodley)
- [#15848](https://github.com/apache/superset/pull/15848) fix: Ensure DatabaseErrorMessage works when extra is undefined (@john-bodley)
- [#15760](https://github.com/apache/superset/pull/15760) fix: Unable to create alerts/report after introduced creation_method (@duynguyenhoang)
- [#15869](https://github.com/apache/superset/pull/15869) fix: revert DEFAULT_SQLLAB_LIMIT to default (@betodealmeida)
- [#15840](https://github.com/apache/superset/pull/15840) fix(explore): show multi queries results in View query modal and data pane (@kgabryje)
- [#15709](https://github.com/apache/superset/pull/15709) fix(dashboard): Remove edit from url params when discarding changes (@geido)
- [#15820](https://github.com/apache/superset/pull/15820) fix: Render value immediately on SQL Editor for Calculated Columns in Edit Dataset modal (@geido)
- [#15817](https://github.com/apache/superset/pull/15817) fix: dataTablesPane cell render undefine when the dot in metric label (@zhaoyongjie)
- [#15803](https://github.com/apache/superset/pull/15803) fix: publish the new example dashboards (@betodealmeida)
- [#15828](https://github.com/apache/superset/pull/15828) fix: Report Schema fix (@AAfghahi)
- [#15821](https://github.com/apache/superset/pull/15821) fix(Explore): Cell height and spacing for Data panel (@geido)
- [#15786](https://github.com/apache/superset/pull/15786) fix: Bust chart cache when metric/column is changed (@etr2460)
- [#15804](https://github.com/apache/superset/pull/15804) fix: create fk model in benchmark script (@betodealmeida)
- [#15800](https://github.com/apache/superset/pull/15800) fix(dashboard): Add z-index to dashboard only when maximizing chart (@geido)
- [#15778](https://github.com/apache/superset/pull/15778) fix(dashboard): Add resize handles to right and bottom of component (@kgabryje)
- [#15715](https://github.com/apache/superset/pull/15715) fix: margin right on warning icon to 8px (@stellalc7)
- [#15770](https://github.com/apache/superset/pull/15770) fix: Edit physical dataset from the Edit Dataset modal (@geido)
- [#15781](https://github.com/apache/superset/pull/15781) fix(explore): dnd multiple columns doesn't work (@kgabryje)
- [#15752](https://github.com/apache/superset/pull/15752) fix: Revert "quote column name if db requires (#15465)" (@eschutho)
- [#15750](https://github.com/apache/superset/pull/15750) fix: Fixing `schemas_allowed_for_upload` field in database connection UX (@hughhhh)
- [#15710](https://github.com/apache/superset/pull/15710) fix(explore): Set label max width with Drag&Drop in Datasource panel (@geido)
- [#15732](https://github.com/apache/superset/pull/15732) fix(explore): wrong error message in conditional formatting (@kgabryje)
- [#15721](https://github.com/apache/superset/pull/15721) fix: Reduce js bundle size (@etr2460)
- [#15731](https://github.com/apache/superset/pull/15731) fix(explore): DndColumnSelect sometimes not working with multi: false (@kgabryje)
- [#15707](https://github.com/apache/superset/pull/15707) fix: use expected label in metrics map (@zhaoyongjie)
- [#15727](https://github.com/apache/superset/pull/15727) fix: Incorrect translations in the SQLLab in Chinese (@chuancyzhang)
- [#15610](https://github.com/apache/superset/pull/15610) fix: Add waiting time for chart animation when screenshot (@u-aiaa)
- [#15662](https://github.com/apache/superset/pull/15662) fix: Action icons on Antd Card in Homepage (@geido)
- [#15669](https://github.com/apache/superset/pull/15669) fix: no lazy translation on SupersetError (@betodealmeida)
- [#15680](https://github.com/apache/superset/pull/15680) fix: remove form title (@xiezhongfu)
- [#15668](https://github.com/apache/superset/pull/15668) fix(dashboard): Make the View Chart In Explore menu option a link (@suddjian)
- [#15634](https://github.com/apache/superset/pull/15634) fix: Show affected charts when interacting with the filters (@michael-s-molina)
- [#15661](https://github.com/apache/superset/pull/15661) fix(dashboard): Filters panel height (@geido)
- [#15670](https://github.com/apache/superset/pull/15670) fix: small fixes for Makefile (@betodealmeida)
- [#15650](https://github.com/apache/superset/pull/15650) fix: safe removal of empty tab with scoped filters (@rusackas)
- [#15645](https://github.com/apache/superset/pull/15645) fix: Fix test connection for extra fields (@hughhhh)
- [#15642](https://github.com/apache/superset/pull/15642) fix: change sslmode to require for Postgres (@hughhhh)
- [#15635](https://github.com/apache/superset/pull/15635) fix: Remove default values for engine and schemas (@hughhhh)
- [#15614](https://github.com/apache/superset/pull/15614) fix: duplicate DB names (@betodealmeida)
- [#15572](https://github.com/apache/superset/pull/15572) fix(native-filters): Fix required filters (@simcha90)
- [#15590](https://github.com/apache/superset/pull/15590) fix: avoid fetching favorite status for anonymous user (@aspedrosa)
- [#15623](https://github.com/apache/superset/pull/15623) fix: clear errors on closing DB Connection Modal (@AAfghahi)
- [#15620](https://github.com/apache/superset/pull/15620) fix: error page status codes (@etr2460)
- [#15619](https://github.com/apache/superset/pull/15619) fix: Database List Sorted (@AAfghahi)
- [#15609](https://github.com/apache/superset/pull/15609) fix: update db for expose in sqllab param (@hughhhh)
- [#15612](https://github.com/apache/superset/pull/15612) fix: Database Connection Modal - corrected tooltip alignment and info alert width (@lyndsiWilliams)
- [#15186](https://github.com/apache/superset/pull/15186) fix(dashboard-list): change name of dashboard is not reflected instantly (@stephenLYZ)
- [#15588](https://github.com/apache/superset/pull/15588) fix: Revert "fix(dashboard): Open "View Chart in Explore" in the same window" (@rusackas)
- [#15589](https://github.com/apache/superset/pull/15589) fix: Revert "chore: Bump @svgr/webpack to 5.5.0" (@rusackas)
- [#15594](https://github.com/apache/superset/pull/15594) fix: Hide Dynamic Link when editing (@AAfghahi)
- [#15595](https://github.com/apache/superset/pull/15595) fix: DBC UI tooltip aligment (@hughhhh)
- [#15587](https://github.com/apache/superset/pull/15587) fix: available endpoint showing specs without drivers (@betodealmeida)
- [#15581](https://github.com/apache/superset/pull/15581) fix: downloadasimage for dashboard (@pkdotson)
- [#15558](https://github.com/apache/superset/pull/15558) fix: revert #15405 #15435 #15444 (@betodealmeida)
- [#15546](https://github.com/apache/superset/pull/15546) fix: examples remove app context at the module level (@dpgaspar)
- [#15547](https://github.com/apache/superset/pull/15547) fix: indentation in helm chart (@mvoitko)
- [#15511](https://github.com/apache/superset/pull/15511) fix: variable context (@jeffreykoetsier)
- [#15526](https://github.com/apache/superset/pull/15526) fix: chartlist card-link to 404 (@xiezhongfu)
- [#15506](https://github.com/apache/superset/pull/15506) fix(native-filters): Fix native filters config modal (@simcha90)
- [#15527](https://github.com/apache/superset/pull/15527) fix: base requirements missing deprecation pkg (@dpgaspar)
- [#15534](https://github.com/apache/superset/pull/15534) fix: show all DBs in available endpoint (@betodealmeida)
- [#15465](https://github.com/apache/superset/pull/15465) fix: quote column name if db requires (@eschutho)
- [#15486](https://github.com/apache/superset/pull/15486) fix: Database connection R6 fixes (@lyndsiWilliams)
- [#15519](https://github.com/apache/superset/pull/15519) fix: Utilizing dashboard native filter feature flag (@john-bodley)
- [#15492](https://github.com/apache/superset/pull/15492) fix(sqllab): add new tab when add sql query (@stephenLYZ)
- [#15487](https://github.com/apache/superset/pull/15487) fix: GSheets supports JOINs (@betodealmeida)
- [#15498](https://github.com/apache/superset/pull/15498) fix(native-filters): chartsInScope were not recalculated in some cases (@kgabryje)
- [#15493](https://github.com/apache/superset/pull/15493) fix: skip set and log when use NullCache (@zhaoyongjie)
- [#15430](https://github.com/apache/superset/pull/15430) fix: bump Redis minor version (@mvoitko)
- [#15438](https://github.com/apache/superset/pull/15438) fix(native-filters): Fix clear all button (@simcha90)
- [#15318](https://github.com/apache/superset/pull/15318) fix: nvd3 bar chart sortby metric (@zhaoyongjie)
- [#15353](https://github.com/apache/superset/pull/15353) fix: raise unexpected error when orderby is empty (@zhaoyongjie)
- [#15455](https://github.com/apache/superset/pull/15455) fix(dashboard): native filters highlight with multiple tabs jumps to first tab (@kgabryje)
- [#15448](https://github.com/apache/superset/pull/15448) fix: refactor(feature_flags configurations): revert of "remove redundant addi… (@villebro)
- [#15446](https://github.com/apache/superset/pull/15446) fix(native-filters): filter type check when using experimental flag (@villebro)
- [#15422](https://github.com/apache/superset/pull/15422) fix: remove unnecessary app context on celery (@dpgaspar)
- [#15444](https://github.com/apache/superset/pull/15444) fix: import superset_config (@villebro)
- [#15314](https://github.com/apache/superset/pull/15314) fix(explore): switch to correct scheme registry for custom sequential color schemes (@sonyasha)
- [#15418](https://github.com/apache/superset/pull/15418) fix: add dashboard markdown id (@krsnik93)
- [#14778](https://github.com/apache/superset/pull/14778) fix(dashboard): Open "View Chart in Explore" in the same window (@geido)
- [#15435](https://github.com/apache/superset/pull/15435) fix: remove pydash merge (@betodealmeida)
- [#15328](https://github.com/apache/superset/pull/15328) fix: downgrade selenium log level on timeout (@dpgaspar)
- [#15429](https://github.com/apache/superset/pull/15429) fix: Select item when allowNewOptions is true and Enter is pressed (@michael-s-molina)
- [#15390](https://github.com/apache/superset/pull/15390) fix: Cascading filter popover widens automatically (@michael-s-molina)
- [#15400](https://github.com/apache/superset/pull/15400) fix: double click slq lab table cell (@graceguo-supercat)
- [#13467](https://github.com/apache/superset/pull/13467) fix: Typo in the `positionJSON` too large warning (@mrshu)
- [#15411](https://github.com/apache/superset/pull/15411) fix(native-filters): show human readable time grain label in indicator (@villebro)
- [#15297](https://github.com/apache/superset/pull/15297) fix: bootstrapScript in values.yaml of the helm chart (@mvoitko)
- [#15407](https://github.com/apache/superset/pull/15407) fix(regression): removed flask_app property can break derived class (@ofekisr)
- [#15373](https://github.com/apache/superset/pull/15373) fix: Enlarged select filter value (@michael-s-molina)
- [#15355](https://github.com/apache/superset/pull/15355) fix: follow up pr 15343 (@zhaoyongjie)
- [#15343](https://github.com/apache/superset/pull/15343) fix: missing orderby in query on the nvd3 timeseries chart (@zhaoyongjie)
- [#15351](https://github.com/apache/superset/pull/15351) fix(native-filters): show default text on filter scoping tree (@villebro)
- [#15339](https://github.com/apache/superset/pull/15339) fix(explore): Fix issue #15335 - Filter comparator losing focus (@m-ajay)
- [#15315](https://github.com/apache/superset/pull/15315) fix(api): handle undefined column type_generic (@serenajiang)
- [#15285](https://github.com/apache/superset/pull/15285) fix: Revert "build(webpack): use [contenthash] instead of [chunkhash]" (@etr2460)
- [#15319](https://github.com/apache/superset/pull/15319) fix: Capitalize time granularity weekdays (@john-bodley)
- [#15184](https://github.com/apache/superset/pull/15184) fix: datasource payload is incorrect (@betodealmeida)
- [#15324](https://github.com/apache/superset/pull/15324) fix(native-filters): Assume that temporal columns exist if column_types is undefined (@kgabryje)
- [#15305](https://github.com/apache/superset/pull/15305) fix(dashboard): Close FiltersBadge popover on window resize (@kgabryje)
- [#15207](https://github.com/apache/superset/pull/15207) fix: return query if it already exists (@eschutho)
- [#15301](https://github.com/apache/superset/pull/15301) fix: Revert "fix: SQL Lab show "Refetch Results" button while fetching new query results" (@graceguo-supercat)
- [#15238](https://github.com/apache/superset/pull/15238) fix: adding new feature flag (@AAfghahi)
- [#15295](https://github.com/apache/superset/pull/15295) fix(native-filters): improve time range filter performance (@villebro)
- [#14704](https://github.com/apache/superset/pull/14704) fix(helm): Use import_datasources.yaml, if it exists (@danielewood)
- [#15257](https://github.com/apache/superset/pull/15257) fix(native-filters): default value checkbox in config modal (@villebro)
- [#15228](https://github.com/apache/superset/pull/15228) fix: Filter bar not occupying 100% height when filter sets FF unset (@michael-s-molina)
- [#15173](https://github.com/apache/superset/pull/15173) fix(examples): calendar chart metric should be metrics (@villebro)
- [#15219](https://github.com/apache/superset/pull/15219) fix(native-filters): Fix Select `Default First Value` by clicked `Clear All` (@simcha90)
- [#15134](https://github.com/apache/superset/pull/15134) fix(dashboard): charts in nested tab is missing control and filter indicator (@stephenLYZ)
- [#15203](https://github.com/apache/superset/pull/15203) fix: fix dataset select list (@pkdotson)
- [#15198](https://github.com/apache/superset/pull/15198) fix: Fix dremio dialect not having a `driver` field (@hughhhh)
- [#15109](https://github.com/apache/superset/pull/15109) fix: SQL Lab show "Refetch Results" button while fetching new query results (@graceguo-supercat)
- [#15123](https://github.com/apache/superset/pull/15123) fix(logging): downgrade csv export log to debug (@nytai)
- [#14891](https://github.com/apache/superset/pull/14891) fix(aarch64): Bump pyarrow version to 4.0.1 (@danielewood)
- [#13614](https://github.com/apache/superset/pull/13614) fix(helm): Set working defaults for google OAuth2 example (@danielewood)
- [#15138](https://github.com/apache/superset/pull/15138) fix(dashboard): avoid duplicated toast component (@stephenLYZ)
- [#15007](https://github.com/apache/superset/pull/15007) fix(docker/helm): Make webserver query timeout adjustable (@danielewood)
- [#15181](https://github.com/apache/superset/pull/15181) fix: ignore errors in GetLog (@betodealmeida)
- [#15175](https://github.com/apache/superset/pull/15175) fix: trim string value of spaces in listview search (@pkdotson)
- [#15108](https://github.com/apache/superset/pull/15108) fix: add another wait for chart element (@eschutho)
- [#15160](https://github.com/apache/superset/pull/15160) fix: use npm v7 in docker compose (@suddjian)
- [#15163](https://github.com/apache/superset/pull/15163) fix: Presto postgres test (@betodealmeida)
- [#15172](https://github.com/apache/superset/pull/15172) fix(dashboard): Prevent rerendering View Query modal on window resize (@kgabryje)
- [#15155](https://github.com/apache/superset/pull/15155) fix: validate DB-specific parameters (@betodealmeida)
- [#15152](https://github.com/apache/superset/pull/15152) fix(typo): in contributing.md (@stellalc7)
- [#15100](https://github.com/apache/superset/pull/15100) fix: Test connection before starting on create transaction (@hughhhh)
- [#15140](https://github.com/apache/superset/pull/15140) fix: Filter overlay in dashboard when scrolling (@michael-s-molina)
- [#15146](https://github.com/apache/superset/pull/15146) fix(native-filters): Don't send unnecessary PUT request on dashboard render (@kgabryje)
- [#15139](https://github.com/apache/superset/pull/15139) fix: improve dashboard fullscreen text (@xiezhongfu)
- [#15091](https://github.com/apache/superset/pull/15091) fix(explore): fix y-axis lower bound 0 value (@stephenLYZ)
- [#15112](https://github.com/apache/superset/pull/15112) fix(native-filters): handle descending sorting correctly (@villebro)
- [#15090](https://github.com/apache/superset/pull/15090) fix(native-filters): fix Select filter crashing when changing filter type (@kgabryje)
- [#14959](https://github.com/apache/superset/pull/14959) fix: show custom errors in SQL Lab (@betodealmeida)
- [#14952](https://github.com/apache/superset/pull/14952) fix(explore): Explore page boolean filter is broken for Presto DB (@m-ajay)
- [#15084](https://github.com/apache/superset/pull/15084) fix(native-filters): empty label indicator (@villebro)
- [#15005](https://github.com/apache/superset/pull/15005) fix(native-filters): show error if default value query failed (@villebro)
- [#15015](https://github.com/apache/superset/pull/15015) fix(native-filters): remove hard-coded default time range (@villebro)
- [#15080](https://github.com/apache/superset/pull/15080) fix(dnd): add isExtra prop to Option (@villebro)
- [#15014](https://github.com/apache/superset/pull/15014) fix(datasets): consistent dataset list (@zhaoyongjie)
- [#15073](https://github.com/apache/superset/pull/15073) fix: disappearing tooltips on dashboards (@etr2460)
- [#15056](https://github.com/apache/superset/pull/15056) fix: confirm overwrite and password on import (@betodealmeida)
- [#15069](https://github.com/apache/superset/pull/15069) fix: move metric parsing to state instantiation (@eschutho)
- [#15047](https://github.com/apache/superset/pull/15047) fix: import metrics with extra (@betodealmeida)
- [#14960](https://github.com/apache/superset/pull/14960) fix: font regression in SQL Lab (@betodealmeida)
- [#15048](https://github.com/apache/superset/pull/15048) fix: edit BQ w/o encrypted_extra (@betodealmeida)
- [#15024](https://github.com/apache/superset/pull/15024) fix: Adds left padding to dashboard edit mode when filter bar is closed (@michael-s-molina)
- [#15038](https://github.com/apache/superset/pull/15038) fix(native-filters): show overridden chart name on scoping tree (@villebro)
- [#15033](https://github.com/apache/superset/pull/15033) fix(explore): Datepicker glitch on hover outside the modal (@kgabryje)
- [#14878](https://github.com/apache/superset/pull/14878) fix: Empty tab component in Dashboard cannot be deleted (@geido)
- [#14954](https://github.com/apache/superset/pull/14954) fix(explore): Long labels should wrap to new line (@geido)
- [#15031](https://github.com/apache/superset/pull/15031) fix: display all metric results in editor (@eschutho)
- [#15025](https://github.com/apache/superset/pull/15025) fix(dashboard): custom css should be removed on unmount (@suddjian)
- [#14995](https://github.com/apache/superset/pull/14995) fix: adding additional configs and colors for queryHistory (@AAfghahi)
- [#15012](https://github.com/apache/superset/pull/15012) fix(native-filters): avoid double load on select initialization (@villebro)
- [#14996](https://github.com/apache/superset/pull/14996) fix: apply template_params on external_metadata (@betodealmeida)
- [#14979](https://github.com/apache/superset/pull/14979) fix: toggle fullscreen on the dashboard (@zhaoyongjie)
- [#14984](https://github.com/apache/superset/pull/14984) fix(native-filters): Fix "undefined" error after editing a filter (@kgabryje)
- [#14982](https://github.com/apache/superset/pull/14982) fix(native-filters): remove implied fetch predicate (@villebro)
- [#14980](https://github.com/apache/superset/pull/14980) fix(native-filters): update cascaded filter state on change (@villebro)
- [#14900](https://github.com/apache/superset/pull/14900) fix(filter box): replace freeform where clause with ilike (@villebro)
- [#14971](https://github.com/apache/superset/pull/14971) fix: renamed sqllab filters to _filters (@cccs-jc)
- [#14964](https://github.com/apache/superset/pull/14964) fix(native-filters): cascading filters not rendering in tab (@villebro)
- [#14953](https://github.com/apache/superset/pull/14953) fix: additional safeguard for ResultSet (@AAfghahi)
- [#14945](https://github.com/apache/superset/pull/14945) fix: time parser truncate to first day of year/month (@zhaoyongjie)
- [#14894](https://github.com/apache/superset/pull/14894) fix: is_temporal should overwrite is_dttm (@zhaoyongjie)
- [#14902](https://github.com/apache/superset/pull/14902) fix: Remove Icon and align close button on DatasetModal (@AAfghahi)
- [#14885](https://github.com/apache/superset/pull/14885) fix: Query History cosmetic issues (@AAfghahi)
- [#14840](https://github.com/apache/superset/pull/14840) fix: Full width tabs flaky behavior (@geido)
- [#14903](https://github.com/apache/superset/pull/14903) fix: permission denied when starting docker with uid 1000 (@shawnzhu)
- [#14855](https://github.com/apache/superset/pull/14855) fix: leverage qs to create new tab (@hughhhh)
- [#14888](https://github.com/apache/superset/pull/14888) fix: Redshift parameters not rendering (@hughhhh)
- [#14839](https://github.com/apache/superset/pull/14839) fix(explore): Icons in "Customize Columns" in "Customize" tab break to a new line (@geido)
- [#14890](https://github.com/apache/superset/pull/14890) fix: time range in filter box error (@zhaoyongjie)
- [#14851](https://github.com/apache/superset/pull/14851) fix: show error on invalid import (@betodealmeida)
- [#14687](https://github.com/apache/superset/pull/14687) fix(explore): Don't run data panel query when control panel has errors (@kgabryje)
- [#14849](https://github.com/apache/superset/pull/14849) fix: DatabaseConnection Modal Margin Bottom (@AAfghahi)
- [#14756](https://github.com/apache/superset/pull/14756) fix: small code review fix (@cccs-jc)
- [#14816](https://github.com/apache/superset/pull/14816) fix(native-filter): Default value multi-select height in native filters (@geido)
- [#14841](https://github.com/apache/superset/pull/14841) fix: filterbox apply single value (@zhaoyongjie)
- [#14838](https://github.com/apache/superset/pull/14838) fix(native-filters): remove indicators outside scope (@villebro)
- [#14852](https://github.com/apache/superset/pull/14852) fix: report dropdown (@AAfghahi)
- [#14850](https://github.com/apache/superset/pull/14850) fix: Big Query Edit Form (@hughhhh)
- [#14813](https://github.com/apache/superset/pull/14813) fix: the calculated columns explicit type convert into date (@zhaoyongjie)
- [#14736](https://github.com/apache/superset/pull/14736) fix(docker): superset permissions and firefox config (@dpgaspar)
- [#14827](https://github.com/apache/superset/pull/14827) fix: OpenAPI boolean type (@betodealmeida)
- [#14822](https://github.com/apache/superset/pull/14822) fix: Fix Big Query API for POST w/ no parameters (@hughhhh)
- [#14489](https://github.com/apache/superset/pull/14489) fix: set table name width to not hide icons when name is too long (@einatbar)
- [#14788](https://github.com/apache/superset/pull/14788) fix(native-filters): fix loop caused by external state handler (@villebro)
- [#14785](https://github.com/apache/superset/pull/14785) fix(native-filters): Manage default value of filters by superset (@simcha90)
- [#14741](https://github.com/apache/superset/pull/14741) fix: Additional ResultSet tests (@AAfghahi)
- [#14528](https://github.com/apache/superset/pull/14528) fix: make dataset list sort case insensitive (@mistercrunch)
- [#14790](https://github.com/apache/superset/pull/14790) fix: use encodeURIComponent when getting table metadata (@betodealmeida)
- [#14787](https://github.com/apache/superset/pull/14787) fix: ensure engine is outside parameters (@betodealmeida)
- [#14771](https://github.com/apache/superset/pull/14771) fix: database modal should close on connect with tab layout (@eschutho)
- [#14770](https://github.com/apache/superset/pull/14770) fix: extra query in Dashboard when native filter enabled (@zhaoyongjie)
- [#14779](https://github.com/apache/superset/pull/14779) fix(native filters): Fix explore state - backend pagination checkbox in table (@simcha90)
- [#14737](https://github.com/apache/superset/pull/14737) fix(explore): DndColumnSelect not handling controls with "multi: false" (@kgabryje)
- [#14766](https://github.com/apache/superset/pull/14766) fix: add DB should not say it's Postgres (@betodealmeida)
- [#14759](https://github.com/apache/superset/pull/14759) fix: save non-parameter DBs (@betodealmeida)
- [#14717](https://github.com/apache/superset/pull/14717) fix(explore): Icons width (@geido)
- [#14742](https://github.com/apache/superset/pull/14742) fix: Set g.user to anon user in Celery (@benjreinhart)
- [#14702](https://github.com/apache/superset/pull/14702) fix: Use g.user for getting the user_id for async queries (@benjreinhart)
- [#14689](https://github.com/apache/superset/pull/14689) fix: Fixes right menu layout in different screen sizes (@michael-s-molina)
- [#14734](https://github.com/apache/superset/pull/14734) fix(dashboard): multiple query trigger when native filter enabled (@zhaoyongjie)
- [#14748](https://github.com/apache/superset/pull/14748) fix(pivot): default missing series to NULL_STRING (@villebro)
- [#14725](https://github.com/apache/superset/pull/14725) fix: homepage card layout (@pkdotson)
- [#14739](https://github.com/apache/superset/pull/14739) fix(native-filters): Unable to clear default value in native select filter (@michael-s-molina)
- [#14722](https://github.com/apache/superset/pull/14722) fix(sqllab): don't store user in localstorage (@suddjian)
- [#14708](https://github.com/apache/superset/pull/14708) fix: reindex when combine metric in legacy pivot table (@zhaoyongjie)
- [#14221](https://github.com/apache/superset/pull/14221) fix(explore): fix clearing select data causes popover dismiss (@stephenLYZ)
- [#14719](https://github.com/apache/superset/pull/14719) fix: check limiting factor on query results (@eschutho)
- [#14679](https://github.com/apache/superset/pull/14679) fix(explore): add margin to the adhoc filter value select (@suddjian)
- [#14701](https://github.com/apache/superset/pull/14701) fix(explore): checkbox form control formatting (@suddjian)
- [#14657](https://github.com/apache/superset/pull/14657) fix(explore): Filter box full chart height (@geido)
- [#14529](https://github.com/apache/superset/pull/14529) fix(Explore): fixes broken layout of tooltips (@rusackas)
- [#14698](https://github.com/apache/superset/pull/14698) fix: import dataset with extra; Vertica URI (@betodealmeida)
- [#14567](https://github.com/apache/superset/pull/14567) fix(explore): #10098 boolean filter not working (@m-ajay)
- [#14664](https://github.com/apache/superset/pull/14664) fix: Fixes email body when sharing a chart by email (@michael-s-molina)
- [#14651](https://github.com/apache/superset/pull/14651) fix(sqllab): fix error message (@stephenLYZ)
- [#14656](https://github.com/apache/superset/pull/14656) fix: Tooltip position of table title (@geido)
- [#14663](https://github.com/apache/superset/pull/14663) fix(explore): Tag component overlap (@geido)
- [#14665](https://github.com/apache/superset/pull/14665) fix(explore): Fix column number calculation (@geido)
- [#14580](https://github.com/apache/superset/pull/14580) fix: nav submenu dropdown styles (@pkdotson)
- [#14674](https://github.com/apache/superset/pull/14674) fix: Fixes group by control icon colors (@michael-s-molina)
- [#14655](https://github.com/apache/superset/pull/14655) fix: Clear search on deleting search keyword (@geido)
- [#14631](https://github.com/apache/superset/pull/14631) fix: fix submenu header double line (@pkdotson)
- [#14648](https://github.com/apache/superset/pull/14648) fix: roles undefined on public dashboards (@suddjian)
- [#14636](https://github.com/apache/superset/pull/14636) fix: DB parameter validation API (@betodealmeida)
- [#14626](https://github.com/apache/superset/pull/14626) fix(dashboard): check edit permissions correctly on frontend (@suddjian)
- [#14624](https://github.com/apache/superset/pull/14624) fix: Fixes top level tabs and automatic scroll (@michael-s-molina)
- [#14609](https://github.com/apache/superset/pull/14609) fix(explore): Missing border in the Popover SQL Editor (@geido)
- [#14120](https://github.com/apache/superset/pull/14120) fix: do not render favorite favStars and filters for anonymous user (@trepmag)
- [#14637](https://github.com/apache/superset/pull/14637) fix: Removing specific column widths, letting things flex naturally. (@rusackas)
- [#14478](https://github.com/apache/superset/pull/14478) fix: fix adhocpopovers tab animate. (@pkdotson)
- [#14627](https://github.com/apache/superset/pull/14627) fix: Pin itsdangerous (@john-bodley)
- [#14557](https://github.com/apache/superset/pull/14557) fix: Consolidating dropdown/NavDropdown user experience (removing React-bootstrap, using AntD) (@michael-s-molina)
- [#14525](https://github.com/apache/superset/pull/14525) fix: add action buttons to time series column popover (@michael-s-molina)
- [#14618](https://github.com/apache/superset/pull/14618) fix(explore): Filters Tooltip is not showing the full content (@geido)
- [#14585](https://github.com/apache/superset/pull/14585) fix: don't show busted label for unknown data types (@rusackas)
- [#14597](https://github.com/apache/superset/pull/14597) fix: error icon spacing in explore (@pkdotson)
- [#14579](https://github.com/apache/superset/pull/14579) fix: properly keep state on queryEditorSetSql on tabstateview PUT (@hughhhh)
- [#14584](https://github.com/apache/superset/pull/14584) fix: bring back dashboard perf logger (@graceguo-supercat)
- [#14582](https://github.com/apache/superset/pull/14582) fix: Adds space under dataset change warning (@rusackas)
- [#14566](https://github.com/apache/superset/pull/14566) fix: Menu does not appear on scroll in Dashboard (@geido)
- [#14551](https://github.com/apache/superset/pull/14551) fix: Column name and icons alignment in the Datasource Panel (Explore) (@geido)
- [#14561](https://github.com/apache/superset/pull/14561) fix: select country in examples chart (@betodealmeida)
- [#14495](https://github.com/apache/superset/pull/14495) fix: White space between Chart and Data panel in Explore (@geido)
- [#14539](https://github.com/apache/superset/pull/14539) fix(viz): apply uniform sorting to all nvd3 timeseries charts (@villebro)
- [#14544](https://github.com/apache/superset/pull/14544) fix: flaky test on reports (@dpgaspar)
- [#14531](https://github.com/apache/superset/pull/14531) fix: bringing metric type icon styles into SelectControl (@rusackas)
- [#14492](https://github.com/apache/superset/pull/14492) fix: Add extra check to loggerMiddleware (@geido)
- [#14506](https://github.com/apache/superset/pull/14506) fix: disable pylint error breaking CI (@hughhhh)
- [#14498](https://github.com/apache/superset/pull/14498) fix: Query History (@AAfghahi)
- [#14490](https://github.com/apache/superset/pull/14490) fix: Fix #13831 (@michael-s-molina)
- [#14467](https://github.com/apache/superset/pull/14467) fix(dashboard): Prevent char overflow when displaying chart description (@m-ajay)
- [#14481](https://github.com/apache/superset/pull/14481) fix: Explore layout is sometimes too short for the viewport (@rusackas)
- [#14471](https://github.com/apache/superset/pull/14471) fix: dashboard datasources filter None (@etr2460)
- [#14465](https://github.com/apache/superset/pull/14465) fix: Ignore database extra fields when saving (@michael-s-molina)
- [#14466](https://github.com/apache/superset/pull/14466) fix: Revert "refactor: split db modal file (#14436)" (@eschutho)
- [#13713](https://github.com/apache/superset/pull/13713) fix(explore): CSV Export Permission is incorrect on Explore page (@duynguyenhoang)
- [#14450](https://github.com/apache/superset/pull/14450) fix: dashboard changed on calculation (@etr2460)
- [#14399](https://github.com/apache/superset/pull/14399) fix(logging): log unexpected exceptions as exceptions (@nytai)
- [#14416](https://github.com/apache/superset/pull/14416) fix: fixing mysql error message (@AAfghahi)
- [#14435](https://github.com/apache/superset/pull/14435) fix: Change relationship filter for datasets to relationOneMany (@hughhhh)
- [#14348](https://github.com/apache/superset/pull/14348) fix: bootstrap data permissions (@dpgaspar)
- [#14360](https://github.com/apache/superset/pull/14360) fix: parse simple string error message values (@samtfm)
**Others**
- [#16251](https://github.com/apache/superset/pull/16251) chore: bump superset-ui packages to 0.17.84 (@pkdotson)
- [#16186](https://github.com/apache/superset/pull/16186) chore: bump superset-ui to 0.17.81 (@villebro)
- [#16174](https://github.com/apache/superset/pull/16174) chore: switch back tag name to popular from highly-used (@junlincc)
- [#16116](https://github.com/apache/superset/pull/16116) chore(explore): change dnd placeholders (@kgabryje)
- [#16133](https://github.com/apache/superset/pull/16133) chore: add stats logging to thumbnail api (@mistercrunch)
- [#16086](https://github.com/apache/superset/pull/16086) chore(explore): bump deckgl to 0.4.9 (@kgabryje)
- [#15942](https://github.com/apache/superset/pull/15942) chore(explore): Create new entrypoints for Echarts Timeseries (@kgabryje)
- [#16058](https://github.com/apache/superset/pull/16058) chore: bump superset-ui to 0.17.78 (@villebro)
- [#16039](https://github.com/apache/superset/pull/16039) chore: Revert Celery 5 upgrade (@robdiciuccio)
- [#16029](https://github.com/apache/superset/pull/16029) chore: Use datetime.timedelta for defining durations in config (@john-bodley)
- [#16034](https://github.com/apache/superset/pull/16034) chore: bump superset-ui to 0.17.77 (@villebro)
- [#16025](https://github.com/apache/superset/pull/16025) chore: Auto focus the viz gallery select (@michael-s-molina)
- [#16032](https://github.com/apache/superset/pull/16032) docs: update api 2 (@nytai)
- [#15927](https://github.com/apache/superset/pull/15927) chore: Bump Celery to 5.1.2 (@john-bodley)
- [#15936](https://github.com/apache/superset/pull/15936) docs: add instructions for how to connect to local database from docker container (@sarthak)
- [#15950](https://github.com/apache/superset/pull/15950) docs: add Hydrolix to users list (@dsztykman)
- [#16005](https://github.com/apache/superset/pull/16005) docs: update api (@nytai)
- [#15958](https://github.com/apache/superset/pull/15958) chore: change dropdown icons from horizontal to vertical (@pkdotson)
- [#15987](https://github.com/apache/superset/pull/15987) chore: Add feature flags to bug report template (@suddjian)
- [#15929](https://github.com/apache/superset/pull/15929) chore: Changes the main menu order as defined in SIP-34 (@michael-s-molina)
- [#15931](https://github.com/apache/superset/pull/15931) docs: add .asf.yaml (@nytai)
- [#15823](https://github.com/apache/superset/pull/15823) chore: Mypy fix **kwargs type (@john-bodley)
- [#15923](https://github.com/apache/superset/pull/15923) chore: bump superset-ui to 0.17.74 (@villebro)
- [#15900](https://github.com/apache/superset/pull/15900) chore: small viz gallery tweaks (@suddjian)
- [#15907](https://github.com/apache/superset/pull/15907) chore: Improves the layout of the VizTypeGallery component (@michael-s-molina)
- [#15904](https://github.com/apache/superset/pull/15904) docs: Slack integration requires "chat:write" permissions scope (@jpuris)
- [#15901](https://github.com/apache/superset/pull/15901) chore: bumping superset-ui 0.17.73 (@zhaoyongjie)
- [#15724](https://github.com/apache/superset/pull/15724) chore: freeze the UUID of examples DB (@betodealmeida)
- [#15868](https://github.com/apache/superset/pull/15868) chore: implement new mockup to the new viz gallery (2nd iteration) (@stephenLYZ)
- [#15895](https://github.com/apache/superset/pull/15895) chore: bumping superset-ui 0.17.72 (@zhaoyongjie)
- [#15885](https://github.com/apache/superset/pull/15885) chore: Adds the tests that need to be coded for the Select component (@michael-s-molina)
- [#15847](https://github.com/apache/superset/pull/15847) chore: bump typescript (@eschutho)
- [#15799](https://github.com/apache/superset/pull/15799) chore: Adds lazy loading and fetchOnlyOnSearch to the Select component (@michael-s-molina)
- [#15839](https://github.com/apache/superset/pull/15839) chore: bump superset-ui to 0.17.71 (@kgabryje)
- [#15802](https://github.com/apache/superset/pull/15802) chore: Changes the pagination API of the Select component (@michael-s-molina)
- [#15787](https://github.com/apache/superset/pull/15787) chore: remove unnecessary deps (@betodealmeida)
- [#15757](https://github.com/apache/superset/pull/15757) chore: Enforce Mypy for non-tests (@john-bodley)
- [#15775](https://github.com/apache/superset/pull/15775) chore: Pylint reenable bad-option-value message (@john-bodley)
- [#15772](https://github.com/apache/superset/pull/15772) chore: Pylint reenable non-problematic messages (@john-bodley)
- [#15788](https://github.com/apache/superset/pull/15788) chore: remove `retry` dependency in favor of `backoff` (@betodealmeida)
- [#15480](https://github.com/apache/superset/pull/15480) chore: Improves the Select component UI/UX - iteration 4 (@michael-s-molina)
- [#15794](https://github.com/apache/superset/pull/15794) docs: Adding Sunbird to users list (@kumarks1122)
- [#15795](https://github.com/apache/superset/pull/15795) chore: bump superset-ui 0.17.70 (@zhaoyongjie)
- [#15734](https://github.com/apache/superset/pull/15734) chore: Add tags to the new viz gallery (@zhaoyongjie)
- [#15776](https://github.com/apache/superset/pull/15776) docs: Update INTHEWILD.md (@MaiTiano)
- [#15555](https://github.com/apache/superset/pull/15555) chore: Select component refactoring - ColorSchemeControl - Iteration 5 (@geido)
- [#15571](https://github.com/apache/superset/pull/15571) chore: Select component refactoring - DateFilterControl - Iteration 5 (@geido)
- [#15753](https://github.com/apache/superset/pull/15753) chore: Reformat Pylint disable checks to multiline (@john-bodley)
- [#15767](https://github.com/apache/superset/pull/15767) chore: bump superet-ui 0.17.69 (@zhaoyongjie)
- [#15742](https://github.com/apache/superset/pull/15742) chore(explore): Bump plugin-chart-pivot-table (@kgabryje)
- [#15718](https://github.com/apache/superset/pull/15718) docs: update dev superset version (@nytai)
- [#15699](https://github.com/apache/superset/pull/15699) perf(dashboard): make loading datasets non-blocking (@ktmud)
- [#15714](https://github.com/apache/superset/pull/15714) chore: better copy for SQL dialog (@betodealmeida)
- [#15690](https://github.com/apache/superset/pull/15690) refactor: remove old Icon component (@pkdotson)
- [#15647](https://github.com/apache/superset/pull/15647) refactor: icon to icons for IconButton and Header component (@pkdotson)
- [#15688](https://github.com/apache/superset/pull/15688) chore: Removes unnecessary uses of preselectNativeFilters (@michael-s-molina)
- [#15648](https://github.com/apache/superset/pull/15648) perf: Refactor Dashboard.datasets_trimmed_for_slices et al. (@john-bodley)
- [#15599](https://github.com/apache/superset/pull/15599) chore: Add documentation for DB Connection UI (@hughhhh)
- [#15646](https://github.com/apache/superset/pull/15646) chore: Add metrics_b as viable metric form data parameter (@john-bodley)
- [#15665](https://github.com/apache/superset/pull/15665) refactor: icon to icons for Querytable, datasource test, and copyclipboard story (@pkdotson)
- [#15574](https://github.com/apache/superset/pull/15574) docs: Add section about updating Python requirements (@john-bodley)
- [#15583](https://github.com/apache/superset/pull/15583) chore: Preserve native filters selection after refresh (@michael-s-molina)
- [#15643](https://github.com/apache/superset/pull/15643) refactor: icon to icons for navbar (@pkdotson)
- [#15644](https://github.com/apache/superset/pull/15644) chore: Reverts reset form in native filters (@michael-s-molina)
- [#15633](https://github.com/apache/superset/pull/15633) chore: bump superset-ui/plugin-chart-echarts 0.17.65 (@zhaoyongjie)
- [#15618](https://github.com/apache/superset/pull/15618) refactor: icon to icons for syntaxhighlighter and querylist components (@pkdotson)
- [#15593](https://github.com/apache/superset/pull/15593) refactor: icon to icons for sqllab (@pkdotson)
- [#15624](https://github.com/apache/superset/pull/15624) refactor: icon to icons for homepage and card compompents (@pkdotson)
- [#15113](https://github.com/apache/superset/pull/15113) docs: fix typos in docs (@jacobhjkim)
- [#15410](https://github.com/apache/superset/pull/15410) docs: Small addition in add new databases with docker (@JavierLopezT)
- [#15607](https://github.com/apache/superset/pull/15607) docs(docker): update README (@jhult)
- [#15579](https://github.com/apache/superset/pull/15579) refactor: icon to icons for toasts component (@pkdotson)
- [#15611](https://github.com/apache/superset/pull/15611) refactor: icon to icons for annotations & css templates modals (@pkdotson)
- [#15597](https://github.com/apache/superset/pull/15597) refactor: icon to icons for Alert & Reports (@pkdotson)
- [#15608](https://github.com/apache/superset/pull/15608) chore: update dataset count badge and tash icon sizing (@andrewbastian)
- [#15615](https://github.com/apache/superset/pull/15615) chore: bumping superset-ui 0.17.64 (@zhaoyongjie)
- [#15359](https://github.com/apache/superset/pull/15359) refactor(annotation): improve annotation modal (@stephenLYZ)
- [#15568](https://github.com/apache/superset/pull/15568) refactor: icon to icons for controls (@pkdotson)
- [#15567](https://github.com/apache/superset/pull/15567) refactor: icon to icons for sliceheader component (@pkdotson)
- [#15591](https://github.com/apache/superset/pull/15591) chore: results of npm audit fix on superset-websocket (@rusackas)
- [#15557](https://github.com/apache/superset/pull/15557) refactor: icon to icons for filterbadge components (@pkdotson)
- [#15542](https://github.com/apache/superset/pull/15542) chore: add changed_on_delta_humanized field on dashboard schema (@zhaoyongjie)
- [#15528](https://github.com/apache/superset/pull/15528) refactor: icon to icons for nativeFilter components (@pkdotson)
- [#15575](https://github.com/apache/superset/pull/15575) docs: updated dremio connection string (@srinify)
- [#15551](https://github.com/apache/superset/pull/15551) refactor: icon to icons for tabs (@pkdotson)
- [#15560](https://github.com/apache/superset/pull/15560) refactor: icon to icons for headeractionsdropdown (@pkdotson)
- [#15550](https://github.com/apache/superset/pull/15550) chore: Enhance Select component (@geido)
- [#15540](https://github.com/apache/superset/pull/15540) refactor: type hints should not be load in runtime (@ofekisr)
- [#15461](https://github.com/apache/superset/pull/15461) refactor: icon to icons for popoversection (@pkdotson)
- [#15521](https://github.com/apache/superset/pull/15521) chore: add metadata for filter box plugin (@suddjian)
- [#15533](https://github.com/apache/superset/pull/15533) refactor: migrate to icons for searchinput icons (@pkdotson)
- [#15523](https://github.com/apache/superset/pull/15523) chore(release-docs): svn update after svn commit (@amitmiran137)
- [#15514](https://github.com/apache/superset/pull/15514) chore(docs): update DASHBOARD_RBAC (@amitmiran137)
- [#15451](https://github.com/apache/superset/pull/15451) refactor: icon to icons for popovercomponent (@pkdotson)
- [#15384](https://github.com/apache/superset/pull/15384) refactor(dashboard): [chart-maximize-mode]put chart full-size state in redux (@stephenLYZ)
- [#15408](https://github.com/apache/superset/pull/15408) refactor: icon to icons for listviewcomponent (@pkdotson)
- [#15398](https://github.com/apache/superset/pull/15398) refactor: icon to icons for infotooltip component (@pkdotson)
- [#15473](https://github.com/apache/superset/pull/15473) refactor(tests): decouple unittests from integration tests (@ofekisr)
- [#14102](https://github.com/apache/superset/pull/14102) docs: improve docs on running tests locally (@EBoisseauSierra)
- [#15365](https://github.com/apache/superset/pull/15365) chore(native-filters): remove instant filtering option (@villebro)
- [#15467](https://github.com/apache/superset/pull/15467) refactor: Moving get_user_datasources to security manager (@john-bodley)
- [#15466](https://github.com/apache/superset/pull/15466) refactor: icon to icons for refreshlabel (@pkdotson)
- [#15437](https://github.com/apache/superset/pull/15437) chore: simplify errors and issue codes (@betodealmeida)
- [#15424](https://github.com/apache/superset/pull/15424) perf(dashboard): Improve perf of highlighting charts in scope of active filter (@kgabryje)
- [#15413](https://github.com/apache/superset/pull/15413) chore(docs): Manage access to Dashboards (@amitmiran137)
- [#14994](https://github.com/apache/superset/pull/14994) docs: add missing logging import (@Bonifacio2)
- [#14630](https://github.com/apache/superset/pull/14630) docs: fix wrong filename mentioned in INSTALL.md (@jeverling)
- [#15401](https://github.com/apache/superset/pull/15401) refactor: icon to icons for lastupdated component (@pkdotson)
- [#15397](https://github.com/apache/superset/pull/15397) refactor: icon to icons for inderteminatecheckbox icon (@pkdotson)
- [#15433](https://github.com/apache/superset/pull/15433) chore: Uses mixed case for native filters headers (@michael-s-molina)
- [#14880](https://github.com/apache/superset/pull/14880) chore: Update CONTRIBUTING.md (@Ibby-B)
- [#15425](https://github.com/apache/superset/pull/15425) refactor(feature_flags configurations): remove defaults values (@ofekisr)
- [#15405](https://github.com/apache/superset/pull/15405) chore(refactor): load configuration and merge recursively (@ofekisr)
- [#15417](https://github.com/apache/superset/pull/15417) chore: add DASHBOARD_FILTERS_EXPERIMENTAL ff to BE default value (@amitmiran137)
- [#14278](https://github.com/apache/superset/pull/14278) docs: Update SIP template (@john-bodley)
- [#14908](https://github.com/apache/superset/pull/14908) chore: Add Slovenian (sl_SI) translation (@dkrat7)
- [#14572](https://github.com/apache/superset/pull/14572) docs: release documentation for 1.2 (@garden-of-delete)
- [#15392](https://github.com/apache/superset/pull/15392) chore: remove unused icon from ImportModal (@pkdotson)
- [#14174](https://github.com/apache/superset/pull/14174) chore: Rewrites dashboard IconButton component (@michael-s-molina)
- [#15265](https://github.com/apache/superset/pull/15265) chore: Migrates ControlHeader icons (@michael-s-molina)
- [#15363](https://github.com/apache/superset/pull/15363) chore: Improves the Select component UI/UX - iteration 3 (@michael-s-molina)
- [#15371](https://github.com/apache/superset/pull/15371) refactor: icon to icons for favestar component (@pkdotson)
- [#15380](https://github.com/apache/superset/pull/15380) refactor: icon to icons infotooltip (@pkdotson)
- [#15320](https://github.com/apache/superset/pull/15320) chore: Add Druid SQL time grains for parity with Druid NoSQL (@john-bodley)
- [#15349](https://github.com/apache/superset/pull/15349) chore: Bump @svgr/webpack to 5.5.0 (@kgabryje)
- [#15341](https://github.com/apache/superset/pull/15341) refactor: icon to icons in erroralert component (@pkdotson)
- [#15336](https://github.com/apache/superset/pull/15336) refactor: icon to icons for basicerror componenet (@pkdotson)
- [#15200](https://github.com/apache/superset/pull/15200) style(sqllab): update table count styling (@stellalc7)
- [#15306](https://github.com/apache/superset/pull/15306) refactor: Icon to icons for certifiedIcon (@pkdotson)
- [#15245](https://github.com/apache/superset/pull/15245) docs: fix hyperlink (@kamalkeshavani-aiinside)
- [#15309](https://github.com/apache/superset/pull/15309) refactor: migrate icon to icons in tablecollection (@pkdotson)
- [#15240](https://github.com/apache/superset/pull/15240) refactor: icon to icons in DatasourceEditor (@pkdotson)
- [#15235](https://github.com/apache/superset/pull/15235) chore: Improves the Select component UI/UX - iteration 2 (@michael-s-molina)
- [#15281](https://github.com/apache/superset/pull/15281) chore: encapsulate flask app into superset app (@ofekisr)
- [#15278](https://github.com/apache/superset/pull/15278) refactor: move SupersetAppInitializer to specific initialization package (@ofekisr)
- [#15223](https://github.com/apache/superset/pull/15223) chore: move calling configure_feature_flags more earlier (@ofekisr)
- [#14691](https://github.com/apache/superset/pull/14691) chore: add dry false config to CleanWebpackPlugin (@MatanBobi)
- [#15040](https://github.com/apache/superset/pull/15040) chore(docs): update releasing docs (@amitmiran137)
- [#15261](https://github.com/apache/superset/pull/15261) refactor: icon to icons for alerts component (@pkdotson)
- [#14956](https://github.com/apache/superset/pull/14956) chore: Add height/width TrashIcon SVG(18px/18px)-Edit Dataset modal (@andrewbastian)
- [#15233](https://github.com/apache/superset/pull/15233) docs: fix naming: Flask-Cache -> Flask-Caching (@shawnzhu)
- [#15220](https://github.com/apache/superset/pull/15220) chore: bump superset-ui to 0.17.58 (@villebro)
- [#14463](https://github.com/apache/superset/pull/14463) refactor: refactor Icon to Icons in sqlEditor component (@pkdotson)
- [#15192](https://github.com/apache/superset/pull/15192) chore: Changes the dashboard highlight color when selecting a filter (@michael-s-molina)
- [#15194](https://github.com/apache/superset/pull/15194) chore: Makes the refresh button only appear when the filter has a datasource (@michael-s-molina)
- [#15178](https://github.com/apache/superset/pull/15178) chore: Allows the user to force fetch the default values (@michael-s-molina)
- [#15115](https://github.com/apache/superset/pull/15115) chore: Improves the native filters bar layout (@michael-s-molina)
- [#14978](https://github.com/apache/superset/pull/14978) refactor: Convert TableElement to TypeScript (@corbinrobb)
- [#15147](https://github.com/apache/superset/pull/15147) refactor(explore): remove side effect in render from CalendarFrame for DatePicker (@MatanBobi)
- [#15168](https://github.com/apache/superset/pull/15168) chore: Bump plugin-chart-pivot-table to 0.17.57 (@kgabryje)
- [#15141](https://github.com/apache/superset/pull/15141) chore: Scrolls top when opening a select filter (@michael-s-molina)
- [#15156](https://github.com/apache/superset/pull/15156) chore: Disable comment logging for ephemeral envs (@robdiciuccio)
- [#15093](https://github.com/apache/superset/pull/15093) chore: update documentation for frontend feature flags (@etr2460)
- [#14823](https://github.com/apache/superset/pull/14823) chore: Homepage cleanup (@pkdotson)
- [#14928](https://github.com/apache/superset/pull/14928) docs: add Ontruck to users list (@rc-ontruck)
- [#15062](https://github.com/apache/superset/pull/15062) refactor: stop using deprecated celery task API (@shawnzhu)
- [#15078](https://github.com/apache/superset/pull/15078) chore: rename 'tables' to 'datasets' in error message (@mistercrunch)
- [#15017](https://github.com/apache/superset/pull/15017) chore: Improves the native filters UI/UX - iteration 7 (@michael-s-molina)
- [#15064](https://github.com/apache/superset/pull/15064) chore: Add 'fetching' status to QueryStatus (@AAfghahi)
- [#14942](https://github.com/apache/superset/pull/14942) build(webpack): use [contenthash] instead of [chunkhash] (@nytai)
- [#15019](https://github.com/apache/superset/pull/15019) docs: jinja (@srinify)
- [#15053](https://github.com/apache/superset/pull/15053) refactor: adopt --app as celery global option (@shawnzhu)
- [#15044](https://github.com/apache/superset/pull/15044) docs: provide config option for openid-connect provider #13948 (@shawnzhu)
- [#14870](https://github.com/apache/superset/pull/14870) chore: Bulk Select X Button Alignment (@lyndsiWilliams)
- [#14846](https://github.com/apache/superset/pull/14846) chore: Align down icons on header (@lyndsiWilliams)
- [#15013](https://github.com/apache/superset/pull/15013) refactor: Add "is_select_query" method to base engine spec to unlock non-SQL dialects (@Ceridan)
- [#15021](https://github.com/apache/superset/pull/15021) chore: rename 'Source' to 'Database' for consistency (@mistercrunch)
- [#15016](https://github.com/apache/superset/pull/15016) chore(ci): fix ci conflict (@villebro)
- [#15010](https://github.com/apache/superset/pull/15010) docs: required information for OAuth2 configuration (@shawnzhu)
- [#14990](https://github.com/apache/superset/pull/14990) docs: Updates index.mdx (@brian-childress)
- [#14950](https://github.com/apache/superset/pull/14950) docs: fix typos on installation.rst (@Bonifacio2)
- [#14997](https://github.com/apache/superset/pull/14997) docs: fix custom oauth config (@nytai)
- [#14830](https://github.com/apache/superset/pull/14830) refactor: Convert TableElement.jsx component from class to functional with hooks (@corbinrobb)
- [#14968](https://github.com/apache/superset/pull/14968) chore: bump superset-ui to 0.17.53 (@villebro)
- [#14932](https://github.com/apache/superset/pull/14932) chore: Improves the native filters UI/UX - iteration 6 (@michael-s-molina)
- [#14896](https://github.com/apache/superset/pull/14896) chore: customize adhoc filter icon and fix creatable label (@villebro)
- [#14919](https://github.com/apache/superset/pull/14919) test(native-filters): add test for Select filter (@simcha90)
- [#14907](https://github.com/apache/superset/pull/14907) chore: upgrade bleach dependency (@willbarrett)
- [#14906](https://github.com/apache/superset/pull/14906) chore: Upgrade cryptography package (@willbarrett)
- [#14871](https://github.com/apache/superset/pull/14871) chore: Bump @superset-ui/legacy-preset-chart-deckgl to 0.4.7 (@kgabryje)
- [#14882](https://github.com/apache/superset/pull/14882) chore: Improves the native filters UI/UX - iteration 5 (@michael-s-molina)
- [#14854](https://github.com/apache/superset/pull/14854) chore: Improves the native filters UI/UX - iteration 4 (@michael-s-molina)
- [#14814](https://github.com/apache/superset/pull/14814) chore(native-filters): introduce experimental feature flag (@villebro)
- [#14824](https://github.com/apache/superset/pull/14824) chore: Improves the native filters UI/UX - iteration 3 (@michael-s-molina)
- [#14826](https://github.com/apache/superset/pull/14826) chore: Update docs on MySQL recommended driver (@betodealmeida)
- [#14752](https://github.com/apache/superset/pull/14752) chore: added BasicParametersMixin to Redshift (@AAfghahi)
- [#14753](https://github.com/apache/superset/pull/14753) chore: Improves the native filters UI/UX - iteration 2 (@michael-s-molina)
- [#14762](https://github.com/apache/superset/pull/14762) other: "revert fix(dashboard): multiple query trigger when native filter enabled" (@zhaoyongjie)
- [#14199](https://github.com/apache/superset/pull/14199) chore: Removes ColorSchemeControl.less (@michael-s-molina)
- [#14684](https://github.com/apache/superset/pull/14684) chore: Perform feature/config condition checks at request time (@benjreinhart)
- [#14723](https://github.com/apache/superset/pull/14723) chore: Update pull request template (@suddjian)
- [#14714](https://github.com/apache/superset/pull/14714) chore: Improves the native filters UI/UX - iteration 1 (@michael-s-molina)
- [#14448](https://github.com/apache/superset/pull/14448) chore: Removes less from SliceAdder (@michael-s-molina)
- [#14558](https://github.com/apache/superset/pull/14558) docs: Update docs for GLOBAL_ASYNC_QUERIES_TRANSPORT (@robdiciuccio)
- [#14650](https://github.com/apache/superset/pull/14650) chore: Register dynamic plugins and add feature checks (@benjreinhart)
- [#14469](https://github.com/apache/superset/pull/14469) chore: Replaces Icon with Icons component - iteration 1 (@michael-s-molina)
- [#14644](https://github.com/apache/superset/pull/14644) chore: Adjust language picker theme to match other menus (@michael-s-molina)
- [#14641](https://github.com/apache/superset/pull/14641) chore: Removes react-bootstrap and react-bootstrap-slider (@michael-s-molina)
- [#14568](https://github.com/apache/superset/pull/14568) chore: use before_request hook for dynamic routes (@benjreinhart)
- [#14633](https://github.com/apache/superset/pull/14633) chore: Bump pip-compile-multi (@john-bodley)
- [#14638](https://github.com/apache/superset/pull/14638) perf: memoize db_engine_spec in database (@villebro)
- [#14625](https://github.com/apache/superset/pull/14625) chore: Change name to `BaseParameters` to `BasicParameters` (@hughhhh)
- [#14546](https://github.com/apache/superset/pull/14546) chore: Consolidating form experience (Bootstrap to AntD) - iteration 4 (@michael-s-molina)
- [#14419](https://github.com/apache/superset/pull/14419) refactor: change xsm Icons and dnd icons to new dynamic icons (@pkdotson)
- [#14581](https://github.com/apache/superset/pull/14581) chore: Update `make py-format` to use pre-commit (@hughhhh)
- [#14227](https://github.com/apache/superset/pull/14227) build(deps): bump ssri from 6.0.1 to 6.0.2 in /docs (@dependabot[bot])
- [#14569](https://github.com/apache/superset/pull/14569) style: adds padding badges to look less claustrophobic (@rusackas)
- [#14536](https://github.com/apache/superset/pull/14536) docs: typo on CONTRIBUTING.md (@Bonifacio2)
- [#14556](https://github.com/apache/superset/pull/14556) chore: Update FAB to 3.3.0 (@benjreinhart)
- [#14476](https://github.com/apache/superset/pull/14476) chore: Adding logging for datasource/save requests (@michellethomas)
- [#13095](https://github.com/apache/superset/pull/13095) chore: update CONTRIBUTING.md tag --lts (@bawantha)
- [#14364](https://github.com/apache/superset/pull/14364) refactor: remove panel from userinfo component (@pkdotson)
- [#14184](https://github.com/apache/superset/pull/14184) refactor(navbar): migrate Bootstrap navbar to AntD menus (@pkdotson)
- [#14496](https://github.com/apache/superset/pull/14496) chore: Removes tabs pane animation by default (@michael-s-molina)
- [#14502](https://github.com/apache/superset/pull/14502) refactor: Bootstrap to AntD - Form - iteration 3 (@michael-s-molina)
- [#14513](https://github.com/apache/superset/pull/14513) docs(UPDATING): Adding downtime for #14234 (@john-bodley)
- [#14412](https://github.com/apache/superset/pull/14412) chore: Improved translation into Russian language (@aleksandrgordienko)
- [#14515](https://github.com/apache/superset/pull/14515) chore: bump superset-ui to 0.17.44 (@villebro)
- [#14494](https://github.com/apache/superset/pull/14494) refactor(utils): replace strtobool with parse_boolean_string (@villebro)
- [#14380](https://github.com/apache/superset/pull/14380) chore: Moves spec files to the src folder - iteration 4 (@michael-s-molina)
- [#14379](https://github.com/apache/superset/pull/14379) refactor: Bootstrap to AntD - Form - iteration 2 (@michael-s-molina)
- [#14195](https://github.com/apache/superset/pull/14195) chore: Adds QueryParamProvider to the testing helper (@michael-s-molina)
- [#14497](https://github.com/apache/superset/pull/14497) test: Attempt to reduce asyncEvent test flakiness (@robdiciuccio)
- [#14477](https://github.com/apache/superset/pull/14477) chore: bump sankey and pivot table chart to 0.17.43 (@ktmud)
- [#14418](https://github.com/apache/superset/pull/14418) chore: Removes common storybook (@michael-s-molina)
- [#14485](https://github.com/apache/superset/pull/14485) chore: Move styles from .less stylesheet to emotion in Explore (@kgabryje)
- [#14417](https://github.com/apache/superset/pull/14417) chore: Update WebSocket server code from feedback (@benjreinhart)
- [#14454](https://github.com/apache/superset/pull/14454) chore: upgrade @emotion (@ktmud)
- [#14356](https://github.com/apache/superset/pull/14356) chore(dashboard): Integrate dashboard app into the SPA bundle (@suddjian)
- [#14436](https://github.com/apache/superset/pull/14436) refactor: split db modal file (@eschutho)
- [#14382](https://github.com/apache/superset/pull/14382) chore: add stack trace to all calls of logger.error (@samtfm)
- [#14432](https://github.com/apache/superset/pull/14432) docs: update README with new docs and recordings (@srinify)
- [#14425](https://github.com/apache/superset/pull/14425) docs: Renamed impyla from implya and updated PIP name to impyla from impala. (@jagamts1)
### 1.2.0 (2021-06-04)
**Features**
- [11498](https://github.com/apache/superset/pull/11498) feat(SIP-39): Websocket sidecar app (#11498) (@Rob DiCiuccio)
- [13894](https://github.com/apache/superset/pull/13894) feat(alert/report): add ALERTS_ATTACH_REPORTS feature flags + feature (#13894) (@Lily Kuang)

View File

@@ -22,21 +22,26 @@ under the License.
This file documents any backwards-incompatible changes in Superset and
assists people when migrating to a new version.
## Next
## 1.3.1
### Breaking Changes
- [16711](https://github.com/apache/incubator-superset/pull/16711): The `url_param` Jinja function will now by default escape the result. For instance, the value `O'Brien` will now be changed to `O''Brien`. To disable this behavior, call `url_param` with `escape_result` set to `False`: `url_param("my_key", "my default", escape_result=False)`.
## 1.3.0
### Breaking Changes
- [15909](https://github.com/apache/incubator-superset/pull/15909): a change which
drops a uniqueness criterion (which may or may not have existed) to the tables table. This constraint was obsolete as it is handled by the ORM due to differences in how MySQL, PostgreSQL, etc. handle uniqueness for NULL values.
- [13772](https://github.com/apache/superset/pull/13772): Row level security (RLS) is now enabled by default. To activate the feature, please run `superset init` to expose the RLS menus to Admin users.
- [13980](https://github.com/apache/superset/pull/13980): Data health checks no longer use the metadata database as an interim cache. Though non-breaking, deployments which implement complex logic should likely memoize the callback function. Refer to documentation in the confg.py file for more detail.
- [14255](https://github.com/apache/superset/pull/14255): The default `CSV_TO_HIVE_UPLOAD_DIRECTORY_FUNC` callable logic has been updated to leverage the specified database and schema to ensure the upload S3 key prefix is unique. Previously tables generated via upload from CSV with the same name but differ schema and/or cluster would use the same S3 key prefix. Note this change does not impact previously imported tables.
### Breaking Changes
### Potential Downtime
- [14234](https://github.com/apache/superset/pull/14234): Adds the `limiting_factor` column to the `query` table. Give the migration includes a DDL operation on a heavily trafficed table, potential service downtime may be required.
## 1.2.0
### Deprecations
- [13440](https://github.com/apache/superset/pull/13440): Dashboard/Charts reports and old Alerts is deprecated. The following config keys are deprecated:
- ENABLE_ALERTS
- SCHEDULED_EMAIL_DEBUG_MODE
@@ -44,8 +49,13 @@ drops a uniqueness criterion (which may or may not have existed) to the tables t
- EMAIL_ASYNC_TIME_LIMIT_SEC
- EMAIL_REPORT_BCC_ADDRESS
- EMAIL_REPORTS_USER
### Other
- [13772](https://github.com/apache/superset/pull/13772): Row level security (RLS) is now enabled by default. To activate the feature, please run `superset init` to expose the RLS menus to Admin users.
- [13980](https://github.com/apache/superset/pull/13980): Data health checks no longer use the metadata database as an interim cache. Though non-breaking, deployments which implement complex logic should likely memoize the callback function. Refer to documentation in the confg.py file for more detail.
- [14255](https://github.com/apache/superset/pull/14255): The default `CSV_TO_HIVE_UPLOAD_DIRECTORY_FUNC` callable logic has been updated to leverage the specified database and schema to ensure the upload S3 key prefix is unique. Previously tables generated via upload from CSV with the same name but differ schema and/or cluster would use the same S3 key prefix. Note this change does not impact previously imported tables.
## 1.1.0
### Breaking Changes

View File

@@ -118,6 +118,9 @@ services:
depends_on: *superset-depends-on
user: *superset-user
volumes: *superset-volumes
# Bump memory limit if processing selenium / thumbails on superset-worker
# mem_limit: 2038m
# mem_reservation: 128M
superset-worker-beat:
image: *superset-image

View File

@@ -9,7 +9,7 @@ version: 1
## Snowflake
The recommended connector library for Snowflake is
[snowflake-sqlalchemy](https://pypi.org/project/snowflake-sqlalchemy/).
[snowflake-sqlalchemy](https://pypi.org/project/snowflake-sqlalchemy/1.2.4/)<=1.2.4. (This version is required until Superset migrates to sqlalchemy>=1.4.0)
The connection string for Snowflake looks like this:

View File

@@ -66,7 +66,7 @@ Navigate to **Data ‣ Datasets** and select the **+ Dataset** button in the top
A modal window should pop up in front of you. Select your **Database**,
**Schema**, and **Table** using the drop downs that appear. In the following example,
we register the **cleaned_sales_data** table from the **examples** database.
we register the **Vehicle Sales** table from the **examples** database.
<img src="/images/tutorial_09_add_new_table.png" />

View File

@@ -91,7 +91,7 @@ flask-login==0.4.1
# via flask-appbuilder
flask-migrate==2.5.3
# via apache-superset
flask-openid==1.2.5
flask-openid==1.3.0
# via flask-appbuilder
flask-sqlalchemy==2.4.4
# via

View File

@@ -133,7 +133,7 @@ setup(
"exasol": ["sqlalchemy-exasol>=2.1.0, <2.2"],
"excel": ["xlrd>=1.2.0, <1.3"],
"firebird": ["sqlalchemy-firebird>=0.7.0, <0.8"],
"gsheets": ["shillelagh[gsheetsapi]>=0.7.1, <0.8"],
"gsheets": ["shillelagh[gsheetsapi]>=1.0.3, <2"],
"hana": ["hdbcli==2.4.162", "sqlalchemy_hana==0.4.0"],
"hive": ["pyhive[hive]>=0.6.1", "tableschema", "thrift>=0.11.0, <1.0.0"],
"impala": ["impyla>0.16.2, <0.17"],
@@ -147,7 +147,13 @@ setup(
"trino": ["sqlalchemy-trino>=0.2"],
"prophet": ["prophet>=1.0.1, <1.1", "pystan<3.0"],
"redshift": ["sqlalchemy-redshift>=0.8.1, < 0.9"],
"snowflake": ["snowflake-sqlalchemy>=1.2.3, <1.3"],
"rockset": ["rockset>=0.7.68, <0.8"],
"shillelagh": [
"shillelagh[datasetteapi,gsheetsapi,socrata,weatherapi]>=1.0.3, <2"
],
"snowflake": [
"snowflake-sqlalchemy==1.2.4"
], # PINNED! 1.2.5 introduced breaking changes requiring sqlalchemy>=1.4.0
"teradata": ["sqlalchemy-teradata==0.9.0.dev0"],
"thumbnails": ["Pillow>=7.0.0, <8.0.0"],
"vertica": ["sqlalchemy-vertica-python>=0.5.9, < 0.6"],

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "superset",
"version": "0.0.0dev",
"version": "1.3.2",
"description": "Superset is a data exploration platform designed to be visual, intuitive, and interactive.",
"license": "Apache-2.0",
"directories": {
@@ -67,35 +67,35 @@
"@emotion/babel-preset-css-prop": "^11.2.0",
"@emotion/cache": "^11.1.3",
"@emotion/react": "^11.1.5",
"@superset-ui/chart-controls": "^0.17.77",
"@superset-ui/core": "^0.17.75",
"@superset-ui/legacy-plugin-chart-calendar": "^0.17.77",
"@superset-ui/legacy-plugin-chart-chord": "^0.17.77",
"@superset-ui/legacy-plugin-chart-country-map": "^0.17.77",
"@superset-ui/legacy-plugin-chart-event-flow": "^0.17.77",
"@superset-ui/legacy-plugin-chart-force-directed": "^0.17.77",
"@superset-ui/legacy-plugin-chart-heatmap": "^0.17.77",
"@superset-ui/legacy-plugin-chart-histogram": "^0.17.77",
"@superset-ui/legacy-plugin-chart-horizon": "^0.17.77",
"@superset-ui/legacy-plugin-chart-map-box": "^0.17.77",
"@superset-ui/legacy-plugin-chart-paired-t-test": "^0.17.77",
"@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.17.77",
"@superset-ui/legacy-plugin-chart-partition": "^0.17.77",
"@superset-ui/legacy-plugin-chart-pivot-table": "^0.17.77",
"@superset-ui/legacy-plugin-chart-rose": "^0.17.77",
"@superset-ui/legacy-plugin-chart-sankey": "^0.17.77",
"@superset-ui/legacy-plugin-chart-sankey-loop": "^0.17.77",
"@superset-ui/legacy-plugin-chart-sunburst": "^0.17.77",
"@superset-ui/legacy-plugin-chart-treemap": "^0.17.77",
"@superset-ui/legacy-plugin-chart-world-map": "^0.17.77",
"@superset-ui/legacy-preset-chart-big-number": "^0.17.77",
"@superset-ui/legacy-preset-chart-deckgl": "^0.4.7",
"@superset-ui/legacy-preset-chart-nvd3": "^0.17.77",
"@superset-ui/plugin-chart-echarts": "^0.17.77",
"@superset-ui/plugin-chart-pivot-table": "^0.17.77",
"@superset-ui/plugin-chart-table": "^0.17.77",
"@superset-ui/plugin-chart-word-cloud": "^0.17.77",
"@superset-ui/preset-chart-xy": "^0.17.77",
"@superset-ui/chart-controls": "^0.17.84",
"@superset-ui/core": "^0.17.81",
"@superset-ui/legacy-plugin-chart-calendar": "^0.17.84",
"@superset-ui/legacy-plugin-chart-chord": "^0.17.84",
"@superset-ui/legacy-plugin-chart-country-map": "^0.17.84",
"@superset-ui/legacy-plugin-chart-event-flow": "^0.17.84",
"@superset-ui/legacy-plugin-chart-force-directed": "^0.17.84",
"@superset-ui/legacy-plugin-chart-heatmap": "^0.17.84",
"@superset-ui/legacy-plugin-chart-histogram": "^0.17.84",
"@superset-ui/legacy-plugin-chart-horizon": "^0.17.84",
"@superset-ui/legacy-plugin-chart-map-box": "^0.17.84",
"@superset-ui/legacy-plugin-chart-paired-t-test": "^0.17.84",
"@superset-ui/legacy-plugin-chart-parallel-coordinates": "^0.17.84",
"@superset-ui/legacy-plugin-chart-partition": "^0.17.84",
"@superset-ui/legacy-plugin-chart-pivot-table": "^0.17.84",
"@superset-ui/legacy-plugin-chart-rose": "^0.17.84",
"@superset-ui/legacy-plugin-chart-sankey": "^0.17.84",
"@superset-ui/legacy-plugin-chart-sankey-loop": "^0.17.84",
"@superset-ui/legacy-plugin-chart-sunburst": "^0.17.84",
"@superset-ui/legacy-plugin-chart-treemap": "^0.17.84",
"@superset-ui/legacy-plugin-chart-world-map": "^0.17.84",
"@superset-ui/legacy-preset-chart-big-number": "^0.17.84",
"@superset-ui/legacy-preset-chart-deckgl": "^0.4.10",
"@superset-ui/legacy-preset-chart-nvd3": "^0.17.84",
"@superset-ui/plugin-chart-echarts": "^0.17.84",
"@superset-ui/plugin-chart-pivot-table": "^0.17.84",
"@superset-ui/plugin-chart-table": "^0.17.84",
"@superset-ui/plugin-chart-word-cloud": "^0.17.84",
"@superset-ui/preset-chart-xy": "^0.17.84",
"@vx/responsive": "^0.0.195",
"abortcontroller-polyfill": "^1.1.9",
"antd": "^4.9.4",

View File

@@ -94,7 +94,11 @@ describe('newEntitiesFromDrop', () => {
expect(result.a.children).toHaveLength(1);
expect(Object.keys(result)).toHaveLength(3);
expect(result[newRowId].type).toBe(ROW_TYPE);
expect(result[newChartId].type).toBe(CHART_TYPE);
const newRow = result[newRowId];
expect(newRow.type).toBe(ROW_TYPE);
expect(newRow.parents).toEqual(['a']);
const newChart = result[newChartId];
expect(newChart.type).toBe(CHART_TYPE);
expect(newChart.parents).toEqual(['a', newRowId]);
});
});

View File

@@ -67,60 +67,14 @@ const sumValueAdhocMetric = new AdhocMetric({
label: 'SUM(value)',
});
describe('MetricsControl', () => {
// TODO: rewrite the tests to RTL
describe.skip('MetricsControl', () => {
it('renders Select', () => {
const { component } = setup();
expect(component.find(LabelsContainer)).toExist();
});
describe('constructor', () => {
it('unifies options for the dropdown select with aggregates', () => {
const { component } = setup();
expect(component.state('options')).toEqual([
{
optionName: '_col_source',
type: 'VARCHAR(255)',
column_name: 'source',
},
{
optionName: '_col_target',
type: 'VARCHAR(255)',
column_name: 'target',
},
{ optionName: '_col_value', type: 'DOUBLE', column_name: 'value' },
...Object.keys(AGGREGATES).map(aggregate => ({
aggregate_name: aggregate,
optionName: `_aggregate_${aggregate}`,
})),
{
optionName: 'sum__value',
metric_name: 'sum__value',
expression: 'SUM(energy_usage.value)',
},
{
optionName: 'avg__value',
metric_name: 'avg__value',
expression: 'AVG(energy_usage.value)',
},
]);
});
it('does not show aggregates in options if no columns', () => {
const { component } = setup({ columns: [] });
expect(component.state('options')).toEqual([
{
optionName: 'sum__value',
metric_name: 'sum__value',
expression: 'SUM(energy_usage.value)',
},
{
optionName: 'avg__value',
metric_name: 'avg__value',
expression: 'AVG(energy_usage.value)',
},
]);
});
it('coerces Adhoc Metrics from form data into instances of the AdhocMetric class and leaves saved metrics', () => {
const { component } = setup({
value: [
@@ -178,194 +132,7 @@ describe('MetricsControl', () => {
});
});
describe('checkIfAggregateInInput', () => {
it('handles an aggregate in the input', () => {
const { component } = setup();
expect(component.state('aggregateInInput')).toBeNull();
component.instance().checkIfAggregateInInput('AVG(');
expect(component.state('aggregateInInput')).toBe(AGGREGATES.AVG);
});
it('handles no aggregate in the input', () => {
const { component } = setup();
expect(component.state('aggregateInInput')).toBeNull();
component.instance().checkIfAggregateInInput('colu');
expect(component.state('aggregateInInput')).toBeNull();
});
});
describe('option filter', () => {
it('includes user defined metrics', () => {
const { component } = setup({ datasourceType: 'druid' });
expect(
!!component.instance().selectFilterOption(
{
data: {
metric_name: 'a_metric',
optionName: 'a_metric',
expression: 'SUM(FANCY(metric))',
},
},
'a',
),
).toBe(true);
});
it('includes auto generated avg metrics for druid', () => {
const { component } = setup({ datasourceType: 'druid' });
expect(
!!component.instance().selectFilterOption(
{
data: {
metric_name: 'avg__metric',
optionName: 'avg__metric',
expression: 'AVG(metric)',
},
},
'a',
),
).toBe(true);
});
it('includes columns and aggregates', () => {
const { component } = setup();
expect(
!!component.instance().selectFilterOption(
{
data: {
type: 'VARCHAR(255)',
column_name: 'source',
optionName: '_col_source',
},
},
'sou',
),
).toBe(true);
expect(
!!component
.instance()
.selectFilterOption(
{ data: { aggregate_name: 'AVG', optionName: '_aggregate_AVG' } },
'av',
),
).toBe(true);
});
it('includes columns based on verbose_name', () => {
const { component } = setup();
expect(
!!component.instance().selectFilterOption(
{
data: {
metric_name: 'sum__num',
verbose_name: 'babies',
optionName: '_col_sum_num',
},
},
'bab',
),
).toBe(true);
});
it('excludes auto generated avg metrics for sqla', () => {
const { component } = setup();
expect(
!!component.instance().selectFilterOption(
{
data: {
metric_name: 'avg__metric',
optionName: 'avg__metric',
expression: 'AVG(metric)',
},
},
'a',
),
).toBe(false);
});
it('includes custom made simple saved metrics', () => {
const { component } = setup();
expect(
!!component.instance().selectFilterOption(
{
data: {
metric_name: 'my_fancy_sum_metric',
optionName: 'my_fancy_sum_metric',
expression: 'SUM(value)',
},
},
'sum',
),
).toBe(true);
});
it('excludes auto generated metrics', () => {
const { component } = setup();
expect(
!!component.instance().selectFilterOption(
{
data: {
metric_name: 'sum__value',
optionName: 'sum__value',
expression: 'SUM(value)',
},
},
'sum',
),
).toBe(false);
expect(
!!component.instance().selectFilterOption(
{
data: {
metric_name: 'sum__value',
optionName: 'sum__value',
expression: 'SUM("table"."value")',
},
},
'sum',
),
).toBe(false);
});
it('filters out metrics if the input begins with an aggregate', () => {
const { component } = setup();
component.setState({ aggregateInInput: true });
expect(
!!component.instance().selectFilterOption(
{
data: { metric_name: 'metric', expression: 'SUM(FANCY(metric))' },
},
'SUM(',
),
).toBe(false);
});
it('includes columns if the input begins with an aggregate', () => {
const { component } = setup();
component.setState({ aggregateInInput: true });
expect(
!!component
.instance()
.selectFilterOption(
{ data: { type: 'DOUBLE', column_name: 'value' } },
'SUM(',
),
).toBe(true);
});
it('Removes metrics if savedMetrics changes', () => {
const { props, component, onChange } = setup({
value: [

View File

@@ -50,7 +50,7 @@ describe('VizTypeControl', () => {
new ChartMetadata({
name: 'vis1',
thumbnail: '',
tags: ['Highly-used'],
tags: ['Popular'],
}),
)
.registerValue(

View File

@@ -399,7 +399,7 @@ describe('async actions', () => {
let isFeatureEnabledMock;
beforeAll(() => {
beforeEach(() => {
isFeatureEnabledMock = jest
.spyOn(featureFlags, 'isFeatureEnabled')
.mockImplementation(
@@ -407,7 +407,7 @@ describe('async actions', () => {
);
});
afterAll(() => {
afterEach(() => {
isFeatureEnabledMock.mockRestore();
});
@@ -612,9 +612,29 @@ describe('async actions', () => {
});
describe('queryEditorSetSql', () => {
it('updates the tab state in the backend', () => {
expect.assertions(2);
describe('with backend persistence flag on', () => {
it('updates the tab state in the backend', () => {
expect.assertions(2);
const sql = 'SELECT * ';
const store = mockStore({});
return store
.dispatch(actions.queryEditorSetSql(queryEditor, sql))
.then(() => {
expect(store.getActions()).toHaveLength(0);
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1);
});
});
});
});
describe('with backend persistence flag off', () => {
it('does not update the tab state in the backend', () => {
const backendPersistenceOffMock = jest
.spyOn(featureFlags, 'isFeatureEnabled')
.mockImplementation(
feature => !(feature === 'SQLLAB_BACKEND_PERSISTENCE'),
);
const sql = 'SELECT * ';
const store = mockStore({});
const expectedActions = [
@@ -624,12 +644,12 @@ describe('async actions', () => {
sql,
},
];
return store
.dispatch(actions.queryEditorSetSql(queryEditor, sql))
.then(() => {
expect(store.getActions()).toEqual(expectedActions);
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(1);
});
store.dispatch(actions.queryEditorSetSql(queryEditor, sql));
expect(store.getActions()).toEqual(expectedActions);
expect(fetchMock.calls(updateTabStateEndpoint)).toHaveLength(0);
backendPersistenceOffMock.mockRestore();
});
});

View File

@@ -898,16 +898,11 @@ export function updateSavedQuery(query) {
export function queryEditorSetSql(queryEditor, sql) {
return function (dispatch) {
const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE)
? SupersetClient.put({
endpoint: encodeURI(`/tabstateview/${queryEditor.id}`),
postPayload: { sql, latest_query_id: queryEditor.latestQueryId },
})
: Promise.resolve();
return sync
.then(() => dispatch({ type: QUERY_EDITOR_SET_SQL, queryEditor, sql }))
.catch(() =>
if (isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE)) {
return SupersetClient.put({
endpoint: encodeURI(`/tabstateview/${queryEditor.id}`),
postPayload: { sql, latest_query_id: queryEditor.latestQueryId },
}).catch(() =>
dispatch(
addDangerToast(
t(
@@ -918,6 +913,8 @@ export function queryEditorSetSql(queryEditor, sql) {
),
),
);
}
return dispatch({ type: QUERY_EDITOR_SET_SQL, queryEditor, sql });
};
}
@@ -952,6 +949,11 @@ export function queryEditorSetQueryLimit(queryEditor, queryLimit) {
export function queryEditorSetTemplateParams(queryEditor, templateParams) {
return function (dispatch) {
dispatch({
type: QUERY_EDITOR_SET_TEMPLATE_PARAMS,
queryEditor,
templateParams,
});
const sync = isFeatureEnabled(FeatureFlag.SQLLAB_BACKEND_PERSISTENCE)
? SupersetClient.put({
endpoint: encodeURI(`/tabstateview/${queryEditor.id}`),
@@ -959,24 +961,16 @@ export function queryEditorSetTemplateParams(queryEditor, templateParams) {
})
: Promise.resolve();
return sync
.then(() =>
dispatch({
type: QUERY_EDITOR_SET_TEMPLATE_PARAMS,
queryEditor,
templateParams,
}),
)
.catch(() =>
dispatch(
addDangerToast(
t(
'An error occurred while setting the tab template parameters. ' +
'Please contact your administrator.',
),
return sync.catch(() =>
dispatch(
addDangerToast(
t(
'An error occurred while setting the tab template parameters. ' +
'Please contact your administrator.',
),
),
);
),
);
};
}

View File

@@ -448,7 +448,7 @@ export default class ResultSet extends React.PureComponent<
if (this.props.cache && this.props.query.cached) {
({ data } = this.state);
}
const { columns } = this.props.query.results;
// Added compute logic to stop user from being able to Save & Explore
const {
saveDatasetRadioBtnState,
@@ -508,7 +508,7 @@ export default class ResultSet extends React.PureComponent<
)}
<CopyToClipboard
text={prepareCopyToClipboardTabularData(data)}
text={prepareCopyToClipboardTabularData(data, columns)}
wrapped={false}
copyNode={
<Button buttonSize="small">

View File

@@ -133,7 +133,8 @@ const TableElement = ({ table, actions, ...props }: TableElementProps) => {
));
}
if (!partitions && !metadata) {
if (!partitions && (!metadata || !metadata.length)) {
// hide partition and metadata card view
return null;
}

View File

@@ -395,13 +395,16 @@ export function exploreJSON(
.then(({ response, json }) => {
if (isFeatureEnabled(FeatureFlag.GLOBAL_ASYNC_QUERIES)) {
// deal with getChartDataRequest transforming the response data
const result = 'result' in json ? json.result[0] : json;
const result = 'result' in json ? json.result : json;
switch (response.status) {
case 200:
// Query results returned synchronously, meaning query was already cached.
return Promise.resolve([result]);
return Promise.resolve(result);
case 202:
// Query is running asynchronously and we must await the results
if (shouldUseLegacyApi(formData)) {
return waitForAsyncData(result[0]);
}
return waitForAsyncData(result);
default:
throw new Error(

View File

@@ -32,3 +32,5 @@ const StyledForm = styled(AntDForm)`
export default function Form(props: FormProps) {
return <StyledForm {...props} />;
}
export { FormProps };

View File

@@ -16,9 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
import Form from './Form';
import Form, { FormProps } from './Form';
import FormItem from './FormItem';
import FormLabel from './FormLabel';
import LabeledErrorBoundInput from './LabeledErrorBoundInput';
export { Form, FormItem, FormLabel, LabeledErrorBoundInput };
export { Form, FormItem, FormLabel, LabeledErrorBoundInput, FormProps };

View File

@@ -27,12 +27,21 @@ interface CardCollectionProps {
prepareRow: TableInstance['prepareRow'];
renderCard?: (row: any) => React.ReactNode;
rows: TableInstance['rows'];
showThumbnails?: boolean;
}
const CardContainer = styled.div`
display: grid;
grid-template-columns: repeat(auto-fit, minmax(459px, 1fr));
grid-gap: ${({ theme }) => theme.gridUnit * 8}px;
const CardContainer = styled.div<{ showThumbnails?: boolean }>`
${({ theme, showThumbnails }) => `
display: grid;
grid-gap: ${theme.gridUnit * 12}px ${theme.gridUnit * 4}px;
grid-template-columns: repeat(auto-fit, 300px);
margin-top: ${theme.gridUnit * -6}px;
padding: ${
showThumbnails
? `${theme.gridUnit * 8 + 3}px ${theme.gridUnit * 9}px`
: `${theme.gridUnit * 8 + 1}px ${theme.gridUnit * 9}px`
};
`}
`;
const CardWrapper = styled.div`
@@ -51,6 +60,7 @@ export default function CardCollection({
prepareRow,
renderCard,
rows,
showThumbnails,
}: CardCollectionProps) {
function handleClick(
event: React.MouseEvent<HTMLDivElement, MouseEvent>,
@@ -65,7 +75,7 @@ export default function CardCollection({
if (!renderCard) return null;
return (
<CardContainer>
<CardContainer showThumbnails={showThumbnails}>
{loading &&
rows.length === 0 &&
[...new Array(25)].map((e, i) => (

View File

@@ -221,6 +221,7 @@ export interface ListViewProps<T extends object = any> {
cardSortSelectOptions?: Array<CardSortSelectOption>;
defaultViewMode?: ViewModeType;
highlightRowId?: number;
showThumbnails?: boolean;
emptyState?: {
message?: string;
slot?: React.ReactNode;
@@ -242,6 +243,7 @@ function ListView<T extends object = any>({
disableBulkSelect = () => {},
renderBulkSelectCopy = selected => t('%s Selected', selected.length),
renderCard,
showThumbnails,
cardSortSelectOptions,
defaultViewMode = 'card',
highlightRowId,
@@ -376,6 +378,7 @@ function ListView<T extends object = any>({
renderCard={renderCard}
rows={rows}
loading={loading}
showThumbnails={showThumbnails}
/>
)}
{viewMode === 'table' && (

View File

@@ -103,6 +103,7 @@ const mockedProps = {
locale: 'en',
version_string: '1.0.0',
version_sha: 'randomSHA',
build_number: 'randomBuildNumber',
},
settings: [
{
@@ -280,10 +281,10 @@ test('should render the Profile link when available', async () => {
expect(profile).toHaveAttribute('href', user_profile_url);
});
test('should render the About section and version_string or sha when available', async () => {
test('should render the About section and version_string, sha or build_number when available', async () => {
const {
data: {
navbar_right: { version_sha, version_string },
navbar_right: { version_sha, version_string, build_number },
},
} = mockedProps;
@@ -292,9 +293,11 @@ test('should render the About section and version_string or sha when available',
const about = await screen.findByText('About');
const version = await screen.findByText(`Version: ${version_string}`);
const sha = await screen.findByText(`SHA: ${version_sha}`);
const build = await screen.findByText(`Build: ${build_number}`);
expect(about).toBeInTheDocument();
expect(version).toBeInTheDocument();
expect(sha).toBeInTheDocument();
expect(build).toBeInTheDocument();
});
test('should render the Documentation link when available', async () => {

View File

@@ -44,6 +44,7 @@ export interface NavBarProps {
bug_report_url?: string;
version_string?: string;
version_sha?: string;
build_number?: string;
documentation_url?: string;
languages: Languages;
show_language_picker: boolean;

View File

@@ -69,15 +69,6 @@ const StyledAnchor = styled.a`
padding-left: ${({ theme }) => theme.gridUnit}px;
`;
const WaterMark = styled.span`
font-size: 13px;
color: #b0b4c3;
margin: 0 ${({ theme }) => theme.gridUnit * 4}px;
@media (max-width: 1070px) {
display: none;
}
`;
const { SubMenu } = Menu;
interface RightMenuProps {
@@ -95,9 +86,6 @@ const RightMenu = ({
}: RightMenuProps) => (
<StyledDiv align={align}>
<Menu mode="horizontal">
{navbarRight.show_watermark && (
<WaterMark>{t('Powered by Apache Superset')}</WaterMark>
)}
{!navbarRight.user_is_anonymous && (
<SubMenu
data-test="new-dropdown"
@@ -148,18 +136,27 @@ const RightMenu = ({
<a href={navbarRight.user_profile_url}>{t('Profile')}</a>
</Menu.Item>
)}
<Menu.Item key="info">
<a href={navbarRight.user_info_url}>{t('Info')}</a>
</Menu.Item>
{navbarRight.user_info_url && (
<Menu.Item key="info">
<a href={navbarRight.user_info_url}>{t('Info')}</a>
</Menu.Item>
)}
<Menu.Item key="logout">
<a href={navbarRight.user_logout_url}>{t('Logout')}</a>
</Menu.Item>
</Menu.ItemGroup>,
]}
{(navbarRight.version_string || navbarRight.version_sha) && [
{(navbarRight.version_string ||
navbarRight.version_sha ||
navbarRight.build_number) && [
<Menu.Divider key="version-info-divider" />,
<Menu.ItemGroup key="about-section" title={t('About')}>
<div className="about-section">
{navbarRight.show_watermark && (
<div css={versionInfoStyles}>
{t('Powered by Apache Superset')}
</div>
)}
{navbarRight.version_string && (
<div css={versionInfoStyles}>
Version: {navbarRight.version_string}
@@ -170,6 +167,11 @@ const RightMenu = ({
SHA: {navbarRight.version_sha}
</div>
)}
{navbarRight.build_number && (
<div css={versionInfoStyles}>
Build: {navbarRight.build_number}
</div>
)}
</div>
</Menu.ItemGroup>,
]}

View File

@@ -18,7 +18,7 @@
*/
import React, { useState } from 'react';
import { useSelector } from 'react-redux';
import { t, SupersetTheme, css } from '@superset-ui/core';
import { t, SupersetTheme, css, useTheme } from '@superset-ui/core';
import Icons from 'src/components/Icons';
import { Switch } from 'src/components/Switch';
import { AlertObject } from 'src/views/CRUD/alert/types';
@@ -47,6 +47,7 @@ export default function HeaderReportActionsDropDown({
currentReportDeleting,
setCurrentReportDeleting,
] = useState<AlertObject | null>(null);
const theme = useTheme();
const toggleActiveKey = async (data: AlertObject, checked: boolean) => {
if (data?.id) {
@@ -60,7 +61,7 @@ export default function HeaderReportActionsDropDown({
};
const menu = () => (
<Menu selectable={false}>
<Menu selectable={false} css={{ width: '200px' }}>
<Menu.Item>
{t('Email reports active')}
<Switch
@@ -68,6 +69,7 @@ export default function HeaderReportActionsDropDown({
checked={report?.active}
onClick={(checked: boolean) => toggleActiveKey(report, checked)}
size="small"
css={{ marginLeft: theme.gridUnit * 2 }}
/>
</Menu.Item>
<Menu.Item onClick={showReportModal}>{t('Edit email report')}</Menu.Item>

View File

@@ -38,6 +38,13 @@ const defaultProps = {
userEmail: 'test@test.com',
dashboardId: 1,
creationMethod: 'charts_dashboards',
props: {
chart: {
sliceFormData: {
viz_type: 'table',
},
},
},
};
describe('Email Report Modal', () => {

View File

@@ -29,22 +29,28 @@ import { bindActionCreators } from 'redux';
import { connect, useDispatch, useSelector } from 'react-redux';
import { addReport, editReport } from 'src/reports/actions/reports';
import { AlertObject } from 'src/views/CRUD/alert/types';
import LabeledErrorBoundInput from 'src/components/Form/LabeledErrorBoundInput';
import TimezoneSelector from 'src/components/TimezoneSelector';
import LabeledErrorBoundInput from 'src/components/Form/LabeledErrorBoundInput';
import Icons from 'src/components/Icons';
import withToasts from 'src/messageToasts/enhancers/withToasts';
import { CronPicker, CronError } from 'src/components/CronPicker';
import { CronError } from 'src/components/CronPicker';
import { RadioChangeEvent } from 'src/common/components';
import {
StyledModal,
StyledTopSection,
StyledBottomSection,
StyledIconWrapper,
StyledScheduleTitle,
StyledCronPicker,
StyledCronError,
noBottomMargin,
StyledFooterButton,
TimezoneHeaderStyle,
SectionHeaderStyle,
StyledMessageContentTitle,
StyledRadio,
StyledRadioGroup,
} from './styles';
interface ReportObject {
@@ -67,6 +73,19 @@ interface ReportObject {
creation_method: string;
}
interface ChartObject {
id: number;
chartAlert: string;
chartStatus: string;
chartUpdateEndTime: number;
chartUpdateStartTime: number;
latestQueryFormData: object;
queryController: { abort: () => {} };
queriesResponse: object;
triggerQuery: boolean;
lastRendered: number;
}
interface ReportProps {
addDangerToast: (msg: string) => void;
addSuccessToast: (msg: string) => void;
@@ -77,26 +96,25 @@ interface ReportProps {
userId: number;
userEmail: string;
dashboardId?: number;
chartId?: number;
chart?: ChartObject;
creationMethod: string;
props: any;
}
enum ActionType {
textChange,
inputChange,
fetched,
reset,
}
interface ReportPayloadType {
name: string;
value: string;
}
enum ActionType {
inputChange,
fetched,
reset,
}
type ReportActionType =
| {
type: ActionType.textChange | ActionType.inputChange;
type: ActionType.inputChange;
payload: ReportPayloadType;
}
| {
@@ -107,17 +125,26 @@ type ReportActionType =
type: ActionType.reset;
};
const DEFAULT_NOTIFICATION_FORMAT = 'TEXT';
const TEXT_BASED_VISUALIZATION_TYPES = [
'pivot_table',
'pivot_table_v2',
'table',
'paired_ttest',
];
const reportReducer = (
state: Partial<ReportObject> | null,
action: ReportActionType,
): Partial<ReportObject> | null => {
const initialState = {
name: state?.name || 'Weekly Report',
report_format: state?.report_format || DEFAULT_NOTIFICATION_FORMAT,
...(state || {}),
};
switch (action.type) {
case ActionType.textChange:
case ActionType.inputChange:
return {
...initialState,
[action.payload.name]: action.payload.value,
@@ -139,6 +166,7 @@ const ReportModal: FunctionComponent<ReportProps> = ({
show = false,
...props
}) => {
const vizType = props.props.chart?.sliceFormData?.viz_type;
const [currentReport, setCurrentReport] = useReducer<
Reducer<Partial<ReportObject> | null, ReportActionType>
>(reportReducer, null);
@@ -166,7 +194,6 @@ const ReportModal: FunctionComponent<ReportProps> = ({
}
}, [reports]);
const onClose = () => {
// setLoading(false);
onHide();
};
const onSave = async () => {
@@ -174,7 +201,7 @@ const ReportModal: FunctionComponent<ReportProps> = ({
const newReportValues: Partial<ReportObject> = {
crontab: currentReport?.crontab,
dashboard: props.props.dashboardId,
chart: props.props.chartId,
chart: props.props.chart?.id,
description: currentReport?.description,
name: currentReport?.name,
owners: [props.props.userId],
@@ -187,9 +214,9 @@ const ReportModal: FunctionComponent<ReportProps> = ({
type: 'Report',
creation_method: props.props.creationMethod,
active: true,
report_format: currentReport?.report_format,
};
// setLoading(true);
if (isEditMode) {
await dispatch(
editReport(currentReport?.id, newReportValues as ReportObject),
@@ -217,7 +244,7 @@ const ReportModal: FunctionComponent<ReportProps> = ({
const renderModalFooter = (
<>
<StyledFooterButton key="back" onClick={onClose}>
Cancel
{t('Cancel')}
</StyledFooterButton>
<StyledFooterButton
key="submit"
@@ -225,11 +252,42 @@ const ReportModal: FunctionComponent<ReportProps> = ({
onClick={onSave}
disabled={!currentReport?.name}
>
Add
{isEditMode ? t('Save') : t('Add')}
</StyledFooterButton>
</>
);
const renderMessageContentSection = (
<>
<StyledMessageContentTitle>
<h4>{t('Message Content')}</h4>
</StyledMessageContentTitle>
<div className="inline-container">
<StyledRadioGroup
onChange={(event: RadioChangeEvent) => {
onChange(ActionType.inputChange, {
name: 'report_format',
value: event.target.value,
});
}}
value={currentReport?.report_format || DEFAULT_NOTIFICATION_FORMAT}
>
{TEXT_BASED_VISUALIZATION_TYPES.includes(vizType) && (
<StyledRadio value="TEXT">
{t('Text embedded in email')}
</StyledRadio>
)}
<StyledRadio value="PNG">
{t('Image (PNG) embedded in email')}
</StyledRadio>
<StyledRadio value="CSV">
{t('Formatted CSV attached in email')}
</StyledRadio>
</StyledRadioGroup>
</div>
</>
);
return (
<StyledModal
show={show}
@@ -248,7 +306,7 @@ const ReportModal: FunctionComponent<ReportProps> = ({
required
validationMethods={{
onChange: ({ target }: { target: HTMLInputElement }) =>
onChange(ActionType.textChange, {
onChange(ActionType.inputChange, {
name: target.name,
value: target.value,
}),
@@ -266,7 +324,7 @@ const ReportModal: FunctionComponent<ReportProps> = ({
value={currentReport?.description || ''}
validationMethods={{
onChange: ({ target }: { target: HTMLInputElement }) =>
onChange(ActionType.textChange, {
onChange(ActionType.inputChange, {
name: target.name,
value: target.value,
}),
@@ -284,16 +342,16 @@ const ReportModal: FunctionComponent<ReportProps> = ({
<StyledBottomSection>
<StyledScheduleTitle>
<h4 css={(theme: SupersetTheme) => SectionHeaderStyle(theme)}>
Schedule
{t('Schedule')}
</h4>
<p>Scheduled reports will be sent to your email as a PNG</p>
<p>{t('Scheduled reports will be sent to your email as a PNG')}</p>
</StyledScheduleTitle>
<CronPicker
<StyledCronPicker
clearButton={false}
value={currentReport?.crontab || '0 12 * * 1'}
setValue={(newValue: string) => {
onChange(ActionType.textChange, {
onChange(ActionType.inputChange, {
name: 'crontab',
value: newValue,
});
@@ -310,12 +368,13 @@ const ReportModal: FunctionComponent<ReportProps> = ({
<TimezoneSelector
onTimezoneChange={value => {
setCurrentReport({
type: ActionType.textChange,
type: ActionType.inputChange,
payload: { name: 'timezone', value },
});
}}
timezone={currentReport?.timezone}
/>
{props.props.chart && renderMessageContentSection}
</StyledBottomSection>
</StyledModal>
);

View File

@@ -20,11 +20,17 @@
import { styled, css, SupersetTheme } from '@superset-ui/core';
import Modal from 'src/components/Modal';
import Button from 'src/components/Button';
import { Radio } from 'src/components/Radio';
import { CronPicker } from 'src/components/CronPicker';
export const StyledModal = styled(Modal)`
.ant-modal-body {
padding: 0;
}
h4 {
font-weight: 600;
}
`;
export const StyledTopSection = styled.div`
@@ -61,6 +67,14 @@ export const StyledIconWrapper = styled.span`
export const StyledScheduleTitle = styled.div`
margin-bottom: ${({ theme }) => theme.gridUnit * 7}px;
h4 {
margin-bottom: ${({ theme }) => theme.gridUnit * 3}px;
}
`;
export const StyledCronPicker = styled(CronPicker)`
margin-bottom: ${({ theme }) => theme.gridUnit * 3}px;
`;
export const StyledCronError = styled.p`
@@ -83,3 +97,17 @@ export const SectionHeaderStyle = (theme: SupersetTheme) => css`
margin: ${theme.gridUnit * 3}px 0;
font-weight: ${theme.typography.weights.bold};
`;
export const StyledMessageContentTitle = styled.div`
margin: ${({ theme }) => theme.gridUnit * 8}px 0
${({ theme }) => theme.gridUnit * 4}px;
`;
export const StyledRadio = styled(Radio)`
display: block;
line-height: ${({ theme }) => theme.gridUnit * 8}px;
`;
export const StyledRadioGroup = styled(Radio.Group)`
margin-left: ${({ theme }) => theme.gridUnit * 0.5}px;
`;

View File

@@ -249,8 +249,15 @@ function styled<
if (forceOverflow) {
Object.assign(restProps, {
closeMenuOnScroll: (e: Event) => {
// ensure menu is open
const menuIsOpen = (stateManager as BasicSelect<OptionType>)?.state
?.menuIsOpen;
const target = e.target as HTMLElement;
return target && !target.classList?.contains('Select__menu-list');
return (
menuIsOpen &&
target &&
!target.classList?.contains('Select__menu-list')
);
},
menuPosition: 'fixed',
});

View File

@@ -47,7 +47,6 @@ type PickedSelectProps = Pick<
AntdSelectAllProps,
| 'allowClear'
| 'autoFocus'
| 'value'
| 'disabled'
| 'filterOption'
| 'notFoundContent'
@@ -310,10 +309,13 @@ const Select = ({
const handleOnDeselect = (value: string | number | AntdLabeledValue) => {
if (Array.isArray(selectValue)) {
const selectedValues = [
...(selectValue as []).filter(opt => opt !== value),
];
setSelectValue(selectedValues);
if (typeof value === 'number' || typeof value === 'string') {
const array = selectValue as (string | number)[];
setSelectValue(array.filter(element => element !== value));
} else {
const array = selectValue as AntdLabeledValue[];
setSelectValue(array.filter(element => element.value !== value.value));
}
}
setSearchedValue('');
};

View File

@@ -156,7 +156,6 @@ const TableView = ({
useSortBy,
usePagination,
);
useEffect(() => {
if (serverPagination && pageIndex !== initialState.pageIndex) {
onServerPagination({

View File

@@ -23,7 +23,7 @@ import moment from 'moment-timezone';
import { NativeGraySelect as Select } from 'src/components/Select';
const DEFAULT_TIMEZONE = 'GMT Standard Time';
const MIN_SELECT_WIDTH = '375px';
const MIN_SELECT_WIDTH = '400px';
const offsetsToName = {
'-300-240': ['Eastern Standard Time', 'Eastern Daylight Time'],

View File

@@ -187,7 +187,7 @@ export const Table = styled.table`
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
max-width: 300px;
max-width: 320px;
line-height: 1;
vertical-align: middle;
&:first-of-type {

View File

@@ -335,7 +335,7 @@ export const hydrateDashboard = (dashboardData, chartData) => (
dashboardInfo: {
...dashboardData,
metadata,
userId: String(user.userId), // legacy, please use state.user instead
userId: user.userId ? String(user.userId) : null, // legacy, please use state.user instead
dash_edit_perm: canEdit,
dash_save_perm: findPermission('can_save_dash', 'Superset', roles),
dash_share_perm: findPermission(

View File

@@ -50,6 +50,7 @@ const propTypes = {
removeSliceFromDashboard: PropTypes.func.isRequired,
triggerQuery: PropTypes.func.isRequired,
logEvent: PropTypes.func.isRequired,
clearDataMaskState: PropTypes.func.isRequired,
}).isRequired,
dashboardInfo: dashboardInfoPropShape.isRequired,
dashboardState: dashboardStatePropShape.isRequired,
@@ -193,6 +194,7 @@ class Dashboard extends React.PureComponent {
componentWillUnmount() {
window.removeEventListener('visibilitychange', this.onVisibilityChange);
this.props.actions.clearDataMaskState();
}
onVisibilityChange() {

View File

@@ -96,6 +96,7 @@ test('Should render "appliedCrossFilterIndicators"', () => {
<DetailsPanel {...props}>
<div data-test="details-panel-content">Content</div>
</DetailsPanel>,
{ useRedux: true },
);
userEvent.click(screen.getByTestId('details-panel-content'));
@@ -129,6 +130,7 @@ test('Should render "appliedIndicators"', () => {
<DetailsPanel {...props}>
<div data-test="details-panel-content">Content</div>
</DetailsPanel>,
{ useRedux: true },
);
userEvent.click(screen.getByTestId('details-panel-content'));
@@ -160,6 +162,7 @@ test('Should render "incompatibleIndicators"', () => {
<DetailsPanel {...props}>
<div data-test="details-panel-content">Content</div>
</DetailsPanel>,
{ useRedux: true },
);
userEvent.click(screen.getByTestId('details-panel-content'));
@@ -193,6 +196,7 @@ test('Should render "unsetIndicators"', () => {
<DetailsPanel {...props}>
<div data-test="details-panel-content">Content</div>
</DetailsPanel>,
{ useRedux: true },
);
userEvent.click(screen.getByTestId('details-panel-content'));
@@ -227,6 +231,7 @@ test('Should render empty', () => {
<DetailsPanel {...props}>
<div data-test="details-panel-content">Content</div>
</DetailsPanel>,
{ useRedux: true },
);
expect(screen.getByTestId('details-panel-content')).toBeInTheDocument();

View File

@@ -17,6 +17,7 @@
* under the License.
*/
import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { Global, css } from '@emotion/react';
import { t, useTheme } from '@superset-ui/core';
import {
@@ -35,6 +36,7 @@ import {
} from 'src/dashboard/components/FiltersBadge/Styles';
import { Indicator } from 'src/dashboard/components/FiltersBadge/selectors';
import FilterIndicator from 'src/dashboard/components/FiltersBadge/FilterIndicator';
import { RootState } from 'src/dashboard/types';
export interface DetailsPanelProps {
appliedCrossFilterIndicators: Indicator[];
@@ -55,6 +57,9 @@ const DetailsPanelPopover = ({
}: DetailsPanelProps) => {
const [visible, setVisible] = useState(false);
const theme = useTheme();
const activeTabs = useSelector<RootState>(
state => state.dashboardState?.activeTabs,
);
// we don't need to clean up useEffect, setting { once: true } removes the event listener after handle function is called
useEffect(() => {
@@ -65,6 +70,11 @@ const DetailsPanelPopover = ({
}
}, [visible]);
// if tabs change, popover doesn't close automatically
useEffect(() => {
setVisible(false);
}, [activeTabs]);
const getDefaultActivePanel = () => {
const result = [];
if (appliedCrossFilterIndicators.length) {

View File

@@ -20,7 +20,12 @@ import { NO_TIME_RANGE, TIME_FILTER_MAP } from 'src/explore/constants';
import { getChartIdsInFilterScope } from 'src/dashboard/util/activeDashboardFilters';
import { ChartConfiguration, Filters } from 'src/dashboard/reducers/types';
import { DataMaskStateWithId, DataMaskType } from 'src/dataMask/types';
import { FeatureFlag, isFeatureEnabled } from '@superset-ui/core';
import {
ensureIsArray,
FeatureFlag,
FilterState,
isFeatureEnabled,
} from '@superset-ui/core';
import { Layout } from '../../types';
import { getTreeCheckedItems } from '../nativeFilters/FiltersConfigModal/FiltersConfigForm/FilterScope/utils';
@@ -50,6 +55,16 @@ type Filter = {
datasourceId: string;
};
const extractLabel = (filter?: FilterState): string | null => {
if (filter?.label) {
return filter.label;
}
if (filter?.value) {
return ensureIsArray(filter?.value).join(', ');
}
return null;
};
const selectIndicatorValue = (
columnKey: string,
filter: Filter,
@@ -182,16 +197,16 @@ export const selectNativeIndicatorsForChart = (
const rejectedColumns = getRejectedColumns(chart);
const getStatus = ({
value,
label,
column,
type = DataMaskType.NativeFilters,
}: {
value: any;
label: string | null;
column?: string;
type?: DataMaskType;
}): IndicatorStatus => {
// a filter is only considered unset if it's value is null
const hasValue = value !== null;
const hasValue = label !== null;
if (type === DataMaskType.CrossFilters && hasValue) {
return IndicatorStatus.CrossFilterApplied;
}
@@ -220,19 +235,14 @@ export const selectNativeIndicatorsForChart = (
)
.map(nativeFilter => {
const column = nativeFilter.targets[0]?.column?.name;
let value =
dataMask[nativeFilter.id]?.filterState?.label ??
dataMask[nativeFilter.id]?.filterState?.value ??
null;
if (!Array.isArray(value) && value !== null) {
value = [value];
}
const filterState = dataMask[nativeFilter.id]?.filterState;
const label = extractLabel(filterState);
return {
column,
name: nativeFilter.name,
path: [nativeFilter.id],
status: getStatus({ value, column }),
value,
status: getStatus({ label, column }),
value: label,
};
});
}
@@ -249,23 +259,26 @@ export const selectNativeIndicatorsForChart = (
),
)
.map(chartConfig => {
let value =
dataMask[chartConfig.id]?.filterState?.label ??
dataMask[chartConfig.id]?.filterState?.value ??
null;
if (!Array.isArray(value) && value !== null) {
value = [value];
}
const filterState = dataMask[chartConfig.id]?.filterState;
const label = extractLabel(filterState);
const filtersState = filterState?.filters;
const column = filtersState && Object.keys(filtersState)[0];
const dashboardLayoutItem = Object.values(dashboardLayout).find(
layoutItem => layoutItem?.meta?.chartId === chartConfig.id,
);
return {
name: Object.values(dashboardLayout).find(
layoutItem => layoutItem?.meta?.chartId === chartConfig.id,
)?.meta?.sliceName as string,
path: [`${chartConfig.id}`],
column,
name: dashboardLayoutItem?.meta?.sliceName as string,
path: [
...(dashboardLayoutItem?.parents ?? []),
dashboardLayoutItem?.id,
],
status: getStatus({
value,
label,
type: DataMaskType.CrossFilters,
}),
value,
value: label,
};
})
.filter(filter => filter.status === IndicatorStatus.CrossFilterApplied);

View File

@@ -48,7 +48,9 @@ import {
SAVE_TYPE_OVERWRITE,
DASHBOARD_POSITION_DATA_LIMIT,
} from 'src/dashboard/util/constants';
import setPeriodicRunner from 'src/dashboard/util/setPeriodicRunner';
import setPeriodicRunner, {
stopPeriodicRender,
} from 'src/dashboard/util/setPeriodicRunner';
import { options as PeriodicRefreshOptions } from 'src/dashboard/components/RefreshIntervalModal';
const propTypes = {
@@ -168,18 +170,20 @@ class Header extends React.PureComponent {
componentDidMount() {
const { refreshFrequency, user, dashboardInfo } = this.props;
this.startPeriodicRender(refreshFrequency * 1000);
if (user && isFeatureEnabled(FeatureFlag.ALERT_REPORTS)) {
if (this.canAddReports()) {
// this is in case there is an anonymous user.
this.props.fetchUISpecificReport(
user.userId,
'dashboard_id',
'dashboards',
dashboardInfo.id,
user.email,
);
}
}
UNSAFE_componentWillReceiveProps(nextProps) {
const { user } = this.props;
if (
UNDO_LIMIT - nextProps.undoLength <= 0 &&
!this.state.didNotifyMaxUndoHistoryToast
@@ -193,9 +197,24 @@ class Header extends React.PureComponent {
) {
this.props.setMaxUndoHistoryExceeded();
}
if (
this.canAddReports() &&
nextProps.dashboardInfo.id !== this.props.dashboardInfo.id
) {
// this is in case there is an anonymous user.
this.props.fetchUISpecificReport(
user.userId,
'dashboard_id',
'dashboards',
nextProps.dashboardInfo.id,
user.email,
);
}
}
componentWillUnmount() {
stopPeriodicRender(this.refreshTimer);
this.props.setRefreshFrequency(0);
clearTimeout(this.ctrlYTimeout);
clearTimeout(this.ctrlZTimeout);
}
@@ -383,32 +402,31 @@ class Header extends React.PureComponent {
renderReportModal() {
const attachedReportExists = !!Object.keys(this.props.reports).length;
const canAddReports = isFeatureEnabled(FeatureFlag.ALERT_REPORTS);
return (
(canAddReports || null) &&
(attachedReportExists ? (
<HeaderReportActionsDropdown
showReportModal={this.showReportModal}
toggleActive={this.props.toggleActive}
deleteActiveReport={this.props.deleteActiveReport}
/>
) : (
<>
<span
role="button"
title={t('Schedule email report')}
tabIndex={0}
className="action-button"
onClick={this.showReportModal}
>
<Icons.Calendar />
</span>
</>
))
return attachedReportExists ? (
<HeaderReportActionsDropdown
showReportModal={this.showReportModal}
toggleActive={this.props.toggleActive}
deleteActiveReport={this.props.deleteActiveReport}
/>
) : (
<>
<span
role="button"
title={t('Schedule email report')}
tabIndex={0}
className="action-button"
onClick={this.showReportModal}
>
<Icons.Calendar />
</span>
</>
);
}
canAddReports() {
if (!isFeatureEnabled(FeatureFlag.ALERT_REPORTS)) {
return false;
}
const { user } = this.props;
if (!user) {
// this is in the case that there is an anonymous user.
@@ -417,7 +435,7 @@ class Header extends React.PureComponent {
const roles = Object.keys(user.roles || []);
const permissions = roles.map(key =>
user.roles[key].filter(
perms => perms[0] === 'can_add' && perms[1] === 'AlertModelView',
perms => perms[0] === 'menu_access' && perms[1] === 'Manage',
),
);
return permissions[0].length > 0;

View File

@@ -36,7 +36,7 @@ jest.mock('src/common/components', () => {
};
});
const createProps = () => ({
const createProps = (viz_type = 'sunburst') => ({
addDangerToast: jest.fn(),
addSuccessToast: jest.fn(),
exploreChart: jest.fn(),
@@ -67,9 +67,9 @@ const createProps = () => ({
time_range: 'No filter',
time_range_endpoints: ['inclusive', 'exclusive'],
url_params: {},
viz_type: 'sunburst',
viz_type,
},
viz_type: 'sunburst',
viz_type,
datasource: '58__table',
description: 'test-description',
description_markeddown: '',
@@ -152,25 +152,30 @@ test('Should "export to CSV"', () => {
expect(props.exportCSV).toBeCalledWith(371);
});
test('Should not show "Export to CSV" if slice is filter box', () => {
const props = createProps('filter_box');
render(<SliceHeaderControls {...props} />, { useRedux: true });
expect(screen.queryByRole('menuitem', { name: 'Export CSV' })).toBe(null);
});
test('Export full CSV is under featureflag', () => {
// @ts-ignore
global.featureFlags = {
[FeatureFlag.ALLOW_FULL_CSV_EXPORT]: false,
};
const props = createProps();
props.slice.viz_type = 'table';
const props = createProps('table');
render(<SliceHeaderControls {...props} />, { useRedux: true });
expect(screen.queryByRole('menuitem', { name: 'Export full CSV' })).toBe(
null,
);
});
test('Should "export full CSV"', () => {
// @ts-ignore
global.featureFlags = {
[FeatureFlag.ALLOW_FULL_CSV_EXPORT]: true,
};
const props = createProps();
props.slice.viz_type = 'table';
const props = createProps('table');
render(<SliceHeaderControls {...props} />, { useRedux: true });
expect(screen.queryByRole('menuitem', { name: 'Export full CSV' })).not.toBe(
null,
@@ -193,6 +198,18 @@ test('Should not show export full CSV if report is not table', () => {
);
});
test('Should not show export full CSV if slice is filter box', () => {
// @ts-ignore
global.featureFlags = {
[FeatureFlag.ALLOW_FULL_CSV_EXPORT]: true,
};
const props = createProps('filter_box');
render(<SliceHeaderControls {...props} />, { useRedux: true });
expect(screen.queryByRole('menuitem', { name: 'Export full CSV' })).toBe(
null,
);
});
test('Should "Toggle chart description"', () => {
const props = createProps();
render(<SliceHeaderControls {...props} />, { useRedux: true });

View File

@@ -324,16 +324,20 @@ class SliceHeaderControls extends React.PureComponent<
{t('Download as image')}
</Menu.Item>
{this.props.supersetCanCSV && (
<Menu.Item key={MENU_KEYS.EXPORT_CSV}>{t('Export CSV')}</Menu.Item>
)}
{isFeatureEnabled(FeatureFlag.ALLOW_FULL_CSV_EXPORT) &&
{this.props.slice.viz_type !== 'filter_box' &&
this.props.supersetCanCSV && (
<Menu.Item key={MENU_KEYS.EXPORT_CSV}>{t('Export CSV')}</Menu.Item>
)}
{this.props.slice.viz_type !== 'filter_box' &&
isFeatureEnabled(FeatureFlag.ALLOW_FULL_CSV_EXPORT) &&
this.props.supersetCanCSV &&
isTable && (
<Menu.Item key={MENU_KEYS.EXPORT_FULL_CSV}>
{t('Export full CSV')}
</Menu.Item>
)}
{isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS) &&
isCrossFilter && (
<Menu.Item key={MENU_KEYS.CROSS_FILTER_SCOPING}>

View File

@@ -56,7 +56,7 @@ export const checkIsMissingRequiredValue = (
const value = filterState?.value;
// TODO: this property should be unhardcoded
return (
filter.controlValues.enableEmptyFilter &&
filter.controlValues?.enableEmptyFilter &&
(value === null || value === undefined)
);
};

View File

@@ -30,6 +30,7 @@ export const FILTER_SUPPORTED_TYPES = {
filter_timegrain: [GenericDataType.TEMPORAL],
filter_timecolumn: [GenericDataType.TEMPORAL],
filter_select: [
GenericDataType.BOOLEAN,
GenericDataType.STRING,
GenericDataType.NUMERIC,
GenericDataType.TEMPORAL,

View File

@@ -33,6 +33,7 @@ import {
getAllActiveFilters,
getRelevantDataMask,
} from 'src/dashboard/util/activeAllDashboardFilters';
import { clearDataMaskState } from '../../dataMask/actions';
function mapStateToProps(state: RootState) {
const {
@@ -83,6 +84,7 @@ function mapDispatchToProps(dispatch: Dispatch) {
actions: bindActionCreators(
{
setDatasources,
clearDataMaskState,
addSliceToDashboard,
removeSliceFromDashboard,
triggerQuery,

View File

@@ -51,8 +51,8 @@ export default function newEntitiesFromDrop({ dropResult, layout }) {
rowWrapper.children = [newDropChild.id];
rowWrapper.parents = (dropEntity.parents || []).concat(dropEntity.id);
newEntities[rowWrapper.id] = rowWrapper;
newDropChild = rowWrapper;
newDropChild.parents = rowWrapper.parents.concat(rowWrapper.id);
newDropChild = rowWrapper;
} else if (dragType === TABS_TYPE) {
// create a new tab component
const tabChild = newComponentFactory(TAB_TYPE);

View File

@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
const stopPeriodicRender = (refreshTimer?: number) => {
export const stopPeriodicRender = (refreshTimer?: number) => {
if (refreshTimer) {
clearInterval(refreshTimer);
}

View File

@@ -22,6 +22,11 @@ import { FeatureFlag, isFeatureEnabled } from '../featureFlags';
import { Filters } from '../dashboard/reducers/types';
import { getInitialDataMask } from './reducer';
export const CLEAR_DATA_MASK_STATE = 'CLEAR_DATA_MASK_STATE';
export interface ClearDataMaskState {
type: typeof CLEAR_DATA_MASK_STATE;
}
export const UPDATE_DATA_MASK = 'UPDATE_DATA_MASK';
export interface UpdateDataMask {
type: typeof UPDATE_DATA_MASK;
@@ -81,7 +86,14 @@ export function clearDataMask(filterId: string | number) {
);
}
export function clearDataMaskState(): ClearDataMaskState {
return {
type: CLEAR_DATA_MASK_STATE,
};
}
export type AnyDataMaskAction =
| ClearDataMaskState
| UpdateDataMask
| SetDataMaskForFilterConfigFail
| SetDataMaskForFilterConfigComplete;

View File

@@ -29,6 +29,7 @@ import { URL_PARAMS } from 'src/constants';
import { DataMaskStateWithId, DataMaskWithId } from './types';
import {
AnyDataMaskAction,
CLEAR_DATA_MASK_STATE,
SET_DATA_MASK_FOR_FILTER_CONFIG_COMPLETE,
UPDATE_DATA_MASK,
} from './actions';
@@ -104,6 +105,8 @@ const dataMaskReducer = produce(
(draft: DataMaskStateWithId, action: AnyDataMaskAction) => {
const cleanState = {};
switch (action.type) {
case CLEAR_DATA_MASK_STATE:
return cleanState;
case UPDATE_DATA_MASK:
draft[action.filterId] = {
...getInitialDataMask(action.filterId),

View File

@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import rison from 'rison';
import React from 'react';
import PropTypes from 'prop-types';
import { Row, Col } from 'src/common/components';
@@ -112,7 +113,7 @@ const ColumnButtonWrapper = styled.div`
const checkboxGenerator = (d, onChange) => (
<CheckboxControl value={d} onChange={onChange} />
);
const DATA_TYPES = ['STRING', 'NUMERIC', 'DATETIME'];
const DATA_TYPES = ['STRING', 'NUMERIC', 'DATETIME', 'BOOLEAN'];
const DATASOURCE_TYPES_ARR = [
{ key: 'physical', label: t('Physical (table or view)') },
@@ -390,7 +391,7 @@ class DatasourceEditor extends React.PureComponent {
this.setState(prevState => ({ isEditMode: !prevState.isEditMode }));
}
onDatasourceChange(datasource, callback) {
onDatasourceChange(datasource, callback = this.validateAndChange) {
this.setState({ datasource }, callback);
}
@@ -459,13 +460,13 @@ class DatasourceEditor extends React.PureComponent {
results.added.push(col.name);
} else if (
currentCol.type !== col.type ||
currentCol.is_dttm !== col.is_dttm
(!currentCol.is_dttm && col.is_dttm)
) {
// modified column
finalColumns.push({
...currentCol,
type: col.type,
is_dttm: col.is_dttm,
is_dttm: currentCol.is_dttm || col.is_dttm,
});
results.modified.push(col.name);
} else {
@@ -485,11 +486,22 @@ class DatasourceEditor extends React.PureComponent {
syncMetadata() {
const { datasource } = this.state;
const endpoint = `/datasource/external_metadata_by_name/${
datasource.type || datasource.datasource_type
}/${datasource.database.database_name || datasource.database.name}/${
datasource.schema
}/${datasource.table_name}/`;
const params = {
datasource_type: datasource.type || datasource.datasource_type,
database_name:
datasource.database.database_name || datasource.database.name,
schema_name: datasource.schema,
table_name: datasource.table_name,
};
Object.entries(params).forEach(([key, value]) => {
// rison can't encode the undefined value
if (value === undefined) {
params[key] = null;
}
});
const endpoint = `/datasource/external_metadata_by_name/?q=${rison.encode(
params,
)}`;
this.setState({ metadataLoading: true });
SupersetClient.get({ endpoint })
@@ -616,7 +628,13 @@ class DatasourceEditor extends React.PureComponent {
'values from the table. Typically the intent would be to limit the scan ' +
'by applying a relative time filter on a partitioned or indexed time-related field.',
)}
control={<TextControl controlId="fetch_values_predicate" />}
control={
<TextAreaControl
language="sql"
controlId="fetch_values_predicate"
minLines={5}
/>
}
/>
)}
{this.state.isSqla && (

View File

@@ -55,11 +55,15 @@ const CopyNode = (
export const CopyToClipboardButton = ({
data,
columns,
}: {
data?: Record<string, any>;
columns?: string[];
}) => (
<CopyToClipboard
text={data ? prepareCopyToClipboardTabularData(data) : ''}
text={
data && columns ? prepareCopyToClipboardTabularData(data, columns) : ''
}
wrapped={false}
copyNode={CopyNode}
/>
@@ -113,29 +117,32 @@ export const useFilteredTableData = (
}, [data, filterText]);
export const useTableColumns = (
colnames?: string[],
data?: Record<string, any>[],
moreConfigs?: { [key: string]: Partial<Column> },
) =>
useMemo(
() =>
data?.length
? Object.keys(data[0]).map(
key =>
({
accessor: row => row[key],
Header: key,
Cell: ({ value }) => {
if (value === true) {
return BOOL_TRUE_DISPLAY;
}
if (value === false) {
return BOOL_FALSE_DISPLAY;
}
return String(value);
},
...moreConfigs?.[key],
} as Column),
)
colnames && data?.length
? colnames
.filter((column: string) => Object.keys(data[0]).includes(column))
.map(
key =>
({
accessor: row => row[key],
Header: key,
Cell: ({ value }) => {
if (value === true) {
return BOOL_TRUE_DISPLAY;
}
if (value === false) {
return BOOL_FALSE_DISPLAY;
}
return String(value);
},
...moreConfigs?.[key],
} as Column),
)
: [],
[data, moreConfigs],
);

View File

@@ -42,9 +42,10 @@ const data = [
[unicodeKey]: unicodeKey,
},
];
const all_columns = ['col01', 'col02', 'col03', asciiKey, unicodeKey];
test('useTableColumns with no options', () => {
const hook = renderHook(() => useTableColumns(data));
const hook = renderHook(() => useTableColumns(all_columns, data));
expect(hook.result.current).toEqual([
{
Cell: expect.any(Function),
@@ -83,7 +84,7 @@ test('useTableColumns with no options', () => {
test('use only the first record columns', () => {
const newData = [data[3], data[0]];
const hook = renderHook(() => useTableColumns(newData));
const hook = renderHook(() => useTableColumns(all_columns, newData));
expect(hook.result.current).toEqual([
{
Cell: expect.any(Function),
@@ -134,7 +135,9 @@ test('use only the first record columns', () => {
});
test('useTableColumns with options', () => {
const hook = renderHook(() => useTableColumns(data, { col01: { id: 'ID' } }));
const hook = renderHook(() =>
useTableColumns(all_columns, data, { col01: { id: 'ID' } }),
);
expect(hook.result.current).toEqual([
{
Cell: expect.any(Function),

View File

@@ -58,6 +58,11 @@ const createProps = () => ({
tableSectionHeight: 156.9,
chartStatus: 'rendered',
onCollapseChange: jest.fn(),
queriesResponse: [
{
colnames: [],
},
],
});
afterAll(() => {

View File

@@ -112,6 +112,7 @@ export const DataTablesPane = ({
chartStatus,
ownState,
errorMessage,
queriesResponse,
}: {
queryFormData: Record<string, any>;
tableSectionHeight: number;
@@ -119,6 +120,7 @@ export const DataTablesPane = ({
ownState?: JsonObject;
onCollapseChange: (openPanelName: string) => void;
errorMessage?: JSX.Element;
queriesResponse: Record<string, any>;
}) => {
const [data, setData] = useState<{
[RESULT_TYPES.results]?: Record<string, any>[];
@@ -128,6 +130,7 @@ export const DataTablesPane = ({
[RESULT_TYPES.results]: true,
[RESULT_TYPES.samples]: true,
});
const [columnNames, setColumnNames] = useState<string[]>([]);
const [error, setError] = useState(NULLISH_RESULTS_STATE);
const [filterText, setFilterText] = useState('');
const [activeTabKey, setActiveTabKey] = useState<string>(
@@ -218,7 +221,14 @@ export const DataTablesPane = ({
...prevState,
[RESULT_TYPES.samples]: true,
}));
}, [queryFormData.adhoc_filters, queryFormData.datasource]);
}, [queryFormData?.adhoc_filters, queryFormData?.datasource]);
useEffect(() => {
if (queriesResponse && chartStatus === 'success') {
const { colnames } = queriesResponse[0];
setColumnNames([...colnames]);
}
}, [queriesResponse]);
useEffect(() => {
if (panelOpen && isRequestPending[RESULT_TYPES.results]) {
@@ -277,9 +287,17 @@ export const DataTablesPane = ({
),
};
// this is to preserve the order of the columns, even if there are integer values,
// while also only grabbing the first column's keys
const columns = {
[RESULT_TYPES.results]: useTableColumns(data[RESULT_TYPES.results]),
[RESULT_TYPES.samples]: useTableColumns(data[RESULT_TYPES.samples]),
[RESULT_TYPES.results]: useTableColumns(
columnNames,
data[RESULT_TYPES.results],
),
[RESULT_TYPES.samples]: useTableColumns(
columnNames,
data[RESULT_TYPES.samples],
),
};
const renderDataTable = (type: string) => {
@@ -316,7 +334,7 @@ export const DataTablesPane = ({
const TableControls = (
<TableControlsWrapper>
<RowCount data={data[activeTabKey]} loading={isLoading[activeTabKey]} />
<CopyToClipboardButton data={data[activeTabKey]} />
<CopyToClipboardButton data={data[activeTabKey]} columns={columnNames} />
<FilterInput onChangeHandler={setFilterText} />
</TableControlsWrapper>
);

View File

@@ -48,7 +48,7 @@ const createProps = () => ({
cache_timeout: null,
changed_on: '2021-03-19T16:30:56.750230',
changed_on_humanized: '3 days ago',
datasource: 'FCC 2018 Survey',
datasource: 'FCC Survey Results',
description: null,
description_markeddown: '',
edit_url: '/chart/edit/318',

View File

@@ -91,6 +91,7 @@ const StyledHeader = styled.div`
}
.action-button {
color: ${({ theme }) => theme.colors.grayscale.base};
margin: 0 ${({ theme }) => theme.gridUnit * 1.5}px 0
${({ theme }) => theme.gridUnit}px;
}
@@ -116,8 +117,8 @@ export class ExploreChartHeader extends React.PureComponent {
}
componentDidMount() {
const { user, chart } = this.props;
if (user && isFeatureEnabled(FeatureFlag.ALERT_REPORTS)) {
if (this.canAddReports()) {
const { user, chart } = this.props;
// this is in the case that there is an anonymous user.
this.props.fetchUISpecificReport(
user.userId,
@@ -164,33 +165,32 @@ export class ExploreChartHeader extends React.PureComponent {
renderReportModal() {
const attachedReportExists = !!Object.keys(this.props.reports).length;
const canAddReports = isFeatureEnabled(FeatureFlag.ALERT_REPORTS);
return (
(canAddReports || null) &&
(attachedReportExists ? (
<HeaderReportActionsDropdown
showReportModal={this.showReportModal}
hideReportModal={this.hideReportModal}
toggleActive={this.props.toggleActive}
deleteActiveReport={this.props.deleteActiveReport}
/>
) : (
<>
<span
role="button"
title={t('Schedule email report')}
tabIndex={0}
className="action-button"
onClick={this.showReportModal}
>
<Icons.Calendar />
</span>
</>
))
return attachedReportExists ? (
<HeaderReportActionsDropdown
showReportModal={this.showReportModal}
hideReportModal={this.hideReportModal}
toggleActive={this.props.toggleActive}
deleteActiveReport={this.props.deleteActiveReport}
/>
) : (
<>
<span
role="button"
title={t('Schedule email report')}
tabIndex={0}
className="action-button"
onClick={this.showReportModal}
>
<Icons.Calendar />
</span>
</>
);
}
canAddReports() {
if (!isFeatureEnabled(FeatureFlag.ALERT_REPORTS)) {
return false;
}
const { user } = this.props;
if (!user) {
// this is in the case that there is an anonymous user.
@@ -199,7 +199,7 @@ export class ExploreChartHeader extends React.PureComponent {
const roles = Object.keys(user.roles || []);
const permissions = roles.map(key =>
user.roles[key].filter(
perms => perms[0] === 'can_add' && perms[1] === 'AlertModelView',
perms => perms[0] === 'menu_access' && perms[1] === 'Manage',
),
);
return permissions[0].length > 0;
@@ -294,7 +294,7 @@ export class ExploreChartHeader extends React.PureComponent {
props={{
userId: this.props.user.userId,
userEmail: this.props.user.email,
chartId: this.props.chart.id,
chart: this.props.chart,
creationMethod: 'charts',
}}
/>

View File

@@ -116,7 +116,6 @@ const ExploreChartPanel = props => {
const theme = useTheme();
const gutterMargin = theme.gridUnit * GUTTER_SIZE_FACTOR;
const gutterHeight = theme.gridUnit * GUTTER_SIZE_FACTOR;
const { height: hHeight, ref: headerRef } = useResizeDetector({
refreshMode: 'debounce',
refreshRate: 300,
@@ -128,11 +127,10 @@ const ExploreChartPanel = props => {
const [splitSizes, setSplitSizes] = useState(
getFromLocalStorage(STORAGE_KEYS.sizes, INITIAL_SIZES),
);
const { slice } = props;
const updateQueryContext = useCallback(
async function fetchChartData() {
if (slice && slice.query_context === null) {
if (props.can_overwrite && slice && slice.query_context === null) {
const queryContext = buildV1ChartDataPayload({
formData: slice.form_data,
force: false,
@@ -147,12 +145,14 @@ const ExploreChartPanel = props => {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query_context: JSON.stringify(queryContext),
query_context_generation: true,
}),
});
}
},
[slice],
);
useEffect(() => {
updateQueryContext();
}, [updateQueryContext]);
@@ -210,7 +210,6 @@ const ExploreChartPanel = props => {
}
setSplitSizes(splitSizes);
};
const renderChart = useCallback(() => {
const { chart, vizType } = props;
const newHeight =
@@ -259,6 +258,22 @@ const ExploreChartPanel = props => {
[chartPanelRef, renderChart],
);
const [queryFormData, setQueryFormData] = useState(
props.chart.latestQueryFormData,
);
useEffect(() => {
// only update when `latestQueryFormData` changes AND `triggerRender`
// is false. No update should be done when only `triggerRender` changes,
// as this can trigger a query downstream based on incomplete form data.
// (`latestQueryFormData` is only updated when a a valid request has been
// triggered).
if (!props.triggerRender) {
setQueryFormData(props.chart.latestQueryFormData);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.chart.latestQueryFormData]);
if (props.standalone) {
// dom manipulation hack to get rid of the boostrap theme's body background
const standaloneClass = 'background-transparent';
@@ -311,11 +326,12 @@ const ExploreChartPanel = props => {
{panelBody}
<DataTablesPane
ownState={props.ownState}
queryFormData={props.chart.latestQueryFormData}
queryFormData={queryFormData}
tableSectionHeight={tableSectionHeight}
onCollapseChange={onCollapseChange}
chartStatus={props.chart.chartStatus}
errorMessage={props.errorMessage}
queriesResponse={props.chart.queriesResponse}
/>
</Split>
)}

View File

@@ -81,7 +81,7 @@ const Styles = styled.div`
text-align: left;
position: relative;
width: 100%;
height: 100%;
max-height: 100%;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
@@ -448,6 +448,7 @@ function ExploreViewContainer(props) {
margin-bottom: 0;
}
body {
height: 100vh;
max-height: 100vh;
overflow: hidden;
}
@@ -458,7 +459,7 @@ function ExploreViewContainer(props) {
#app {
flex-basis: 100%;
overflow: hidden;
height: 100vh;
height: 100%;
}
#app-menu {
flex-shrink: 0;

View File

@@ -29,7 +29,7 @@ const createProps = () => ({
cache_timeout: null,
changed_on: '2021-03-19T16:30:56.750230',
changed_on_humanized: '7 days ago',
datasource: 'FCC 2018 Survey',
datasource: 'FCC Survey Results',
description: null,
description_markeddown: '',
edit_url: '/chart/edit/318',

View File

@@ -125,6 +125,8 @@ const ConditionalFormattingControl = ({
}: ConditionalFormattingConfig) => {
const columnName = (column && verboseMap?.[column]) ?? column;
switch (operator) {
case COMPARATOR.NONE:
return `${columnName}`;
case COMPARATOR.BETWEEN:
return `${targetValueLeft} ${COMPARATOR.LESS_THAN} ${columnName} ${COMPARATOR.LESS_THAN} ${targetValueRight}`;
case COMPARATOR.BETWEEN_OR_EQUAL:

View File

@@ -16,9 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
import React, { useCallback, useMemo } from 'react';
import React from 'react';
import { styled, t } from '@superset-ui/core';
import { Form, FormItem } from 'src/components/Form';
import { Form, FormItem, FormProps } from 'src/components/Form';
import { Select } from 'src/components';
import { Col, InputNumber, Row } from 'src/common/components';
import Button from 'src/components/Button';
@@ -44,6 +44,7 @@ const colorSchemeOptions = [
];
const operatorOptions = [
{ value: COMPARATOR.NONE, label: 'None' },
{ value: COMPARATOR.GREATER_THAN, label: '>' },
{ value: COMPARATOR.LESS_THAN, label: '<' },
{ value: COMPARATOR.GREATER_OR_EQUAL, label: '≥' },
@@ -56,6 +57,127 @@ const operatorOptions = [
{ value: COMPARATOR.BETWEEN_OR_RIGHT_EQUAL, label: '< x ≤' },
];
const targetValueValidator = (
compare: (targetValue: number, compareValue: number) => boolean,
rejectMessage: string,
) => (targetValue: number | string) => (
_: any,
compareValue: number | string,
) => {
if (
!targetValue ||
!compareValue ||
compare(Number(targetValue), Number(compareValue))
) {
return Promise.resolve();
}
return Promise.reject(new Error(rejectMessage));
};
const targetValueLeftValidator = targetValueValidator(
(target: number, val: number) => target > val,
t('This value should be smaller than the right target value'),
);
const targetValueRightValidator = targetValueValidator(
(target: number, val: number) => target < val,
t('This value should be greater than the left target value'),
);
const isOperatorMultiValue = (operator?: COMPARATOR) =>
operator && MULTIPLE_VALUE_COMPARATORS.includes(operator);
const isOperatorNone = (operator?: COMPARATOR) =>
!operator || operator === COMPARATOR.NONE;
const rulesRequired = [{ required: true, message: t('Required') }];
type GetFieldValue = Pick<Required<FormProps>['form'], 'getFieldValue'>;
const rulesTargetValueLeft = [
{ required: true, message: t('Required') },
({ getFieldValue }: GetFieldValue) => ({
validator: targetValueLeftValidator(getFieldValue('targetValueRight')),
}),
];
const rulesTargetValueRight = [
{ required: true, message: t('Required') },
({ getFieldValue }: GetFieldValue) => ({
validator: targetValueRightValidator(getFieldValue('targetValueLeft')),
}),
];
const targetValueLeftDeps = ['targetValueRight'];
const targetValueRightDeps = ['targetValueLeft'];
const shouldFormItemUpdate = (
prevValues: ConditionalFormattingConfig,
currentValues: ConditionalFormattingConfig,
) =>
isOperatorNone(prevValues.operator) !==
isOperatorNone(currentValues.operator) ||
isOperatorMultiValue(prevValues.operator) !==
isOperatorMultiValue(currentValues.operator);
const operatorField = (
<FormItem
name="operator"
label={t('Operator')}
rules={rulesRequired}
initialValue={operatorOptions[0].value}
>
<Select ariaLabel={t('Operator')} options={operatorOptions} />
</FormItem>
);
const renderOperatorFields = ({ getFieldValue }: GetFieldValue) =>
isOperatorNone(getFieldValue('operator')) ? (
<Row gutter={12}>
<Col span={6}>{operatorField}</Col>
</Row>
) : isOperatorMultiValue(getFieldValue('operator')) ? (
<Row gutter={12}>
<Col span={9}>
<FormItem
name="targetValueLeft"
label={t('Left value')}
rules={rulesTargetValueLeft}
dependencies={targetValueLeftDeps}
validateTrigger="onBlur"
trigger="onBlur"
>
<FullWidthInputNumber />
</FormItem>
</Col>
<Col span={6}>{operatorField}</Col>
<Col span={9}>
<FormItem
name="targetValueRight"
label={t('Right value')}
rules={rulesTargetValueRight}
dependencies={targetValueRightDeps}
validateTrigger="onBlur"
trigger="onBlur"
>
<FullWidthInputNumber />
</FormItem>
</Col>
</Row>
) : (
<Row gutter={12}>
<Col span={6}>{operatorField}</Col>
<Col span={18}>
<FormItem
name="targetValue"
label={t('Target value')}
rules={rulesRequired}
>
<FullWidthInputNumber />
</FormItem>
</Col>
</Row>
);
export const FormattingPopoverContent = ({
config,
onChange,
@@ -64,158 +186,44 @@ export const FormattingPopoverContent = ({
config?: ConditionalFormattingConfig;
onChange: (config: ConditionalFormattingConfig) => void;
columns: { label: string; value: string }[];
}) => {
const isOperatorMultiValue = (operator?: COMPARATOR) =>
operator && MULTIPLE_VALUE_COMPARATORS.includes(operator);
const operatorField = useMemo(
() => (
<FormItem
name="operator"
label={t('Operator')}
rules={[{ required: true, message: t('Required') }]}
initialValue={operatorOptions[0].value}
>
<Select ariaLabel={t('Operator')} options={operatorOptions} />
</FormItem>
),
[],
);
const targetValueLeftValidator = useCallback(
(rightValue?: number) => (_: any, value?: number) => {
if (!value || !rightValue || rightValue > value) {
return Promise.resolve();
}
return Promise.reject(
new Error(
t('This value should be smaller than the right target value'),
),
);
},
[],
);
const targetValueRightValidator = useCallback(
(leftValue?: number) => (_: any, value?: number) => {
if (!value || !leftValue || leftValue < value) {
return Promise.resolve();
}
return Promise.reject(
new Error(t('This value should be greater than the left target value')),
);
},
[],
);
return (
<Form
onFinish={onChange}
initialValues={config}
requiredMark="optional"
layout="vertical"
>
<Row gutter={12}>
<Col span={12}>
<FormItem
name="column"
label={t('Column')}
rules={[{ required: true, message: t('Required') }]}
initialValue={columns[0]?.value}
>
<Select ariaLabel={t('Select column')} options={columns} />
</FormItem>
</Col>
<Col span={12}>
<FormItem
name="colorScheme"
label={t('Color scheme')}
rules={[{ required: true, message: t('Required') }]}
initialValue={colorSchemeOptions[0].value}
>
<Select
ariaLabel={t('Color scheme')}
options={colorSchemeOptions}
/>
</FormItem>
</Col>
</Row>
<FormItem
noStyle
shouldUpdate={(
prevValues: ConditionalFormattingConfig,
currentValues: ConditionalFormattingConfig,
) =>
isOperatorMultiValue(prevValues.operator) !==
isOperatorMultiValue(currentValues.operator)
}
>
{({ getFieldValue }) =>
isOperatorMultiValue(getFieldValue('operator')) ? (
<Row gutter={12}>
<Col span={9}>
<FormItem
name="targetValueLeft"
label={t('Left value')}
rules={[
{ required: true, message: t('Required') },
({ getFieldValue }) => ({
validator: targetValueLeftValidator(
getFieldValue('targetValueRight'),
),
}),
]}
dependencies={['targetValueRight']}
validateTrigger="onBlur"
trigger="onBlur"
>
<FullWidthInputNumber />
</FormItem>
</Col>
<Col span={6}>{operatorField}</Col>
<Col span={9}>
<FormItem
name="targetValueRight"
label={t('Right value')}
rules={[
{ required: true, message: t('Required') },
({ getFieldValue }) => ({
validator: targetValueRightValidator(
getFieldValue('targetValueLeft'),
),
}),
]}
dependencies={['targetValueLeft']}
validateTrigger="onBlur"
trigger="onBlur"
>
<FullWidthInputNumber />
</FormItem>
</Col>
</Row>
) : (
<Row gutter={12}>
<Col span={6}>{operatorField}</Col>
<Col span={18}>
<FormItem
name="targetValue"
label={t('Target value')}
rules={[{ required: true, message: t('Required') }]}
>
<FullWidthInputNumber />
</FormItem>
</Col>
</Row>
)
}
</FormItem>
<FormItem>
<JustifyEnd>
<Button htmlType="submit" buttonStyle="primary">
{t('Apply')}
</Button>
</JustifyEnd>
</FormItem>
</Form>
);
};
}) => (
<Form
onFinish={onChange}
initialValues={config}
requiredMark="optional"
layout="vertical"
>
<Row gutter={12}>
<Col span={12}>
<FormItem
name="column"
label={t('Column')}
rules={rulesRequired}
initialValue={columns[0]?.value}
>
<Select ariaLabel={t('Select column')} options={columns} />
</FormItem>
</Col>
<Col span={12}>
<FormItem
name="colorScheme"
label={t('Color scheme')}
rules={rulesRequired}
initialValue={colorSchemeOptions[0].value}
>
<Select ariaLabel={t('Color scheme')} options={colorSchemeOptions} />
</FormItem>
</Col>
</Row>
<FormItem noStyle shouldUpdate={shouldFormItemUpdate}>
{renderOperatorFields}
</FormItem>
<FormItem>
<JustifyEnd>
<Button htmlType="submit" buttonStyle="primary">
{t('Apply')}
</Button>
</JustifyEnd>
</FormItem>
</Form>
);

View File

@@ -22,6 +22,7 @@ import { PopoverProps } from 'antd/lib/popover';
import { ControlComponentProps } from '@superset-ui/chart-controls/lib/shared-controls/components/types';
export enum COMPARATOR {
NONE = 'None',
GREATER_THAN = '>',
LESS_THAN = '<',
GREATER_OR_EQUAL = '≥',

View File

@@ -29,7 +29,7 @@ const defaultProps: LabelProps = {
test('renders with default props', () => {
render(<DndColumnSelect {...defaultProps} />, { useDnd: true });
expect(screen.getByText('Drop columns')).toBeInTheDocument();
expect(screen.getByText('Drop columns here')).toBeInTheDocument();
});
test('renders with value', () => {

View File

@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import React, { useCallback, useEffect, useMemo } from 'react';
import React, { useCallback, useMemo } from 'react';
import { tn } from '@superset-ui/core';
import { ColumnMeta } from '@superset-ui/chart-controls';
import { isEmpty } from 'lodash';
@@ -27,6 +27,7 @@ import { OptionSelector } from 'src/explore/components/controls/DndColumnSelectC
import { DatasourcePanelDndItem } from 'src/explore/components/DatasourcePanel/types';
import { DndItemType } from 'src/explore/components/DndItemType';
import { StyledColumnOption } from 'src/explore/components/optionRenderers';
import { useComponentDidUpdate } from 'src/common/hooks/useComponentDidUpdate';
export const DndColumnSelect = (props: LabelProps) => {
const {
@@ -45,7 +46,7 @@ export const DndColumnSelect = (props: LabelProps) => {
);
// synchronize values in case of dataset changes
useEffect(() => {
const handleOptionsChange = useCallback(() => {
const optionSelectorValues = optionSelector.getValues();
if (typeof value !== typeof optionSelectorValues) {
onChange(optionSelectorValues);
@@ -65,9 +66,12 @@ export const DndColumnSelect = (props: LabelProps) => {
) {
onChange(optionSelectorValues);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [JSON.stringify(value), JSON.stringify(optionSelector.getValues())]);
// useComponentDidUpdate to avoid running this for the first render, to avoid
// calling onChange when the initial value is not valid for the dataset
useComponentDidUpdate(handleOptionsChange);
const onDrop = useCallback(
(item: DatasourcePanelDndItem) => {
const column = item.value as ColumnMeta;
@@ -139,7 +143,8 @@ export const DndColumnSelect = (props: LabelProps) => {
accept={DndItemType.Column}
displayGhostButton={multi || optionSelector.values.length === 0}
ghostButtonText={
ghostButtonText || tn('Drop column', 'Drop columns', multi ? 2 : 1)
ghostButtonText ||
tn('Drop column here', 'Drop columns here', multi ? 2 : 1)
}
{...props}
/>

View File

@@ -38,7 +38,7 @@ const defaultProps = {
test('renders with default props', () => {
render(<DndFilterSelect {...defaultProps} />, { useDnd: true });
expect(screen.getByText('Drop columns or metrics')).toBeInTheDocument();
expect(screen.getByText('Drop columns or metrics here')).toBeInTheDocument();
});
test('renders with value', () => {
@@ -56,7 +56,7 @@ test('renders options with saved metric', () => {
render(<DndFilterSelect {...defaultProps} formData={['saved_metric']} />, {
useDnd: true,
});
expect(screen.getByText('Drop columns or metrics')).toBeInTheDocument();
expect(screen.getByText('Drop columns or metrics here')).toBeInTheDocument();
});
test('renders options with column', () => {
@@ -76,7 +76,7 @@ test('renders options with column', () => {
useDnd: true,
},
);
expect(screen.getByText('Drop columns or metrics')).toBeInTheDocument();
expect(screen.getByText('Drop columns or metrics here')).toBeInTheDocument();
});
test('renders options with adhoc metric', () => {
@@ -87,5 +87,5 @@ test('renders options with adhoc metric', () => {
render(<DndFilterSelect {...defaultProps} formData={[adhocMetric]} />, {
useDnd: true,
});
expect(screen.getByText('Drop columns or metrics')).toBeInTheDocument();
expect(screen.getByText('Drop columns or metrics here')).toBeInTheDocument();
});

View File

@@ -374,7 +374,7 @@ export const DndFilterSelect = (props: DndFilterSelectProps) => {
canDrop={canDrop}
valuesRenderer={valuesRenderer}
accept={DND_ACCEPTED_TYPES}
ghostButtonText={t('Drop columns or metrics')}
ghostButtonText={t('Drop columns or metrics here')}
{...props}
/>
<AdhocFilterPopoverTrigger

View File

@@ -31,10 +31,10 @@ const defaultProps = {
test('renders with default props', () => {
render(<DndMetricSelect {...defaultProps} />, { useDnd: true });
expect(screen.getByText('Drop column or metric')).toBeInTheDocument();
expect(screen.getByText('Drop column or metric here')).toBeInTheDocument();
});
test('renders with default props and multi = true', () => {
render(<DndMetricSelect {...defaultProps} multi />, { useDnd: true });
expect(screen.getByText('Drop columns or metrics')).toBeInTheDocument();
expect(screen.getByText('Drop columns or metrics here')).toBeInTheDocument();
});

View File

@@ -185,6 +185,9 @@ export const DndMetricSelect = (props: any) => {
const onMetricEdit = useCallback(
(changedMetric: Metric | AdhocMetric, oldMetric: Metric | AdhocMetric) => {
if (oldMetric instanceof AdhocMetric && oldMetric.equals(changedMetric)) {
return;
}
const newValue = value.map(value => {
if (
// compare saved metrics
@@ -245,7 +248,10 @@ export const DndMetricSelect = (props: any) => {
[props.savedMetrics, props.value],
);
const handleDropLabel = useCallback(() => onChange(value), [onChange, value]);
const handleDropLabel = useCallback(
() => onChange(multi ? value : value[0]),
[multi, onChange, value],
);
const valueRenderer = useCallback(
(option: Metric | AdhocMetric | string, index: number) => (
@@ -262,12 +268,14 @@ export const DndMetricSelect = (props: any) => {
onMoveLabel={moveLabel}
onDropLabel={handleDropLabel}
type={`${DndItemType.AdhocMetricOption}_${props.name}_${props.label}`}
multi={multi}
/>
),
[
getSavedMetricOptionsForMetric,
handleDropLabel,
moveLabel,
multi,
onMetricEdit,
onRemoveMetric,
props.columns,
@@ -334,8 +342,8 @@ export const DndMetricSelect = (props: any) => {
valuesRenderer={valuesRenderer}
accept={DND_ACCEPTED_TYPES}
ghostButtonText={tn(
'Drop column or metric',
'Drop columns or metrics',
'Drop column or metric here',
'Drop columns or metrics here',
multi ? 2 : 1,
)}
displayGhostButton={multi || value.length === 0}

View File

@@ -33,7 +33,7 @@ const defaultProps = {
test('renders with default props', async () => {
render(<DndSelectLabel {...defaultProps} />, { useDnd: true });
expect(await screen.findByText('Drop columns')).toBeInTheDocument();
expect(await screen.findByText('Drop columns here')).toBeInTheDocument();
});
test('renders ghost button when empty', async () => {

View File

@@ -55,7 +55,7 @@ export default function DndSelectLabel<T, O>({
return (
<AddControlLabel cancelHover>
<Icons.PlusSmall iconColor={theme.colors.grayscale.light1} />
{t(props.ghostButtonText || 'Drop columns')}
{t(props.ghostButtonText || 'Drop columns here')}
</AddControlLabel>
);
}

View File

@@ -64,7 +64,7 @@ export class OptionSelector {
}
has(value: string): boolean {
return !!this.getValues()?.includes(value);
return ensureIsArray(this.getValues()).includes(value);
}
getValues(): string[] | string | undefined {

View File

@@ -37,6 +37,7 @@ const propTypes = {
onDropLabel: PropTypes.func,
index: PropTypes.number,
type: PropTypes.string,
multi: PropTypes.bool,
};
class AdhocMetricOption extends React.PureComponent {
@@ -62,6 +63,7 @@ class AdhocMetricOption extends React.PureComponent {
onDropLabel,
index,
type,
multi,
} = this.props;
return (
@@ -84,6 +86,7 @@ class AdhocMetricOption extends React.PureComponent {
type={type ?? DndItemType.AdhocMetricOption}
withCaret
isFunction
multi={multi}
/>
</AdhocMetricPopoverTrigger>
);

View File

@@ -49,6 +49,7 @@ export default function MetricDefinitionValue({
onDropLabel,
index,
type,
multi,
}) {
const getSavedMetricByName = metricName =>
savedMetrics.find(metric => metric.metric_name === metricName);
@@ -76,6 +77,7 @@ export default function MetricDefinitionValue({
index,
savedMetric: savedMetric ?? {},
type,
multi,
};
return <AdhocMetricOption {...metricOptionProps} />;

View File

@@ -16,16 +16,10 @@
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import { t, withTheme } from '@superset-ui/core';
import { isEqual } from 'lodash';
import { ensureIsArray, t, useTheme } from '@superset-ui/core';
import ControlHeader from 'src/explore/components/ControlHeader';
import {
AGGREGATES_OPTIONS,
sqlaAutoGeneratedMetricNameRegex,
druidAutoGeneratedMetricRegex,
} from 'src/explore/constants';
import Icons from 'src/components/Icons';
import {
AddIconButton,
@@ -34,7 +28,6 @@ import {
LabelsContainer,
} from 'src/explore/components/controls/OptionControls';
import columnType from './columnType';
import MetricDefinitionOption from './MetricDefinitionOption';
import MetricDefinitionValue from './MetricDefinitionValue';
import AdhocMetric from './AdhocMetric';
import savedMetricType from './savedMetricType';
@@ -82,9 +75,9 @@ function isDictionaryForAdhocMetric(value) {
return value && !(value instanceof AdhocMetric) && value.expressionType;
}
function columnsContainAllMetrics(value, nextProps) {
function columnsContainAllMetrics(value, columns, savedMetrics) {
const columnNames = new Set(
[...(nextProps.columns || []), ...(nextProps.savedMetrics || [])]
[...(columns || []), ...(savedMetrics || [])]
// eslint-disable-next-line camelcase
.map(({ column_name, metric_name }) => column_name || metric_name),
);
@@ -123,294 +116,228 @@ function coerceAdhocMetrics(value) {
});
}
class MetricsControl extends React.PureComponent {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
this.onMetricEdit = this.onMetricEdit.bind(this);
this.onNewMetric = this.onNewMetric.bind(this);
this.onRemoveMetric = this.onRemoveMetric.bind(this);
this.moveLabel = this.moveLabel.bind(this);
this.checkIfAggregateInInput = this.checkIfAggregateInInput.bind(this);
this.optionsForSelect = this.optionsForSelect.bind(this);
this.selectFilterOption = this.selectFilterOption.bind(this);
this.isAutoGeneratedMetric = this.isAutoGeneratedMetric.bind(this);
this.optionRenderer = option => <MetricDefinitionOption option={option} />;
this.valueRenderer = (option, index) => (
const emptySavedMetric = { metric_name: '', expression: '' };
const MetricsControl = ({
onChange,
multi,
value: propsValue,
columns,
savedMetrics,
datasource,
datasourceType,
...props
}) => {
const [value, setValue] = useState(coerceAdhocMetrics(propsValue));
const theme = useTheme();
const handleChange = useCallback(
opts => {
// if clear out options
if (opts === null) {
onChange(null);
return;
}
const transformedOpts = ensureIsArray(opts);
const optionValues = transformedOpts
.map(option => {
// pre-defined metric
if (option.metric_name) {
return option.metric_name;
}
return option;
})
.filter(option => option);
onChange(multi ? optionValues : optionValues[0]);
},
[multi, onChange],
);
const onNewMetric = useCallback(
newMetric => {
const newValue = [...value, newMetric];
setValue(newValue);
handleChange(newValue);
},
[handleChange, value],
);
const onMetricEdit = useCallback(
(changedMetric, oldMetric) => {
const newValue = value.map(val => {
if (
// compare saved metrics
val === oldMetric.metric_name ||
// compare adhoc metrics
typeof val.optionName !== 'undefined'
? val.optionName === oldMetric.optionName
: false
) {
return changedMetric;
}
return val;
});
setValue(newValue);
handleChange(newValue);
},
[handleChange, value],
);
const onRemoveMetric = useCallback(
index => {
if (!Array.isArray(value)) {
return;
}
const valuesCopy = [...value];
valuesCopy.splice(index, 1);
setValue(valuesCopy);
handleChange(valuesCopy);
},
[handleChange, value],
);
const moveLabel = useCallback(
(dragIndex, hoverIndex) => {
const newValues = [...value];
[newValues[hoverIndex], newValues[dragIndex]] = [
newValues[dragIndex],
newValues[hoverIndex],
];
setValue(newValues);
},
[value],
);
const isAddNewMetricDisabled = useCallback(() => !multi && value.length > 0, [
multi,
value.length,
]);
const savedMetricOptions = useMemo(
() => getOptionsForSavedMetrics(savedMetrics, propsValue, null),
[propsValue, savedMetrics],
);
const newAdhocMetric = useMemo(() => new AdhocMetric({ isNew: true }), [
value,
]);
const addNewMetricPopoverTrigger = useCallback(
trigger => {
if (isAddNewMetricDisabled()) {
return trigger;
}
return (
<AdhocMetricPopoverTrigger
adhocMetric={newAdhocMetric}
onMetricEdit={onNewMetric}
columns={columns}
savedMetricsOptions={savedMetricOptions}
datasource={datasource}
savedMetric={emptySavedMetric}
datasourceType={datasourceType}
createNew
>
{trigger}
</AdhocMetricPopoverTrigger>
);
},
[
columns,
datasource,
datasourceType,
isAddNewMetricDisabled,
newAdhocMetric,
onNewMetric,
savedMetricOptions,
],
);
useEffect(() => {
// Remove all metrics if selected value no longer a valid column
// in the dataset. Must use `nextProps` here because Redux reducers may
// have already updated the value for this control.
if (!columnsContainAllMetrics(propsValue, columns, savedMetrics)) {
handleChange([]);
}
}, [columns, savedMetrics]);
useEffect(() => {
setValue(coerceAdhocMetrics(propsValue));
}, [propsValue]);
const onDropLabel = useCallback(() => handleChange(value), [
handleChange,
value,
]);
const valueRenderer = useCallback(
(option, index) => (
<MetricDefinitionValue
key={index}
index={index}
option={option}
onMetricEdit={this.onMetricEdit}
onRemoveMetric={this.onRemoveMetric}
columns={this.props.columns}
datasource={this.props.datasource}
savedMetrics={this.props.savedMetrics}
onMetricEdit={onMetricEdit}
onRemoveMetric={onRemoveMetric}
columns={columns}
datasource={datasource}
savedMetrics={savedMetrics}
savedMetricsOptions={getOptionsForSavedMetrics(
this.props.savedMetrics,
this.props.value,
this.props.value?.[index],
savedMetrics,
value,
value?.[index],
)}
datasourceType={this.props.datasourceType}
onMoveLabel={this.moveLabel}
onDropLabel={() => this.props.onChange(this.state.value)}
datasourceType={datasourceType}
onMoveLabel={moveLabel}
onDropLabel={onDropLabel}
multi={multi}
/>
);
this.select = null;
this.selectRef = ref => {
if (ref) {
this.select = ref.select;
} else {
this.select = null;
}
};
this.state = {
aggregateInInput: null,
options: this.optionsForSelect(this.props),
value: coerceAdhocMetrics(this.props.value),
};
}
),
[
columns,
datasource,
datasourceType,
moveLabel,
multi,
onDropLabel,
onMetricEdit,
onRemoveMetric,
savedMetrics,
value,
],
);
UNSAFE_componentWillReceiveProps(nextProps) {
const { value } = this.props;
if (
!isEqual(this.props.columns, nextProps.columns) ||
!isEqual(this.props.savedMetrics, nextProps.savedMetrics)
) {
this.setState({ options: this.optionsForSelect(nextProps) });
// Remove all metrics if selected value no longer a valid column
// in the dataset. Must use `nextProps` here because Redux reducers may
// have already updated the value for this control.
if (!columnsContainAllMetrics(nextProps.value, nextProps)) {
this.props.onChange([]);
}
}
if (value !== nextProps.value) {
this.setState({ value: coerceAdhocMetrics(nextProps.value) });
}
}
onNewMetric(newMetric) {
this.setState(
prevState => ({
...prevState,
value: [...prevState.value, newMetric],
}),
() => {
this.onChange(this.state.value);
},
);
}
onMetricEdit(changedMetric, oldMetric) {
this.setState(
prevState => ({
value: prevState.value.map(value => {
if (
// compare saved metrics
value === oldMetric.metric_name ||
// compare adhoc metrics
typeof value.optionName !== 'undefined'
? value.optionName === oldMetric.optionName
: false
) {
return changedMetric;
}
return value;
}),
}),
() => {
this.onChange(this.state.value);
},
);
}
onRemoveMetric(index) {
if (!Array.isArray(this.state.value)) {
return;
}
const valuesCopy = [...this.state.value];
valuesCopy.splice(index, 1);
this.setState(prevState => ({
...prevState,
value: valuesCopy,
}));
this.props.onChange(valuesCopy);
}
onChange(opts) {
// if clear out options
if (opts === null) {
this.props.onChange(null);
return;
}
let transformedOpts;
if (Array.isArray(opts)) {
transformedOpts = opts;
} else {
transformedOpts = opts ? [opts] : [];
}
const optionValues = transformedOpts
.map(option => {
// pre-defined metric
if (option.metric_name) {
return option.metric_name;
}
return option;
})
.filter(option => option);
this.props.onChange(this.props.multi ? optionValues : optionValues[0]);
}
moveLabel(dragIndex, hoverIndex) {
const { value } = this.state;
const newValues = [...value];
[newValues[hoverIndex], newValues[dragIndex]] = [
newValues[dragIndex],
newValues[hoverIndex],
];
this.setState({ value: newValues });
}
isAddNewMetricDisabled() {
return !this.props.multi && this.state.value.length > 0;
}
addNewMetricPopoverTrigger(trigger) {
if (this.isAddNewMetricDisabled()) {
return trigger;
}
return (
<AdhocMetricPopoverTrigger
adhocMetric={new AdhocMetric({ isNew: true })}
onMetricEdit={this.onNewMetric}
columns={this.props.columns}
savedMetricsOptions={getOptionsForSavedMetrics(
this.props.savedMetrics,
this.props.value,
null,
return (
<div className="metrics-select">
<HeaderContainer>
<ControlHeader {...props} />
{addNewMetricPopoverTrigger(
<AddIconButton
disabled={isAddNewMetricDisabled()}
data-test="add-metric-button"
>
<Icons.PlusLarge
iconSize="s"
iconColor={theme.colors.grayscale.light5}
/>
</AddIconButton>,
)}
datasource={this.props.datasource}
savedMetric={{ metric_name: '', expression: '' }}
datasourceType={this.props.datasourceType}
createNew
>
{trigger}
</AdhocMetricPopoverTrigger>
);
}
checkIfAggregateInInput(input) {
const lowercaseInput = input.toLowerCase();
const aggregateInInput =
AGGREGATES_OPTIONS.find(x =>
lowercaseInput.startsWith(`${x.toLowerCase()}(`),
) || null;
this.clearedAggregateInInput = this.state.aggregateInInput;
this.setState({ aggregateInInput });
}
optionsForSelect(props) {
const { columns, savedMetrics } = props;
const aggregates =
columns && columns.length
? AGGREGATES_OPTIONS.map(aggregate => ({
aggregate_name: aggregate,
}))
: [];
const options = [
...(columns || []),
...aggregates,
...(savedMetrics || []),
];
return options.reduce((results, option) => {
if (option.metric_name) {
results.push({ ...option, optionName: option.metric_name });
} else if (option.column_name) {
results.push({ ...option, optionName: `_col_${option.column_name}` });
} else if (option.aggregate_name) {
results.push({
...option,
optionName: `_aggregate_${option.aggregate_name}`,
});
}
return results;
}, []);
}
isAutoGeneratedMetric(savedMetric) {
if (this.props.datasourceType === 'druid') {
return druidAutoGeneratedMetricRegex.test(savedMetric.verbose_name);
}
return sqlaAutoGeneratedMetricNameRegex.test(savedMetric.metric_name);
}
selectFilterOption({ data: option }, filterValue) {
if (this.state.aggregateInInput) {
let endIndex = filterValue.length;
if (filterValue.endsWith(')')) {
endIndex = filterValue.length - 1;
}
const valueAfterAggregate = filterValue.substring(
filterValue.indexOf('(') + 1,
endIndex,
);
return (
option.column_name &&
option.column_name.toLowerCase().indexOf(valueAfterAggregate) >= 0
);
}
return (
option.optionName &&
(!option.metric_name ||
!this.isAutoGeneratedMetric(option) ||
option.verbose_name) &&
(option.optionName.toLowerCase().indexOf(filterValue) >= 0 ||
(option.verbose_name &&
option.verbose_name.toLowerCase().indexOf(filterValue) >= 0))
);
}
render() {
const { theme } = this.props;
return (
<div className="metrics-select">
<HeaderContainer>
<ControlHeader {...this.props} />
{this.addNewMetricPopoverTrigger(
<AddIconButton
disabled={this.isAddNewMetricDisabled()}
data-test="add-metric-button"
>
<Icons.PlusLarge
iconSize="s"
iconColor={theme.colors.grayscale.light5}
/>
</AddIconButton>,
)}
</HeaderContainer>
<LabelsContainer>
{this.state.value.length > 0
? this.state.value.map((value, index) =>
this.valueRenderer(value, index),
)
: this.addNewMetricPopoverTrigger(
<AddControlLabel>
<Icons.PlusSmall iconColor={theme.colors.grayscale.light1} />
{t('Add metric')}
</AddControlLabel>,
)}
</LabelsContainer>
</div>
);
}
}
</HeaderContainer>
<LabelsContainer>
{value.length > 0
? value.map((value, index) => valueRenderer(value, index))
: addNewMetricPopoverTrigger(
<AddControlLabel>
<Icons.PlusSmall iconColor={theme.colors.grayscale.light1} />
{t('Add metric')}
</AddControlLabel>,
)}
</LabelsContainer>
</div>
);
};
MetricsControl.propTypes = propTypes;
MetricsControl.defaultProps = defaultProps;
export default withTheme(MetricsControl);
export default MetricsControl;

View File

@@ -177,6 +177,7 @@ export const OptionControlLabel = ({
index,
isExtra,
tooltipTitle,
multi = true,
...props
}: {
label: string | React.ReactNode;
@@ -192,15 +193,22 @@ export const OptionControlLabel = ({
index: number;
isExtra?: boolean;
tooltipTitle: string;
multi?: boolean;
}) => {
const theme = useTheme();
const ref = useRef<HTMLDivElement>(null);
const [, drop] = useDrop({
accept: type,
drop() {
if (!multi) {
return;
}
onDropLabel?.();
},
hover(item: DragItem, monitor: DropTargetMonitor) {
if (!multi) {
return;
}
if (!ref.current) {
return;
}

View File

@@ -120,7 +120,7 @@ describe('VizTypeControl', () => {
expect(visualizations).toHaveTextContent(/Time-series Table/);
expect(visualizations).toHaveTextContent(/Time-series Chart/);
expect(visualizations).toHaveTextContent(/Mixed timeseries chart/);
expect(visualizations).toHaveTextContent(/Mixed Time-Series/);
expect(visualizations).not.toHaveTextContent(/Line Chart/);
});
});

View File

@@ -62,17 +62,22 @@ enum SECTIONS {
const DEFAULT_ORDER = [
'line',
'big_number',
'big_number_total',
'table',
'pivot_table_v2',
'echarts_timeseries_line',
'echarts_area',
'echarts_timeseries_bar',
'echarts_timeseries_scatter',
'pie',
'mixed_timeseries',
'filter_box',
'dist_bar',
'area',
'bar',
'deck_polygon',
'pie',
'time_table',
'pivot_table_v2',
'histogram',
'big_number_total',
'deck_scatter',
'deck_hex',
'time_pivot',
@@ -116,11 +121,7 @@ const OTHER_CATEGORY = t('Other');
const ALL_CHARTS = t('All charts');
const RECOMMENDED_TAGS = [
t('Highly-used'),
t('ECharts'),
t('Advanced-Analytics'),
];
const RECOMMENDED_TAGS = [t('Popular'), t('ECharts'), t('Advanced-Analytics')];
export const VIZ_TYPE_CONTROL_TEST_ID = 'viz-type-control';

View File

@@ -28,6 +28,7 @@ export default class FilterGroupByPlugin extends ChartPlugin {
name: t('Group By'),
description: t('Group By filter plugin'),
behaviors: [Behavior.INTERACTIVE_CHART, Behavior.NATIVE_FILTER],
tags: [t('Experimental')],
thumbnail,
});

View File

@@ -28,6 +28,7 @@ export default class RangeFilterPlugin extends ChartPlugin {
name: t('Range filter'),
description: t('Range filter plugin using AntD'),
behaviors: [Behavior.INTERACTIVE_CHART, Behavior.NATIVE_FILTER],
tags: [t('Experimental')],
thumbnail,
});

View File

@@ -20,6 +20,7 @@
import {
AppSection,
DataMask,
DataRecordValue,
ensureIsArray,
ExtraFormData,
GenericDataType,
@@ -36,7 +37,11 @@ import { useImmerReducer } from 'use-immer';
import { FormItemProps } from 'antd/lib/form';
import { PluginFilterSelectProps, SelectValue } from './types';
import { StyledFormItem, FilterPluginStyle, StatusMessage } from '../common';
import { getDataRecordFormatter, getSelectExtraFormData } from '../../utils';
import {
formatFilterValue,
getDataRecordFormatter,
getSelectExtraFormData,
} from '../../utils';
type DataMaskAction =
| { type: 'ownState'; ownState: JsonObject }
@@ -119,7 +124,7 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
filterState: {
...filterState,
label: values?.length
? `${(values || []).join(', ')}${suffix}`
? `${(values || []).map(formatFilterValue).join(', ')}${suffix}`
: undefined,
value:
appSection === AppSection.FILTER_CONFIG_MODAL && defaultToFirstItem
@@ -249,12 +254,12 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
}
const options = useMemo(() => {
const options: { label: string; value: string | number }[] = [];
const options: { label: string; value: DataRecordValue }[] = [];
data.forEach(row => {
const [value] = groupby.map(col => row[col]);
options.push({
label: labelFormatter(value, datatype),
value: typeof value === 'number' ? value : String(value),
value,
});
});
return options;
@@ -286,6 +291,7 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
loading={isRefreshing}
maxTagCount={5}
invertSelection={inverseSelection}
// @ts-ignore
options={options}
/>
</StyledFormItem>

View File

@@ -29,6 +29,7 @@ export default class FilterSelectPlugin extends ChartPlugin {
description: t('Select filter plugin using AntD'),
behaviors: [Behavior.INTERACTIVE_CHART, Behavior.NATIVE_FILTER],
enableNoResults: false,
tags: [t('Experimental')],
thumbnail,
});

View File

@@ -28,6 +28,7 @@ export default class TimeFilterPlugin extends ChartPlugin {
description: t('Custom time filter plugin'),
behaviors: [Behavior.INTERACTIVE_CHART, Behavior.NATIVE_FILTER],
thumbnail,
tags: [t('Experimental')],
datasourceCount: 0,
});

View File

@@ -28,6 +28,7 @@ export default class FilterTimeColumnPlugin extends ChartPlugin {
name: t('Time column'),
description: t('Time column filter plugin'),
behaviors: [Behavior.INTERACTIVE_CHART, Behavior.NATIVE_FILTER],
tags: [t('Experimental')],
thumbnail,
});

View File

@@ -28,6 +28,7 @@ export default class FilterTimeGrainPlugin extends ChartPlugin {
name: t('Time grain'),
description: t('Time grain filter plugin'),
behaviors: [Behavior.INTERACTIVE_CHART, Behavior.NATIVE_FILTER],
tags: [t('Experimental')],
thumbnail,
});

View File

@@ -28,7 +28,7 @@ import { FALSE_STRING, NULL_STRING, TRUE_STRING } from 'src/utils/common';
export const getSelectExtraFormData = (
col: string,
value?: null | (string | number)[],
value?: null | (string | number | boolean | null)[],
emptyFilter = false,
inverseSelection = false,
): ExtraFormData => {
@@ -46,6 +46,7 @@ export const getSelectExtraFormData = (
{
col,
op: inverseSelection ? ('NOT IN' as const) : ('IN' as const),
// @ts-ignore
val: value,
},
];
@@ -116,3 +117,18 @@ export function getDataRecordFormatter({
return String(value);
};
}
export function formatFilterValue(
value: string | number | boolean | null,
): string {
if (value === null) {
return NULL_STRING;
}
if (typeof value === 'string') {
return value;
}
if (typeof value === 'number') {
return String(value);
}
return value ? TRUE_STRING : FALSE_STRING;
}

View File

@@ -19,6 +19,7 @@
/* eslint camelcase: 0 */
import { t, SupersetClient } from '@superset-ui/core';
import rison from 'rison';
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
import { addDangerToast, addSuccessToast } from '../../messageToasts/actions';
export const SET_REPORT = 'SET_REPORT';
@@ -102,15 +103,21 @@ export const addReport = report => dispatch => {
endpoint: `/api/v1/report/`,
jsonPayload: report,
})
.then(() => {
dispatch({ type: ADD_REPORT, report });
.then(({ json }) => {
dispatch({ type: ADD_REPORT, json });
dispatch(addSuccessToast(t('The report has been created')));
})
.catch(() =>
.catch(async e => {
const parsedError = await getClientErrorObject(e);
const errorMessage = parsedError.message;
const errorArr = Object.keys(errorMessage);
const error = errorMessage[errorArr[0]][0];
dispatch(
addDangerToast(t('An error occurred while creating this report.')),
),
);
addDangerToast(
t('An error occurred while editing this report: %s', error),
),
);
});
};
export const EDIT_REPORT = 'EDIT_REPORT';

View File

@@ -87,10 +87,21 @@ export function optionFromValue(opt) {
return { value: optionValue(opt), label: optionLabel(opt) };
}
export function prepareCopyToClipboardTabularData(data) {
export function prepareCopyToClipboardTabularData(data, columns) {
let result = '';
for (let i = 0; i < data.length; i += 1) {
result += `${Object.values(data[i]).join('\t')}\n`;
const row = {};
for (let j = 0; j < columns.length; j += 1) {
// JavaScript does not mantain 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]) {
row[j] = data[i][key];
} else {
row[j] = data[i][parseFloat(key)];
}
}
result += `${Object.values(row).join('\t')}\n`;
}
return result;
}

View File

@@ -46,15 +46,18 @@ describe('utils/common', () => {
describe('prepareCopyToClipboardTabularData', () => {
it('converts empty array', () => {
const array = [];
expect(prepareCopyToClipboardTabularData(array)).toEqual('');
const column = [];
expect(prepareCopyToClipboardTabularData(array, column)).toEqual('');
});
it('converts non empty array', () => {
const array = [
{ column1: 'lorem', column2: 'ipsum' },
{ column1: 'dolor', column2: 'sit', column3: 'amet' },
];
expect(prepareCopyToClipboardTabularData(array)).toEqual(
'lorem\tipsum\ndolor\tsit\tamet\n',
const column = ['column1', 'column2', 'column3'];
console.log(prepareCopyToClipboardTabularData(array, column));
expect(prepareCopyToClipboardTabularData(array, column)).toEqual(
'lorem\tipsum\t\ndolor\tsit\tamet\n',
);
});
});

View File

@@ -25,12 +25,12 @@ import {
import React, { useMemo, useState } from 'react';
import rison from 'rison';
import { uniqBy } from 'lodash';
import moment from 'moment';
import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
import {
createErrorHandler,
createFetchRelated,
handleChartDelete,
CardStylesOverrides,
} from 'src/views/CRUD/utils';
import {
useChartEditModal,
@@ -160,6 +160,9 @@ function ChartList(props: ChartListProps) {
const [passwordFields, setPasswordFields] = useState<string[]>([]);
const [preparingExport, setPreparingExport] = useState<boolean>(false);
const { userId } = props.user;
const userKey = getFromLocalStorage(userId.toString(), null);
const openChartImportModal = () => {
showImportModal(true);
};
@@ -270,23 +273,33 @@ function ChartList(props: ChartListProps) {
Cell: ({
row: {
original: {
changed_by_name: changedByName,
last_saved_by: lastSavedBy,
changed_by_url: changedByUrl,
},
},
}: any) => <a href={changedByUrl}>{changedByName}</a>,
}: any) => (
<a href={changedByUrl}>
{lastSavedBy?.first_name
? `${lastSavedBy?.first_name} ${lastSavedBy?.last_name}`
: null}
</a>
),
Header: t('Modified by'),
accessor: 'changed_by.first_name',
accessor: 'last_saved_by.first_name',
size: 'xl',
},
{
Cell: ({
row: {
original: { changed_on_delta_humanized: changedOn },
original: { last_saved_at: lastSavedAt },
},
}: any) => <span className="no-wrap">{changedOn}</span>,
}: any) => (
<span className="no-wrap">
{lastSavedAt ? moment.utc(lastSavedAt).fromNow() : null}
</span>
),
Header: t('Last modified'),
accessor: 'changed_on_delta_humanized',
accessor: 'last_saved_at',
size: 'xl',
},
{
@@ -532,29 +545,25 @@ function ChartList(props: ChartListProps) {
];
function renderCard(chart: Chart) {
const { userId } = props.user;
const userKey = getFromLocalStorage(userId.toString(), null);
return (
<CardStylesOverrides>
<ChartCard
chart={chart}
showThumbnails={
userKey
? userKey.thumbnails
: isFeatureEnabled(FeatureFlag.THUMBNAILS)
}
hasPerm={hasPerm}
openChartEditModal={openChartEditModal}
bulkSelectEnabled={bulkSelectEnabled}
addDangerToast={addDangerToast}
addSuccessToast={addSuccessToast}
refreshData={refreshData}
loading={loading}
favoriteStatus={favoriteStatus[chart.id]}
saveFavoriteStatus={saveFavoriteStatus}
handleBulkChartExport={handleBulkChartExport}
/>
</CardStylesOverrides>
<ChartCard
chart={chart}
showThumbnails={
userKey
? userKey.thumbnails
: isFeatureEnabled(FeatureFlag.THUMBNAILS)
}
hasPerm={hasPerm}
openChartEditModal={openChartEditModal}
bulkSelectEnabled={bulkSelectEnabled}
addDangerToast={addDangerToast}
addSuccessToast={addSuccessToast}
refreshData={refreshData}
loading={loading}
favoriteStatus={favoriteStatus[chart.id]}
saveFavoriteStatus={saveFavoriteStatus}
handleBulkChartExport={handleBulkChartExport}
/>
);
}
const subMenuButtons: SubMenuProps['buttons'] = [];
@@ -644,6 +653,11 @@ function ChartList(props: ChartListProps) {
loading={loading}
pageSize={PAGE_SIZE}
renderCard={renderCard}
showThumbnails={
userKey
? userKey.thumbnails
: isFeatureEnabled(FeatureFlag.THUMBNAILS)
}
defaultViewMode={
isFeatureEnabled(FeatureFlag.LISTVIEWS_DEFAULT_CARD_VIEW)
? 'card'

View File

@@ -25,7 +25,6 @@ import {
createFetchRelated,
createErrorHandler,
handleDashboardDelete,
CardStylesOverrides,
} from 'src/views/CRUD/utils';
import { useListViewResource, useFavoriteStatus } from 'src/views/CRUD/hooks';
import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
@@ -140,6 +139,9 @@ function DashboardList(props: DashboardListProps) {
refreshData();
};
const { userId } = props.user;
const userKey = getFromLocalStorage(userId.toString(), null);
const canCreate = hasPerm('can_write');
const canEdit = hasPerm('can_write');
const canDelete = hasPerm('can_write');
@@ -499,29 +501,25 @@ function DashboardList(props: DashboardListProps) {
];
function renderCard(dashboard: Dashboard) {
const { userId } = props.user;
const userKey = getFromLocalStorage(userId.toString(), null);
return (
<CardStylesOverrides>
<DashboardCard
dashboard={dashboard}
hasPerm={hasPerm}
bulkSelectEnabled={bulkSelectEnabled}
refreshData={refreshData}
showThumbnails={
userKey
? userKey.thumbnails
: isFeatureEnabled(FeatureFlag.THUMBNAILS)
}
loading={loading}
addDangerToast={addDangerToast}
addSuccessToast={addSuccessToast}
openDashboardEditModal={openDashboardEditModal}
saveFavoriteStatus={saveFavoriteStatus}
favoriteStatus={favoriteStatus[dashboard.id]}
handleBulkDashboardExport={handleBulkDashboardExport}
/>
</CardStylesOverrides>
<DashboardCard
dashboard={dashboard}
hasPerm={hasPerm}
bulkSelectEnabled={bulkSelectEnabled}
refreshData={refreshData}
showThumbnails={
userKey
? userKey.thumbnails
: isFeatureEnabled(FeatureFlag.THUMBNAILS)
}
loading={loading}
addDangerToast={addDangerToast}
addSuccessToast={addSuccessToast}
openDashboardEditModal={openDashboardEditModal}
saveFavoriteStatus={saveFavoriteStatus}
favoriteStatus={favoriteStatus[dashboard.id]}
handleBulkDashboardExport={handleBulkDashboardExport}
/>
);
}
@@ -614,6 +612,11 @@ function DashboardList(props: DashboardListProps) {
initialSort={initialSort}
loading={loading}
pageSize={PAGE_SIZE}
showThumbnails={
userKey
? userKey.thumbnails
: isFeatureEnabled(FeatureFlag.THUMBNAILS)
}
renderCard={renderCard}
defaultViewMode={
isFeatureEnabled(FeatureFlag.LISTVIEWS_DEFAULT_CARD_VIEW)

View File

@@ -61,6 +61,8 @@ interface FieldPropTypes {
onParametersUploadFileChange: (value: any) => string;
changeMethods: { onParametersChange: (value: any) => string } & {
onChange: (value: any) => string;
} & {
onQueryChange: (value: any) => string;
} & { onParametersUploadFileChange: (value: any) => string } & {
onAddTableCatalog: () => void;
onRemoveTableCatalog: (idx: number) => void;
@@ -415,15 +417,15 @@ const queryField = ({
db,
}: FieldPropTypes) => (
<ValidatedInput
id="query"
name="query"
id="query_input"
name="query_input"
required={required}
value={db?.parameters?.query}
value={db?.query_input || ''}
validationMethods={{ onBlur: getValidation }}
errorMessage={validationErrors?.query}
placeholder="e.g. param1=value1&param2=value2"
label="Additional Parameters"
onChange={changeMethods.onParametersChange}
onChange={changeMethods.onQueryChange}
helpText={t('Add additional custom parameters')}
/>
);
@@ -475,6 +477,7 @@ const DatabaseConnectionForm = ({
dbModel: { parameters },
onParametersChange,
onChange,
onQueryChange,
onParametersUploadFileChange,
onAddTableCatalog,
onRemoveTableCatalog,
@@ -496,6 +499,9 @@ const DatabaseConnectionForm = ({
onChange: (
event: FormEvent<InputProps> | { target: HTMLInputElement },
) => void;
onQueryChange: (
event: FormEvent<InputProps> | { target: HTMLInputElement },
) => void;
onParametersUploadFileChange?: (
event: FormEvent<InputProps> | { target: HTMLInputElement },
) => void;
@@ -523,6 +529,7 @@ const DatabaseConnectionForm = ({
changeMethods: {
onParametersChange,
onChange,
onQueryChange,
onParametersUploadFileChange,
onAddTableCatalog,
onRemoveTableCatalog,

View File

@@ -169,9 +169,9 @@ const ExtraOptions = ({
<StyledInputContainer css={no_margin_bottom}>
<div className="input-container">
<IndeterminateCheckbox
id="cost_query_enabled"
id="cost_estimate_enabled"
indeterminate={false}
checked={!!db?.extra_json?.cost_query_enabled}
checked={!!db?.extra_json?.cost_estimate_enabled}
onChange={onExtraInputChange}
labelText={t('Enable query cost estimation')}
/>
@@ -392,11 +392,13 @@ const ExtraOptions = ({
indeterminate={false}
checked={!!db?.impersonate_user}
onChange={onInputChange}
labelText={t('Impersonate logged in user (Presto & Hive)')}
labelText={t(
'Impersonate logged in user (Presto, Trino, Hive, and GSheets)',
)}
/>
<InfoTooltip
tooltip={t(
'If Presto, all the queries in SQL Lab are going to be executed as the ' +
'If Presto or Trino, all the queries in SQL Lab are going to be executed as the ' +
'currently logged on user who must have permission to run them. If Hive ' +
'and hive.server2.enable.doAs is enabled, will run the queries as ' +
'service account, but impersonate the currently logged on user via ' +
@@ -490,8 +492,8 @@ const ExtraOptions = ({
</div>
<div className="helper">
{t(
'Specify this databases version. This should be used with ' +
'Presto databases so that the syntax is correct.',
'Specify the database version. This should be used with ' +
'Presto in order to enable query cost estimation.',
)}
</div>
</StyledInputContainer>

View File

@@ -76,6 +76,18 @@ import {
} from './styles';
import ModalHeader, { DOCUMENTATION_LINK } from './ModalHeader';
const engineSpecificAlertMapping = {
gsheets: {
message: 'Why do I need to create a database?',
description:
'To begin using your Google Sheets, you need to create a database first. ' +
'Databases are used as a way to identify ' +
'your data so that it can be queried and visualized. This ' +
'database will hold all of your individual Google Sheets ' +
'you choose to connect here.',
},
};
const errorAlertMapping = {
CONNECTION_MISSING_PARAMETERS_ERROR: {
message: 'Missing Required Fields',
@@ -134,6 +146,7 @@ enum ActionType {
extraEditorChange,
addTableCatalogSheet,
removeTableCatalogSheet,
queryChange,
}
interface DBReducerPayloadType {
@@ -151,6 +164,7 @@ type DBReducerActionType =
| ActionType.extraEditorChange
| ActionType.extraInputChange
| ActionType.textChange
| ActionType.queryChange
| ActionType.inputChange
| ActionType.editorChange
| ActionType.parametersChange;
@@ -193,7 +207,8 @@ function dbReducer(
const trimmedState = {
...(state || {}),
};
let query = '';
let query = {};
let query_input = '';
let deserializeExtraJSON = {};
let extra_json: DatabaseObject['extra_json'];
@@ -306,6 +321,15 @@ function dbReducer(
...trimmedState,
[action.payload.name]: action.payload.json,
};
case ActionType.queryChange:
return {
...trimmedState,
parameters: {
...trimmedState.parameters,
query: Object.fromEntries(new URLSearchParams(action.payload.value)),
},
query_input: action.payload.value,
};
case ActionType.textChange:
return {
...trimmedState,
@@ -327,6 +351,12 @@ function dbReducer(
};
}
// convert query to a string and store in query_input
query = action.payload?.parameters?.query || {};
query_input = Object.entries(query)
.map(([key, value]) => `${key}=${value}`)
.join('&');
if (
action.payload.backend === 'bigquery' &&
action.payload.configuration_method ===
@@ -338,11 +368,12 @@ function dbReducer(
configuration_method: action.payload.configuration_method,
extra_json: deserializeExtraJSON,
parameters: {
query,
credentials_info: JSON.stringify(
action.payload?.parameters?.credentials_info || '',
),
query,
},
query_input,
};
}
@@ -364,37 +395,18 @@ function dbReducer(
name: e,
value: engineParamsCatalog[e],
})),
query_input,
} as DatabaseObject;
}
if (action.payload?.parameters?.query) {
// convert query into URI params string
query = new URLSearchParams(
action.payload.parameters.query as string,
).toString();
return {
...action.payload,
encrypted_extra: action.payload.encrypted_extra || '',
engine: action.payload.backend || trimmedState.engine,
configuration_method: action.payload.configuration_method,
extra_json: deserializeExtraJSON,
parameters: {
...action.payload.parameters,
query,
},
};
}
return {
...action.payload,
encrypted_extra: action.payload.encrypted_extra || '',
engine: action.payload.backend || trimmedState.engine,
configuration_method: action.payload.configuration_method,
extra_json: deserializeExtraJSON,
parameters: {
...action.payload.parameters,
},
parameters: action.payload.parameters,
query_input,
};
case ActionType.dbSelected:
@@ -454,10 +466,11 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
const sslForced = isFeatureEnabled(
FeatureFlag.FORCE_DATABASE_CONNECTIONS_SSL,
);
const hasAlert =
connectionAlert || !!(db?.engine && engineSpecificAlertMapping[db.engine]);
const useSqlAlchemyForm =
db?.configuration_method === CONFIGURATION_METHOD.SQLALCHEMY_URI;
const useTabLayout = isEditMode || useSqlAlchemyForm;
// Database fetch logic
const {
state: { loading: dbLoading, resource: dbFetched, error: dbErrors },
@@ -471,9 +484,9 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
addDangerToast,
);
const isDynamic = (engine: string | undefined) =>
availableDbs?.databases.filter(
availableDbs?.databases?.find(
(DB: DatabaseObject) => DB.backend === engine || DB.engine === engine,
)[0].parameters !== undefined;
)?.parameters !== undefined;
const showDBError = validationErrors || dbErrors;
const isEmpty = (data?: Object | null) =>
data && Object.keys(data).length === 0;
@@ -527,21 +540,6 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
return;
}
if (dbToUpdate?.parameters?.query) {
// convert query params into dictionary
dbToUpdate.parameters.query = JSON.parse(
`{"${decodeURI((dbToUpdate?.parameters?.query as string) || '')
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"')}"}`,
);
} else if (
dbToUpdate?.parameters?.query === '' &&
'query' in dbModel.parameters.properties
) {
dbToUpdate.parameters.query = {};
}
const engine = dbToUpdate.backend || dbToUpdate.engine;
if (engine === 'bigquery' && dbToUpdate.parameters?.credentials_info) {
// wrap encrypted_extra in credentials_info only for BigQuery
@@ -834,6 +832,37 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
setTabKey(key);
};
const renderStepTwoAlert = () => {
const { hostname } = window.location;
let ipAlert = connectionAlert?.REGIONAL_IPS?.default || '';
const regionalIPs = connectionAlert?.REGIONAL_IPS || {};
Object.entries(regionalIPs).forEach(([regex, ipRange]) => {
if (regex.match(hostname)) {
ipAlert = ipRange;
}
});
return (
db?.engine && (
<StyledAlertMargin>
<Alert
closable={false}
css={(theme: SupersetTheme) => antDAlertStyles(theme)}
type="info"
showIcon
message={
engineSpecificAlertMapping[db.engine]?.message ||
connectionAlert?.DEFAULT?.message
}
description={
engineSpecificAlertMapping[db.engine]?.description ||
connectionAlert?.DEFAULT?.description + ipAlert
}
/>
</StyledAlertMargin>
)
);
};
const errorAlert = () => {
if (
isEmpty(dbErrors) ||
@@ -929,6 +958,12 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
value: target.value,
})
}
onQueryChange={({ target }: { target: HTMLInputElement }) =>
onChange(ActionType.queryChange, {
name: target.name,
value: target.value,
})
}
onAddTableCatalog={() =>
setDB({ type: ActionType.addTableCatalogSheet })
}
@@ -1050,6 +1085,12 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
value: target.value,
})
}
onQueryChange={({ target }: { target: HTMLInputElement }) =>
onChange(ActionType.queryChange, {
name: target.name,
value: target.value,
})
}
onAddTableCatalog={() =>
setDB({ type: ActionType.addTableCatalogSheet })
}
@@ -1188,18 +1229,7 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
dbName={dbName}
dbModel={dbModel}
/>
{connectionAlert && (
<StyledAlertMargin>
<Alert
closable={false}
css={(theme: SupersetTheme) => antDAlertStyles(theme)}
type="info"
showIcon
message={t('IP Allowlist')}
description={connectionAlert.ALLOWED_IPS}
/>
</StyledAlertMargin>
)}
{hasAlert && renderStepTwoAlert()}
<DatabaseConnectionForm
db={db}
sslForced={sslForced}
@@ -1207,6 +1237,12 @@ const DatabaseModal: FunctionComponent<DatabaseModalProps> = ({
onAddTableCatalog={() => {
setDB({ type: ActionType.addTableCatalogSheet });
}}
onQueryChange={({ target }: { target: HTMLInputElement }) =>
onChange(ActionType.queryChange, {
name: target.name,
value: target.value,
})
}
onRemoveTableCatalog={(idx: number) => {
setDB({
type: ActionType.removeTableCatalogSheet,

View File

@@ -45,15 +45,12 @@ export type DatabaseObject = {
password?: string;
encryption?: boolean;
credentials_info?: string;
query?: string | object;
catalog?: {};
query?: Record<string, string>;
catalog?: Record<string, string>;
};
configuration_method: CONFIGURATION_METHOD;
engine?: string;
// Gsheets temporary storage
catalog?: Array<CatalogObject>;
// Performance
cache_timeout?: string;
allow_run_async?: boolean;
@@ -85,11 +82,14 @@ export type DatabaseObject = {
allows_virtual_table_explore?: boolean; // in SQL Lab
schemas_allowed_for_csv_upload?: string[]; // in Security
cancel_query_on_windows_unload?: boolean; // in Performance
version?: string;
// todo: ask beto where this should live
cost_query_enabled?: boolean; // in SQL Lab
version?: string;
cost_estimate_enabled?: boolean; // in SQL Lab
};
// Temporary storage
catalog?: Array<CatalogObject>;
query_input?: string;
extra?: string;
};

View File

@@ -17,7 +17,8 @@
* under the License.
*/
// storage keys for welcome page sticky tabs..
// storage keys for welcome page sticky tabs and tables
export const HOMEPAGE_CHART_FILTER = 'homepage_chart_filter';
export const HOMEPAGE_ACTIVITY_FILTER = 'homepage_activity_filter';
export const HOMEPAGE_DASHBOARD_FILTER = 'homepage_dashboard_filter';
export const HOMEPAGE_COLLAPSE_STATE = 'homepage_collapse_state';

View File

@@ -32,7 +32,7 @@ export enum TableTabTypes {
export type Filters = {
col: string;
opr: string;
value: string;
value: string | number;
};
export interface DashboardTableProps {

View File

@@ -132,10 +132,19 @@ export const getRecentAcitivtyObjs = (
) =>
SupersetClient.get({ endpoint: recent }).then(recentsRes => {
const res: any = {};
const filters = [
{
col: 'created_by',
opr: 'rel_o_m',
value: 0,
},
];
const newBatch = [
SupersetClient.get({ endpoint: `/api/v1/chart/?q=${getParams()}` }),
SupersetClient.get({
endpoint: `/api/v1/dashboard/?q=${getParams()}`,
endpoint: `/api/v1/chart/?q=${getParams(filters)}`,
}),
SupersetClient.get({
endpoint: `/api/v1/dashboard/?q=${getParams(filters)}`,
}),
];
return Promise.all(newBatch)
@@ -269,15 +278,12 @@ export function shortenSQL(sql: string, maxLines: number) {
return lines.join('\n');
}
// loading card count for homepage
export const loadingCardCount = 5;
const breakpoints = [576, 768, 992, 1200];
export const mq = breakpoints.map(bp => `@media (max-width: ${bp}px)`);
export const CardStylesOverrides = styled.div`
.ant-card-cover > div {
height: 264px;
}
`;
export const CardContainer = styled.div<{
showThumbnails?: boolean | undefined;
}>`
@@ -286,7 +292,7 @@ export const CardContainer = styled.div<{
display: grid;
grid-gap: ${theme.gridUnit * 12}px ${theme.gridUnit * 4}px;
grid-template-columns: repeat(auto-fit, 300px);
max-height: ${showThumbnails ? '314' : '140'}px;
max-height: ${showThumbnails ? '314' : '148'}px;
margin-top: ${theme.gridUnit * -6}px;
padding: ${
showThumbnails

View File

@@ -21,9 +21,9 @@ import moment from 'moment';
import { styled, t } from '@superset-ui/core';
import { setInLocalStorage } from 'src/utils/localStorageHelpers';
import Loading from 'src/components/Loading';
import ListViewCard from 'src/components/ListViewCard';
import SubMenu from 'src/components/Menu/SubMenu';
import { LoadingCards, ActivityData } from 'src/views/CRUD/welcome/Welcome';
import {
CardStyles,
getEditedObjects,
@@ -34,7 +34,7 @@ import { Chart } from 'src/types/Chart';
import { Dashboard, SavedQueryObject } from 'src/views/CRUD/types';
import Icons from 'src/components/Icons';
import { ActivityData } from './Welcome';
import EmptyState from './EmptyState';
/**
@@ -230,7 +230,7 @@ export default function ActivityTable({
const doneFetching = loadedCount < 3;
if ((loadingState && !editedObjs) || doneFetching) {
return <Loading position="inline" />;
return <LoadingCards />;
}
return (
<Styles>

View File

@@ -35,6 +35,7 @@ import PropertiesModal from 'src/explore/components/PropertiesModal';
import { User } from 'src/types/bootstrapTypes';
import { CardContainer, PAGE_SIZE } from 'src/views/CRUD/utils';
import { HOMEPAGE_CHART_FILTER } from 'src/views/CRUD/storageKeys';
import { LoadingCards } from 'src/views/CRUD/welcome/Welcome';
import ChartCard from 'src/views/CRUD/chart/ChartCard';
import Chart from 'src/types/Chart';
import handleResourceExport from 'src/utils/export';
@@ -131,6 +132,12 @@ function ChartTable({
operator: 'chart_is_favorite',
value: true,
});
} else if (filterName === 'Examples') {
filters.push({
id: 'created_by',
operator: 'rel_o_m',
value: 0,
});
}
return filters;
};
@@ -177,7 +184,7 @@ function ChartTable({
});
}
if (loading) return <Loading position="inline" />;
if (loading) return <LoadingCards cover={showThumbnails} />;
return (
<ErrorBoundary>
{sliceCurrentlyEditing && (

View File

@@ -31,6 +31,7 @@ import {
setInLocalStorage,
getFromLocalStorage,
} from 'src/utils/localStorageHelpers';
import { LoadingCards } from 'src/views/CRUD/welcome/Welcome';
import {
createErrorHandler,
CardContainer,
@@ -142,6 +143,12 @@ function DashboardTable({
operator: 'dashboard_is_favorite',
value: true,
});
} else if (filterName === 'Examples') {
filters.push({
id: 'created_by',
operator: 'rel_o_m',
value: 0,
});
}
return filters;
};
@@ -189,7 +196,7 @@ function DashboardTable({
filters: getFilters(filter),
});
if (loading) return <Loading position="inline" />;
if (loading) return <LoadingCards cover={showThumbnails} />;
return (
<>
<SubMenu

View File

@@ -22,7 +22,7 @@ import SyntaxHighlighter from 'react-syntax-highlighter/dist/cjs/light';
import sql from 'react-syntax-highlighter/dist/cjs/languages/hljs/sql';
import github from 'react-syntax-highlighter/dist/cjs/styles/hljs/github';
import withToasts from 'src/messageToasts/enhancers/withToasts';
import Loading from 'src/components/Loading';
import { LoadingCards } from 'src/views/CRUD/welcome/Welcome';
import { Dropdown, Menu } from 'src/common/components';
import { useListViewResource, copyQueryLink } from 'src/views/CRUD/hooks';
import ListViewCard from 'src/components/ListViewCard';
@@ -240,7 +240,7 @@ const SavedQueries = ({
</Menu>
);
if (loading) return <Loading position="inline" />;
if (loading) return <LoadingCards cover={showThumbnails} />;
return (
<>
{queryDeleteModal && (

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