From 6d77efad29c41f6905b9db82628e3b0d12987c81 Mon Sep 17 00:00:00 2001 From: Joe Li Date: Wed, 19 Aug 2026 13:57:28 -0700 Subject: [PATCH 1/7] fix(chart): stop contextmenu propagation in BigNumberViz (#43267) Co-authored-by: Claude Sonnet 5 --- .../src/BigNumber/BigNumberViz.test.tsx | 34 +++++++++++++++++++ .../src/BigNumber/BigNumberViz.tsx | 1 + 2 files changed, 35 insertions(+) diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.test.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.test.tsx index e201d96176c1..dd7a495ee89a 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.test.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.test.tsx @@ -17,6 +17,10 @@ * under the License. */ +import { getNumberFormatter } from '@superset-ui/core'; +import { render, fireEvent } from '../../../../spec/helpers/testing-library'; +import BigNumberVis from './BigNumberViz'; + /** * Tests for the color threshold formatter logic in BigNumberViz. * @@ -83,3 +87,33 @@ describe('BigNumberViz color formatters', () => { expect(getColorFromValue).not.toHaveBeenCalled(); }); }); + +describe('BigNumberViz context menu', () => { + test('invokes onContextMenu and stops the event bubbling to ancestor handlers', () => { + const onContextMenu = jest.fn(); + const ancestorHandler = jest.fn(); + + const { container } = render( +
+ +
, + ); + + const headerLine = container.querySelector('.header-line'); + fireEvent.contextMenu(headerLine!, { clientX: 10, clientY: 20 }); + + expect(onContextMenu).toHaveBeenCalledWith(10, 20); + expect(ancestorHandler).not.toHaveBeenCalled(); + }); +}); diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.tsx index dc7d3cd2e856..ea4e89cd0ee7 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.tsx +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberViz.tsx @@ -224,6 +224,7 @@ function BigNumberVis({ const handleContextMenu = (e: MouseEvent) => { if (onContextMenu) { e.preventDefault(); + e.stopPropagation(); onContextMenu(e.nativeEvent.clientX, e.nativeEvent.clientY); } }; From 8c500ccee1f53e421f4fd5dcdf7c1469eec2fcbb Mon Sep 17 00:00:00 2001 From: Amin Ghadersohi Date: Wed, 19 Aug 2026 14:57:46 -0600 Subject: [PATCH 2/7] fix(users): show password validation errors (#43191) Co-authored-by: Claude --- .../src/features/users/UserListModal.tsx | 41 ++------ .../src/features/users/utils.test.ts | 99 +++++++++++++++++++ superset-frontend/src/features/users/utils.ts | 41 +++++++- 3 files changed, 147 insertions(+), 34 deletions(-) create mode 100644 superset-frontend/src/features/users/utils.test.ts diff --git a/superset-frontend/src/features/users/UserListModal.tsx b/superset-frontend/src/features/users/UserListModal.tsx index ca398cb7ea7b..045b727ac9aa 100644 --- a/superset-frontend/src/features/users/UserListModal.tsx +++ b/superset-frontend/src/features/users/UserListModal.tsx @@ -31,7 +31,12 @@ import { import { Group, Role, UserObject } from 'src/pages/UsersList/types'; import { Actions } from 'src/constants'; import { BaseUserListModalProps, FormValues } from './types'; -import { createUser, updateUser, atLeastOneRoleOrGroup } from './utils'; +import { + createUser, + updateUser, + atLeastOneRoleOrGroup, + handleUserError, +} from './utils'; export interface UserModalProps extends BaseUserListModalProps { roles: Role[]; @@ -51,36 +56,6 @@ function UserListModal({ }: UserModalProps) { const { addDangerToast, addSuccessToast } = useToasts(); const handleFormSubmit = async (values: FormValues) => { - const handleError = async ( - err: any, - action: Actions.CREATE | Actions.UPDATE, - ) => { - let errorMessage = - action === Actions.CREATE - ? t('There was an error creating the user. Please, try again.') - : t('There was an error updating the user. Please, try again.'); - - if (err.status === 422) { - const errorData = await err.json(); - const detail = errorData?.message || ''; - - if (detail.includes('duplicate key value')) { - if (detail.includes('ab_user_username_key')) { - errorMessage = t( - 'This username is already taken. Please choose another one.', - ); - } else if (detail.includes('ab_user_email_key')) { - errorMessage = t( - 'This email is already associated with an account. Please choose another one.', - ); - } - } - } - - addDangerToast(errorMessage); - throw err; - }; - if (isEditMode) { if (!user) { throw new Error('User is required in edit mode'); @@ -89,14 +64,14 @@ function UserListModal({ await updateUser(user.id, values); addSuccessToast(t('The user has been updated successfully.')); } catch (err) { - await handleError(err, Actions.UPDATE); + await handleUserError(err as Response, Actions.UPDATE, addDangerToast); } } else { try { await createUser(values); addSuccessToast(t('The user has been created successfully.')); } catch (err) { - await handleError(err, Actions.CREATE); + await handleUserError(err as Response, Actions.CREATE, addDangerToast); } } }; diff --git a/superset-frontend/src/features/users/utils.test.ts b/superset-frontend/src/features/users/utils.test.ts new file mode 100644 index 000000000000..bd9c80efa390 --- /dev/null +++ b/superset-frontend/src/features/users/utils.test.ts @@ -0,0 +1,99 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { Actions } from 'src/constants'; +import { handleUserError } from './utils'; + +test('shows the password validation message from a 400 response', async () => { + const error = new Response( + JSON.stringify({ + message: { + password: ['Password must be at least 8 characters long.'], + }, + }), + { status: 400 }, + ); + const addDangerToast = jest.fn(); + + await expect( + handleUserError(error, Actions.CREATE, addDangerToast), + ).rejects.toBe(error); + expect(addDangerToast).toHaveBeenCalledWith( + 'Password must be at least 8 characters long.', + ); +}); + +test('shows a plain string message from a 400 response', async () => { + const error = new Response( + JSON.stringify({ message: 'User must have at least one role or group!' }), + { status: 400 }, + ); + const addDangerToast = jest.fn(); + + await expect( + handleUserError(error, Actions.UPDATE, addDangerToast), + ).rejects.toBe(error); + expect(addDangerToast).toHaveBeenCalledWith( + 'User must have at least one role or group!', + ); +}); + +test('keeps the duplicate username message for a 422 response', async () => { + const error = new Response( + JSON.stringify({ + message: + 'duplicate key value violates unique constraint "ab_user_username_key"', + }), + { status: 422 }, + ); + const addDangerToast = jest.fn(); + + await expect( + handleUserError(error, Actions.CREATE, addDangerToast), + ).rejects.toBe(error); + expect(addDangerToast).toHaveBeenCalledWith( + 'This username is already taken. Please choose another one.', + ); +}); + +test('shows the generic message when a 422 response has no message', async () => { + const error = new Response(JSON.stringify({ foo: 'bar' }), { status: 422 }); + const addDangerToast = jest.fn(); + + await expect( + handleUserError(error, Actions.CREATE, addDangerToast), + ).rejects.toBe(error); + expect(addDangerToast).toHaveBeenCalledWith( + 'There was an error creating the user. Please, try again.', + ); +}); + +test('shows the generic message when a 400 response is not JSON', async () => { + const error = new Response('Bad request', { + status: 400, + headers: { 'Content-Type': 'text/html' }, + }); + const addDangerToast = jest.fn(); + + await expect( + handleUserError(error, Actions.CREATE, addDangerToast), + ).rejects.toBe(error); + expect(addDangerToast).toHaveBeenCalledWith( + 'There was an error creating the user. Please, try again.', + ); +}); diff --git a/superset-frontend/src/features/users/utils.ts b/superset-frontend/src/features/users/utils.ts index 5450bdca49fa..4baa60fbf3ce 100644 --- a/superset-frontend/src/features/users/utils.ts +++ b/superset-frontend/src/features/users/utils.ts @@ -17,10 +17,49 @@ * under the License. */ import { t } from '@apache-superset/core/translation'; -import { SupersetClient } from '@superset-ui/core'; +import { getClientErrorObject, SupersetClient } from '@superset-ui/core'; import { SelectOption } from 'src/components/ListView'; +import { Actions } from 'src/constants'; import { FormValues } from './types'; +type AddDangerToast = (message: string) => void; + +export const handleUserError = async ( + err: Response, + action: Actions.CREATE | Actions.UPDATE, + addDangerToast: AddDangerToast, +): Promise => { + let errorMessage = + action === Actions.CREATE + ? t('There was an error creating the user. Please, try again.') + : t('There was an error updating the user. Please, try again.'); + + if (err.status === 400 || err.status === 422) { + const errorData = await getClientErrorObject(err); + const message: unknown = errorData.message; + + if (err.status === 400 && message && errorData.error) { + errorMessage = errorData.error; + } else if ( + err.status === 422 && + errorData.error?.includes('duplicate key value') + ) { + if (errorData.error.includes('ab_user_username_key')) { + errorMessage = t( + 'This username is already taken. Please choose another one.', + ); + } else if (errorData.error.includes('ab_user_email_key')) { + errorMessage = t( + 'This email is already associated with an account. Please choose another one.', + ); + } + } + } + + addDangerToast(errorMessage); + throw err; +}; + export const createUser = async (values: FormValues) => { const { confirmPassword: _confirmPassword, ...payload } = values; if (payload.active == null) { From c10054f521eb16a39174321f6f6c4911859b240d Mon Sep 17 00:00:00 2001 From: Alejandro Solares <219859296+ASolarers-Rodriguez@users.noreply.github.com> Date: Wed, 19 Aug 2026 15:35:38 -0600 Subject: [PATCH 3/7] fix(plugin-chart-chord): declare react as a peerDependency (#43304) --- superset-frontend/package-lock.json | 19 +++++-------------- .../plugins/plugin-chart-chord/package.json | 6 +++--- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 6b48d65a953a..357098f954cf 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -20612,7 +20612,7 @@ "version": "0.8.0", "resolved": "https://registry.npmjs.org/expect-playwright/-/expect-playwright-0.8.0.tgz", "integrity": "sha512-+kn8561vHAY+dt+0gMqqj1oY+g5xWrsuGMk4QGxotT2WS545nVqqjs37z6hrYfIuucwqthzwJfCJUEYqixyljg==", - "deprecated": "⚠️ The 'expect-playwright' package is deprecated. The Playwright core assertions (via @playwright/test) now cover the same functionality. Please migrate to built-in expect. See https://playwright.dev/docs/test-assertions for migration.", + "deprecated": "\u26a0\ufe0f The 'expect-playwright' package is deprecated. The Playwright core assertions (via @playwright/test) now cover the same functionality. Please migrate to built-in expect. See https://playwright.dev/docs/test-assertions for migration.", "dev": true, "license": "MIT" }, @@ -26023,7 +26023,7 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/jest-process-manager/-/jest-process-manager-0.4.0.tgz", "integrity": "sha512-80Y6snDyb0p8GG83pDxGI/kQzwVTkCxc7ep5FPe/F6JYdvRDhwr6RzRmPSP7SEwuLhxo80lBS/NqOdUIbHIfhw==", - "deprecated": "⚠️ The 'jest-process-manager' package is deprecated. Please migrate to Playwright's built-in test runner (@playwright/test) which now includes full Jest-style features and parallel testing. See https://playwright.dev/docs/intro for details.", + "deprecated": "\u26a0\ufe0f The 'jest-process-manager' package is deprecated. Please migrate to Playwright's built-in test runner (@playwright/test) which now includes full Jest-style features and parallel testing. See https://playwright.dev/docs/intro for details.", "dev": true, "license": "MIT", "dependencies": { @@ -43429,22 +43429,13 @@ "license": "Apache-2.0", "dependencies": { "d3": "^3.5.17", - "prop-types": "^15.8.1", - "react": "^19.2.7" + "prop-types": "^15.8.1" }, "peerDependencies": { "@apache-superset/core": "*", "@superset-ui/chart-controls": "*", - "@superset-ui/core": "*" - } - }, - "plugins/plugin-chart-chord/node_modules/react": { - "version": "19.2.8", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", - "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "@superset-ui/core": "*", + "react": "^18.3.0" } }, "plugins/plugin-chart-country-map": { diff --git a/superset-frontend/plugins/plugin-chart-chord/package.json b/superset-frontend/plugins/plugin-chart-chord/package.json index 2570e2372120..c1972537c56e 100644 --- a/superset-frontend/plugins/plugin-chart-chord/package.json +++ b/superset-frontend/plugins/plugin-chart-chord/package.json @@ -30,12 +30,12 @@ }, "dependencies": { "d3": "^3.5.17", - "prop-types": "^15.8.1", - "react": "^19.2.7" + "prop-types": "^15.8.1" }, "peerDependencies": { "@apache-superset/core": "*", "@superset-ui/chart-controls": "*", - "@superset-ui/core": "*" + "@superset-ui/core": "*", + "react": "^18.3.0" } } From b8fca2145d99ad7f65ddae41215d894e8758011f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Aug 2026 14:40:02 -0700 Subject: [PATCH 4/7] chore(deps): bump astral-sh/setup-uv from 9.0.0 to 10.0.0 (#43322) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/bump-python-package.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bump-python-package.yml b/.github/workflows/bump-python-package.yml index f3356bf6ea26..50cc0d2a805c 100644 --- a/.github/workflows/bump-python-package.yml +++ b/.github/workflows/bump-python-package.yml @@ -48,7 +48,7 @@ jobs: python-version: "3.11" - name: Install uv - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 + uses: astral-sh/setup-uv@ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d # v10.0.0 with: python-version: "3.11" enable-cache: true From faf7c34c0a9bef8358a20af9cf82a1e9f754b241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CA=88=E1=B5=83=E1=B5=A2?= Date: Wed, 19 Aug 2026 15:37:09 -0700 Subject: [PATCH 5/7] fix(explore): legacy boolean filters and limit available operators based on calculated column type (#43341) --- .../AdhocFilter/AdhocFilter.test.ts | 68 +++++++++++++++++++ .../FilterControl/AdhocFilter/index.ts | 19 ++++++ ...FilterEditPopoverSimpleTabContent.test.tsx | 22 ++++++ .../index.tsx | 6 +- 4 files changed, 114 insertions(+), 1 deletion(-) diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/AdhocFilter.test.ts b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/AdhocFilter.test.ts index 42a4d6d70fbe..18d58848df71 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/AdhocFilter.test.ts +++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/AdhocFilter.test.ts @@ -270,6 +270,74 @@ describe('AdhocFilter', () => { }); expect(adhocFilter.comparator).toBe(undefined); }); + // Charts saved before #32701 persisted `==` as the operation for IS_TRUE and + // IS_FALSE, alongside a boolean comparator. `translateToSql` and the backend + // both key off `operator`, so dropping the comparator would render such a + // filter as `col =` and query it as `col IS NULL`. + test('keeps the legacy boolean comparator for IS_TRUE', () => { + const adhocFilter = new AdhocFilter({ + expressionType: ExpressionTypes.Simple, + subject: 'col', + operator: '==', + operatorId: Operators.IsTrue, + comparator: true, + clause: Clauses.Where, + }); + expect(adhocFilter.operator).toBe('=='); + expect(adhocFilter.comparator).toBe(true); + expect(adhocFilter.translateToSql()).toBe("col = 'TRUE'"); + }); + test('keeps the legacy boolean comparator for IS_FALSE', () => { + const adhocFilter = new AdhocFilter({ + expressionType: ExpressionTypes.Simple, + subject: 'col', + operator: '==', + operatorId: Operators.IsFalse, + comparator: false, + clause: Clauses.Where, + }); + expect(adhocFilter.operator).toBe('=='); + expect(adhocFilter.comparator).toBe(false); + expect(adhocFilter.translateToSql()).toBe("col = 'FALSE'"); + }); + test('restores the boolean even when the stored comparator is missing', () => { + const adhocFilter = new AdhocFilter({ + expressionType: ExpressionTypes.Simple, + subject: 'col', + operator: '==', + operatorId: Operators.IsTrue, + clause: Clauses.Where, + }); + expect(adhocFilter.comparator).toBe(true); + }); + test('keeps a legacy boolean filter intact when the control re-posts it', () => { + const stored = { + expressionType: ExpressionTypes.Simple, + subject: 'col', + operator: '==', + operatorId: Operators.IsTrue, + comparator: true, + clause: Clauses.Where, + }; + // DndFilterSelect wraps props.value and hands those instances to onChange + const posted = JSON.parse(JSON.stringify(new AdhocFilter(stored))); + expect(posted.operator).toBe('=='); + expect(posted.comparator).toBe(true); + expect(posted.operatorId).toBe(Operators.IsTrue); + }); + test('leaves a genuine equality filter on a boolean value alone', () => { + const adhocFilter = new AdhocFilter({ + expressionType: ExpressionTypes.Simple, + subject: 'col', + operator: '==', + operatorId: Operators.Equals, + comparator: true, + clause: Clauses.Where, + }); + expect(adhocFilter.operator).toBe('=='); + expect(adhocFilter.comparator).toBe(true); + expect(adhocFilter.translateToSql()).toBe("col = 'TRUE'"); + }); test('sets the label properly if subject is a string', () => { const adhocFilter = new AdhocFilter({ expressionType: ExpressionTypes.Simple, diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/index.ts b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/index.ts index 31c544ffc4a3..649b8135ba37 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/index.ts +++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilter/index.ts @@ -30,6 +30,15 @@ const CUSTOM_OPERATIONS = [...CUSTOM_OPERATORS].map( op => OPERATOR_ENUM_TO_OPERATOR_TYPE[op].operation, ); +// Charts saved before #32701 store `==` for IS_TRUE/IS_FALSE with the boolean +// in the comparator; blanking it makes them query `col IS NULL`. Restoring it +// leaves the emitted SQL untouched -- reconciling `operator` to `IS TRUE` +// would not, and Druid rejects that predicate on VARCHAR columns. +const LEGACY_BOOLEAN_COMPARATORS = new Map([ + [Operators.IsTrue, true], + [Operators.IsFalse, false], +]); + interface AdhocFilterInput { expressionType?: string; subject?: string | { column_name?: string; [key: string]: unknown } | null; @@ -77,6 +86,16 @@ export default class AdhocFilter { ) { this.comparator = undefined; } + if ( + this.operator === + OPERATOR_ENUM_TO_OPERATOR_TYPE[Operators.Equals].operation && + adhocFilter.operatorId && + LEGACY_BOOLEAN_COMPARATORS.has(adhocFilter.operatorId) + ) { + this.comparator = LEGACY_BOOLEAN_COMPARATORS.get( + adhocFilter.operatorId, + ); + } this.clause = adhocFilter.clause || Clauses.Where; this.sqlExpression = null; } else if (this.expressionType === ExpressionTypes.Sql) { diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/AdhocFilterEditPopoverSimpleTabContent.test.tsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/AdhocFilterEditPopoverSimpleTabContent.test.tsx index b86c8f526e04..f9687b491723 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/AdhocFilterEditPopoverSimpleTabContent.test.tsx +++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/AdhocFilterEditPopoverSimpleTabContent.test.tsx @@ -515,6 +515,28 @@ test('will not display boolean operators when column type is string', () => { }); }); +test.each(['STRING', 'DATE'])( + 'will not display boolean operators when an expression column declares type %s', + type => { + const props = setup({ + datasource: { + type: 'table' as const, + datasource_name: 'table1', + schema: 'schema', + columns: [{ column_name: 'value', type, expression: '"value"' }], + }, + adhocFilter: simpleAdhocFilter, + }); + const { isOperatorRelevant } = useSimpleTabFilterProps( + props as unknown as Props, + ); + const booleanOnlyOperators = [Operators.IsTrue, Operators.IsFalse]; + booleanOnlyOperators.forEach(operator => { + expect(isOperatorRelevant(operator, 'value')).toBe(false); + }); + }, +); + test('will display boolean operators when column is an expression', () => { const props = setup({ datasource: { diff --git a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx index 4e0e7eb21456..c093e703acf2 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx +++ b/superset-frontend/src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent/index.tsx @@ -160,7 +160,11 @@ export const useSimpleTabFilterProps = (props: Props) => { ].includes(operator); } if (operator === Operators.IsTrue || operator === Operators.IsFalse) { - return isColumnBoolean || isColumnNumber || isColumnFunction; + // An expression column may evaluate to a boolean, but that is only a + // safe assumption while its type is unknown; a declared type wins. + return ( + isColumnBoolean || isColumnNumber || (isColumnFunction && !column?.type) + ); } if (isColumnBoolean) { return operator === Operators.IsNull || operator === Operators.IsNotNull; From 5a96c3f538f50a9f2b25e565718834934513227e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=90=E1=BB=97=20Tr=E1=BB=8Dng=20H=E1=BA=A3i?= <41283691+hainenber@users.noreply.github.com> Date: Thu, 20 Aug 2026 06:54:44 +0700 Subject: [PATCH 6/7] chore(ci): disable Git commit info capture in Playwright E2E tests to avoid timeout (#43213) Signed-off-by: hainenber Co-authored-by: Joe Li --- superset-frontend/playwright.config.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/superset-frontend/playwright.config.ts b/superset-frontend/playwright.config.ts index 6b67bb3975b3..edbe94b326a6 100644 --- a/superset-frontend/playwright.config.ts +++ b/superset-frontend/playwright.config.ts @@ -47,6 +47,10 @@ export default defineConfig({ // Retry logic - 2 retries in CI, 0 locally retries: process.env.CI ? 2 : 0, + // Disable capturing Git commit info as the project's history is increasingly dense + // and breach Playwright's default 3-seconds `git` command timeout limit + captureGitInfo: { commit: false, diff: false }, + // Reporter configuration - multiple reporters for better visibility reporter: process.env.CI ? [ From c2d653b4b8051e0d3e51c42b0304db71aafb31c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Gailly?= <59643626+greggailly@users.noreply.github.com> Date: Thu, 20 Aug 2026 01:56:30 +0200 Subject: [PATCH 7/7] fix: set maxHeight of List components to height when in AutoSizer (#43056) Co-authored-by: Evan Rusackas --- superset-frontend/src/dashboard/components/SliceAdder.tsx | 2 +- .../src/explore/components/DatasourcePanel/DatasourceItems.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/superset-frontend/src/dashboard/components/SliceAdder.tsx b/superset-frontend/src/dashboard/components/SliceAdder.tsx index f260bc0a2d58..690d1ad6d4de 100644 --- a/superset-frontend/src/dashboard/components/SliceAdder.tsx +++ b/superset-frontend/src/dashboard/components/SliceAdder.tsx @@ -468,7 +468,7 @@ function SliceAdder({ {({ height, width }: { height: number; width: number }) => (