Compare commits

..

7 Commits

Author SHA1 Message Date
Evan
9826ce9100 fix(plugin-chart-echarts): import t from @apache-superset/core/translation
@superset-ui/core no longer exports t, so Radar/utils.ts failed to
type-check and its test threw "t is not a function". Import t the
same way the sibling Radar files (index.ts, controlPanel.tsx) already
do.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 08:55:18 -07:00
Evan
c346fba656 fix(plugin-chart-echarts): wrap radar tooltip N/A string for i18n
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 04:38:35 -07:00
Evan
8794b8a729 style(plugin-chart-echarts): fix prettier formatting in radar test
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 04:12:21 -07:00
Evan
53076ec5e7 fix(plugin-chart-echarts): denormalize missing radar metrics as null, not NaN
The null-preservation fix for #30270 kept a missing metric out of the
denormalization map, so label/tooltip lookups fell through to
Number("null") and rendered "NaN" instead of a gap. Record an explicit
null entry and skip formatting it in the label and tooltip renderers.
Also convert the single-case describe() block to standalone tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 20:27:07 -07:00
Evan Rusackas
8c959a3008 style(plugin-chart-echarts): prettier-format radar null test (#30270)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 18:17:55 -07:00
Evan
5881cc8de0 fix(plugin-chart-echarts): preserve null metrics in radar normalization
Radar transformProps normalized each series value with value / max in
normalizeArray. A null (missing) metric coerced to 0 in that division, so
a missing data point was plotted at the center of the radar as a real
zero instead of being left out as a gap (#30270).

Preserve null/non-finite values through normalization so ECharts renders
them as gaps, and exclude nulls when computing the per-series max.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 17:20:02 -07:00
Claude Code
b94aa33de9 test(plugin-chart-echarts): reproduce radar null values plotted as 0 (#30270)
Radar transformProps normalizes each value with null / max, coercing a
missing (null) metric into 0 so it is plotted at the center instead of
left as a gap. Adds a regression test asserting nulls are preserved.

Closes #30270

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 11:13:39 -07:00
438 changed files with 3400 additions and 12688 deletions

View File

@@ -50,8 +50,9 @@ body:
attributes:
label: Python version
options:
- "3.9"
- "3.10"
- "3.11"
- "3.12"
- Not applicable
- I don't know
validations:

View File

@@ -32,6 +32,8 @@ 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

View File

@@ -42,7 +42,7 @@ jobs:
- name: Set up Python ${{ inputs.python-version }}
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.11"
python-version: "3.10"
- name: Install uv
run: pip install uv

View File

@@ -47,7 +47,7 @@ jobs:
steps:
- id: set_matrix
run: |
MATRIX_CONFIG=$(if [ "${{ github.event_name }}" == "pull_request" ]; then echo '["dev", "lean"]'; else echo '["dev", "lean", "websocket", "dockerize", "py311", "py312"]'; fi)
MATRIX_CONFIG=$(if [ "${{ github.event_name }}" == "pull_request" ]; then echo '["dev", "lean"]'; else echo '["dev", "lean", "py310", "websocket", "dockerize", "py311", "py312"]'; fi)
echo "matrix_config=${MATRIX_CONFIG}" >> $GITHUB_OUTPUT
echo $GITHUB_OUTPUT
@@ -123,20 +123,13 @@ jobs:
# pull timeouts, 504/401 on push, ECONNRESET) that otherwise fail
# the whole job. buildx reuses the buildkit layer cache from the
# failed attempt, so a retry mostly re-does just the failed push.
#
# supersetbot's "dev"/"lean" presets pin their own --build-arg
# PY_VER, which lands ahead of --extra-flags on the assembled
# buildx command line; docker/buildx keeps the last value for a
# repeated --build-arg key, so appending PY_VER here overrides
# supersetbot's pin and keeps the build on the Dockerfile's own
# supported Python version.
for attempt in 1 2 3; do
if supersetbot docker \
$PUSH_OR_LOAD \
--preset "$BUILD_PRESET" \
--context "$EVENT" \
--context-ref "$RELEASE" $FORCE_LATEST \
--extra-flags "--build-arg PY_VER=3.11.14-slim-trixie --build-arg INCLUDE_CHROMIUM=false --tag $IMAGE_TAG" \
--extra-flags "--build-arg INCLUDE_CHROMIUM=false --tag $IMAGE_TAG" \
$PLATFORM_ARG; then
break
fi

View File

@@ -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.4.0
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: "./superset-embedded-sdk/.nvmrc"
- run: npm ci

View File

@@ -24,7 +24,7 @@ jobs:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: "./superset-embedded-sdk/.nvmrc"
registry-url: "https://registry.npmjs.org"

View File

@@ -37,7 +37,7 @@ jobs:
persist-credentials: false
submodules: recursive
- name: Setup Java
uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
with:
distribution: "temurin"
java-version: "11"

View File

@@ -14,7 +14,7 @@ jobs:
pull-requests: write
runs-on: ubuntu-24.04
steps:
- uses: actions/labeler@b8dd2d9be0f68b860e7dae5dae7d772984eacd6d # v6.2.0
- uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0
with:
sync-labels: true

View File

@@ -23,7 +23,7 @@ jobs:
persist-credentials: false
submodules: recursive
- name: Setup Java
uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
with:
distribution: "temurin"
java-version: "11"

View File

@@ -25,7 +25,7 @@ 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", "next"]') }}
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["current", "previous", "next"]') }}
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
@@ -45,7 +45,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.4.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: "superset-frontend/.nvmrc"
cache: "npm"

View File

@@ -50,7 +50,7 @@ jobs:
- name: Install Node.js
if: env.HAS_TAGS
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: "./superset-frontend/.nvmrc"

View File

@@ -98,7 +98,7 @@ jobs:
# Mirror the same matrix the release publisher uses so every variant
# operators consume from Docker Hub gets the refreshed base.
matrix:
build_preset: ["dev", "lean", "websocket", "dockerize", "py311", "py312"]
build_preset: ["dev", "lean", "py310", "websocket", "dockerize", "py311", "py312"]
fail-fast: false
steps:
- name: "Checkout release tag: ${{ needs.config.outputs.latest-release }}"
@@ -117,7 +117,7 @@ jobs:
build: "true"
- name: Use Node.js 20
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: 20

View File

@@ -66,12 +66,12 @@ jobs:
persist-credentials: false
submodules: recursive
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: "./docs/.nvmrc"
- name: Setup Python
uses: ./.github/actions/setup-backend/
- uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
- uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
with:
distribution: "zulu"
java-version: "21"

View File

@@ -78,7 +78,7 @@ jobs:
persist-credentials: false
submodules: recursive
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
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.4.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: "./docs/.nvmrc"
- name: yarn install

View File

@@ -128,7 +128,7 @@ jobs:
with:
run: testdata
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
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.4.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: "./superset-frontend/.nvmrc"
cache: "npm"

View File

@@ -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('["current", "next"]') }}
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["previous", "current", "next"]') }}
defaults:
run:
working-directory: superset-extensions-cli

View File

@@ -51,7 +51,7 @@ jobs:
git show -s --format=raw HEAD
docker buildx build \
-t $TAG \
--cache-from=type=registry,ref=apache/superset-cache:3.11-slim-trixie \
--cache-from=type=registry,ref=apache/superset-cache:3.10-slim-trixie \
--target superset-node-ci \
.

View File

@@ -114,7 +114,7 @@ jobs:
with:
run: playwright_testdata
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: "./superset-frontend/.nvmrc"
cache: "npm"

View File

@@ -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", "next"]') }}
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["current", "previous", "next"]') }}
env:
PYTHONPATH: ${{ github.workspace }}
SUPERSET_CONFIG: tests.integration_tests.superset_test_config

View File

@@ -50,15 +50,9 @@ 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", "next"]') }}
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["previous", "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

View File

@@ -38,7 +38,7 @@ jobs:
- name: Setup Node.js
if: steps.check.outputs.frontend
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: "./superset-frontend/.nvmrc"
cache: "npm"

View File

@@ -56,7 +56,7 @@ jobs:
strategy:
matrix:
build_preset:
["dev", "lean", "websocket", "dockerize", "py311", "py312"]
["dev", "lean", "py310", "websocket", "dockerize", "py311", "py312"]
fail-fast: false
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
@@ -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.4.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
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.4.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: 20
package-manager-cache: false

View File

@@ -37,7 +37,7 @@ jobs:
persist-credentials: false
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: "./superset-frontend/.nvmrc"

View File

@@ -63,7 +63,7 @@ repos:
hooks:
- id: prettier-frontend
name: prettier (frontend)
entry: bash -c 'cd superset-frontend && files=(); for f in "$@"; do files+=("${f#superset-frontend/}"); done; npx prettier --write -- "${files[@]}"' --
entry: bash -c 'cd superset-frontend && for file in "$@"; do npx prettier --write "${file#superset-frontend/}"; done'
language: system
pass_filenames: true
files: ^superset-frontend/.*\.(js|jsx|ts|tsx|css|scss|sass|json)$
@@ -81,12 +81,6 @@ 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'

View File

@@ -7,7 +7,6 @@
.codecov.yml
.eslintrc
.eslintignore
.stylelintignore
.flake8
.nvmrc
.prettierrc

View File

@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
FROM python:3.11-slim-trixie
FROM python:3.10-slim-trixie
RUN useradd --user-group --create-home --no-log-init --shell /bin/bash superset

View File

@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
FROM python:3.11-slim-trixie
FROM python:3.10-slim-trixie
RUN useradd --user-group --create-home --no-log-init --shell /bin/bash superset

View File

@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
FROM python:3.11-slim-trixie
FROM python:3.10-slim-trixie
ARG VERSION
RUN git clone --depth 1 --branch ${VERSION} https://github.com/apache/superset.git /superset

View File

@@ -14,7 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
FROM python:3.11-slim-trixie
FROM python:3.10-slim-trixie
RUN apt-get update -y
RUN apt-get install -y \

View File

@@ -24,13 +24,6 @@ assists people when migrating to a new version.
## Next
### Python 3.10 support removed
Python 3.10 is no longer supported. Superset now requires **Python 3.11 or higher**.
Update your environment (virtualenv, Docker base image, CI configuration, etc.) to
Python 3.11+ before upgrading. The `apache/superset-cache:3.10-slim-trixie` and
`py310` Docker image variants are no longer published.
### Owners, dashboard roles, and RLS roles replaced by Subjects
Superset now uses subject-based access assignments for dashboards, charts, datasets,
@@ -160,16 +153,6 @@ 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.

View File

@@ -71,7 +71,7 @@ x-common-build: &common-build
context: .
target: ${SUPERSET_BUILD_TARGET:-dev} # can use `dev` (default) or `lean`
cache_from:
- apache/superset-cache:3.11-slim-trixie
- apache/superset-cache:3.10-slim-trixie
args:
DEV_MODE: "true"
INCLUDE_CHROMIUM: ${INCLUDE_CHROMIUM:-false}

View File

@@ -33,7 +33,7 @@ x-common-build: &common-build
context: .
target: dev
cache_from:
- apache/superset-cache:3.11-slim-trixie
- apache/superset-cache:3.10-slim-trixie
services:
redis:

View File

@@ -38,7 +38,7 @@ x-common-build: &common-build
context: .
target: ${SUPERSET_BUILD_TARGET:-dev} # can use `dev` (default) or `lean`
cache_from:
- apache/superset-cache:3.11-slim-trixie
- apache/superset-cache:3.10-slim-trixie
args:
DEV_MODE: "true"
INCLUDE_CHROMIUM: ${INCLUDE_CHROMIUM:-false}

View File

@@ -99,7 +99,7 @@ See [Connecting AI Clients](#connecting-ai-clients) for Claude Code, Claude Web,
## Prerequisites
- Apache Superset 5.0+ running and accessible
- Python 3.11+
- Python 3.10+
- The `fastmcp` package (`pip install fastmcp`)
---
@@ -529,7 +529,6 @@ MCP_RESPONSE_SIZE_CONFIG = {
"enabled": True,
"token_limit": 25000,
"warn_threshold_pct": 80,
"max_list_items": 100,
"excluded_tools": [
"health_check",
"get_chart_preview",
@@ -544,7 +543,6 @@ 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

View File

@@ -379,7 +379,7 @@ functioning across environments.
Make sure your machine meets the [OS dependencies](https://superset.apache.org/docs/installation/pypi#os-dependencies) before following these steps.
You also need to install MySQL.
Ensure that you are using Python version 3.11 or 3.12, then proceed with:
Ensure that you are using Python version 3.9, 3.10 or 3.11, then proceed with:
```bash
# Create a virtual environment and activate it (recommended)

View File

@@ -82,7 +82,7 @@ If applicable, add screenshots or recordings.
### Environment
- Superset version: [e.g., 3.0.0]
- Python version: [e.g., 3.11.7]
- Python version: [e.g., 3.9.7]
- Node version: [e.g., 18.17.0]
- Database: [e.g., PostgreSQL 14]
- Browser: [e.g., Chrome 120]
@@ -125,7 +125,7 @@ No error messages in browser console or server logs.
### Environment
- Superset version: 3.0.0
- Python version: 3.11.16
- Python version: 3.9.16
- Database: PostgreSQL 14.9
- Browser: Chrome 120.0.6099.71
- OS: macOS 14.2

View File

@@ -52,7 +52,7 @@ Everything you need to contribute to the Apache Superset project. This section i
## Development Resources
### Prerequisites
- **Python**: 3.11 or 3.12
- **Python**: 3.9, 3.10, or 3.11
- **Node.js**: 18.x or 20.x
- **npm**: 9.x or 10.x
- **Git**: Basic understanding

View File

@@ -63,7 +63,7 @@
"@swc/core": "^1.15.43",
"antd": "^6.5.0",
"baseline-browser-mapping": "^2.10.40",
"caniuse-lite": "^1.0.30001803",
"caniuse-lite": "^1.0.30001799",
"docusaurus-plugin-openapi-docs": "^5.1.0",
"docusaurus-theme-openapi-docs": "^5.1.0",
"js-yaml": "^5.2.0",
@@ -90,7 +90,7 @@
"@types/js-yaml": "^4.0.9",
"@types/react": "^19.1.8",
"@typescript-eslint/eslint-plugin": "^8.59.3",
"@typescript-eslint/parser": "^8.63.0",
"@typescript-eslint/parser": "^8.61.0",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.6",
@@ -98,7 +98,7 @@
"globals": "^17.7.0",
"prettier": "^3.9.1",
"typescript": "~6.0.3",
"typescript-eslint": "^8.63.0",
"typescript-eslint": "^8.62.0",
"webpack": "^5.108.2"
},
"browserslist": {

View File

@@ -5504,110 +5504,100 @@
dependencies:
"@types/yargs-parser" "*"
"@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==
"@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==
dependencies:
"@eslint-community/regexpp" "^4.12.2"
"@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"
"@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"
ignore "^7.0.5"
natural-compare "^1.4.0"
ts-api-utils "^2.5.0"
"@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==
"@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==
dependencies:
"@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"
"@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"
debug "^4.4.3"
"@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==
"@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==
dependencies:
"@typescript-eslint/tsconfig-utils" "^8.63.0"
"@typescript-eslint/types" "^8.63.0"
"@typescript-eslint/tsconfig-utils" "^8.62.1"
"@typescript-eslint/types" "^8.62.1"
debug "^4.4.3"
"@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==
"@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==
dependencies:
"@typescript-eslint/types" "8.63.0"
"@typescript-eslint/visitor-keys" "8.63.0"
"@typescript-eslint/types" "8.62.1"
"@typescript-eslint/visitor-keys" "8.62.1"
"@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/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.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==
"@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==
dependencies:
"@typescript-eslint/types" "8.63.0"
"@typescript-eslint/typescript-estree" "8.63.0"
"@typescript-eslint/utils" "8.63.0"
"@typescript-eslint/types" "8.62.1"
"@typescript-eslint/typescript-estree" "8.62.1"
"@typescript-eslint/utils" "8.62.1"
debug "^4.4.3"
ts-api-utils "^2.5.0"
"@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/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.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==
"@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==
dependencies:
"@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"
"@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"
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.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==
"@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==
dependencies:
"@eslint-community/eslint-utils" "^4.9.1"
"@typescript-eslint/scope-manager" "8.63.0"
"@typescript-eslint/types" "8.63.0"
"@typescript-eslint/typescript-estree" "8.63.0"
"@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.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==
"@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==
dependencies:
"@typescript-eslint/types" "8.63.0"
"@typescript-eslint/types" "8.62.1"
eslint-visitor-keys "^5.0.0"
"@ungap/structured-clone@^1.0.0":
@@ -6577,10 +6567,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, 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==
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==
ccount@^2.0.0:
version "2.0.1"
@@ -15272,15 +15262,15 @@ types-ramda@^0.30.1:
dependencies:
ts-toolbelt "^9.6.0"
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==
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==
dependencies:
"@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-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@~6.0.3:
version "6.0.3"

View File

@@ -24,12 +24,13 @@ name = "apache_superset"
description = "A modern, enterprise-ready business intelligence web application"
readme = "README.md"
dynamic = ["version", "scripts", "entry-points"]
requires-python = ">=3.11"
requires-python = ">=3.10"
license = { file="LICENSE.txt" }
authors = [
{ name = "Apache Software Foundation", email = "dev@superset.apache.org" },
]
classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
@@ -42,7 +43,7 @@ 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>=7.1.4, <8",
"cachetools>=6.2.1, <7",
"celery>=5.6.3, <6.0.0",
"click>=8.4.0",
"click-option-group",
@@ -66,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.99, <1",
"holidays>=0.45, <1",
"humanize",
"isodate",
"jsonpath-ng>=1.8.0, <2",
@@ -109,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.12.0, <31",
"sqlglot>=30.8.0, <31",
# newer pandas needs 0.9+
"tabulate>=0.10.0, <1.0",
"typing-extensions>=4, <5",
@@ -164,8 +165,9 @@ 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.28.21", "sqlalchemy_hana==0.4.0"]
hive = [
"pyhive[hive]>=0.6.5;python_version<'3.11'",
"pyhive[hive_pure_sasl]>=0.7.0",
"tableschema",
"thrift>=0.23.0, <1.0.0",
@@ -197,8 +199,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.6.0,<0.7.0"]
sqlite = ["syntaqlite>=0.1.0,<0.5.0"]
spark = [
"pyhive[hive]>=0.6.5;python_version<'3.11'",
"pyhive[hive_pure_sasl]>=0.7",
"tableschema",
"thrift>=0.23.0, <1",
@@ -241,7 +244,7 @@ development = [
"ruff",
"sqloxide",
"statsd",
"syntaqlite>=0.6.0,<0.7.0",
"syntaqlite>=0.4.2,<0.5.0",
]
[project.urls]
@@ -341,8 +344,8 @@ exclude = [
line-length = 88
indent-width = 4
# Assume Python 3.11
target-version = "py311"
# Assume Python 3.10
target-version = "py310"
[tool.ruff.lint]
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.

View File

@@ -35,13 +35,13 @@ 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 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 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

View File

@@ -44,7 +44,7 @@ cachelib==0.13.0
# via
# flask-caching
# flask-session
cachetools==7.1.4
cachetools==6.2.1
# via apache-superset (pyproject.toml)
cattrs==25.1.1
# via requests-cache
@@ -175,7 +175,7 @@ h11==0.16.0
# via wsproto
hashids==1.3.1
# via apache-superset (pyproject.toml)
holidays==0.100
holidays==0.82
# via apache-superset (pyproject.toml)
humanize==4.12.3
# via apache-superset (pyproject.toml)
@@ -414,7 +414,7 @@ sqlalchemy-utils==0.42.1
# apache-superset (pyproject.toml)
# apache-superset-core
# flask-appbuilder
sqlglot==30.12.0
sqlglot==30.8.0
# via
# apache-superset (pyproject.toml)
# apache-superset-core

View File

@@ -97,7 +97,7 @@ cachelib==0.13.0
# -c requirements/base-constraint.txt
# flask-caching
# flask-session
cachetools==7.1.4
cachetools==6.2.1
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -407,7 +407,7 @@ hashids==1.3.1
# via
# -c requirements/base-constraint.txt
# apache-superset
holidays==0.100
holidays==0.82
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -998,7 +998,7 @@ sqlalchemy-utils==0.42.1
# apache-superset
# apache-superset-core
# flask-appbuilder
sqlglot==30.12.0
sqlglot==30.8.0
# via
# -c requirements/base-constraint.txt
# apache-superset
@@ -1017,7 +1017,7 @@ starlette==1.3.1
# mcp
statsd==4.0.1
# via apache-superset
syntaqlite==0.6.0
syntaqlite==0.4.2
# via apache-superset
tabulate==0.10.0
# via

View File

@@ -153,11 +153,10 @@ def main( # noqa: C901
)
print(f"Migration goes from {down_revision} to {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}")
current_revision = db.engine.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:

View File

@@ -133,8 +133,8 @@ def main(docker: bool, frontend: bool, backend: bool) -> None: # noqa: C901
requirements = [
Requirement(
"python",
(Version("3.11.0"), Version("3.11.999")),
(Version("3.11.0"), Version("3.12.999")),
(Version("3.10.0"), Version("3.10.999")),
(Version("3.9.0"), Version("3.11.999")),
"backend",
"python --version",
),

View File

@@ -1,49 +0,0 @@
#!/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

View File

@@ -92,33 +92,4 @@ 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

View File

@@ -21,7 +21,7 @@ under the License.
[![PyPI version](https://badge.fury.io/py/apache-superset-core.svg)](https://badge.fury.io/py/apache-superset-core)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
The official core package for building Apache Superset backend extensions and integrations. This package provides essential building blocks including base classes, API utilities, type definitions, and decorators for both the host application and extensions.

View File

@@ -26,7 +26,7 @@ authors = [
]
license = "Apache-2.0"
license-files = ["LICENSE.txt"]
requires-python = ">=3.11"
requires-python = ">=3.10"
keywords = ["superset", "apache", "analytics", "business-intelligence", "extensions", "visualization"]
classifiers = [
"Development Status :: 3 - Alpha",
@@ -34,6 +34,7 @@ classifiers = [
"Intended Audience :: Developers",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Database",

View File

@@ -21,7 +21,7 @@ under the License.
[![PyPI version](https://badge.fury.io/py/apache-superset-extensions-cli.svg)](https://badge.fury.io/py/apache-superset-extensions-cli)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
Official command-line interface for building, bundling, and managing Apache Superset extensions. This CLI tool provides developers with everything needed to create, develop, and package extensions for the Superset ecosystem.

View File

@@ -25,7 +25,7 @@ authors = [
]
license = "Apache-2.0"
license-files = ["LICENSE.txt"]
requires-python = ">=3.11"
requires-python = ">=3.10"
keywords = ["superset", "apache", "cli", "extensions", "analytics", "business-intelligence", "development-tools"]
classifiers = [
"Development Status :: 3 - Alpha",
@@ -33,6 +33,7 @@ classifiers = [
"Intended Audience :: Developers",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Database",

View File

@@ -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 } from 'antd';
import { App, Layout, Space, Content } from 'antd';
import 'src/theme.ts';
import './storybook.css';

View File

@@ -1,9 +0,0 @@
# 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

View File

@@ -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-content')
cy.get('.ant-popover-content .ant-select-selector')
.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-content')
cy.get('.ant-popover-inner-content')
.find('[class^=ant-input]')
.first()
.type(`${startRange}`);
}
if (endRange) {
cy.get('.ant-popover-content')
cy.get('.ant-popover-inner-content')
.find('[class^=ant-input]')
.last()
.type(`${endRange}`);
@@ -514,14 +514,15 @@ export function inputNativeFilterDefaultValue(
)
.eq(1)
.within(() => {
cy.get('.ant-select-input').type(`${defaultValue}{enter}`, {
force: true,
});
cy.get('.ant-select-selection-search-input').type(
`${defaultValue}{enter}`,
{ force: true },
);
});
});
} else {
cy.getBySel('default-input').within(() => {
cy.get('.ant-select-input').click();
cy.get('.ant-select-selection-search-input').click();
cy.get('.ant-select-item-option-content').contains(defaultValue).click();
});
}

View File

@@ -53,7 +53,9 @@ export function saveChartToDashboard(chartName: string, dashboardName: string) {
.then($modal => {
cy.wait(500);
cy.wrap($modal)
.find('.ant-select-input[aria-label*="Select a dashboard"]')
.find(
'.ant-select-selection-search-input[aria-label*="Select a dashboard"]',
)
.type(dashboardName, { force: true });
cy.get(`.ant-select-item-option[title="${dashboardName}"]`).click();

View File

@@ -52,7 +52,7 @@ export const securityAccess = {
};
export const homePage = {
homeSection: {
sectionArea: '.ant-collapse-body',
sectionArea: '.ant-collapse-content-box',
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: '.ant-select-input',
dbDropdown: '[class="ant-select-selection-search-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: '.ant-select-input[aria-label="Sort"]',
sort: '[class="ant-select-selection-search-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-input',
filterInfoInput: '.ant-select-selection-search-input',
inputDropdown: '.ant-select-item-option-content',
columnEmptyInput: '.ant-select-placeholder',
columnEmptyInput: '.ant-select-selection-placeholder',
filterTypeInput: dataTestLocator('filters-config-modal__filter-type'),
fieldInput: dataTestLocator('field-input'),
filterTypeItem: '.ant-select-content-has-value, .ant-select-selection-item',
filterTypeItem: '.ant-select-selection-item',
filterGear: dataTestLocator('filterbar-orientation-icon'),
},
filterFromDashboardView: {
filterValueInput: '.ant-select-input',
filterValueInput: '[class="ant-select-selection-search-input"]',
expand: dataTestLocator('filter-bar__expand-button'),
collapse: dataTestLocator('filter-bar__collapse-button'),
filterName: dataTestLocator('filter-control-name'),
filterContent: '.ant-select-content-has-value, .ant-select-selection-item',
filterContent: '.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-placeholder',
collapsedSectionContainer: '[class="ant-collapse-body"]',
filterPlaceholder: '.ant-select-selection-placeholder',
collapsedSectionContainer: '[class="ant-collapse-content-box"]',
},
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-input',
filterItemDropdown: '.ant-select-selection-search',
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-input',
dashboardNameInput: '.ant-select-selection-search-input',
addToDashboardInput: dataTestLocator(
'save-chart-modal-select-dashboard-form',
),
@@ -495,7 +495,7 @@ export const exploreView = {
},
controlPanel: {
panel: dataTestLocator('control-tabs'),
categoryArea: '.ant-collapse-body',
categoryArea: '.ant-collapse-content-box',
dragField: dataTestLocator('datasource'),
metricsField: dataTestLocator('metrics'),
optionField: dataTestLocator('option-label'),
@@ -594,7 +594,7 @@ export const exploreView = {
},
};
export const createChartView = {
chooseDatasetInput: '.ant-select-input',
chooseDatasetInput: '.ant-select-selection-search-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-content-has-value, .ant-select-selection-item',
rangeTypeField: '.ant-select-selection-item',
startTimeInputNumber: '.ant-input-number-input',
datePicker: '.ant-picker-input',
applyButton: dataTestLocator('date-filter-control__apply-button'),

View File

@@ -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/*.|@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)',
'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)',
],
preset: 'ts-jest',
transform: {

View File

@@ -52,6 +52,10 @@
// 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
@@ -192,7 +196,6 @@
// === 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",
@@ -200,31 +203,20 @@
"jsx-a11y/aria-proptypes": "error",
"jsx-a11y/aria-role": ["error", { "ignoreNonDOM": false }],
"jsx-a11y/aria-unsupported-elements": "error",
"jsx-a11y/autocomplete-valid": "error",
"jsx-a11y/click-events-have-key-events": "off",
"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": "error",
"jsx-a11y/mouse-events-have-key-events": "off",
"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",
// TODO: Graduate to "error" — remaining violations are event-boundary
// wrappers (divs/spans whose handlers only stopPropagation) and test
// scaffolding where adding a role is semantically wrong. Needs a
// dedicated pass; see PR enabling the other jsx-a11y interactive rules.
"jsx-a11y/no-static-element-interactions": "off",
"jsx-a11y/role-has-required-aria-props": "error",
"jsx-a11y/role-supports-aria-props": "error",
@@ -240,14 +232,7 @@
"@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": [
"error",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"caughtErrors": "none"
}
],
"@typescript-eslint/no-unused-vars": "warn",
"@typescript-eslint/prefer-optional-chain": "error",
// === Unicorn rules (bonus coverage) ===
@@ -297,16 +282,6 @@
"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": [

File diff suppressed because it is too large Load Diff

View File

@@ -57,9 +57,7 @@
"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 && npm run stylelint",
"stylelint": "stylelint \"{src,packages/*/src,plugins/*/src}/**/*.{ts,tsx}\"",
"stylelint-fix": "stylelint --fix \"{src,packages/*/src,plugins/*/src}/**/*.{ts,tsx}\"",
"lint:full": "npm run lint && npm run check:custom-rules",
"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",
@@ -130,9 +128,10 @@
"@luma.gl/shadertools": "~9.2.5",
"@luma.gl/webgl": "~9.2.5",
"@reduxjs/toolkit": "^1.9.3",
"@rjsf/core": "^6.6.2",
"@rjsf/utils": "^6.6.2",
"@rjsf/validator-ajv8": "^6.6.2",
"@rjsf/antd": "^5.24.13",
"@rjsf/core": "^5.24.13",
"@rjsf/utils": "^5.24.3",
"@rjsf/validator-ajv8": "^5.24.13",
"@scarf/scarf": "^1.4.0",
"@superset-ui/chart-controls": "file:./packages/superset-ui-chart-controls",
"@superset-ui/core": "file:./packages/superset-ui-core",
@@ -168,7 +167,7 @@
"@visx/xychart": "^4.0.0",
"ag-grid-community": "36.0.0",
"ag-grid-react": "36.0.0",
"antd": "^6.0.0",
"antd": "^5.26.0",
"chrono-node": "^2.9.1",
"classnames": "^2.2.5",
"content-disposition": "^2.0.1",
@@ -187,7 +186,7 @@
"geostyler-style": "11.0.2",
"geostyler-wfs-parser": "^3.0.1",
"google-auth-library": "^10.9.0",
"immer": "^11.1.11",
"immer": "^11.1.9",
"interweave": "^13.1.1",
"jquery": "^4.0.0",
"js-levenshtein": "^1.1.6",
@@ -206,7 +205,7 @@
"query-string": "9.4.1",
"re-resizable": "^6.11.2",
"react": "^18.3.0",
"react-arborist": "^3.13.2",
"react-arborist": "^3.12.0",
"react-checkbox-tree": "^1.8.0",
"react-diff-viewer-continued": "^4.2.2",
"react-dnd": "^11.1.3",
@@ -273,7 +272,7 @@
"@storybook/test-runner": "0.24.4",
"@svgr/webpack": "^8.1.0",
"@swc/core": "^1.15.43",
"@swc/plugin-emotion": "^14.15.0",
"@swc/plugin-emotion": "^14.14.1",
"@swc/plugin-transform-imports": "^12.5.0",
"@testing-library/dom": "^9.3.4",
"@testing-library/jest-dom": "^6.9.1",
@@ -300,13 +299,13 @@
"@types/rison": "0.1.0",
"@types/tinycolor2": "^1.4.3",
"@types/unzipper": "^0.10.11",
"@typescript-eslint/eslint-plugin": "^8.63.0",
"@typescript-eslint/parser": "^8.63.0",
"@typescript-eslint/eslint-plugin": "^8.62.1",
"@typescript-eslint/parser": "^8.62.1",
"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.42",
"baseline-browser-mapping": "^2.10.41",
"cheerio": "1.2.0",
"concurrently": "^10.0.3",
"copy-webpack-plugin": "^14.0.0",
@@ -345,9 +344,8 @@
"mini-css-extract-plugin": "^2.10.2",
"minimizer-webpack-plugin": "^5.6.1",
"open-cli": "^9.0.0",
"oxlint": "^1.73.0",
"oxlint": "^1.72.0",
"po2json": "^0.4.5",
"postcss-styled-syntax": "^0.7.2",
"prettier": "3.9.4",
"prettier-plugin-packagejson": "^3.0.2",
"process": "^0.11.10",
@@ -360,21 +358,20 @@
"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.23.0",
"tsx": "^4.22.5",
"typescript": "5.4.5",
"unzipper": "^0.12.5",
"vm-browserify": "^1.1.2",
"wait-on": "^9.0.10",
"webpack": "^5.108.4",
"webpack": "^5.108.3",
"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.1",
"webpack-sources": "^3.5.0",
"webpack-visualizer-plugin2": "^2.0.0"
},
"peerDependencies": {
@@ -390,9 +387,6 @@
},
"overrides": {
"uuid": "$uuid",
"@great-expectations/jsonforms-antd-renderers": {
"antd": "$antd"
},
"core-js": "^3.38.1",
"dompurify": "^3.4.11",
"esbuild": "^0.28.1",

View File

@@ -110,7 +110,7 @@
"react-loadable": "^5.5.0",
"tinycolor2": "*",
"lodash": "^4.18.1",
"antd": "^6.0.0",
"antd": "^5.26.0",
"jed": "^1.1.1"
},
"scripts": {

View File

@@ -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,18 +105,8 @@ export const GlobalStyles = () => {
cursor: pointer;
}
/* 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 */
// 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;
}
@@ -127,7 +117,7 @@ export const GlobalStyles = () => {
}
.superset-explore-popover.ant-popover
.ant-popover-container:has(.ant-popover-title) {
.ant-popover-inner:has(.ant-popover-title) {
padding-top: 0;
}
.superset-explore-popover.ant-popover .ant-popover-title {
@@ -136,7 +126,7 @@ export const GlobalStyles = () => {
line-height: 1;
}
.superset-explore-popover.ant-popover
.ant-popover-container:has(.ant-popover-title)
.ant-popover-inner:has(.ant-popover-title)
.ant-tabs-tab {
padding-top: 0;
}

View File

@@ -18,34 +18,7 @@
*/
import { theme as antdThemeImport } from 'antd';
import { Theme } from './Theme';
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;
}
import { AnyThemeConfig, ThemeAlgorithm } from './types';
// Mock emotion's cache to avoid actual DOM operations
jest.mock('@emotion/cache', () => ({
@@ -365,10 +338,10 @@ test('Theme edge cases correctly applies base theme tokens in dark mode', () =>
algorithm: antdThemeImport.defaultAlgorithm,
};
const baseThemeDark = {
const baseThemeDark: AnyThemeConfig = {
...baseTheme,
algorithm: antdThemeImport.darkAlgorithm,
} as AnyThemeConfig;
};
// Simulate light mode with base theme
const lightTheme = Theme.fromConfig({}, baseTheme);
@@ -500,10 +473,10 @@ test('Theme base theme integration handles base theme with dark algorithm correc
},
};
const baseThemeDark = {
const baseThemeDark: AnyThemeConfig = {
...baseTheme,
algorithm: antdThemeImport.darkAlgorithm,
} as AnyThemeConfig;
};
const userDarkTheme: AnyThemeConfig = {
algorithm: antdThemeImport.darkAlgorithm,
@@ -552,7 +525,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();
@@ -631,9 +604,7 @@ test('Theme base theme integration handles cssVar, hashed and inherit properties
// User properties override/add to base
expect(serialized.inherit).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.cssVar).toBe(true);
expect(serialized.hashed).toBe(false);
// Tokens are still merged
@@ -938,81 +909,3 @@ 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);
});

View File

@@ -1,730 +0,0 @@
// 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,
}
`;

View File

@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import { theme as antdThemeImport, type MappingAlgorithm } from 'antd';
import { theme as antdThemeImport } from 'antd';
import {
type AntdThemeConfig,
type AnyThemeConfig,
@@ -48,8 +48,8 @@ export function isSerializableConfig(
export function deserializeThemeConfig(
config: SerializableThemeConfig,
): AntdThemeConfig {
const { algorithm, cssVar, ...rest } = config;
const algorithmMap: Record<string, MappingAlgorithm> = {
const { algorithm, ...rest } = config;
const algorithmMap: Record<string, any> = {
default: antdThemeImport.defaultAlgorithm,
dark: antdThemeImport.darkAlgorithm,
compact: antdThemeImport.compactAlgorithm,
@@ -74,17 +74,9 @@ 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 } : {}),
};
}

View File

@@ -31,7 +31,7 @@
"lodash-es": "^4.18.1"
},
"peerDependencies": {
"@ant-design/icons": "^5.6.1 || ^6.0.0",
"@ant-design/icons": "^5.6.1",
"@emotion/react": "^11.4.1",
"@superset-ui/core": "*",
"@testing-library/dom": "^9.3.4",

View File

@@ -54,7 +54,7 @@
"react-ace": "^14.0.1",
"react-draggable": "^4.7.0",
"react-error-boundary": "^6.1.2",
"react-js-cron": "^6.0.2",
"react-js-cron": "^5.2.0",
"react-markdown": "^10.1.0",
"react-resize-detector": "^7.1.2",
"react-syntax-highlighter": "^16.1.1",
@@ -100,7 +100,7 @@
"@types/react-loadable": "*",
"@types/react-window": "^1.8.8",
"@types/tinycolor2": "*",
"antd": "^6.0.0",
"antd": "^5.26.0",
"nanoid": "^5.0.9",
"react": "^18.3.0",
"react-dom": "^18.3.0",

View File

@@ -92,7 +92,7 @@ const MatrixNoDataComponent = () => {
* Individual grid cell component - memoized to prevent unnecessary re-renders
*/
const MatrixifyGridCell = memo(
({ cell, hooks }: MatrixifyGridCellProps) => {
({ cell, rowHeight, datasource, hooks }: MatrixifyGridCellProps) => {
// Use computed title from template (will be empty string if no template)
const cellLabel = cell.title || '';

View File

@@ -118,6 +118,7 @@ interface MatrixifyGridRendererProps {
function MatrixifyGridRenderer({
formData,
datasource,
width,
height,
hooks,
}: MatrixifyGridRendererProps) {
@@ -248,7 +249,7 @@ function MatrixifyGridRenderer({
{/* Row cells for this column group */}
{row
.slice(colGroup.startIdx, colGroup.endIdx)
.map(cell =>
.map((cell, colIdx) =>
cell ? (
<MatrixifyGridCell
key={cell.id}

View File

@@ -444,6 +444,7 @@ 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};

View File

@@ -82,10 +82,8 @@ test('collapses on click', async () => {
await userEvent.click(screen.getAllByRole('button')[0]);
// 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.getByText('Content 1').parentNode).toHaveClass(
'ant-collapse-content-hidden',
);
expect(screen.queryByText('Content 2')).not.toBeInTheDocument();
});

View File

@@ -52,10 +52,10 @@ const StyledCollapse = styled((props: CollapseProps) => (
`
border-bottom: 1px solid ${theme.colorBorderSecondary};
`}
.ant-collapse-panel {
.ant-collapse-content {
color: ${({ theme }) => theme.colorText};
.ant-collapse-body {
.ant-collapse-content-box {
.loading.inline {
margin: ${({ theme }) => theme.sizeUnit * 12}px auto;
display: block;

View File

@@ -17,7 +17,7 @@
* under the License.
*/
import { render, waitFor } from '@superset-ui/core/spec';
import { render } from '@superset-ui/core/spec';
import * as ReactCronPicker from 'react-js-cron';
import { CronPicker } from '.';
@@ -37,25 +37,3 @@ 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();
});

View File

@@ -175,7 +175,7 @@ export const CronPicker = styled((props: CronProps) => (
.react-js-cron-select.ant-select {
width: 100%;
.ant-select-content {
.ant-select-selector {
flex-wrap: nowrap;
}
}
@@ -188,12 +188,12 @@ export const CronPicker = styled((props: CronProps) => (
}
}
.react-js-cron-custom-select .ant-select-placeholder {
.react-js-cron-custom-select .ant-select-selection-placeholder {
flex: auto;
border-radius: ${theme.borderRadius}px;
}
.react-js-cron-custom-select .ant-select-content-item {
.react-js-cron-custom-select .ant-select-selection-overflow-item {
align-self: center;
}

View File

@@ -338,9 +338,9 @@ export const DropdownContainer = forwardRef(
<>
<Global
styles={css`
.ant-popover-container {
/* Some OS versions only show the scroll when hovering. */
/* These settings will make the scroll always visible. */
.ant-popover-inner {
// 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,8 +361,7 @@ export const DropdownContainer = forwardRef(
<Popover
styles={{
// antd v6 renamed the inner content slot `body` -> `container`
container: {
body: {
maxHeight: `${MAX_HEIGHT}px`,
overflow: showOverflow ? 'auto' : 'visible',
},

View File

@@ -60,7 +60,6 @@ export const FaveStar = ({
className="fave-unfave-icon"
data-test="fave-unfave-icon"
role="button"
tabIndex={0}
>
{isStarred ? (
<Icons.StarFilled

View File

@@ -28,10 +28,6 @@ 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`};
`;
@@ -66,8 +62,6 @@ export const LabeledErrorBoundInput = ({
get_url,
description,
isValidating = false,
renderAsTextArea,
textAreaCss,
...props
}: LabeledErrorBoundInputProps) => {
const hasError = !!errorMessage;
@@ -104,8 +98,6 @@ export const LabeledErrorBoundInput = ({
}
role="textbox"
/>
) : renderAsTextArea ? (
<StyledTextArea css={textAreaCss} {...props} {...validationMethods} />
) : (
<StyledInput {...props} {...validationMethods} />
)}

View File

@@ -16,10 +16,7 @@
* 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;
@@ -34,7 +31,5 @@ export interface LabeledErrorBoundInputProps {
classname?: string;
visibilityToggle?: boolean;
isValidating?: boolean;
renderAsTextArea?: boolean;
textAreaCss?: SerializedStyles;
[x: string]: any;
}

View File

@@ -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
}
`;

View File

@@ -18,7 +18,6 @@
*/
import { fireEvent, render } from '@superset-ui/core/spec';
import { Icons } from '../Icons';
import { Label } from '.';
import { LabelGallery, options } from './Label.stories';
@@ -40,21 +39,6 @@ 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',
});
});
// test stories from the storybook!
test('renders all the storybook gallery variants', () => {
const { container } = render(<LabelGallery />);

View File

@@ -34,7 +34,7 @@ export const Label = forwardRef<HTMLSpanElement, LabelProps>((props, ref) => {
onClick,
children,
icon,
id: _id,
id,
...rest
} = props;
@@ -76,27 +76,7 @@ export const Label = forwardRef<HTMLSpanElement, LabelProps>((props, ref) => {
onClick={onClick}
role={onClick ? 'button' : undefined}
style={style}
/*
* 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
)
}
icon={icon}
css={labelStyles}
{...rest}
>

View File

@@ -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,8 +344,6 @@ const CustomModal = ({
className="draggable-trigger"
onMouseOver={() => dragDisabled && setDragDisabled(false)}
onMouseOut={() => !dragDisabled && setDragDisabled(true)}
onFocus={() => dragDisabled && setDragDisabled(false)}
onBlur={() => !dragDisabled && setDragDisabled(true)}
>
{title}
</div>

View File

@@ -106,11 +106,7 @@ test('stops propagation of navigation keys to parent elements', async () => {
expect(handleParentKeyDown).not.toHaveBeenCalled();
}
// `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'];
const allowedKeys = ['Escape', 'Tab'];
for (const key of allowedKeys) {
handleParentKeyDown.mockClear();

View File

@@ -41,7 +41,7 @@ const defaultProps: PageHeaderWithActionsProps = {
data-test="additional-actions-menu"
/>
),
menuDropdownProps: { onOpenChange: jest.fn(), open: true },
menuDropdownProps: { onVisibleChange: jest.fn(), visible: 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.onOpenChange).toHaveBeenCalled();
expect(defaultProps.menuDropdownProps.onVisibleChange).toHaveBeenCalled();
});

View File

@@ -36,7 +36,6 @@ const RefreshLabel = ({
<Icons.SyncOutlined
iconSize="l"
role="button"
tabIndex={disabled ? -1 : 0}
onClick={disabled ? undefined : onClick}
css={(theme: SupersetTheme) => ({
cursor: 'pointer',

View File

@@ -119,22 +119,10 @@ const findAllSelectOptions = () =>
waitFor(() => getElementsByClassName('.ant-select-item-option-content'));
const findSelectValue = () =>
// 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',
),
);
waitFor(() => getElementByClassName('.ant-select-selection-item'));
const findAllSelectValues = () =>
// 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',
),
);
waitFor(() => getElementsByClassName('.ant-select-selection-item'));
const clearAll = () => userEvent.click(screen.getByLabelText('close-circle'));
@@ -393,7 +381,7 @@ test('searches for custom fields', async () => {
test('removes duplicated values', async () => {
render(<AsyncSelect {...defaultProps} mode="multiple" allowNewOptions />);
const input = getElementByClassName('.ant-select-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
getData: () => 'a,b,b,b,c,d,d',
@@ -412,7 +400,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-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
getData: () => 'a, b, c , d',
@@ -845,7 +833,7 @@ test('Renders only an overflow tag if dropdown is open in oneLine mode', async (
);
await open();
const withinSelector = within(getElementByClassName('.ant-select-content'));
const withinSelector = within(getElementByClassName('.ant-select-selector'));
await waitFor(() => {
expect(
withinSelector.queryByText(OPTIONS[0].label),
@@ -918,7 +906,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-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
getData: () => OPTIONS[0].label,
@@ -1195,7 +1183,7 @@ test('keeps loading indicator while a newer request is in flight after a stale r
});
const isSpinnerVisible = (): boolean =>
Boolean(document.querySelector('.ant-select-suffix .ant-spin'));
Boolean(document.querySelector('.ant-select-arrow .ant-spin'));
try {
render(<AsyncSelect {...defaultProps} options={loadOptions} />);
@@ -1343,7 +1331,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-suffix .ant-spin')).toBeNull(),
expect(document.querySelector('.ant-select-arrow .ant-spin')).toBeNull(),
);
// Trigger pagination by dispatching a scroll event on the virtual-list
@@ -1423,7 +1411,7 @@ test('pasting an existing option does not duplicate it', async () => {
}));
render(<AsyncSelect {...defaultProps} options={options} />);
await open();
const input = getElementByClassName('.ant-select-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
getData: () => OPTIONS[0].label,
@@ -1451,7 +1439,7 @@ test('pasting an existing option does not duplicate it in multiple mode', async
/>,
);
await open();
const input = getElementByClassName('.ant-select-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
getData: () => 'John,Liam,Peter',
@@ -1473,7 +1461,7 @@ test('pasting an non-existent option should not add it if allowNewOptions is fal
/>,
);
await open();
const input = getElementByClassName('.ant-select-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
getData: () => 'John',
@@ -1487,7 +1475,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-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const lastOption = OPTIONS[OPTIONS.length - 1];
const paste = createEvent.paste(input, {
clipboardData: {

View File

@@ -86,7 +86,6 @@ import {
MAX_TAG_COUNT,
TOKEN_SEPARATORS,
DEFAULT_SORT_COMPARATOR,
DROPDOWN_BUILTIN_PLACEMENTS,
} from './constants';
const Error = ({ error }: { error: string }) => (
@@ -153,7 +152,6 @@ 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);
@@ -695,14 +693,7 @@ const AsyncSelect = forwardRef(
setSelectValue(value);
}
} else {
// 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 token = tokenSeparators.find(token => pastedText.includes(token));
const array = token
? uniq(
pastedText
@@ -779,7 +770,7 @@ const AsyncSelect = forwardRef(
// surface, but the underlying input accepts it and we rely on that.
onPaste={onPaste}
onPopupScroll={handlePagination}
onSearch={shouldShowSearch ? handleOnSearch : undefined}
onSearch={showSearch ? handleOnSearch : undefined}
onSelect={
handleOnSelect as unknown as (
value: unknown,
@@ -790,15 +781,10 @@ const AsyncSelect = forwardRef(
options={fullSelectOptions}
optionRender={option => <Space>{option.label || option.value}</Space>}
placeholder={placeholder}
showSearch={shouldShowSearch}
showSearch={allowNewOptions ? true : showSearch}
tokenSeparators={tokenSeparators}
builtinPlacements={DROPDOWN_BUILTIN_PLACEMENTS}
value={selectValue}
suffixIcon={getSuffixIcon(
isLoading,
shouldShowSearch,
isDropdownVisible,
)}
suffixIcon={getSuffixIcon(isLoading, showSearch, isDropdownVisible)}
menuItemSelectedIcon={
invertSelection ? (
<StyledStopOutlined iconSize="m" aria-label="stop" />

View File

@@ -108,22 +108,10 @@ const findAllSelectOptions = () =>
waitFor(() => getElementsByClassName('.ant-select-item-option-content'));
const findSelectValue = () =>
// 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',
),
);
waitFor(() => getElementByClassName('.ant-select-selection-item'));
const findAllSelectValues = () =>
// 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',
),
]);
waitFor(() => [...getElementsByClassName('.ant-select-selection-item')]);
const clearAll = () => userEvent.click(screen.getByLabelText('close-circle'));
@@ -375,7 +363,7 @@ test('searches for custom fields', async () => {
test('removes duplicated values', async () => {
render(<Select {...defaultProps} mode="multiple" allowNewOptions />);
const input = getElementByClassName('.ant-select-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
getData: () => 'a,b,b,b,c,d,d',
@@ -392,7 +380,7 @@ test('removes duplicated values', async () => {
test('trims whitespace from pasted comma-separated values', async () => {
render(<Select {...defaultProps} mode="multiple" allowNewOptions />);
const input = getElementByClassName('.ant-select-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
getData: () => 'a, b, c , d',
@@ -789,7 +777,7 @@ test('Renders only an overflow tag if dropdown is open in oneLine mode', async (
);
await open();
const withinSelector = within(getElementByClassName('.ant-select-content'));
const withinSelector = within(getElementByClassName('.ant-select-selector'));
await waitFor(() => {
expect(
withinSelector.queryByText(OPTIONS[0].label),
@@ -822,7 +810,7 @@ test('Maintains stable maxTagCount to prevent click target disappearing in oneLi
/>,
);
const withinSelector = within(getElementByClassName('.ant-select-content'));
const withinSelector = within(getElementByClassName('.ant-select-selector'));
expect(withinSelector.getByText(OPTIONS[0].label)).toBeVisible();
expect(withinSelector.getByText('+ 2 ...')).toBeVisible();
@@ -859,7 +847,9 @@ test('dropdown width matches input width after tags collapse in oneLine mode', a
// Wait for RAF to complete and tags to collapse
await waitFor(() => {
const withinSelector = within(getElementByClassName('.ant-select-content'));
const withinSelector = within(
getElementByClassName('.ant-select-selector'),
);
expect(
withinSelector.queryByText(OPTIONS[0].label),
).not.toBeInTheDocument();
@@ -1095,7 +1085,7 @@ test('fires onChange when pasting a selection', async () => {
const onChange = jest.fn();
render(<Select {...defaultProps} onChange={onChange} />);
await open();
const input = getElementByClassName('.ant-select-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
getData: () => OPTIONS[0].label,
@@ -1123,7 +1113,7 @@ test('does not duplicate options when using numeric values', async () => {
test('pasting an existing option does not duplicate it', async () => {
render(<Select {...defaultProps} options={[OPTIONS[0]]} />);
await open();
const input = getElementByClassName('.ant-select-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
getData: () => OPTIONS[0].label,
@@ -1149,7 +1139,7 @@ test('pasting an existing option does not duplicate it in multiple mode', async
/>,
);
await open();
const input = getElementByClassName('.ant-select-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
getData: () => 'John,Liam,Peter',
@@ -1163,7 +1153,7 @@ test('pasting an existing option does not duplicate it in multiple mode', async
test('pasting an non-existent option should not add it if allowNewOptions is false', async () => {
render(<Select {...defaultProps} options={[]} allowNewOptions={false} />);
await open();
const input = getElementByClassName('.ant-select-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
getData: () => 'John',
@@ -1189,7 +1179,7 @@ test('keeps pasted values outside loaded options when allowNewOptionsOnPaste is
onChange={onChange}
/>,
);
const input = getElementByClassName('.ant-select-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
// Liam is a loaded option; OutsideValue is not in the loaded page.
@@ -1225,7 +1215,7 @@ test('trims whitespace around pasted comma-separated values', async () => {
onChange={onChange}
/>,
);
const input = getElementByClassName('.ant-select-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
// Note the space after the comma — it must not leak into the value.
@@ -1258,7 +1248,7 @@ test('does not create an empty option when pasting blank text', async () => {
onChange={onChange}
/>,
);
const input = getElementByClassName('.ant-select-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
getData: () => ' ',
@@ -1279,7 +1269,7 @@ test('does not create an empty option when pasting blank text', async () => {
test('drops pasted values outside loaded options when allowNewOptionsOnPaste is false', async () => {
render(<Select {...defaultProps} mode="multiple" allowNewOptions={false} />);
const input = getElementByClassName('.ant-select-input');
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
getData: () => 'Liam,OutsideValue',

View File

@@ -66,7 +66,7 @@ import {
} from './styles';
import {
DEFAULT_SORT_COMPARATOR,
DROPDOWN_BUILTIN_PLACEMENTS,
DROPDOWN_ALIGN_BOTTOM,
EMPTY_OPTIONS,
MAX_TAG_COUNT,
TOKEN_SEPARATORS,
@@ -130,9 +130,7 @@ const Select = forwardRef(
ref: Ref<RefSelectProps>,
) => {
const isSingleMode = mode === 'single';
// antd v6 widened `showSearch` to `boolean | SearchConfig`; coerce to a
// plain boolean for Superset's internal toggles and helpers.
const shouldShowSearch = allowNewOptions ? true : Boolean(showSearch);
const shouldShowSearch = allowNewOptions ? true : showSearch;
const [selectValue, setSelectValue] = useState(value);
const [inputValue, setInputValue] = useState('');
const [isDropdownVisible, setIsDropdownVisible] = useState(false);
@@ -721,14 +719,7 @@ const Select = forwardRef(
setSelectValue(value);
}
} else {
// 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 token = tokenSeparators.find(token => pastedText.includes(token));
const array = token
? uniq(
pastedText
@@ -877,8 +868,8 @@ const Select = forwardRef(
optionRender={option => <Space>{option.label || option.value}</Space>}
oneLine={oneLine}
popupMatchSelectWidth={oneLine ? dropdownWidth : true}
builtinPlacements={DROPDOWN_BUILTIN_PLACEMENTS}
css={props.css}
dropdownAlign={DROPDOWN_ALIGN_BOTTOM}
{...props}
showSearch={shouldShowSearch}
ref={ref}

View File

@@ -18,10 +18,7 @@
*/
import type { LabeledValue as AntdLabeledValue } from 'antd/es/select';
import {
DEFAULT_SORT_COMPARATOR,
DROPDOWN_BUILTIN_PLACEMENTS,
} from './constants';
import { DEFAULT_SORT_COMPARATOR } from './constants';
test('DEFAULT_SORT_COMPARATOR sorts by label text when both labels are strings', () => {
const a = { value: 'b', label: 'banana' } as AntdLabeledValue;
@@ -50,46 +47,3 @@ test('DEFAULT_SORT_COMPARATOR uses rankedSearchCompare when search is provided',
// 'bc' is an exact match to search 'bc', so it should sort first (lower index = negative diff)
expect(DEFAULT_SORT_COMPARATOR(a, b, 'bc')).toBeGreaterThan(0);
});
// Characterization of the dropdown-positioning fix from #36963. antd 6
// removed Select's `dropdownAlign`, so the fix is expressed through
// `builtinPlacements` — which replaces antd's ENTIRE placement map. This
// pins two things a future edit could silently break:
// 1. every placement disables horizontal viewport adjustment (the point
// of #36963 — the popup must not shift away from its input), and
// 2. all four of antd's placements are present with antd's own
// points/offsets, so replacing the map does not change unrelated
// positioning behavior.
test('DROPDOWN_BUILTIN_PLACEMENTS pins #36963 across all four antd placements', () => {
const placements = DROPDOWN_BUILTIN_PLACEMENTS as Record<
string,
{
points: string[];
offset: number[];
overflow: { adjustX: boolean; adjustY: boolean };
}
>;
expect(Object.keys(placements).sort()).toEqual([
'bottomLeft',
'bottomRight',
'topLeft',
'topRight',
]);
// antd 6's default points/offsets, unchanged
expect(placements.bottomLeft.points).toEqual(['tl', 'bl']);
expect(placements.bottomLeft.offset).toEqual([0, 4]);
expect(placements.bottomRight.points).toEqual(['tr', 'br']);
expect(placements.bottomRight.offset).toEqual([0, 4]);
expect(placements.topLeft.points).toEqual(['bl', 'tl']);
expect(placements.topLeft.offset).toEqual([0, -4]);
expect(placements.topRight.points).toEqual(['br', 'tr']);
expect(placements.topRight.offset).toEqual([0, -4]);
// the #36963 delta: no horizontal adjustment, vertical still allowed
Object.values(placements).forEach(placement => {
expect(placement.overflow.adjustX).toBe(false);
expect(placement.overflow.adjustY).toBe(true);
});
});

View File

@@ -16,54 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
import {
LabeledValue as AntdLabeledValue,
SelectProps as AntdSelectProps,
} from 'antd/es/select';
import { LabeledValue as AntdLabeledValue } from 'antd/es/select';
import { t } from '@apache-superset/core/translation';
import { rankedSearchCompare } from '../../utils/rankedSearchCompare';
import { RawValue } from './types';
import { RawValue, SelectProps } from './types';
export const MAX_TAG_COUNT = 4;
const PLACEMENT_SHARED_CONFIG = {
overflow: { adjustX: false, adjustY: true, shiftY: true },
htmlRegion: 'visible' as const,
dynamicInset: true,
};
/**
* antd 6's default Select popup placements with horizontal viewport
* adjustment disabled, preserving the dropdown-positioning fix from
* #36963. antd 6 removed the `dropdownAlign` prop that fix was expressed
* through; `builtinPlacements` is its supported replacement, but it
* replaces the entire placement map, so all four entries are provided
* (mirroring antd's own defaults except for `adjustX`).
*/
export const DROPDOWN_BUILTIN_PLACEMENTS: AntdSelectProps['builtinPlacements'] =
{
bottomLeft: {
...PLACEMENT_SHARED_CONFIG,
points: ['tl', 'bl'],
offset: [0, 4],
},
bottomRight: {
...PLACEMENT_SHARED_CONFIG,
points: ['tr', 'br'],
offset: [0, 4],
},
topLeft: {
...PLACEMENT_SHARED_CONFIG,
points: ['bl', 'tl'],
offset: [0, -4],
},
topRight: {
...PLACEMENT_SHARED_CONFIG,
points: ['br', 'tr'],
offset: [0, -4],
},
};
export const TOKEN_SEPARATORS = [',', '\r\n', '\n', '\t', ';'];
export const EMPTY_OPTIONS = [];
@@ -74,6 +33,12 @@ export const SELECT_ALL_VALUE: RawValue = t('Select All');
export const VIRTUAL_THRESHOLD = 20;
export const DROPDOWN_ALIGN_BOTTOM: SelectProps['dropdownAlign'] = {
points: ['tl', 'bl'],
offset: [0, 4],
overflow: { adjustX: 0, adjustY: 1 },
};
export const SELECT_ALL_OPTION = {
value: SELECT_ALL_VALUE,
label: String(SELECT_ALL_VALUE),

View File

@@ -54,13 +54,11 @@ export const StyledSelect = styled(Select, {
flex: ${headerPosition === 'left' ? 1 : 0};
line-height: ${theme.sizeXL}px;
&& .ant-select-input {
&& .ant-select-selection-search {
left: 0px;
}
&& .ant-select-content,
&& .ant-select-selection-item,
&& .ant-select-placeholder {
&& .ant-select-selection-item, .ant-select-selection-placeholder {
max-height: ${theme.sizeXL}px;
}
.ant-select-selection-item::after {
@@ -70,16 +68,16 @@ export const StyledSelect = styled(Select, {
${
oneLine &&
`
.ant-select-content {
.ant-select-selection-overflow {
flex-wrap: nowrap;
}
.ant-select-content-item:not(.ant-select-content-item-rest):not(.ant-select-content-item-suffix) {
.ant-select-selection-overflow-item:not(.ant-select-selection-overflow-item-rest):not(.ant-select-selection-overflow-item-suffix) {
flex-shrink: 1;
min-width: ${theme.sizeUnit * 13}px;
}
.ant-select-content-item-suffix {
.ant-select-selection-overflow-item-suffix {
flex: unset;
min-width: 0px;
}

View File

@@ -71,24 +71,12 @@ export type AntdExposedProps = Pick<
| 'virtual'
| 'getPopupContainer'
| 'menuItemSelectedIcon'
| 'dropdownAlign'
>;
export type SelectOptionsType = Exclude<AntdProps['options'], undefined>;
export interface BaseSelectProps extends AntdExposedProps {
/**
* Whether the select is searchable. antd 6 also accepts a `SearchConfig`
* object here, but Superset's Select manages search behavior itself
* (filtering, sorting, "create option" handling), so only the boolean
* form is supported — an object would be silently discarded.
*/
showSearch?: boolean;
/**
* Separators used to tokenize pasted text into multiple values.
* antd 6 also accepts a function form, but Superset's paste handling
* only supports the array form.
*/
tokenSeparators?: string[];
/**
* Optional CSS class name to apply to the select container
*/

View File

@@ -99,12 +99,9 @@ test('Body should be visible', () => {
test('Body content should be blurred loading', () => {
render(<TableCollection {...defaultProps} loading />);
// antd v6 removed the `ant-spin-blur` class. The body content is now dimmed
// via CSS applied to `.ant-spin-container` while its wrapping `.ant-spin`
// carries the `.ant-spin-spinning` class.
const container = screen.getByTestId('listview-table').parentNode;
expect(container).toHaveClass('ant-spin-container');
expect(container?.parentNode).toHaveClass('ant-spin-spinning');
expect(screen.getByTestId('listview-table').parentNode).toHaveClass(
'ant-spin-blur',
);
});
test('Should the loading-indicator be visible during loading', () => {

View File

@@ -48,7 +48,7 @@ describe('Tabs', () => {
expect(getByText('Tab 3')).toBeInTheDocument();
const activeTabContent = container.querySelector(
'.ant-tabs-content-active',
'.ant-tabs-tabpane-active',
);
expect(activeTabContent).toBeDefined();
@@ -61,7 +61,7 @@ describe('Tabs', () => {
const { container } = render(<Tabs items={defaultItems} />);
const tabsElement = container.querySelector('.ant-tabs');
const tabsNav = container.querySelector('.ant-tabs-nav');
const tabsContent = container.querySelector('.ant-tabs-body-holder');
const tabsContent = container.querySelector('.ant-tabs-content-holder');
expect(tabsElement).toBeDefined();
expect(tabsNav).toBeDefined();
@@ -213,7 +213,7 @@ describe('Tabs', () => {
expect(getByText('Legacy Tab 2')).toBeInTheDocument();
const activeTabContent = container.querySelector(
'.ant-tabs-content-active [data-testid="legacy-content-1"]',
'.ant-tabs-tabpane-active [data-testid="legacy-content-1"]',
);
expect(activeTabContent).toBeDefined();
@@ -311,9 +311,9 @@ test('fullHeight prop renders component hierarchy correctly', () => {
const { container } = render(<Tabs items={defaultItems} fullHeight />);
const tabsElement = container.querySelector('.ant-tabs');
const contentHolder = container.querySelector('.ant-tabs-body-holder');
const content = container.querySelector('.ant-tabs-body');
const tabPane = container.querySelector('.ant-tabs-content');
const contentHolder = container.querySelector('.ant-tabs-content-holder');
const content = container.querySelector('.ant-tabs-content');
const tabPane = container.querySelector('.ant-tabs-tabpane');
expect(tabsElement).toBeInTheDocument();
expect(contentHolder).toBeInTheDocument();
@@ -343,7 +343,9 @@ test('fullHeight prop maintains structure when content updates', () => {
rerender(<Tabs items={newItems} fullHeight />);
const updatedTabsElement = container.querySelector('.ant-tabs');
const updatedContentHolder = container.querySelector('.ant-tabs-body-holder');
const updatedContentHolder = container.querySelector(
'.ant-tabs-content-holder',
);
expect(updatedTabsElement).toBeInTheDocument();
expect(updatedContentHolder).toBeInTheDocument();
@@ -357,7 +359,7 @@ test('fullHeight prop works with allowOverflow to handle tall content', () => {
const tabsElement = container.querySelector('.ant-tabs') as HTMLElement;
const contentHolder = container.querySelector(
'.ant-tabs-body-holder',
'.ant-tabs-content-holder',
) as HTMLElement;
expect(tabsElement).toBeInTheDocument();

View File

@@ -48,7 +48,7 @@ const StyledTabs = ({
overflow: ${allowOverflow ? 'visible' : 'hidden'};
${fullHeight && 'height: 100%;'}
.ant-tabs-body-holder {
.ant-tabs-content-holder {
overflow: ${allowOverflow ? 'visible' : 'auto'};
${fullHeight && 'height: 100%;'}
${
@@ -57,10 +57,10 @@ const StyledTabs = ({
}
${contentPadding}
}
.ant-tabs-body {
.ant-tabs-content {
${fullHeight && 'height: 100%;'}
}
.ant-tabs-content {
.ant-tabs-tabpane {
${fullHeight && 'height: 100%;'}
${contentStyle}
}
@@ -112,7 +112,7 @@ const Tabs = Object.assign(StyledTabs, {
const StyledEditableTabs = styled(StyledTabs)`
${({ theme, contentStyle }) => `
.ant-tabs-body-holder {
.ant-tabs-content-holder {
background: ${theme.colorBgContainer};
${contentStyle}
}

View File

@@ -67,7 +67,7 @@ function findMatchingTimezone(
export default function TimezoneSelector({
onTimezoneChange,
timezone,
minWidth: _minWidth = MIN_SELECT_WIDTH,
minWidth = MIN_SELECT_WIDTH,
placeholder,
...rest
}: TimezoneSelectorProps) {

View File

@@ -78,7 +78,7 @@ InteractiveTooltip.argTypes = {
control: { type: 'color' },
description: 'Custom background color for the tooltip.',
},
onOpenChange: { action: 'onOpenChange' },
onVisibleChange: { action: 'onVisibleChange' },
};
InteractiveTooltip.parameters = {

View File

@@ -27,8 +27,7 @@ export const Tooltip = forwardRef<TooltipRef, TooltipProps>(
<AntdTooltip
ref={ref}
styles={{
// antd v6 renamed the inner content slot `body` -> `container`
container: { overflow: 'hidden', textOverflow: 'ellipsis' },
body: { overflow: 'hidden', textOverflow: 'ellipsis' },
root: overlayStyle ?? {},
}}
{...props}

View File

@@ -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, waitFor } from '@superset-ui/core/spec';
import TooltipParagraph from '.';
test('starts hidden with default props', () => {
@@ -42,11 +42,30 @@ test('not render on hover when not truncated', async () => {
expect(screen.queryByRole('tooltip')).not.toBeInTheDocument();
});
// NOTE: there is intentionally no "renders tooltip when truncated" test here.
// The tooltip only activates once antd's Typography reports the text as
// truncated via its `onEllipsis` callback, which depends on real layout
// measurement (offset/scroll widths, ResizeObserver). jsdom performs no layout,
// so truncation is never detected and the tooltip never opens. Ant Design v5 set
// `aria-describedby` on the trigger regardless, which let this case be asserted;
// v6 only wires it once the tooltip has content, so the truncated branch is no
// longer observable in jsdom. It is covered by visual / end-to-end testing.
test('render on hover when truncated', async () => {
render(
<div style={{ width: '200px' }}>
<TooltipParagraph>
<span data-test="test-text">This is too long and should truncate.</span>
</TooltipParagraph>
</div>,
);
// Get the div with the ellipsis class to verify it's truncated
const ellipsisElement = screen
.getByTestId('test-text')
.closest('.ant-typography-ellipsis');
expect(ellipsisElement).toBeInTheDocument();
// Hover over the text
await userEvent.hover(screen.getByTestId('test-text'));
// In Ant Design v5, we can check if the aria-describedby attribute is present
// which indicates the tooltip functionality is active
await waitFor(() => {
const element = screen
.getByTestId('test-text')
.closest('[aria-describedby]');
expect(element).toHaveAttribute('aria-describedby');
});
});

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