mirror of
https://github.com/apache/superset.git
synced 2026-09-10 09:14:26 +00:00
Compare commits
176
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a52c89a7f | ||
|
|
e495fd80f6 | ||
|
|
bda62cb28d | ||
|
|
45354994a6 | ||
|
|
0f61d9a3fb | ||
|
|
b432f8c917 | ||
|
|
25f6c6c80b | ||
|
|
ce8d4397cd | ||
|
|
68297886d4 | ||
|
|
91f814abea | ||
|
|
6a330c251b | ||
|
|
5bf26f77f8 | ||
|
|
5750d82426 | ||
|
|
c84a154b52 | ||
|
|
5b837e844e | ||
|
|
a99c98c6fe | ||
|
|
f8cfa459ef | ||
|
|
eef3dac72f | ||
|
|
73aa8ef280 | ||
|
|
f3255c46ac | ||
|
|
9f230bcfc0 | ||
|
|
c05ac138fb | ||
|
|
c0781ba316 | ||
|
|
905b20a3f2 | ||
|
|
fc506c06da | ||
|
|
69b144ec16 | ||
|
|
9ccd365652 | ||
|
|
60fb1c6f01 | ||
|
|
929ec58276 | ||
|
|
bef03fb850 | ||
|
|
8d36dca9e8 | ||
|
|
9bb0e376b3 | ||
|
|
778800ba82 | ||
|
|
ba309fd016 | ||
|
|
7eab41f904 | ||
|
|
dbef0c3fee | ||
|
|
157ef61fd8 | ||
|
|
f467d36a24 | ||
|
|
9f8e1508ca | ||
|
|
20e6dfd37d | ||
|
|
5067230484 | ||
|
|
c45b9002e6 | ||
|
|
edca579625 | ||
|
|
30b93ab743 | ||
|
|
9e38c1dd13 | ||
|
|
b459601ceb | ||
|
|
6f154377d7 | ||
|
|
5b57eb38d2 | ||
|
|
33bc35d135 | ||
|
|
2dfd08fdb3 | ||
|
|
661535e390 | ||
|
|
059f6944eb | ||
|
|
3b7647eb6e | ||
|
|
e84f870787 | ||
|
|
de300c70b9 | ||
|
|
2f7afe4b47 | ||
|
|
aea4585c6d | ||
|
|
f697a0c24d | ||
|
|
13d38a9cbd | ||
|
|
8603048518 | ||
|
|
635b18103d | ||
|
|
d57569c54a | ||
|
|
409605de70 | ||
|
|
8ce6d42942 | ||
|
|
2bbb7d0638 | ||
|
|
1b0c6aaed3 | ||
|
|
1f786f1949 | ||
|
|
c540f782a3 | ||
|
|
6fe4655ba2 | ||
|
|
689fc34ac2 | ||
|
|
e564389a01 | ||
|
|
da518d7a10 | ||
|
|
c6da740ce2 | ||
|
|
e28b259de0 | ||
|
|
beb9d53687 | ||
|
|
90f9238f8a | ||
|
|
f38fff2a19 | ||
|
|
cec9afb165 | ||
|
|
753113d169 | ||
|
|
5c8e14e9dc | ||
|
|
e66d58361a | ||
|
|
7bc1895050 | ||
|
|
071c431580 | ||
|
|
53c4603c8c | ||
|
|
f010affbc2 | ||
|
|
6c2b7aceb5 | ||
|
|
2af66b2c9b | ||
|
|
a540f56f5c | ||
|
|
aa85455a5c | ||
|
|
d80267d00b | ||
|
|
7953382d10 | ||
|
|
abafe195bf | ||
|
|
b4373f60b3 | ||
|
|
6dcd95e14a | ||
|
|
ad8f2cf268 | ||
|
|
9db88203e1 | ||
|
|
13121fcd58 | ||
|
|
be768efc0f | ||
|
|
d68e84e731 | ||
|
|
0a3c263606 | ||
|
|
bd9ba24266 | ||
|
|
4446967d0f | ||
|
|
c04a0295ef | ||
|
|
e3a0d0e41e | ||
|
|
256cc71c87 | ||
|
|
4b07d43050 | ||
|
|
ef3a9e925e | ||
|
|
76bb5f8e69 | ||
|
|
1f41899deb | ||
|
|
c4bee525ea | ||
|
|
0dcf67fa27 | ||
|
|
4e10a96253 | ||
|
|
24959d1656 | ||
|
|
7075e9f253 | ||
|
|
d5e75c4813 | ||
|
|
cc32d16e0c | ||
|
|
258f4c035b | ||
|
|
d49365757c | ||
|
|
68881a60ca | ||
|
|
41efdb3082 | ||
|
|
e86dcf7b82 | ||
|
|
35d05cc278 | ||
|
|
115ce12ff7 | ||
|
|
ccfccdf237 | ||
|
|
42523f8cc4 | ||
|
|
e165762bb7 | ||
|
|
8afad27c84 | ||
|
|
e420812eb7 | ||
|
|
993a43396a | ||
|
|
a2a71760ed | ||
|
|
836dce9b05 | ||
|
|
2857b29ab9 | ||
|
|
3c648ca264 | ||
|
|
25ce83cc58 | ||
|
|
ce8219672e | ||
|
|
3e957b63d4 | ||
|
|
9818da445a | ||
|
|
8262aa7d6b | ||
|
|
d4a31d1d78 | ||
|
|
18f1dd394b | ||
|
|
62ccdfacc2 | ||
|
|
c5131bff47 | ||
|
|
521e51429a | ||
|
|
64df96be00 | ||
|
|
d43a0d7633 | ||
|
|
873566c827 | ||
|
|
e063f5093c | ||
|
|
1627fab741 | ||
|
|
5dd060b714 | ||
|
|
68ebc22e1e | ||
|
|
8f75f1a353 | ||
|
|
2fac66d1a3 | ||
|
|
029d49539b | ||
|
|
3c6982252b | ||
|
|
1c7a3f395f | ||
|
|
f7b7bad9a8 | ||
|
|
0cf217cdea | ||
|
|
96bf1a1e4c | ||
|
|
9180ace1dd | ||
|
|
bb5d7dbb17 | ||
|
|
73925922a9 | ||
|
|
8f339545ad | ||
|
|
c762b75ddc | ||
|
|
c0e5f5226d | ||
|
|
d380663e71 | ||
|
|
98c3ec7a71 | ||
|
|
12f28ce235 | ||
|
|
e403d9b074 | ||
|
|
0ca51d9796 | ||
|
|
db1912c6d9 | ||
|
|
2a18a556b0 | ||
|
|
a1d1d69f5e | ||
|
|
13c5a32402 | ||
|
|
958d1ab256 | ||
|
|
b8c0171976 | ||
|
|
c77c709b79 |
Submodule .github/actions/file-changes-action deleted from a6ca26c142
@@ -32,8 +32,6 @@ runs:
|
||||
elif [ "$INPUT_PYTHON_VERSION" = "next" ]; then
|
||||
# currently disabled in GHA matrixes because of library compatibility issues
|
||||
RESOLVED_VERSION="3.12"
|
||||
elif [ "$INPUT_PYTHON_VERSION" = "previous" ]; then
|
||||
RESOLVED_VERSION="3.10"
|
||||
elif printf '%s' "$INPUT_PYTHON_VERSION" | grep -Eq '^[0-9]+\.[0-9]+(\.[0-9]+)?$'; then
|
||||
RESOLVED_VERSION="$INPUT_PYTHON_VERSION"
|
||||
else
|
||||
|
||||
+26
-2
@@ -10,8 +10,15 @@ updates:
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
ignore:
|
||||
- dependency-name: "@rjsf/*"
|
||||
# TODO: remove below entries until React >= 19.0.0
|
||||
# TODO: remove below entries once the application supports React >= 19.0.0
|
||||
- dependency-name: "react"
|
||||
update-types: ["version-update:semver-major"]
|
||||
- dependency-name: "react-dom"
|
||||
update-types: ["version-update:semver-major"]
|
||||
- dependency-name: "@types/react"
|
||||
update-types: ["version-update:semver-major"]
|
||||
- dependency-name: "@types/react-dom"
|
||||
update-types: ["version-update:semver-major"]
|
||||
- dependency-name: "react-icons"
|
||||
# JSDOM v30 doesn't play well with Jest v30
|
||||
# Source: https://jestjs.io/blog#known-issues
|
||||
@@ -41,6 +48,23 @@ updates:
|
||||
labels:
|
||||
- npm
|
||||
- dependabot
|
||||
groups:
|
||||
rjsf:
|
||||
patterns:
|
||||
- "@rjsf/*"
|
||||
typescript-eslint:
|
||||
patterns:
|
||||
- "@typescript-eslint/*"
|
||||
- "typescript-eslint"
|
||||
babel:
|
||||
patterns:
|
||||
- "@babel/*"
|
||||
deckgl:
|
||||
patterns:
|
||||
- "@deck.gl/*"
|
||||
lumagl:
|
||||
patterns:
|
||||
- "@luma.gl/*"
|
||||
open-pull-requests-limit: 30
|
||||
versioning-strategy: increase
|
||||
cooldown:
|
||||
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||
uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -75,6 +75,6 @@ jobs:
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@54f647b7e1bb85c95cddabcd46b0c578ec92bc1a # v4.36.3
|
||||
uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
# token, which makes npm attempt token auth and skip the OIDC
|
||||
# trusted-publishing exchange. With no .npmrc auth line, npm authenticates
|
||||
# via OIDC against the default registry (registry.npmjs.org).
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: "./superset-embedded-sdk/.nvmrc"
|
||||
- run: npm ci
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: "./superset-embedded-sdk/.nvmrc"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
|
||||
uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: "11"
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0
|
||||
- uses: actions/labeler@b8dd2d9be0f68b860e7dae5dae7d772984eacd6d # v6.2.0
|
||||
with:
|
||||
sync-labels: true
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
|
||||
uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: "11"
|
||||
|
||||
@@ -8,10 +8,16 @@ on:
|
||||
# Possible values: https://help.github.com/en/actions/reference/events-that-trigger-workflows#pull-request-event-pull_request
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
|
||||
# cancel previous workflow jobs for PRs
|
||||
# Serialize runs per PR without cancelling: the `edited` trigger means a PR
|
||||
# opened and then edited has two queued runs for the same head SHA. On
|
||||
# first-time-contributor PRs those runs start together when a maintainer
|
||||
# approves workflows, and cancel-in-progress lets the older run cancel the
|
||||
# newer one — leaving a permanently-cancelled required check on the head SHA
|
||||
# that blocks merging until manually re-run. This job takes seconds, so let
|
||||
# queued runs complete instead.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
lint-check:
|
||||
|
||||
@@ -7,6 +7,11 @@ on:
|
||||
- "[0-9].[0-9]*"
|
||||
pull_request:
|
||||
types: [synchronize, opened, reopened, ready_for_review]
|
||||
# Nightly full-tree sweep. Per-PR runs only lint changed files, so a change
|
||||
# that invalidates an untouched file (e.g. a type change that breaks an
|
||||
# importing test) can pass every PR yet leave master red. This catches that.
|
||||
schedule:
|
||||
- cron: "0 6 * * *"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -25,13 +30,16 @@ jobs:
|
||||
# Run the full version spread on push (master/release) and nightly,
|
||||
# but only the current version on PRs — lint/format/type results
|
||||
# rarely differ across patch versions, so 3x per PR is wasteful.
|
||||
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["current", "previous", "next"]') }}
|
||||
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["current", "next"]') }}
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
# Full history so we can diff a PR/push against its base commit to
|
||||
# determine changed files (see "Determine changed files" below).
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Python
|
||||
uses: ./.github/actions/setup-backend/
|
||||
@@ -45,7 +53,7 @@ jobs:
|
||||
run: go install github.com/norwoodj/helm-docs/cmd/helm-docs@v1.14.2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: "superset-frontend/.nvmrc"
|
||||
cache: "npm"
|
||||
@@ -69,19 +77,82 @@ jobs:
|
||||
restore-keys: |
|
||||
pre-commit-v2-${{ runner.os }}-py${{ matrix.python-version }}-
|
||||
|
||||
- name: Get changed files
|
||||
- name: Determine changed files
|
||||
id: changed_files
|
||||
uses: ./.github/actions/file-changes-action
|
||||
with:
|
||||
output: " "
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
BEFORE_SHA: ${{ github.event.before }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Scheduled runs check the whole tree (see the pre-commit step).
|
||||
if [ "${EVENT_NAME}" = "schedule" ]; then
|
||||
echo "mode=all" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Resolve the commit to diff against.
|
||||
base=""
|
||||
if [ "${EVENT_NAME}" = "pull_request" ]; then
|
||||
base="${BASE_SHA}"
|
||||
elif [ -n "${BEFORE_SHA:-}" ] && \
|
||||
[ "${BEFORE_SHA}" != "0000000000000000000000000000000000000000" ]; then
|
||||
base="${BEFORE_SHA}"
|
||||
fi
|
||||
|
||||
# Fail closed: if the diff base can't be resolved, check every file
|
||||
# instead of silently checking nothing. Previously an empty file list
|
||||
# made `pre-commit run --files` a no-op that still reported success,
|
||||
# which let unlinted code reach master.
|
||||
if [ -z "${base}" ] || ! git cat-file -e "${base}^{commit}" 2>/dev/null; then
|
||||
echo "::notice::Could not resolve a diff base; falling back to --all-files."
|
||||
echo "mode=all" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Files present in HEAD that changed since the base (drop deletions).
|
||||
files="$(git diff --name-only --diff-filter=ACMRT "${base}...HEAD")"
|
||||
|
||||
if [ -z "${files}" ]; then
|
||||
echo "mode=none" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "mode=files" >> "$GITHUB_OUTPUT"
|
||||
{
|
||||
echo "files<<__CHANGED_FILES_EOF__"
|
||||
echo "${files}"
|
||||
echo "__CHANGED_FILES_EOF__"
|
||||
} >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: pre-commit
|
||||
env:
|
||||
MODE: ${{ steps.changed_files.outputs.mode }}
|
||||
CHANGED_FILES: ${{ steps.changed_files.outputs.files }}
|
||||
run: |
|
||||
set +e # Don't exit immediately on failure
|
||||
export SKIP=type-checking-frontend
|
||||
pre-commit run --files $CHANGED_FILES
|
||||
|
||||
case "${MODE}" in
|
||||
all)
|
||||
echo "ℹ️ Running pre-commit on all files."
|
||||
pre-commit run --all-files
|
||||
;;
|
||||
files)
|
||||
echo "ℹ️ Running pre-commit on changed files:"
|
||||
echo "${CHANGED_FILES}"
|
||||
# shellcheck disable=SC2086
|
||||
pre-commit run --files ${CHANGED_FILES}
|
||||
;;
|
||||
none)
|
||||
echo "ℹ️ No source files changed; nothing for pre-commit to check."
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "⚠️ Unrecognized changed-files mode '${MODE}'; checking all files."
|
||||
pre-commit run --all-files
|
||||
;;
|
||||
esac
|
||||
PRE_COMMIT_EXIT_CODE=$?
|
||||
git diff --quiet --exit-code
|
||||
GIT_DIFF_EXIT_CODE=$?
|
||||
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
|
||||
- name: Install Node.js
|
||||
if: env.HAS_TAGS
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: "./superset-frontend/.nvmrc"
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ jobs:
|
||||
build: "true"
|
||||
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
|
||||
@@ -66,12 +66,12 @@ jobs:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: "./docs/.nvmrc"
|
||||
- name: Setup Python
|
||||
uses: ./.github/actions/setup-backend/
|
||||
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
|
||||
- uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
|
||||
with:
|
||||
distribution: "zulu"
|
||||
java-version: "21"
|
||||
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: "./docs/.nvmrc"
|
||||
- name: yarn install
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: "./docs/.nvmrc"
|
||||
- name: yarn install
|
||||
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
with:
|
||||
run: testdata
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: "./superset-frontend/.nvmrc"
|
||||
cache: "npm"
|
||||
@@ -238,7 +238,7 @@ jobs:
|
||||
with:
|
||||
run: playwright_testdata
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: "./superset-frontend/.nvmrc"
|
||||
cache: "npm"
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
matrix:
|
||||
# Full version spread on push (master/release) + nightly; current only
|
||||
# on PRs to cut runner cost (cross-version breaks are caught at merge).
|
||||
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["previous", "current", "next"]') }}
|
||||
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["current", "next"]') }}
|
||||
defaults:
|
||||
run:
|
||||
working-directory: superset-extensions-cli
|
||||
|
||||
@@ -2,17 +2,20 @@ name: "Helm: lint and test charts"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
types: [opened, reopened, synchronize]
|
||||
paths:
|
||||
- "helm/**"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# cancel previous workflow jobs for PRs
|
||||
# Serialize runs per PR without cancelling: when a first-time contributor's
|
||||
# queued runs are approved together, cancel-in-progress lets an older run
|
||||
# cancel a newer one, leaving a permanently-cancelled required check on the
|
||||
# head SHA. Queued runs are cheap here, so let them all complete.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
lint-test:
|
||||
|
||||
@@ -114,7 +114,7 @@ jobs:
|
||||
with:
|
||||
run: playwright_testdata
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: "./superset-frontend/.nvmrc"
|
||||
cache: "npm"
|
||||
|
||||
@@ -135,7 +135,7 @@ jobs:
|
||||
matrix:
|
||||
# Full version spread on push (master/release) + nightly; current only
|
||||
# on PRs to cut runner cost (cross-version breaks are caught at merge).
|
||||
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["current", "previous", "next"]') }}
|
||||
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["current", "next"]') }}
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
SUPERSET_CONFIG: tests.integration_tests.superset_test_config
|
||||
|
||||
@@ -50,9 +50,15 @@ jobs:
|
||||
matrix:
|
||||
# Full version spread on push (master/release) + nightly; current only
|
||||
# on PRs to cut runner cost (cross-version breaks are caught at merge).
|
||||
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["previous", "current", "next"]') }}
|
||||
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["current", "next"]') }}
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
# Promotes the SQLAlchemy 2.0 deprecation warnings already locked in as
|
||||
# errors via pytest.ini's `filterwarnings` to actually run in CI, so a
|
||||
# regression on those fails the build instead of relying on a
|
||||
# contributor remembering to set this locally. See the migration
|
||||
# battleplan: https://github.com/apache/superset/discussions/40273
|
||||
SQLALCHEMY_WARN_20: "1"
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
- name: Setup Node.js
|
||||
if: steps.check.outputs.frontend
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: "./superset-frontend/.nvmrc"
|
||||
cache: "npm"
|
||||
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
|
||||
- name: Use Node.js 20
|
||||
# zizmor: ignore[cache-poisoning] - node only runs the supersetbot CLI; no dependency cache is enabled
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 20
|
||||
package-manager-cache: false
|
||||
@@ -133,7 +133,7 @@ jobs:
|
||||
|
||||
- name: Use Node.js 20
|
||||
# zizmor: ignore[cache-poisoning] - node only runs the supersetbot CLI; no dependency cache is enabled
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: 20
|
||||
package-manager-cache: false
|
||||
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version-file: "./superset-frontend/.nvmrc"
|
||||
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
[submodule ".github/actions/pr-lint-action"]
|
||||
path = .github/actions/pr-lint-action
|
||||
url = https://github.com/morrisoncole/pr-lint-action
|
||||
[submodule ".github/actions/file-changes-action"]
|
||||
path = .github/actions/file-changes-action
|
||||
url = https://github.com/trilom/file-changes-action
|
||||
[submodule ".github/actions/cached-dependencies"]
|
||||
path = .github/actions/cached-dependencies
|
||||
url = https://github.com/apache-superset/cached-dependencies
|
||||
|
||||
+10
-3
@@ -31,9 +31,10 @@ repos:
|
||||
types-simplejson,
|
||||
types-python-dateutil,
|
||||
types-requests,
|
||||
# types-redis 4.6.0.5 is failing mypy
|
||||
# because of https://github.com/python/typeshed/pull/10531
|
||||
types-redis==4.6.0.4,
|
||||
# types-redis is intentionally absent: redis-py ships its own
|
||||
# bundled types (py.typed, since 4.6+/5.x), while the stub
|
||||
# package is unmaintained (frozen at the 4.6.0.x API surface).
|
||||
# Installing the stub would shadow the accurate inline types.
|
||||
types-pytz,
|
||||
types-croniter,
|
||||
types-PyYAML,
|
||||
@@ -81,6 +82,12 @@ repos:
|
||||
language: system
|
||||
pass_filenames: true
|
||||
files: ^superset-frontend/.*\.(js|jsx|ts|tsx)$
|
||||
- id: stylelint-frontend
|
||||
name: stylelint (frontend css-in-js)
|
||||
entry: ./scripts/stylelint.sh
|
||||
language: system
|
||||
pass_filenames: true
|
||||
files: ^superset-frontend/.*\.(js|jsx|ts|tsx)$
|
||||
- id: eslint-docs
|
||||
name: eslint (docs)
|
||||
entry: bash -c 'cd docs && FILES=$(printf "%s\n" "$@" | sed "s|^docs/||" | tr "\n" " ") && yarn eslint --fix --quiet $FILES'
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
.codecov.yml
|
||||
.eslintrc
|
||||
.eslintignore
|
||||
.stylelintignore
|
||||
.flake8
|
||||
.nvmrc
|
||||
.prettierrc
|
||||
|
||||
+17
@@ -112,6 +112,8 @@ in a later major version.
|
||||
|
||||
- [41044](https://github.com/apache/superset/issues/41044): Removes the deprecated `AVOID_COLORS_COLLISION` feature flag (it defaulted to `True`). Color-collision avoidance is now permanently enabled; any config override setting it to `False` is ignored.
|
||||
|
||||
- [41813](https://github.com/apache/superset/pull/41813): `redis` (the Python client, `redis-py`) is bumped from 5.3.1 to 8.0.1. redis-py 8 changes several connection defaults; Superset's own Redis-backed features (`GLOBAL_ASYNC_QUERIES_CACHE_BACKEND`, `DISTRIBUTED_COORDINATION_CONFIG`, and the MCP Redis store) explicitly pin the pre-upgrade behavior so this bump is a no-op for them: the wire protocol stays RESP2 (not the new RESP3 default, which requires Redis/Sentinel 6+ to speak `HELLO`) and there is still no socket timeout by default (redis-py 8 defaults to 5s, which could otherwise newly time out large cached payloads or slow networks). The no-timeout default can now be overridden via two new config keys, `CACHE_REDIS_SOCKET_TIMEOUT` / `CACHE_REDIS_SOCKET_CONNECT_TIMEOUT`, on any `CacheConfig` dict using `CACHE_TYPE: RedisCache` or `RedisSentinelCache`. Separately, redis-py 6+ changed the default for `ssl_check_hostname` from `False` to `True` for SSL connections using `ssl_cert_reqs="required"` (the default) — this is a security improvement, so it has **not** been reverted; deployments with `CACHE_REDIS_SSL=True` whose certificates lack a hostname matching the connection address should set `CACHE_REDIS_SSL_CERT_REQS="none"` (disables cert verification entirely, matching hostname-check bypass) or replace the certificate. General-purpose cache/results backends configured via `CACHE_CONFIG` / `DATA_CACHE_CONFIG` / `RESULTS_BACKEND` with `CACHE_TYPE: RedisCache` go through `flask-caching`'s own Redis backend (outside Superset's code) and are subject to the same new defaults; pass `socket_timeout` / `protocol` via `CACHE_OPTIONS` there if needed. Celery broker and result-backend connections (built by `kombu`, also outside Superset's code) keep their no-socket-timeout behavior (`kombu` passes `socket_timeout=None` explicitly) but do **not** pin the wire protocol, so they follow redis-py's RESP3 default — which requires a Redis server new enough to speak `HELLO` (Redis 6+). Deployments using a pre-6.0 Redis server (EOL) as a Celery broker should upgrade the server before taking this bump.
|
||||
|
||||
- [39925](https://github.com/apache/superset/pull/39925): URL prefixing for `SUPERSET_APP_ROOT` subdirectory deployments is now handled automatically by helpers in `src/utils/navigationUtils` (`openInNewTab`, `redirect`, `getShareableUrl`, `<AppLink>`). Direct imports of `ensureAppRoot` / `makeUrl` from `src/utils/pathUtils` are forbidden outside `navigationUtils.ts` (enforced by a static-invariant test); contributors writing new code should use the focused helpers instead. No runtime behaviour change for existing callers — all 19 prior call sites have been migrated and four pre-existing double-prefix and missing-prefix bugs are fixed as part of the migration.
|
||||
|
||||
- [39925](https://github.com/apache/superset/pull/39925): `SupersetClient.getUrl()` now strips a single leading application-root segment from the supplied `endpoint` before building the request URL, so a caller that accidentally pre-prefixes its endpoint (for example by wrapping it with `ensureAppRoot` before passing it to the client) no longer produces a doubled `/superset/superset/...` URL under subdirectory deployment. The strip is **single-pass** — a genuine `/superset/superset/<slug>` route is preserved, not collapsed — and **silent** (no console warning); the static-invariant test remains the primary signal for pre-prefixing at the call site, and this runtime strip is a safety net beneath it. Code that intentionally targeted a literal `/<app_root>/<app_root>/...` endpoint through `getUrl` (a configuration that has no legitimate use under the prefixing model) would have its first redundant segment removed.
|
||||
@@ -153,6 +155,16 @@ virtual-dataset SQL) raises a parse error in SQL Lab and dashboard-generated
|
||||
queries. Deployments that legitimately run queries above this size should raise
|
||||
the value, and `SQL_MAX_PARSE_LENGTH = None` disables the check entirely.
|
||||
|
||||
### Ant Design upgraded from v5 to v6
|
||||
|
||||
The frontend now builds against Ant Design 6, and `@superset-ui/core` / `@apache-superset/core` peer-depend on `antd ^6`. Custom plugins, extensions, and themes that interact with Ant Design need review:
|
||||
|
||||
- **Internal DOM classes were renamed**, so any custom CSS targeting `.ant-*` internals silently stops matching. Notable renames: `.ant-tabs-content-holder` → `.ant-tabs-body-holder`, `.ant-tabs-content` → `.ant-tabs-body`, `.ant-tabs-tabpane` → `.ant-tabs-content`; `.ant-select-selector` → `.ant-select-content`, `.ant-select-selection-placeholder` → `.ant-select-placeholder`, `.ant-select-arrow` → `.ant-select-suffix`; `.ant-tooltip-inner` → `.ant-tooltip-container`; `.ant-popover-inner` → `.ant-popover-container`; `.ant-steps-item-tail` → `.ant-steps-item-rail`.
|
||||
- **Some component props changed or were removed** — e.g. `Select` no longer accepts `dropdownAlign`, `visible`/`onVisibleChange` are `open`/`onOpenChange`, `Dropdown` `overlay` is `menu`, `Steps.Step` children are the `items` prop, and `styles.body` on Tooltip/Popover is `styles.container`.
|
||||
- **CSS variables are on by default** in antd 6, and `ThemeConfig.cssVar` no longer accepts a boolean; Superset theme configs using `cssVar: true`/`false` are coerced (`true` → `{}`, `false` → omitted).
|
||||
|
||||
Theme tokens are unaffected — antd 6 removed none of the tokens Superset exposes, so existing theme configurations continue to work. See the [Ant Design v6 migration guide](https://ant.design/docs/react/migration-v6) for the complete upstream list.
|
||||
|
||||
### Guest-token RLS rules reject unknown fields
|
||||
|
||||
The `rls` rules passed to `POST /api/v1/security/guest_token/` are now validated strictly: a rule may only contain `dataset` and `clause`. Previously unknown fields were silently dropped, so a mistyped or legacy scope key (most commonly `datasource` instead of `dataset`) produced a rule with no `dataset`, which is treated as a *global* rule applied to every dataset the embedded resource can reach. Such a request now returns HTTP 400 identifying the offending field instead of issuing a token with an unintended global rule. Integrators that were sending extra fields in RLS rules must remove them; valid dataset-scoped (`{"dataset": 41, "clause": "..."}`) and global (`{"clause": "..."}`) rules are unaffected.
|
||||
@@ -182,6 +194,10 @@ helm upgrade <release-name> superset/superset
|
||||
|
||||
Alternatively, perform a fresh install. This is a one-time migration; subsequent upgrades are unaffected.
|
||||
|
||||
### Time-series tooltips follow the selected time grain
|
||||
|
||||
Tooltips on the Time-series and Mixed Time-series ECharts plugins now respect the chart's time grain (and any dashboard-level time-grain override delivered via `extra_form_data`) when the tooltip time format is left on Adaptive formatting (the default). Tooltips read grain-appropriate labels such as `Jan 2021` (month), `2021 Q1` (quarter), `2021` (year), and weekly date ranges, becoming grain-aware like the x-axis, though the two are formatted independently and their labels may not always match exactly. Only a custom, explicitly-set tooltip time format (a d3 format string) is unaffected — that always wins over the grain.
|
||||
|
||||
### Pivot table First/Last aggregations follow data order
|
||||
|
||||
The pivot table chart's `First` and `Last` aggregations now return the first and last value in data (query result) order, instead of effectively returning the minimum and maximum. Existing pivot tables that use these aggregations for totals/subtotals may show different values after upgrading. For deterministic results, ensure the underlying query has a stable sort order.
|
||||
@@ -721,6 +737,7 @@ See `superset/mcp_service/PRODUCTION.md` for deployment guides.
|
||||
|
||||
---
|
||||
|
||||
- [38358](https://github.com/apache/superset/pull/38358): Switched CrateDB PyPI package from `crate[sqlalchemy]` to `sqlalchemy-cratedb`.
|
||||
- [35621](https://github.com/apache/superset/pull/35621): The default hash algorithm has changed from MD5 to SHA-256 for improved security and FedRAMP compliance. This affects cache keys for thumbnails, dashboard digests, chart digests, and filter option names. Existing cached data will be invalidated upon upgrade. To opt out of this change and maintain backward compatibility, set `HASH_ALGORITHM = "md5"` in your `superset_config.py`.
|
||||
- [35062](https://github.com/apache/superset/pull/35062): Changed the function signature of `setupExtensions` to `setupCodeOverrides` with options as arguments.
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ Superset sends an HTTP POST with `Content-Type: application/json`:
|
||||
}
|
||||
```
|
||||
|
||||
When a report includes file attachments (CSV, PDF, or PNG screenshots), the request is sent as `multipart/form-data` instead. In that case, each top-level payload field (`name`, `text`, `description`, `url`) becomes its own form field, and nested structures like `header` are serialized as a JSON-encoded string in their own field. Every attachment is added as a repeated form field named `files`:
|
||||
When a report includes file attachments (CSV, Excel, PDF, or PNG screenshots), the request is sent as `multipart/form-data` instead. In that case, each top-level payload field (`name`, `text`, `description`, `url`) becomes its own form field, and nested structures like `header` are serialized as a JSON-encoded string in their own field. Every attachment is added as a repeated form field named `files`:
|
||||
|
||||
```
|
||||
POST /webhook HTTP/1.1
|
||||
@@ -361,7 +361,7 @@ This is the best source of information about the problem. In a docker compose d
|
||||
|
||||
### Check web browser and webdriver installation
|
||||
|
||||
To take a screenshot, the worker visits the dashboard or chart using a headless browser, then takes a screenshot. If you are able to send a chart as CSV or text but can't send as PNG, your problem may lie with the browser.
|
||||
To take a screenshot, the worker visits the dashboard or chart using a headless browser, then takes a screenshot. If you are able to send a chart as CSV, XLSX, or text but can't send as PNG, your problem may lie with the browser.
|
||||
|
||||
If you are handling the installation of the headless browser on your own, do your own verification to ensure that the headless browser opens successfully in the worker environment.
|
||||
|
||||
|
||||
@@ -529,6 +529,7 @@ MCP_RESPONSE_SIZE_CONFIG = {
|
||||
"enabled": True,
|
||||
"token_limit": 25000,
|
||||
"warn_threshold_pct": 80,
|
||||
"max_list_items": 100,
|
||||
"excluded_tools": [
|
||||
"health_check",
|
||||
"get_chart_preview",
|
||||
@@ -543,6 +544,7 @@ MCP_RESPONSE_SIZE_CONFIG = {
|
||||
| `enabled` | `True` | Enable response size checking |
|
||||
| `token_limit` | `25000` | Maximum estimated token count per response |
|
||||
| `warn_threshold_pct` | `80` | Warn when response exceeds this percentage of the limit |
|
||||
| `max_list_items` | `100` | Cap on list-field length (e.g. `charts`, `native_filters`) applied to the `get_*_info` tools before falling back to more aggressive truncation. Raised from a hardcoded 30 in earlier versions; the higher default only keeps more data before the same token-budget fallback kicks in, so it's not a breaking change, but tenants that tuned workflows around the old 30-item cap should lower this value explicitly. |
|
||||
| `excluded_tools` | See above | Tools exempt from size checking (e.g., tools that return URLs, not data) |
|
||||
|
||||
### Caching
|
||||
|
||||
+6
-6
@@ -63,9 +63,9 @@
|
||||
"@swc/core": "^1.15.43",
|
||||
"antd": "^6.5.0",
|
||||
"baseline-browser-mapping": "^2.10.40",
|
||||
"caniuse-lite": "^1.0.30001799",
|
||||
"docusaurus-plugin-openapi-docs": "^5.1.0",
|
||||
"docusaurus-theme-openapi-docs": "^5.1.0",
|
||||
"caniuse-lite": "^1.0.30001803",
|
||||
"docusaurus-plugin-openapi-docs": "^5.1.1",
|
||||
"docusaurus-theme-openapi-docs": "^5.1.1",
|
||||
"js-yaml": "^5.2.0",
|
||||
"json-bigint": "^1.0.0",
|
||||
"prism-react-renderer": "^2.4.1",
|
||||
@@ -90,15 +90,15 @@
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/react": "^19.1.8",
|
||||
"@typescript-eslint/eslint-plugin": "^8.59.3",
|
||||
"@typescript-eslint/parser": "^8.61.0",
|
||||
"@typescript-eslint/parser": "^8.63.0",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-prettier": "^5.5.6",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"globals": "^17.7.0",
|
||||
"prettier": "^3.9.1",
|
||||
"prettier": "^3.9.5",
|
||||
"typescript": "~6.0.3",
|
||||
"typescript-eslint": "^8.62.0",
|
||||
"typescript-eslint": "^8.63.0",
|
||||
"webpack": "^5.108.2"
|
||||
},
|
||||
"browserslist": {
|
||||
|
||||
@@ -3672,7 +3672,7 @@
|
||||
"drivers": [
|
||||
{
|
||||
"name": "crate",
|
||||
"pypi_package": "crate[sqlalchemy]",
|
||||
"pypi_package": "sqlalchemy-cratedb",
|
||||
"connection_string": "crate://{host}:{port}",
|
||||
"is_recommended": true
|
||||
}
|
||||
|
||||
@@ -291,7 +291,7 @@ This is the best source of information about the problem. In a docker compose d
|
||||
|
||||
### Check web browser and webdriver installation
|
||||
|
||||
To take a screenshot, the worker visits the dashboard or chart using a headless browser, then takes a screenshot. If you are able to send a chart as CSV or text but can't send as PNG, your problem may lie with the browser.
|
||||
To take a screenshot, the worker visits the dashboard or chart using a headless browser, then takes a screenshot. If you are able to send a chart as CSV, XLSX, or text but can't send as PNG, your problem may lie with the browser.
|
||||
|
||||
Superset docker images that have a tag ending with `-dev` have the Firefox headless browser and geckodriver already installed. You can test that these are installed and in the proper path by entering your Superset worker and running `firefox --headless` and then `geckodriver`. Both commands should start those applications.
|
||||
|
||||
|
||||
+97
-87
@@ -5504,100 +5504,110 @@
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@8.62.1", "@typescript-eslint/eslint-plugin@^8.59.3":
|
||||
version "8.62.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.1.tgz#1736dcdca6cae3359d818456a47d18b674761f7f"
|
||||
integrity sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA==
|
||||
"@typescript-eslint/eslint-plugin@8.63.0", "@typescript-eslint/eslint-plugin@^8.59.3":
|
||||
version "8.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.63.0.tgz#0d85d0ec1a28b0e35f484cc8220a8bf084019e71"
|
||||
integrity sha512-rvwSgqT+DHpWdzfSzPatRLm02a0GlESt++9iy3hLCDY4BgkaLcl8LBi9Yh7XGFBpwcBE/K3024QuXWTpbz4FfQ==
|
||||
dependencies:
|
||||
"@eslint-community/regexpp" "^4.12.2"
|
||||
"@typescript-eslint/scope-manager" "8.62.1"
|
||||
"@typescript-eslint/type-utils" "8.62.1"
|
||||
"@typescript-eslint/utils" "8.62.1"
|
||||
"@typescript-eslint/visitor-keys" "8.62.1"
|
||||
"@typescript-eslint/scope-manager" "8.63.0"
|
||||
"@typescript-eslint/type-utils" "8.63.0"
|
||||
"@typescript-eslint/utils" "8.63.0"
|
||||
"@typescript-eslint/visitor-keys" "8.63.0"
|
||||
ignore "^7.0.5"
|
||||
natural-compare "^1.4.0"
|
||||
ts-api-utils "^2.5.0"
|
||||
|
||||
"@typescript-eslint/parser@8.62.1", "@typescript-eslint/parser@^8.61.0":
|
||||
version "8.62.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.62.1.tgz#d3f7ba18f1bf78bfb7256fea021d1927b48e7080"
|
||||
integrity sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==
|
||||
"@typescript-eslint/parser@8.63.0", "@typescript-eslint/parser@^8.63.0":
|
||||
version "8.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.63.0.tgz#2993338c379903e6afc72c3532ae6e15b97334d4"
|
||||
integrity sha512-gwh4gvvlaVDKKxyfxMG+Gnu1u9X0OQBwyGLkbwB65dIzBKnxeRiJlNFqlI3zwVhNXJIs6qV7mlFCn/BIajlVig==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "8.62.1"
|
||||
"@typescript-eslint/types" "8.62.1"
|
||||
"@typescript-eslint/typescript-estree" "8.62.1"
|
||||
"@typescript-eslint/visitor-keys" "8.62.1"
|
||||
"@typescript-eslint/scope-manager" "8.63.0"
|
||||
"@typescript-eslint/types" "8.63.0"
|
||||
"@typescript-eslint/typescript-estree" "8.63.0"
|
||||
"@typescript-eslint/visitor-keys" "8.63.0"
|
||||
debug "^4.4.3"
|
||||
|
||||
"@typescript-eslint/project-service@8.62.1":
|
||||
version "8.62.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.62.1.tgz#78d880eb1cf6859b5ec263d04f95403e9f90ae47"
|
||||
integrity sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==
|
||||
"@typescript-eslint/project-service@8.63.0":
|
||||
version "8.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.63.0.tgz#01a3d0550a860127444a9939749ab434591cd71a"
|
||||
integrity sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/tsconfig-utils" "^8.62.1"
|
||||
"@typescript-eslint/types" "^8.62.1"
|
||||
"@typescript-eslint/tsconfig-utils" "^8.63.0"
|
||||
"@typescript-eslint/types" "^8.63.0"
|
||||
debug "^4.4.3"
|
||||
|
||||
"@typescript-eslint/scope-manager@8.62.1":
|
||||
version "8.62.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.62.1.tgz#7ee65e9a6eb3ccdc4816593a4ff38840306de88a"
|
||||
integrity sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==
|
||||
"@typescript-eslint/scope-manager@8.63.0":
|
||||
version "8.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.63.0.tgz#c9cf7ecd234f7ec346f62e5c7d28dfaf4d507b4d"
|
||||
integrity sha512-uUyfMWCnDSN8bCpcrY8nGP2BLkQ9Xn0GsipcONcpIDWhwhO4ZSyHvyS14U3X75mzxWxL3I2UZIrenTzdzcJO8A==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.62.1"
|
||||
"@typescript-eslint/visitor-keys" "8.62.1"
|
||||
"@typescript-eslint/types" "8.63.0"
|
||||
"@typescript-eslint/visitor-keys" "8.63.0"
|
||||
|
||||
"@typescript-eslint/tsconfig-utils@8.62.1", "@typescript-eslint/tsconfig-utils@^8.62.1":
|
||||
version "8.62.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.1.tgz#e2b5f24fe721044189cb7e81117c96d75979d627"
|
||||
integrity sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==
|
||||
"@typescript-eslint/tsconfig-utils@8.63.0":
|
||||
version "8.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.63.0.tgz#f7e1cf9a029bb71f4027ffa4194ba82afb564cd3"
|
||||
integrity sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==
|
||||
|
||||
"@typescript-eslint/type-utils@8.62.1":
|
||||
version "8.62.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.62.1.tgz#ebd30b13bacb13070917259a23309cf644121f9a"
|
||||
integrity sha512-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg==
|
||||
"@typescript-eslint/tsconfig-utils@^8.63.0":
|
||||
version "8.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz#c62ac8ea9173c3cac8b38b8e66e30a046b548851"
|
||||
integrity sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==
|
||||
|
||||
"@typescript-eslint/type-utils@8.63.0":
|
||||
version "8.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.63.0.tgz#9c00f362140186c588da86b3e10c212800b64b85"
|
||||
integrity sha512-Nzzh/OGxVCOjObjaj1CQF2RUasyYy2Jfuh+zZ3PjLzG2fYRriAiZLib9UKtO+CpQAS3YHiAS+ckZDclwqI1TPA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.62.1"
|
||||
"@typescript-eslint/typescript-estree" "8.62.1"
|
||||
"@typescript-eslint/utils" "8.62.1"
|
||||
"@typescript-eslint/types" "8.63.0"
|
||||
"@typescript-eslint/typescript-estree" "8.63.0"
|
||||
"@typescript-eslint/utils" "8.63.0"
|
||||
debug "^4.4.3"
|
||||
ts-api-utils "^2.5.0"
|
||||
|
||||
"@typescript-eslint/types@8.62.1", "@typescript-eslint/types@^8.62.1":
|
||||
version "8.62.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.62.1.tgz#c58be954e483b2fc98275374d5bcb40b99842dc1"
|
||||
integrity sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==
|
||||
"@typescript-eslint/types@8.63.0":
|
||||
version "8.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.63.0.tgz#6b32b0a5913520554d81a986acfffba2d32b6963"
|
||||
integrity sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==
|
||||
|
||||
"@typescript-eslint/typescript-estree@8.62.1":
|
||||
version "8.62.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.1.tgz#98c1bb17635d5b026b24193a8d29188ac64380ff"
|
||||
integrity sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==
|
||||
"@typescript-eslint/types@^8.63.0":
|
||||
version "8.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.64.0.tgz#b41f8ef5dd40616908658b991197a9d486cda60b"
|
||||
integrity sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==
|
||||
|
||||
"@typescript-eslint/typescript-estree@8.63.0":
|
||||
version "8.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.63.0.tgz#a6f9c9cd290e98203ad29850b0d72529dc83be73"
|
||||
integrity sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==
|
||||
dependencies:
|
||||
"@typescript-eslint/project-service" "8.62.1"
|
||||
"@typescript-eslint/tsconfig-utils" "8.62.1"
|
||||
"@typescript-eslint/types" "8.62.1"
|
||||
"@typescript-eslint/visitor-keys" "8.62.1"
|
||||
"@typescript-eslint/project-service" "8.63.0"
|
||||
"@typescript-eslint/tsconfig-utils" "8.63.0"
|
||||
"@typescript-eslint/types" "8.63.0"
|
||||
"@typescript-eslint/visitor-keys" "8.63.0"
|
||||
debug "^4.4.3"
|
||||
minimatch "^10.2.2"
|
||||
semver "^7.7.3"
|
||||
tinyglobby "^0.2.15"
|
||||
ts-api-utils "^2.5.0"
|
||||
|
||||
"@typescript-eslint/utils@8.62.1":
|
||||
version "8.62.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.62.1.tgz#1622b75c7e6df308181dd0b44855dc4228da0457"
|
||||
integrity sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g==
|
||||
"@typescript-eslint/utils@8.63.0":
|
||||
version "8.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.63.0.tgz#b6a6c8aff1cebd1de4410b3a42b7ec9ba6703f23"
|
||||
integrity sha512-fUKaeAvrTuQg/Tgt3nliAUSZHJM6DlCcfyEmxCvlX8kieWSStBX+5O5Fnidtc3i2JrH+9c/GL4RY2iasd/GPTA==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.9.1"
|
||||
"@typescript-eslint/scope-manager" "8.62.1"
|
||||
"@typescript-eslint/types" "8.62.1"
|
||||
"@typescript-eslint/typescript-estree" "8.62.1"
|
||||
"@typescript-eslint/scope-manager" "8.63.0"
|
||||
"@typescript-eslint/types" "8.63.0"
|
||||
"@typescript-eslint/typescript-estree" "8.63.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@8.62.1":
|
||||
version "8.62.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.1.tgz#499657d77ffafb8a99eb1d6c97847ca430234722"
|
||||
integrity sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==
|
||||
"@typescript-eslint/visitor-keys@8.63.0":
|
||||
version "8.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.63.0.tgz#2853a8e33b52f23570338f96cc89cb223daabd44"
|
||||
integrity sha512-UexrHGnGTpbuQHct2ExOc2ZcFbGUS9FOesCxxqdBGcpI1BxYu/LZ6U8Aq6/72XtF/qRBk9nhuGHFJIXXMhPMdw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.62.1"
|
||||
"@typescript-eslint/types" "8.63.0"
|
||||
eslint-visitor-keys "^5.0.0"
|
||||
|
||||
"@ungap/structured-clone@^1.0.0":
|
||||
@@ -6567,10 +6577,10 @@ caniuse-api@^3.0.0:
|
||||
lodash.memoize "^4.1.2"
|
||||
lodash.uniq "^4.5.0"
|
||||
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001799:
|
||||
version "1.0.30001800"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001800.tgz#b896c773e1c39400809415162bb5320371291b36"
|
||||
integrity sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001799, caniuse-lite@^1.0.30001803:
|
||||
version "1.0.30001803"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001803.tgz#b2a5d696e042bc8304dcd4942c39fe330fbbcb24"
|
||||
integrity sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==
|
||||
|
||||
ccount@^2.0.0:
|
||||
version "2.0.1"
|
||||
@@ -7819,10 +7829,10 @@ doctrine@^2.1.0:
|
||||
dependencies:
|
||||
esutils "^2.0.2"
|
||||
|
||||
docusaurus-plugin-openapi-docs@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/docusaurus-plugin-openapi-docs/-/docusaurus-plugin-openapi-docs-5.1.0.tgz#9732f81f45a5bc126bcafcb150332b7623ddece7"
|
||||
integrity sha512-ocRemE3KmdhhPKaow5hja1m1NLIPfNlfRYFt7pja+nG26Wlp0MEC9ERS99gSWEWaxU+txDFBpUXsxo7nGlk8ZA==
|
||||
docusaurus-plugin-openapi-docs@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/docusaurus-plugin-openapi-docs/-/docusaurus-plugin-openapi-docs-5.1.1.tgz#78959b32222095408460aee3ee3e889082766d69"
|
||||
integrity sha512-msmXMA6DuKUgGq2eGlsk++Gq58nvXAFghq4VAH4OCCLqNqtY3lGLJeHa5hMPM12x8gH3wBsZhLWRpC3CZi/mAQ==
|
||||
dependencies:
|
||||
"@apidevtools/json-schema-ref-parser" "^15.3.3"
|
||||
"@redocly/openapi-core" "^2.25.2"
|
||||
@@ -7840,10 +7850,10 @@ docusaurus-plugin-openapi-docs@^5.1.0:
|
||||
swagger2openapi "^7.0.8"
|
||||
xml-formatter "^3.6.6"
|
||||
|
||||
docusaurus-theme-openapi-docs@^5.1.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/docusaurus-theme-openapi-docs/-/docusaurus-theme-openapi-docs-5.1.0.tgz#4b9b7024130bd299a56bffe238c584280851012f"
|
||||
integrity sha512-SLHg1lodIM5AEGo0EJx4pJ+9szDDrN8Q+hNk5qqTVJ60k/zKjHAnwGCzVDT25K1CCMDPaUzuuruj43d7TRoQKw==
|
||||
docusaurus-theme-openapi-docs@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/docusaurus-theme-openapi-docs/-/docusaurus-theme-openapi-docs-5.1.1.tgz#6e404a60942c74dbf31ecabb3bb8276d91de811c"
|
||||
integrity sha512-oGpHomo51qfKD8KM6gEfZRw98o8IRFpOhe3QnMljqy+L69vownXblJEMxZlXUMJML922NbN98tf3Bh4zKXqZTQ==
|
||||
dependencies:
|
||||
"@hookform/error-message" "^2.0.1"
|
||||
"@reduxjs/toolkit" "^2.8.2"
|
||||
@@ -12998,10 +13008,10 @@ prettier-linter-helpers@^1.0.1:
|
||||
dependencies:
|
||||
fast-diff "^1.1.2"
|
||||
|
||||
prettier@^3.9.1:
|
||||
version "3.9.4"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.9.4.tgz#a9c477cf1614376bd1f6bbc593d8c0d414bcec87"
|
||||
integrity sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==
|
||||
prettier@^3.9.5:
|
||||
version "3.9.5"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.9.5.tgz#4fec97736e33b9d0b620b48914fe93b530e835ad"
|
||||
integrity sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==
|
||||
|
||||
pretty-error@^4.0.0:
|
||||
version "4.0.0"
|
||||
@@ -15262,15 +15272,15 @@ types-ramda@^0.30.1:
|
||||
dependencies:
|
||||
ts-toolbelt "^9.6.0"
|
||||
|
||||
typescript-eslint@^8.62.0:
|
||||
version "8.62.1"
|
||||
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.62.1.tgz#eb93fd94d527aa04ec5b844fb0b4ada613cc7d3f"
|
||||
integrity sha512-vymnnM5g0AKQDSAyfP12nMIBvgwgA42syg74kkuZ4x1VuTzwQKwc5h9rGxeShCjny5o+zWAb6OEoz7XLgrIkIw==
|
||||
typescript-eslint@^8.63.0:
|
||||
version "8.63.0"
|
||||
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.63.0.tgz#1c2b65c989572af7113fbae7f54c7ffab0fbeb12"
|
||||
integrity sha512-xgwXyzG4sK9ALkBxbyGkTMMOS+imnW65iPhxCQMK83KhxyoDNW7l+IDqEf9vMdoUidHpOoS967RCq4eMiTexwQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/eslint-plugin" "8.62.1"
|
||||
"@typescript-eslint/parser" "8.62.1"
|
||||
"@typescript-eslint/typescript-estree" "8.62.1"
|
||||
"@typescript-eslint/utils" "8.62.1"
|
||||
"@typescript-eslint/eslint-plugin" "8.63.0"
|
||||
"@typescript-eslint/parser" "8.63.0"
|
||||
"@typescript-eslint/typescript-estree" "8.63.0"
|
||||
"@typescript-eslint/utils" "8.63.0"
|
||||
|
||||
typescript@~6.0.3:
|
||||
version "6.0.3"
|
||||
|
||||
@@ -29,7 +29,7 @@ maintainers:
|
||||
- name: craig-rueda
|
||||
email: craig@craigrueda.com
|
||||
url: https://github.com/craig-rueda
|
||||
version: 0.20.0 # See [README](https://github.com/apache/superset/blob/master/helm/superset/README.md#versioning) for version details.
|
||||
version: 0.21.0 # See [README](https://github.com/apache/superset/blob/master/helm/superset/README.md#versioning) for version details.
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
version: 16.7.27
|
||||
|
||||
@@ -23,7 +23,7 @@ NOTE: This file is generated by helm-docs: https://github.com/norwoodj/helm-docs
|
||||
|
||||
# superset
|
||||
|
||||

|
||||

|
||||
|
||||
Apache Superset is a modern, enterprise-ready business intelligence web application
|
||||
|
||||
@@ -149,6 +149,7 @@ Alternatively, perform a fresh install. This is a one-time migration; subsequent
|
||||
| init.createAdmin | bool | `true` | |
|
||||
| init.enabled | bool | `true` | |
|
||||
| init.extraContainers | list | `[]` | Launch additional containers into init job pod |
|
||||
| init.extraInitContainers | list | `[]` | Extra init containers appended after init job initContainers |
|
||||
| init.initContainers | list | a container waiting for postgres | List of initContainers |
|
||||
| init.initscript | string | unused; kept for backwards-compatibility only | DEPRECATED: this field is no longer used by the chart. The init script is rendered entirely from the internal `superset.initScript` template (which runs `superset db upgrade`, `superset init`, admin creation, and examples). Any customization placed here is silently ignored. See UPGRADING.md. |
|
||||
| init.jobAnnotations."helm.sh/hook" | string | `"post-install,post-upgrade"` | |
|
||||
@@ -186,6 +187,7 @@ Alternatively, perform a fresh install. This is a one-time migration; subsequent
|
||||
| supersetCeleryBeat.deploymentAnnotations | object | `{}` | Annotations to be added to supersetCeleryBeat deployment |
|
||||
| supersetCeleryBeat.enabled | bool | `false` | This is only required if you intend to use alerts and reports |
|
||||
| supersetCeleryBeat.extraContainers | list | `[]` | Launch additional containers into supersetCeleryBeat pods |
|
||||
| supersetCeleryBeat.extraInitContainers | list | `[]` | Extra init containers appended after supersetCeleryBeat initContainers |
|
||||
| supersetCeleryBeat.forceReload | bool | `false` | If true, forces deployment to reload on each upgrade |
|
||||
| supersetCeleryBeat.initContainers | list | a container waiting for postgres | List of init containers |
|
||||
| supersetCeleryBeat.podAnnotations | object | `{}` | Annotations to be added to supersetCeleryBeat pods |
|
||||
@@ -205,6 +207,7 @@ Alternatively, perform a fresh install. This is a one-time migration; subsequent
|
||||
| supersetCeleryFlower.deploymentAnnotations | object | `{}` | Annotations to be added to supersetCeleryFlower deployment |
|
||||
| supersetCeleryFlower.enabled | bool | `false` | Enables a Celery flower deployment (management UI to monitor celery jobs) WARNING: on superset 1.x, this requires a Superset image that has `flower<1.0.0` installed (which is NOT the case of the default images) flower>=1.0.0 requires Celery 5+ which Superset 1.5 does not support |
|
||||
| supersetCeleryFlower.extraContainers | list | `[]` | Launch additional containers into supersetCeleryFlower pods |
|
||||
| supersetCeleryFlower.extraInitContainers | list | `[]` | Extra init containers appended after supersetCeleryFlower initContainers |
|
||||
| supersetCeleryFlower.initContainers | list | a container waiting for postgres and redis | List of init containers |
|
||||
| supersetCeleryFlower.livenessProbe.failureThreshold | int | `3` | |
|
||||
| supersetCeleryFlower.livenessProbe.httpGet.path | string | `"/api/workers"` | |
|
||||
@@ -256,6 +259,7 @@ Alternatively, perform a fresh install. This is a one-time migration; subsequent
|
||||
| supersetNode.deploymentLabels | object | `{}` | Labels to be added to supersetNode deployment |
|
||||
| supersetNode.env | object | `{}` | |
|
||||
| supersetNode.extraContainers | list | `[]` | Launch additional containers into supersetNode pod |
|
||||
| supersetNode.extraInitContainers | list | `[]` | Extra init containers appended after supersetNode initContainers |
|
||||
| supersetNode.forceReload | bool | `false` | If true, forces deployment to reload on each upgrade |
|
||||
| supersetNode.initContainers | list | a container waiting for postgres | Init containers |
|
||||
| supersetNode.lifecycle | object | `{}` | Container lifecycle hooks, e.g. a preStop sleep so the Service/Ingress stops routing to the pod before gunicorn receives SIGTERM |
|
||||
@@ -355,6 +359,7 @@ Alternatively, perform a fresh install. This is a one-time migration; subsequent
|
||||
| supersetWorker.deploymentAnnotations | object | `{}` | Annotations to be added to supersetWorker deployment |
|
||||
| supersetWorker.deploymentLabels | object | `{}` | Labels to be added to supersetWorker deployment |
|
||||
| supersetWorker.extraContainers | list | `[]` | Launch additional containers into supersetWorker pod |
|
||||
| supersetWorker.extraInitContainers | list | `[]` | Extra init containers appended after supersetWorker initContainers |
|
||||
| supersetWorker.forceReload | bool | `false` | If true, forces deployment to reload on each upgrade |
|
||||
| supersetWorker.healthCheck | object | `{"enabled":false,"livenessFile":"/tmp/celery_worker_alive","livenessHeartbeatInterval":10,"readinessFile":"/tmp/celery_worker_ready"}` | Celery worker file-based health check (worker writes readiness/liveness files via signals; point supersetWorker.readinessProbe/livenessProbe at these files to use them) |
|
||||
| supersetWorker.healthCheck.enabled | bool | `false` | Enable the file-based Celery worker health check |
|
||||
|
||||
@@ -75,8 +75,14 @@ spec:
|
||||
{{- if .Values.supersetCeleryBeat.podSecurityContext }}
|
||||
{{- toYaml .Values.supersetCeleryBeat.podSecurityContext | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if or .Values.supersetCeleryBeat.initContainers .Values.supersetCeleryBeat.extraInitContainers }}
|
||||
initContainers:
|
||||
{{- if .Values.supersetCeleryBeat.initContainers }}
|
||||
initContainers: {{- tpl (toYaml .Values.supersetCeleryBeat.initContainers) . | nindent 6 }}
|
||||
{{- tpl (toYaml .Values.supersetCeleryBeat.initContainers) . | nindent 6 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetCeleryBeat.extraInitContainers }}
|
||||
{{- tpl (toYaml .Values.supersetCeleryBeat.extraInitContainers) . | nindent 6 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.hostAliases }}
|
||||
hostAliases: {{- toYaml . | nindent 6 }}
|
||||
|
||||
@@ -61,8 +61,14 @@ spec:
|
||||
{{- if .Values.supersetCeleryFlower.podSecurityContext }}
|
||||
{{- toYaml .Values.supersetCeleryFlower.podSecurityContext | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if or .Values.supersetCeleryFlower.initContainers .Values.supersetCeleryFlower.extraInitContainers }}
|
||||
initContainers:
|
||||
{{- if .Values.supersetCeleryFlower.initContainers }}
|
||||
initContainers: {{- tpl (toYaml .Values.supersetCeleryFlower.initContainers) . | nindent 6 }}
|
||||
{{- tpl (toYaml .Values.supersetCeleryFlower.initContainers) . | nindent 6 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetCeleryFlower.extraInitContainers }}
|
||||
{{- tpl (toYaml .Values.supersetCeleryFlower.extraInitContainers) . | nindent 6 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.hostAliases }}
|
||||
hostAliases: {{- toYaml . | nindent 6 }}
|
||||
|
||||
@@ -81,8 +81,14 @@ spec:
|
||||
{{- if .Values.supersetWorker.podSecurityContext }}
|
||||
{{- toYaml .Values.supersetWorker.podSecurityContext | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if or .Values.supersetWorker.initContainers .Values.supersetWorker.extraInitContainers }}
|
||||
initContainers:
|
||||
{{- if .Values.supersetWorker.initContainers }}
|
||||
initContainers: {{- tpl (toYaml .Values.supersetWorker.initContainers) . | nindent 6 }}
|
||||
{{- tpl (toYaml .Values.supersetWorker.initContainers) . | nindent 6 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetWorker.extraInitContainers }}
|
||||
{{- tpl (toYaml .Values.supersetWorker.extraInitContainers) . | nindent 6 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.hostAliases }}
|
||||
hostAliases: {{- toYaml . | nindent 6 }}
|
||||
|
||||
@@ -83,8 +83,14 @@ spec:
|
||||
{{- if .Values.supersetNode.podSecurityContext }}
|
||||
{{- toYaml .Values.supersetNode.podSecurityContext | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if or .Values.supersetNode.initContainers .Values.supersetNode.extraInitContainers }}
|
||||
initContainers:
|
||||
{{- if .Values.supersetNode.initContainers }}
|
||||
initContainers: {{- tpl (toYaml .Values.supersetNode.initContainers) . | nindent 6 }}
|
||||
{{- tpl (toYaml .Values.supersetNode.initContainers) . | nindent 6 }}
|
||||
{{- end }}
|
||||
{{- if .Values.supersetNode.extraInitContainers }}
|
||||
{{- tpl (toYaml .Values.supersetNode.extraInitContainers) . | nindent 6 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.hostAliases }}
|
||||
hostAliases: {{- toYaml . | nindent 6 }}
|
||||
|
||||
@@ -56,8 +56,14 @@ spec:
|
||||
{{- if .Values.init.podSecurityContext }}
|
||||
{{- toYaml .Values.init.podSecurityContext | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if or .Values.init.initContainers .Values.init.extraInitContainers }}
|
||||
initContainers:
|
||||
{{- if .Values.init.initContainers }}
|
||||
initContainers: {{- tpl (toYaml .Values.init.initContainers) . | nindent 6 }}
|
||||
{{- tpl (toYaml .Values.init.initContainers) . | nindent 6 }}
|
||||
{{- end }}
|
||||
{{- if .Values.init.extraInitContainers }}
|
||||
{{- tpl (toYaml .Values.init.extraInitContainers) . | nindent 6 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.hostAliases }}
|
||||
hostAliases: {{- toYaml . | nindent 6 }}
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
suite: init containers + extraInitContainers
|
||||
templates:
|
||||
- deployment.yaml
|
||||
- deployment-worker.yaml
|
||||
- deployment-beat.yaml
|
||||
- deployment-flower.yaml
|
||||
- init-job.yaml
|
||||
tests:
|
||||
- it: renders only the default wait-for container by default
|
||||
template: deployment.yaml
|
||||
asserts:
|
||||
- lengthEqual:
|
||||
path: spec.template.spec.initContainers
|
||||
count: 1
|
||||
- equal:
|
||||
path: spec.template.spec.initContainers[0].name
|
||||
value: wait-for-postgres
|
||||
|
||||
- it: appends extraInitContainers after the default initContainers
|
||||
template: deployment.yaml
|
||||
set:
|
||||
supersetNode.extraInitContainers:
|
||||
- name: prepare-assets
|
||||
image: busybox
|
||||
command: ["sh", "-c", "echo preparing"]
|
||||
asserts:
|
||||
- lengthEqual:
|
||||
path: spec.template.spec.initContainers
|
||||
count: 2
|
||||
- equal:
|
||||
path: spec.template.spec.initContainers[0].name
|
||||
value: wait-for-postgres
|
||||
- equal:
|
||||
path: spec.template.spec.initContainers[1].name
|
||||
value: prepare-assets
|
||||
|
||||
- it: templates values inside extraInitContainers
|
||||
template: deployment.yaml
|
||||
set:
|
||||
image.repository: example/superset
|
||||
supersetNode.extraInitContainers:
|
||||
- name: prepare-assets
|
||||
image: "{{ .Values.image.repository }}:test"
|
||||
asserts:
|
||||
- equal:
|
||||
path: spec.template.spec.initContainers[1].image
|
||||
value: example/superset:test
|
||||
|
||||
- it: keeps replace semantics for initContainers while appending extras
|
||||
template: deployment.yaml
|
||||
set:
|
||||
supersetNode.initContainers:
|
||||
- name: custom-wait
|
||||
image: busybox
|
||||
supersetNode.extraInitContainers:
|
||||
- name: prepare-assets
|
||||
image: busybox
|
||||
asserts:
|
||||
- lengthEqual:
|
||||
path: spec.template.spec.initContainers
|
||||
count: 2
|
||||
- equal:
|
||||
path: spec.template.spec.initContainers[0].name
|
||||
value: custom-wait
|
||||
- equal:
|
||||
path: spec.template.spec.initContainers[1].name
|
||||
value: prepare-assets
|
||||
|
||||
- it: renders only extras when initContainers is cleared
|
||||
template: deployment.yaml
|
||||
set:
|
||||
supersetNode.initContainers: []
|
||||
supersetNode.extraInitContainers:
|
||||
- name: prepare-assets
|
||||
image: busybox
|
||||
asserts:
|
||||
- lengthEqual:
|
||||
path: spec.template.spec.initContainers
|
||||
count: 1
|
||||
- equal:
|
||||
path: spec.template.spec.initContainers[0].name
|
||||
value: prepare-assets
|
||||
|
||||
- it: appends worker extraInitContainers after wait-for-postgres-redis
|
||||
template: deployment-worker.yaml
|
||||
set:
|
||||
supersetWorker.extraInitContainers:
|
||||
- name: prepare-assets
|
||||
image: busybox
|
||||
asserts:
|
||||
- lengthEqual:
|
||||
path: spec.template.spec.initContainers
|
||||
count: 2
|
||||
- equal:
|
||||
path: spec.template.spec.initContainers[0].name
|
||||
value: wait-for-postgres-redis
|
||||
- equal:
|
||||
path: spec.template.spec.initContainers[1].name
|
||||
value: prepare-assets
|
||||
|
||||
- it: appends beat extraInitContainers after wait-for-postgres-redis
|
||||
template: deployment-beat.yaml
|
||||
set:
|
||||
supersetCeleryBeat.enabled: true
|
||||
supersetCeleryBeat.extraInitContainers:
|
||||
- name: prepare-assets
|
||||
image: busybox
|
||||
asserts:
|
||||
- lengthEqual:
|
||||
path: spec.template.spec.initContainers
|
||||
count: 2
|
||||
- equal:
|
||||
path: spec.template.spec.initContainers[0].name
|
||||
value: wait-for-postgres-redis
|
||||
- equal:
|
||||
path: spec.template.spec.initContainers[1].name
|
||||
value: prepare-assets
|
||||
|
||||
- it: appends flower extraInitContainers after wait-for-postgres-redis
|
||||
template: deployment-flower.yaml
|
||||
set:
|
||||
supersetCeleryFlower.enabled: true
|
||||
supersetCeleryFlower.extraInitContainers:
|
||||
- name: prepare-assets
|
||||
image: busybox
|
||||
asserts:
|
||||
- lengthEqual:
|
||||
path: spec.template.spec.initContainers
|
||||
count: 2
|
||||
- equal:
|
||||
path: spec.template.spec.initContainers[0].name
|
||||
value: wait-for-postgres-redis
|
||||
- equal:
|
||||
path: spec.template.spec.initContainers[1].name
|
||||
value: prepare-assets
|
||||
|
||||
- it: appends init job extraInitContainers after wait-for-postgres
|
||||
template: init-job.yaml
|
||||
set:
|
||||
init.extraInitContainers:
|
||||
- name: prepare-assets
|
||||
image: busybox
|
||||
asserts:
|
||||
- lengthEqual:
|
||||
path: spec.template.spec.initContainers
|
||||
count: 2
|
||||
- equal:
|
||||
path: spec.template.spec.initContainers[0].name
|
||||
value: wait-for-postgres
|
||||
- equal:
|
||||
path: spec.template.spec.initContainers[1].name
|
||||
value: prepare-assets
|
||||
@@ -415,6 +415,8 @@ supersetNode:
|
||||
cpu: "250m"
|
||||
memory: "128Mi"
|
||||
|
||||
# -- Extra init containers appended after supersetNode initContainers
|
||||
extraInitContainers: []
|
||||
# -- Launch additional containers into supersetNode pod
|
||||
extraContainers: []
|
||||
# -- Annotations to be added to supersetNode deployment
|
||||
@@ -542,6 +544,8 @@ supersetWorker:
|
||||
requests:
|
||||
cpu: "250m"
|
||||
memory: "128Mi"
|
||||
# -- Extra init containers appended after supersetWorker initContainers
|
||||
extraInitContainers: []
|
||||
# -- Launch additional containers into supersetWorker pod
|
||||
extraContainers: []
|
||||
# -- Annotations to be added to supersetWorker deployment
|
||||
@@ -663,6 +667,8 @@ supersetCeleryBeat:
|
||||
requests:
|
||||
cpu: "250m"
|
||||
memory: "128Mi"
|
||||
# -- Extra init containers appended after supersetCeleryBeat initContainers
|
||||
extraInitContainers: []
|
||||
# -- Launch additional containers into supersetCeleryBeat pods
|
||||
extraContainers: []
|
||||
# -- Annotations to be added to supersetCeleryBeat deployment
|
||||
@@ -780,6 +786,8 @@ supersetCeleryFlower:
|
||||
requests:
|
||||
cpu: "250m"
|
||||
memory: "128Mi"
|
||||
# -- Extra init containers appended after supersetCeleryFlower initContainers
|
||||
extraInitContainers: []
|
||||
# -- Launch additional containers into supersetCeleryFlower pods
|
||||
extraContainers: []
|
||||
# -- Annotations to be added to supersetCeleryFlower deployment
|
||||
@@ -967,6 +975,8 @@ init:
|
||||
requests:
|
||||
cpu: "250m"
|
||||
memory: "128Mi"
|
||||
# -- Extra init containers appended after init job initContainers
|
||||
extraInitContainers: []
|
||||
# -- DEPRECATED: this field is no longer used by the chart.
|
||||
# The init script is rendered entirely from the internal `superset.initScript` template
|
||||
# (which runs `superset db upgrade`, `superset init`, admin creation, and examples).
|
||||
|
||||
+12
-12
@@ -43,9 +43,9 @@ dependencies = [
|
||||
# ``google-auth`` 2.53+ dropped it, so Superset must declare it
|
||||
# explicitly to keep fresh ``pip install apache-superset`` working
|
||||
# without the ``base.txt`` lock file (#40962).
|
||||
"cachetools>=6.2.1, <7",
|
||||
"cachetools>=7.1.4, <8",
|
||||
"celery>=5.6.3, <6.0.0",
|
||||
"click>=8.4.0",
|
||||
"click>=8.4.2",
|
||||
"click-option-group",
|
||||
"colorama",
|
||||
"flask-cors>=6.0.5, <7.0",
|
||||
@@ -67,7 +67,7 @@ dependencies = [
|
||||
"gunicorn>=26.0.0, <27; sys_platform != 'win32'",
|
||||
"hashids>=1.3.1, <2",
|
||||
# holidays>=0.45 required for security fix
|
||||
"holidays>=0.45, <1",
|
||||
"holidays>=0.99, <1",
|
||||
"humanize",
|
||||
"isodate",
|
||||
"jsonpath-ng>=1.8.0, <2",
|
||||
@@ -100,7 +100,7 @@ dependencies = [
|
||||
"pyarrow>=24.0.0, <25", # before upgrading pyarrow, check that all db dependencies support this, see e.g. https://github.com/apache/superset/pull/34693
|
||||
"pyyaml>=6.0.3, <7.0.0",
|
||||
"PyJWT>=2.4.0, <3.0",
|
||||
"redis>=5.0.0, <6.0",
|
||||
"redis>=5.0.0, <9.0",
|
||||
"rison>=2.0.1, <3.0",
|
||||
"selenium>=4.45.0, <5.0",
|
||||
"shillelagh[gsheetsapi]>=1.4.4, <2.0",
|
||||
@@ -110,7 +110,7 @@ dependencies = [
|
||||
"sqlalchemy>=1.4, <2",
|
||||
"sqlalchemy-continuum>=1.6.0, <2.0.0",
|
||||
"sqlalchemy-utils>=0.42.1, <0.43", # expanding lowerbound to work with pydoris
|
||||
"sqlglot>=30.8.0, <31",
|
||||
"sqlglot>=30.12.0, <31",
|
||||
# newer pandas needs 0.9+
|
||||
"tabulate>=0.10.0, <1.0",
|
||||
"typing-extensions>=4, <5",
|
||||
@@ -130,7 +130,7 @@ bigquery = [
|
||||
"sqlalchemy-bigquery>=1.17.0",
|
||||
"google-cloud-bigquery>=3.42.1",
|
||||
]
|
||||
clickhouse = ["clickhouse-connect>=1.1.1, <2.0"]
|
||||
clickhouse = ["clickhouse-connect>=1.4.2, <2.0"]
|
||||
cockroachdb = ["cockroachdb>=0.3.5, <0.4"]
|
||||
crate = ["sqlalchemy-cratedb>=0.41.0, <1"]
|
||||
d1 = [
|
||||
@@ -155,7 +155,7 @@ elasticsearch = ["elasticsearch-dbapi>=0.2.13, <0.3.0"]
|
||||
exasol = ["sqlalchemy-exasol>=2.4.0, <8.0"]
|
||||
excel = ["xlrd>=2.0.2, <2.1"]
|
||||
fastmcp = [
|
||||
"fastmcp>=3.4.2,<4.0",
|
||||
"fastmcp>=3.4.3,<4.0",
|
||||
# tiktoken backs the response-size-guard token estimator. Without
|
||||
# it, the middleware falls back to a coarser character-based
|
||||
# heuristic that under-counts JSON-heavy MCP responses.
|
||||
@@ -165,7 +165,7 @@ firebird = ["sqlalchemy-firebird>=0.8.0, <2.2"]
|
||||
firebolt = ["firebolt-sqlalchemy>=1.0.0, <2"]
|
||||
gevent = ["gevent>=26.4.0"]
|
||||
gsheets = ["shillelagh[gsheetsapi]>=1.4.4, <2"]
|
||||
hana = ["hdbcli==2.28.21", "sqlalchemy_hana==3.0.3"]
|
||||
hana = ["hdbcli==2.29.25", "sqlalchemy_hana==3.0.3"]
|
||||
hive = [
|
||||
"pyhive[hive]>=0.6.5;python_version<'3.11'",
|
||||
"pyhive[hive_pure_sasl]>=0.7.0",
|
||||
@@ -192,14 +192,14 @@ pinot = ["pinotdb>=5.0.0, <10.0.0"]
|
||||
playwright = ["playwright>=1.61.0, <2"]
|
||||
postgres = ["psycopg2-binary==2.9.12"]
|
||||
presto = ["pyhive[presto]>=0.6.5"]
|
||||
trino = ["trino>=0.337.0"]
|
||||
trino = ["trino>=0.338.0"]
|
||||
prophet = ["prophet>=1.1.6, <2"]
|
||||
redshift = ["sqlalchemy-redshift>=0.8.1, <0.9"]
|
||||
risingwave = ["sqlalchemy-risingwave"]
|
||||
shillelagh = ["shillelagh[all]>=1.4.4, <2"]
|
||||
singlestore = ["sqlalchemy-singlestoredb>=1.2.1, <2"]
|
||||
snowflake = ["snowflake-sqlalchemy>=1.10.2, <2"]
|
||||
sqlite = ["syntaqlite>=0.1.0,<0.5.0"]
|
||||
sqlite = ["syntaqlite>=0.7.0,<0.8.0"]
|
||||
spark = [
|
||||
"pyhive[hive]>=0.6.5;python_version<'3.11'",
|
||||
"pyhive[hive_pure_sasl]>=0.7",
|
||||
@@ -215,7 +215,7 @@ thumbnails = [] # deprecated, will be removed in 7.0
|
||||
vertica = ["sqlalchemy-vertica-python>= 0.6.3, < 0.7"]
|
||||
netezza = ["nzalchemy>=11.0.2, < 11.2"]
|
||||
starrocks = ["starrocks>=1.3.3, <2"]
|
||||
doris = ["pydoris>=1.0.0, <2.0.0"]
|
||||
doris = ["pydoris>=1.2.0, <2.0.0"]
|
||||
oceanbase = ["oceanbase_py>=0.0.1.2"]
|
||||
ydb = ["ydb-sqlalchemy>=0.1.22", "ydb-sqlglot-plugin>=0.2.8"]
|
||||
development = [
|
||||
@@ -244,7 +244,7 @@ development = [
|
||||
"ruff",
|
||||
"sqloxide",
|
||||
"statsd",
|
||||
"syntaqlite>=0.4.2,<0.5.0",
|
||||
"syntaqlite>=0.7.0,<0.8.0",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
|
||||
+5
-6
@@ -35,13 +35,12 @@ filterwarnings =
|
||||
# error:"SqlMetric" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"TableColumn" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"TaggedObject" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The ``as_declarative\(\)`` function is now available:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The autoload parameter is deprecated:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The connection.execute\(\) method:sqlalchemy.exc.RemovedIn20Warning
|
||||
error:The autoload parameter is deprecated:sqlalchemy.exc.RemovedIn20Warning
|
||||
error:The current statement is being autocommitted using implicit autocommit:sqlalchemy.exc.RemovedIn20Warning
|
||||
error:The connection.execute\(\) method:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The current statement is being autocommitted using implicit autocommit:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The `database` package is deprecated:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The ``declarative_base\(\)`` function is now available:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:The Engine.execute\(\) method is considered legacy:sqlalchemy.exc.RemovedIn20Warning
|
||||
error:The ``declarative_base\(\)`` function is now available:sqlalchemy.exc.RemovedIn20Warning
|
||||
error:The Engine.execute\(\) method is considered legacy:sqlalchemy.exc.RemovedIn20Warning
|
||||
error:The legacy calling style of select\(\) is deprecated:sqlalchemy.exc.RemovedIn20Warning
|
||||
error:The "whens" argument to case:sqlalchemy.exc.RemovedIn20Warning
|
||||
# error:"User" object is being merged into a Session:sqlalchemy.exc.RemovedIn20Warning
|
||||
|
||||
@@ -44,7 +44,7 @@ cachelib==0.13.0
|
||||
# via
|
||||
# flask-caching
|
||||
# flask-session
|
||||
cachetools==6.2.1
|
||||
cachetools==7.1.4
|
||||
# via apache-superset (pyproject.toml)
|
||||
cattrs==25.1.1
|
||||
# via requests-cache
|
||||
@@ -60,7 +60,7 @@ cffi==2.0.0
|
||||
# pynacl
|
||||
charset-normalizer==3.4.2
|
||||
# via requests
|
||||
click==8.4.1
|
||||
click==8.4.2
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# celery
|
||||
@@ -175,7 +175,7 @@ h11==0.16.0
|
||||
# via wsproto
|
||||
hashids==1.3.1
|
||||
# via apache-superset (pyproject.toml)
|
||||
holidays==0.82
|
||||
holidays==0.100
|
||||
# via apache-superset (pyproject.toml)
|
||||
humanize==4.12.3
|
||||
# via apache-superset (pyproject.toml)
|
||||
@@ -321,7 +321,6 @@ pyjwt==2.13.0
|
||||
# apache-superset (pyproject.toml)
|
||||
# flask-appbuilder
|
||||
# flask-jwt-extended
|
||||
# redis
|
||||
pynacl==1.6.2
|
||||
# via paramiko
|
||||
pyopenssl==26.3.0
|
||||
@@ -353,7 +352,7 @@ pyyaml==6.0.3
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# apispec
|
||||
redis==5.3.1
|
||||
redis==8.0.1
|
||||
# via apache-superset (pyproject.toml)
|
||||
referencing==0.36.2
|
||||
# via
|
||||
@@ -414,7 +413,7 @@ sqlalchemy-utils==0.42.1
|
||||
# apache-superset (pyproject.toml)
|
||||
# apache-superset-core
|
||||
# flask-appbuilder
|
||||
sqlglot==30.8.0
|
||||
sqlglot==30.12.0
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# apache-superset-core
|
||||
|
||||
@@ -97,7 +97,7 @@ cachelib==0.13.0
|
||||
# -c requirements/base-constraint.txt
|
||||
# flask-caching
|
||||
# flask-session
|
||||
cachetools==6.2.1
|
||||
cachetools==7.1.4
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -130,7 +130,7 @@ charset-normalizer==3.4.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# requests
|
||||
click==8.4.1
|
||||
click==8.4.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -238,9 +238,9 @@ et-xmlfile==2.0.0
|
||||
# openpyxl
|
||||
exceptiongroup==1.3.0
|
||||
# via fastmcp-slim
|
||||
fastmcp==3.4.2
|
||||
fastmcp==3.4.4
|
||||
# via apache-superset
|
||||
fastmcp-slim==3.4.2
|
||||
fastmcp-slim==3.4.4
|
||||
# via fastmcp
|
||||
filelock==3.20.3
|
||||
# via
|
||||
@@ -407,7 +407,7 @@ hashids==1.3.1
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
holidays==0.82
|
||||
holidays==0.100
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -784,7 +784,6 @@ pyjwt==2.13.0
|
||||
# flask-appbuilder
|
||||
# flask-jwt-extended
|
||||
# mcp
|
||||
# redis
|
||||
pylint==3.3.7
|
||||
# via apache-superset
|
||||
pynacl==1.6.2
|
||||
@@ -870,7 +869,7 @@ pyyaml==6.0.3
|
||||
# fastmcp-slim
|
||||
# jsonschema-path
|
||||
# pre-commit
|
||||
redis==5.3.1
|
||||
redis==8.0.1
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -998,7 +997,7 @@ sqlalchemy-utils==0.42.1
|
||||
# apache-superset
|
||||
# apache-superset-core
|
||||
# flask-appbuilder
|
||||
sqlglot==30.8.0
|
||||
sqlglot==30.12.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -1017,7 +1016,7 @@ starlette==1.3.1
|
||||
# mcp
|
||||
statsd==4.0.1
|
||||
# via apache-superset
|
||||
syntaqlite==0.4.2
|
||||
syntaqlite==0.7.1
|
||||
# via apache-superset
|
||||
tabulate==0.10.0
|
||||
# via
|
||||
@@ -1033,7 +1032,7 @@ tqdm==4.67.1
|
||||
# via
|
||||
# cmdstanpy
|
||||
# prophet
|
||||
trino==0.337.0
|
||||
trino==0.338.0
|
||||
# via apache-superset
|
||||
trio==0.33.0
|
||||
# via
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# 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.
|
||||
@@ -153,10 +153,11 @@ def main( # noqa: C901
|
||||
)
|
||||
|
||||
print(f"Migration goes from {down_revision} to {revision}")
|
||||
current_revision = db.engine.execute(
|
||||
text("SELECT version_num FROM alembic_version")
|
||||
).scalar()
|
||||
print(f"Current version of the DB is {current_revision}")
|
||||
with db.engine.connect() as conn:
|
||||
current_revision = conn.execute(
|
||||
text("SELECT version_num FROM alembic_version")
|
||||
).scalar()
|
||||
print(f"Current version of the DB is {current_revision}")
|
||||
|
||||
if current_revision != down_revision:
|
||||
if not force:
|
||||
|
||||
@@ -20,9 +20,21 @@ import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
from typing import List, Optional
|
||||
from urllib.error import HTTPError, URLError
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
# The change detector gates the entire CI matrix, so a single transient GitHub
|
||||
# API hiccup should not fail the build. Retry server errors and network blips
|
||||
# with exponential backoff before giving up.
|
||||
MAX_RETRIES: int = 4
|
||||
RETRY_BACKOFF_SECONDS: int = 2
|
||||
REQUEST_TIMEOUT_SECONDS: int = 30
|
||||
# GitHub returns 429 (and 403 for secondary rate limits) when throttling, which
|
||||
# is transient and worth retrying alongside 5xx server errors.
|
||||
RETRYABLE_STATUS_CODES: frozenset[int] = frozenset({403, 429})
|
||||
|
||||
# Define patterns for each group of files you're interested in
|
||||
PATTERNS = {
|
||||
"python": [
|
||||
@@ -57,15 +69,34 @@ GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN")
|
||||
|
||||
|
||||
def fetch_files_github_api(url: str): # type: ignore
|
||||
"""Fetches data using GitHub API."""
|
||||
"""Fetches data using GitHub API, retrying on transient failures."""
|
||||
req = Request(url) # noqa: S310
|
||||
req.add_header("Authorization", f"Bearer {GITHUB_TOKEN}")
|
||||
req.add_header("Accept", "application/vnd.github.v3+json")
|
||||
|
||||
print(f"Fetching from {url}")
|
||||
with urlopen(req) as response: # noqa: S310
|
||||
body = response.read()
|
||||
return json.loads(body)
|
||||
for attempt in range(1, MAX_RETRIES + 1):
|
||||
try:
|
||||
with urlopen(req, timeout=REQUEST_TIMEOUT_SECONDS) as response: # noqa: S310
|
||||
body = response.read()
|
||||
return json.loads(body)
|
||||
except (HTTPError, URLError) as err:
|
||||
# Retry transient failures: network errors (URLError has no status
|
||||
# code), 5xx server errors, and GitHub rate-limit responses. Other
|
||||
# 4xx client errors are deterministic, so re-raise immediately. Also
|
||||
# re-raise once the retry budget is exhausted.
|
||||
status = getattr(err, "code", None)
|
||||
is_transient = (
|
||||
status is None or status >= 500 or status in RETRYABLE_STATUS_CODES
|
||||
)
|
||||
if not is_transient or attempt == MAX_RETRIES:
|
||||
raise
|
||||
wait = RETRY_BACKOFF_SECONDS * 2 ** (attempt - 1)
|
||||
print(
|
||||
f"Attempt {attempt}/{MAX_RETRIES} failed ({err}); "
|
||||
f"retrying in {wait}s..."
|
||||
)
|
||||
time.sleep(wait)
|
||||
|
||||
|
||||
def fetch_changed_files_pr(repo: str, pr_number: str) -> List[str]:
|
||||
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# 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.
|
||||
|
||||
set -e
|
||||
|
||||
script_dir="$(dirname "$(realpath "$0")")"
|
||||
root_dir="$(dirname "$script_dir")"
|
||||
frontend_dir=superset-frontend
|
||||
|
||||
if [[ ! -d "$root_dir/$frontend_dir" ]]; then
|
||||
echo "Error: $frontend_dir directory not found in $root_dir" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$root_dir/$frontend_dir"
|
||||
|
||||
# Filter files to only include JS/TS files and remove the frontend dir prefix
|
||||
js_ts_files=()
|
||||
for file in "$@"; do
|
||||
# Remove superset-frontend/ prefix if present
|
||||
cleaned_file="${file#$frontend_dir/}"
|
||||
|
||||
# Only include JS/TS files (Emotion styles live in these)
|
||||
if [[ "$cleaned_file" =~ \.(js|jsx|ts|tsx)$ ]]; then
|
||||
js_ts_files+=("$cleaned_file")
|
||||
fi
|
||||
done
|
||||
|
||||
# Only run if we have JS/TS files to lint. --allow-empty-input keeps the hook
|
||||
# green when every passed file is covered by .stylelintignore.
|
||||
if [ ${#js_ts_files[@]} -gt 0 ]; then
|
||||
npx stylelint --allow-empty-input "${js_ts_files[@]}"
|
||||
else
|
||||
echo "No JavaScript/TypeScript files to lint for styles"
|
||||
fi
|
||||
@@ -92,4 +92,33 @@ do
|
||||
fi
|
||||
done
|
||||
|
||||
# A few UI labels ("% calculation" etc.) parse as accidentally-valid
|
||||
# %-format directives (a space flag plus a conversion character), so
|
||||
# babel auto-flags them python-format on every write and msgfmt then
|
||||
# fatals on their translations. The app never %-formats them; strip the
|
||||
# flag AFTER the update pass (babel re-adds it during the update, so
|
||||
# this must run last). Line-targeted so canonical wrapping is untouched.
|
||||
python3 - <<'PYEOF'
|
||||
import glob
|
||||
|
||||
TARGETS = {'msgid "% calculation"\n', 'msgid "% of parent"\n', 'msgid "% of total"\n'}
|
||||
paths = ["superset/translations/messages.pot"] + sorted(
|
||||
glob.glob("superset/translations/*/LC_MESSAGES/messages.po")
|
||||
)
|
||||
for path in paths:
|
||||
lines = open(path, encoding="utf-8").readlines()
|
||||
changed = False
|
||||
for i, line in enumerate(lines):
|
||||
if line in TARGETS and i > 0 and lines[i - 1].startswith("#,"):
|
||||
tokens = [t.strip() for t in lines[i - 1][2:].split(",")]
|
||||
if "python-format" in tokens:
|
||||
tokens = [t for t in tokens if t != "python-format"]
|
||||
if "no-python-format" not in tokens:
|
||||
tokens.append("no-python-format")
|
||||
lines[i - 1] = "#, " + ", ".join(tokens) + "\n"
|
||||
changed = True
|
||||
if changed:
|
||||
open(path, "w", encoding="utf-8").writelines(lines)
|
||||
PYEOF
|
||||
|
||||
cd $CURRENT_DIR
|
||||
|
||||
@@ -138,6 +138,22 @@ def _lang_name(code: str) -> str:
|
||||
return LANGUAGE_NAMES.get(code, code)
|
||||
|
||||
|
||||
# An ISO 639-1/639-2 code, optionally followed by an ISO 3166 region
|
||||
# (``_BR``, two uppercase) or an ISO 15924 script (``_Latn``, titlecase). The
|
||||
# script subtag matters for catalogs like ``sr_Latn`` (Serbian in Latin script);
|
||||
# without it the code was rejected and the catalog silently skipped.
|
||||
_LANG_CODE_RE: re.Pattern[str] = re.compile(r"[a-z]{2,3}(_([A-Z]{2}|[A-Z][a-z]{3}))?")
|
||||
|
||||
|
||||
def _is_valid_lang_code(lang: str) -> bool:
|
||||
"""Validate a language code before it lands, unsanitized, in a filesystem path.
|
||||
|
||||
Guards against path traversal while allowing the region and script subtags
|
||||
Superset actually ships (e.g. ``pt_BR``, ``sr_Latn``).
|
||||
"""
|
||||
return bool(_LANG_CODE_RE.fullmatch(lang))
|
||||
|
||||
|
||||
def _plural_key(msgid: str, msgid_plural: str) -> str:
|
||||
"""Build the translation index key used for pluralized entries."""
|
||||
return f"{msgid}\x00{msgid_plural}"
|
||||
@@ -667,13 +683,11 @@ def backfill(
|
||||
mark_fuzzy: bool = True,
|
||||
) -> None:
|
||||
"""Backfill missing translations in the target language's .po file."""
|
||||
# Defense against path traversal: ``lang`` lands in a filesystem path
|
||||
# without further sanitization, so reject anything that isn't an
|
||||
# ISO 639-1/639-2 code with an optional ISO 3166 region (e.g. ``pt_BR``).
|
||||
if not re.fullmatch(r"[a-z]{2,3}(_[A-Z]{2})?", lang):
|
||||
if not _is_valid_lang_code(lang):
|
||||
print(
|
||||
f"Invalid language code: {lang!r} "
|
||||
"(expected ISO 639 code, optionally with _<REGION>, e.g. 'fr' or 'pt_BR')",
|
||||
"(expected ISO 639 code, optionally with a _<REGION> or _<Script> "
|
||||
"subtag, e.g. 'fr', 'pt_BR', or 'sr_Latn')",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -96,6 +96,26 @@ describe("guest token refresh", () => {
|
||||
expect(timing).toBe(DEFAULT_TOKEN_EXP_MS - REFRESH_TIMING_BUFFER_MS);
|
||||
});
|
||||
|
||||
it("falls back to default timing for a completely malformed token", () => {
|
||||
const timing = getGuestTokenRefreshTiming("not-a-jwt");
|
||||
|
||||
expect(timing).toBe(DEFAULT_TOKEN_EXP_MS - REFRESH_TIMING_BUFFER_MS);
|
||||
});
|
||||
|
||||
it("falls back to default timing for an empty string token", () => {
|
||||
const timing = getGuestTokenRefreshTiming("");
|
||||
|
||||
expect(timing).toBe(DEFAULT_TOKEN_EXP_MS - REFRESH_TIMING_BUFFER_MS);
|
||||
});
|
||||
|
||||
it("falls back to default timing for a token with invalid base64 payload", () => {
|
||||
const timing = getGuestTokenRefreshTiming(
|
||||
"header.!!!invalid-base64!!!.signature",
|
||||
);
|
||||
|
||||
expect(timing).toBe(DEFAULT_TOKEN_EXP_MS - REFRESH_TIMING_BUFFER_MS);
|
||||
});
|
||||
|
||||
it("exposes a positive retry delay for failed token refreshes", () => {
|
||||
// The refresh loop reschedules itself after this delay when a fetch
|
||||
// fails or times out, so it must be a sane positive value.
|
||||
|
||||
@@ -25,16 +25,20 @@ export const DEFAULT_TOKEN_REFRESH_RETRY_MS = 10000; // wait before retrying a f
|
||||
|
||||
// when do we refresh the guest token?
|
||||
export function getGuestTokenRefreshTiming(currentGuestToken: string) {
|
||||
const parsedJwt = jwtDecode<Record<string, any>>(currentGuestToken);
|
||||
// if exp is int, it is in seconds, but Date() takes milliseconds
|
||||
const exp = new Date(
|
||||
/[^0-9\.]/g.test(parsedJwt.exp)
|
||||
? parsedJwt.exp
|
||||
: parseFloat(parsedJwt.exp) * 1000,
|
||||
);
|
||||
const isValidDate = exp.toString() !== "Invalid Date";
|
||||
const ttl = isValidDate
|
||||
? Math.max(MIN_REFRESH_WAIT_MS, exp.getTime() - Date.now())
|
||||
: DEFAULT_TOKEN_EXP_MS;
|
||||
return ttl - REFRESH_TIMING_BUFFER_MS;
|
||||
try {
|
||||
const parsedJwt = jwtDecode<Record<string, any>>(currentGuestToken);
|
||||
// if exp is int, it is in seconds, but Date() takes milliseconds
|
||||
const exp = new Date(
|
||||
/[^0-9\.]/g.test(parsedJwt.exp)
|
||||
? parsedJwt.exp
|
||||
: parseFloat(parsedJwt.exp) * 1000,
|
||||
);
|
||||
const isValidDate = exp.toString() !== "Invalid Date";
|
||||
const ttl = isValidDate
|
||||
? Math.max(MIN_REFRESH_WAIT_MS, exp.getTime() - Date.now())
|
||||
: DEFAULT_TOKEN_EXP_MS;
|
||||
return ttl - REFRESH_TIMING_BUFFER_MS;
|
||||
} catch {
|
||||
return DEFAULT_TOKEN_EXP_MS - REFRESH_TIMING_BUFFER_MS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import thunk from 'redux-thunk';
|
||||
import { Provider } from 'react-redux';
|
||||
import reducerIndex from 'spec/helpers/reducerIndex';
|
||||
import { Global } from '@emotion/react';
|
||||
import { App, Layout, Space, Content } from 'antd';
|
||||
import { App, Layout } from 'antd';
|
||||
|
||||
import 'src/theme.ts';
|
||||
import './storybook.css';
|
||||
|
||||
@@ -17,45 +17,38 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { Component, ReactNode } from 'react';
|
||||
import { useState, useCallback, ReactNode } from 'react';
|
||||
|
||||
export type Props = {
|
||||
children: ReactNode;
|
||||
expandableWhat?: string;
|
||||
};
|
||||
|
||||
type State = {
|
||||
open: boolean;
|
||||
};
|
||||
export default function Expandable({ children, expandableWhat }: Props) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
export default class Expandable extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = { open: false };
|
||||
this.handleToggle = this.handleToggle.bind(this);
|
||||
}
|
||||
const handleToggle = useCallback(() => {
|
||||
setOpen(prevOpen => !prevOpen);
|
||||
}, []);
|
||||
|
||||
handleToggle() {
|
||||
this.setState(({ open }) => ({ open: !open }));
|
||||
}
|
||||
const label = expandableWhat
|
||||
? `${open ? 'Hide' : 'Show'} ${expandableWhat}`
|
||||
: open
|
||||
? 'Hide'
|
||||
: 'Show';
|
||||
|
||||
render() {
|
||||
const { open } = this.state;
|
||||
const { children, expandableWhat } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary btn-sm"
|
||||
onClick={this.handleToggle}
|
||||
>
|
||||
{`${open ? 'Hide' : 'Show'} ${expandableWhat}`}
|
||||
</button>
|
||||
<br />
|
||||
<br />
|
||||
{open ? children : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary btn-sm"
|
||||
onClick={handleToggle}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
<br />
|
||||
<br />
|
||||
{open ? children : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { Component, ReactNode } from 'react';
|
||||
import { useState, useEffect, useCallback, useRef, ReactNode } from 'react';
|
||||
import { t } from '@apache-superset/core/translation';
|
||||
import {
|
||||
SupersetClient,
|
||||
@@ -36,12 +36,6 @@ export type Props = {
|
||||
postPayload?: string;
|
||||
};
|
||||
|
||||
type State = {
|
||||
didVerify: boolean;
|
||||
error?: Error | SupersetApiError;
|
||||
payload?: object;
|
||||
};
|
||||
|
||||
export const renderError = (error: Error) => (
|
||||
<div>
|
||||
The following error occurred, make sure you have <br />
|
||||
@@ -54,29 +48,37 @@ export const renderError = (error: Error) => (
|
||||
</div>
|
||||
);
|
||||
|
||||
export default class VerifyCORS extends Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = { didVerify: false };
|
||||
this.handleVerify = this.handleVerify.bind(this);
|
||||
}
|
||||
export default function VerifyCORS({
|
||||
children,
|
||||
endpoint,
|
||||
host,
|
||||
method,
|
||||
postPayload,
|
||||
}: Props): JSX.Element {
|
||||
const [didVerify, setDidVerify] = useState(false);
|
||||
const [error, setError] = useState<Error | SupersetApiError | undefined>(
|
||||
undefined,
|
||||
);
|
||||
const [payload, setPayload] = useState<object | undefined>(undefined);
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
const { endpoint, host, postPayload, method } = this.props;
|
||||
const prevPropsRef = useRef({ endpoint, host, postPayload, method });
|
||||
|
||||
useEffect(() => {
|
||||
const prevProps = prevPropsRef.current;
|
||||
if (
|
||||
(this.state.didVerify || this.state.error) &&
|
||||
(didVerify || error) &&
|
||||
(prevProps.endpoint !== endpoint ||
|
||||
prevProps.host !== host ||
|
||||
prevProps.postPayload !== postPayload ||
|
||||
prevProps.method !== method)
|
||||
) {
|
||||
// eslint-disable-next-line react/no-did-update-set-state
|
||||
this.setState({ didVerify: false, error: undefined });
|
||||
setDidVerify(false);
|
||||
setError(undefined);
|
||||
}
|
||||
}
|
||||
prevPropsRef.current = { endpoint, host, postPayload, method };
|
||||
}, [endpoint, host, postPayload, method, didVerify, error]);
|
||||
|
||||
handleVerify() {
|
||||
const { endpoint, host, postPayload, method } = this.props;
|
||||
const handleVerify = useCallback(() => {
|
||||
SupersetClient.reset();
|
||||
SupersetClient.configure({
|
||||
credentials: 'include',
|
||||
@@ -94,43 +96,40 @@ export default class VerifyCORS extends Component<Props, State> {
|
||||
}
|
||||
return { error: 'Must provide valid endpoint and payload.' };
|
||||
})
|
||||
.then(result =>
|
||||
this.setState({ didVerify: true, error: undefined, payload: result }),
|
||||
)
|
||||
.catch(error => this.setState({ error }));
|
||||
}
|
||||
.then(result => {
|
||||
setDidVerify(true);
|
||||
setError(undefined);
|
||||
setPayload(result);
|
||||
})
|
||||
.catch(err => setError(err));
|
||||
}, [endpoint, host, method, postPayload]);
|
||||
|
||||
render() {
|
||||
const { didVerify, error, payload } = this.state;
|
||||
const { children } = this.props;
|
||||
|
||||
return didVerify ? (
|
||||
children({ payload })
|
||||
) : (
|
||||
<div className="row">
|
||||
<div className="col-md-10">
|
||||
This example requires CORS requests from this domain. <br />
|
||||
<br />
|
||||
1) enable CORS requests in your Superset App from{' '}
|
||||
{`${window.location.origin}`}
|
||||
<br />
|
||||
2) configure your Superset App host name below <br />
|
||||
3) click below to verify authentication. You may debug CORS further
|
||||
using the `@superset-ui/connection` story. <br />
|
||||
<br />
|
||||
<Button type="primary" size="small" onClick={this.handleVerify}>
|
||||
{t('Verify')}
|
||||
</Button>
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="col-md-8">
|
||||
<ErrorMessage error={error} />
|
||||
</div>
|
||||
)}
|
||||
return didVerify ? (
|
||||
<>{children({ payload })}</>
|
||||
) : (
|
||||
<div className="row">
|
||||
<div className="col-md-10">
|
||||
This example requires CORS requests from this domain. <br />
|
||||
<br />
|
||||
1) enable CORS requests in your Superset App from{' '}
|
||||
{`${window.location.origin}`}
|
||||
<br />
|
||||
2) configure your Superset App host name below <br />
|
||||
3) click below to verify authentication. You may debug CORS further
|
||||
using the `@superset-ui/connection` story. <br />
|
||||
<br />
|
||||
<Button type="primary" size="small" onClick={handleVerify}>
|
||||
{t('Verify')}
|
||||
</Button>
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
{error && (
|
||||
<div className="col-md-8">
|
||||
<ErrorMessage error={error} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Files whose Emotion template literals nest a `css` tag inside an
|
||||
# interpolation (e.g. styled(X)`${props => css`...`}`) or otherwise use a
|
||||
# structure that postcss-styled-syntax cannot parse. Stylelint reports a
|
||||
# CssSyntaxError on these rather than a real lint finding, so they are
|
||||
# excluded until the syntax gains nested-template support.
|
||||
src/components/ListView/Filters/Base.ts
|
||||
src/features/databases/DatabaseModal/styles.ts
|
||||
src/features/databases/UploadDataModel/styles.ts
|
||||
packages/superset-ui-core/src/components/Typography/index.tsx
|
||||
@@ -471,19 +471,19 @@ export function applyAdvancedTimeRangeFilterOnDashboard(
|
||||
endRange?: string,
|
||||
) {
|
||||
cy.get('.control-label').contains('Range type').should('be.visible');
|
||||
cy.get('.ant-popover-content .ant-select-selector')
|
||||
cy.get('.ant-popover-content .ant-select-content')
|
||||
.should('be.visible')
|
||||
.click();
|
||||
cy.get(`[label="Advanced"]`).should('be.visible').click();
|
||||
cy.get('.section-title').contains('Advanced Time Range').should('be.visible');
|
||||
if (startRange) {
|
||||
cy.get('.ant-popover-inner-content')
|
||||
cy.get('.ant-popover-content')
|
||||
.find('[class^=ant-input]')
|
||||
.first()
|
||||
.type(`${startRange}`);
|
||||
}
|
||||
if (endRange) {
|
||||
cy.get('.ant-popover-inner-content')
|
||||
cy.get('.ant-popover-content')
|
||||
.find('[class^=ant-input]')
|
||||
.last()
|
||||
.type(`${endRange}`);
|
||||
@@ -514,15 +514,14 @@ export function inputNativeFilterDefaultValue(
|
||||
)
|
||||
.eq(1)
|
||||
.within(() => {
|
||||
cy.get('.ant-select-selection-search-input').type(
|
||||
`${defaultValue}{enter}`,
|
||||
{ force: true },
|
||||
);
|
||||
cy.get('.ant-select-input').type(`${defaultValue}{enter}`, {
|
||||
force: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
cy.getBySel('default-input').within(() => {
|
||||
cy.get('.ant-select-selection-search-input').click();
|
||||
cy.get('.ant-select-input').click();
|
||||
cy.get('.ant-select-item-option-content').contains(defaultValue).click();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -53,9 +53,7 @@ export function saveChartToDashboard(chartName: string, dashboardName: string) {
|
||||
.then($modal => {
|
||||
cy.wait(500);
|
||||
cy.wrap($modal)
|
||||
.find(
|
||||
'.ant-select-selection-search-input[aria-label*="Select a dashboard"]',
|
||||
)
|
||||
.find('.ant-select-input[aria-label*="Select a dashboard"]')
|
||||
.type(dashboardName, { force: true });
|
||||
|
||||
cy.get(`.ant-select-item-option[title="${dashboardName}"]`).click();
|
||||
|
||||
@@ -52,7 +52,7 @@ export const securityAccess = {
|
||||
};
|
||||
export const homePage = {
|
||||
homeSection: {
|
||||
sectionArea: '.ant-collapse-content-box',
|
||||
sectionArea: '.ant-collapse-body',
|
||||
sectionElement: '.ant-card-meta-title',
|
||||
},
|
||||
sections: {
|
||||
@@ -91,7 +91,7 @@ export const databasesPage = {
|
||||
preferredBlockSheets: '.preferred > :nth-child(6)',
|
||||
supportedDatabasesText: '.control-label',
|
||||
orChoose: '.available-label',
|
||||
dbDropdown: '[class="ant-select-selection-search-input"]',
|
||||
dbDropdown: '.ant-select-input',
|
||||
dbDropdownMenu: '.rc-virtual-list-holder-inner',
|
||||
dbDropdownMenuItem: '[class="ant-select-item-option-content"]',
|
||||
infoAlert: '.ant-alert',
|
||||
@@ -277,7 +277,7 @@ export const chartListView = {
|
||||
header: {
|
||||
cardView: '[aria-label="card-view"]',
|
||||
listView: '[aria-label="list-view"]',
|
||||
sort: '[class="ant-select-selection-search-input"][aria-label="Sort"]',
|
||||
sort: '.ant-select-input[aria-label="Sort"]',
|
||||
sortRecentlyModifiedMenuOption: '[label="Recently modified"]',
|
||||
sortAlphabeticalMenuOption: '[label="Alphabetical"]',
|
||||
sortDropdown: '.Select__menu',
|
||||
@@ -338,20 +338,20 @@ export const nativeFilters = {
|
||||
filtersPanel: {
|
||||
filterName: dataTestLocator('filters-config-modal__name-input'),
|
||||
datasetName: dataTestLocator('filters-config-modal__datasource-input'),
|
||||
filterInfoInput: '.ant-select-selection-search-input',
|
||||
filterInfoInput: '.ant-select-input',
|
||||
inputDropdown: '.ant-select-item-option-content',
|
||||
columnEmptyInput: '.ant-select-selection-placeholder',
|
||||
columnEmptyInput: '.ant-select-placeholder',
|
||||
filterTypeInput: dataTestLocator('filters-config-modal__filter-type'),
|
||||
fieldInput: dataTestLocator('field-input'),
|
||||
filterTypeItem: '.ant-select-selection-item',
|
||||
filterTypeItem: '.ant-select-content-has-value, .ant-select-selection-item',
|
||||
filterGear: dataTestLocator('filterbar-orientation-icon'),
|
||||
},
|
||||
filterFromDashboardView: {
|
||||
filterValueInput: '[class="ant-select-selection-search-input"]',
|
||||
filterValueInput: '.ant-select-input',
|
||||
expand: dataTestLocator('filter-bar__expand-button'),
|
||||
collapse: dataTestLocator('filter-bar__collapse-button'),
|
||||
filterName: dataTestLocator('filter-control-name'),
|
||||
filterContent: '.ant-select-selection-item',
|
||||
filterContent: '.ant-select-content-has-value, .ant-select-selection-item',
|
||||
createFilterButton: dataTestLocator('filter-bar__create-filter'),
|
||||
timeRangeFilterContent: dataTestLocator('time-range-trigger'),
|
||||
},
|
||||
@@ -365,8 +365,8 @@ export const nativeFilters = {
|
||||
checkedCheckbox: '.ant-checkbox-wrapper-checked',
|
||||
infoTooltip: '[aria-label="Show info tooltip"]',
|
||||
parentFilterInput: dataTestLocator('parent-filter-input'),
|
||||
filterPlaceholder: '.ant-select-selection-placeholder',
|
||||
collapsedSectionContainer: '[class="ant-collapse-content-box"]',
|
||||
filterPlaceholder: '.ant-select-placeholder',
|
||||
collapsedSectionContainer: '[class="ant-collapse-body"]',
|
||||
},
|
||||
filtersList: {
|
||||
list: '.ant-tabs-nav-list',
|
||||
@@ -376,7 +376,7 @@ export const nativeFilters = {
|
||||
removeIcon: '[aria-label="delete"]',
|
||||
},
|
||||
filterItem: dataTestLocator('form-item-value'),
|
||||
filterItemDropdown: '.ant-select-selection-search',
|
||||
filterItemDropdown: '.ant-select-input',
|
||||
applyFilter: dataTestLocator('filter-bar__apply-button'),
|
||||
defaultInput: dataTestLocator('default-input'),
|
||||
filterIcon: dataTestLocator('filter-icon'),
|
||||
@@ -484,7 +484,7 @@ export const exploreView = {
|
||||
saveModal: {
|
||||
modal: '.ant-modal-content',
|
||||
chartNameInput: dataTestLocator('new-chart-name'),
|
||||
dashboardNameInput: '.ant-select-selection-search-input',
|
||||
dashboardNameInput: '.ant-select-input',
|
||||
addToDashboardInput: dataTestLocator(
|
||||
'save-chart-modal-select-dashboard-form',
|
||||
),
|
||||
@@ -495,7 +495,7 @@ export const exploreView = {
|
||||
},
|
||||
controlPanel: {
|
||||
panel: dataTestLocator('control-tabs'),
|
||||
categoryArea: '.ant-collapse-content-box',
|
||||
categoryArea: '.ant-collapse-body',
|
||||
dragField: dataTestLocator('datasource'),
|
||||
metricsField: dataTestLocator('metrics'),
|
||||
optionField: dataTestLocator('option-label'),
|
||||
@@ -594,7 +594,7 @@ export const exploreView = {
|
||||
},
|
||||
};
|
||||
export const createChartView = {
|
||||
chooseDatasetInput: '.ant-select-selection-search-input',
|
||||
chooseDatasetInput: '.ant-select-input',
|
||||
chooseDatasetOption: '.ant-select-item-option-content',
|
||||
chooseDatasetList: '.rc-virtual-list-holder-inner',
|
||||
tableVizType: "[alt='Table']",
|
||||
@@ -641,7 +641,7 @@ export const dashboardView = {
|
||||
secondTabSalesDashboard: dataTestLocator('dragdroppable-object'),
|
||||
},
|
||||
timeRangeModal: {
|
||||
rangeTypeField: '.ant-select-selection-item',
|
||||
rangeTypeField: '.ant-select-content-has-value, .ant-select-selection-item',
|
||||
startTimeInputNumber: '.ant-input-number-input',
|
||||
datePicker: '.ant-picker-input',
|
||||
applyButton: dataTestLocator('date-filter-control__apply-button'),
|
||||
|
||||
@@ -75,7 +75,7 @@ module.exports = {
|
||||
// @ant-design/colors and @ant-design/fast-color are allowed through because
|
||||
// @ant-design/icons >= 6.3 deep-imports the ESM build of @ant-design/colors
|
||||
// from its CJS output, so babel-jest must transform those files.
|
||||
'node_modules/(?!@ant-design/(colors|fast-color)|@formatjs/.*|d3-(array|interpolate|color|time|scale|time-format|format)|internmap|@mapbox/tiny-sdf|remark-gfm|(?!@ngrx|(?!deck.gl)|d3-scale)|markdown-table|micromark-*.|decode-named-character-reference|character-entities|mdast-util-*.|unist-util-*.|ccount|escape-string-regexp|nanoid|uuid|@rjsf/*.|echarts|zrender|fetch-mock|pretty-ms|parse-ms|ol|@babel/runtime|@emotion|cheerio|cheerio/lib|parse5|dom-serializer|entities|htmlparser2|rehype-sanitize|hast-util-sanitize|unified|unist-.*|hast-.*|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|property-information|space-separated-tokens|comma-separated-tokens|bail|devlop|zwitch|longest-streak|geostyler|geostyler-.*|(?!geostyler)lodash|react-error-boundary|react-json-tree|react-base16-styling|lodash-es|rbush|quickselect|react-diff-viewer-continued|storybook/*.|json-stringify-pretty-compact)',
|
||||
'node_modules/(?!@ant-design/(colors|fast-color)|@formatjs/.*|d3-(array|interpolate|color|time|scale|time-format|format)|internmap|@mapbox/tiny-sdf|remark-gfm|(?!@ngrx|(?!deck.gl)|d3-scale)|markdown-table|micromark-*.|decode-named-character-reference|character-entities|mdast-util-*.|unist-util-*.|ccount|escape-string-regexp|nanoid|uuid|@rjsf/*.|@x0k/.*|echarts|zrender|fetch-mock|pretty-ms|parse-ms|ol|@babel/runtime|@emotion|cheerio|cheerio/lib|parse5|dom-serializer|entities|htmlparser2|rehype-sanitize|hast-util-sanitize|unified|unist-.*|hast-.*|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|property-information|space-separated-tokens|comma-separated-tokens|bail|devlop|zwitch|longest-streak|geostyler|geostyler-.*|(?!geostyler)lodash|react-error-boundary|react-json-tree|react-base16-styling|lodash-es|rbush|quickselect|react-diff-viewer-continued|storybook/*.|json-stringify-pretty-compact|@x0k/json-schema-merge)',
|
||||
],
|
||||
preset: 'ts-jest',
|
||||
transform: {
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "17.0.2"
|
||||
"version": "18.3.1"
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
@@ -52,10 +52,6 @@
|
||||
// require-default-props, sort-comp, static-property-placement
|
||||
// (prefer-stateless-function / function-component-definition
|
||||
// are represented by react/prefer-function-component below)
|
||||
// jsx-a11y: interactive-supports-focus,
|
||||
// no-interactive-element-to-noninteractive-role,
|
||||
// no-noninteractive-element-interactions,
|
||||
// no-noninteractive-element-to-interactive-role
|
||||
// typescript: naming-convention
|
||||
// unicorn: prevent-abbreviations
|
||||
|
||||
@@ -128,8 +124,7 @@
|
||||
"import/no-mutable-exports": "error",
|
||||
"import/no-amd": "error",
|
||||
"import/first": "error",
|
||||
// TODO: Consolidate duplicate imports in DatasetList and other files
|
||||
"import/no-duplicates": "warn",
|
||||
"import/no-duplicates": "error",
|
||||
"import/no-absolute-path": "error",
|
||||
"import/no-dynamic-require": "error",
|
||||
"import/no-webpack-loader-syntax": "error",
|
||||
@@ -196,6 +191,7 @@
|
||||
|
||||
// === JSX-a11y rules ===
|
||||
"jsx-a11y/alt-text": "error",
|
||||
"jsx-a11y/anchor-ambiguous-text": "error",
|
||||
"jsx-a11y/anchor-has-content": "error",
|
||||
"jsx-a11y/anchor-is-valid": "error",
|
||||
"jsx-a11y/aria-activedescendant-has-tabindex": "error",
|
||||
@@ -203,21 +199,28 @@
|
||||
"jsx-a11y/aria-proptypes": "error",
|
||||
"jsx-a11y/aria-role": ["error", { "ignoreNonDOM": false }],
|
||||
"jsx-a11y/aria-unsupported-elements": "error",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/autocomplete-valid": "error",
|
||||
"jsx-a11y/click-events-have-key-events": "error",
|
||||
"jsx-a11y/control-has-associated-label": "error",
|
||||
"jsx-a11y/heading-has-content": "error",
|
||||
"jsx-a11y/html-has-lang": "error",
|
||||
"jsx-a11y/iframe-has-title": "error",
|
||||
"jsx-a11y/img-redundant-alt": "error",
|
||||
"jsx-a11y/interactive-supports-focus": "error",
|
||||
"jsx-a11y/label-has-associated-control": "error",
|
||||
"jsx-a11y/lang": "error",
|
||||
"jsx-a11y/media-has-caption": "error",
|
||||
"jsx-a11y/mouse-events-have-key-events": "off",
|
||||
"jsx-a11y/mouse-events-have-key-events": "error",
|
||||
"jsx-a11y/no-access-key": "error",
|
||||
"jsx-a11y/no-aria-hidden-on-focusable": "error",
|
||||
"jsx-a11y/no-autofocus": ["error", { "ignoreNonDOM": true }],
|
||||
"jsx-a11y/no-distracting-elements": "error",
|
||||
"jsx-a11y/no-interactive-element-to-noninteractive-role": "error",
|
||||
"jsx-a11y/no-noninteractive-element-interactions": "error",
|
||||
"jsx-a11y/no-noninteractive-element-to-interactive-role": "error",
|
||||
"jsx-a11y/no-noninteractive-tabindex": "error",
|
||||
"jsx-a11y/no-redundant-roles": "error",
|
||||
"jsx-a11y/no-static-element-interactions": "off",
|
||||
"jsx-a11y/no-static-element-interactions": "error",
|
||||
"jsx-a11y/role-has-required-aria-props": "error",
|
||||
"jsx-a11y/role-supports-aria-props": "error",
|
||||
"jsx-a11y/scope": "error",
|
||||
@@ -232,7 +235,14 @@
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-unused-vars": "warn",
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"argsIgnorePattern": "^_",
|
||||
"varsIgnorePattern": "^_",
|
||||
"caughtErrors": "none"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/prefer-optional-chain": "error",
|
||||
|
||||
// === Unicorn rules (bonus coverage) ===
|
||||
@@ -282,6 +292,16 @@
|
||||
"rules": {
|
||||
"jsx-a11y/no-redundant-roles": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
// The pivot table intentionally exposes `role="grid"` on its <table>
|
||||
// for interactive grid semantics (asserted by tableRenders.test.tsx).
|
||||
"files": [
|
||||
"plugins/plugin-chart-pivot-table/src/react-pivottable/TableRenderers.tsx"
|
||||
],
|
||||
"rules": {
|
||||
"jsx-a11y/no-noninteractive-element-to-interactive-role": "off"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ignorePatterns": [
|
||||
|
||||
Generated
+2397
-649
File diff suppressed because it is too large
Load Diff
@@ -57,7 +57,9 @@
|
||||
"lint:all": "npx oxlint --config oxlint.json && npm run type",
|
||||
"lint-fix": "npx oxlint --config oxlint.json --fix --quiet",
|
||||
"lint-fix:all": "npx oxlint --config oxlint.json --fix",
|
||||
"lint:full": "npm run lint && npm run check:custom-rules",
|
||||
"lint:full": "npm run lint && npm run check:custom-rules && npm run stylelint",
|
||||
"stylelint": "stylelint \"{src,packages/*/src,plugins/*/src}/**/*.{ts,tsx}\"",
|
||||
"stylelint-fix": "stylelint --fix \"{src,packages/*/src,plugins/*/src}/**/*.{ts,tsx}\"",
|
||||
"check:custom-rules": "node scripts/check-custom-rules.js",
|
||||
"check:storybook-coverage": "node scripts/check-storybook-coverage.js",
|
||||
"ensure-oxc": "echo 'OXC linter is ready' && npx oxlint --version",
|
||||
@@ -128,10 +130,9 @@
|
||||
"@luma.gl/shadertools": "~9.2.5",
|
||||
"@luma.gl/webgl": "~9.2.5",
|
||||
"@reduxjs/toolkit": "^1.9.3",
|
||||
"@rjsf/antd": "^5.24.13",
|
||||
"@rjsf/core": "^5.24.13",
|
||||
"@rjsf/utils": "^5.24.3",
|
||||
"@rjsf/validator-ajv8": "^5.24.13",
|
||||
"@rjsf/core": "^6.6.2",
|
||||
"@rjsf/utils": "^6.6.2",
|
||||
"@rjsf/validator-ajv8": "^6.6.2",
|
||||
"@scarf/scarf": "^1.4.0",
|
||||
"@superset-ui/chart-controls": "file:./packages/superset-ui-chart-controls",
|
||||
"@superset-ui/core": "file:./packages/superset-ui-core",
|
||||
@@ -167,7 +168,7 @@
|
||||
"@visx/xychart": "^4.0.0",
|
||||
"ag-grid-community": "36.0.0",
|
||||
"ag-grid-react": "36.0.0",
|
||||
"antd": "^5.26.0",
|
||||
"antd": "^6.0.0",
|
||||
"chrono-node": "^2.9.1",
|
||||
"classnames": "^2.2.5",
|
||||
"content-disposition": "^2.0.1",
|
||||
@@ -186,7 +187,7 @@
|
||||
"geostyler-style": "11.0.2",
|
||||
"geostyler-wfs-parser": "^3.0.1",
|
||||
"google-auth-library": "^10.9.0",
|
||||
"immer": "^11.1.9",
|
||||
"immer": "^11.1.11",
|
||||
"interweave": "^13.1.1",
|
||||
"jquery": "^4.0.0",
|
||||
"js-levenshtein": "^1.1.6",
|
||||
@@ -194,7 +195,7 @@
|
||||
"json-stringify-pretty-compact": "^4.0.0",
|
||||
"lodash": "^4.18.1",
|
||||
"lodash-es": "^4.18.1",
|
||||
"mapbox-gl": "^3.25.0",
|
||||
"mapbox-gl": "^3.26.0",
|
||||
"markdown-to-jsx": "^9.8.2",
|
||||
"match-sorter": "^8.3.0",
|
||||
"memoize-one": "^6.0.0",
|
||||
@@ -205,9 +206,9 @@
|
||||
"query-string": "9.4.1",
|
||||
"re-resizable": "^6.11.2",
|
||||
"react": "^18.3.0",
|
||||
"react-arborist": "^3.12.0",
|
||||
"react-arborist": "^3.13.2",
|
||||
"react-checkbox-tree": "^1.8.0",
|
||||
"react-diff-viewer-continued": "^4.2.2",
|
||||
"react-diff-viewer-continued": "^4.3.0",
|
||||
"react-dnd": "^11.1.3",
|
||||
"react-dnd-html5-backend": "^11.1.3",
|
||||
"react-dom": "^18.3.0",
|
||||
@@ -262,7 +263,7 @@
|
||||
"@babel/types": "^7.29.7",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
"@emotion/jest": "^11.14.2",
|
||||
"@formatjs/intl-durationformat": "^0.10.15",
|
||||
"@formatjs/intl-durationformat": "^0.10.16",
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@playwright/test": "^1.61.1",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.2",
|
||||
@@ -272,7 +273,7 @@
|
||||
"@storybook/test-runner": "0.24.4",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@swc/core": "^1.15.43",
|
||||
"@swc/plugin-emotion": "^14.14.1",
|
||||
"@swc/plugin-emotion": "^14.15.0",
|
||||
"@swc/plugin-transform-imports": "^12.5.0",
|
||||
"@testing-library/dom": "^9.3.4",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
@@ -286,7 +287,7 @@
|
||||
"@types/json-bigint": "^1.0.4",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/mousetrap": "^1.6.15",
|
||||
"@types/node": "^26.1.0",
|
||||
"@types/node": "^26.1.1",
|
||||
"@types/react": "^18.3.0",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@types/react-loadable": "^5.5.11",
|
||||
@@ -299,19 +300,19 @@
|
||||
"@types/rison": "0.1.0",
|
||||
"@types/tinycolor2": "^1.4.3",
|
||||
"@types/unzipper": "^0.10.11",
|
||||
"@typescript-eslint/eslint-plugin": "^8.62.1",
|
||||
"@typescript-eslint/parser": "^8.62.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.63.0",
|
||||
"@typescript-eslint/parser": "^8.63.0",
|
||||
"babel-jest": "^30.4.1",
|
||||
"babel-loader": "^10.1.1",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
|
||||
"baseline-browser-mapping": "^2.10.41",
|
||||
"baseline-browser-mapping": "^2.10.42",
|
||||
"cheerio": "1.2.0",
|
||||
"concurrently": "^10.0.3",
|
||||
"copy-webpack-plugin": "^14.0.0",
|
||||
"cross-env": "^10.1.0",
|
||||
"css-loader": "^7.1.4",
|
||||
"eslint": "^10.6.0",
|
||||
"eslint": "^10.7.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-import-resolver-typescript": "^4.4.5",
|
||||
@@ -344,9 +345,10 @@
|
||||
"mini-css-extract-plugin": "^2.10.2",
|
||||
"minimizer-webpack-plugin": "^5.6.1",
|
||||
"open-cli": "^9.0.0",
|
||||
"oxlint": "^1.72.0",
|
||||
"oxlint": "^1.73.0",
|
||||
"po2json": "^0.4.5",
|
||||
"prettier": "3.9.4",
|
||||
"postcss-styled-syntax": "^0.7.2",
|
||||
"prettier": "3.9.5",
|
||||
"prettier-plugin-packagejson": "^3.0.2",
|
||||
"process": "^0.11.10",
|
||||
"react-dnd-test-backend": "^16.0.1",
|
||||
@@ -358,20 +360,21 @@
|
||||
"speed-measure-webpack-plugin": "^1.6.0",
|
||||
"storybook": "10.4.6",
|
||||
"style-loader": "^4.0.0",
|
||||
"stylelint": "^17.14.0",
|
||||
"swc-loader": "^0.2.7",
|
||||
"ts-jest": "^29.4.11",
|
||||
"tscw-config": "^1.1.2",
|
||||
"tsx": "^4.22.5",
|
||||
"tsx": "^4.23.0",
|
||||
"typescript": "5.4.5",
|
||||
"unzipper": "^0.12.5",
|
||||
"vm-browserify": "^1.1.2",
|
||||
"wait-on": "^9.0.10",
|
||||
"webpack": "^5.108.3",
|
||||
"webpack": "^5.108.4",
|
||||
"webpack-bundle-analyzer": "^5.3.0",
|
||||
"webpack-cli": "^7.0.3",
|
||||
"webpack-dev-server": "^5.2.5",
|
||||
"webpack-manifest-plugin": "^6.0.1",
|
||||
"webpack-sources": "^3.5.0",
|
||||
"webpack-sources": "^3.5.1",
|
||||
"webpack-visualizer-plugin2": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -387,6 +390,9 @@
|
||||
},
|
||||
"overrides": {
|
||||
"uuid": "$uuid",
|
||||
"@great-expectations/jsonforms-antd-renderers": {
|
||||
"antd": "$antd"
|
||||
},
|
||||
"core-js": "^3.38.1",
|
||||
"dompurify": "^3.4.11",
|
||||
"esbuild": "^0.28.1",
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
"react-loadable": "^5.5.0",
|
||||
"tinycolor2": "*",
|
||||
"lodash": "^4.18.1",
|
||||
"antd": "^5.26.0",
|
||||
"antd": "^6.0.0",
|
||||
"jed": "^1.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -38,7 +38,7 @@ export const GlobalStyles = () => {
|
||||
<Global
|
||||
key={`global-${theme.colorLink}`}
|
||||
styles={css`
|
||||
// SPA
|
||||
/* SPA */
|
||||
html {
|
||||
color-scheme: ${isDark ? 'dark' : 'light'};
|
||||
}
|
||||
@@ -93,7 +93,7 @@ export const GlobalStyles = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// Overriding bootstrap styles
|
||||
/* Overriding bootstrap styles */
|
||||
#app {
|
||||
flex: 1 1 auto;
|
||||
position: relative;
|
||||
@@ -105,8 +105,18 @@ export const GlobalStyles = () => {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
// Override geostyler CSS that hides AntD ColorPicker alpha input
|
||||
// See: https://github.com/apache/superset/issues/34721
|
||||
/* antd 6 removed the Tag's default trailing margin (v5 shipped */
|
||||
/* margin-inline-end: 8px on every tag) in favor of parents spacing */
|
||||
/* tags via flex/Space gaps. The app's layouts predate that and rely */
|
||||
/* on the v5 default (e.g. the dashboard header's Published tag), */
|
||||
/* so restore it for visual parity. Remove once Tag-adjacent layouts */
|
||||
/* declare their own gaps. */
|
||||
.ant-tag {
|
||||
margin-inline-end: ${theme.marginXS}px;
|
||||
}
|
||||
|
||||
/* Override geostyler CSS that hides AntD ColorPicker alpha input */
|
||||
/* See: https://github.com/apache/superset/issues/34721 */
|
||||
.ant-color-picker .ant-color-picker-alpha-input {
|
||||
display: block;
|
||||
}
|
||||
@@ -117,7 +127,7 @@ export const GlobalStyles = () => {
|
||||
}
|
||||
|
||||
.superset-explore-popover.ant-popover
|
||||
.ant-popover-inner:has(.ant-popover-title) {
|
||||
.ant-popover-container:has(.ant-popover-title) {
|
||||
padding-top: 0;
|
||||
}
|
||||
.superset-explore-popover.ant-popover .ant-popover-title {
|
||||
@@ -126,7 +136,7 @@ export const GlobalStyles = () => {
|
||||
line-height: 1;
|
||||
}
|
||||
.superset-explore-popover.ant-popover
|
||||
.ant-popover-inner:has(.ant-popover-title)
|
||||
.ant-popover-container:has(.ant-popover-title)
|
||||
.ant-tabs-tab {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,34 @@
|
||||
*/
|
||||
import { theme as antdThemeImport } from 'antd';
|
||||
import { Theme } from './Theme';
|
||||
import { AnyThemeConfig, ThemeAlgorithm } from './types';
|
||||
import { AnyThemeConfig, ThemeAlgorithm, allowedAntdTokens } from './types';
|
||||
|
||||
/**
|
||||
* A fixed, Superset-representative base theme used by the characterization
|
||||
* tests below. Mirrors the real THEME_DEFAULT token set so the snapshots are
|
||||
* stable and meaningful. Do NOT churn this casually — it is the baseline the
|
||||
* Ant Design v6 upgrade is measured against.
|
||||
*/
|
||||
const SUPERSET_BASE_THEME: AnyThemeConfig = {
|
||||
token: {
|
||||
colorPrimary: '#2893B3',
|
||||
colorError: '#e04355',
|
||||
colorWarning: '#fcc700',
|
||||
colorSuccess: '#5ac189',
|
||||
colorInfo: '#66bcfe',
|
||||
fontFamily: "'Inter', Helvetica, Arial",
|
||||
fontFamilyCode: "'IBM Plex Mono', 'Courier New', monospace",
|
||||
},
|
||||
};
|
||||
|
||||
/** Collect the computed value of every allow-listed antd token, sorted. */
|
||||
function pickAllowedTokens(theme: Theme): Record<string, unknown> {
|
||||
const out: Record<string, unknown> = {};
|
||||
[...allowedAntdTokens].sort().forEach(key => {
|
||||
out[key] = (theme.theme as unknown as Record<string, unknown>)[key];
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
// Mock emotion's cache to avoid actual DOM operations
|
||||
jest.mock('@emotion/cache', () => ({
|
||||
@@ -338,10 +365,10 @@ test('Theme edge cases correctly applies base theme tokens in dark mode', () =>
|
||||
algorithm: antdThemeImport.defaultAlgorithm,
|
||||
};
|
||||
|
||||
const baseThemeDark: AnyThemeConfig = {
|
||||
const baseThemeDark = {
|
||||
...baseTheme,
|
||||
algorithm: antdThemeImport.darkAlgorithm,
|
||||
};
|
||||
} as AnyThemeConfig;
|
||||
|
||||
// Simulate light mode with base theme
|
||||
const lightTheme = Theme.fromConfig({}, baseTheme);
|
||||
@@ -473,10 +500,10 @@ test('Theme base theme integration handles base theme with dark algorithm correc
|
||||
},
|
||||
};
|
||||
|
||||
const baseThemeDark: AnyThemeConfig = {
|
||||
const baseThemeDark = {
|
||||
...baseTheme,
|
||||
algorithm: antdThemeImport.darkAlgorithm,
|
||||
};
|
||||
} as AnyThemeConfig;
|
||||
|
||||
const userDarkTheme: AnyThemeConfig = {
|
||||
algorithm: antdThemeImport.darkAlgorithm,
|
||||
@@ -525,7 +552,7 @@ test('Theme base theme integration works with real-world Superset base theme con
|
||||
const darkTheme = Theme.fromConfig(themeDark, {
|
||||
...supersetBaseTheme,
|
||||
algorithm: antdThemeImport.darkAlgorithm,
|
||||
});
|
||||
} as AnyThemeConfig);
|
||||
expect(darkTheme.theme.fontFamily).toBe("'Inter', Helvetica, Arial");
|
||||
|
||||
const darkSerialized = darkTheme.toSerializedConfig();
|
||||
@@ -604,7 +631,9 @@ test('Theme base theme integration handles cssVar, hashed and inherit properties
|
||||
|
||||
// User properties override/add to base
|
||||
expect(serialized.inherit).toBe(true);
|
||||
expect(serialized.cssVar).toBe(true);
|
||||
// Ant Design v6 dropped boolean `cssVar`; Superset normalizes `true` to the
|
||||
// object form `{}` at the antd boundary (CSS variables are on by default).
|
||||
expect(serialized.cssVar).toEqual({});
|
||||
expect(serialized.hashed).toBe(false);
|
||||
|
||||
// Tokens are still merged
|
||||
@@ -909,3 +938,81 @@ test('colorLink is preserved in setConfig when explicitly set', () => {
|
||||
expect(theme.theme.colorPrimary).toBe('#f759ab');
|
||||
expect(theme.theme.colorLink).toBe('#ff0000');
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Characterization tests — theming safety net for the Ant Design v6 upgrade.
|
||||
//
|
||||
// These lock the bridge between Superset's theme and Ant Design's design
|
||||
// tokens (getDesignToken -> allowedAntdTokens -> SupersetTheme). When antd
|
||||
// renames or removes a token, the completeness tests fail loudly instead of
|
||||
// letting a token silently resolve to `undefined`, and the snapshots make any
|
||||
// value drift reviewable. See packages/superset-core/src/theme/types.ts.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test('every allowed antd token resolves to a defined value in light mode', () => {
|
||||
const theme = Theme.fromConfig(
|
||||
{ algorithm: antdThemeImport.defaultAlgorithm },
|
||||
SUPERSET_BASE_THEME,
|
||||
);
|
||||
|
||||
const missing = allowedAntdTokens.filter(
|
||||
key =>
|
||||
(theme.theme as unknown as Record<string, unknown>)[key] === undefined,
|
||||
);
|
||||
|
||||
// A non-empty list means antd dropped/renamed a token we still allow-list.
|
||||
expect(missing).toEqual([]);
|
||||
});
|
||||
|
||||
test('every allowed antd token resolves to a defined value in dark mode', () => {
|
||||
const theme = Theme.fromConfig(
|
||||
{ algorithm: antdThemeImport.darkAlgorithm },
|
||||
SUPERSET_BASE_THEME,
|
||||
);
|
||||
|
||||
const missing = allowedAntdTokens.filter(
|
||||
key =>
|
||||
(theme.theme as unknown as Record<string, unknown>)[key] === undefined,
|
||||
);
|
||||
|
||||
expect(missing).toEqual([]);
|
||||
});
|
||||
|
||||
test('allowed antd token key set is stable', () => {
|
||||
// Locks the *set* of tokens Superset exposes (independent of their values),
|
||||
// so additions/removals to allowedAntdTokens are an explicit, reviewed diff.
|
||||
expect([...allowedAntdTokens].sort()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('computed light-mode token values match snapshot', () => {
|
||||
const theme = Theme.fromConfig(
|
||||
{ algorithm: antdThemeImport.defaultAlgorithm },
|
||||
SUPERSET_BASE_THEME,
|
||||
);
|
||||
|
||||
expect(pickAllowedTokens(theme)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('computed dark-mode token values match snapshot', () => {
|
||||
const theme = Theme.fromConfig(
|
||||
{ algorithm: antdThemeImport.darkAlgorithm },
|
||||
SUPERSET_BASE_THEME,
|
||||
);
|
||||
|
||||
expect(pickAllowedTokens(theme)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('dark mode actually diverges from light mode for background tokens', () => {
|
||||
const light = Theme.fromConfig(
|
||||
{ algorithm: antdThemeImport.defaultAlgorithm },
|
||||
SUPERSET_BASE_THEME,
|
||||
);
|
||||
const dark = Theme.fromConfig(
|
||||
{ algorithm: antdThemeImport.darkAlgorithm },
|
||||
SUPERSET_BASE_THEME,
|
||||
);
|
||||
|
||||
// Guards the dark algorithm wiring: base surface and text must invert.
|
||||
expect(dark.theme.colorBgBase).not.toBe(light.theme.colorBgBase);
|
||||
expect(dark.theme.colorTextBase).not.toBe(light.theme.colorTextBase);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,730 @@
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`allowed antd token key set is stable 1`] = `
|
||||
[
|
||||
"borderRadius",
|
||||
"borderRadiusLG",
|
||||
"borderRadiusOuter",
|
||||
"borderRadiusSM",
|
||||
"borderRadiusXS",
|
||||
"boxShadow",
|
||||
"boxShadowDrawerLeft",
|
||||
"boxShadowDrawerRight",
|
||||
"boxShadowDrawerUp",
|
||||
"boxShadowPopoverArrow",
|
||||
"boxShadowSecondary",
|
||||
"boxShadowTabsOverflowBottom",
|
||||
"boxShadowTabsOverflowLeft",
|
||||
"boxShadowTabsOverflowRight",
|
||||
"boxShadowTabsOverflowTop",
|
||||
"boxShadowTertiary",
|
||||
"colorBgBase",
|
||||
"colorBgBlur",
|
||||
"colorBgContainer",
|
||||
"colorBgContainerDisabled",
|
||||
"colorBgElevated",
|
||||
"colorBgLayout",
|
||||
"colorBgMask",
|
||||
"colorBgSpotlight",
|
||||
"colorBgTextActive",
|
||||
"colorBgTextHover",
|
||||
"colorBorder",
|
||||
"colorBorderBg",
|
||||
"colorBorderSecondary",
|
||||
"colorError",
|
||||
"colorErrorActive",
|
||||
"colorErrorBg",
|
||||
"colorErrorBgActive",
|
||||
"colorErrorBgHover",
|
||||
"colorErrorBorder",
|
||||
"colorErrorBorderHover",
|
||||
"colorErrorHover",
|
||||
"colorErrorOutline",
|
||||
"colorErrorText",
|
||||
"colorErrorTextActive",
|
||||
"colorErrorTextHover",
|
||||
"colorFill",
|
||||
"colorFillAlter",
|
||||
"colorFillContent",
|
||||
"colorFillContentHover",
|
||||
"colorFillQuaternary",
|
||||
"colorFillSecondary",
|
||||
"colorFillTertiary",
|
||||
"colorHighlight",
|
||||
"colorIcon",
|
||||
"colorIconHover",
|
||||
"colorInfo",
|
||||
"colorInfoActive",
|
||||
"colorInfoBg",
|
||||
"colorInfoBgHover",
|
||||
"colorInfoBorder",
|
||||
"colorInfoBorderHover",
|
||||
"colorInfoHover",
|
||||
"colorInfoText",
|
||||
"colorInfoTextActive",
|
||||
"colorInfoTextHover",
|
||||
"colorLink",
|
||||
"colorLinkActive",
|
||||
"colorLinkHover",
|
||||
"colorPrimary",
|
||||
"colorPrimaryActive",
|
||||
"colorPrimaryBg",
|
||||
"colorPrimaryBgHover",
|
||||
"colorPrimaryBorder",
|
||||
"colorPrimaryBorderHover",
|
||||
"colorPrimaryHover",
|
||||
"colorPrimaryText",
|
||||
"colorPrimaryTextActive",
|
||||
"colorPrimaryTextHover",
|
||||
"colorSplit",
|
||||
"colorSuccess",
|
||||
"colorSuccessActive",
|
||||
"colorSuccessBg",
|
||||
"colorSuccessBgHover",
|
||||
"colorSuccessBorder",
|
||||
"colorSuccessBorderHover",
|
||||
"colorSuccessHover",
|
||||
"colorSuccessText",
|
||||
"colorSuccessTextActive",
|
||||
"colorSuccessTextHover",
|
||||
"colorText",
|
||||
"colorTextBase",
|
||||
"colorTextDescription",
|
||||
"colorTextDisabled",
|
||||
"colorTextHeading",
|
||||
"colorTextLabel",
|
||||
"colorTextLightSolid",
|
||||
"colorTextPlaceholder",
|
||||
"colorTextQuaternary",
|
||||
"colorTextSecondary",
|
||||
"colorTextTertiary",
|
||||
"colorWarning",
|
||||
"colorWarningActive",
|
||||
"colorWarningBg",
|
||||
"colorWarningBgHover",
|
||||
"colorWarningBorder",
|
||||
"colorWarningBorderHover",
|
||||
"colorWarningHover",
|
||||
"colorWarningOutline",
|
||||
"colorWarningText",
|
||||
"colorWarningTextActive",
|
||||
"colorWarningTextHover",
|
||||
"colorWhite",
|
||||
"controlHeight",
|
||||
"controlHeightLG",
|
||||
"controlHeightSM",
|
||||
"controlHeightXS",
|
||||
"controlInteractiveSize",
|
||||
"controlItemBgActive",
|
||||
"controlItemBgActiveDisabled",
|
||||
"controlItemBgActiveHover",
|
||||
"controlItemBgHover",
|
||||
"controlOutline",
|
||||
"controlOutlineWidth",
|
||||
"controlPaddingHorizontal",
|
||||
"controlPaddingHorizontalSM",
|
||||
"controlTmpOutline",
|
||||
"fontFamily",
|
||||
"fontFamilyCode",
|
||||
"fontHeight",
|
||||
"fontHeightLG",
|
||||
"fontHeightSM",
|
||||
"fontSize",
|
||||
"fontSizeHeading1",
|
||||
"fontSizeHeading2",
|
||||
"fontSizeHeading3",
|
||||
"fontSizeHeading4",
|
||||
"fontSizeHeading5",
|
||||
"fontSizeIcon",
|
||||
"fontSizeLG",
|
||||
"fontSizeSM",
|
||||
"fontSizeXL",
|
||||
"fontWeightStrong",
|
||||
"lineHeight",
|
||||
"lineHeightHeading1",
|
||||
"lineHeightHeading2",
|
||||
"lineHeightHeading3",
|
||||
"lineHeightHeading4",
|
||||
"lineHeightHeading5",
|
||||
"lineHeightLG",
|
||||
"lineHeightSM",
|
||||
"lineType",
|
||||
"lineWidth",
|
||||
"lineWidthBold",
|
||||
"lineWidthFocus",
|
||||
"linkDecoration",
|
||||
"linkFocusDecoration",
|
||||
"linkHoverDecoration",
|
||||
"margin",
|
||||
"marginLG",
|
||||
"marginMD",
|
||||
"marginSM",
|
||||
"marginXL",
|
||||
"marginXS",
|
||||
"marginXXL",
|
||||
"marginXXS",
|
||||
"motion",
|
||||
"motionBase",
|
||||
"motionDurationFast",
|
||||
"motionDurationMid",
|
||||
"motionDurationSlow",
|
||||
"motionEaseInBack",
|
||||
"motionEaseInOut",
|
||||
"motionEaseInOutCirc",
|
||||
"motionEaseInQuint",
|
||||
"motionEaseOut",
|
||||
"motionEaseOutBack",
|
||||
"motionEaseOutCirc",
|
||||
"motionEaseOutQuint",
|
||||
"motionUnit",
|
||||
"opacityImage",
|
||||
"opacityLoading",
|
||||
"padding",
|
||||
"paddingContentHorizontal",
|
||||
"paddingContentHorizontalLG",
|
||||
"paddingContentHorizontalSM",
|
||||
"paddingContentVertical",
|
||||
"paddingContentVerticalLG",
|
||||
"paddingContentVerticalSM",
|
||||
"paddingLG",
|
||||
"paddingMD",
|
||||
"paddingSM",
|
||||
"paddingXL",
|
||||
"paddingXS",
|
||||
"paddingXXS",
|
||||
"screenLG",
|
||||
"screenLGMax",
|
||||
"screenLGMin",
|
||||
"screenMD",
|
||||
"screenMDMax",
|
||||
"screenMDMin",
|
||||
"screenSM",
|
||||
"screenSMMax",
|
||||
"screenSMMin",
|
||||
"screenXL",
|
||||
"screenXLMax",
|
||||
"screenXLMin",
|
||||
"screenXS",
|
||||
"screenXSMax",
|
||||
"screenXSMin",
|
||||
"screenXXL",
|
||||
"screenXXLMin",
|
||||
"size",
|
||||
"sizeLG",
|
||||
"sizeMD",
|
||||
"sizeMS",
|
||||
"sizePopupArrow",
|
||||
"sizeSM",
|
||||
"sizeStep",
|
||||
"sizeUnit",
|
||||
"sizeXL",
|
||||
"sizeXS",
|
||||
"sizeXXL",
|
||||
"sizeXXS",
|
||||
"wireframe",
|
||||
"zIndexBase",
|
||||
"zIndexPopupBase",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`computed dark-mode token values match snapshot 1`] = `
|
||||
{
|
||||
"borderRadius": 6,
|
||||
"borderRadiusLG": 8,
|
||||
"borderRadiusOuter": 4,
|
||||
"borderRadiusSM": 4,
|
||||
"borderRadiusXS": 2,
|
||||
"boxShadow": "
|
||||
0 6px 16px 0 rgba(255,255,255,0.016),
|
||||
0 3px 6px -4px rgba(255,255,255,0.024),
|
||||
0 9px 28px 8px rgba(255,255,255,0.010000000000000002)
|
||||
",
|
||||
"boxShadowDrawerLeft": "
|
||||
6px 0 16px 0 rgba(255,255,255,0.016),
|
||||
3px 0 6px -4px rgba(255,255,255,0.024),
|
||||
9px 0 28px 8px rgba(255,255,255,0.010000000000000002)
|
||||
",
|
||||
"boxShadowDrawerRight": "
|
||||
-6px 0 16px 0 rgba(255,255,255,0.016),
|
||||
-3px 0 6px -4px rgba(255,255,255,0.024),
|
||||
-9px 0 28px 8px rgba(255,255,255,0.010000000000000002)
|
||||
",
|
||||
"boxShadowDrawerUp": "
|
||||
0 6px 16px 0 rgba(255,255,255,0.016),
|
||||
0 3px 6px -4px rgba(255,255,255,0.024),
|
||||
0 9px 28px 8px rgba(255,255,255,0.010000000000000002)
|
||||
",
|
||||
"boxShadowPopoverArrow": "2px 2px 5px rgba(255,255,255,0.010000000000000002)",
|
||||
"boxShadowSecondary": "
|
||||
0 6px 16px 0 rgba(255,255,255,0.016),
|
||||
0 3px 6px -4px rgba(255,255,255,0.024),
|
||||
0 9px 28px 8px rgba(255,255,255,0.010000000000000002)
|
||||
",
|
||||
"boxShadowTabsOverflowBottom": "inset 0 -10px 8px -8px rgba(255,255,255,0.016)",
|
||||
"boxShadowTabsOverflowLeft": "inset 10px 0 8px -8px rgba(255,255,255,0.016)",
|
||||
"boxShadowTabsOverflowRight": "inset -10px 0 8px -8px rgba(255,255,255,0.016)",
|
||||
"boxShadowTabsOverflowTop": "inset 0 10px 8px -8px rgba(255,255,255,0.016)",
|
||||
"boxShadowTertiary": "
|
||||
0 1px 2px 0 rgba(255,255,255,0.010000000000000002),
|
||||
0 1px 6px -1px rgba(255,255,255,0.006),
|
||||
0 2px 4px 0 rgba(255,255,255,0.006)
|
||||
",
|
||||
"colorBgBase": "#000",
|
||||
"colorBgBlur": "rgba(255,255,255,0.04)",
|
||||
"colorBgContainer": "#141414",
|
||||
"colorBgContainerDisabled": "rgba(255,255,255,0.08)",
|
||||
"colorBgElevated": "#1f1f1f",
|
||||
"colorBgLayout": "#000000",
|
||||
"colorBgMask": "rgba(0,0,0,0.45)",
|
||||
"colorBgSpotlight": "#424242",
|
||||
"colorBgTextActive": "rgba(255,255,255,0.18)",
|
||||
"colorBgTextHover": "rgba(255,255,255,0.12)",
|
||||
"colorBorder": "#424242",
|
||||
"colorBorderBg": "#141414",
|
||||
"colorBorderSecondary": "#303030",
|
||||
"colorError": "#e04355",
|
||||
"colorErrorActive": "#99333e",
|
||||
"colorErrorBg": "#271619",
|
||||
"colorErrorBgActive": "#512228",
|
||||
"colorErrorBgHover": "#3e1b20",
|
||||
"colorErrorBorder": "#512228",
|
||||
"colorErrorBorderHover": "#702931",
|
||||
"colorErrorHover": "#d7646e",
|
||||
"colorErrorOutline": "rgba(231,42,76,0.09)",
|
||||
"colorErrorText": "#c13c4b",
|
||||
"colorErrorTextActive": "#99333e",
|
||||
"colorErrorTextHover": "#d7646e",
|
||||
"colorFill": "rgba(255,255,255,0.18)",
|
||||
"colorFillAlter": "rgba(255,255,255,0.04)",
|
||||
"colorFillContent": "rgba(255,255,255,0.12)",
|
||||
"colorFillContentHover": "rgba(255,255,255,0.18)",
|
||||
"colorFillQuaternary": "rgba(255,255,255,0.04)",
|
||||
"colorFillSecondary": "rgba(255,255,255,0.12)",
|
||||
"colorFillTertiary": "rgba(255,255,255,0.08)",
|
||||
"colorHighlight": "#c13c4b",
|
||||
"colorIcon": "rgba(255,255,255,0.45)",
|
||||
"colorIconHover": "rgba(255,255,255,0.85)",
|
||||
"colorInfo": "#66bcfe",
|
||||
"colorInfoActive": "#4981ac",
|
||||
"colorInfoBg": "#19222c",
|
||||
"colorInfoBgHover": "#223545",
|
||||
"colorInfoBorder": "#2d465a",
|
||||
"colorInfoBorderHover": "#39607d",
|
||||
"colorInfoHover": "#39607d",
|
||||
"colorInfoText": "#5aa3db",
|
||||
"colorInfoTextActive": "#4981ac",
|
||||
"colorInfoTextHover": "#83bfe8",
|
||||
"colorLink": "#2893B3",
|
||||
"colorLinkActive": "#21677b",
|
||||
"colorLinkHover": "#1d4d5c",
|
||||
"colorPrimary": "#2893B3",
|
||||
"colorPrimaryActive": "#21677b",
|
||||
"colorPrimaryBg": "#1a3a44",
|
||||
"colorPrimaryBgHover": "#1d4d5c",
|
||||
"colorPrimaryBorder": "#1a3a44",
|
||||
"colorPrimaryBorderHover": "#1d4d5c",
|
||||
"colorPrimaryHover": "#4499ae",
|
||||
"colorPrimaryText": "#25809b",
|
||||
"colorPrimaryTextActive": "#21677b",
|
||||
"colorPrimaryTextHover": "#4499ae",
|
||||
"colorSplit": "rgba(253,253,253,0.12)",
|
||||
"colorSuccess": "#5ac189",
|
||||
"colorSuccessActive": "#428460",
|
||||
"colorSuccessBg": "#17231d",
|
||||
"colorSuccessBgHover": "#1f362a",
|
||||
"colorSuccessBorder": "#294837",
|
||||
"colorSuccessBorderHover": "#346249",
|
||||
"colorSuccessHover": "#346249",
|
||||
"colorSuccessText": "#50a777",
|
||||
"colorSuccessTextActive": "#428460",
|
||||
"colorSuccessTextHover": "#77bc94",
|
||||
"colorText": "rgba(255,255,255,0.85)",
|
||||
"colorTextBase": "#fff",
|
||||
"colorTextDescription": "rgba(255,255,255,0.45)",
|
||||
"colorTextDisabled": "rgba(255,255,255,0.25)",
|
||||
"colorTextHeading": "rgba(255,255,255,0.85)",
|
||||
"colorTextLabel": "rgba(255,255,255,0.65)",
|
||||
"colorTextLightSolid": "#fff",
|
||||
"colorTextPlaceholder": "rgba(255,255,255,0.25)",
|
||||
"colorTextQuaternary": "rgba(255,255,255,0.25)",
|
||||
"colorTextSecondary": "rgba(255,255,255,0.65)",
|
||||
"colorTextTertiary": "rgba(255,255,255,0.45)",
|
||||
"colorWarning": "#fcc700",
|
||||
"colorWarningActive": "#ab8807",
|
||||
"colorWarningBg": "#2b2411",
|
||||
"colorWarningBgHover": "#45370f",
|
||||
"colorWarningBorder": "#5a4a0e",
|
||||
"colorWarningBorderHover": "#7c650b",
|
||||
"colorWarningHover": "#7c650b",
|
||||
"colorWarningOutline": "rgba(173,127,0,0.15)",
|
||||
"colorWarningText": "#d9ac03",
|
||||
"colorWarningTextActive": "#ab8807",
|
||||
"colorWarningTextHover": "#e8c427",
|
||||
"colorWhite": "#fff",
|
||||
"controlHeight": 32,
|
||||
"controlHeightLG": 40,
|
||||
"controlHeightSM": 24,
|
||||
"controlHeightXS": 16,
|
||||
"controlInteractiveSize": 16,
|
||||
"controlItemBgActive": "#1a3a44",
|
||||
"controlItemBgActiveDisabled": "rgba(255,255,255,0.18)",
|
||||
"controlItemBgActiveHover": "#1d4d5c",
|
||||
"controlItemBgHover": "rgba(255,255,255,0.08)",
|
||||
"controlOutline": "rgba(49,201,249,0.21)",
|
||||
"controlOutlineWidth": 2,
|
||||
"controlPaddingHorizontal": 12,
|
||||
"controlPaddingHorizontalSM": 8,
|
||||
"controlTmpOutline": "rgba(255,255,255,0.04)",
|
||||
"fontFamily": "'Inter', Helvetica, Arial",
|
||||
"fontFamilyCode": "'IBM Plex Mono', 'Courier New', monospace",
|
||||
"fontHeight": 22,
|
||||
"fontHeightLG": 24,
|
||||
"fontHeightSM": 20,
|
||||
"fontSize": 14,
|
||||
"fontSizeHeading1": 38,
|
||||
"fontSizeHeading2": 30,
|
||||
"fontSizeHeading3": 24,
|
||||
"fontSizeHeading4": 20,
|
||||
"fontSizeHeading5": 16,
|
||||
"fontSizeIcon": 12,
|
||||
"fontSizeLG": 16,
|
||||
"fontSizeSM": 12,
|
||||
"fontSizeXL": 20,
|
||||
"fontWeightStrong": 600,
|
||||
"lineHeight": 1.5714285714285714,
|
||||
"lineHeightHeading1": 1.2105263157894737,
|
||||
"lineHeightHeading2": 1.2666666666666666,
|
||||
"lineHeightHeading3": 1.3333333333333333,
|
||||
"lineHeightHeading4": 1.4,
|
||||
"lineHeightHeading5": 1.5,
|
||||
"lineHeightLG": 1.5,
|
||||
"lineHeightSM": 1.6666666666666667,
|
||||
"lineType": "solid",
|
||||
"lineWidth": 1,
|
||||
"lineWidthBold": 2,
|
||||
"lineWidthFocus": 3,
|
||||
"linkDecoration": "none",
|
||||
"linkFocusDecoration": "none",
|
||||
"linkHoverDecoration": "none",
|
||||
"margin": 16,
|
||||
"marginLG": 24,
|
||||
"marginMD": 20,
|
||||
"marginSM": 12,
|
||||
"marginXL": 32,
|
||||
"marginXS": 8,
|
||||
"marginXXL": 48,
|
||||
"marginXXS": 4,
|
||||
"motion": true,
|
||||
"motionBase": 0,
|
||||
"motionDurationFast": "0.1s",
|
||||
"motionDurationMid": "0.2s",
|
||||
"motionDurationSlow": "0.3s",
|
||||
"motionEaseInBack": "cubic-bezier(0.71, -0.46, 0.88, 0.6)",
|
||||
"motionEaseInOut": "cubic-bezier(0.645, 0.045, 0.355, 1)",
|
||||
"motionEaseInOutCirc": "cubic-bezier(0.78, 0.14, 0.15, 0.86)",
|
||||
"motionEaseInQuint": "cubic-bezier(0.755, 0.05, 0.855, 0.06)",
|
||||
"motionEaseOut": "cubic-bezier(0.215, 0.61, 0.355, 1)",
|
||||
"motionEaseOutBack": "cubic-bezier(0.12, 0.4, 0.29, 1.46)",
|
||||
"motionEaseOutCirc": "cubic-bezier(0.08, 0.82, 0.17, 1)",
|
||||
"motionEaseOutQuint": "cubic-bezier(0.23, 1, 0.32, 1)",
|
||||
"motionUnit": 0.1,
|
||||
"opacityImage": 1,
|
||||
"opacityLoading": 0.65,
|
||||
"padding": 16,
|
||||
"paddingContentHorizontal": 16,
|
||||
"paddingContentHorizontalLG": 24,
|
||||
"paddingContentHorizontalSM": 16,
|
||||
"paddingContentVertical": 12,
|
||||
"paddingContentVerticalLG": 16,
|
||||
"paddingContentVerticalSM": 8,
|
||||
"paddingLG": 24,
|
||||
"paddingMD": 20,
|
||||
"paddingSM": 12,
|
||||
"paddingXL": 32,
|
||||
"paddingXS": 8,
|
||||
"paddingXXS": 4,
|
||||
"screenLG": 992,
|
||||
"screenLGMax": 1199,
|
||||
"screenLGMin": 992,
|
||||
"screenMD": 768,
|
||||
"screenMDMax": 991,
|
||||
"screenMDMin": 768,
|
||||
"screenSM": 576,
|
||||
"screenSMMax": 767,
|
||||
"screenSMMin": 576,
|
||||
"screenXL": 1200,
|
||||
"screenXLMax": 1599,
|
||||
"screenXLMin": 1200,
|
||||
"screenXS": 480,
|
||||
"screenXSMax": 575,
|
||||
"screenXSMin": 480,
|
||||
"screenXXL": 1600,
|
||||
"screenXXLMin": 1600,
|
||||
"size": 16,
|
||||
"sizeLG": 24,
|
||||
"sizeMD": 20,
|
||||
"sizeMS": 16,
|
||||
"sizePopupArrow": 16,
|
||||
"sizeSM": 12,
|
||||
"sizeStep": 4,
|
||||
"sizeUnit": 4,
|
||||
"sizeXL": 32,
|
||||
"sizeXS": 8,
|
||||
"sizeXXL": 48,
|
||||
"sizeXXS": 4,
|
||||
"wireframe": false,
|
||||
"zIndexBase": 0,
|
||||
"zIndexPopupBase": 1000,
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`computed light-mode token values match snapshot 1`] = `
|
||||
{
|
||||
"borderRadius": 6,
|
||||
"borderRadiusLG": 8,
|
||||
"borderRadiusOuter": 4,
|
||||
"borderRadiusSM": 4,
|
||||
"borderRadiusXS": 2,
|
||||
"boxShadow": "
|
||||
0 6px 16px 0 rgba(0,0,0,0.08),
|
||||
0 3px 6px -4px rgba(0,0,0,0.12),
|
||||
0 9px 28px 8px rgba(0,0,0,0.05)
|
||||
",
|
||||
"boxShadowDrawerLeft": "
|
||||
6px 0 16px 0 rgba(0,0,0,0.08),
|
||||
3px 0 6px -4px rgba(0,0,0,0.12),
|
||||
9px 0 28px 8px rgba(0,0,0,0.05)
|
||||
",
|
||||
"boxShadowDrawerRight": "
|
||||
-6px 0 16px 0 rgba(0,0,0,0.08),
|
||||
-3px 0 6px -4px rgba(0,0,0,0.12),
|
||||
-9px 0 28px 8px rgba(0,0,0,0.05)
|
||||
",
|
||||
"boxShadowDrawerUp": "
|
||||
0 6px 16px 0 rgba(0,0,0,0.08),
|
||||
0 3px 6px -4px rgba(0,0,0,0.12),
|
||||
0 9px 28px 8px rgba(0,0,0,0.05)
|
||||
",
|
||||
"boxShadowPopoverArrow": "2px 2px 5px rgba(0,0,0,0.05)",
|
||||
"boxShadowSecondary": "
|
||||
0 6px 16px 0 rgba(0,0,0,0.08),
|
||||
0 3px 6px -4px rgba(0,0,0,0.12),
|
||||
0 9px 28px 8px rgba(0,0,0,0.05)
|
||||
",
|
||||
"boxShadowTabsOverflowBottom": "inset 0 -10px 8px -8px rgba(0,0,0,0.08)",
|
||||
"boxShadowTabsOverflowLeft": "inset 10px 0 8px -8px rgba(0,0,0,0.08)",
|
||||
"boxShadowTabsOverflowRight": "inset -10px 0 8px -8px rgba(0,0,0,0.08)",
|
||||
"boxShadowTabsOverflowTop": "inset 0 10px 8px -8px rgba(0,0,0,0.08)",
|
||||
"boxShadowTertiary": "
|
||||
0 1px 2px 0 rgba(0,0,0,0.05),
|
||||
0 1px 6px -1px rgba(0,0,0,0.03),
|
||||
0 2px 4px 0 rgba(0,0,0,0.03)
|
||||
",
|
||||
"colorBgBase": "#fff",
|
||||
"colorBgBlur": "transparent",
|
||||
"colorBgContainer": "#ffffff",
|
||||
"colorBgContainerDisabled": "rgba(0,0,0,0.04)",
|
||||
"colorBgElevated": "#ffffff",
|
||||
"colorBgLayout": "#f5f5f5",
|
||||
"colorBgMask": "rgba(0,0,0,0.45)",
|
||||
"colorBgSpotlight": "rgba(0,0,0,0.85)",
|
||||
"colorBgTextActive": "rgba(0,0,0,0.15)",
|
||||
"colorBgTextHover": "rgba(0,0,0,0.06)",
|
||||
"colorBorder": "#d9d9d9",
|
||||
"colorBorderBg": "#ffffff",
|
||||
"colorBorderSecondary": "#f0f0f0",
|
||||
"colorError": "#e04355",
|
||||
"colorErrorActive": "#ba2f43",
|
||||
"colorErrorBg": "#fff0f0",
|
||||
"colorErrorBgActive": "#ffc7c8",
|
||||
"colorErrorBgHover": "#fff0f0",
|
||||
"colorErrorBorder": "#ffc7c8",
|
||||
"colorErrorBorderHover": "#fa9ba0",
|
||||
"colorErrorHover": "#ed6d78",
|
||||
"colorErrorOutline": "rgba(255,5,5,0.06)",
|
||||
"colorErrorText": "#e04355",
|
||||
"colorErrorTextActive": "#ba2f43",
|
||||
"colorErrorTextHover": "#ed6d78",
|
||||
"colorFill": "rgba(0,0,0,0.15)",
|
||||
"colorFillAlter": "rgba(0,0,0,0.02)",
|
||||
"colorFillContent": "rgba(0,0,0,0.06)",
|
||||
"colorFillContentHover": "rgba(0,0,0,0.15)",
|
||||
"colorFillQuaternary": "rgba(0,0,0,0.02)",
|
||||
"colorFillSecondary": "rgba(0,0,0,0.06)",
|
||||
"colorFillTertiary": "rgba(0,0,0,0.04)",
|
||||
"colorHighlight": "#e04355",
|
||||
"colorIcon": "rgba(0,0,0,0.45)",
|
||||
"colorIconHover": "rgba(0,0,0,0.88)",
|
||||
"colorInfo": "#66bcfe",
|
||||
"colorInfoActive": "#4c97d9",
|
||||
"colorInfoBg": "#f0fbff",
|
||||
"colorInfoBgHover": "#f0faff",
|
||||
"colorInfoBorder": "#e0f5ff",
|
||||
"colorInfoBorderHover": "#b8e5ff",
|
||||
"colorInfoHover": "#b8e5ff",
|
||||
"colorInfoText": "#66bcfe",
|
||||
"colorInfoTextActive": "#4c97d9",
|
||||
"colorInfoTextHover": "#8fd2ff",
|
||||
"colorLink": "#2893B3",
|
||||
"colorLinkActive": "#186d8c",
|
||||
"colorLinkHover": "#6ebccc",
|
||||
"colorPrimary": "#2893B3",
|
||||
"colorPrimaryActive": "#186d8c",
|
||||
"colorPrimaryBg": "#e4f1f2",
|
||||
"colorPrimaryBgHover": "#c5e2e6",
|
||||
"colorPrimaryBorder": "#98d0d9",
|
||||
"colorPrimaryBorderHover": "#6ebccc",
|
||||
"colorPrimaryHover": "#49a8bf",
|
||||
"colorPrimaryText": "#2893b3",
|
||||
"colorPrimaryTextActive": "#186d8c",
|
||||
"colorPrimaryTextHover": "#49a8bf",
|
||||
"colorSplit": "rgba(5,5,5,0.06)",
|
||||
"colorSuccess": "#5ac189",
|
||||
"colorSuccessActive": "#419c6d",
|
||||
"colorSuccessBg": "#f0fff4",
|
||||
"colorSuccessBgHover": "#e6f5eb",
|
||||
"colorSuccessBorder": "#dae8df",
|
||||
"colorSuccessBorderHover": "#addbbf",
|
||||
"colorSuccessHover": "#addbbf",
|
||||
"colorSuccessText": "#5ac189",
|
||||
"colorSuccessTextActive": "#419c6d",
|
||||
"colorSuccessTextHover": "#82cfa2",
|
||||
"colorText": "rgba(0,0,0,0.88)",
|
||||
"colorTextBase": "#000",
|
||||
"colorTextDescription": "rgba(0,0,0,0.45)",
|
||||
"colorTextDisabled": "rgba(0,0,0,0.25)",
|
||||
"colorTextHeading": "rgba(0,0,0,0.88)",
|
||||
"colorTextLabel": "rgba(0,0,0,0.65)",
|
||||
"colorTextLightSolid": "#fff",
|
||||
"colorTextPlaceholder": "rgba(0,0,0,0.25)",
|
||||
"colorTextQuaternary": "rgba(0,0,0,0.25)",
|
||||
"colorTextSecondary": "rgba(0,0,0,0.65)",
|
||||
"colorTextTertiary": "rgba(0,0,0,0.45)",
|
||||
"colorWarning": "#fcc700",
|
||||
"colorWarningActive": "#d6a100",
|
||||
"colorWarningBg": "#fffee6",
|
||||
"colorWarningBgHover": "#fff7a3",
|
||||
"colorWarningBorder": "#fff07a",
|
||||
"colorWarningBorderHover": "#ffe552",
|
||||
"colorWarningHover": "#ffe552",
|
||||
"colorWarningOutline": "rgba(255,245,5,0.1)",
|
||||
"colorWarningText": "#fcc700",
|
||||
"colorWarningTextActive": "#d6a100",
|
||||
"colorWarningTextHover": "#ffd829",
|
||||
"colorWhite": "#fff",
|
||||
"controlHeight": 32,
|
||||
"controlHeightLG": 40,
|
||||
"controlHeightSM": 24,
|
||||
"controlHeightXS": 16,
|
||||
"controlInteractiveSize": 16,
|
||||
"controlItemBgActive": "#e4f1f2",
|
||||
"controlItemBgActiveDisabled": "rgba(0,0,0,0.15)",
|
||||
"controlItemBgActiveHover": "#c5e2e6",
|
||||
"controlItemBgHover": "rgba(0,0,0,0.04)",
|
||||
"controlOutline": "rgba(10,128,137,0.11)",
|
||||
"controlOutlineWidth": 2,
|
||||
"controlPaddingHorizontal": 12,
|
||||
"controlPaddingHorizontalSM": 8,
|
||||
"controlTmpOutline": "rgba(0,0,0,0.02)",
|
||||
"fontFamily": "'Inter', Helvetica, Arial",
|
||||
"fontFamilyCode": "'IBM Plex Mono', 'Courier New', monospace",
|
||||
"fontHeight": 22,
|
||||
"fontHeightLG": 24,
|
||||
"fontHeightSM": 20,
|
||||
"fontSize": 14,
|
||||
"fontSizeHeading1": 38,
|
||||
"fontSizeHeading2": 30,
|
||||
"fontSizeHeading3": 24,
|
||||
"fontSizeHeading4": 20,
|
||||
"fontSizeHeading5": 16,
|
||||
"fontSizeIcon": 12,
|
||||
"fontSizeLG": 16,
|
||||
"fontSizeSM": 12,
|
||||
"fontSizeXL": 20,
|
||||
"fontWeightStrong": 600,
|
||||
"lineHeight": 1.5714285714285714,
|
||||
"lineHeightHeading1": 1.2105263157894737,
|
||||
"lineHeightHeading2": 1.2666666666666666,
|
||||
"lineHeightHeading3": 1.3333333333333333,
|
||||
"lineHeightHeading4": 1.4,
|
||||
"lineHeightHeading5": 1.5,
|
||||
"lineHeightLG": 1.5,
|
||||
"lineHeightSM": 1.6666666666666667,
|
||||
"lineType": "solid",
|
||||
"lineWidth": 1,
|
||||
"lineWidthBold": 2,
|
||||
"lineWidthFocus": 3,
|
||||
"linkDecoration": "none",
|
||||
"linkFocusDecoration": "none",
|
||||
"linkHoverDecoration": "none",
|
||||
"margin": 16,
|
||||
"marginLG": 24,
|
||||
"marginMD": 20,
|
||||
"marginSM": 12,
|
||||
"marginXL": 32,
|
||||
"marginXS": 8,
|
||||
"marginXXL": 48,
|
||||
"marginXXS": 4,
|
||||
"motion": true,
|
||||
"motionBase": 0,
|
||||
"motionDurationFast": "0.1s",
|
||||
"motionDurationMid": "0.2s",
|
||||
"motionDurationSlow": "0.3s",
|
||||
"motionEaseInBack": "cubic-bezier(0.71, -0.46, 0.88, 0.6)",
|
||||
"motionEaseInOut": "cubic-bezier(0.645, 0.045, 0.355, 1)",
|
||||
"motionEaseInOutCirc": "cubic-bezier(0.78, 0.14, 0.15, 0.86)",
|
||||
"motionEaseInQuint": "cubic-bezier(0.755, 0.05, 0.855, 0.06)",
|
||||
"motionEaseOut": "cubic-bezier(0.215, 0.61, 0.355, 1)",
|
||||
"motionEaseOutBack": "cubic-bezier(0.12, 0.4, 0.29, 1.46)",
|
||||
"motionEaseOutCirc": "cubic-bezier(0.08, 0.82, 0.17, 1)",
|
||||
"motionEaseOutQuint": "cubic-bezier(0.23, 1, 0.32, 1)",
|
||||
"motionUnit": 0.1,
|
||||
"opacityImage": 1,
|
||||
"opacityLoading": 0.65,
|
||||
"padding": 16,
|
||||
"paddingContentHorizontal": 16,
|
||||
"paddingContentHorizontalLG": 24,
|
||||
"paddingContentHorizontalSM": 16,
|
||||
"paddingContentVertical": 12,
|
||||
"paddingContentVerticalLG": 16,
|
||||
"paddingContentVerticalSM": 8,
|
||||
"paddingLG": 24,
|
||||
"paddingMD": 20,
|
||||
"paddingSM": 12,
|
||||
"paddingXL": 32,
|
||||
"paddingXS": 8,
|
||||
"paddingXXS": 4,
|
||||
"screenLG": 992,
|
||||
"screenLGMax": 1199,
|
||||
"screenLGMin": 992,
|
||||
"screenMD": 768,
|
||||
"screenMDMax": 991,
|
||||
"screenMDMin": 768,
|
||||
"screenSM": 576,
|
||||
"screenSMMax": 767,
|
||||
"screenSMMin": 576,
|
||||
"screenXL": 1200,
|
||||
"screenXLMax": 1599,
|
||||
"screenXLMin": 1200,
|
||||
"screenXS": 480,
|
||||
"screenXSMax": 575,
|
||||
"screenXSMin": 480,
|
||||
"screenXXL": 1600,
|
||||
"screenXXLMin": 1600,
|
||||
"size": 16,
|
||||
"sizeLG": 24,
|
||||
"sizeMD": 20,
|
||||
"sizeMS": 16,
|
||||
"sizePopupArrow": 16,
|
||||
"sizeSM": 12,
|
||||
"sizeStep": 4,
|
||||
"sizeUnit": 4,
|
||||
"sizeXL": 32,
|
||||
"sizeXS": 8,
|
||||
"sizeXXL": 48,
|
||||
"sizeXXS": 4,
|
||||
"wireframe": false,
|
||||
"zIndexBase": 0,
|
||||
"zIndexPopupBase": 1000,
|
||||
}
|
||||
`;
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { theme as antdThemeImport } from 'antd';
|
||||
import { theme as antdThemeImport, type MappingAlgorithm } from 'antd';
|
||||
import {
|
||||
type AntdThemeConfig,
|
||||
type AnyThemeConfig,
|
||||
@@ -48,8 +48,8 @@ export function isSerializableConfig(
|
||||
export function deserializeThemeConfig(
|
||||
config: SerializableThemeConfig,
|
||||
): AntdThemeConfig {
|
||||
const { algorithm, ...rest } = config;
|
||||
const algorithmMap: Record<string, any> = {
|
||||
const { algorithm, cssVar, ...rest } = config;
|
||||
const algorithmMap: Record<string, MappingAlgorithm> = {
|
||||
default: antdThemeImport.defaultAlgorithm,
|
||||
dark: antdThemeImport.darkAlgorithm,
|
||||
compact: antdThemeImport.compactAlgorithm,
|
||||
@@ -74,9 +74,17 @@ export function deserializeThemeConfig(
|
||||
resolvedAlgorithm = antdThemeImport.defaultAlgorithm;
|
||||
}
|
||||
|
||||
// Ant Design v6 dropped `boolean` from ThemeConfig['cssVar'] (it is now
|
||||
// object-only, and CSS variables are enabled by default). Superset keeps a
|
||||
// boolean-friendly serializable API, so coerce at the antd boundary:
|
||||
// `true` -> `{}` (defaults), `false`/undefined -> omit (antd default).
|
||||
const normalizedCssVar =
|
||||
typeof cssVar === 'boolean' ? (cssVar ? {} : undefined) : cssVar;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
algorithm: resolvedAlgorithm,
|
||||
...(normalizedCssVar !== undefined ? { cssVar: normalizedCssVar } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"lodash-es": "^4.18.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ant-design/icons": "^5.6.1",
|
||||
"@ant-design/icons": "^5.6.1 || ^6.0.0",
|
||||
"@emotion/react": "^11.4.1",
|
||||
"@superset-ui/core": "*",
|
||||
"@testing-library/dom": "^9.3.4",
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"react-ace": "^14.0.1",
|
||||
"react-draggable": "^4.7.0",
|
||||
"react-error-boundary": "^6.1.2",
|
||||
"react-js-cron": "^5.2.0",
|
||||
"react-js-cron": "^6.0.2",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-resize-detector": "^7.1.2",
|
||||
"react-syntax-highlighter": "^16.1.1",
|
||||
@@ -78,7 +78,7 @@
|
||||
"@types/d3-time-format": "^4.0.3",
|
||||
"@types/jquery": "^4.0.1",
|
||||
"@types/lodash": "^4.17.24",
|
||||
"@types/node": "^26.1.0",
|
||||
"@types/node": "^26.1.1",
|
||||
"@types/prop-types": "^15.7.15",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@types/react-table": "^7.7.20",
|
||||
@@ -100,7 +100,7 @@
|
||||
"@types/react-loadable": "*",
|
||||
"@types/react-window": "^1.8.8",
|
||||
"@types/tinycolor2": "*",
|
||||
"antd": "^5.26.0",
|
||||
"antd": "^6.0.0",
|
||||
"nanoid": "^5.0.9",
|
||||
"react": "^18.3.0",
|
||||
"react-dom": "^18.3.0",
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ const MatrixNoDataComponent = () => {
|
||||
* Individual grid cell component - memoized to prevent unnecessary re-renders
|
||||
*/
|
||||
const MatrixifyGridCell = memo(
|
||||
({ cell, rowHeight, datasource, hooks }: MatrixifyGridCellProps) => {
|
||||
({ cell, hooks }: MatrixifyGridCellProps) => {
|
||||
// Use computed title from template (will be empty string if no template)
|
||||
const cellLabel = cell.title || '';
|
||||
|
||||
|
||||
+1
-2
@@ -19,8 +19,7 @@
|
||||
|
||||
import { render } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom';
|
||||
import { ThemeProvider } from '@apache-superset/core/theme';
|
||||
import { supersetTheme } from '@apache-superset/core/theme';
|
||||
import { ThemeProvider, supersetTheme } from '@apache-superset/core/theme';
|
||||
import MatrixifyGridRenderer from './MatrixifyGridRenderer';
|
||||
import type { MatrixifyMode } from '../../types/matrixify';
|
||||
import { generateMatrixifyGrid } from './MatrixifyGridGenerator';
|
||||
|
||||
+1
-2
@@ -118,7 +118,6 @@ interface MatrixifyGridRendererProps {
|
||||
function MatrixifyGridRenderer({
|
||||
formData,
|
||||
datasource,
|
||||
width,
|
||||
height,
|
||||
hooks,
|
||||
}: MatrixifyGridRendererProps) {
|
||||
@@ -249,7 +248,7 @@ function MatrixifyGridRenderer({
|
||||
{/* Row cells for this column group */}
|
||||
{row
|
||||
.slice(colGroup.startIdx, colGroup.endIdx)
|
||||
.map((cell, colIdx) =>
|
||||
.map(cell =>
|
||||
cell ? (
|
||||
<MatrixifyGridCell
|
||||
key={cell.id}
|
||||
|
||||
+1
-2
@@ -32,8 +32,7 @@ import {
|
||||
wordCloudFormData,
|
||||
} from '../../../../superset-ui-core/test/chart/fixtures/formData';
|
||||
|
||||
import { Expandable } from '@storybook-shared';
|
||||
import { VerifyCORS, renderError } from '@storybook-shared';
|
||||
import { Expandable, VerifyCORS, renderError } from '@storybook-shared';
|
||||
|
||||
const BIG_NUMBER = bigNumberFormData.viz_type;
|
||||
const SANKEY = sankeyFormData.viz_type;
|
||||
|
||||
+13
-1
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { render, screen, userEvent } from '@superset-ui/core/spec';
|
||||
import { render, screen, userEvent, fireEvent } from '@superset-ui/core/spec';
|
||||
import { Icons } from '@superset-ui/core/components/Icons';
|
||||
import { ActionButton } from '.';
|
||||
|
||||
@@ -45,6 +45,18 @@ test('calls onClick when clicked', async () => {
|
||||
expect(onClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('calls onClick when activated with the keyboard', () => {
|
||||
const onClick = jest.fn();
|
||||
render(<ActionButton {...defaultProps} onClick={onClick} />);
|
||||
|
||||
const button = screen.getByRole('button');
|
||||
fireEvent.keyDown(button, { key: 'Enter' });
|
||||
expect(onClick).toHaveBeenCalledTimes(1);
|
||||
|
||||
fireEvent.keyDown(button, { key: ' ' });
|
||||
expect(onClick).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
test('renders with tooltip when tooltip prop is provided', async () => {
|
||||
const tooltipText = 'This is a tooltip';
|
||||
render(<ActionButton {...defaultProps} tooltip={tooltipText} />);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { handleKeyboardActivation } from '../../utils';
|
||||
import type { ReactElement, ReactNode } from 'react';
|
||||
import { Tooltip, type TooltipPlacement } from '@superset-ui/core/components';
|
||||
import { css, useTheme } from '@apache-superset/core/theme';
|
||||
@@ -55,6 +56,7 @@ export const ActionButton = ({
|
||||
className="action-button"
|
||||
data-test={label}
|
||||
onClick={onClick}
|
||||
onKeyDown={onClick ? handleKeyboardActivation(onClick) : undefined}
|
||||
>
|
||||
{icon}
|
||||
</span>
|
||||
|
||||
@@ -444,7 +444,6 @@ export function AsyncAceEditor(
|
||||
/* Adjust tooltip styles */
|
||||
.ace_tooltip {
|
||||
margin-left: ${token.margin}px;
|
||||
padding: ${token.sizeUnit * 2}px;
|
||||
background-color: ${token.colorBgElevated} !important;
|
||||
color: ${token.colorText} !important;
|
||||
border: 1px solid ${token.colorBorderSecondary};
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { fireEvent, render } from '@superset-ui/core/spec';
|
||||
import { Button } from '.';
|
||||
import {
|
||||
ButtonGallery,
|
||||
SIZES as buttonSizes,
|
||||
STYLES as buttonStyles,
|
||||
} from './Button.stories';
|
||||
import {
|
||||
Button,
|
||||
getSecondaryButtonStyle,
|
||||
getSecondaryButtonHoverStyles,
|
||||
} from './index';
|
||||
|
||||
+4
-2
@@ -82,8 +82,10 @@ test('collapses on click', async () => {
|
||||
|
||||
await userEvent.click(screen.getAllByRole('button')[0]);
|
||||
|
||||
expect(screen.getByText('Content 1').parentNode).toHaveClass(
|
||||
'ant-collapse-content-hidden',
|
||||
// antd v6 moved the hidden state to the panel wrapper element
|
||||
// (ant-collapse-panel-hidden) instead of the content wrapper.
|
||||
expect(screen.getByText('Content 1').parentElement).toHaveClass(
|
||||
'ant-collapse-panel-hidden',
|
||||
);
|
||||
expect(screen.queryByText('Content 2')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -52,10 +52,10 @@ const StyledCollapse = styled((props: CollapseProps) => (
|
||||
`
|
||||
border-bottom: 1px solid ${theme.colorBorderSecondary};
|
||||
`}
|
||||
.ant-collapse-content {
|
||||
.ant-collapse-panel {
|
||||
color: ${({ theme }) => theme.colorText};
|
||||
|
||||
.ant-collapse-content-box {
|
||||
.ant-collapse-body {
|
||||
.loading.inline {
|
||||
margin: ${({ theme }) => theme.sizeUnit * 12}px auto;
|
||||
display: block;
|
||||
|
||||
+23
-1
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { render } from '@superset-ui/core/spec';
|
||||
import { render, waitFor } from '@superset-ui/core/spec';
|
||||
import * as ReactCronPicker from 'react-js-cron';
|
||||
import { CronPicker } from '.';
|
||||
|
||||
@@ -37,3 +37,25 @@ test('Should send correct props to ReactCronPicker', () => {
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
// The invalid-cron error styling was silently dead under antd 6 until
|
||||
// react-js-cron was bumped to v6 (its v5 stylesheet targeted antd 5 Select
|
||||
// classes that no longer exist). Pin the error contract: an invalid value
|
||||
// must fire onError and apply the vendor's error class — the hook that both
|
||||
// the vendor stylesheet and Superset's overrides style the error state
|
||||
// through.
|
||||
test('flags an invalid cron value with the error state', async () => {
|
||||
const onError = jest.fn();
|
||||
const { container } = render(
|
||||
<CronPicker value="not a cron" setValue={jest.fn()} onError={onError} />,
|
||||
);
|
||||
await waitFor(() =>
|
||||
expect(onError).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ type: expect.any(String) }),
|
||||
),
|
||||
);
|
||||
expect(
|
||||
// eslint-disable-next-line testing-library/no-node-access
|
||||
container.querySelector('.react-js-cron-error'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -175,7 +175,7 @@ export const CronPicker = styled((props: CronProps) => (
|
||||
|
||||
.react-js-cron-select.ant-select {
|
||||
width: 100%;
|
||||
.ant-select-selector {
|
||||
.ant-select-content {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
}
|
||||
@@ -188,12 +188,12 @@ export const CronPicker = styled((props: CronProps) => (
|
||||
}
|
||||
}
|
||||
|
||||
.react-js-cron-custom-select .ant-select-selection-placeholder {
|
||||
.react-js-cron-custom-select .ant-select-placeholder {
|
||||
flex: auto;
|
||||
border-radius: ${theme.borderRadius}px;
|
||||
}
|
||||
|
||||
.react-js-cron-custom-select .ant-select-selection-overflow-item {
|
||||
.react-js-cron-custom-select .ant-select-content-item {
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -338,9 +338,9 @@ export const DropdownContainer = forwardRef(
|
||||
<>
|
||||
<Global
|
||||
styles={css`
|
||||
.ant-popover-inner {
|
||||
// Some OS versions only show the scroll when hovering.
|
||||
// These settings will make the scroll always visible.
|
||||
.ant-popover-container {
|
||||
/* Some OS versions only show the scroll when hovering. */
|
||||
/* These settings will make the scroll always visible. */
|
||||
::-webkit-scrollbar {
|
||||
-webkit-appearance: none;
|
||||
width: 14px;
|
||||
@@ -361,7 +361,8 @@ export const DropdownContainer = forwardRef(
|
||||
|
||||
<Popover
|
||||
styles={{
|
||||
body: {
|
||||
// antd v6 renamed the inner content slot `body` -> `container`
|
||||
container: {
|
||||
maxHeight: `${MAX_HEIGHT}px`,
|
||||
overflow: showOverflow ? 'auto' : 'visible',
|
||||
},
|
||||
|
||||
@@ -60,6 +60,7 @@ export const FaveStar = ({
|
||||
className="fave-unfave-icon"
|
||||
data-test="fave-unfave-icon"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
{isStarred ? (
|
||||
<Icons.StarFilled
|
||||
|
||||
+8
@@ -28,6 +28,10 @@ const StyledInput = styled(Input)`
|
||||
margin: ${({ theme }) => `${theme.sizeUnit}px 0 ${theme.sizeUnit * 2}px`};
|
||||
`;
|
||||
|
||||
const StyledTextArea = styled(Input.TextArea)`
|
||||
margin: ${({ theme }) => `${theme.sizeUnit}px 0 ${theme.sizeUnit * 2}px`};
|
||||
`;
|
||||
|
||||
const StyledInputPassword = styled(Input.Password)`
|
||||
margin: ${({ theme }) => `${theme.sizeUnit}px 0 ${theme.sizeUnit * 2}px`};
|
||||
`;
|
||||
@@ -62,6 +66,8 @@ export const LabeledErrorBoundInput = ({
|
||||
get_url,
|
||||
description,
|
||||
isValidating = false,
|
||||
renderAsTextArea,
|
||||
textAreaCss,
|
||||
...props
|
||||
}: LabeledErrorBoundInputProps) => {
|
||||
const hasError = !!errorMessage;
|
||||
@@ -98,6 +104,8 @@ export const LabeledErrorBoundInput = ({
|
||||
}
|
||||
role="textbox"
|
||||
/>
|
||||
) : renderAsTextArea ? (
|
||||
<StyledTextArea css={textAreaCss} {...props} {...validationMethods} />
|
||||
) : (
|
||||
<StyledInput {...props} {...validationMethods} />
|
||||
)}
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import type { SerializedStyles } from '@emotion/react';
|
||||
|
||||
export type { FormProps, FormInstance, FormItemProps } from 'antd/es/form';
|
||||
export type { SerializedStyles };
|
||||
|
||||
export interface LabeledErrorBoundInputProps {
|
||||
label?: string;
|
||||
@@ -31,5 +34,7 @@ export interface LabeledErrorBoundInputProps {
|
||||
classname?: string;
|
||||
visibilityToggle?: boolean;
|
||||
isValidating?: boolean;
|
||||
renderAsTextArea?: boolean;
|
||||
textAreaCss?: SerializedStyles;
|
||||
[x: string]: any;
|
||||
}
|
||||
|
||||
@@ -63,11 +63,11 @@ const IconBlock = styled.div`
|
||||
|
||||
span {
|
||||
margin-top: ${({ theme }) =>
|
||||
2 * theme.sizeUnit}px; // Add spacing between icon and name
|
||||
2 * theme.sizeUnit}px; /* Add spacing between icon and name */
|
||||
font-size: ${({ theme }) =>
|
||||
theme.fontSizeSM}; // Optional: adjust font size for elegance
|
||||
theme.fontSizeSM}; /* Optional: adjust font size for elegance */
|
||||
color: ${({ theme }) =>
|
||||
theme.colorText}; // Optional: subtle color for the name
|
||||
theme.colorText}; /* Optional: subtle color for the name */
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
import { fireEvent, render } from '@superset-ui/core/spec';
|
||||
|
||||
import { Icons } from '../Icons';
|
||||
import { Label } from '.';
|
||||
import { LabelGallery, options } from './Label.stories';
|
||||
|
||||
@@ -39,6 +40,40 @@ test('renders with monospace prop', () => {
|
||||
expect(getByText('monospace text')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Regression: Ant Design v6's Tag clones the element passed via its `icon` prop
|
||||
// and overwrites that element's inline `style`, which silently dropped the
|
||||
// icon's own color. Label wraps the icon in a span so the wrapper (not the
|
||||
// icon) is Tag's clone target and the icon keeps its explicit color.
|
||||
test('preserves a custom icon color (antd v6 Tag icon-style regression)', () => {
|
||||
const { container } = render(
|
||||
<Label icon={<Icons.CheckCircleOutlined iconColor="#aabbcc" />}>
|
||||
labeled
|
||||
</Label>,
|
||||
);
|
||||
expect(container.querySelector('[role="img"]')).toHaveStyle({
|
||||
color: '#aabbcc',
|
||||
});
|
||||
});
|
||||
|
||||
// Regression: wrapping the icon in a span (see above) stops antd's
|
||||
// `> .anticon + span` rule from matching, which had spaced the icon from the
|
||||
// label text. Label restores that gap on the content span next to the icon.
|
||||
test('keeps a gap between the icon and the label text (#42139)', () => {
|
||||
const { getByText } = render(
|
||||
<Label icon={<Icons.CheckCircleOutlined iconColor="#aabbcc" />}>
|
||||
labeled
|
||||
</Label>,
|
||||
);
|
||||
expect(getByText('labeled')).toHaveStyle({ marginInlineStart: '8px' });
|
||||
});
|
||||
|
||||
// The icon gap must not leak onto icon-less labels: the content spacing is
|
||||
// gated on `icon`, so a label without an icon keeps its natural spacing.
|
||||
test('does not add the icon gap to an icon-less label (#42139)', () => {
|
||||
const { getByText } = render(<Label>unlabeled</Label>);
|
||||
expect(getByText('unlabeled')).not.toHaveStyle({ marginInlineStart: '8px' });
|
||||
});
|
||||
|
||||
// test stories from the storybook!
|
||||
test('renders all the storybook gallery variants', () => {
|
||||
const { container } = render(<LabelGallery />);
|
||||
|
||||
@@ -34,7 +34,7 @@ export const Label = forwardRef<HTMLSpanElement, LabelProps>((props, ref) => {
|
||||
onClick,
|
||||
children,
|
||||
icon,
|
||||
id,
|
||||
id: _id,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
@@ -76,7 +76,40 @@ export const Label = forwardRef<HTMLSpanElement, LabelProps>((props, ref) => {
|
||||
onClick={onClick}
|
||||
role={onClick ? 'button' : undefined}
|
||||
style={style}
|
||||
icon={icon}
|
||||
/*
|
||||
* Ant Design v6's Tag renders the label text in a content span and spaces
|
||||
* it from the icon via the `> .anticon + span` rule. That rule only matches
|
||||
* when the icon is a direct `.anticon` child, which the wrapper span below
|
||||
* breaks, so the gap is restored explicitly on the content span. Gate it on
|
||||
* `icon` so icon-less labels keep their natural spacing regardless of how
|
||||
* antd renders the content slot.
|
||||
*/
|
||||
styles={
|
||||
icon
|
||||
? { content: { marginInlineStart: theme.sizeUnit * 2 } }
|
||||
: undefined
|
||||
}
|
||||
/*
|
||||
* Ant Design v6's Tag clones the `icon` element and overrides its inline
|
||||
* `style` (see antd/es/tag: cloneElement(icon, { style: mergedStyles.icon })),
|
||||
* which would drop the icon's own color/size. Wrapping the icon in a span
|
||||
* lets Tag override the (empty) wrapper style while the real icon keeps its
|
||||
* own inline styling.
|
||||
*/
|
||||
icon={
|
||||
icon ? (
|
||||
<span
|
||||
css={css`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
`}
|
||||
>
|
||||
{icon}
|
||||
</span>
|
||||
) : (
|
||||
icon
|
||||
)
|
||||
}
|
||||
css={labelStyles}
|
||||
{...rest}
|
||||
>
|
||||
|
||||
@@ -120,8 +120,8 @@ export const StyledModal = styled(BaseModal)<StyledModalProps>`
|
||||
right: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
// Keep the close button clickable when modal body content uses
|
||||
// position: sticky with elevated z-index (e.g. DatabaseModal header).
|
||||
/* Keep the close button clickable when modal body content uses */
|
||||
/* position: sticky with elevated z-index (e.g. DatabaseModal header). */
|
||||
z-index: ${theme.zIndexPopupBase + 1};
|
||||
}
|
||||
|
||||
@@ -344,6 +344,8 @@ const CustomModal = ({
|
||||
className="draggable-trigger"
|
||||
onMouseOver={() => dragDisabled && setDragDisabled(false)}
|
||||
onMouseOut={() => !dragDisabled && setDragDisabled(true)}
|
||||
onFocus={() => dragDisabled && setDragDisabled(false)}
|
||||
onBlur={() => !dragDisabled && setDragDisabled(true)}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
|
||||
+7
-1
@@ -80,6 +80,8 @@ test('stops propagation of navigation keys to parent elements', async () => {
|
||||
const handleParentKeyDown = jest.fn();
|
||||
|
||||
render(
|
||||
// Test-only harness div asserting keydown doesn't bubble past the modal.
|
||||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
||||
<div onKeyDown={handleParentKeyDown}>
|
||||
<ModalTrigger
|
||||
triggerNode={<span>Trigger</span>}
|
||||
@@ -106,7 +108,11 @@ test('stops propagation of navigation keys to parent elements', async () => {
|
||||
expect(handleParentKeyDown).not.toHaveBeenCalled();
|
||||
}
|
||||
|
||||
const allowedKeys = ['Escape', 'Tab'];
|
||||
// `Tab` must be checked before `Escape`: pressing `Escape` legitimately
|
||||
// closes the modal (a real global listener unmounts the dialog), so any
|
||||
// key fired on the stale `input` reference afterwards can no longer
|
||||
// bubble anywhere.
|
||||
const allowedKeys = ['Tab', 'Escape'];
|
||||
|
||||
for (const key of allowedKeys) {
|
||||
handleParentKeyDown.mockClear();
|
||||
|
||||
@@ -16,12 +16,13 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { handleKeyboardActivation } from '../../utils';
|
||||
import {
|
||||
forwardRef,
|
||||
ForwardedRef,
|
||||
useState,
|
||||
ReactNode,
|
||||
MouseEvent,
|
||||
SyntheticEvent,
|
||||
} from 'react';
|
||||
|
||||
import { Button } from '../Button';
|
||||
@@ -96,7 +97,7 @@ export const ModalTrigger = forwardRef(
|
||||
onExit?.();
|
||||
};
|
||||
|
||||
const open = (e: MouseEvent) => {
|
||||
const open = (e: SyntheticEvent) => {
|
||||
e.preventDefault();
|
||||
beforeOpen?.();
|
||||
setShowModal(true);
|
||||
@@ -126,7 +127,13 @@ export const ModalTrigger = forwardRef(
|
||||
</Button>
|
||||
)}
|
||||
{!isButton && (
|
||||
<div data-test="span-modal-trigger" onClick={open} role="button">
|
||||
<div
|
||||
data-test="span-modal-trigger"
|
||||
onClick={open}
|
||||
onKeyDown={handleKeyboardActivation(open)}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
{triggerNode}
|
||||
</div>
|
||||
)}
|
||||
@@ -149,6 +156,9 @@ export const ModalTrigger = forwardRef(
|
||||
draggableConfig={draggableConfig}
|
||||
destroyOnHidden={destroyOnHidden}
|
||||
>
|
||||
{/* Pure event-boundary wrapper: only stops menu-navigation keys
|
||||
from bubbling out of the modal, not itself interactive. */}
|
||||
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */}
|
||||
<div
|
||||
style={{ display: 'contents' }}
|
||||
onKeyDown={e => {
|
||||
|
||||
+2
-2
@@ -41,7 +41,7 @@ const defaultProps: PageHeaderWithActionsProps = {
|
||||
data-test="additional-actions-menu"
|
||||
/>
|
||||
),
|
||||
menuDropdownProps: { onVisibleChange: jest.fn(), visible: true },
|
||||
menuDropdownProps: { onOpenChange: jest.fn(), open: true },
|
||||
};
|
||||
|
||||
test('Renders', async () => {
|
||||
@@ -52,5 +52,5 @@ test('Renders', async () => {
|
||||
expect(screen.getByText('Save')).toBeVisible();
|
||||
|
||||
await userEvent.click(screen.getByLabelText('Menu actions trigger'));
|
||||
expect(defaultProps.menuDropdownProps.onVisibleChange).toHaveBeenCalled();
|
||||
expect(defaultProps.menuDropdownProps.onOpenChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { MouseEventHandler, ReactNode } from 'react';
|
||||
import { handleKeyboardActivation } from '../../utils';
|
||||
import { ReactNode, SyntheticEvent } from 'react';
|
||||
import { css, useTheme } from '@apache-superset/core/theme';
|
||||
import { Icons } from '@superset-ui/core/components/Icons';
|
||||
import { Tooltip } from '../Tooltip';
|
||||
@@ -24,7 +25,10 @@ import { Tooltip } from '../Tooltip';
|
||||
export interface PopoverSectionProps {
|
||||
title: string;
|
||||
isSelected?: boolean;
|
||||
onSelect?: MouseEventHandler<HTMLDivElement>;
|
||||
// `SyntheticEvent` (rather than `MouseEventHandler`) so the same callback
|
||||
// can be reused as the keyboard-activation handler via
|
||||
// `handleKeyboardActivation`, which invokes it with a `KeyboardEvent`.
|
||||
onSelect?: (event: SyntheticEvent) => void;
|
||||
info?: string;
|
||||
children?: ReactNode;
|
||||
}
|
||||
@@ -48,6 +52,7 @@ export default function PopoverSection({
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={onSelect}
|
||||
onKeyDown={onSelect ? handleKeyboardActivation(onSelect) : undefined}
|
||||
css={css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -36,6 +36,7 @@ const RefreshLabel = ({
|
||||
<Icons.SyncOutlined
|
||||
iconSize="l"
|
||||
role="button"
|
||||
tabIndex={disabled ? -1 : 0}
|
||||
onClick={disabled ? undefined : onClick}
|
||||
css={(theme: SupersetTheme) => ({
|
||||
cursor: 'pointer',
|
||||
|
||||
+24
-12
@@ -119,10 +119,22 @@ const findAllSelectOptions = () =>
|
||||
waitFor(() => getElementsByClassName('.ant-select-item-option-content'));
|
||||
|
||||
const findSelectValue = () =>
|
||||
waitFor(() => getElementByClassName('.ant-select-selection-item'));
|
||||
// antd v6: single-mode value is `.ant-select-content-has-value`, multiple-mode
|
||||
// tags remain `.ant-select-selection-item`.
|
||||
waitFor(() =>
|
||||
getElementByClassName(
|
||||
'.ant-select-content-has-value, .ant-select-selection-item',
|
||||
),
|
||||
);
|
||||
|
||||
const findAllSelectValues = () =>
|
||||
waitFor(() => getElementsByClassName('.ant-select-selection-item'));
|
||||
// antd v6: multiple-mode tags keep `.ant-select-selection-item`, single-mode
|
||||
// value is `.ant-select-content-has-value`.
|
||||
waitFor(() =>
|
||||
getElementsByClassName(
|
||||
'.ant-select-selection-item, .ant-select-content-has-value',
|
||||
),
|
||||
);
|
||||
|
||||
const clearAll = () => userEvent.click(screen.getByLabelText('close-circle'));
|
||||
|
||||
@@ -381,7 +393,7 @@ test('searches for custom fields', async () => {
|
||||
|
||||
test('removes duplicated values', async () => {
|
||||
render(<AsyncSelect {...defaultProps} mode="multiple" allowNewOptions />);
|
||||
const input = getElementByClassName('.ant-select-selection-search-input');
|
||||
const input = getElementByClassName('.ant-select-input');
|
||||
const paste = createEvent.paste(input, {
|
||||
clipboardData: {
|
||||
getData: () => 'a,b,b,b,c,d,d',
|
||||
@@ -400,7 +412,7 @@ test('removes duplicated values', async () => {
|
||||
|
||||
test('trims whitespace from pasted comma-separated values', async () => {
|
||||
render(<AsyncSelect {...defaultProps} mode="multiple" allowNewOptions />);
|
||||
const input = getElementByClassName('.ant-select-selection-search-input');
|
||||
const input = getElementByClassName('.ant-select-input');
|
||||
const paste = createEvent.paste(input, {
|
||||
clipboardData: {
|
||||
getData: () => 'a, b, c , d',
|
||||
@@ -833,7 +845,7 @@ test('Renders only an overflow tag if dropdown is open in oneLine mode', async (
|
||||
);
|
||||
await open();
|
||||
|
||||
const withinSelector = within(getElementByClassName('.ant-select-selector'));
|
||||
const withinSelector = within(getElementByClassName('.ant-select-content'));
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
withinSelector.queryByText(OPTIONS[0].label),
|
||||
@@ -906,7 +918,7 @@ test('fires onChange when pasting a selection', async () => {
|
||||
const onChange = jest.fn();
|
||||
render(<AsyncSelect {...defaultProps} onChange={onChange} />);
|
||||
await open();
|
||||
const input = getElementByClassName('.ant-select-selection-search-input');
|
||||
const input = getElementByClassName('.ant-select-input');
|
||||
const paste = createEvent.paste(input, {
|
||||
clipboardData: {
|
||||
getData: () => OPTIONS[0].label,
|
||||
@@ -1183,7 +1195,7 @@ test('keeps loading indicator while a newer request is in flight after a stale r
|
||||
});
|
||||
|
||||
const isSpinnerVisible = (): boolean =>
|
||||
Boolean(document.querySelector('.ant-select-arrow .ant-spin'));
|
||||
Boolean(document.querySelector('.ant-select-suffix .ant-spin'));
|
||||
|
||||
try {
|
||||
render(<AsyncSelect {...defaultProps} options={loadOptions} />);
|
||||
@@ -1331,7 +1343,7 @@ test('appends page>1 results during an active search and discards them when sear
|
||||
// Wait for loading to finish so handlePagination's `!isLoading` gate is
|
||||
// open before we fire scroll.
|
||||
await waitFor(() =>
|
||||
expect(document.querySelector('.ant-select-arrow .ant-spin')).toBeNull(),
|
||||
expect(document.querySelector('.ant-select-suffix .ant-spin')).toBeNull(),
|
||||
);
|
||||
|
||||
// Trigger pagination by dispatching a scroll event on the virtual-list
|
||||
@@ -1411,7 +1423,7 @@ test('pasting an existing option does not duplicate it', async () => {
|
||||
}));
|
||||
render(<AsyncSelect {...defaultProps} options={options} />);
|
||||
await open();
|
||||
const input = getElementByClassName('.ant-select-selection-search-input');
|
||||
const input = getElementByClassName('.ant-select-input');
|
||||
const paste = createEvent.paste(input, {
|
||||
clipboardData: {
|
||||
getData: () => OPTIONS[0].label,
|
||||
@@ -1439,7 +1451,7 @@ test('pasting an existing option does not duplicate it in multiple mode', async
|
||||
/>,
|
||||
);
|
||||
await open();
|
||||
const input = getElementByClassName('.ant-select-selection-search-input');
|
||||
const input = getElementByClassName('.ant-select-input');
|
||||
const paste = createEvent.paste(input, {
|
||||
clipboardData: {
|
||||
getData: () => 'John,Liam,Peter',
|
||||
@@ -1461,7 +1473,7 @@ test('pasting an non-existent option should not add it if allowNewOptions is fal
|
||||
/>,
|
||||
);
|
||||
await open();
|
||||
const input = getElementByClassName('.ant-select-selection-search-input');
|
||||
const input = getElementByClassName('.ant-select-input');
|
||||
const paste = createEvent.paste(input, {
|
||||
clipboardData: {
|
||||
getData: () => 'John',
|
||||
@@ -1475,7 +1487,7 @@ test('onChange is called with the value property when pasting an option that was
|
||||
const onChange = jest.fn();
|
||||
render(<AsyncSelect {...defaultProps} onChange={onChange} />);
|
||||
await open();
|
||||
const input = getElementByClassName('.ant-select-selection-search-input');
|
||||
const input = getElementByClassName('.ant-select-input');
|
||||
const lastOption = OPTIONS[OPTIONS.length - 1];
|
||||
const paste = createEvent.paste(input, {
|
||||
clipboardData: {
|
||||
|
||||
@@ -86,6 +86,7 @@ import {
|
||||
MAX_TAG_COUNT,
|
||||
TOKEN_SEPARATORS,
|
||||
DEFAULT_SORT_COMPARATOR,
|
||||
DROPDOWN_BUILTIN_PLACEMENTS,
|
||||
} from './constants';
|
||||
|
||||
const Error = ({ error }: { error: string }) => (
|
||||
@@ -152,6 +153,7 @@ const AsyncSelect = forwardRef(
|
||||
ref: ForwardedRef<AsyncSelectRef>,
|
||||
) => {
|
||||
const isSingleMode = mode === 'single';
|
||||
const shouldShowSearch = allowNewOptions ? true : Boolean(showSearch);
|
||||
const [selectValue, setSelectValue] = useState(value);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [isLoading, setIsLoading] = useState(loading);
|
||||
@@ -693,7 +695,14 @@ const AsyncSelect = forwardRef(
|
||||
setSelectValue(value);
|
||||
}
|
||||
} else {
|
||||
const token = tokenSeparators.find(token => pastedText.includes(token));
|
||||
// antd v6 widened `tokenSeparators` to `string[] | (input => string[])`;
|
||||
// Superset always uses the array form.
|
||||
const separators = Array.isArray(tokenSeparators)
|
||||
? tokenSeparators
|
||||
: [];
|
||||
const token = separators.find((token: string) =>
|
||||
pastedText.includes(token),
|
||||
);
|
||||
const array = token
|
||||
? uniq(
|
||||
pastedText
|
||||
@@ -770,7 +779,7 @@ const AsyncSelect = forwardRef(
|
||||
// surface, but the underlying input accepts it and we rely on that.
|
||||
onPaste={onPaste}
|
||||
onPopupScroll={handlePagination}
|
||||
onSearch={showSearch ? handleOnSearch : undefined}
|
||||
onSearch={shouldShowSearch ? handleOnSearch : undefined}
|
||||
onSelect={
|
||||
handleOnSelect as unknown as (
|
||||
value: unknown,
|
||||
@@ -781,10 +790,15 @@ const AsyncSelect = forwardRef(
|
||||
options={fullSelectOptions}
|
||||
optionRender={option => <Space>{option.label || option.value}</Space>}
|
||||
placeholder={placeholder}
|
||||
showSearch={allowNewOptions ? true : showSearch}
|
||||
showSearch={shouldShowSearch}
|
||||
tokenSeparators={tokenSeparators}
|
||||
builtinPlacements={DROPDOWN_BUILTIN_PLACEMENTS}
|
||||
value={selectValue}
|
||||
suffixIcon={getSuffixIcon(isLoading, showSearch, isDropdownVisible)}
|
||||
suffixIcon={getSuffixIcon(
|
||||
isLoading,
|
||||
shouldShowSearch,
|
||||
isDropdownVisible,
|
||||
)}
|
||||
menuItemSelectedIcon={
|
||||
invertSelection ? (
|
||||
<StyledStopOutlined iconSize="m" aria-label="stop" />
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user