Compare commits
71 Commits
remove-mor
...
4.0.0rc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c35842e9f1 | ||
|
|
6a21f6da3d | ||
|
|
0d5be95826 | ||
|
|
b7fa3edcf4 | ||
|
|
b8e556d6f2 | ||
|
|
bb5c0b4086 | ||
|
|
2fa1b35c16 | ||
|
|
a024b4ac1b | ||
|
|
5c567895cc | ||
|
|
51ad63426c | ||
|
|
34b06f94ab | ||
|
|
32aa25ae17 | ||
|
|
29553939a2 | ||
|
|
d8517213bf | ||
|
|
3bfa8a9cbc | ||
|
|
7c14968e6d | ||
|
|
4ff331a66c | ||
|
|
a2fb13b522 | ||
|
|
fe95adac98 | ||
|
|
90afb34df5 | ||
|
|
336f6bd021 | ||
|
|
4a59f23be3 | ||
|
|
652e6cfa3f | ||
|
|
634d72b19d | ||
|
|
4b750c0caf | ||
|
|
f52647ff48 | ||
|
|
35562198f8 | ||
|
|
94f677850c | ||
|
|
d4314a92d8 | ||
|
|
297849a8b5 | ||
|
|
1016fd92f6 | ||
|
|
017e0fc733 | ||
|
|
0ee452e9f9 | ||
|
|
131c254fe7 | ||
|
|
759863553d | ||
|
|
15557f493d | ||
|
|
5b6e5e475e | ||
|
|
2060e545e6 | ||
|
|
0baa2af200 | ||
|
|
db6df71fad | ||
|
|
4f3a7f3931 | ||
|
|
94aeef5f82 | ||
|
|
19efb146b2 | ||
|
|
25b7eb0ec9 | ||
|
|
ecebd70ba0 | ||
|
|
e9ebc386f2 | ||
|
|
85efe2525c | ||
|
|
f0632405c2 | ||
|
|
f5750dda8c | ||
|
|
c0fa4f43e1 | ||
|
|
e881675f58 | ||
|
|
8103f22fa3 | ||
|
|
74842fe6e8 | ||
|
|
5cba9bcb0e | ||
|
|
824367564a | ||
|
|
6b82e27561 | ||
|
|
2e80b688bb | ||
|
|
b9ec312350 | ||
|
|
524dbd570d | ||
|
|
8fd47c62c1 | ||
|
|
7de3e0fcda | ||
|
|
7115b2cce6 | ||
|
|
3650228f88 | ||
|
|
ffa0c73776 | ||
|
|
c29873cc81 | ||
|
|
ef2ca408cc | ||
|
|
d47fd44b49 | ||
|
|
3440a301ba | ||
|
|
a523d9aac6 | ||
|
|
d69be3286a | ||
|
|
beb9ec77a5 |
@@ -4,7 +4,8 @@ on:
|
||||
paths:
|
||||
- "superset/migrations/**"
|
||||
branches:
|
||||
- 'master'
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
pull_request:
|
||||
paths:
|
||||
- "superset/migrations/**"
|
||||
|
||||
72
.github/workflows/chromatic-master.yml
vendored
@@ -1,72 +0,0 @@
|
||||
# .github/workflows/chromatic.yml
|
||||
# see https://www.chromatic.com/docs/github-actions
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# Workflow name
|
||||
name: 'Chromatic Storybook Master'
|
||||
|
||||
# Event for the workflow
|
||||
# Only run if changes were made in superset-frontend folder of repo on merge to Master
|
||||
on:
|
||||
# This will trigger when a branch merges to master when the PR has changes in the frontend folder updating the chromatic baseline
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- "superset-frontend/**"
|
||||
|
||||
# List of jobs
|
||||
jobs:
|
||||
config:
|
||||
runs-on: "ubuntu-latest"
|
||||
outputs:
|
||||
has-secrets: ${{ steps.check.outputs.has-secrets }}
|
||||
steps:
|
||||
- name: "Check for secrets"
|
||||
id: check
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -n "${{ (secrets.CHROMATIC_PROJECT_TOKEN != '') || '' }}" ]; then
|
||||
echo "has-secrets=1" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
chromatic-deployment:
|
||||
needs: config
|
||||
if: needs.config.outputs.has-secrets
|
||||
# Operating System
|
||||
runs-on: ubuntu-latest
|
||||
# Job steps
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # 👈 Required to retrieve git history
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
working-directory: superset-frontend
|
||||
# 👇 Build and publish Storybook to Chromatic
|
||||
- name: Build and publish Storybook to Chromatic
|
||||
id: chromatic-master
|
||||
uses: chromaui/action@v10
|
||||
# Required options for the Chromatic GitHub Action
|
||||
with:
|
||||
# 👇 Location of package.json from root of mono-repo
|
||||
workingDir: superset-frontend
|
||||
# 👇 Chromatic projectToken, refer to the manage page to obtain it.
|
||||
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
|
||||
exitZeroOnChanges: true # 👈 Option to prevent the workflow from failing
|
||||
autoAcceptChanges: true # 👈 Option to accept all changes when merging to master
|
||||
12
.github/workflows/codeql-analysis.yml
vendored
@@ -2,16 +2,16 @@ name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
branches: ["master", "[0-9].[0-9]"]
|
||||
paths:
|
||||
- 'superset/**'
|
||||
- "superset/**"
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "master" ]
|
||||
branches: ["master"]
|
||||
paths:
|
||||
- 'superset/**'
|
||||
- "superset/**"
|
||||
schedule:
|
||||
- cron: '0 4 * * *'
|
||||
- cron: "0 4 * * *"
|
||||
|
||||
# cancel previous workflow jobs for PRs
|
||||
concurrency:
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'python', 'javascript' ]
|
||||
language: ["python", "javascript"]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
|
||||
6
.github/workflows/docker.yml
vendored
@@ -3,9 +3,11 @@ name: Docker
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
pull_request:
|
||||
types: [synchronize, opened, reopened, ready_for_review]
|
||||
branches:
|
||||
- "master"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
|
||||
5
.github/workflows/embedded-sdk-release.yml
vendored
@@ -3,7 +3,8 @@ name: Embedded SDK Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
|
||||
jobs:
|
||||
config:
|
||||
@@ -31,7 +32,7 @@ jobs:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "16"
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
- run: npm ci
|
||||
- run: npm run ci:release
|
||||
env:
|
||||
|
||||
5
.github/workflows/generate-FOSSA-report.yml
vendored
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
|
||||
jobs:
|
||||
config:
|
||||
@@ -33,8 +34,8 @@ jobs:
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '11'
|
||||
distribution: "temurin"
|
||||
java-version: "11"
|
||||
- name: Generate fossa report
|
||||
env:
|
||||
FOSSA_API_KEY: ${{ secrets.FOSSA_API_KEY }}
|
||||
|
||||
3
.github/workflows/pre-commit.yml
vendored
@@ -3,7 +3,8 @@ name: pre-commit checks
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
pull_request:
|
||||
types: [synchronize, opened, reopened, ready_for_review]
|
||||
|
||||
|
||||
3
.github/workflows/prefer-typescript.yml
vendored
@@ -3,7 +3,8 @@ name: Prefer TypeScript
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
paths:
|
||||
- "superset-frontend/src/**"
|
||||
pull_request:
|
||||
|
||||
3
.github/workflows/release.yml
vendored
@@ -3,7 +3,8 @@ name: release-workflow
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
|
||||
jobs:
|
||||
config:
|
||||
|
||||
7
.github/workflows/superset-cli.yml
vendored
@@ -3,7 +3,8 @@ name: Superset CLI tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
pull_request:
|
||||
types: [synchronize, opened, reopened, ready_for_review]
|
||||
|
||||
@@ -55,8 +56,8 @@ jobs:
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'requirements/testing.txt'
|
||||
cache: "pip"
|
||||
cache-dependency-path: "requirements/testing.txt"
|
||||
- name: Install dependencies
|
||||
if: steps.check.outcome == 'failure'
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
|
||||
3
.github/workflows/superset-docs-deploy.yml
vendored
@@ -6,6 +6,7 @@ on:
|
||||
- "docs/**"
|
||||
branches:
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
|
||||
jobs:
|
||||
config:
|
||||
@@ -38,7 +39,7 @@ jobs:
|
||||
- name: Set up Node.js 16
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '16'
|
||||
node-version: "16"
|
||||
- name: yarn install
|
||||
run: |
|
||||
yarn install --check-cache
|
||||
|
||||
3
.github/workflows/superset-e2e.yml
vendored
@@ -3,7 +3,8 @@ name: E2E
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
pull_request:
|
||||
types: [synchronize, opened, reopened, ready_for_review]
|
||||
|
||||
|
||||
1
.github/workflows/superset-frontend.yml
vendored
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
paths:
|
||||
- "superset-frontend/**"
|
||||
pull_request:
|
||||
|
||||
1
.github/workflows/superset-helm-release.yml
vendored
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
paths:
|
||||
- "helm/**"
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ name: Python-Integration
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
pull_request:
|
||||
types: [synchronize, opened, reopened, ready_for_review]
|
||||
|
||||
@@ -54,8 +55,8 @@ jobs:
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'requirements/testing.txt'
|
||||
cache: "pip"
|
||||
cache-dependency-path: "requirements/testing.txt"
|
||||
- name: Install dependencies
|
||||
if: steps.check.outcome == 'failure'
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
@@ -120,8 +121,8 @@ jobs:
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'requirements/testing.txt'
|
||||
cache: "pip"
|
||||
cache-dependency-path: "requirements/testing.txt"
|
||||
- name: Install dependencies
|
||||
if: steps.check.outcome == 'failure'
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
@@ -180,8 +181,8 @@ jobs:
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'requirements/testing.txt'
|
||||
cache: "pip"
|
||||
cache-dependency-path: "requirements/testing.txt"
|
||||
- name: Install dependencies
|
||||
if: steps.check.outcome == 'failure'
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
|
||||
3
.github/workflows/superset-python-misc.yml
vendored
@@ -4,7 +4,8 @@ name: Python Misc
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
paths:
|
||||
- "superset/**"
|
||||
pull_request:
|
||||
|
||||
@@ -4,7 +4,8 @@ name: Python Presto/Hive
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
paths:
|
||||
- "superset/**"
|
||||
pull_request:
|
||||
@@ -70,8 +71,8 @@ jobs:
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'requirements/testing.txt'
|
||||
cache: "pip"
|
||||
cache-dependency-path: "requirements/testing.txt"
|
||||
- name: Install dependencies
|
||||
if: steps.check.outcome == 'failure'
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
@@ -147,8 +148,8 @@ jobs:
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'requirements/testing.txt'
|
||||
cache: "pip"
|
||||
cache-dependency-path: "requirements/testing.txt"
|
||||
- name: Install dependencies
|
||||
if: steps.check.outcome == 'failure'
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
|
||||
@@ -4,7 +4,8 @@ name: Python-Unit
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
paths:
|
||||
- "superset/**"
|
||||
pull_request:
|
||||
@@ -43,9 +44,9 @@ jobs:
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'requirements/testing.txt'
|
||||
# TODO: separated requirements.txt file just for unit tests
|
||||
cache: "pip"
|
||||
cache-dependency-path: "requirements/testing.txt"
|
||||
# TODO: separated requirements.txt file just for unit tests
|
||||
- name: Install dependencies
|
||||
if: steps.check.outcome == 'failure'
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
|
||||
5
.github/workflows/superset-translations.yml
vendored
@@ -3,7 +3,8 @@ name: Translations
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
pull_request:
|
||||
types: [synchronize, opened, reopened, ready_for_review]
|
||||
|
||||
@@ -24,7 +25,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '16'
|
||||
node-version: "16"
|
||||
- name: Install dependencies
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
|
||||
3
.github/workflows/superset-websocket.yml
vendored
@@ -2,7 +2,8 @@ name: WebSocket server
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- "master"
|
||||
- "[0-9].[0-9]"
|
||||
paths:
|
||||
- "superset-websocket/**"
|
||||
pull_request:
|
||||
|
||||
5
.github/workflows/tech-debt.yml
vendored
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- "[0-9].[0-9]"
|
||||
|
||||
jobs:
|
||||
config:
|
||||
@@ -31,7 +32,7 @@ jobs:
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '16'
|
||||
node-version: "16"
|
||||
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
@@ -39,7 +40,7 @@ jobs:
|
||||
|
||||
- name: Run Script
|
||||
env:
|
||||
SPREADSHEET_ID: '1oABNnzxJYzwUrHjr_c9wfYEq9dFL1ScVof9LlaAdxvo'
|
||||
SPREADSHEET_ID: "1oABNnzxJYzwUrHjr_c9wfYEq9dFL1ScVof9LlaAdxvo"
|
||||
SERVICE_ACCOUNT_KEY: ${{ secrets.GSHEET_KEY }}
|
||||
run: npm run lint-stats
|
||||
continue-on-error: true
|
||||
|
||||
@@ -37,3 +37,4 @@ under the License.
|
||||
- [3.0.3](./CHANGELOG/3.0.3.md)
|
||||
- [3.0.4](./CHANGELOG/3.0.4.md)
|
||||
- [3.1.0](./CHANGELOG/3.1.0.md)
|
||||
- [4.0.0](./CHANGELOG/4.0.0.md)
|
||||
|
||||
472
CHANGELOG/4.0.0.md
Normal file
@@ -0,0 +1,472 @@
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
|
||||
## Change Log
|
||||
|
||||
### 4.0 (Mon Apr 1 10:04:00 2024 -0500)
|
||||
|
||||
**Database Migrations**
|
||||
|
||||
- [#27119](https://github.com/apache/superset/pull/27119) refactor: Updates some database columns to MediumText (@michael-s-molina)
|
||||
- [#27029](https://github.com/apache/superset/pull/27029) chore: Add granular permissions for actions in Dashboard (@geido)
|
||||
- [#26654](https://github.com/apache/superset/pull/26654) chore: add unique constraint to tagged_objects (@mistercrunch)
|
||||
- [#26377](https://github.com/apache/superset/pull/26377) refactor: Removes the deprecated redirect endpoint (@michael-s-molina)
|
||||
- [#26328](https://github.com/apache/superset/pull/26328) refactor: Removes the Filter Box code (@michael-s-molina)
|
||||
- [#26350](https://github.com/apache/superset/pull/26350) refactor: Migrates legacy Sunburst charts to ECharts and removes legacy code (@michael-s-molina)
|
||||
- [#26369](https://github.com/apache/superset/pull/26369) refactor: Removes the filters set feature (@michael-s-molina)
|
||||
- [#26416](https://github.com/apache/superset/pull/26416) fix: improve performance on reports log queries (@dpgaspar)
|
||||
- [#26290](https://github.com/apache/superset/pull/26290) feat(echarts-funnel): Implement % calculation type (@kgabryje)
|
||||
- [#26288](https://github.com/apache/superset/pull/26288) chore: Ensure Mixins are ordered according to the MRO (@john-bodley)
|
||||
|
||||
**Features**
|
||||
|
||||
- [#27159](https://github.com/apache/superset/pull/27159) feat: bump FAB to 4.4.0 (@dpgaspar)
|
||||
- [#26202](https://github.com/apache/superset/pull/26202) feat(Alerts and Reports): Modal redesign (@rtexelm)
|
||||
- [#26907](https://github.com/apache/superset/pull/26907) feat(storybook): Co-habitating/Upgrading Storybooks to v7 (dependency madness ensues) (@rusackas)
|
||||
- [#27092](https://github.com/apache/superset/pull/27092) feat(plugins): Tooltips on BigNumber with Time Comparison chart (@Antonio-RiveroMartnez)
|
||||
- [#27052](https://github.com/apache/superset/pull/27052) feat(plugins): Adding colors to BigNumber with Time Comparison chart (@Antonio-RiveroMartnez)
|
||||
- [#27054](https://github.com/apache/superset/pull/27054) feat(plugins): Update custom controls for BigNumber with Time Comparison chart (@Antonio-RiveroMartnez)
|
||||
- [#27055](https://github.com/apache/superset/pull/27055) feat(docker): allow for docker release builds to be multi-platform (@mistercrunch)
|
||||
- [#26923](https://github.com/apache/superset/pull/26923) feat: docker image tags documentation + tweaks (@mistercrunch)
|
||||
- [#26945](https://github.com/apache/superset/pull/26945) feat(ci): kill duplicate CI jobs on PRs (@mistercrunch)
|
||||
- [#26639](https://github.com/apache/superset/pull/26639) feat(components): Add static class name with button style (@mskelton)
|
||||
- [#26908](https://github.com/apache/superset/pull/26908) feat: Period over Period Big Number comparison chart (@eschutho)
|
||||
- [#26912](https://github.com/apache/superset/pull/26912) feat(ci): unleash dependabot on our github actions (@mistercrunch)
|
||||
- [#26300](https://github.com/apache/superset/pull/26300) feat(maps): Consolidating all country maps (and TS) into the Jupyter notebook workflow. (@rusackas)
|
||||
- [#26877](https://github.com/apache/superset/pull/26877) feat(ci): add a check to make sure there's no hold label on the PR (@mistercrunch)
|
||||
- [#26880](https://github.com/apache/superset/pull/26880) feat: configuring an extensible PR auto-labeler (@mistercrunch)
|
||||
- [#25323](https://github.com/apache/superset/pull/25323) feat(i18n): add ukranian translations (@GlugovGrGlib)
|
||||
- [#26443](https://github.com/apache/superset/pull/26443) feat: add chart id and dataset id to global logs (@eschutho)
|
||||
- [#26754](https://github.com/apache/superset/pull/26754) feat: Stop editor scrolling to top (@puridach-w)
|
||||
- [#26745](https://github.com/apache/superset/pull/26745) feat: auto-label PRs that contain db migrations (@mistercrunch)
|
||||
- [#26418](https://github.com/apache/superset/pull/26418) feat: global logs context (@eschutho)
|
||||
- [#26604](https://github.com/apache/superset/pull/26604) feat(celery): upgrade celery and its dependencies packages (@Musa10)
|
||||
- [#26407](https://github.com/apache/superset/pull/26407) feat: Add ValuePercent option to LABEL TYPE for Pie and Funnel charts (@kainchow)
|
||||
- [#26278](https://github.com/apache/superset/pull/26278) feat(releasing): adding SHA512 and RSA signature validation script to verify releases (@rusackas)
|
||||
- [#26011](https://github.com/apache/superset/pull/26011) feat(telemetry): Adding Scarf based telemetry to Superset (@rusackas)
|
||||
- [#26196](https://github.com/apache/superset/pull/26196) feat(docker): Add ARM builds (@alekseyolg)
|
||||
- [#26161](https://github.com/apache/superset/pull/26161) feat: Create db_engine_spec ibmi.py (@wAVeckx)
|
||||
|
||||
**Fixes**
|
||||
|
||||
- [#27706](https://github.com/apache/superset/pull/27706) fix: Select onChange is fired when the same item is selected in single mode (@michael-s-molina)
|
||||
- [#27763](https://github.com/apache/superset/pull/27763) fix: Removes filter plugins from viz gallery (@michael-s-molina)
|
||||
- [#27744](https://github.com/apache/superset/pull/27744) fix: reduce alert error to warning (@eschutho)
|
||||
- [#27558](https://github.com/apache/superset/pull/27558) fix(explore): drag and drop indicator UX (@justinpark)
|
||||
- [#27644](https://github.com/apache/superset/pull/27644) fix: Provide more inclusive error handling for saved queries (@john-bodley)
|
||||
- [#27646](https://github.com/apache/superset/pull/27646) fix: Leverage actual database for rendering Jinjarized SQL (@john-bodley)
|
||||
- [#27550](https://github.com/apache/superset/pull/27550) fix(AlertReports): disabling value when not null option is active (@fisjac)
|
||||
- [#27636](https://github.com/apache/superset/pull/27636) fix(sqllab): unable to remove table (@justinpark)
|
||||
- [#27022](https://github.com/apache/superset/pull/27022) fix(Chart Annotation modal): Table and Superset annotation options will paginate, exceeding previous max limit 100 (@rtexelm)
|
||||
- [#27552](https://github.com/apache/superset/pull/27552) fix(AlertReports): defaulting grace period to undefined (@fisjac)
|
||||
- [#27551](https://github.com/apache/superset/pull/27551) fix(AlertReports): clearing custom_width when disabled (@fisjac)
|
||||
- [#27470](https://github.com/apache/superset/pull/27470) fix(sql_parse): Ensure table extraction handles Jinja templating (@john-bodley)
|
||||
- [#27601](https://github.com/apache/superset/pull/27601) fix: Persist query params appended to permalink (@kgabryje)
|
||||
- [#27613](https://github.com/apache/superset/pull/27613) fix(Dashboard): Add editMode conditional for translate3d fix on charts to allow intended Fullscreen (@rtexelm)
|
||||
- [#27388](https://github.com/apache/superset/pull/27388) fix(utils): fix off-by-one error in how rolling window's min_periods truncates dataframe (@sfirke)
|
||||
- [#19595](https://github.com/apache/superset/pull/19595) fix: Volatile datasource ordering in dashboard export (@pnadolny13)
|
||||
- [#27577](https://github.com/apache/superset/pull/27577) fix: sqlglot SQL Server (@betodealmeida)
|
||||
- [#27576](https://github.com/apache/superset/pull/27576) fix: bump sqlglot to support materialized CTEs (@betodealmeida)
|
||||
- [#27567](https://github.com/apache/superset/pull/27567) fix(db_engine_specs): Update convert_dttm to work correctly with CrateDB (@hlcianfagna)
|
||||
- [#27605](https://github.com/apache/superset/pull/27605) fix: Skips Hive tests that are blocking PRs (@michael-s-molina)
|
||||
- [#27566](https://github.com/apache/superset/pull/27566) fix: guest queries (@betodealmeida)
|
||||
- [#27464](https://github.com/apache/superset/pull/27464) fix: pass valid SQL to SM (@betodealmeida)
|
||||
- [#26748](https://github.com/apache/superset/pull/26748) fix: `improve _extract_tables_from_sql` (@betodealmeida)
|
||||
- [#27539](https://github.com/apache/superset/pull/27539) fix(explore): Allow only saved metrics and columns (@justinpark)
|
||||
- [#27260](https://github.com/apache/superset/pull/27260) fix(alerts/reports): implementing custom_width as an Antd number input (@fisjac)
|
||||
- [#27487](https://github.com/apache/superset/pull/27487) fix(postprocessing): resample with holes (@villebro)
|
||||
- [#27484](https://github.com/apache/superset/pull/27484) fix: check if guest user modified query (@betodealmeida)
|
||||
- [#27471](https://github.com/apache/superset/pull/27471) fix(webpack): remove double-dotted file extensions in webpack config (@rusackas)
|
||||
- [#27186](https://github.com/apache/superset/pull/27186) fix: SSH Tunnel configuration settings (@geido)
|
||||
- [#27411](https://github.com/apache/superset/pull/27411) fix(dashboard): Only fetch CSS templates for dashboard header menu when in edit mode (@mskelton)
|
||||
- [#27315](https://github.com/apache/superset/pull/27315) fix(deps): resolving canvg and html2canvas module not found (@fisjac)
|
||||
- [#27403](https://github.com/apache/superset/pull/27403) fix: missing shared color in mixed timeseries (@justinpark)
|
||||
- [#27402](https://github.com/apache/superset/pull/27402) fix: typescript errors in 4.0 (@justinpark)
|
||||
- [#27390](https://github.com/apache/superset/pull/27390) fix: Re-enable CI checks on release branches (@michael-s-molina)
|
||||
- [#27391](https://github.com/apache/superset/pull/27391) fix(sqllab): Close already removed tab (@justinpark)
|
||||
- [#27364](https://github.com/apache/superset/pull/27364) fix(API): Updating assets via the API should preserve ownership configuration (@Vitor-Avila)
|
||||
- [#27395](https://github.com/apache/superset/pull/27395) fix: improve explore REST api validations (@dpgaspar)
|
||||
- [#27262](https://github.com/apache/superset/pull/27262) fix(Alerts & Reports): Fixing bug that resets cron value to default when empty (@fisjac)
|
||||
- [#27366](https://github.com/apache/superset/pull/27366) fix: Results section in Explore shows an infinite spinner (@michael-s-molina)
|
||||
- [#27187](https://github.com/apache/superset/pull/27187) fix: numexpr to fix CVE-2023-39631 (2.8.4 => 2.9.0) (@nigzak)
|
||||
- [#27361](https://github.com/apache/superset/pull/27361) fix: Missing SQL Lab permission (@michael-s-molina)
|
||||
- [#27360](https://github.com/apache/superset/pull/27360) fix: Heatmap numeric sorting (@michael-s-molina)
|
||||
- [#27313](https://github.com/apache/superset/pull/27313) fix(sqllab): Missing empty query result state (@justinpark)
|
||||
- [#27308](https://github.com/apache/superset/pull/27308) fix(dashboard): table chart drag preview overflowing container (@rtexelm)
|
||||
- [#27295](https://github.com/apache/superset/pull/27295) fix(sqllab): invalid dump sql shown after closing tab (@justinpark)
|
||||
- [#27285](https://github.com/apache/superset/pull/27285) fix(plugin-chart-echarts): calculate Gauge Chart intervals correctly when min value is set (@goto-loop)
|
||||
- [#27307](https://github.com/apache/superset/pull/27307) fix: Incorrect data type on import page (@michael-s-molina)
|
||||
- [#27291](https://github.com/apache/superset/pull/27291) fix: Data zoom with horizontal orientation (@michael-s-molina)
|
||||
- [#27273](https://github.com/apache/superset/pull/27273) fix: Navigating to an invalid page index in lists (@michael-s-molina)
|
||||
- [#27271](https://github.com/apache/superset/pull/27271) fix: Inoperable dashboard filter slider when range is <= 1 (@michael-s-molina)
|
||||
- [#27154](https://github.com/apache/superset/pull/27154) fix(import-datasources): Use "admin" user as default for importing datasources (@ddxv)
|
||||
- [#27258](https://github.com/apache/superset/pull/27258) fix: Sorting charts/dashboards makes the applied filters ineffective (@michael-s-molina)
|
||||
- [#27213](https://github.com/apache/superset/pull/27213) fix(trino): bumping trino to fix hudi schema fetching (@rusackas)
|
||||
- [#27236](https://github.com/apache/superset/pull/27236) fix(reports): fixing unit test (@fisjac)
|
||||
- [#27217](https://github.com/apache/superset/pull/27217) fix(sqlglot): Address regressions introduced in #26476 (@john-bodley)
|
||||
- [#27233](https://github.com/apache/superset/pull/27233) fix: bump FAB to 4.4.1 (perf issue) (@dpgaspar)
|
||||
- [#27167](https://github.com/apache/superset/pull/27167) fix: setting important lower bounds versions on requirements (@dpgaspar)
|
||||
- [#27215](https://github.com/apache/superset/pull/27215) fix: no limit in SELECT * for TOP dbs (@betodealmeida)
|
||||
- [#27214](https://github.com/apache/superset/pull/27214) fix(releasing): fixes npm script for release validation (@rusackas)
|
||||
- [#26074](https://github.com/apache/superset/pull/26074) fix: Translations related to the date range filter (@Ralkion)
|
||||
- [#26699](https://github.com/apache/superset/pull/26699) fix(dashboard): drag and drop indicator UX (@justinpark)
|
||||
- [#27191](https://github.com/apache/superset/pull/27191) fix: Failed to execute importScripts on worker-css (@michael-s-molina)
|
||||
- [#27181](https://github.com/apache/superset/pull/27181) fix(sqllab): typeahead search is broken in db selector (@justinpark)
|
||||
- [#27164](https://github.com/apache/superset/pull/27164) fix: unlock and bump werkzeug (@dpgaspar)
|
||||
- [#27161](https://github.com/apache/superset/pull/27161) fix(ci): mypy pre-commit issues (@dpgaspar)
|
||||
- [#27138](https://github.com/apache/superset/pull/27138) fix(plugins): Apply dashboard filters to comparison query in BigNumber with Time Comparison chart (@Antonio-RiveroMartnez)
|
||||
- [#27135](https://github.com/apache/superset/pull/27135) fix: Duplicated toast messages (@michael-s-molina)
|
||||
- [#27132](https://github.com/apache/superset/pull/27132) fix: Plain error message when visiting a dashboard via permalink without permissions (@michael-s-molina)
|
||||
- [#27130](https://github.com/apache/superset/pull/27130) fix: ID param for DELETE ssh_tunnel endpoint (@geido)
|
||||
- [#22840](https://github.com/apache/superset/pull/22840) fix(pivot-table-v2): Added forgotten translation pivot table v2 (@Always-prog)
|
||||
- [#27128](https://github.com/apache/superset/pull/27128) fix: RLS modal overflow (@michael-s-molina)
|
||||
- [#27112](https://github.com/apache/superset/pull/27112) fix: gevent upgrade to 23.9.1 (@dpgaspar)
|
||||
- [#27117](https://github.com/apache/superset/pull/27117) fix: removes old deprecated sqllab endpoints (@dpgaspar)
|
||||
- [#27124](https://github.com/apache/superset/pull/27124) fix: bump grpcio, urllib3 and paramiko (@dpgaspar)
|
||||
- [#27116](https://github.com/apache/superset/pull/27116) fix(docker): \*-dev tags target right stage from Dockerfile (@lodu)
|
||||
- [#26791](https://github.com/apache/superset/pull/26791) fix(sqllab): flaky json explore modal due to over-rendering (@justinpark)
|
||||
- [#27113](https://github.com/apache/superset/pull/27113) fix: upgrade cryptography to major 42 (@dpgaspar)
|
||||
- [#27106](https://github.com/apache/superset/pull/27106) fix: Timeseries Y-axis format with contribution mode (@michael-s-molina)
|
||||
- [#27098](https://github.com/apache/superset/pull/27098) fix: try to fix cypress with magic (@mistercrunch)
|
||||
- [#27094](https://github.com/apache/superset/pull/27094) fix(helm): typo on ssl_cert_reqs variable (@pcop00)
|
||||
- [#27091](https://github.com/apache/superset/pull/27091) fix(deps): un-bumping dom-to-pdf ro resolve missing file warnings (@rusackas)
|
||||
- [#27087](https://github.com/apache/superset/pull/27087) fix(ci): Docker master builds fail while checking version (@mistercrunch)
|
||||
- [#27085](https://github.com/apache/superset/pull/27085) fix(ci): new PR comments cancel ongoing ephemeral builds (@dpgaspar)
|
||||
- [#26663](https://github.com/apache/superset/pull/26663) fix(helm): Include option to use Redis with SSL (@shakeelansari63)
|
||||
- [#27060](https://github.com/apache/superset/pull/27060) fix(ephemeral): last try fixing this GH action (@mistercrunch)
|
||||
- [#27058](https://github.com/apache/superset/pull/27058) fix(ephemeral): point to the full tag name (@mistercrunch)
|
||||
- [#27057](https://github.com/apache/superset/pull/27057) fix(ephemeral): fix tagging command for ECR (@mistercrunch)
|
||||
- [#27056](https://github.com/apache/superset/pull/27056) fix(ephemeral): fix ephemeral builds in PR (@mistercrunch)
|
||||
- [#27048](https://github.com/apache/superset/pull/27048) fix(actions): correcting malformed labeler configs (@rusackas)
|
||||
- [#19744](https://github.com/apache/superset/pull/19744) fix(webpack-dev-server): parse env args (@jdbranham)
|
||||
- [#27042](https://github.com/apache/superset/pull/27042) fix(ci): fix action script v7 breaking changes v3 (@dpgaspar)
|
||||
- [#27040](https://github.com/apache/superset/pull/27040) fix(ci): fix action script v7 breaking changes v2 (@dpgaspar)
|
||||
- [#27014](https://github.com/apache/superset/pull/27014) fix(maps): france_regions.geojson generated with the notebook, from natural earth data (@qleroy)
|
||||
- [#26966](https://github.com/apache/superset/pull/26966) fix(actions): make tech debt uploader not block CI and skip w/o creds (@rusackas)
|
||||
- [#27001](https://github.com/apache/superset/pull/27001) fix(cypress): resolving random dri3 error on cypress runner (@rusackas)
|
||||
- [#27013](https://github.com/apache/superset/pull/27013) fix(plugins): Fix dashboard filter in Period Over Period KPI plugin (@Antonio-RiveroMartnez)
|
||||
- [#27005](https://github.com/apache/superset/pull/27005) fix(helm): Fix inconsistency for the chart appVersion and default image tag (@dnskr)
|
||||
- [#26995](https://github.com/apache/superset/pull/26995) fix(maps): Move Overseas department and regions closer to France mainland (@qleroy)
|
||||
- [#26987](https://github.com/apache/superset/pull/26987) fix(ci): typo in my bash script (@mistercrunch)
|
||||
- [#26985](https://github.com/apache/superset/pull/26985) fix(plugin): Period Over Period KPI Plugin Feature flag value (@Antonio-RiveroMartnez)
|
||||
- [#26969](https://github.com/apache/superset/pull/26969) fix(ci): support action/script v5 breaking change v2 (@dpgaspar)
|
||||
- [#26968](https://github.com/apache/superset/pull/26968) fix(ci): support action/script v5 breaking change (@dpgaspar)
|
||||
- [#26963](https://github.com/apache/superset/pull/26963) fix(plugin-chart-table): Revert "fix(chart table in dashboard): improve screen reading of table (#26453)" (@kgabryje)
|
||||
- [#26949](https://github.com/apache/superset/pull/26949) fix(actions): specify branch on monorepo lockfile pusher (@rusackas)
|
||||
- [#26921](https://github.com/apache/superset/pull/26921) fix(ci): remove deprecated set-output on github workflows (@dpgaspar)
|
||||
- [#26920](https://github.com/apache/superset/pull/26920) fix(ci): lint issue on update-monorepo-lockfiles.yml (@dpgaspar)
|
||||
- [#26919](https://github.com/apache/superset/pull/26919) fix(ci): ephemeral env build and up dependency (@dpgaspar)
|
||||
- [#26852](https://github.com/apache/superset/pull/26852) fix(ci): ephemeral env build (@dpgaspar)
|
||||
- [#26917](https://github.com/apache/superset/pull/26917) fix: remove ephemeral docker build from required workflow (@dpgaspar)
|
||||
- [#26787](https://github.com/apache/superset/pull/26787) fix(docker): improve docker tags to be cleared and avoid conflicts (@mistercrunch)
|
||||
- [#26904](https://github.com/apache/superset/pull/26904) fix(dependabot): lockfile updater won't fail when there's nothing to push (@rusackas)
|
||||
- [#26888](https://github.com/apache/superset/pull/26888) fix(dependencies): adding auth for dependabot lockfile action (@rusackas)
|
||||
- [#26901](https://github.com/apache/superset/pull/26901) fix(svg): reformatting svgs to allow license without breaking images (@rusackas)
|
||||
- [#26453](https://github.com/apache/superset/pull/26453) fix(chart table in dashboard): improve screen reading of table (@ncar285)
|
||||
- [#26801](https://github.com/apache/superset/pull/26801) fix: docker should always run, even in forks (@mistercrunch)
|
||||
- [#26752](https://github.com/apache/superset/pull/26752) fix: add user to latest-release-tag workflow (@eschutho)
|
||||
- [#26772](https://github.com/apache/superset/pull/26772) fix(docker): credentials issues around superset-cache in forks (@mistercrunch)
|
||||
- [#25510](https://github.com/apache/superset/pull/25510) fix: change the validation logic for python_date_format (@mapledan)
|
||||
- [#26710](https://github.com/apache/superset/pull/26710) fix(dependencies): stopping (and preventing) full lodash library import... now using only method level imports. (@rusackas)
|
||||
- [#26473](https://github.com/apache/superset/pull/26473) fix: docker ephemeral environment, push only on testenv comment (@dpgaspar)
|
||||
- [#26682](https://github.com/apache/superset/pull/26682) fix: Revert "build(deps): bump @mdx-js/react from 1.6.22 to 3.0.0 in /docs" (@rusackas)
|
||||
- [#26679](https://github.com/apache/superset/pull/26679) fix: Revert "buld(deps): bump swagger-ui-react from 4.1.3 to 5.11.0 in docs (#26552) (@michael-s-molina)
|
||||
- [#26648](https://github.com/apache/superset/pull/26648) fix: Removes unused cache cleanup (@michael-s-molina)
|
||||
- [#26649](https://github.com/apache/superset/pull/26649) fix: remove possible unnecessary file 1 (@dpgaspar)
|
||||
- [#26351](https://github.com/apache/superset/pull/26351) fix: stringify scarf pixel value (@eschutho)
|
||||
- [#26205](https://github.com/apache/superset/pull/26205) fix(docker): Remove race condition when building image (@alekseyolg)
|
||||
|
||||
**Others**
|
||||
|
||||
- [#27441](https://github.com/apache/superset/pull/27441) chore: Adds the 4.0 release notes (@michael-s-molina)
|
||||
- [#27768](https://github.com/apache/superset/pull/27768) chore(docs): Cleanup UPDATING.md (@john-bodley)
|
||||
- [#27625](https://github.com/apache/superset/pull/27625) perf(explore): virtualized datasource field sections (@justinpark)
|
||||
- [#27488](https://github.com/apache/superset/pull/27488) perf(sqllab): reduce bootstrap data delay by queries (@justinpark)
|
||||
- [#27281](https://github.com/apache/superset/pull/27281) chore: bump cryptography minimum to 42.0.4 (@sadpandajoe)
|
||||
- [#27232](https://github.com/apache/superset/pull/27232) chore: Removes Chromatic workflow and dependencies (@michael-s-molina)
|
||||
- [#27169](https://github.com/apache/superset/pull/27169) chore: Updates CHANGELOG.md with 3.0.4 data (@michael-s-molina)
|
||||
- [#27166](https://github.com/apache/superset/pull/27166) docs: add Dropit Shopping to users list (@IlyaDropit)
|
||||
- [#27143](https://github.com/apache/superset/pull/27143) refactor: Migrate ErrorBoundary to typescript (@EnxDev)
|
||||
- [#27136](https://github.com/apache/superset/pull/27136) chore(tests): Remove unnecessary explicit Flask-SQLAlchemy session expunges (@john-bodley)
|
||||
- [#27134](https://github.com/apache/superset/pull/27134) docs: add Geotab to users list (@JZ6)
|
||||
- [#26693](https://github.com/apache/superset/pull/26693) chore(hail mary): Update package-lock.json via npm-audit-fix (@rusackas)
|
||||
- [#27129](https://github.com/apache/superset/pull/27129) chore: lower cryptography min version to 41.0.2 (@sadpandajoe)
|
||||
- [#27120](https://github.com/apache/superset/pull/27120) docs(miscellaneous): Export Datasoruces: export datasources exports to ZIP (@ddxv)
|
||||
- [#27078](https://github.com/apache/superset/pull/27078) chore(internet_port): added new ports and removed unnecessary string class (@anirudh-hegde)
|
||||
- [#27118](https://github.com/apache/superset/pull/27118) chore: bump firebolt-sqlalchemy to support service account auth (@Vitor-Avila)
|
||||
- [#27090](https://github.com/apache/superset/pull/27090) chore(plugins): Update dropdown control for BigNumber with Time Comparison range (@Antonio-RiveroMartnez)
|
||||
- [#26909](https://github.com/apache/superset/pull/26909) refactor: Ensure Flask framework leverages the Flask-SQLAlchemy session (Phase II) (@john-bodley)
|
||||
- [#27030](https://github.com/apache/superset/pull/27030) chore: Migrate AlteredSliceTag to typescript (@EnxDev)
|
||||
- [#26773](https://github.com/apache/superset/pull/26773) chore(translations): updating pot -> po -> json files (babel 2.9.1) (@rusackas)
|
||||
- [#27071](https://github.com/apache/superset/pull/27071) chore(docs): adding meta db to Feature Flags page (@rusackas)
|
||||
- [#27072](https://github.com/apache/superset/pull/27072) docs(installation): document multi-platform support in Docker builds (@mistercrunch)
|
||||
- [#27053](https://github.com/apache/superset/pull/27053) chore: prevent prophet from logging non-errors as errors (@betodealmeida)
|
||||
- [#27038](https://github.com/apache/superset/pull/27038) chore(docs): bump version number in docs example (@sfirke)
|
||||
- [#26973](https://github.com/apache/superset/pull/26973) build(deps-dev): bump @types/jest from 26.0.24 to 29.5.12 in /superset-frontend/plugins/plugin-chart-handlebars (@dependabot[bot])
|
||||
- [#26260](https://github.com/apache/superset/pull/26260) chore(dashboard): migrate enzyme to RTL (@justinpark)
|
||||
- [#26989](https://github.com/apache/superset/pull/26989) chore: Remove database ID dependency for SSH Tunnel creation (@geido)
|
||||
- [#26981](https://github.com/apache/superset/pull/26981) build(deps): bump react-js-cron from 1.2.0 to 2.1.2 in /superset-frontend (@dependabot[bot])
|
||||
- [#26893](https://github.com/apache/superset/pull/26893) build(deps-dev): bump copy-webpack-plugin from 9.1.0 to 12.0.2 in /superset-frontend (@dependabot[bot])
|
||||
- [#26171](https://github.com/apache/superset/pull/26171) chore(sqllab): migrate to typescript (@justinpark)
|
||||
- [#27021](https://github.com/apache/superset/pull/27021) chore(plugins): Description, Category and Tags for BigNumber with Period Time Comparison plugin (@Antonio-RiveroMartnez)
|
||||
- [#27020](https://github.com/apache/superset/pull/27020) docs: add a note about database drivers in Docker builds (@mistercrunch)
|
||||
- [#26979](https://github.com/apache/superset/pull/26979) build(deps): bump @types/seedrandom from 2.4.30 to 3.0.8 in /superset-frontend (@dependabot[bot])
|
||||
- [#27000](https://github.com/apache/superset/pull/27000) chore(github): adding code owners for translation and country map wor… (@rusackas)
|
||||
- [#26998](https://github.com/apache/superset/pull/26998) docs: add notes to RELEASING about how to deploy docker images (@mistercrunch)
|
||||
- [#26996](https://github.com/apache/superset/pull/26996) build(deps): bump react-intersection-observer from 9.4.1 to 9.6.0 in /superset-frontend (@dependabot[bot])
|
||||
- [#26986](https://github.com/apache/superset/pull/26986) docs(presto): add Presto SSL connection details (@rusackas)
|
||||
- [#26526](https://github.com/apache/superset/pull/26526) build(deps): bump @vx/legend from 0.0.198 to 0.0.199 in /superset-frontend/plugins/legacy-plugin-chart-histogram (@dependabot[bot])
|
||||
- [#26903](https://github.com/apache/superset/pull/26903) chore(dependencies): bump encodable to 0.7.8 (@rusackas)
|
||||
- [#26977](https://github.com/apache/superset/pull/26977) build(deps-dev): bump webpack from 5.90.0 to 5.90.1 in /docs (@dependabot[bot])
|
||||
- [#26974](https://github.com/apache/superset/pull/26974) build(deps-dev): bump @types/node from 20.11.14 to 20.11.16 in /superset-websocket (@dependabot[bot])
|
||||
- [#26971](https://github.com/apache/superset/pull/26971) build(deps): bump actions/checkout from 2 to 4 (@dependabot[bot])
|
||||
- [#26972](https://github.com/apache/superset/pull/26972) build(deps): bump actions/cache from 1 to 4 (@dependabot[bot])
|
||||
- [#26970](https://github.com/apache/superset/pull/26970) build(deps): bump actions/setup-python from 4 to 5 (@dependabot[bot])
|
||||
- [#26950](https://github.com/apache/superset/pull/26950) chore(actions): getting fancier with labels (@rusackas)
|
||||
- [#26952](https://github.com/apache/superset/pull/26952) build(deps): bump actions/setup-java from 1 to 4 (@dependabot[bot])
|
||||
- [#26958](https://github.com/apache/superset/pull/26958) build(deps-dev): bump mock-socket from 9.0.3 to 9.3.1 in /superset-frontend (@dependabot[bot])
|
||||
- [#26953](https://github.com/apache/superset/pull/26953) build(deps): bump actions/github-script from 3 to 7 (@dependabot[bot])
|
||||
- [#26927](https://github.com/apache/superset/pull/26927) build(deps): bump actions/setup-node from 2 to 4 (@dependabot[bot])
|
||||
- [#26954](https://github.com/apache/superset/pull/26954) build(deps): bump aws-actions/configure-aws-credentials from 1 to 4 (@dependabot[bot])
|
||||
- [#26955](https://github.com/apache/superset/pull/26955) build(deps): bump aws-actions/amazon-ecr-login from 1 to 2 (@dependabot[bot])
|
||||
- [#26956](https://github.com/apache/superset/pull/26956) build(deps): bump github/codeql-action from 2 to 3 (@dependabot[bot])
|
||||
- [#26938](https://github.com/apache/superset/pull/26938) build(deps): bump moment from 2.29.4 to 2.30.1 in /superset-frontend (@dependabot[bot])
|
||||
- [#26943](https://github.com/apache/superset/pull/26943) chore(dependencies): Push lockfile for monorepo updates on rebuild/rebase (@rusackas)
|
||||
- [#26875](https://github.com/apache/superset/pull/26875) chore: make TS enums strictly PascalCase (@villebro)
|
||||
- [#26942](https://github.com/apache/superset/pull/26942) chore(ci): run pre-commit across the repo (@mistercrunch)
|
||||
- [#26935](https://github.com/apache/superset/pull/26935) build(deps): bump interweave from 13.0.0 to 13.1.0 in /superset-frontend (@dependabot[bot])
|
||||
- [#26941](https://github.com/apache/superset/pull/26941) build(deps): bump emotion-rgba from 0.0.9 to 0.0.12 in /superset-frontend (@dependabot[bot])
|
||||
- [#26939](https://github.com/apache/superset/pull/26939) build(deps-dev): bump @babel/core from 7.22.8 to 7.23.9 in /superset-frontend (@dependabot[bot])
|
||||
- [#26940](https://github.com/apache/superset/pull/26940) build(deps): bump shortid from 2.2.14 to 2.2.16 in /superset-frontend (@dependabot[bot])
|
||||
- [#26924](https://github.com/apache/superset/pull/26924) build(deps-dev): bump @types/node from 20.11.10 to 20.11.14 in /superset-websocket (@dependabot[bot])
|
||||
- [#26928](https://github.com/apache/superset/pull/26928) build(deps): bump chromaui/action from 1 to 10 (@dependabot[bot])
|
||||
- [#26929](https://github.com/apache/superset/pull/26929) build(deps): bump azure/setup-helm from 1 to 3 (@dependabot[bot])
|
||||
- [#26930](https://github.com/apache/superset/pull/26930) build(deps): bump actions/upload-artifact from 3 to 4 (@dependabot[bot])
|
||||
- [#26931](https://github.com/apache/superset/pull/26931) build(deps): bump actions/dependency-review-action from 2 to 4 (@dependabot[bot])
|
||||
- [#26918](https://github.com/apache/superset/pull/26918) chore(ci): notify PMCs of changes on required workflows (@dpgaspar)
|
||||
- [#26372](https://github.com/apache/superset/pull/26372) refactor: Removes the deprecated GENERIC_CHART_AXES feature flag (@michael-s-molina)
|
||||
- [#26881](https://github.com/apache/superset/pull/26881) build(deps-dev): update @babel/types requirement from ^7.13.12 to ^7.23.9 in /superset-frontend/plugins/plugin-chart-pivot-table (@dependabot[bot])
|
||||
- [#26727](https://github.com/apache/superset/pull/26727) build(deps): bump @ant-design/icons from 5.0.1 to 5.2.6 in /superset-frontend (@dependabot[bot])
|
||||
- [#26894](https://github.com/apache/superset/pull/26894) build(deps): bump @vx/scale from 0.0.197 to 0.0.199 in /superset-frontend (@dependabot[bot])
|
||||
- [#26840](https://github.com/apache/superset/pull/26840) build(deps): bump d3-selection from 1.4.2 to 3.0.0 in /superset-frontend (@dependabot[bot])
|
||||
- [#26861](https://github.com/apache/superset/pull/26861) build(deps): bump @visx/axis from 3.5.0 to 3.8.0 in /superset-frontend (@dependabot[bot])
|
||||
- [#26272](https://github.com/apache/superset/pull/26272) chore(explore): migrate enzyme to RTL (@justinpark)
|
||||
- [#26899](https://github.com/apache/superset/pull/26899) build(deps): bump @types/rison from 0.0.6 to 0.0.9 in /superset-frontend (@dependabot[bot])
|
||||
- [#26831](https://github.com/apache/superset/pull/26831) build(deps): bump @types/rison from 0.0.6 to 0.0.9 in /superset-frontend/packages/superset-ui-core (@dependabot[bot])
|
||||
- [#26869](https://github.com/apache/superset/pull/26869) build(deps): bump dom-to-image-more from 2.16.0 to 3.2.0 in /superset-frontend (@dependabot[bot])
|
||||
- [#26902](https://github.com/apache/superset/pull/26902) chore(docs): remove misplaced k8s installation instructions (@sfirke)
|
||||
- [#26897](https://github.com/apache/superset/pull/26897) build(deps-dev): bump webpack-bundle-analyzer from 4.9.0 to 4.10.1 in /superset-frontend (@dependabot[bot])
|
||||
- [#26900](https://github.com/apache/superset/pull/26900) chore(ci): make action/labeler work on fork PRs (@mistercrunch)
|
||||
- [#26879](https://github.com/apache/superset/pull/26879) chore(dependabot): ignore css-minimizer-webpack-plugin (@mistercrunch)
|
||||
- [#26860](https://github.com/apache/superset/pull/26860) build(deps): bump rehype-sanitize from 5.0.1 to 6.0.0 in /superset-frontend (@dependabot[bot])
|
||||
- [#26872](https://github.com/apache/superset/pull/26872) chore(dependabot): auto-update lockfiles for monorepo package bumps (@rusackas)
|
||||
- [#26859](https://github.com/apache/superset/pull/26859) build(deps): bump @types/enzyme from 3.10.10 to 3.10.18 in /superset-frontend (@dependabot[bot])
|
||||
- [#26874](https://github.com/apache/superset/pull/26874) chore(license): adding a missing license blurb to a translation file (@rusackas)
|
||||
- [#26870](https://github.com/apache/superset/pull/26870) build(deps): bump yargs and @types/yargs in /superset-frontend (@dependabot[bot])
|
||||
- [#26841](https://github.com/apache/superset/pull/26841) chore(dependencies): bump less from 3.12.2 to 4.2.0 in /superset-frontend (@dependabot[bot])
|
||||
- [#26868](https://github.com/apache/superset/pull/26868) chore(actions): run docs actions on Node 16 to conform with the project (@rusackas)
|
||||
- [#26857](https://github.com/apache/superset/pull/26857) chore(actions): generate FOSSA report on master, and ALWAYS check for… (@rusackas)
|
||||
- [#26826](https://github.com/apache/superset/pull/26826) build(deps-dev): bump @types/uuid from 9.0.7 to 9.0.8 in /superset-websocket (@dependabot[bot])
|
||||
- [#26867](https://github.com/apache/superset/pull/26867) build(deps): bump @testing-library/react-hooks from 5.0.3 to 5.1.3 in /superset-frontend (@dependabot[bot])
|
||||
- [#26866](https://github.com/apache/superset/pull/26866) build(deps): bump mousetrap and @types/mousetrap in /superset-frontend (@dependabot[bot])
|
||||
- [#26865](https://github.com/apache/superset/pull/26865) build(deps): bump react-redux from 7.2.8 to 7.2.9 in /superset-frontend (@dependabot[bot])
|
||||
- [#26855](https://github.com/apache/superset/pull/26855) chore(dependabot): lowering bump cadence from weekly to monthly (@rusackas)
|
||||
- [#26854](https://github.com/apache/superset/pull/26854) chore(CI): get docs building on ALL branches. (@rusackas)
|
||||
- [#26825](https://github.com/apache/superset/pull/26825) build(deps-dev): bump @types/node from 20.11.5 to 20.11.10 in /superset-websocket (@dependabot[bot])
|
||||
- [#26820](https://github.com/apache/superset/pull/26820) chore(lint/a11y): fixing and locking down jsx-a11y/anchor-is-valid (@rusackas)
|
||||
- [#26819](https://github.com/apache/superset/pull/26819) chore(dependencies): bumps match-sorter (@rusackas)
|
||||
- [#26798](https://github.com/apache/superset/pull/26798) chore: Add permission to view and drill on Dashboard context (@geido)
|
||||
- [#26827](https://github.com/apache/superset/pull/26827) build(deps): bump use-immer from 0.8.1 to 0.9.0 in /superset-frontend (@dependabot[bot])
|
||||
- [#24272](https://github.com/apache/superset/pull/24272) chore(deps): bump typescript to 4.8.4 (@jansule)
|
||||
- [#26832](https://github.com/apache/superset/pull/26832) build(deps): bump @types/react-table from 7.0.29 to 7.7.19 in /superset-frontend (@dependabot[bot])
|
||||
- [#26834](https://github.com/apache/superset/pull/26834) build(deps-dev): bump @docusaurus/module-type-aliases from 3.1.0 to 3.1.1 in /docs (@dependabot[bot])
|
||||
- [#26839](https://github.com/apache/superset/pull/26839) build(deps-dev): bump webpack from 5.89.0 to 5.90.0 in /docs (@dependabot[bot])
|
||||
- [#23873](https://github.com/apache/superset/pull/23873) chore: Slovenian translation update (@dkrat7)
|
||||
- [#26702](https://github.com/apache/superset/pull/26702) chore: fix GitHub 'Unchanged files with check annotations' reports in PR (@mistercrunch)
|
||||
- [#26726](https://github.com/apache/superset/pull/26726) build(deps): bump prism-react-renderer from 1.2.1 to 2.3.1 in /docs (@dependabot[bot])
|
||||
- [#26813](https://github.com/apache/superset/pull/26813) chore(ci): change code owners for .github (@dpgaspar)
|
||||
- [#26794](https://github.com/apache/superset/pull/26794) chore(dependencies): bumping jinja2 (@rusackas)
|
||||
- [#26816](https://github.com/apache/superset/pull/26816) chore: add google-auth for new example dashboard (@betodealmeida)
|
||||
- [#26815](https://github.com/apache/superset/pull/26815) chore: Reformat changelogs (@geido)
|
||||
- [#26793](https://github.com/apache/superset/pull/26793) chore(dependencies): bumping fonttools (@rusackas)
|
||||
- [#26442](https://github.com/apache/superset/pull/26442) chore: Technical Debt Metrics (@rusackas)
|
||||
- [#26800](https://github.com/apache/superset/pull/26800) chore: Splits the CHANGELOG into multiple files (@michael-s-molina)
|
||||
- [#26621](https://github.com/apache/superset/pull/26621) build(deps): update jquery requirement from ^3.4.1 to ^3.7.1 in /superset-frontend/packages/superset-ui-demo (@dependabot[bot])
|
||||
- [#26789](https://github.com/apache/superset/pull/26789) chore(RESOURCES): fix markdown for table formatting (@qleroy)
|
||||
- [#26759](https://github.com/apache/superset/pull/26759) chore: Add Embed Modal extension override and tests (@geido)
|
||||
- [#26656](https://github.com/apache/superset/pull/26656) build(deps-dev): bump css-minimizer-webpack-plugin from 3.4.1 to 6.0.0 in /superset-frontend (@dependabot[bot])
|
||||
- [#26704](https://github.com/apache/superset/pull/26704) chore: improve/decouple eslint and tsc 'npm run' commands (@mistercrunch)
|
||||
- [#26728](https://github.com/apache/superset/pull/26728) build(deps): bump @visx/grid from 3.0.1 to 3.5.0 in /superset-frontend (@dependabot[bot])
|
||||
- [#26729](https://github.com/apache/superset/pull/26729) build(deps): update classnames requirement from ^2.3.2 to ^2.5.1 in /superset-frontend/plugins/plugin-chart-table (@dependabot[bot])
|
||||
- [#26766](https://github.com/apache/superset/pull/26766) chore: prevent CI double runs on push + pull_request (@mistercrunch)
|
||||
- [#26528](https://github.com/apache/superset/pull/26528) build(deps-dev): bump jest from 26.6.3 to 29.7.0 in /superset-frontend/plugins/plugin-chart-handlebars (@dependabot[bot])
|
||||
- [#26513](https://github.com/apache/superset/pull/26513) build(deps): bump d3-color from 1.4.1 to 3.1.0 in /superset-frontend/plugins/legacy-plugin-chart-world-map (@dependabot[bot])
|
||||
- [#26596](https://github.com/apache/superset/pull/26596) build(deps): update @types/math-expression-evaluator requirement from ^1.2.1 to ^1.3.3 in /superset-frontend/packages/superset-ui-core (@dependabot[bot])
|
||||
- [#26595](https://github.com/apache/superset/pull/26595) build(deps-dev): update @types/lodash requirement from ^4.14.149 to ^4.14.202 in /superset-frontend/plugins/plugin-chart-handlebars (@dependabot[bot])
|
||||
- [#26698](https://github.com/apache/superset/pull/26698) build: Parallelize the CI image builds (continued) (@mistercrunch)
|
||||
- [#26499](https://github.com/apache/superset/pull/26499) build(deps): update d3-cloud requirement from ^1.2.5 to ^1.2.7 in /superset-frontend/plugins/plugin-chart-word-cloud (@dependabot[bot])
|
||||
- [#26481](https://github.com/apache/superset/pull/26481) build(deps-dev): bump @types/jest from 26.0.24 to 29.5.11 in /superset-frontend/plugins/plugin-chart-pivot-table (@dependabot[bot])
|
||||
- [#26546](https://github.com/apache/superset/pull/26546) build(deps-dev): bump @docusaurus/module-type-aliases from 2.4.1 to 3.1.0 in /docs (@dependabot[bot])
|
||||
- [#26105](https://github.com/apache/superset/pull/26105) docs(storybook): fix typo in TimeFormatStories.tsx (@HurSungYun)
|
||||
- [#26594](https://github.com/apache/superset/pull/26594) build(deps): update whatwg-fetch requirement from ^3.0.0 to ^3.6.20 in /superset-frontend/packages/superset-ui-core (@dependabot[bot])
|
||||
- [#26753](https://github.com/apache/superset/pull/26753) chore: do not mark helm releases as github latest (@eschutho)
|
||||
- [#26718](https://github.com/apache/superset/pull/26718) build(deps): bump @svgr/webpack from 5.5.0 to 8.1.0 in /docs (@dependabot[bot])
|
||||
- [#26714](https://github.com/apache/superset/pull/26714) build(deps): bump @visx/axis from 3.0.1 to 3.5.0 in /superset-frontend (@dependabot[bot])
|
||||
- [#26760](https://github.com/apache/superset/pull/26760) docs: update fixed CVEs for version 3.0.3 (@dpgaspar)
|
||||
- [#26483](https://github.com/apache/superset/pull/26483) build(deps): update @types/d3-cloud requirement from ^1.2.1 to ^1.2.9 in /superset-frontend/plugins/plugin-chart-word-cloud (@dependabot[bot])
|
||||
- [#26616](https://github.com/apache/superset/pull/26616) build(deps): bump fuse.js from 6.4.6 to 7.0.0 in /superset-frontend (@dependabot[bot])
|
||||
- [#26717](https://github.com/apache/superset/pull/26717) build(deps-dev): bump webpack from 5.76.0 to 5.89.0 in /docs (@dependabot[bot])
|
||||
- [#26570](https://github.com/apache/superset/pull/26570) build(deps-dev): bump prettier-plugin-packagejson from 2.2.15 to 2.4.9 in /superset-frontend (@dependabot[bot])
|
||||
- [#26556](https://github.com/apache/superset/pull/26556) build(deps-dev): bump @babel/register from 7.22.5 to 7.23.7 in /superset-frontend (@dependabot[bot])
|
||||
- [#26522](https://github.com/apache/superset/pull/26522) build(deps): update react-table requirement from ^7.6.3 to ^7.8.0 in /superset-frontend/plugins/plugin-chart-table (@dependabot[bot])
|
||||
- [#26613](https://github.com/apache/superset/pull/26613) build(deps): bump react-github-btn from 1.2.1 to 1.4.0 in /docs (@dependabot[bot])
|
||||
- [#26572](https://github.com/apache/superset/pull/26572) build(deps-dev): bump eslint-plugin-react-hooks from 4.2.0 to 4.6.0 in /superset-frontend (@dependabot[bot])
|
||||
- [#26576](https://github.com/apache/superset/pull/26576) build(deps): bump @emotion/babel-preset-css-prop from 11.2.0 to 11.11.0 in /superset-frontend (@dependabot[bot])
|
||||
- [#26724](https://github.com/apache/superset/pull/26724) build(deps): bump @saucelabs/theme-github-codeblock from 0.1.1 to 0.2.3 in /docs (@dependabot[bot])
|
||||
- [#26720](https://github.com/apache/superset/pull/26720) build(deps): bump @docsearch/react from 3.3.3 to 3.5.2 in /docs (@dependabot[bot])
|
||||
- [#26708](https://github.com/apache/superset/pull/26708) chore(dependencies): loosen constraints on dependency checker (@rusackas)
|
||||
- [#25665](https://github.com/apache/superset/pull/25665) build(deps): bump @babel/traverse from 7.22.8 to 7.23.2 in /superset-frontend (@dependabot[bot])
|
||||
- [#26694](https://github.com/apache/superset/pull/26694) chore(dependencies): removes unsued d3-color and d3-array (@rusackas)
|
||||
- [#26692](https://github.com/apache/superset/pull/26692) chore(dependencies): removes unused minimist (@rusackas)
|
||||
- [#26690](https://github.com/apache/superset/pull/26690) chore(dependencies): remove unused global-box (@rusackas)
|
||||
- [#26689](https://github.com/apache/superset/pull/26689) chore(dependencies): remove unused lodash-es (@rusackas)
|
||||
- [#26688](https://github.com/apache/superset/pull/26688) chore(dependencies): remove unused react-datetime (@rusackas)
|
||||
- [#26687](https://github.com/apache/superset/pull/26687) chore(dependencies): remove unused ansi-regex (@rusackas)
|
||||
- [#26686](https://github.com/apache/superset/pull/26686) chore(dependencies): removes unused @visx/tooltip (@rusackas)
|
||||
- [#26685](https://github.com/apache/superset/pull/26685) chore(dependencies): remove unused @babel/runtime-corejs3 (@rusackas)
|
||||
- [#26684](https://github.com/apache/superset/pull/26684) chore(dependencies): removes unused bootstrap-slider (@rusackas)
|
||||
- [#26691](https://github.com/apache/superset/pull/26691) chore(dependencies): npm audit fix for superset-ui-demo (@rusackas)
|
||||
- [#26703](https://github.com/apache/superset/pull/26703) chore: silence SECRET_KEY warning when running tests (@mistercrunch)
|
||||
- [#26733](https://github.com/apache/superset/pull/26733) build(deps-dev): bump @types/node from 20.11.1 to 20.11.5 in /superset-websocket (@dependabot[bot])
|
||||
- [#26329](https://github.com/apache/superset/pull/26329) refactor: Removes the deprecated DASHBOARD_NATIVE_FILTERS feature flag (@michael-s-molina)
|
||||
- [#26347](https://github.com/apache/superset/pull/26347) refactor: Removes the deprecated VERSIONED_EXPORT feature flag (@michael-s-molina)
|
||||
- [#26677](https://github.com/apache/superset/pull/26677) chore: Updates the Release Process link in the issue template (@michael-s-molina)
|
||||
- [#26375](https://github.com/apache/superset/pull/26375) chore: Updates the bug report template (@michael-s-molina)
|
||||
- [#26462](https://github.com/apache/superset/pull/26462) refactor: Removes the Profile feature (@michael-s-molina)
|
||||
- [#26665](https://github.com/apache/superset/pull/26665) build(deps): bump the npm_and_yarn group group in /superset-frontend with 2 updates (@dependabot[bot])
|
||||
- [#26661](https://github.com/apache/superset/pull/26661) chore: Updates CHANGELOG.md and UPDATING.md with 3.1.0 data (@michael-s-molina)
|
||||
- [#26330](https://github.com/apache/superset/pull/26330) refactor: Removes the deprecated DASHBOARD_FILTERS_EXPERIMENTAL feature flag (@michael-s-molina)
|
||||
- [#26547](https://github.com/apache/superset/pull/26547) build(deps): bump @mdx-js/react from 1.6.22 to 3.0.0 in /docs (@dependabot[bot])
|
||||
- [#26552](https://github.com/apache/superset/pull/26552) build(deps): bump swagger-ui-react from 4.1.3 to 5.11.0 in /docs (@dependabot[bot])
|
||||
- [#26555](https://github.com/apache/superset/pull/26555) build(deps-dev): bump @tsconfig/docusaurus from 1.0.7 to 2.0.2 in /docs (@dependabot[bot])
|
||||
- [#26344](https://github.com/apache/superset/pull/26344) refactor: Removes the deprecated ENABLE_EXPLORE_JSON_CSRF_PROTECTION feature flag (@michael-s-molina)
|
||||
- [#26345](https://github.com/apache/superset/pull/26345) refactor: Removes the deprecated ENABLE_TEMPLATE_REMOVE_FILTERS feature flag (@michael-s-molina)
|
||||
- [#25800](https://github.com/apache/superset/pull/25800) docs: update embedded readme with user params context (@jbat)
|
||||
- [#12175](https://github.com/apache/superset/pull/12175) build(deps): bump node-notifier from 8.0.0 to 8.0.1 in /superset-frontend (@dependabot[bot])
|
||||
- [#26549](https://github.com/apache/superset/pull/26549) build(deps): bump clsx from 1.1.1 to 2.1.0 in /docs (@dependabot[bot])
|
||||
- [#26560](https://github.com/apache/superset/pull/26560) build(deps-dev): bump typescript from 4.4.4 to 5.3.3 in /docs (@dependabot[bot])
|
||||
- [#26650](https://github.com/apache/superset/pull/26650) chore: Updates CHANGELOG.md and UPDATING.md with 3.0.3 data (@michael-s-molina)
|
||||
- [#26346](https://github.com/apache/superset/pull/26346) refactor: Removes the deprecated REMOVE_SLICE_LEVEL_LABEL_COLORS feature flag (@michael-s-molina)
|
||||
- [#26200](https://github.com/apache/superset/pull/26200) refactor: Ensure Flask framework leverages the Flask-SQLAlchemy session (Phase I) (@john-bodley)
|
||||
- [#26633](https://github.com/apache/superset/pull/26633) chore: Deprecates the DASHBOARD_CROSS_FILTERS feature flag (@michael-s-molina)
|
||||
- [#26635](https://github.com/apache/superset/pull/26635) chore: Deprecates the ENABLE_JAVASCRIPT_CONTROLS feature flag (@michael-s-molina)
|
||||
- [#26636](https://github.com/apache/superset/pull/26636) chore: Sets DASHBOARD_VIRTUALIZATION feature flag to True by default (@michael-s-molina)
|
||||
- [#26540](https://github.com/apache/superset/pull/26540) chore(API): Include changed_by.id in Get Charts and Get Datasets API responses (@Vitor-Avila)
|
||||
- [#26637](https://github.com/apache/superset/pull/26637) chore: Sets the DRILL_BY feature flag to True by default (@michael-s-molina)
|
||||
- [#26186](https://github.com/apache/superset/pull/26186) refactor: Ensure Celery leverages the Flask-SQLAlchemy session (@john-bodley)
|
||||
- [#26500](https://github.com/apache/superset/pull/26500) build(deps): update datamaps requirement from ^0.5.8 to ^0.5.9 in /superset-frontend/plugins/legacy-plugin-chart-world-map (@dependabot[bot])
|
||||
- [#25663](https://github.com/apache/superset/pull/25663) build(deps-dev): bump @babel/traverse from 7.16.10 to 7.23.2 in /superset-embedded-sdk (@dependabot[bot])
|
||||
- [#25664](https://github.com/apache/superset/pull/25664) build(deps): bump @babel/traverse from 7.21.4 to 7.23.2 in /superset-frontend/cypress-base (@dependabot[bot])
|
||||
- [#25662](https://github.com/apache/superset/pull/25662) build(deps): bump @babel/traverse from 7.16.3 to 7.23.2 in /docs (@dependabot[bot])
|
||||
- [#26606](https://github.com/apache/superset/pull/26606) docs: fix links (@fenilgmehta)
|
||||
- [#26348](https://github.com/apache/superset/pull/26348) refactor: Removes the deprecated CLIENT_CACHE feature flag (@michael-s-molina)
|
||||
- [#26349](https://github.com/apache/superset/pull/26349) refactor: Removes the deprecated DASHBOARD_CACHE feature flag (@michael-s-molina)
|
||||
- [#26450](https://github.com/apache/superset/pull/26450) chore: Deprecates the KV_STORE feature flag (@michael-s-molina)
|
||||
- [#26343](https://github.com/apache/superset/pull/26343) refactor: Removes the deprecated ENABLE_EXPLORE_DRAG_AND_DROP feature flag (@michael-s-molina)
|
||||
- [#26331](https://github.com/apache/superset/pull/26331) refactor: Removes the deprecated DISABLE_DATASET_SOURCE_EDIT feature flag (@michael-s-molina)
|
||||
- [#26589](https://github.com/apache/superset/pull/26589) build(deps): update lodash requirement from ^4.17.11 to ^4.17.21 in /superset-frontend/plugins/legacy-preset-chart-nvd3 (@dependabot[bot])
|
||||
- [#26506](https://github.com/apache/superset/pull/26506) build(deps): update urijs requirement from ^1.19.8 to ^1.19.11 in /superset-frontend/plugins/legacy-preset-chart-nvd3 (@dependabot[bot])
|
||||
- [#26520](https://github.com/apache/superset/pull/26520) build(deps-dev): bump style-loader from 3.3.3 to 3.3.4 in /superset-frontend (@dependabot[bot])
|
||||
- [#26538](https://github.com/apache/superset/pull/26538) build(deps-dev): bump @types/urijs from 1.19.19 to 1.19.25 in /superset-frontend (@dependabot[bot])
|
||||
- [#26530](https://github.com/apache/superset/pull/26530) build(deps): update lodash requirement from ^4.17.15 to ^4.17.21 in /superset-frontend/packages/superset-ui-chart-controls (@dependabot[bot])
|
||||
- [#26539](https://github.com/apache/superset/pull/26539) build(deps): update xss requirement from ^1.0.10 to ^1.0.14 in /superset-frontend/plugins/plugin-chart-table (@dependabot[bot])
|
||||
- [#26545](https://github.com/apache/superset/pull/26545) build(deps): bump moment-timezone from 0.5.37 to 0.5.44 in /superset-frontend (@dependabot[bot])
|
||||
- [#26562](https://github.com/apache/superset/pull/26562) build(deps): bump less from 4.1.3 to 4.2.0 in /docs (@dependabot[bot])
|
||||
- [#26612](https://github.com/apache/superset/pull/26612) build(deps): bump @docusaurus/preset-classic from 2.4.1 to 2.4.3 in /docs (@dependabot[bot])
|
||||
- [#26619](https://github.com/apache/superset/pull/26619) build(deps-dev): bump @types/node from 20.11.0 to 20.11.1 in /superset-websocket (@dependabot[bot])
|
||||
- [#26503](https://github.com/apache/superset/pull/26503) build(deps): update prop-types requirement from ^15.6.2 to ^15.8.1 in /superset-frontend/plugins/legacy-plugin-chart-sunburst (@dependabot[bot])
|
||||
- [#26509](https://github.com/apache/superset/pull/26509) build(deps): update prop-types requirement from ^15.6.2 to ^15.8.1 in /superset-frontend/plugins/legacy-plugin-chart-rose (@dependabot[bot])
|
||||
- [#26515](https://github.com/apache/superset/pull/26515) build(deps): update prop-types requirement from ^15.6.2 to ^15.8.1 in /superset-frontend/plugins/legacy-plugin-chart-country-map (@dependabot[bot])
|
||||
- [#26524](https://github.com/apache/superset/pull/26524) build(deps): update prop-types requirement from ^15.6.2 to ^15.8.1 in /superset-frontend/plugins/legacy-plugin-chart-partition (@dependabot[bot])
|
||||
- [#26525](https://github.com/apache/superset/pull/26525) build(deps): update prop-types requirement from ^15.6.2 to ^15.8.1 in /superset-frontend/plugins/legacy-plugin-chart-chord (@dependabot[bot])
|
||||
- [#26535](https://github.com/apache/superset/pull/26535) build(deps): update prop-types requirement from ^15.6.2 to ^15.8.1 in /superset-frontend/plugins/legacy-plugin-chart-histogram (@dependabot[bot])
|
||||
- [#26536](https://github.com/apache/superset/pull/26536) build(deps): update prop-types requirement from ^15.6.2 to ^15.8.1 in /superset-frontend/plugins/legacy-plugin-chart-calendar (@dependabot[bot])
|
||||
- [#26541](https://github.com/apache/superset/pull/26541) build(deps): update lodash requirement from ^4.17.15 to ^4.17.21 in /superset-frontend/plugins/plugin-chart-echarts (@dependabot[bot])
|
||||
- [#26569](https://github.com/apache/superset/pull/26569) build(deps): update prop-types requirement from ^15.6.2 to ^15.8.1 in /superset-frontend/plugins/legacy-plugin-chart-map-box (@dependabot[bot])
|
||||
- [#26574](https://github.com/apache/superset/pull/26574) build(deps): update prop-types requirement from ^15.7.2 to ^15.8.1 in /superset-frontend/plugins/legacy-plugin-chart-parallel-coordinates (@dependabot[bot])
|
||||
- [#26580](https://github.com/apache/superset/pull/26580) build(deps): update prop-types requirement from ^15.6.2 to ^15.8.1 in /superset-frontend/plugins/legacy-plugin-chart-horizon (@dependabot[bot])
|
||||
- [#26587](https://github.com/apache/superset/pull/26587) build(deps): update prop-types requirement from ^15.7.2 to ^15.8.1 in /superset-frontend/packages/superset-ui-chart-controls (@dependabot[bot])
|
||||
- [#26477](https://github.com/apache/superset/pull/26477) build(deps): update prop-types requirement from ^15.6.2 to ^15.8.1 in /superset-frontend/plugins/legacy-plugin-chart-sankey (@dependabot[bot])
|
||||
- [#26480](https://github.com/apache/superset/pull/26480) build(deps): update prop-types requirement from ^15.6.2 to ^15.8.1 in /superset-frontend/plugins/legacy-preset-chart-nvd3 (@dependabot[bot])
|
||||
- [#26484](https://github.com/apache/superset/pull/26484) build(deps): update prop-types requirement from ^15.6.2 to ^15.8.1 in /superset-frontend/plugins/legacy-plugin-chart-world-map (@dependabot[bot])
|
||||
- [#26486](https://github.com/apache/superset/pull/26486) build(deps): update prop-types requirement from ^15.6.2 to ^15.8.1 in /superset-frontend/plugins/legacy-plugin-chart-event-flow (@dependabot[bot])
|
||||
- [#26488](https://github.com/apache/superset/pull/26488) build(deps): update prop-types requirement from ^15.6.2 to ^15.8.1 in /superset-frontend/plugins/legacy-plugin-chart-heatmap (@dependabot[bot])
|
||||
- [#26492](https://github.com/apache/superset/pull/26492) build(deps): update prop-types requirement from ^15.6.2 to ^15.8.1 in /superset-frontend/plugins/legacy-plugin-chart-sankey-loop (@dependabot[bot])
|
||||
- [#26558](https://github.com/apache/superset/pull/26558) build(deps): bump @docusaurus/plugin-client-redirects from 2.4.1 to 2.4.3 in /docs (@dependabot[bot])
|
||||
- [#26554](https://github.com/apache/superset/pull/26554) build(deps): bump @algolia/client-search from 4.13.0 to 4.22.1 in /docs (@dependabot[bot])
|
||||
- [#26559](https://github.com/apache/superset/pull/26559) build(deps): bump react-draggable from 4.4.3 to 4.4.6 in /superset-frontend (@dependabot[bot])
|
||||
- [#26568](https://github.com/apache/superset/pull/26568) build(deps): bump react-resizable from 3.0.4 to 3.0.5 in /superset-frontend (@dependabot[bot])
|
||||
- [#26591](https://github.com/apache/superset/pull/26591) build(deps): update lodash requirement from ^4.17.11 to ^4.17.21 in /superset-frontend/packages/generator-superset (@dependabot[bot])
|
||||
- [#26592](https://github.com/apache/superset/pull/26592) build(deps): update prop-types requirement from ^15.6.2 to ^15.8.1 in /superset-frontend/plugins/legacy-plugin-chart-paired-t-test (@dependabot[bot])
|
||||
- [#26600](https://github.com/apache/superset/pull/26600) build(deps-dev): update yeoman-assert requirement from ^3.1.0 to ^3.1.1 in /superset-frontend/packages/generator-superset (@dependabot[bot])
|
||||
- [#26601](https://github.com/apache/superset/pull/26601) build(deps): update fast-safe-stringify requirement from ^2.0.6 to ^2.1.1 in /superset-frontend/plugins/legacy-preset-chart-nvd3 (@dependabot[bot])
|
||||
- [#26444](https://github.com/apache/superset/pull/26444) chore(deps): adding dependabot for plugins/packages and upping PR limits. (@rusackas)
|
||||
- [#26468](https://github.com/apache/superset/pull/26468) docs: Update installing-superset-from-scratch.mdx (@nytai)
|
||||
- [#26455](https://github.com/apache/superset/pull/26455) build(deps-dev): bump @types/node from 20.10.8 to 20.11.0 in /superset-websocket (@dependabot[bot])
|
||||
- [#26447](https://github.com/apache/superset/pull/26447) build(deps-dev): bump @types/node from 20.10.7 to 20.10.8 in /superset-websocket (@dependabot[bot])
|
||||
- [#26441](https://github.com/apache/superset/pull/26441) build(deps): bump follow-redirects from 1.15.2 to 1.15.4 in /superset-frontend (@dependabot[bot])
|
||||
- [#26440](https://github.com/apache/superset/pull/26440) build(deps-dev): bump follow-redirects from 1.15.3 to 1.15.4 in /superset-embedded-sdk (@dependabot[bot])
|
||||
- [#26438](https://github.com/apache/superset/pull/26438) build(deps): bump follow-redirects from 1.14.8 to 1.15.4 in /docs (@dependabot[bot])
|
||||
- [#26428](https://github.com/apache/superset/pull/26428) chore(docs): remove incorrect answer from FAQ (@sfirke)
|
||||
- [#26425](https://github.com/apache/superset/pull/26425) build(deps-dev): bump @types/node from 20.10.6 to 20.10.7 in /superset-websocket (@dependabot[bot])
|
||||
- [#24605](https://github.com/apache/superset/pull/24605) chore: Reenable SQLite tests which leverage foreign key constraints et al. (@john-bodley)
|
||||
- [#26386](https://github.com/apache/superset/pull/26386) build(deps-dev): bump @types/node from 20.10.5 to 20.10.6 in /superset-websocket (@dependabot[bot])
|
||||
- [#26381](https://github.com/apache/superset/pull/26381) docs: fix spelling and grammar (@fenilgmehta)
|
||||
- [#26363](https://github.com/apache/superset/pull/26363) build(deps): bump ws from 8.15.0 to 8.16.0 in /superset-websocket (@dependabot[bot])
|
||||
- [#26371](https://github.com/apache/superset/pull/26371) docs: fix config webdriver snippet in install on K8s (@dbaltor)
|
||||
- [#26368](https://github.com/apache/superset/pull/26368) chore(docs): point to correct StackOverflow page (@sfirke)
|
||||
- [#26308](https://github.com/apache/superset/pull/26308) docs: update CVEs fixed on 3.0.2 and 2.1.3 (@dpgaspar)
|
||||
- [#26305](https://github.com/apache/superset/pull/26305) build(deps-dev): bump @types/node from 20.10.4 to 20.10.5 in /superset-websocket (@dependabot[bot])
|
||||
- [#26301](https://github.com/apache/superset/pull/26301) chore(sqlalchemy): import from correct path (@villebro)
|
||||
- [#26294](https://github.com/apache/superset/pull/26294) build(deps-dev): bump eslint from 8.55.0 to 8.56.0 in /superset-websocket (@dependabot[bot])
|
||||
- [#26293](https://github.com/apache/superset/pull/26293) chore(cleanup): removing redundant rendering logic in telemetry pixel (@rusackas)
|
||||
- [#26285](https://github.com/apache/superset/pull/26285) chore(docs): fix typo "loader balancer" -> "load balancer" (@sfirke)
|
||||
- [#26280](https://github.com/apache/superset/pull/26280) chore(in the wild): Making it even easer to add a name (and cleanup) (@rusackas)
|
||||
- [#26253](https://github.com/apache/superset/pull/26253) chore(docs): add troubleshooting guide to alerts & reports (@sfirke)
|
||||
- [#26259](https://github.com/apache/superset/pull/26259) chore(async queries): sending statsd event for async events API call (@zephyring)
|
||||
- [#25628](https://github.com/apache/superset/pull/25628) chore: adding 'no-experimental-fetch' node option by default (@rusackas)
|
||||
- [#26220](https://github.com/apache/superset/pull/26220) chore(tests): Add tests to the column denormalization flow (@Vitor-Avila)
|
||||
- [#26078](https://github.com/apache/superset/pull/26078) chore: add class component tasklist file (@eschutho)
|
||||
- [#26234](https://github.com/apache/superset/pull/26234) build(deps): bump ws from 8.14.2 to 8.15.0 in /superset-websocket (@dependabot[bot])
|
||||
- [#26233](https://github.com/apache/superset/pull/26233) build(deps-dev): bump ts-node from 10.9.1 to 10.9.2 in /superset-websocket (@dependabot[bot])
|
||||
- [#26204](https://github.com/apache/superset/pull/26204) build(deps-dev): bump @types/node from 20.10.3 to 20.10.4 in /superset-websocket (@dependabot[bot])
|
||||
- [#26174](https://github.com/apache/superset/pull/26174) build(deps-dev): bump eslint from 8.54.0 to 8.55.0 in /superset-websocket (@dependabot[bot])
|
||||
- [#26150](https://github.com/apache/superset/pull/26150) docs: update CHANGELOG for 2.1.2 (@dpgaspar)
|
||||
- [#26166](https://github.com/apache/superset/pull/26166) build(deps-dev): bump eslint-config-prettier from 9.0.0 to 9.1.0 in /superset-websocket (@dependabot[bot])
|
||||
- [#26167](https://github.com/apache/superset/pull/26167) build(deps-dev): bump @types/node from 20.10.1 to 20.10.3 in /superset-websocket (@dependabot[bot])
|
||||
- [#26129](https://github.com/apache/superset/pull/26129) docs: add quickstart (@artofcomputing)
|
||||
- [#26143](https://github.com/apache/superset/pull/26143) build(deps-dev): bump @types/node from 20.10.0 to 20.10.1 in /superset-websocket (@dependabot[bot])
|
||||
- [#26149](https://github.com/apache/superset/pull/26149) docs: update CVEs fixed on 3.0.0 (@dpgaspar)
|
||||
- [#26038](https://github.com/apache/superset/pull/26038) docs(drivers): refresh guide on adding a db driver in docker (@sfirke)
|
||||
- [#26124](https://github.com/apache/superset/pull/26124) docs: add Increff to INTHEWILD (@ishansinghania)
|
||||
- [#26112](https://github.com/apache/superset/pull/26112) docs: add Onebeat to users list (@GuyAttia)
|
||||
- [#26119](https://github.com/apache/superset/pull/26119) docs: Update Trino Kerberos configuration (@john-bodley)
|
||||
- [#26100](https://github.com/apache/superset/pull/26100) build(deps-dev): bump @types/node from 20.9.4 to 20.10.0 in /superset-websocket (@dependabot[bot])
|
||||
- [#26099](https://github.com/apache/superset/pull/26099) build(deps-dev): bump @types/cookie from 0.5.4 to 0.6.0 in /superset-websocket (@dependabot[bot])
|
||||
- [#26104](https://github.com/apache/superset/pull/26104) docs: update CVEs fixed on 2.1.2 (@dpgaspar)
|
||||
151
RELEASING/release-notes-4-0/README.md
Normal file
@@ -0,0 +1,151 @@
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
|
||||
# Release Notes for Superset 4.0.0
|
||||
|
||||
4.0.0 brings a plethora of exciting changes to Superset. We have introduced several breaking changes to improve the overall architecture and scalability of our codebase. These changes may require some code updates, but they are designed to enhance performance and maintainability in the long run. We have also upgraded various dependencies to their latest versions and deprecated certain features that are no longer aligned with our long-term roadmap. We encourage all developers to carefully review the `CHANGELOG.md` and `UPDATING.md` files and update their code accordingly. While our main focus was on code cleanup, this release also contains exciting new features and marks a significant milestone for the project.
|
||||
|
||||
Here are some of the highlights of this release.
|
||||
|
||||
### Alerts and Reports modal redesign
|
||||
|
||||
The Alerts and Reports modal has been [redesigned](https://github.com/apache/superset/discussions/25729) to improve the user experience and make it more intuitive. The new design has the following goals:
|
||||
|
||||
- Declutter the interface by providing a cleaner, more organized layout
|
||||
- Create a linear setup process with the necessary options in a step-by-step manner to make alert/report setup more intuitive
|
||||
- Prepare the interface for additional features that will be introduced in future releases, like the ability to pre-filter a dashboard being sent.
|
||||
|
||||
<div>
|
||||
<img src="media/alert-modal-1.png" alt="Image" width="33%">
|
||||
<img src="media/alert-modal-2.png" alt="Image" width="33%">
|
||||
<img src="media/alert-modal-3.png" alt="Image" width="33%">
|
||||
</div>
|
||||
|
||||
### Tags
|
||||
|
||||
Tags are available using the `TAGGING_SYSTEM` feature flag. They address many of the [requests made by the community](https://github.com/apache/superset/discussions/19194) and aim to make it easier to organize and curate charts, dashboards, and saved queries, allowing for effortless data discovery and collaboration within an organization. Users can create flexible and customizable tags for each piece of content, enabling different ways of organizing assets. Programmatic access to tag-related operations are supported via the RESTful API.
|
||||
|
||||
<div>
|
||||
<img src="media/tags-1.png" alt="Image" width="100%">
|
||||
<img src="media/tags-2.png" alt="Image" width="100%">
|
||||
</div>
|
||||
|
||||
### New CHANGELOG format
|
||||
|
||||
We changed the structure of the `CHANGELOG.md` file in [#26800](https://github.com/apache/superset/pull/26800) to better organize the contents of each release and also to deal with GitHub size limitations when displaying the file. Now every release will have its own file at `CHANGELOG/<version>.md`. The main `CHANGELOG.md` file is now an index with links to all releases.
|
||||
|
||||
### Improved drag and drop experience when editing a dashboard
|
||||
|
||||
When a component was being dragged towards the edge of the tab container or the row/column containers, multiple drop indicators were often displayed. This created confusion about the exact insertion point of the element. To fix this, we built in [#26699](https://github.com/apache/superset/pull/26699) and [#26313](https://github.com/apache/superset/pull/26313) a distinct, non-conflicting area for the drop zone, which is highlighted during the dragging process to clearly indicate where the element will be placed. We also improved the forbidden drop zones to prevent users from dropping elements in invalid locations.
|
||||
|
||||
<div>
|
||||
<img src="media/dashboard-dnd-1.png" alt="Image" width="100%">
|
||||
<img src="media/dashboard-dnd-2.png" alt="Image" width="100%">
|
||||
</div>
|
||||
|
||||
### Improved drag and drop experience when editing a chart
|
||||
|
||||
Now, during dragging, all droppable zones are highlighted, with distinct colors indicating available and unavailable drop locations. This enhancement clarifies potential drop points and helps avoid inadvertent placements in invalid areas. The update also aligns the drag-over feedback with the dashboard's drag-and-drop modifications, ensuring a uniform and enhanced user experience.
|
||||
|
||||

|
||||
|
||||
### Dropping support for 3.0.X versions
|
||||
|
||||
In accordance with our [release process](https://github.com/apache/superset/wiki/Release-Process), we are dropping support for the 3.0.X versions. As a result, we will no longer be providing bug fixes for these versions. We strongly recommend that all users upgrade to the latest version to take advantage of the newest features and bug fixes. Moving forward, the supported versions will be 3.1.X and 4.0.X. Bug fixes will continue to be backported to 3.1.X until the next minor release. For more information, please refer to our [release schedule](https://github.com/apache/superset/wiki/Release-Process#schedule).
|
||||
|
||||
### Feature flag changes
|
||||
|
||||
Following our 4.0 proposals, the following feature flags were removed, i.e., the feature was permanently enabled or removed.
|
||||
|
||||
- `VERSIONED_EXPORT`
|
||||
- `DASHBOARD_FILTERS_EXPERIMENTAL`
|
||||
- `ENABLE_EXPLORE_JSON_CSRF_PROTECTION`
|
||||
- `ENABLE_TEMPLATE_REMOVE_FILTERS`
|
||||
- `REMOVE_SLICE_LEVEL_LABEL_COLORS`
|
||||
- `CLIENT_CACHE`
|
||||
- `DASHBOARD_CACHE`
|
||||
- `DASHBOARD_NATIVE_FILTERS_SET`
|
||||
- `ENABLE_EXPLORE_DRAG_AND_DROP`
|
||||
- `DISABLE_DATASET_SOURCE_EDIT`
|
||||
- `DASHBOARD_NATIVE_FILTERS`
|
||||
- `GENERIC_CHART_AXES`
|
||||
|
||||
The following feature flags were deprecated:
|
||||
|
||||
- `DASHBOARD_CROSS_FILTERS`
|
||||
- `ENABLE_JAVASCRIPT_CONTROLS`
|
||||
- `KV_STORE`
|
||||
|
||||
The following feature flags were enabled by default:
|
||||
|
||||
- `DASHBOARD_VIRTUALIZATION`
|
||||
- `DRILL_BY`
|
||||
|
||||
### Removed features
|
||||
|
||||
As part of the 4.0 approved initiatives, the following features were removed from Superset:
|
||||
|
||||
- Filter Box: [#26328](https://github.com/apache/superset/pull/26328) removed the Filter Box code and its associated dependencies `react-select` and `array-move`. It also removed the `DeprecatedSelect` and `AsyncSelect` components that were exclusively used by filter boxes. Existing filter boxes will be automatically migrated to native dashboard filters.
|
||||
|
||||
- Filter Sets: [#26369](https://github.com/apache/superset/pull/26369) removed the Filters Set feature including the deprecated `DASHBOARD_NATIVE_FILTERS_SET` feature flag and all related API endpoints. The feature is permanently removed as it was not being actively maintained, it was not widely used, and it was full of bugs. We also considered that if we were to provide a similar feature, it would be better to re-implement it from scratch given the amount of technical debt that the implementation had.
|
||||
|
||||
- Profile: [#26462](https://github.com/apache/superset/pull/26462) removed the Profile feature given that it was not actively maintained nor widely used.
|
||||
|
||||
- Redirect API: [#26377](https://github.com/apache/superset/pull/26377) removed the deprecated Redirect API that supported short URLs (`/r`) and the `url` metadata table used to store them that was used before the permalink feature. Users lost the ability to generate R links ~1.5 years ago which seems sufficient time to remove the API.
|
||||
|
||||
### Business logic improvements
|
||||
|
||||
As part of [[SIP-99] Proposal for correctly handling business logic](https://github.com/apache/superset/issues/25048) (specifically [SIP-99A](https://github.com/apache/superset/issues/25107) and [SIP-99B](https://github.com/apache/superset/issues/25108)), this release contains many improvements to the handling of business logic in Superset, specifically related to SQLAlchemy sessions and transactions. The goal of these efforts is to simplify the code, improve code quality, ensure a consistent "unit of work" approach, and provide clear guidance and examples of accepted code standards. These changes aim to improve developer experience by making the code simpler, improving testing, and ensuring a more streamlined and reliable system. We still have a long way to go to fully implement the SIP-99 proposal, but we are making progress and we are excited about the improvements that have been made so far.
|
||||
|
||||
### All country maps are now managed via Jupyter Notebook
|
||||
|
||||
In this release we made updates to the Jupyter Notebook to ensure reliable execution by removing deprecated methods, adding new countries, including missing maps, and fixing filename inconsistencies. This will make it easier to add more countries, dynamically add them to the country map plugin, and update map regions periodically. You can check [#26300](https://github.com/apache/superset/pull/26300) for more details.
|
||||
|
||||
### Sunburst chart migrated to ECharts
|
||||
|
||||
The ECharts version of the Sunburst chart was introduced by [#22833](https://github.com/apache/superset/pull/22833) as part of our efforts to complete [SIP-50](https://github.com/apache/superset/issues/10418). In 4.0, legacy Sunburst charts are [automatically migrated](https://github.com/apache/superset/pull/26350) to ECharts and the legacy version was removed.
|
||||
|
||||

|
||||
|
||||
### Some cool stats
|
||||
|
||||
- ~15K lines of code were removed by PRs related to 4.0 proposals
|
||||
- We reduced the number of NPM packages vulnerabilities by 72%
|
||||
- 3.1: 90 vulnerabilities (42 moderate, 34 high, 14 critical)
|
||||
- 4.0: 25 vulnerabilities (16 moderate, 8 high, 1 critical)
|
||||
- 40+ dependency changes (upgrades, additions, and removals)
|
||||
|
||||
### How to upgrade
|
||||
|
||||
As with any Superset version upgrade, the process is simple in the broadest strokes, as outlined in the documentation. However, as with any upgrade, we expect to see numerous speed bumps along that path depending on your configuration, your infrastructure, your databases in use, and other customizations/configurations. To make a safe leap to this version, we'd suggest the following steps:
|
||||
|
||||
- Back up your databases
|
||||
- Carefully read `CHANGELOG.md` for all the incremental changes in this version (and any prior versions between your current installation and 4.0.0).
|
||||
- Similarly, review `UPDATING.md` to keep an eye out for all changes that have been explicitly marked as breaking changes.
|
||||
- Adjust your feature flags and configurations to meet your feature requirements and preferences.
|
||||
- Execute the migrations
|
||||
- If you have third-party apps interacting with Superset, check for relevant dependency updates or API endpoint changes that may affect compatibility.
|
||||
|
||||
Your mileage may vary depending on:
|
||||
|
||||
- How you install and deploy Superset (e.g. docker vs. pip vs. helm)
|
||||
- How you’ve configured Superset
|
||||
- What integrations, databases, etc. you're using
|
||||
|
||||
Reach out in `#deploying-superset` on Slack in case you find any problems, and if you find a reproducible bug, please file a new issue on GitHub.
|
||||
BIN
RELEASING/release-notes-4-0/media/alert-modal-1.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
RELEASING/release-notes-4-0/media/alert-modal-2.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
RELEASING/release-notes-4-0/media/alert-modal-3.png
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
RELEASING/release-notes-4-0/media/dashboard-dnd-1.png
Normal file
|
After Width: | Height: | Size: 611 KiB |
BIN
RELEASING/release-notes-4-0/media/dashboard-dnd-2.png
Normal file
|
After Width: | Height: | Size: 441 KiB |
BIN
RELEASING/release-notes-4-0/media/explore-dnd.png
Normal file
|
After Width: | Height: | Size: 626 KiB |
BIN
RELEASING/release-notes-4-0/media/sunburst.png
Normal file
|
After Width: | Height: | Size: 374 KiB |
BIN
RELEASING/release-notes-4-0/media/tags-1.png
Normal file
|
After Width: | Height: | Size: 267 KiB |
BIN
RELEASING/release-notes-4-0/media/tags-2.png
Normal file
|
After Width: | Height: | Size: 294 KiB |
@@ -39,7 +39,7 @@ PYTHON=$(get_python_command)
|
||||
PIP=$(get_pip_command)
|
||||
|
||||
# Get the release directory's path. If you unzip an Apache release and just run the npm script to validate the release, this will be a file name like `apache-superset-x.x.xrcx-source.tar.gz`
|
||||
RELEASE_DIR_NAME="../../$(basename "$(dirname "$(pwd)")").tar.gz"
|
||||
RELEASE_ZIP_PATH="../../$(basename "$(dirname "$(pwd)")")-source.tar.gz"
|
||||
|
||||
# Install dependencies from requirements.txt if the file exists
|
||||
if [ -f "path/to/requirements.txt" ]; then
|
||||
@@ -47,8 +47,5 @@ if [ -f "path/to/requirements.txt" ]; then
|
||||
$PYTHON -m $PIP install -r path/to/requirements.txt
|
||||
fi
|
||||
|
||||
# echo $PYTHON
|
||||
# echo $RELEASE_DIR_NAME
|
||||
|
||||
# Run the Python script with the parent directory name as an argument
|
||||
$PYTHON ../RELEASING/verify_release.py "$RELEASE_DIR_NAME"
|
||||
$PYTHON ../RELEASING/verify_release.py "$RELEASE_ZIP_PATH"
|
||||
|
||||
@@ -22,7 +22,7 @@ under the License.
|
||||
This file documents any backwards-incompatible changes in Superset and
|
||||
assists people when migrating to a new version.
|
||||
|
||||
## Next
|
||||
## 4.0.0
|
||||
|
||||
- [27119](https://github.com/apache/superset/pull/27119): Updates various database columns to use the `MediumText` type, potentially requiring a table lock on MySQL dbs or taking some time to complete on large deployments.
|
||||
|
||||
@@ -30,7 +30,7 @@ assists people when migrating to a new version.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- [27130](https://github.com/apache/superset/pull/27130): Fixes the DELETE `/database/{id}/ssh_tunnel/`` endpoint to now correctly accept a database ID as a parameter, rather than an SSH tunnel ID.
|
||||
- [27130](https://github.com/apache/superset/pull/27130): Fixes the DELETE `/database/{id}/ssh_tunnel/` endpoint to now correctly accept a database ID as a parameter, rather than an SSH tunnel ID.
|
||||
- [27117](https://github.com/apache/superset/pull/27117): Removes the following deprecated endpoints: `/superset/sqllab`, `/superset/sqllab/history`, `/sqllab/my_queries` use `/sqllab`, `/sqllab/history`, `/savedqueryview/list/?_flt_0_user={get_user_id()}` instead.
|
||||
- [26347](https://github.com/apache/superset/issues/26347): Removes the deprecated `VERSIONED_EXPORT` feature flag. The previous value of the feature flag was `True` and now the feature is permanently enabled.
|
||||
- [26328](https://github.com/apache/superset/issues/26328): Removes the deprecated Filter Box code and it's associated dependencies `react-select` and `array-move`. It also removes the `DeprecatedSelect` and `AsyncSelect` components that were exclusively used by filter boxes. Existing filter boxes will be automatically migrated to native filters.
|
||||
@@ -53,7 +53,7 @@ assists people when migrating to a new version.
|
||||
|
||||
### Potential Downtime
|
||||
|
||||
- [26416](https://github.com/apache/superset/pull/26416): adds 2 database indexes to report_execution_log and 1 to report_recipient to improve performance, this may cause downtime on large deployments.
|
||||
- [26416](https://github.com/apache/superset/pull/26416): Adds two database indexes to the `report_execution_log` table and one database index to the `report_recipient` to improve performance. Scheduled downtime may be required for large deployments.
|
||||
|
||||
## 3.1.0
|
||||
|
||||
|
||||
@@ -17,3 +17,6 @@
|
||||
# under the License.
|
||||
#
|
||||
-e file:.
|
||||
urllib3>=1.26.18
|
||||
werkzeug>=3.0.1
|
||||
numexpr>=2.9.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SHA1:a9dde048f1ee1f00586264d726d0e89f16e56183
|
||||
# SHA1:85649679306ea016e401f37adfbad832028d2e5f
|
||||
#
|
||||
# This file is autogenerated by pip-compile-multi
|
||||
# To update, run:
|
||||
@@ -78,7 +78,7 @@ cron-descriptor==1.2.24
|
||||
# via apache-superset
|
||||
croniter==1.0.15
|
||||
# via apache-superset
|
||||
cryptography==42.0.2
|
||||
cryptography==42.0.4
|
||||
# via
|
||||
# apache-superset
|
||||
# paramiko
|
||||
@@ -104,7 +104,7 @@ flask==2.2.5
|
||||
# flask-session
|
||||
# flask-sqlalchemy
|
||||
# flask-wtf
|
||||
flask-appbuilder==4.4.0
|
||||
flask-appbuilder==4.4.1
|
||||
# via apache-superset
|
||||
flask-babel==1.0.0
|
||||
# via flask-appbuilder
|
||||
@@ -143,7 +143,9 @@ geopy==2.2.0
|
||||
google-auth==2.27.0
|
||||
# via shillelagh
|
||||
greenlet==3.0.3
|
||||
# via shillelagh
|
||||
# via
|
||||
# shillelagh
|
||||
# sqlalchemy
|
||||
gunicorn==21.2.0
|
||||
# via apache-superset
|
||||
hashids==1.3.1
|
||||
@@ -208,8 +210,10 @@ nh3==0.2.11
|
||||
# via apache-superset
|
||||
numba==0.57.1
|
||||
# via pandas
|
||||
numexpr==2.8.4
|
||||
# via pandas
|
||||
numexpr==2.9.0
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# pandas
|
||||
numpy==1.23.5
|
||||
# via
|
||||
# apache-superset
|
||||
@@ -232,7 +236,9 @@ packaging==23.1
|
||||
pandas[performance]==2.0.3
|
||||
# via apache-superset
|
||||
paramiko==3.4.0
|
||||
# via sshtunnel
|
||||
# via
|
||||
# apache-superset
|
||||
# sshtunnel
|
||||
parsedatetime==2.6
|
||||
# via apache-superset
|
||||
pgsanity==0.2.9
|
||||
@@ -336,7 +342,7 @@ sqlalchemy-utils==0.38.3
|
||||
# via
|
||||
# apache-superset
|
||||
# flask-appbuilder
|
||||
sqlglot==20.8.0
|
||||
sqlglot==23.0.2
|
||||
# via apache-superset
|
||||
sqlparse==0.4.4
|
||||
# via apache-superset
|
||||
@@ -358,6 +364,7 @@ url-normalize==1.4.3
|
||||
# via requests-cache
|
||||
urllib3==1.26.18
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# requests
|
||||
# requests-cache
|
||||
# selenium
|
||||
@@ -370,6 +377,7 @@ wcwidth==0.2.5
|
||||
# via prompt-toolkit
|
||||
werkzeug==3.0.1
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# flask
|
||||
# flask-appbuilder
|
||||
# flask-jwt-extended
|
||||
|
||||
@@ -82,10 +82,6 @@ ptyprocess==0.7.0
|
||||
# via pexpect
|
||||
pure-eval==0.2.2
|
||||
# via stack-data
|
||||
pure-sasl==0.6.2
|
||||
# via
|
||||
# pyhive
|
||||
# thrift-sasl
|
||||
pydruid==0.6.5
|
||||
# via apache-superset
|
||||
pyhive[hive_pure_sasl]==0.7.0
|
||||
@@ -109,12 +105,7 @@ tableschema==1.20.2
|
||||
tabulator==1.53.5
|
||||
# via tableschema
|
||||
thrift==0.16.0
|
||||
# via
|
||||
# apache-superset
|
||||
# pyhive
|
||||
# thrift-sasl
|
||||
thrift-sasl==0.4.3
|
||||
# via pyhive
|
||||
# via apache-superset
|
||||
tomlkit==0.11.8
|
||||
# via pylint
|
||||
traitlets==5.9.0
|
||||
|
||||
@@ -15,6 +15,5 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
-r base.in
|
||||
-e .[postgres]
|
||||
gevent
|
||||
-e .[postgres,gevent]
|
||||
greenlet>=2.0.2
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SHA1:439e3ee196ce81f342c935117ba5e0eeee8c385b
|
||||
# SHA1:f00a57c70a52607d638c19f64f426f887382927e
|
||||
#
|
||||
# This file is autogenerated by pip-compile-multi
|
||||
# To update, run:
|
||||
@@ -11,7 +11,7 @@
|
||||
# -r requirements/base.in
|
||||
# -r requirements/docker.in
|
||||
gevent==23.9.1
|
||||
# via -r requirements/docker.in
|
||||
# via apache-superset
|
||||
psycopg2-binary==2.9.6
|
||||
# via apache-superset
|
||||
zope-event==4.5.0
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
docker
|
||||
flask-testing
|
||||
freezegun
|
||||
grpcio>=1.55.3
|
||||
openapi-spec-validator
|
||||
parameterized
|
||||
pyfakefs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# SHA1:95300275481abb1413eb98a5c79fb7cf96814cdd
|
||||
# SHA1:a37a1037f359c1101162ef43288178fbf00c487d
|
||||
#
|
||||
# This file is autogenerated by pip-compile-multi
|
||||
# To update, run:
|
||||
@@ -62,6 +62,7 @@ googleapis-common-protos==1.59.0
|
||||
# grpcio-status
|
||||
grpcio==1.60.1
|
||||
# via
|
||||
# -r requirements/testing.in
|
||||
# google-api-core
|
||||
# google-cloud-bigquery
|
||||
# grpcio-status
|
||||
@@ -132,7 +133,7 @@ tqdm==4.65.0
|
||||
# via
|
||||
# cmdstanpy
|
||||
# prophet
|
||||
trino==0.324.0
|
||||
trino==0.328.0
|
||||
# via apache-superset
|
||||
tzlocal==4.3
|
||||
# via trino
|
||||
|
||||
10
setup.py
@@ -80,11 +80,10 @@ setup(
|
||||
"colorama",
|
||||
"croniter>=0.3.28",
|
||||
"cron-descriptor",
|
||||
# snowflake-connector-python as of 3.7.0 doesn't support >=42.* therefore lowering the min to 41.0.2
|
||||
"cryptography>=41.0.2, <43.0.0",
|
||||
"cryptography>=42.0.4, <43.0.0",
|
||||
"deprecation>=2.1.0, <2.2.0",
|
||||
"flask>=2.2.5, <3.0.0",
|
||||
"flask-appbuilder>=4.4.0, <5.0.0",
|
||||
"flask-appbuilder>=4.4.1, <5.0.0",
|
||||
"flask-caching>=2.1.0, <3",
|
||||
"flask-compress>=1.13, <2.0",
|
||||
"flask-talisman>=1.0.0, <2.0",
|
||||
@@ -108,6 +107,7 @@ setup(
|
||||
"packaging",
|
||||
"pandas[performance]>=2.0.3, <2.1",
|
||||
"parsedatetime",
|
||||
"paramiko>=3.4.0",
|
||||
"pgsanity",
|
||||
"polyline>=2.0.0, <3.0",
|
||||
"pyparsing>=3.0.6, <4",
|
||||
@@ -126,7 +126,7 @@ setup(
|
||||
"slack_sdk>=3.19.0, <4",
|
||||
"sqlalchemy>=1.4, <2",
|
||||
"sqlalchemy-utils>=0.38.3, <0.39",
|
||||
"sqlglot>=20,<21",
|
||||
"sqlglot>=23.0.2,<24",
|
||||
"sqlparse>=0.4.4, <0.5",
|
||||
"tabulate>=0.8.9, <0.9",
|
||||
"typing-extensions>=4, <5",
|
||||
@@ -189,7 +189,7 @@ setup(
|
||||
"playwright": ["playwright>=1.37.0, <2"],
|
||||
"postgres": ["psycopg2-binary==2.9.6"],
|
||||
"presto": ["pyhive[presto]>=0.6.5"],
|
||||
"trino": ["trino>=0.324.0"],
|
||||
"trino": ["trino>=0.328.0"],
|
||||
"prophet": ["prophet>=1.1.5, <2"],
|
||||
"redshift": ["sqlalchemy-redshift>=0.8.1, <0.9"],
|
||||
"rockset": ["rockset-sqlalchemy>=0.0.1, <1"],
|
||||
|
||||
@@ -173,6 +173,13 @@ describe('Charts list', () => {
|
||||
orderAlphabetical();
|
||||
cy.getBySel('styled-card').first().contains('% Rural');
|
||||
});
|
||||
|
||||
it('should preserve other filters when sorting', () => {
|
||||
cy.getBySel('styled-card').should('have.length', 25);
|
||||
setFilter('Type', 'Big Number');
|
||||
setFilter('Sort', 'Least recently modified');
|
||||
cy.getBySel('styled-card').should('have.length', 3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('common actions', () => {
|
||||
|
||||
@@ -117,6 +117,13 @@ describe('Dashboards list', () => {
|
||||
orderAlphabetical();
|
||||
cy.getBySel('styled-card').first().contains('Supported Charts Dashboard');
|
||||
});
|
||||
|
||||
it('should preserve other filters when sorting', () => {
|
||||
cy.getBySel('styled-card').should('have.length', 5);
|
||||
setFilter('Status', 'Published');
|
||||
setFilter('Sort', 'Least recently modified');
|
||||
cy.getBySel('styled-card').should('have.length', 3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('common actions', () => {
|
||||
|
||||
@@ -210,7 +210,7 @@ describe('Time range filter', () => {
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.get('.ant-radio-group').children().its('length').should('eq', 5);
|
||||
cy.get('.ant-radio-checked + span').contains('last year');
|
||||
cy.get('.ant-radio-checked + span').contains('Last year');
|
||||
cy.get('[data-test=cancel-button]').click();
|
||||
});
|
||||
});
|
||||
|
||||
2682
superset-frontend/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "superset",
|
||||
"version": "0.0.0-dev",
|
||||
"version": "4.0.0",
|
||||
"description": "Superset is a data exploration platform designed to be visual, intuitive, and interactive.",
|
||||
"keywords": [
|
||||
"big",
|
||||
@@ -43,7 +43,6 @@
|
||||
"build-instrumented": "cross-env NODE_ENV=production BABEL_ENV=instrumented webpack --mode=production --color",
|
||||
"build-storybook": "storybook build",
|
||||
"check-translation": "prettier --check ../superset/translations/**/LC_MESSAGES/*.json",
|
||||
"chromatic": "npx chromatic --skip 'dependabot/**' --only-changed",
|
||||
"clean-translation": "prettier --write ../superset/translations/**/LC_MESSAGES/*.json",
|
||||
"core:cover": "cross-env NODE_ENV=test jest --coverage --coverageThreshold='{\"global\":{\"statements\":100,\"branches\":100,\"functions\":100,\"lines\":100}}' --collectCoverageFrom='[\"packages/**/src/**/*.{js,ts}\", \"!packages/superset-ui-demo/**/*\"]' packages",
|
||||
"cover": "cross-env NODE_ENV=test jest --coverage",
|
||||
@@ -57,7 +56,6 @@
|
||||
"plugins:build": "node ./scripts/build.js",
|
||||
"plugins:build-assets": "node ./scripts/copyAssets.js",
|
||||
"plugins:build-storybook": "cd packages/superset-ui-demo && npm run build-storybook",
|
||||
"plugins:chromatic": "cd packages/superset-ui-demo && npm run chromatic",
|
||||
"plugins:create-conventional-version": "npm run prune && lerna version --conventional-commits --create-release github --no-private --yes",
|
||||
"plugins:create-minor-version": "npm run prune && lerna version minor --no-private --yes",
|
||||
"plugins:create-patch-version": "npm run prune && lerna version patch --no-private --yes",
|
||||
@@ -268,6 +266,7 @@
|
||||
"@types/react-table": "^7.7.19",
|
||||
"@types/react-transition-group": "^4.4.10",
|
||||
"@types/react-ultimate-pagination": "^1.2.0",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.4",
|
||||
"@types/react-window": "^1.8.5",
|
||||
"@types/redux-localstorage": "^1.0.8",
|
||||
"@types/redux-mock-store": "^1.0.2",
|
||||
@@ -283,7 +282,6 @@
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
"babel-plugin-jsx-remove-data-test-id": "^2.1.3",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
"chromatic": "^6.7.4",
|
||||
"copy-webpack-plugin": "^12.0.2",
|
||||
"cross-env": "^5.2.1",
|
||||
"css-loader": "^6.8.1",
|
||||
|
||||
@@ -115,11 +115,11 @@ export default function makeApi<
|
||||
jsonPayload: undefined as JsonObject | undefined,
|
||||
};
|
||||
if (requestType === 'search') {
|
||||
requestConfig.searchParams = payload as URLSearchParams;
|
||||
requestConfig.searchParams = payload as unknown as URLSearchParams;
|
||||
} else if (requestType === 'rison') {
|
||||
requestConfig.endpoint = `${endpoint}?q=${rison.encode(payload)}`;
|
||||
} else if (requestType === 'form') {
|
||||
requestConfig.postPayload = payload as FormData;
|
||||
requestConfig.postPayload = payload as unknown as FormData;
|
||||
} else {
|
||||
requestConfig.jsonPayload = payload as JsonObject;
|
||||
}
|
||||
|
||||
@@ -44,15 +44,15 @@ interface MenuObjectChildProps {
|
||||
disable?: boolean;
|
||||
}
|
||||
|
||||
export interface SwitchProps {
|
||||
isEditMode: boolean;
|
||||
dbFetched: any;
|
||||
disableSSHTunnelingForEngine?: boolean;
|
||||
useSSHTunneling: boolean;
|
||||
setUseSSHTunneling: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setDB: React.Dispatch<any>;
|
||||
isSSHTunneling: boolean;
|
||||
}
|
||||
// loose typing to avoid any circular dependencies
|
||||
// refer to SSHTunnelSwitch component for strict typing
|
||||
type SwitchProps = {
|
||||
db: object;
|
||||
changeMethods: {
|
||||
onParametersChange: (event: any) => void;
|
||||
};
|
||||
clearValidationErrors: () => void;
|
||||
};
|
||||
|
||||
type ConfigDetailsProps = {
|
||||
embeddedId: string;
|
||||
|
||||
@@ -62,7 +62,6 @@
|
||||
"@babel/preset-typescript": "^7.23.3",
|
||||
"@storybook/react-webpack5": "^7.6.13",
|
||||
"babel-loader": "^8.1.0",
|
||||
"chromatic": "^5.4.0",
|
||||
"fork-ts-checker-webpack-plugin": "^5.0.7",
|
||||
"ts-loader": "^7.0.4",
|
||||
"typescript": "^4.5.4"
|
||||
|
||||
@@ -90,9 +90,6 @@ export default function createQueryStory({
|
||||
</div>
|
||||
);
|
||||
};
|
||||
story.parameters = {
|
||||
chromatic: { disable: true },
|
||||
};
|
||||
story.args = {
|
||||
host: 'localhost:8088',
|
||||
mode: keys[0],
|
||||
|
||||
@@ -40,7 +40,7 @@ export default {
|
||||
],
|
||||
};
|
||||
|
||||
export const configureCORS = ({
|
||||
export const ConfigureCORS = ({
|
||||
host,
|
||||
selectEndpoint,
|
||||
customEndpoint,
|
||||
@@ -84,18 +84,14 @@ export const configureCORS = ({
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
configureCORS.parameters = {
|
||||
chromatic: { disable: true },
|
||||
};
|
||||
configureCORS.args = {
|
||||
ConfigureCORS.args = {
|
||||
host: 'localhost:8088',
|
||||
selectEndpoint: '/api/v1/chart/data',
|
||||
customEndpoint: '',
|
||||
methodOption: 'POST', // TODO disable when custonEndpoint and selectEndpoint are empty
|
||||
postPayloadContents: JSON.stringify({ form_data: bigNumberFormData }),
|
||||
};
|
||||
configureCORS.argTypes = {
|
||||
ConfigureCORS.argTypes = {
|
||||
host: {
|
||||
control: 'text',
|
||||
description: 'Set Superset App host for CORS request',
|
||||
@@ -122,4 +118,4 @@ configureCORS.argTypes = {
|
||||
description: 'Set POST payload contents',
|
||||
},
|
||||
};
|
||||
configureCORS.storyName = 'Verify CORS';
|
||||
ConfigureCORS.storyName = 'Verify CORS';
|
||||
|
||||
@@ -177,15 +177,12 @@ function Heatmap(element, props) {
|
||||
}
|
||||
}
|
||||
|
||||
function ordScale(k, rangeBands, sortMethod) {
|
||||
function ordScale(k, rangeBands, sortMethod, formatter) {
|
||||
let domain = {};
|
||||
const actualKeys = {}; // hack to preserve type of keys when number
|
||||
records.forEach(d => {
|
||||
domain[d[k]] = (domain[d[k]] || 0) + d.v;
|
||||
actualKeys[d[k]] = d[k];
|
||||
});
|
||||
// Not using object.keys() as it converts to strings
|
||||
const keys = Object.keys(actualKeys).map(s => actualKeys[s]);
|
||||
const keys = Object.keys(domain).map(k => formatter(k));
|
||||
if (sortMethod === 'alpha_asc') {
|
||||
domain = keys.sort(cmp);
|
||||
} else if (sortMethod === 'alpha_desc') {
|
||||
@@ -252,10 +249,10 @@ function Heatmap(element, props) {
|
||||
|
||||
const fp = getNumberFormatter(NumberFormats.PERCENT_2_POINT);
|
||||
|
||||
const xScale = ordScale('x', null, sortXAxis);
|
||||
const yScale = ordScale('y', null, sortYAxis);
|
||||
const xRbScale = ordScale('x', [0, hmWidth], sortXAxis);
|
||||
const yRbScale = ordScale('y', [hmHeight, 0], sortYAxis);
|
||||
const xScale = ordScale('x', null, sortXAxis, xAxisFormatter);
|
||||
const yScale = ordScale('y', null, sortYAxis, yAxisFormatter);
|
||||
const xRbScale = ordScale('x', [0, hmWidth], sortXAxis, xAxisFormatter);
|
||||
const yRbScale = ordScale('y', [hmHeight, 0], sortYAxis, yAxisFormatter);
|
||||
const X = 0;
|
||||
const Y = 1;
|
||||
const heatmapDim = [xRbScale.domain().length, yRbScale.domain().length];
|
||||
|
||||
@@ -57,11 +57,15 @@ export default function transformProps(chartProps) {
|
||||
const xAxisFormatter =
|
||||
coltypes[0] === GenericDataType.Temporal
|
||||
? getTimeFormatter(timeFormat)
|
||||
: String;
|
||||
: coltypes[0] === GenericDataType.Numeric
|
||||
? Number
|
||||
: String;
|
||||
const yAxisFormatter =
|
||||
coltypes[1] === GenericDataType.Temporal
|
||||
? getTimeFormatter(timeFormat)
|
||||
: String;
|
||||
: coltypes[1] === GenericDataType.Numeric
|
||||
? Number
|
||||
: String;
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
|
||||
@@ -48,11 +48,12 @@ import { getDefaultTooltip } from '../utils/tooltip';
|
||||
import { Refs } from '../types';
|
||||
import { getColtypesMapping } from '../utils/series';
|
||||
|
||||
const setIntervalBoundsAndColors = (
|
||||
export const getIntervalBoundsAndColors = (
|
||||
intervals: string,
|
||||
intervalColorIndices: string,
|
||||
colorFn: CategoricalColorScale,
|
||||
normalizer: number,
|
||||
min: number,
|
||||
max: number,
|
||||
): Array<[number, string]> => {
|
||||
let intervalBoundsNonNormalized;
|
||||
let intervalColorIndicesArray;
|
||||
@@ -65,7 +66,7 @@ const setIntervalBoundsAndColors = (
|
||||
}
|
||||
|
||||
const intervalBounds = intervalBoundsNonNormalized.map(
|
||||
bound => bound / normalizer,
|
||||
bound => (bound - min) / (max - min),
|
||||
);
|
||||
const intervalColors = intervalColorIndicesArray.map(
|
||||
ind => colorFn.colors[(ind - 1) % colorFn.colors.length],
|
||||
@@ -221,12 +222,12 @@ export default function transformProps(
|
||||
const axisLabelLength = Math.max(
|
||||
...axisLabels.map(label => numberFormatter(label).length).concat([1]),
|
||||
);
|
||||
const normalizer = max;
|
||||
const intervalBoundsAndColors = setIntervalBoundsAndColors(
|
||||
const intervalBoundsAndColors = getIntervalBoundsAndColors(
|
||||
intervals,
|
||||
intervalColorIndices,
|
||||
colorFn,
|
||||
normalizer,
|
||||
min,
|
||||
max,
|
||||
);
|
||||
const splitLineDistance =
|
||||
axisLineWidth + splitLineLength + OFFSETS.ticksFromLine;
|
||||
|
||||
@@ -410,8 +410,9 @@ export default function transformProps(
|
||||
|
||||
rawSeriesB.forEach(entry => {
|
||||
const entryName = String(entry.name || '');
|
||||
const seriesName = `${inverted[entryName] || entryName} (1)`;
|
||||
const colorScaleKey = getOriginalSeries(seriesName, array);
|
||||
const seriesEntry = inverted[entryName] || entryName;
|
||||
const seriesName = `${seriesEntry} (1)`;
|
||||
const colorScaleKey = getOriginalSeries(seriesEntry, array);
|
||||
|
||||
const seriesFormatter = getFormatter(
|
||||
customFormattersSecondary,
|
||||
|
||||
@@ -575,7 +575,6 @@ export default function transformProps(
|
||||
right: TIMESERIES_CONSTANTS.toolboxRight,
|
||||
feature: {
|
||||
dataZoom: {
|
||||
yAxisIndex: false,
|
||||
title: {
|
||||
zoom: t('zoom area'),
|
||||
back: t('restore zoom'),
|
||||
@@ -590,6 +589,7 @@ export default function transformProps(
|
||||
start: TIMESERIES_CONSTANTS.dataZoomStart,
|
||||
end: TIMESERIES_CONSTANTS.dataZoomEnd,
|
||||
bottom: TIMESERIES_CONSTANTS.zoomBottom,
|
||||
yAxisIndex: isHorizontal ? 0 : undefined,
|
||||
},
|
||||
]
|
||||
: [],
|
||||
|
||||
@@ -570,9 +570,10 @@ export function getPadding(
|
||||
yAxisTitlePosition && yAxisTitlePosition === 'Top'
|
||||
? TIMESERIES_CONSTANTS.gridOffsetTop + (Number(yAxisTitleMargin) || 0)
|
||||
: TIMESERIES_CONSTANTS.gridOffsetTop + yAxisOffset,
|
||||
bottom: zoomable
|
||||
? TIMESERIES_CONSTANTS.gridOffsetBottomZoomable + xAxisOffset
|
||||
: TIMESERIES_CONSTANTS.gridOffsetBottom + xAxisOffset,
|
||||
bottom:
|
||||
zoomable && !isHorizontal
|
||||
? TIMESERIES_CONSTANTS.gridOffsetBottomZoomable + xAxisOffset
|
||||
: TIMESERIES_CONSTANTS.gridOffsetBottom + xAxisOffset,
|
||||
left:
|
||||
yAxisTitlePosition === 'Left'
|
||||
? TIMESERIES_CONSTANTS.gridOffsetLeft +
|
||||
|
||||
@@ -16,8 +16,15 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { ChartProps, SqlaFormData, supersetTheme } from '@superset-ui/core';
|
||||
import transformProps from '../../src/Gauge/transformProps';
|
||||
import {
|
||||
CategoricalColorNamespace,
|
||||
ChartProps,
|
||||
SqlaFormData,
|
||||
supersetTheme,
|
||||
} from '@superset-ui/core';
|
||||
import transformProps, {
|
||||
getIntervalBoundsAndColors,
|
||||
} from '../../src/Gauge/transformProps';
|
||||
import { EchartsGaugeChartProps } from '../../src/Gauge/types';
|
||||
|
||||
describe('Echarts Gauge transformProps', () => {
|
||||
@@ -256,8 +263,9 @@ describe('Echarts Gauge transformProps', () => {
|
||||
const formData: SqlaFormData = {
|
||||
...baseFormData,
|
||||
groupby: ['year', 'platform'],
|
||||
intervals: '50,100',
|
||||
intervals: '60,100',
|
||||
intervalColorIndices: '1,2',
|
||||
minVal: 20,
|
||||
};
|
||||
const queriesData = [
|
||||
{
|
||||
@@ -342,3 +350,43 @@ describe('Echarts Gauge transformProps', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getIntervalBoundsAndColors', () => {
|
||||
it('should generate correct interval bounds and colors', () => {
|
||||
const colorFn = CategoricalColorNamespace.getScale(
|
||||
'supersetColors' as string,
|
||||
);
|
||||
expect(getIntervalBoundsAndColors('', '', colorFn, 0, 10)).toEqual([]);
|
||||
expect(getIntervalBoundsAndColors('4, 10', '1, 2', colorFn, 0, 10)).toEqual(
|
||||
[
|
||||
[0.4, '#1f77b4'],
|
||||
[1, '#ff7f0e'],
|
||||
],
|
||||
);
|
||||
expect(
|
||||
getIntervalBoundsAndColors('4, 8, 10', '9, 8, 7', colorFn, 0, 10),
|
||||
).toEqual([
|
||||
[0.4, '#bcbd22'],
|
||||
[0.8, '#7f7f7f'],
|
||||
[1, '#e377c2'],
|
||||
]);
|
||||
expect(getIntervalBoundsAndColors('4, 10', '1, 2', colorFn, 2, 10)).toEqual(
|
||||
[
|
||||
[0.25, '#1f77b4'],
|
||||
[1, '#ff7f0e'],
|
||||
],
|
||||
);
|
||||
expect(
|
||||
getIntervalBoundsAndColors('-4, 0', '1, 2', colorFn, -10, 0),
|
||||
).toEqual([
|
||||
[0.6, '#1f77b4'],
|
||||
[1, '#ff7f0e'],
|
||||
]);
|
||||
expect(
|
||||
getIntervalBoundsAndColors('-4, -2', '1, 2', colorFn, -10, -2),
|
||||
).toEqual([
|
||||
[0.75, '#1f77b4'],
|
||||
[1, '#ff7f0e'],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -743,15 +743,18 @@ export function removeQueryEditor(queryEditor) {
|
||||
|
||||
return sync
|
||||
.then(() => dispatch({ type: REMOVE_QUERY_EDITOR, queryEditor }))
|
||||
.catch(() =>
|
||||
dispatch(
|
||||
addDangerToast(
|
||||
t(
|
||||
'An error occurred while removing tab. Please contact your administrator.',
|
||||
.catch(({ status }) => {
|
||||
if (status !== 404) {
|
||||
return dispatch(
|
||||
addDangerToast(
|
||||
t(
|
||||
'An error occurred while removing tab. Please contact your administrator.',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
return dispatch({ type: REMOVE_QUERY_EDITOR, queryEditor });
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1127,9 +1130,11 @@ export function removeTables(tables) {
|
||||
const sync = isFeatureEnabled(FeatureFlag.SqllabBackendPersistence)
|
||||
? Promise.all(
|
||||
tablesToRemove.map(table =>
|
||||
SupersetClient.delete({
|
||||
endpoint: encodeURI(`/tableschemaview/${table.id}`),
|
||||
}),
|
||||
table.initialized
|
||||
? SupersetClient.delete({
|
||||
endpoint: encodeURI(`/tableschemaview/${table.id}`),
|
||||
})
|
||||
: Promise.resolve(),
|
||||
),
|
||||
)
|
||||
: Promise.resolve();
|
||||
|
||||
@@ -883,7 +883,7 @@ describe('async actions', () => {
|
||||
it('updates the table schema state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const table = { id: 1 };
|
||||
const table = { id: 1, initialized: true };
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
@@ -900,7 +900,10 @@ describe('async actions', () => {
|
||||
it('deletes multiple tables and updates the table schema state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const tables = [{ id: 1 }, { id: 2 }];
|
||||
const tables = [
|
||||
{ id: 1, initialized: true },
|
||||
{ id: 2, initialized: true },
|
||||
];
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
@@ -913,6 +916,23 @@ describe('async actions', () => {
|
||||
expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('only updates the initialized table schema state in the backend', () => {
|
||||
expect.assertions(2);
|
||||
|
||||
const tables = [{ id: 1 }, { id: 2, initialized: true }];
|
||||
const store = mockStore({});
|
||||
const expectedActions = [
|
||||
{
|
||||
type: actions.REMOVE_TABLES,
|
||||
tables,
|
||||
},
|
||||
];
|
||||
return store.dispatch(actions.removeTables(tables)).then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
expect(fetchMock.calls(updateTableSchemaEndpoint)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('migrateQueryEditorFromLocalStorage', () => {
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render, screen } from 'spec/helpers/testing-library';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import * as uiCore from '@superset-ui/core';
|
||||
import { FeatureFlag, QueryState } from '@superset-ui/core';
|
||||
import { render, screen, waitFor } from 'spec/helpers/testing-library';
|
||||
import QueryHistory from 'src/SqlLab/components/QueryHistory';
|
||||
import { initialState } from 'src/SqlLab/fixtures';
|
||||
|
||||
@@ -27,18 +30,72 @@ const mockedProps = {
|
||||
latestQueryId: 'yhMUZCGb',
|
||||
};
|
||||
|
||||
const fakeApiResult = {
|
||||
count: 4,
|
||||
ids: [692],
|
||||
result: [
|
||||
{
|
||||
changed_on: '2024-03-12T20:01:02.497775',
|
||||
client_id: 'b0ZDzRYzn',
|
||||
database: {
|
||||
database_name: 'examples',
|
||||
id: 1,
|
||||
},
|
||||
end_time: '1710273662496.047852',
|
||||
error_message: null,
|
||||
executed_sql: 'SELECT * from "FCC 2018 Survey"\nLIMIT 1001',
|
||||
id: 692,
|
||||
limit: 1000,
|
||||
limiting_factor: 'DROPDOWN',
|
||||
progress: 100,
|
||||
results_key: null,
|
||||
rows: 443,
|
||||
schema: 'main',
|
||||
select_as_cta: false,
|
||||
sql: 'SELECT * from "FCC 2018 Survey" ',
|
||||
sql_editor_id: '22',
|
||||
start_time: '1710273662445.992920',
|
||||
status: QueryState.Success,
|
||||
tab_name: 'Untitled Query 16',
|
||||
tmp_table_name: null,
|
||||
tracking_url: null,
|
||||
user: {
|
||||
first_name: 'admin',
|
||||
id: 1,
|
||||
last_name: 'user',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const setup = (overrides = {}) => (
|
||||
<QueryHistory {...mockedProps} {...overrides} />
|
||||
);
|
||||
|
||||
describe('QueryHistory', () => {
|
||||
it('Renders an empty state for query history', () => {
|
||||
render(setup(), { useRedux: true, initialState });
|
||||
test('Renders an empty state for query history', () => {
|
||||
render(setup(), { useRedux: true, initialState });
|
||||
|
||||
const emptyStateText = screen.getByText(
|
||||
/run a query to display query history/i,
|
||||
const emptyStateText = screen.getByText(
|
||||
/run a query to display query history/i,
|
||||
);
|
||||
|
||||
expect(emptyStateText).toBeVisible();
|
||||
});
|
||||
|
||||
test('fetches the query history when the persistence mode is enabled', async () => {
|
||||
const isFeatureEnabledMock = jest
|
||||
.spyOn(uiCore, 'isFeatureEnabled')
|
||||
.mockImplementation(
|
||||
featureFlag => featureFlag === FeatureFlag.SqllabBackendPersistence,
|
||||
);
|
||||
|
||||
expect(emptyStateText).toBeVisible();
|
||||
});
|
||||
const editorQueryApiRoute = `glob:*/api/v1/query/?q=*`;
|
||||
fetchMock.get(editorQueryApiRoute, fakeApiResult);
|
||||
render(setup(), { useRedux: true, initialState });
|
||||
await waitFor(() =>
|
||||
expect(fetchMock.calls(editorQueryApiRoute).length).toBe(1),
|
||||
);
|
||||
const queryResultText = screen.getByText(fakeApiResult.result[0].rows);
|
||||
expect(queryResultText).toBeInTheDocument();
|
||||
isFeatureEnabledMock.mockClear();
|
||||
});
|
||||
|
||||
@@ -16,12 +16,23 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React, { useMemo } from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { shallowEqual, useSelector } from 'react-redux';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import { omit } from 'lodash';
|
||||
import { EmptyStateMedium } from 'src/components/EmptyState';
|
||||
import { t, styled } from '@superset-ui/core';
|
||||
import {
|
||||
t,
|
||||
styled,
|
||||
css,
|
||||
FeatureFlag,
|
||||
isFeatureEnabled,
|
||||
} from '@superset-ui/core';
|
||||
import QueryTable from 'src/SqlLab/components/QueryTable';
|
||||
import { SqlLabRootState } from 'src/SqlLab/types';
|
||||
import { useEditorQueriesQuery } from 'src/hooks/apiResources/queries';
|
||||
import { Skeleton } from 'src/components';
|
||||
import useEffectEvent from 'src/hooks/useEffectEvent';
|
||||
|
||||
interface QueryHistoryProps {
|
||||
queryEditorId: string | number;
|
||||
@@ -40,39 +51,92 @@ const StyledEmptyStateWrapper = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const getEditorQueries = (
|
||||
queries: SqlLabRootState['sqlLab']['queries'],
|
||||
queryEditorId: string | number,
|
||||
) =>
|
||||
Object.values(queries).filter(
|
||||
({ sqlEditorId }) => String(sqlEditorId) === String(queryEditorId),
|
||||
);
|
||||
|
||||
const QueryHistory = ({
|
||||
queryEditorId,
|
||||
displayLimit,
|
||||
latestQueryId,
|
||||
}: QueryHistoryProps) => {
|
||||
const [ref, hasReachedBottom] = useInView({ threshold: 0 });
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const queries = useSelector(
|
||||
({ sqlLab: { queries } }: SqlLabRootState) => queries,
|
||||
shallowEqual,
|
||||
);
|
||||
const { data, isLoading, isFetching } = useEditorQueriesQuery(
|
||||
{ editorId: `${queryEditorId}`, pageIndex },
|
||||
{
|
||||
skip: !isFeatureEnabled(FeatureFlag.SqllabBackendPersistence),
|
||||
},
|
||||
);
|
||||
const editorQueries = useMemo(
|
||||
() =>
|
||||
Object.values(queries).filter(
|
||||
({ sqlEditorId }) => String(sqlEditorId) === String(queryEditorId),
|
||||
),
|
||||
[queries, queryEditorId],
|
||||
data
|
||||
? getEditorQueries(
|
||||
omit(
|
||||
queries,
|
||||
data.result.map(({ id }) => id),
|
||||
),
|
||||
queryEditorId,
|
||||
)
|
||||
.concat(data.result)
|
||||
.reverse()
|
||||
: getEditorQueries(queries, queryEditorId),
|
||||
[queries, data, queryEditorId],
|
||||
);
|
||||
|
||||
const loadNext = useEffectEvent(() => {
|
||||
setPageIndex(pageIndex + 1);
|
||||
});
|
||||
|
||||
const loadedDataCount = data?.result.length || 0;
|
||||
const totalCount = data?.count || 0;
|
||||
|
||||
useEffect(() => {
|
||||
if (hasReachedBottom && loadedDataCount < totalCount) {
|
||||
loadNext();
|
||||
}
|
||||
}, [hasReachedBottom, loadNext, loadedDataCount, totalCount]);
|
||||
|
||||
if (!editorQueries.length && isLoading) {
|
||||
return <Skeleton active />;
|
||||
}
|
||||
|
||||
return editorQueries.length > 0 ? (
|
||||
<QueryTable
|
||||
columns={[
|
||||
'state',
|
||||
'started',
|
||||
'duration',
|
||||
'progress',
|
||||
'rows',
|
||||
'sql',
|
||||
'results',
|
||||
'actions',
|
||||
]}
|
||||
queries={editorQueries}
|
||||
displayLimit={displayLimit}
|
||||
latestQueryId={latestQueryId}
|
||||
/>
|
||||
<>
|
||||
<QueryTable
|
||||
columns={[
|
||||
'state',
|
||||
'started',
|
||||
'duration',
|
||||
'progress',
|
||||
'rows',
|
||||
'sql',
|
||||
'results',
|
||||
'actions',
|
||||
]}
|
||||
queries={editorQueries}
|
||||
displayLimit={displayLimit}
|
||||
latestQueryId={latestQueryId}
|
||||
/>
|
||||
{data && loadedDataCount < totalCount && (
|
||||
<div
|
||||
ref={ref}
|
||||
css={css`
|
||||
position: relative;
|
||||
top: -150px;
|
||||
`}
|
||||
/>
|
||||
)}
|
||||
{isFetching && <Skeleton active />}
|
||||
</>
|
||||
) : (
|
||||
<StyledEmptyStateWrapper>
|
||||
<EmptyStateMedium
|
||||
|
||||
@@ -29,7 +29,7 @@ import { LOCALSTORAGE_MAX_QUERY_AGE_MS } from '../../constants';
|
||||
const EXTRA_HEIGHT_RESULTS = 8; // we need extra height in RESULTS tab. because the height from props was calculated based on PREVIEW tab.
|
||||
|
||||
type Props = {
|
||||
latestQueryId: string;
|
||||
latestQueryId?: string;
|
||||
height: number;
|
||||
displayLimit: number;
|
||||
defaultQueryLimit: number;
|
||||
|
||||
@@ -123,6 +123,19 @@ test('should render offline when the state is offline', async () => {
|
||||
expect(getByText(STATUS_OPTIONS.offline)).toBeVisible();
|
||||
});
|
||||
|
||||
test('should render empty result state when latestQuery is empty', () => {
|
||||
const { getAllByRole } = render(
|
||||
<SouthPane {...mockedProps} latestQueryId={undefined} />,
|
||||
{
|
||||
useRedux: true,
|
||||
initialState: mockState,
|
||||
},
|
||||
);
|
||||
|
||||
const resultPanel = getAllByRole('tabpanel')[0];
|
||||
expect(resultPanel).toHaveTextContent('Run a query to display results');
|
||||
});
|
||||
|
||||
test('should render tabs for table preview queries', () => {
|
||||
const { getAllByRole } = render(<SouthPane {...mockedProps} />, {
|
||||
useRedux: true,
|
||||
|
||||
@@ -144,14 +144,12 @@ const SouthPane = ({
|
||||
animated={false}
|
||||
>
|
||||
<Tabs.TabPane tab={t('Results')} key="Results">
|
||||
{latestQueryId && (
|
||||
<Results
|
||||
height={innerTabContentHeight}
|
||||
latestQueryId={latestQueryId}
|
||||
displayLimit={displayLimit}
|
||||
defaultQueryLimit={defaultQueryLimit}
|
||||
/>
|
||||
)}
|
||||
<Results
|
||||
height={innerTabContentHeight}
|
||||
latestQueryId={latestQueryId}
|
||||
displayLimit={displayLimit}
|
||||
defaultQueryLimit={defaultQueryLimit}
|
||||
/>
|
||||
</Tabs.TabPane>
|
||||
<Tabs.TabPane tab={t('Query history')} key="History">
|
||||
<QueryHistory
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import * as uiCore from '@superset-ui/core';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { fireEvent, render, waitFor } from 'spec/helpers/testing-library';
|
||||
import fetchMock from 'fetch-mock';
|
||||
@@ -31,7 +32,7 @@ import {
|
||||
import SqlEditorLeftBar from 'src/SqlLab/components/SqlEditorLeftBar';
|
||||
import ResultSet from 'src/SqlLab/components/ResultSet';
|
||||
import { api } from 'src/hooks/apiResources/queryApi';
|
||||
import { getExtensionsRegistry } from '@superset-ui/core';
|
||||
import { getExtensionsRegistry, FeatureFlag } from '@superset-ui/core';
|
||||
import setupExtensions from 'src/setup/setupExtensions';
|
||||
import type { Action, Middleware, Store } from 'redux';
|
||||
import SqlEditor, { Props } from '.';
|
||||
@@ -63,6 +64,7 @@ fetchMock.get('glob:*/api/v1/database/*/function_names/', {
|
||||
});
|
||||
fetchMock.get('glob:*/api/v1/database/*', { result: [] });
|
||||
fetchMock.get('glob:*/api/v1/database/*/tables/*', { options: [] });
|
||||
fetchMock.get('glob:*/tabstateview/*', defaultQueryEditor);
|
||||
fetchMock.post('glob:*/sqllab/execute/*', { result: [] });
|
||||
|
||||
let store: Store;
|
||||
@@ -291,4 +293,43 @@ describe('SqlEditor', () => {
|
||||
await findByText('sqleditor.extension.form extension component'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('with SqllabBackendPersistence enabled', () => {
|
||||
let isFeatureEnabledMock: jest.MockInstance<
|
||||
boolean,
|
||||
[feature: FeatureFlag]
|
||||
>;
|
||||
beforeEach(() => {
|
||||
isFeatureEnabledMock = jest
|
||||
.spyOn(uiCore, 'isFeatureEnabled')
|
||||
.mockImplementation(
|
||||
featureFlag =>
|
||||
featureFlag === uiCore.FeatureFlag.SqllabBackendPersistence,
|
||||
);
|
||||
});
|
||||
afterEach(() => {
|
||||
isFeatureEnabledMock.mockClear();
|
||||
});
|
||||
|
||||
it('should render loading state when its Editor is not loaded', async () => {
|
||||
const switchTabApi = `glob:*/tabstateview/${defaultQueryEditor.id}/activate`;
|
||||
fetchMock.post(switchTabApi, {});
|
||||
const { getByTestId } = setup(
|
||||
{
|
||||
...mockedProps,
|
||||
queryEditor: {
|
||||
...mockedProps.queryEditor,
|
||||
loaded: false,
|
||||
},
|
||||
},
|
||||
store,
|
||||
);
|
||||
const indicator = getByTestId('sqlEditor-loading');
|
||||
expect(indicator).toBeInTheDocument();
|
||||
await waitFor(() =>
|
||||
expect(fetchMock.calls('glob:*/tabstateview/*').length).toBe(1),
|
||||
);
|
||||
expect(fetchMock.calls(switchTabApi).length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -54,7 +54,7 @@ import Mousetrap from 'mousetrap';
|
||||
import Button from 'src/components/Button';
|
||||
import Timer from 'src/components/Timer';
|
||||
import ResizableSidebar from 'src/components/ResizableSidebar';
|
||||
import { AntdDropdown, AntdSwitch } from 'src/components';
|
||||
import { AntdDropdown, AntdSwitch, Skeleton } from 'src/components';
|
||||
import { Input } from 'src/components/Input';
|
||||
import { Menu } from 'src/components/Menu';
|
||||
import Icons from 'src/components/Icons';
|
||||
@@ -77,6 +77,7 @@ import {
|
||||
setActiveSouthPaneTab,
|
||||
updateSavedQuery,
|
||||
formatQuery,
|
||||
switchQueryEditor,
|
||||
} from 'src/SqlLab/actions/sqlLab';
|
||||
import {
|
||||
STATE_TYPE_MAP,
|
||||
@@ -494,6 +495,16 @@ const SqlEditor: React.FC<Props> = ({
|
||||
}
|
||||
});
|
||||
|
||||
const shouldLoadQueryEditor =
|
||||
isFeatureEnabled(FeatureFlag.SqllabBackendPersistence) &&
|
||||
!queryEditor.loaded;
|
||||
|
||||
const loadQueryEditor = useEffectEvent(() => {
|
||||
if (shouldLoadQueryEditor) {
|
||||
dispatch(switchQueryEditor(queryEditor, displayLimit));
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// We need to measure the height of the sql editor post render to figure the height of
|
||||
// the south pane so it gets rendered properly
|
||||
@@ -503,6 +514,7 @@ const SqlEditor: React.FC<Props> = ({
|
||||
WINDOW_RESIZE_THROTTLE_MS,
|
||||
);
|
||||
if (isActive) {
|
||||
loadQueryEditor();
|
||||
window.addEventListener('resize', handleWindowResizeWithThrottle);
|
||||
window.addEventListener('beforeunload', onBeforeUnload);
|
||||
}
|
||||
@@ -512,7 +524,7 @@ const SqlEditor: React.FC<Props> = ({
|
||||
window.removeEventListener('beforeunload', onBeforeUnload);
|
||||
};
|
||||
// TODO: Remove useEffectEvent deps once https://github.com/facebook/react/pull/25881 is released
|
||||
}, [onBeforeUnload, isActive]);
|
||||
}, [onBeforeUnload, loadQueryEditor, isActive]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!database || isEmpty(database)) {
|
||||
@@ -847,7 +859,17 @@ const SqlEditor: React.FC<Props> = ({
|
||||
)}
|
||||
</ResizableSidebar>
|
||||
</CSSTransition>
|
||||
{showEmptyState ? (
|
||||
{shouldLoadQueryEditor ? (
|
||||
<div
|
||||
data-test="sqlEditor-loading"
|
||||
css={css`
|
||||
flex: 1;
|
||||
padding: ${theme.gridUnit * 4}px;
|
||||
`}
|
||||
>
|
||||
<Skeleton active />
|
||||
</div>
|
||||
) : showEmptyState ? (
|
||||
<EmptyStateBig
|
||||
image="vector.svg"
|
||||
title={t('Select a database to write a query')}
|
||||
|
||||
@@ -25,7 +25,6 @@ const apiData = {
|
||||
common: DEFAULT_COMMON_BOOTSTRAP_DATA,
|
||||
tab_state_ids: [],
|
||||
databases: [],
|
||||
queries: {},
|
||||
user: {
|
||||
userId: 1,
|
||||
username: 'some name',
|
||||
@@ -220,18 +219,20 @@ describe('getInitialState', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
const latestQuery = {
|
||||
...runningQuery,
|
||||
id: 'latestPersisted',
|
||||
startDttm: Number(startDttmInStr),
|
||||
endDttm: Number(endDttmInStr),
|
||||
};
|
||||
const initializedQueries = getInitialState({
|
||||
...apiData,
|
||||
queries: {
|
||||
backendPersisted: {
|
||||
...runningQuery,
|
||||
id: 'backendPersisted',
|
||||
startDttm: startDttmInStr,
|
||||
endDttm: endDttmInStr,
|
||||
},
|
||||
...apiDataWithTabState,
|
||||
active_tab: {
|
||||
...apiDataWithTabState.active_tab,
|
||||
latest_query: latestQuery,
|
||||
},
|
||||
}).sqlLab.queries;
|
||||
expect(initializedQueries.backendPersisted).toEqual(
|
||||
expect(initializedQueries.latestPersisted).toEqual(
|
||||
expect.objectContaining({
|
||||
startDttm: Number(startDttmInStr),
|
||||
endDttm: Number(endDttmInStr),
|
||||
|
||||
@@ -57,7 +57,7 @@ export default function getInitialState({
|
||||
version: LatestQueryEditorVersion,
|
||||
loaded: true,
|
||||
name: t('Untitled query'),
|
||||
sql: 'SELECT *\nFROM\nWHERE',
|
||||
sql: '',
|
||||
latestQueryId: null,
|
||||
autorun: false,
|
||||
dbId: common.conf.SQLLAB_DEFAULT_DBID,
|
||||
@@ -136,7 +136,12 @@ export default function getInitialState({
|
||||
});
|
||||
}
|
||||
|
||||
const queries = { ...queries_ };
|
||||
const queries = {
|
||||
...queries_,
|
||||
...(activeTab?.latest_query && {
|
||||
[activeTab.latest_query.id]: activeTab.latest_query,
|
||||
}),
|
||||
};
|
||||
|
||||
/**
|
||||
* If the `SQLLAB_BACKEND_PERSISTENCE` feature flag is off, or if the user
|
||||
|
||||
@@ -152,7 +152,10 @@ export default function sqlLabReducer(state = {}, action) {
|
||||
|
||||
newState = {
|
||||
...newState,
|
||||
tabHistory,
|
||||
tabHistory:
|
||||
tabHistory.length === 0 && newState.queryEditors.length > 0
|
||||
? newState.queryEditors.slice(-1).map(qe => qe.id)
|
||||
: tabHistory,
|
||||
tables,
|
||||
queries,
|
||||
unsavedQueryEditor: {
|
||||
|
||||
@@ -75,6 +75,25 @@ describe('sqlLabReducer', () => {
|
||||
initialState.queryEditors.length,
|
||||
);
|
||||
});
|
||||
it('should select the latest query editor when tabHistory is empty', () => {
|
||||
const currentQE = newState.queryEditors[0];
|
||||
newState = {
|
||||
...initialState,
|
||||
tabHistory: [initialState.queryEditors[0]],
|
||||
};
|
||||
const action = {
|
||||
type: actions.REMOVE_QUERY_EDITOR,
|
||||
queryEditor: currentQE,
|
||||
};
|
||||
newState = sqlLabReducer(newState, action);
|
||||
expect(newState.queryEditors).toHaveLength(
|
||||
initialState.queryEditors.length - 1,
|
||||
);
|
||||
expect(newState.queryEditors.map(qe => qe.id)).not.toContainEqual(
|
||||
currentQE.id,
|
||||
);
|
||||
expect(newState.tabHistory).toEqual([initialState.queryEditors[2].id]);
|
||||
});
|
||||
it('should remove a query editor including unsaved changes', () => {
|
||||
expect(newState.queryEditors).toHaveLength(
|
||||
initialState.queryEditors.length + 1,
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { QueryFormData } from '@superset-ui/core';
|
||||
import { ControlPanelConfig } from 'packages/superset-ui-chart-controls/src/types';
|
||||
import { ControlPanelConfig } from '@superset-ui/chart-controls';
|
||||
import { DiffType, RowType } from './index';
|
||||
|
||||
export const defaultProps: Record<string, Partial<QueryFormData>> = {
|
||||
|
||||
@@ -179,7 +179,7 @@ class AlteredSliceTag extends React.Component<
|
||||
return '[]';
|
||||
}
|
||||
return value
|
||||
.map(v => {
|
||||
.map((v: FilterItemType) => {
|
||||
const filterVal =
|
||||
v.comparator && v.comparator.constructor === Array
|
||||
? `[${v.comparator.join(', ')}]`
|
||||
@@ -198,14 +198,14 @@ class AlteredSliceTag extends React.Component<
|
||||
return value.map(v => safeStringify(v)).join(', ');
|
||||
}
|
||||
if (controlsMap[key]?.type === 'MetricsControl' && Array.isArray(value)) {
|
||||
const formattedValue = value.map(v => v?.label ?? v);
|
||||
const formattedValue = value.map((v: FilterItemType) => v?.label ?? v);
|
||||
return formattedValue.length ? formattedValue.join(', ') : '[]';
|
||||
}
|
||||
if (typeof value === 'boolean') {
|
||||
return value ? 'true' : 'false';
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
const formattedValue = value.map(v => v?.label ?? v);
|
||||
const formattedValue = value.map((v: FilterItemType) => v?.label ?? v);
|
||||
return formattedValue.length ? formattedValue.join(', ') : '[]';
|
||||
}
|
||||
if (typeof value === 'string' || typeof value === 'number') {
|
||||
|
||||
@@ -24,11 +24,15 @@ import {
|
||||
TextMode as OrigTextMode,
|
||||
} from 'brace';
|
||||
import AceEditor, { IAceEditorProps } from 'react-ace';
|
||||
import { config } from 'ace-builds';
|
||||
import { acequire } from 'ace-builds/src-noconflict/ace';
|
||||
import AsyncEsmComponent, {
|
||||
PlaceholderProps,
|
||||
} from 'src/components/AsyncEsmComponent';
|
||||
import useEffectEvent from 'src/hooks/useEffectEvent';
|
||||
import cssWorkerUrl from 'ace-builds/src-noconflict/worker-css';
|
||||
|
||||
config.setModuleUrl('ace/mode/css_worker', cssWorkerUrl);
|
||||
|
||||
export interface AceCompleterKeywordData {
|
||||
name: string;
|
||||
|
||||
@@ -229,6 +229,32 @@ test('Should database select display options', async () => {
|
||||
expect(await screen.findByText('test-mysql')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Should fetch the search keyword when total count exceeds initial options', async () => {
|
||||
fetchMock.get(
|
||||
databaseApiRoute,
|
||||
{
|
||||
...fakeDatabaseApiResult,
|
||||
count: fakeDatabaseApiResult.result.length + 1,
|
||||
},
|
||||
{ overwriteRoutes: true },
|
||||
);
|
||||
|
||||
const props = createProps();
|
||||
render(<DatabaseSelector {...props} />, { useRedux: true, store });
|
||||
const select = screen.getByRole('combobox', {
|
||||
name: 'Select database or type to search databases',
|
||||
});
|
||||
await waitFor(() =>
|
||||
expect(fetchMock.calls(databaseApiRoute)).toHaveLength(1),
|
||||
);
|
||||
expect(select).toBeInTheDocument();
|
||||
userEvent.type(select, 'keywordtest');
|
||||
await waitFor(() =>
|
||||
expect(fetchMock.calls(databaseApiRoute)).toHaveLength(2),
|
||||
);
|
||||
expect(fetchMock.calls(databaseApiRoute)[1][0]).toContain('keywordtest');
|
||||
});
|
||||
|
||||
test('should show empty state if there are no options', async () => {
|
||||
fetchMock.reset();
|
||||
fetchMock.get(databaseApiRoute, { result: [] });
|
||||
|
||||
@@ -167,7 +167,7 @@ export default function DatabaseSelector({
|
||||
});
|
||||
const endpoint = `/api/v1/database/?q=${queryParams}`;
|
||||
return SupersetClient.get({ endpoint }).then(({ json }) => {
|
||||
const { result } = json;
|
||||
const { result, count } = json;
|
||||
if (getDbList) {
|
||||
getDbList(result);
|
||||
}
|
||||
@@ -189,7 +189,7 @@ export default function DatabaseSelector({
|
||||
|
||||
return {
|
||||
data: options,
|
||||
totalCount: options.length,
|
||||
totalCount: count ?? options.length,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ import { styled, t } from '@superset-ui/core';
|
||||
import { Select } from 'src/components';
|
||||
import { FormLabel } from 'src/components/Form';
|
||||
import { SELECT_WIDTH } from './utils';
|
||||
import { CardSortSelectOption, FetchDataConfig, SortColumn } from './types';
|
||||
import { CardSortSelectOption, SortColumn } from './types';
|
||||
|
||||
const SortContainer = styled.div`
|
||||
display: inline-flex;
|
||||
@@ -32,22 +32,22 @@ const SortContainer = styled.div`
|
||||
`;
|
||||
|
||||
interface CardViewSelectSortProps {
|
||||
onChange: (conf: FetchDataConfig) => any;
|
||||
onChange: (value: SortColumn[]) => void;
|
||||
options: Array<CardSortSelectOption>;
|
||||
initialSort?: SortColumn[];
|
||||
pageIndex: number;
|
||||
pageSize: number;
|
||||
}
|
||||
|
||||
export const CardSortSelect = ({
|
||||
initialSort,
|
||||
onChange,
|
||||
options,
|
||||
pageIndex,
|
||||
pageSize,
|
||||
}: CardViewSelectSortProps) => {
|
||||
const defaultSort =
|
||||
(initialSort && options.find(({ id }) => id === initialSort[0].id)) ||
|
||||
(initialSort &&
|
||||
options.find(
|
||||
({ id, desc }) =>
|
||||
id === initialSort[0].id && desc === initialSort[0].desc,
|
||||
)) ||
|
||||
options[0];
|
||||
|
||||
const [value, setValue] = useState({
|
||||
@@ -72,7 +72,7 @@ export const CardSortSelect = ({
|
||||
desc: originalOption.desc,
|
||||
},
|
||||
];
|
||||
onChange({ pageIndex, pageSize, sortBy, filters: [] });
|
||||
onChange(sortBy);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -82,7 +82,7 @@ export const CardSortSelect = ({
|
||||
ariaLabel={t('Sort')}
|
||||
header={<FormLabel>{t('Sort')}</FormLabel>}
|
||||
labelInValue
|
||||
onChange={(value: CardSortSelectOption) => handleOnChange(value)}
|
||||
onChange={handleOnChange}
|
||||
options={formattedOptions}
|
||||
showSearch
|
||||
value={value}
|
||||
|
||||
74
superset-frontend/src/components/ListView/ListView.test.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render, waitFor } from 'spec/helpers/testing-library';
|
||||
import ListView from './ListView';
|
||||
|
||||
const mockedProps = {
|
||||
title: 'Data Table',
|
||||
columns: [
|
||||
{
|
||||
accessor: 'id',
|
||||
Header: 'ID',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
accessor: 'age',
|
||||
Header: 'Age',
|
||||
},
|
||||
{
|
||||
accessor: 'name',
|
||||
Header: 'Name',
|
||||
},
|
||||
{
|
||||
accessor: 'time',
|
||||
Header: 'Time',
|
||||
},
|
||||
],
|
||||
data: [
|
||||
{ id: 1, name: 'data 1', age: 10, time: '2020-11-18T07:53:45.354Z' },
|
||||
{ id: 2, name: 'data 2', age: 1, time: '2020-11-18T07:53:45.354Z' },
|
||||
],
|
||||
count: 2,
|
||||
pageSize: 1,
|
||||
loading: false,
|
||||
refreshData: jest.fn(),
|
||||
addSuccessToast: jest.fn(),
|
||||
addDangerToast: jest.fn(),
|
||||
};
|
||||
|
||||
test('redirects to first page when page index is invalid', async () => {
|
||||
const fetchData = jest.fn();
|
||||
window.history.pushState({}, '', '/?pageIndex=9');
|
||||
render(<ListView {...mockedProps} fetchData={fetchData} />, {
|
||||
useRouter: true,
|
||||
useQueryParams: true,
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(window.location.search).toEqual('?pageIndex=0');
|
||||
expect(fetchData).toBeCalledTimes(2);
|
||||
expect(fetchData).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ pageIndex: 9 }),
|
||||
);
|
||||
expect(fetchData).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ pageIndex: 0 }),
|
||||
);
|
||||
});
|
||||
fetchData.mockClear();
|
||||
});
|
||||
@@ -271,10 +271,11 @@ function ListView<T extends object = any>({
|
||||
pageCount = 1,
|
||||
gotoPage,
|
||||
applyFilterValue,
|
||||
setSortBy,
|
||||
selectedFlatRows,
|
||||
toggleAllRowsSelected,
|
||||
setViewMode,
|
||||
state: { pageIndex, pageSize, internalFilters, viewMode },
|
||||
state: { pageIndex, pageSize, internalFilters, sortBy, viewMode },
|
||||
query,
|
||||
} = useListViewState({
|
||||
bulkSelectColumnConfig,
|
||||
@@ -321,6 +322,12 @@ function ListView<T extends object = any>({
|
||||
if (!bulkSelectEnabled) toggleAllRowsSelected(false);
|
||||
}, [bulkSelectEnabled, toggleAllRowsSelected]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && pageIndex > pageCount - 1 && pageCount > 0) {
|
||||
gotoPage(0);
|
||||
}
|
||||
}, [gotoPage, loading, pageCount, pageIndex]);
|
||||
|
||||
return (
|
||||
<ListViewStyles>
|
||||
{allowBulkTagActions && (
|
||||
@@ -350,11 +357,9 @@ function ListView<T extends object = any>({
|
||||
)}
|
||||
{viewMode === 'card' && cardSortSelectOptions && (
|
||||
<CardSortSelect
|
||||
initialSort={initialSort}
|
||||
onChange={fetchData}
|
||||
initialSort={sortBy}
|
||||
onChange={(value: SortColumn[]) => setSortBy(value)}
|
||||
options={cardSortSelectOptions}
|
||||
pageIndex={pageIndex}
|
||||
pageSize={pageSize}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -464,7 +469,7 @@ function ListView<T extends object = any>({
|
||||
<div className="pagination-container">
|
||||
<Pagination
|
||||
totalPages={pageCount || 0}
|
||||
currentPage={pageCount ? pageIndex + 1 : 0}
|
||||
currentPage={pageCount && pageIndex < pageCount ? pageIndex + 1 : 0}
|
||||
onChange={(p: number) => gotoPage(p - 1)}
|
||||
hideFirstAndLastPageLinks
|
||||
/>
|
||||
|
||||
@@ -23,8 +23,6 @@ export interface SortColumn {
|
||||
desc?: boolean;
|
||||
}
|
||||
|
||||
export type SortColumns = SortColumn[];
|
||||
|
||||
export interface SelectOption {
|
||||
label: string;
|
||||
value: any;
|
||||
@@ -84,7 +82,7 @@ export interface FilterValue {
|
||||
export interface FetchDataConfig {
|
||||
pageIndex: number;
|
||||
pageSize: number;
|
||||
sortBy: SortColumns;
|
||||
sortBy: SortColumn[];
|
||||
filters: FilterValue[];
|
||||
}
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ export function useListViewState({
|
||||
query.sortColumn && query.sortOrder
|
||||
? [{ id: query.sortColumn, desc: query.sortOrder === 'desc' }]
|
||||
: initialSort,
|
||||
[query.sortColumn, query.sortOrder],
|
||||
[initialSort, query.sortColumn, query.sortOrder],
|
||||
);
|
||||
|
||||
const initialState = {
|
||||
@@ -256,6 +256,7 @@ export function useListViewState({
|
||||
pageCount,
|
||||
gotoPage,
|
||||
setAllFilters,
|
||||
setSortBy,
|
||||
selectedFlatRows,
|
||||
toggleAllRowsSelected,
|
||||
state: { pageIndex, pageSize, sortBy, filters },
|
||||
@@ -373,6 +374,7 @@ export function useListViewState({
|
||||
rows,
|
||||
selectedFlatRows,
|
||||
setAllFilters,
|
||||
setSortBy,
|
||||
state: { pageIndex, pageSize, sortBy, filters, internalFilters, viewMode },
|
||||
toggleAllRowsSelected,
|
||||
applyFilterValue,
|
||||
|
||||
@@ -939,6 +939,18 @@ test('pasting an existing option does not duplicate it in multiple mode', async
|
||||
expect(await findAllSelectOptions()).toHaveLength(4);
|
||||
});
|
||||
|
||||
test('does not fire onChange if the same value is selected in single mode', async () => {
|
||||
const onChange = jest.fn();
|
||||
render(<AsyncSelect {...defaultProps} onChange={onChange} />);
|
||||
const optionText = 'Emma';
|
||||
await open();
|
||||
expect(onChange).toHaveBeenCalledTimes(0);
|
||||
userEvent.click(await findSelectOption(optionText));
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
userEvent.click(await findSelectOption(optionText));
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
/*
|
||||
TODO: Add tests that require scroll interaction. Needs further investigation.
|
||||
- Fetches more data when scrolling and more data is available
|
||||
|
||||
@@ -50,10 +50,12 @@ import {
|
||||
mapOptions,
|
||||
getOption,
|
||||
isObject,
|
||||
isEqual as utilsIsEqual,
|
||||
} from './utils';
|
||||
import {
|
||||
AsyncSelectProps,
|
||||
AsyncSelectRef,
|
||||
RawValue,
|
||||
SelectOptionsPagePromise,
|
||||
SelectOptionsType,
|
||||
SelectOptionsTypePage,
|
||||
@@ -220,7 +222,16 @@ const AsyncSelect = forwardRef(
|
||||
|
||||
const handleOnSelect: SelectProps['onSelect'] = (selectedItem, option) => {
|
||||
if (isSingleMode) {
|
||||
// on select is fired in single value mode if the same value is selected
|
||||
const valueChanged = !utilsIsEqual(
|
||||
selectedItem,
|
||||
selectValue as RawValue | AntdLabeledValue,
|
||||
'value',
|
||||
);
|
||||
setSelectValue(selectedItem);
|
||||
if (valueChanged) {
|
||||
fireOnChange();
|
||||
}
|
||||
} else {
|
||||
setSelectValue(previousState => {
|
||||
const array = ensureIsArray(previousState);
|
||||
@@ -234,8 +245,8 @@ const AsyncSelect = forwardRef(
|
||||
}
|
||||
return previousState;
|
||||
});
|
||||
fireOnChange();
|
||||
}
|
||||
fireOnChange();
|
||||
onSelect?.(selectedItem, option);
|
||||
};
|
||||
|
||||
|
||||
@@ -1053,6 +1053,18 @@ test('pasting an existing option does not duplicate it in multiple mode', async
|
||||
expect(await findAllSelectOptions()).toHaveLength(4);
|
||||
});
|
||||
|
||||
test('does not fire onChange if the same value is selected in single mode', async () => {
|
||||
const onChange = jest.fn();
|
||||
render(<Select {...defaultProps} onChange={onChange} />);
|
||||
const optionText = 'Emma';
|
||||
await open();
|
||||
expect(onChange).toHaveBeenCalledTimes(0);
|
||||
userEvent.click(await findSelectOption(optionText));
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
userEvent.click(await findSelectOption(optionText));
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
/*
|
||||
TODO: Add tests that require scroll interaction. Needs further investigation.
|
||||
- Fetches more data when scrolling and more data is available
|
||||
|
||||
@@ -53,6 +53,7 @@ import {
|
||||
hasCustomLabels,
|
||||
getOption,
|
||||
isObject,
|
||||
isEqual as utilsIsEqual,
|
||||
} from './utils';
|
||||
import { RawValue, SelectOptionsType, SelectProps } from './types';
|
||||
import {
|
||||
@@ -227,7 +228,16 @@ const Select = forwardRef(
|
||||
|
||||
const handleOnSelect: SelectProps['onSelect'] = (selectedItem, option) => {
|
||||
if (isSingleMode) {
|
||||
// on select is fired in single value mode if the same value is selected
|
||||
const valueChanged = !utilsIsEqual(
|
||||
selectedItem,
|
||||
selectValue as RawValue | AntdLabeledValue,
|
||||
'value',
|
||||
);
|
||||
setSelectValue(selectedItem);
|
||||
if (valueChanged) {
|
||||
fireOnChange();
|
||||
}
|
||||
} else {
|
||||
setSelectValue(previousState => {
|
||||
const array = ensureIsArray(previousState);
|
||||
@@ -259,8 +269,8 @@ const Select = forwardRef(
|
||||
}
|
||||
return previousState;
|
||||
});
|
||||
fireOnChange();
|
||||
}
|
||||
fireOnChange();
|
||||
onSelect?.(selectedItem, option);
|
||||
};
|
||||
|
||||
|
||||
@@ -49,22 +49,24 @@ export function getValue(
|
||||
return isLabeledValue(option) ? option.value : option;
|
||||
}
|
||||
|
||||
export function isEqual(a: V | LabeledValue, b: V | LabeledValue, key: string) {
|
||||
const actualA = isObject(a) && key in a ? a[key] : a;
|
||||
const actualB = isObject(b) && key in b ? b[key] : b;
|
||||
// When comparing the values we use the equality
|
||||
// operator to automatically convert different types
|
||||
// eslint-disable-next-line eqeqeq
|
||||
return actualA == actualB;
|
||||
}
|
||||
|
||||
export function getOption(
|
||||
value: V,
|
||||
options?: V | LabeledValue | (V | LabeledValue)[],
|
||||
checkLabel = false,
|
||||
): V | LabeledValue {
|
||||
const optionsArray = ensureIsArray(options);
|
||||
// When comparing the values we use the equality
|
||||
// operator to automatically convert different types
|
||||
return optionsArray.find(
|
||||
x =>
|
||||
// eslint-disable-next-line eqeqeq
|
||||
x == value ||
|
||||
(isObject(x) &&
|
||||
// eslint-disable-next-line eqeqeq
|
||||
(('value' in x && x.value == value) ||
|
||||
(checkLabel && 'label' in x && x.label === value))),
|
||||
isEqual(x, value, 'value') || (checkLabel && isEqual(x, value, 'label')),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ const TelemetryPixel = ({
|
||||
const pixelPath = `https://apachesuperset.gateway.scarf.sh/pixel/${PIXEL_ID}/${version}/${sha}/${build}`;
|
||||
return process.env.SCARF_ANALYTICS === 'false' ? null : (
|
||||
<img
|
||||
// @ts-ignore
|
||||
referrerPolicy="no-referrer-when-downgrade"
|
||||
src={pixelPath}
|
||||
width={0}
|
||||
|
||||
@@ -189,3 +189,11 @@ export const DEFAULT_COMMON_BOOTSTRAP_DATA: CommonBootstrapData = {
|
||||
export const DEFAULT_BOOTSTRAP_DATA: BootstrapData = {
|
||||
common: DEFAULT_COMMON_BOOTSTRAP_DATA,
|
||||
};
|
||||
|
||||
export enum FilterPlugins {
|
||||
Select = 'filter_select',
|
||||
Range = 'filter_range',
|
||||
Time = 'filter_time',
|
||||
TimeColumn = 'filter_timecolumn',
|
||||
TimeGrain = 'filter_timegrain',
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import { render, screen, waitFor } from 'spec/helpers/testing-library';
|
||||
import { CssEditor as AceCssEditor } from 'src/components/AsyncAceEditor';
|
||||
import { IAceEditorProps } from 'react-ace';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import CssEditor from '.';
|
||||
|
||||
jest.mock('src/components/AsyncAceEditor', () => ({
|
||||
@@ -33,46 +34,59 @@ jest.mock('src/components/AsyncAceEditor', () => ({
|
||||
}));
|
||||
|
||||
const templates = [
|
||||
{ label: 'Template A', css: 'background-color: red;' },
|
||||
{ label: 'Template B', css: 'background-color: blue;' },
|
||||
{ label: 'Template C', css: 'background-color: yellow;' },
|
||||
{ template_name: 'Template A', css: 'background-color: red;' },
|
||||
{ template_name: 'Template B', css: 'background-color: blue;' },
|
||||
{ template_name: 'Template C', css: 'background-color: yellow;' },
|
||||
];
|
||||
|
||||
fetchMock.get('glob:*/csstemplateasyncmodelview/api/read', {
|
||||
result: templates,
|
||||
});
|
||||
|
||||
AceCssEditor.preload = () => new Promise(() => {});
|
||||
|
||||
test('renders with default props', () => {
|
||||
render(<CssEditor triggerNode={<>Click</>} />);
|
||||
const defaultProps = {
|
||||
triggerNode: <>Click</>,
|
||||
addDangerToast: jest.fn(),
|
||||
};
|
||||
|
||||
test('renders with default props', async () => {
|
||||
await waitFor(() => render(<CssEditor {...defaultProps} />));
|
||||
expect(screen.getByRole('button', { name: 'Click' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders with initial CSS', () => {
|
||||
test('renders with initial CSS', async () => {
|
||||
const initialCss = 'margin: 10px;';
|
||||
render(<CssEditor triggerNode={<>Click</>} initialCss={initialCss} />);
|
||||
await waitFor(() =>
|
||||
render(<CssEditor {...defaultProps} initialCss={initialCss} />),
|
||||
);
|
||||
userEvent.click(screen.getByRole('button', { name: 'Click' }));
|
||||
expect(screen.getByText(initialCss)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders with templates', async () => {
|
||||
render(<CssEditor triggerNode={<>Click</>} templates={templates} />);
|
||||
await waitFor(() => render(<CssEditor {...defaultProps} />));
|
||||
userEvent.click(screen.getByRole('button', { name: 'Click' }));
|
||||
userEvent.hover(screen.getByText('Load a CSS template'));
|
||||
await waitFor(() => {
|
||||
templates.forEach(template =>
|
||||
expect(screen.getByText(template.label)).toBeInTheDocument(),
|
||||
expect(screen.getByText(template.template_name)).toBeInTheDocument(),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('triggers onChange when using the editor', () => {
|
||||
test('triggers onChange when using the editor', async () => {
|
||||
const onChange = jest.fn();
|
||||
const initialCss = 'margin: 10px;';
|
||||
const additionalCss = 'color: red;';
|
||||
render(
|
||||
<CssEditor
|
||||
triggerNode={<>Click</>}
|
||||
initialCss={initialCss}
|
||||
onChange={onChange}
|
||||
/>,
|
||||
await waitFor(() =>
|
||||
render(
|
||||
<CssEditor
|
||||
{...defaultProps}
|
||||
initialCss={initialCss}
|
||||
onChange={onChange}
|
||||
/>,
|
||||
),
|
||||
);
|
||||
userEvent.click(screen.getByRole('button', { name: 'Click' }));
|
||||
expect(onChange).not.toHaveBeenCalled();
|
||||
@@ -82,12 +96,8 @@ test('triggers onChange when using the editor', () => {
|
||||
|
||||
test('triggers onChange when selecting a template', async () => {
|
||||
const onChange = jest.fn();
|
||||
render(
|
||||
<CssEditor
|
||||
triggerNode={<>Click</>}
|
||||
templates={templates}
|
||||
onChange={onChange}
|
||||
/>,
|
||||
await waitFor(() =>
|
||||
render(<CssEditor {...defaultProps} onChange={onChange} />),
|
||||
);
|
||||
userEvent.click(screen.getByRole('button', { name: 'Click' }));
|
||||
userEvent.click(screen.getByText('Load a CSS template'));
|
||||
|
||||
@@ -21,7 +21,7 @@ import PropTypes from 'prop-types';
|
||||
import { AntdDropdown } from 'src/components';
|
||||
import { Menu } from 'src/components/Menu';
|
||||
import Button from 'src/components/Button';
|
||||
import { t, styled } from '@superset-ui/core';
|
||||
import { t, styled, SupersetClient } from '@superset-ui/core';
|
||||
import ModalTrigger from 'src/components/ModalTrigger';
|
||||
import { CssEditor as AceCssEditor } from 'src/components/AsyncAceEditor';
|
||||
|
||||
@@ -47,7 +47,7 @@ const propTypes = {
|
||||
initialCss: PropTypes.string,
|
||||
triggerNode: PropTypes.node.isRequired,
|
||||
onChange: PropTypes.func,
|
||||
templates: PropTypes.array,
|
||||
addDangerToast: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
@@ -60,6 +60,7 @@ class CssEditor extends React.PureComponent {
|
||||
super(props);
|
||||
this.state = {
|
||||
css: props.initialCss,
|
||||
templates: [],
|
||||
};
|
||||
this.changeCss = this.changeCss.bind(this);
|
||||
this.changeCssTemplate = this.changeCssTemplate.bind(this);
|
||||
@@ -67,6 +68,22 @@ class CssEditor extends React.PureComponent {
|
||||
|
||||
componentDidMount() {
|
||||
AceCssEditor.preload();
|
||||
|
||||
SupersetClient.get({ endpoint: '/csstemplateasyncmodelview/api/read' })
|
||||
.then(({ json }) => {
|
||||
const templates = json.result.map(row => ({
|
||||
value: row.template_name,
|
||||
css: row.css,
|
||||
label: row.template_name,
|
||||
}));
|
||||
|
||||
this.setState({ templates });
|
||||
})
|
||||
.catch(() => {
|
||||
this.props.addDangerToast(
|
||||
t('An error occurred while fetching available CSS templates'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
changeCss(css) {
|
||||
@@ -80,10 +97,10 @@ class CssEditor extends React.PureComponent {
|
||||
}
|
||||
|
||||
renderTemplateSelector() {
|
||||
if (this.props.templates) {
|
||||
if (this.state.templates) {
|
||||
const menu = (
|
||||
<Menu onClick={this.changeCssTemplate}>
|
||||
{this.props.templates.map(template => (
|
||||
{this.state.templates.map(template => (
|
||||
<Menu.Item key={template.css}>{template.label}</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
|
||||
@@ -44,7 +44,7 @@ import BuilderComponentPane from 'src/dashboard/components/BuilderComponentPane'
|
||||
import DashboardHeader from 'src/dashboard/containers/DashboardHeader';
|
||||
import Icons from 'src/components/Icons';
|
||||
import IconButton from 'src/dashboard/components/IconButton';
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import { Droppable } from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
|
||||
import WithPopoverMenu from 'src/dashboard/components/menu/WithPopoverMenu';
|
||||
import getDirectPathToTabIndex from 'src/dashboard/util/getDirectPathToTabIndex';
|
||||
@@ -81,6 +81,7 @@ import {
|
||||
MAIN_HEADER_HEIGHT,
|
||||
OPEN_FILTER_BAR_MAX_WIDTH,
|
||||
OPEN_FILTER_BAR_WIDTH,
|
||||
EMPTY_CONTAINER_Z_INDEX,
|
||||
} from 'src/dashboard/constants';
|
||||
import { getRootLevelTabsComponent, shouldFocusTabs } from './utils';
|
||||
import DashboardContainer from './DashboardContainer';
|
||||
@@ -107,12 +108,27 @@ const StickyPanel = styled.div<{ width: number }>`
|
||||
|
||||
// @z-index-above-dashboard-popovers (99) + 1 = 100
|
||||
const StyledHeader = styled.div`
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
max-width: 100vw;
|
||||
${({ theme }) => css`
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
max-width: 100vw;
|
||||
|
||||
.empty-droptarget:before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
display: none;
|
||||
width: calc(100% - ${theme.gridUnit * 2}px);
|
||||
height: calc(100% - ${theme.gridUnit * 2}px);
|
||||
left: ${theme.gridUnit}px;
|
||||
top: ${theme.gridUnit}px;
|
||||
border: 1px dashed transparent;
|
||||
border-radius: ${theme.gridUnit}px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledContent = styled.div<{
|
||||
@@ -211,13 +227,9 @@ const DashboardContentWrapper = styled.div`
|
||||
|
||||
/* provide hit area in case row contents is edge to edge */
|
||||
.dashboard-component-tabs-content {
|
||||
.dragdroppable-row {
|
||||
> .dragdroppable-row {
|
||||
padding-top: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
|
||||
& > div:not(:last-child):not(.empty-droptarget) {
|
||||
margin-bottom: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-component-chart-holder {
|
||||
@@ -250,25 +262,21 @@ const DashboardContentWrapper = styled.div`
|
||||
}
|
||||
|
||||
& > .empty-droptarget {
|
||||
z-index: ${EMPTY_CONTAINER_Z_INDEX};
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
& > .empty-droptarget:first-child:not(.empty-droptarget--full) {
|
||||
height: ${theme.gridUnit * 4}px;
|
||||
top: -2px;
|
||||
z-index: 10;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
& > .empty-droptarget:last-child {
|
||||
height: ${theme.gridUnit * 3}px;
|
||||
bottom: 0;
|
||||
height: ${theme.gridUnit * 4}px;
|
||||
bottom: ${-theme.gridUnit * 4}px;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-droptarget:first-child .drop-indicator--bottom {
|
||||
top: ${theme.gridUnit * 6}px;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
@@ -616,8 +624,9 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => {
|
||||
)}
|
||||
<StyledHeader ref={headerRef}>
|
||||
{/* @ts-ignore */}
|
||||
<DragDroppable
|
||||
<Droppable
|
||||
data-test="top-level-tabs"
|
||||
className={cx(!topLevelTabs && editMode && 'empty-droptarget')}
|
||||
component={dashboardRoot}
|
||||
parentComponent={null}
|
||||
depth={DASHBOARD_ROOT_DEPTH}
|
||||
@@ -630,7 +639,7 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => {
|
||||
style={draggableStyle}
|
||||
>
|
||||
{renderDraggableContent}
|
||||
</DragDroppable>
|
||||
</Droppable>
|
||||
</StyledHeader>
|
||||
<StyledContent fullSizeChartId={fullSizeChartId}>
|
||||
<Global
|
||||
|
||||
@@ -17,11 +17,19 @@
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { fireEvent, render } from 'spec/helpers/testing-library';
|
||||
import { fireEvent, render, waitFor } from 'spec/helpers/testing-library';
|
||||
import { OptionControlLabel } from 'src/explore/components/controls/OptionControls';
|
||||
|
||||
import DashboardWrapper from './DashboardWrapper';
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
test('should render children', () => {
|
||||
const { getByTestId } = render(
|
||||
<DashboardWrapper>
|
||||
@@ -32,7 +40,7 @@ test('should render children', () => {
|
||||
expect(getByTestId('mock-children')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should update the style on dragging state', () => {
|
||||
test('should update the style on dragging state', async () => {
|
||||
const defaultProps = {
|
||||
label: <span>Test label</span>,
|
||||
tooltipTitle: 'This is a tooltip title',
|
||||
@@ -69,7 +77,13 @@ test('should update the style on dragging state', () => {
|
||||
container.getElementsByClassName('dragdroppable--dragging'),
|
||||
).toHaveLength(0);
|
||||
fireEvent.dragStart(getByText('Label 1'));
|
||||
await waitFor(() => jest.runAllTimers());
|
||||
expect(
|
||||
container.getElementsByClassName('dragdroppable--dragging'),
|
||||
).toHaveLength(1);
|
||||
fireEvent.dragEnd(getByText('Label 1'));
|
||||
// immediately discards dragging state after dragEnd
|
||||
expect(
|
||||
container.getElementsByClassName('dragdroppable--dragging'),
|
||||
).toHaveLength(0);
|
||||
});
|
||||
|
||||
@@ -17,11 +17,12 @@
|
||||
* under the License.
|
||||
*/
|
||||
import React, { useEffect } from 'react';
|
||||
import { css, styled } from '@superset-ui/core';
|
||||
import { FAST_DEBOUNCE, css, styled } from '@superset-ui/core';
|
||||
import { RootState } from 'src/dashboard/types';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useDragDropManager } from 'react-dnd';
|
||||
import classNames from 'classnames';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
const StyledDiv = styled.div`
|
||||
${({ theme }) => css`
|
||||
@@ -32,10 +33,20 @@ const StyledDiv = styled.div`
|
||||
flex: 1;
|
||||
/* Special cases */
|
||||
|
||||
&.dragdroppable--dragging
|
||||
.dashboard-component-tabs-content
|
||||
> .empty-droptarget.empty-droptarget--full {
|
||||
height: 100%;
|
||||
&.dragdroppable--dragging {
|
||||
&
|
||||
.dashboard-component-tabs-content
|
||||
> .empty-droptarget.empty-droptarget--full {
|
||||
height: 100%;
|
||||
}
|
||||
& .empty-droptarget:before {
|
||||
display: block;
|
||||
border-color: ${theme.colors.primary.light1};
|
||||
background-color: ${theme.colors.primary.light3};
|
||||
}
|
||||
& .grid-row:after {
|
||||
border-style: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
/* A row within a column has inset hover menu */
|
||||
@@ -106,12 +117,22 @@ const DashboardWrapper: React.FC<Props> = ({ children }) => {
|
||||
|
||||
useEffect(() => {
|
||||
const monitor = dragDropManager.getMonitor();
|
||||
const debouncedSetIsDragged = debounce(setIsDragged, FAST_DEBOUNCE);
|
||||
const unsub = monitor.subscribeToStateChange(() => {
|
||||
setIsDragged(monitor.isDragging());
|
||||
const isDragging = monitor.isDragging();
|
||||
if (isDragging) {
|
||||
// set a debounced function to prevent HTML5 drag source
|
||||
// from interfering with the drop zone highlighting
|
||||
debouncedSetIsDragged(true);
|
||||
} else {
|
||||
debouncedSetIsDragged.cancel();
|
||||
setIsDragged(false);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
unsub();
|
||||
debouncedSetIsDragged.cancel();
|
||||
};
|
||||
}, [dragDropManager]);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import { addAlpha, css, styled, t } from '@superset-ui/core';
|
||||
import { EmptyStateBig } from 'src/components/EmptyState';
|
||||
import { componentShape } from '../util/propShapes';
|
||||
import DashboardComponent from '../containers/DashboardComponent';
|
||||
import DragDroppable from './dnd/DragDroppable';
|
||||
import { Droppable } from './dnd/DragDroppable';
|
||||
import { GRID_GUTTER_SIZE, GRID_COLUMN_COUNT } from '../util/constants';
|
||||
import { TAB_TYPE } from '../util/componentTypes';
|
||||
|
||||
@@ -41,15 +41,8 @@ const propTypes = {
|
||||
|
||||
const defaultProps = {};
|
||||
|
||||
const renderDraggableContentBottom = dropProps =>
|
||||
dropProps.dropIndicatorProps && (
|
||||
<div className="drop-indicator drop-indicator--bottom" />
|
||||
);
|
||||
|
||||
const renderDraggableContentTop = dropProps =>
|
||||
dropProps.dropIndicatorProps && (
|
||||
<div className="drop-indicator drop-indicator--top" />
|
||||
);
|
||||
const renderDraggableContent = dropProps =>
|
||||
dropProps.dropIndicatorProps && <div {...dropProps.dropIndicatorProps} />;
|
||||
|
||||
const DashboardEmptyStateContainer = styled.div`
|
||||
position: absolute;
|
||||
@@ -60,28 +53,42 @@ const DashboardEmptyStateContainer = styled.div`
|
||||
`;
|
||||
|
||||
const GridContent = styled.div`
|
||||
${({ theme }) => css`
|
||||
${({ theme, editMode }) => css`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
/* gutters between rows */
|
||||
& > div:not(:last-child):not(.empty-droptarget) {
|
||||
margin-bottom: ${theme.gridUnit * 4}px;
|
||||
${!editMode && `margin-bottom: ${theme.gridUnit * 4}px`};
|
||||
}
|
||||
|
||||
& > .empty-droptarget {
|
||||
.empty-droptarget {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
height: ${theme.gridUnit * 4}px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: ${theme.gridUnit}px;
|
||||
overflow: hidden;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: calc(100% - ${theme.gridUnit * 2}px);
|
||||
height: calc(100% - ${theme.gridUnit * 2}px);
|
||||
border: 1px dashed transparent;
|
||||
border-radius: ${theme.gridUnit}px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
& > .empty-droptarget:first-child {
|
||||
height: ${theme.gridUnit * 12}px;
|
||||
margin-top: ${theme.gridUnit * -6}px;
|
||||
height: ${theme.gridUnit * 4}px;
|
||||
margin-top: ${theme.gridUnit * -4}px;
|
||||
}
|
||||
|
||||
& > .empty-droptarget:last-child {
|
||||
height: ${theme.gridUnit * 12}px;
|
||||
margin-top: ${theme.gridUnit * -6}px;
|
||||
height: ${theme.gridUnit * 24}px;
|
||||
}
|
||||
|
||||
& > .empty-droptarget.empty-droptarget--full:only-child {
|
||||
@@ -265,10 +272,14 @@ class DashboardGrid extends React.PureComponent {
|
||||
</DashboardEmptyStateContainer>
|
||||
)}
|
||||
<div className="dashboard-grid" ref={this.setGridRef}>
|
||||
<GridContent className="grid-content" data-test="grid-content">
|
||||
<GridContent
|
||||
className="grid-content"
|
||||
data-test="grid-content"
|
||||
editMode={editMode}
|
||||
>
|
||||
{/* make the area above components droppable */}
|
||||
{editMode && (
|
||||
<DragDroppable
|
||||
<Droppable
|
||||
component={gridComponent}
|
||||
depth={depth}
|
||||
parentComponent={null}
|
||||
@@ -281,41 +292,43 @@ class DashboardGrid extends React.PureComponent {
|
||||
gridComponent?.children?.length === 0,
|
||||
})}
|
||||
editMode
|
||||
dropToChild={gridComponent?.children?.length === 0}
|
||||
>
|
||||
{renderDraggableContentTop}
|
||||
</DragDroppable>
|
||||
{renderDraggableContent}
|
||||
</Droppable>
|
||||
)}
|
||||
{gridComponent?.children?.map((id, index) => (
|
||||
<DashboardComponent
|
||||
key={id}
|
||||
id={id}
|
||||
parentId={gridComponent.id}
|
||||
depth={depth + 1}
|
||||
index={index}
|
||||
availableColumnCount={GRID_COLUMN_COUNT}
|
||||
columnWidth={columnWidth}
|
||||
isComponentVisible={isComponentVisible}
|
||||
onResizeStart={this.handleResizeStart}
|
||||
onResize={this.handleResize}
|
||||
onResizeStop={this.handleResizeStop}
|
||||
onChangeTab={this.handleChangeTab}
|
||||
/>
|
||||
<React.Fragment key={id}>
|
||||
<DashboardComponent
|
||||
id={id}
|
||||
parentId={gridComponent.id}
|
||||
depth={depth + 1}
|
||||
index={index}
|
||||
availableColumnCount={GRID_COLUMN_COUNT}
|
||||
columnWidth={columnWidth}
|
||||
isComponentVisible={isComponentVisible}
|
||||
onResizeStart={this.handleResizeStart}
|
||||
onResize={this.handleResize}
|
||||
onResizeStop={this.handleResizeStop}
|
||||
onChangeTab={this.handleChangeTab}
|
||||
/>
|
||||
{/* make the area below components droppable */}
|
||||
{editMode && (
|
||||
<Droppable
|
||||
component={gridComponent}
|
||||
depth={depth}
|
||||
parentComponent={null}
|
||||
index={index + 1}
|
||||
orientation="column"
|
||||
onDrop={handleComponentDrop}
|
||||
className="empty-droptarget"
|
||||
editMode
|
||||
>
|
||||
{renderDraggableContent}
|
||||
</Droppable>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
{/* make the area below components droppable */}
|
||||
{editMode && gridComponent?.children?.length > 0 && (
|
||||
<DragDroppable
|
||||
component={gridComponent}
|
||||
depth={depth}
|
||||
parentComponent={null}
|
||||
index={gridComponent.children.length}
|
||||
orientation="column"
|
||||
onDrop={handleComponentDrop}
|
||||
className="empty-droptarget"
|
||||
editMode
|
||||
>
|
||||
{renderDraggableContentBottom}
|
||||
</DragDroppable>
|
||||
)}
|
||||
{isResizing &&
|
||||
Array(GRID_COLUMN_COUNT)
|
||||
.fill(null)
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { SupersetClient, t } from '@superset-ui/core';
|
||||
import { t } from '@superset-ui/core';
|
||||
import { Menu } from 'src/components/Menu';
|
||||
import { URL_PARAMS } from 'src/constants';
|
||||
import ShareMenuItems from 'src/dashboard/components/menu/ShareMenuItems';
|
||||
@@ -99,7 +99,6 @@ class HeaderActionsDropdown extends React.PureComponent {
|
||||
super(props);
|
||||
this.state = {
|
||||
css: props.customCss,
|
||||
cssTemplates: [],
|
||||
showReportSubMenu: null,
|
||||
};
|
||||
|
||||
@@ -109,23 +108,6 @@ class HeaderActionsDropdown extends React.PureComponent {
|
||||
this.setShowReportSubMenu = this.setShowReportSubMenu.bind(this);
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
SupersetClient.get({ endpoint: '/csstemplateasyncmodelview/api/read' })
|
||||
.then(({ json }) => {
|
||||
const cssTemplates = json.result.map(row => ({
|
||||
value: row.template_name,
|
||||
css: row.css,
|
||||
label: row.template_name,
|
||||
}));
|
||||
this.setState({ cssTemplates });
|
||||
})
|
||||
.catch(() => {
|
||||
this.props.addDangerToast(
|
||||
t('An error occurred while fetching available CSS templates'),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
if (this.props.customCss !== nextProps.customCss) {
|
||||
this.setState({ css: nextProps.customCss }, () => {
|
||||
@@ -257,8 +239,8 @@ class HeaderActionsDropdown extends React.PureComponent {
|
||||
<CssEditor
|
||||
triggerNode={<span>{t('Edit CSS')}</span>}
|
||||
initialCss={this.state.css}
|
||||
templates={this.state.cssTemplates}
|
||||
onChange={this.changeCss}
|
||||
addDangerToast={addDangerToast}
|
||||
/>
|
||||
</Menu.Item>
|
||||
)}
|
||||
|
||||
@@ -25,12 +25,7 @@ import { css, styled } from '@superset-ui/core';
|
||||
|
||||
import { componentShape } from '../../util/propShapes';
|
||||
import { dragConfig, dropConfig } from './dragDroppableConfig';
|
||||
import {
|
||||
DROP_TOP,
|
||||
DROP_RIGHT,
|
||||
DROP_BOTTOM,
|
||||
DROP_LEFT,
|
||||
} from '../../util/getDropPosition';
|
||||
import { DROP_FORBIDDEN } from '../../util/getDropPosition';
|
||||
|
||||
const propTypes = {
|
||||
children: PropTypes.func,
|
||||
@@ -39,6 +34,7 @@ const propTypes = {
|
||||
parentComponent: componentShape,
|
||||
depth: PropTypes.number.isRequired,
|
||||
disableDragDrop: PropTypes.bool,
|
||||
dropToChild: PropTypes.bool,
|
||||
orientation: PropTypes.oneOf(['row', 'column']),
|
||||
index: PropTypes.number.isRequired,
|
||||
style: PropTypes.object,
|
||||
@@ -61,6 +57,7 @@ const defaultProps = {
|
||||
style: null,
|
||||
parentComponent: null,
|
||||
disableDragDrop: false,
|
||||
dropToChild: false,
|
||||
children() {},
|
||||
onDrop() {},
|
||||
onHover() {},
|
||||
@@ -77,6 +74,14 @@ const defaultProps = {
|
||||
const DragDroppableStyles = styled.div`
|
||||
${({ theme }) => css`
|
||||
position: relative;
|
||||
/*
|
||||
Next line is a workaround for a bug in react-dnd where the drag
|
||||
preview expands outside of the bounds of the drag source card, see:
|
||||
https://github.com/react-dnd/react-dnd/issues/832#issuecomment-442071628
|
||||
*/
|
||||
&.dragdroppable--edit-mode {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
&.dragdroppable--dragging {
|
||||
opacity: 0.2;
|
||||
@@ -90,49 +95,18 @@ const DragDroppableStyles = styled.div`
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
&.empty-droptarget--full > .drop-indicator--top {
|
||||
height: 100%;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
& {
|
||||
.drop-indicator {
|
||||
display: block;
|
||||
background-color: ${theme.colors.primary.base};
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.drop-indicator--top {
|
||||
top: ${-theme.gridUnit - 2}px;
|
||||
left: 0;
|
||||
height: ${theme.gridUnit}px;
|
||||
opacity: 0.3;
|
||||
width: 100%;
|
||||
min-width: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
|
||||
.drop-indicator--bottom {
|
||||
bottom: ${-theme.gridUnit - 2}px;
|
||||
left: 0;
|
||||
height: ${theme.gridUnit}px;
|
||||
width: 100%;
|
||||
min-width: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
|
||||
.drop-indicator--right {
|
||||
top: 0;
|
||||
left: calc(100% - ${theme.gridUnit}px);
|
||||
height: 100%;
|
||||
width: ${theme.gridUnit}px;
|
||||
min-height: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
|
||||
.drop-indicator--left {
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: ${theme.gridUnit}px;
|
||||
min-height: ${theme.gridUnit * 4}px;
|
||||
&.drop-indicator--forbidden {
|
||||
background-color: ${theme.colors.error.light1};
|
||||
}
|
||||
}
|
||||
}
|
||||
`};
|
||||
@@ -189,10 +163,7 @@ export class UnwrappedDragDroppable extends React.PureComponent {
|
||||
? {
|
||||
className: cx(
|
||||
'drop-indicator',
|
||||
dropIndicator === DROP_TOP && 'drop-indicator--top',
|
||||
dropIndicator === DROP_BOTTOM && 'drop-indicator--bottom',
|
||||
dropIndicator === DROP_LEFT && 'drop-indicator--left',
|
||||
dropIndicator === DROP_RIGHT && 'drop-indicator--right',
|
||||
dropIndicator === DROP_FORBIDDEN && 'drop-indicator--forbidden',
|
||||
),
|
||||
}
|
||||
: null;
|
||||
@@ -211,6 +182,7 @@ export class UnwrappedDragDroppable extends React.PureComponent {
|
||||
data-test="dragdroppable-object"
|
||||
className={cx(
|
||||
'dragdroppable',
|
||||
editMode && 'dragdroppable--edit-mode',
|
||||
orientation === 'row' && 'dragdroppable-row',
|
||||
orientation === 'column' && 'dragdroppable-column',
|
||||
isDragging && 'dragdroppable--dragging',
|
||||
@@ -226,6 +198,9 @@ export class UnwrappedDragDroppable extends React.PureComponent {
|
||||
UnwrappedDragDroppable.propTypes = propTypes;
|
||||
UnwrappedDragDroppable.defaultProps = defaultProps;
|
||||
|
||||
export const Draggable = DragSource(...dragConfig)(UnwrappedDragDroppable);
|
||||
export const Droppable = DropTarget(...dropConfig)(UnwrappedDragDroppable);
|
||||
|
||||
// note that the composition order here determines using
|
||||
// component.method() vs decoratedComponentInstance.method() in the drag/drop config
|
||||
export default DragSource(...dragConfig)(
|
||||
|
||||
@@ -18,10 +18,7 @@
|
||||
*/
|
||||
import getDropPosition, {
|
||||
clearDropCache,
|
||||
DROP_TOP,
|
||||
DROP_RIGHT,
|
||||
DROP_BOTTOM,
|
||||
DROP_LEFT,
|
||||
DROP_FORBIDDEN,
|
||||
} from '../../util/getDropPosition';
|
||||
|
||||
export default function handleDrop(props, monitor, Component) {
|
||||
@@ -31,7 +28,7 @@ export default function handleDrop(props, monitor, Component) {
|
||||
Component.setState(() => ({ dropIndicator: null }));
|
||||
const dropPosition = getDropPosition(monitor, Component);
|
||||
|
||||
if (!dropPosition) {
|
||||
if (!dropPosition || dropPosition === DROP_FORBIDDEN) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -40,19 +37,11 @@ export default function handleDrop(props, monitor, Component) {
|
||||
component,
|
||||
index: componentIndex,
|
||||
onDrop,
|
||||
orientation,
|
||||
dropToChild,
|
||||
} = Component.props;
|
||||
|
||||
const draggingItem = monitor.getItem();
|
||||
|
||||
const dropAsChildOrSibling =
|
||||
(orientation === 'row' &&
|
||||
(dropPosition === DROP_TOP || dropPosition === DROP_BOTTOM)) ||
|
||||
(orientation === 'column' &&
|
||||
(dropPosition === DROP_LEFT || dropPosition === DROP_RIGHT))
|
||||
? 'sibling'
|
||||
: 'child';
|
||||
|
||||
const dropResult = {
|
||||
source: {
|
||||
id: draggingItem.parentId,
|
||||
@@ -67,12 +56,18 @@ export default function handleDrop(props, monitor, Component) {
|
||||
};
|
||||
|
||||
// simplest case, append as child
|
||||
if (dropAsChildOrSibling === 'child') {
|
||||
if (dropToChild) {
|
||||
dropResult.destination = {
|
||||
id: component.id,
|
||||
type: component.type,
|
||||
index: component.children.length,
|
||||
};
|
||||
} else if (!parentComponent) {
|
||||
dropResult.destination = {
|
||||
id: component.id,
|
||||
type: component.type,
|
||||
index: componentIndex,
|
||||
};
|
||||
} else {
|
||||
// if the item is in the same list with a smaller index, you must account for the
|
||||
// "missing" index upon movement within the list
|
||||
@@ -81,10 +76,9 @@ export default function handleDrop(props, monitor, Component) {
|
||||
const sameParentLowerIndex =
|
||||
sameParent && draggingItem.index < componentIndex;
|
||||
|
||||
let nextIndex = sameParentLowerIndex ? componentIndex - 1 : componentIndex;
|
||||
if (dropPosition === DROP_BOTTOM || dropPosition === DROP_RIGHT) {
|
||||
nextIndex += 1;
|
||||
}
|
||||
const nextIndex = sameParentLowerIndex
|
||||
? componentIndex - 1
|
||||
: componentIndex;
|
||||
|
||||
dropResult.destination = {
|
||||
id: parentComponent.id,
|
||||
|
||||
@@ -25,7 +25,7 @@ import { LayoutItem, RootState } from 'src/dashboard/types';
|
||||
import AnchorLink from 'src/dashboard/components/AnchorLink';
|
||||
import Chart from 'src/dashboard/containers/Chart';
|
||||
import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import { Draggable } from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
|
||||
import ResizableContainer from 'src/dashboard/components/resizable/ResizableContainer';
|
||||
import getChartAndLabelComponentIdFromPath from 'src/dashboard/util/getChartAndLabelComponentIdFromPath';
|
||||
@@ -243,7 +243,7 @@ const ChartHolder: React.FC<ChartHolderProps> = ({
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<DragDroppable
|
||||
<Draggable
|
||||
component={component}
|
||||
parentComponent={parentComponent}
|
||||
orientation={parentComponent.type === ROW_TYPE ? 'column' : 'row'}
|
||||
@@ -253,7 +253,7 @@ const ChartHolder: React.FC<ChartHolderProps> = ({
|
||||
disableDragDrop={false}
|
||||
editMode={editMode}
|
||||
>
|
||||
{({ dropIndicatorProps, dragSourceRef }) => (
|
||||
{({ dragSourceRef }) => (
|
||||
<ResizableContainer
|
||||
id={component.id}
|
||||
adjustableWidth={parentComponent.type === ROW_TYPE}
|
||||
@@ -324,10 +324,9 @@ const ChartHolder: React.FC<ChartHolderProps> = ({
|
||||
</HoverMenu>
|
||||
)}
|
||||
</div>
|
||||
{dropIndicatorProps && <div {...dropIndicatorProps} />}
|
||||
</ResizableContainer>
|
||||
)}
|
||||
</DragDroppable>
|
||||
</Draggable>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||