diff --git a/superset-frontend/cypress-base/cypress/e2e/dashboard_list/list.test.ts b/superset-frontend/cypress-base/cypress/e2e/dashboard_list/list.test.ts
index 859cd5de355..87fe3245462 100644
--- a/superset-frontend/cypress-base/cypress/e2e/dashboard_list/list.test.ts
+++ b/superset-frontend/cypress-base/cypress/e2e/dashboard_list/list.test.ts
@@ -89,8 +89,10 @@ describe('Dashboards list', () => {
it('should bulk select in list mode', () => {
toggleBulkSelect();
- cy.get('[aria-label="Select all"]').click();
- cy.get('.ant-checkbox-input')
+ cy.get('th.ant-table-cell input[aria-label="Select all"]').click();
+ cy.get(
+ '.ant-checkbox-input:not(th.ant-table-measure-cell .ant-checkbox-input)',
+ )
.should('be.checked')
.should('have.length', 6);
cy.getBySel('bulk-select-copy').contains('5 Selected');
diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json
index a6e1920ebf0..25cc03c8994 100644
--- a/superset-frontend/package-lock.json
+++ b/superset-frontend/package-lock.json
@@ -57,7 +57,7 @@
"@visx/xychart": "^3.5.1",
"ag-grid-community": "34.2.0",
"ag-grid-react": "34.2.0",
- "antd": "^5.24.9",
+ "antd": "^5.26.0",
"chrono-node": "^2.7.8",
"classnames": "^2.2.5",
"content-disposition": "^0.5.4",
@@ -11204,7 +11204,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@rc-component/context/-/context-1.4.0.tgz",
"integrity": "sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==",
- "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.10.1",
"rc-util": "^5.27.0"
@@ -11263,14 +11262,12 @@
}
},
"node_modules/@rc-component/qrcode": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.0.0.tgz",
- "integrity": "sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg==",
- "license": "MIT",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.0.1.tgz",
+ "integrity": "sha512-g8eeeaMyFXVlq8cZUeaxCDhfIYjpao0l9cvm5gFwKXy/Vm1yDWV7h2sjH5jHYzdFedlVKBpATFB1VKMrHzwaWQ==",
"dependencies": {
"@babel/runtime": "^7.24.7",
- "classnames": "^2.3.2",
- "rc-util": "^5.38.0"
+ "classnames": "^2.3.2"
},
"engines": {
"node": ">=8.x"
@@ -11301,10 +11298,9 @@
}
},
"node_modules/@rc-component/trigger": {
- "version": "2.2.6",
- "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.2.6.tgz",
- "integrity": "sha512-/9zuTnWwhQ3S3WT1T8BubuFTT46kvnXgaERR9f4BTKyn61/wpf/BvbImzYBubzJibU707FxwbKszLlHjcLiv1Q==",
- "license": "MIT",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.3.0.tgz",
+ "integrity": "sha512-iwaxZyzOuK0D7lS+0AQEtW52zUWxoGqTGkke3dRyb8pYiShmRpCjB/8TzPI4R6YySCH7Vm9BZj/31VPiiQTLBg==",
"dependencies": {
"@babel/runtime": "^7.23.2",
"@rc-component/portal": "^1.1.0",
@@ -19504,10 +19500,9 @@
}
},
"node_modules/antd": {
- "version": "5.25.4",
- "resolved": "https://registry.npmjs.org/antd/-/antd-5.25.4.tgz",
- "integrity": "sha512-yXdWqq1NJSZnD1HoPZWnWuQJGVYYnB3h0Ufsz4sbt3T0N9SdJ4G9GPpLMk8Gn9zWtwBekfR4THPVZ9uzAyhBHQ==",
- "license": "MIT",
+ "version": "5.27.6",
+ "resolved": "https://registry.npmjs.org/antd/-/antd-5.27.6.tgz",
+ "integrity": "sha512-70HrjVbzDXvtiUQ5MP1XdNudr/wGAk9Ivaemk6f36yrAeJurJSmZ8KngOIilolLRHdGuNc6/Vk+4T1OZpSjpag==",
"dependencies": {
"@ant-design/colors": "^7.2.1",
"@ant-design/cssinjs": "^1.23.0",
@@ -19518,9 +19513,9 @@
"@babel/runtime": "^7.26.0",
"@rc-component/color-picker": "~2.0.1",
"@rc-component/mutate-observer": "^1.1.0",
- "@rc-component/qrcode": "~1.0.0",
+ "@rc-component/qrcode": "~1.0.1",
"@rc-component/tour": "~1.15.1",
- "@rc-component/trigger": "^2.2.6",
+ "@rc-component/trigger": "^2.3.0",
"classnames": "^2.5.1",
"copy-to-clipboard": "^3.3.3",
"dayjs": "^1.11.11",
@@ -19545,12 +19540,12 @@
"rc-resize-observer": "^1.4.3",
"rc-segmented": "~2.7.0",
"rc-select": "~14.16.8",
- "rc-slider": "~11.1.8",
+ "rc-slider": "~11.1.9",
"rc-steps": "~6.0.1",
"rc-switch": "~4.1.0",
- "rc-table": "~7.50.5",
- "rc-tabs": "~15.6.1",
- "rc-textarea": "~1.10.0",
+ "rc-table": "~7.54.0",
+ "rc-tabs": "~15.7.0",
+ "rc-textarea": "~1.10.2",
"rc-tooltip": "~6.4.0",
"rc-tree": "~5.13.1",
"rc-tree-select": "~5.27.0",
@@ -47878,7 +47873,6 @@
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.2.1.tgz",
"integrity": "sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA==",
- "license": "MIT",
"dependencies": {
"@babel/runtime": "^7.18.3",
"@rc-component/trigger": "^2.0.0",
@@ -48202,10 +48196,9 @@
}
},
"node_modules/rc-slider": {
- "version": "11.1.8",
- "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.8.tgz",
- "integrity": "sha512-2gg/72YFSpKP+Ja5AjC5DPL1YnV8DEITDQrcc1eASrUYjl0esptaBVJBh5nLTXCCp15eD8EuGjwezVGSHhs9tQ==",
- "license": "MIT",
+ "version": "11.1.9",
+ "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.9.tgz",
+ "integrity": "sha512-h8IknhzSh3FEM9u8ivkskh+Ef4Yo4JRIY2nj7MrH6GQmrwV6mcpJf5/4KgH5JaVI1H3E52yCdpOlVyGZIeph5A==",
"dependencies": {
"@babel/runtime": "^7.10.1",
"classnames": "^2.2.5",
@@ -48253,10 +48246,9 @@
}
},
"node_modules/rc-table": {
- "version": "7.50.5",
- "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.50.5.tgz",
- "integrity": "sha512-FDZu8aolhSYd3v9KOc3lZOVAU77wmRRu44R0Wfb8Oj1dXRUsloFaXMSl6f7yuWZUxArJTli7k8TEOX2mvhDl4A==",
- "license": "MIT",
+ "version": "7.54.0",
+ "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-7.54.0.tgz",
+ "integrity": "sha512-/wDTkki6wBTjwylwAGjpLKYklKo9YgjZwAU77+7ME5mBoS32Q4nAwoqhA2lSge6fobLW3Tap6uc5xfwaL2p0Sw==",
"dependencies": {
"@babel/runtime": "^7.10.1",
"@rc-component/context": "^1.4.0",
@@ -48274,10 +48266,9 @@
}
},
"node_modules/rc-tabs": {
- "version": "15.6.1",
- "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.6.1.tgz",
- "integrity": "sha512-/HzDV1VqOsUWyuC0c6AkxVYFjvx9+rFPKZ32ejxX0Uc7QCzcEjTA9/xMgv4HemPKwzBNX8KhGVbbumDjnj92aA==",
- "license": "MIT",
+ "version": "15.7.0",
+ "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.7.0.tgz",
+ "integrity": "sha512-ZepiE+6fmozYdWf/9gVp7k56PKHB1YYoDsKeQA1CBlJ/POIhjkcYiv0AGP0w2Jhzftd3AVvZP/K+V+Lpi2ankA==",
"dependencies": {
"@babel/runtime": "^7.11.2",
"classnames": "2.x",
@@ -48296,10 +48287,9 @@
}
},
"node_modules/rc-textarea": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.10.0.tgz",
- "integrity": "sha512-ai9IkanNuyBS4x6sOL8qu/Ld40e6cEs6pgk93R+XLYg0mDSjNBGey6/ZpDs5+gNLD7urQ14po3V6Ck2dJLt9SA==",
- "license": "MIT",
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.10.2.tgz",
+ "integrity": "sha512-HfaeXiaSlpiSp0I/pvWpecFEHpVysZ9tpDLNkxQbMvMz6gsr7aVZ7FpWP9kt4t7DB+jJXesYS0us1uPZnlRnwQ==",
"dependencies": {
"@babel/runtime": "^7.10.1",
"classnames": "^2.2.1",
@@ -61414,7 +61404,7 @@
"typescript": "^5.0.0"
},
"peerDependencies": {
- "antd": "^5.24.9",
+ "antd": "^5.26.0",
"react": "^17.0.2"
}
},
@@ -64203,7 +64193,7 @@
"@types/react-loadable": "*",
"@types/react-window": "^1.8.8",
"@types/tinycolor2": "*",
- "antd": "^5.24.9",
+ "antd": "^5.26.0",
"nanoid": "^5.0.9",
"react": "^17.0.2",
"react-dom": "^17.0.2",
diff --git a/superset-frontend/package.json b/superset-frontend/package.json
index 3e684c47fdd..8e9596c4332 100644
--- a/superset-frontend/package.json
+++ b/superset-frontend/package.json
@@ -135,7 +135,7 @@
"@visx/xychart": "^3.5.1",
"ag-grid-community": "34.2.0",
"ag-grid-react": "34.2.0",
- "antd": "^5.24.9",
+ "antd": "^5.26.0",
"chrono-node": "^2.7.8",
"classnames": "^2.2.5",
"content-disposition": "^0.5.4",
diff --git a/superset-frontend/packages/superset-core/package.json b/superset-frontend/packages/superset-core/package.json
index 01f14ad6cea..a7449278352 100644
--- a/superset-frontend/packages/superset-core/package.json
+++ b/superset-frontend/packages/superset-core/package.json
@@ -22,7 +22,7 @@
"typescript": "^5.0.0"
},
"peerDependencies": {
- "antd": "^5.24.9",
+ "antd": "^5.26.0",
"react": "^17.0.2"
},
"scripts": {
diff --git a/superset-frontend/packages/superset-ui-core/package.json b/superset-frontend/packages/superset-ui-core/package.json
index 0f173993d91..0862fe177f3 100644
--- a/superset-frontend/packages/superset-ui-core/package.json
+++ b/superset-frontend/packages/superset-ui-core/package.json
@@ -91,7 +91,7 @@
"timezone-mock": "1.3.6"
},
"peerDependencies": {
- "antd": "^5.24.9",
+ "antd": "^5.26.0",
"@emotion/cache": "^11.4.0",
"@emotion/react": "^11.4.1",
"@emotion/styled": "^11.14.1",
diff --git a/superset-frontend/packages/superset-ui-core/src/components/Select/AsyncSelect.test.tsx b/superset-frontend/packages/superset-ui-core/src/components/Select/AsyncSelect.test.tsx
index 2a1cefd83f2..48264a05428 100644
--- a/superset-frontend/packages/superset-ui-core/src/components/Select/AsyncSelect.test.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/components/Select/AsyncSelect.test.tsx
@@ -513,8 +513,9 @@ test('opens the select without any data', async () => {
test('displays the loading indicator when opening', async () => {
render();
+ userEvent.click(getSelect());
+
await waitFor(async () => {
- await userEvent.click(getSelect());
expect(screen.getByText(LOADING)).toBeInTheDocument();
});
expect(screen.queryByText(LOADING)).not.toBeInTheDocument();
diff --git a/superset-frontend/packages/superset-ui-core/src/components/Table/Table.test.tsx b/superset-frontend/packages/superset-ui-core/src/components/Table/Table.test.tsx
index 48fe7327bbd..a99f9f8f9e8 100644
--- a/superset-frontend/packages/superset-ui-core/src/components/Table/Table.test.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/components/Table/Table.test.tsx
@@ -68,7 +68,11 @@ test('renders with default props', async () => {
);
await waitFor(() =>
testColumns.forEach(column =>
- expect(screen.getByText(column.title as string)).toBeInTheDocument(),
+ expect(
+ screen
+ .getAllByText(column.title as string)
+ .find(el => el.closest('th')),
+ ).toBeInTheDocument(),
),
);
testData.forEach(row => {
diff --git a/superset-frontend/packages/superset-ui-core/src/components/TableCollection/TableCollection.test.tsx b/superset-frontend/packages/superset-ui-core/src/components/TableCollection/TableCollection.test.tsx
index 41bb917bc06..39620b9ecdf 100644
--- a/superset-frontend/packages/superset-ui-core/src/components/TableCollection/TableCollection.test.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/components/TableCollection/TableCollection.test.tsx
@@ -79,8 +79,8 @@ beforeEach(() => {
test('Headers should be visible', () => {
render();
- expect(screen.getByText('Column 1')).toBeVisible();
- expect(screen.getByText('Column 2')).toBeVisible();
+ expect(screen.getByLabelText('Column 1')).toBeVisible();
+ expect(screen.getByLabelText('Column 2')).toBeVisible();
});
test('Body should be visible', () => {
@@ -226,7 +226,7 @@ test('should call setSortBy when clicking sortable column header', () => {
render();
// Target the nested field column (the column that needs the array-to-dot conversion)
- const nestedFieldHeader = screen.getByText('Nested Field');
+ const nestedFieldHeader = screen.getAllByText('Nested Field')[0];
expect(nestedFieldHeader).toBeInTheDocument();
// Click on the nested field column header to trigger sorting
diff --git a/superset-frontend/packages/superset-ui-core/src/components/TableView/TableView.test.tsx b/superset-frontend/packages/superset-ui-core/src/components/TableView/TableView.test.tsx
index 7d0b9a30005..60d1d2634ff 100644
--- a/superset-frontend/packages/superset-ui-core/src/components/TableView/TableView.test.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/components/TableView/TableView.test.tsx
@@ -59,9 +59,9 @@ test('should render a table', () => {
test('should render the headers', () => {
render();
expect(screen.getAllByRole('columnheader')).toHaveLength(3);
- expect(screen.getByText('ID')).toBeInTheDocument();
- expect(screen.getByText('Age')).toBeInTheDocument();
- expect(screen.getByText('Name')).toBeInTheDocument();
+ expect(screen.getByTitle('ID')).toBeInTheDocument();
+ expect(screen.getByTitle('Age')).toBeInTheDocument();
+ expect(screen.getByTitle('Name')).toBeInTheDocument();
});
test('should render the rows', () => {
diff --git a/superset-frontend/src/SqlLab/components/EstimateQueryCostButton/EstimateQueryCostButton.test.tsx b/superset-frontend/src/SqlLab/components/EstimateQueryCostButton/EstimateQueryCostButton.test.tsx
index d2af4cafece..684aef7b06e 100644
--- a/superset-frontend/src/SqlLab/components/EstimateQueryCostButton/EstimateQueryCostButton.test.tsx
+++ b/superset-frontend/src/SqlLab/components/EstimateQueryCostButton/EstimateQueryCostButton.test.tsx
@@ -111,7 +111,7 @@ describe('EstimateQueryCostButton', () => {
});
test('renders estimation success result', async () => {
- const { queryByText, getByText } = setup(
+ const { queryByText, getByText, findByTitle } = setup(
{},
mockStore({
...initialState,
@@ -129,7 +129,7 @@ describe('EstimateQueryCostButton', () => {
expect(queryByText('Estimate cost')).toBeInTheDocument();
fireEvent.click(getByText('Estimate cost'));
-
- expect(queryByText('Total cost')).toBeInTheDocument();
+ const totalCostTitle = await findByTitle('Total cost');
+ expect(totalCostTitle).toBeInTheDocument();
});
});
diff --git a/superset-frontend/src/components/Datasource/DatasourceModal/DatasourceModal.test.jsx b/superset-frontend/src/components/Datasource/DatasourceModal/DatasourceModal.test.jsx
index 5f449cc3d29..87dd400eb36 100644
--- a/superset-frontend/src/components/Datasource/DatasourceModal/DatasourceModal.test.jsx
+++ b/superset-frontend/src/components/Datasource/DatasourceModal/DatasourceModal.test.jsx
@@ -125,15 +125,18 @@ describe('DatasourceModal', () => {
const putSpy = jest
.spyOn(SupersetClient, 'put')
.mockRejectedValue(new Error('Something went wrong'));
+
await act(async () => {
const saveButton = screen.getByTestId('datasource-modal-save');
fireEvent.click(saveButton);
const okButton = await screen.findByRole('button', { name: 'OK' });
okButton.click();
});
+
await act(async () => {
- const errorTitle = await screen.findByText('Error saving dataset');
- expect(errorTitle).toBeInTheDocument();
+ const errorElements = await screen.findAllByText('Error saving dataset');
+ const errorDiv = errorElements.find(el => el.closest('div'));
+ expect(errorDiv).toBeInTheDocument();
});
putSpy.mockRestore();
});
diff --git a/superset-frontend/src/components/Datasource/components/DatasourceEditor/components/DatasetUsageTab/DatasetUsageTab.test.tsx b/superset-frontend/src/components/Datasource/components/DatasourceEditor/components/DatasetUsageTab/DatasetUsageTab.test.tsx
index a66c2da1c1e..27da480b1db 100644
--- a/superset-frontend/src/components/Datasource/components/DatasourceEditor/components/DatasetUsageTab/DatasetUsageTab.test.tsx
+++ b/superset-frontend/src/components/Datasource/components/DatasourceEditor/components/DatasetUsageTab/DatasetUsageTab.test.tsx
@@ -183,10 +183,23 @@ test('renders correct column headers', async () => {
setupTest();
await waitFor(() => {
- expect(screen.getByText('Chart')).toBeInTheDocument();
- expect(screen.getByText('Chart owners')).toBeInTheDocument();
- expect(screen.getByText('Last modified')).toBeInTheDocument();
- expect(screen.getByText('Dashboard usage')).toBeInTheDocument();
+ const chartHeader = screen
+ .getAllByText('Chart')
+ .find(el => el.closest('th'));
+ const ownersHeader = screen
+ .getAllByText('Chart owners')
+ .find(el => el.closest('th'));
+ const lastModifiedHeader = screen
+ .getAllByText('Last modified')
+ .find(el => el.closest('th'));
+ const dashboardHeader = screen
+ .getAllByText('Dashboard usage')
+ .find(el => el.closest('th'));
+
+ expect(chartHeader).toBeInTheDocument();
+ expect(ownersHeader).toBeInTheDocument();
+ expect(lastModifiedHeader).toBeInTheDocument();
+ expect(dashboardHeader).toBeInTheDocument();
});
});
@@ -211,16 +224,29 @@ test('displays data in correct order (last modified desc)', async () => {
test('enables sorting for Chart and Last modified columns', async () => {
setupTest();
-
await waitFor(() => {
- const chartHeader = screen.getByText('Chart').closest('th');
- const lastModifiedHeader = screen.getByText('Last modified').closest('th');
- const ownersHeader = screen.getByText('Chart owners').closest('th');
- const dashboardHeader = screen.getByText('Dashboard usage').closest('th');
+ const chartHeader = screen
+ .getAllByText('Chart')
+ .find(el => el.closest('th'))
+ ?.closest('th');
+
+ const lastModifiedHeader = screen
+ .getAllByText('Last modified')
+ .find(el => el.closest('th'))
+ ?.closest('th');
+
+ const ownersHeader = screen
+ .getAllByText('Chart owners')
+ .find(el => el.closest('th'))
+ ?.closest('th');
+
+ const dashboardHeader = screen
+ .getAllByText('Dashboard usage')
+ .find(el => el.closest('th'))
+ ?.closest('th');
expect(chartHeader).toHaveClass('ant-table-column-has-sorters');
expect(lastModifiedHeader).toHaveClass('ant-table-column-has-sorters');
-
expect(ownersHeader).not.toHaveClass('ant-table-column-has-sorters');
expect(dashboardHeader).not.toHaveClass('ant-table-column-has-sorters');
});
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx
index 9a8f2e060ab..af57bee7597 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal.test.tsx
@@ -340,8 +340,8 @@ test('validates the pre-filter value', async () => {
try {
defaultRender();
- await userEvent.click(screen.getByText(FILTER_SETTINGS_REGEX));
- await userEvent.click(getCheckbox(PRE_FILTER_REGEX));
+ userEvent.click(screen.getByText(FILTER_SETTINGS_REGEX));
+ userEvent.click(getCheckbox(PRE_FILTER_REGEX));
jest.runAllTimers();
} finally {
diff --git a/superset-frontend/src/explore/components/controls/ControlPopover/ControlPopover.tsx b/superset-frontend/src/explore/components/controls/ControlPopover/ControlPopover.tsx
index 75159b6c7c7..6837e350d5b 100644
--- a/superset-frontend/src/explore/components/controls/ControlPopover/ControlPopover.tsx
+++ b/superset-frontend/src/explore/components/controls/ControlPopover/ControlPopover.tsx
@@ -57,7 +57,6 @@ const ControlPopover: FC = ({
...props
}) => {
const triggerElementRef = useRef();
-
const [visible, setVisible] = useState(
visibleProp === undefined ? props.defaultOpen : visibleProp,
);
@@ -65,7 +64,7 @@ const ControlPopover: FC = ({
React.useState(initialPlacement);
const calculatePlacement = useCallback(() => {
- if (!triggerElementRef.current) return;
+ if (!triggerElementRef.current || !visible) return;
const { yRatio, xRatio } = getVisibilityRatio(triggerElementRef.current);
@@ -87,10 +86,10 @@ const ControlPopover: FC = ({
if (newPlacement !== placement) {
setPlacement(newPlacement);
}
- }, [getVisibilityRatio]);
+ }, [getVisibilityRatio, visible, placement]);
const changeContainerScrollStatus = useCallback(
- visible => {
+ (visible: boolean | undefined) => {
const element = getSectionContainerElement();
if (element) {
element.style.setProperty(
@@ -106,7 +105,6 @@ const ControlPopover: FC = ({
const handleGetPopupContainer = useCallback(
(triggerNode: HTMLElement) => {
triggerElementRef.current = triggerNode;
-
return getPopupContainer?.(triggerNode) || document.body;
},
[calculatePlacement, getPopupContainer],
@@ -117,7 +115,6 @@ const ControlPopover: FC = ({
if (visible === undefined) {
changeContainerScrollStatus(visible);
}
-
setVisible(!!visible);
props.onOpenChange?.(!!visible);
},
@@ -133,6 +130,14 @@ const ControlPopover: FC = ({
},
[props],
);
+ const handleAfterOpenChange = useCallback(
+ (open: boolean) => {
+ if (open) {
+ calculatePlacement();
+ }
+ },
+ [calculatePlacement],
+ );
useEffect(() => {
if (visibleProp !== undefined) {
@@ -157,9 +162,34 @@ const ControlPopover: FC = ({
}, [handleDocumentKeyDownListener, visible]);
useEffect(() => {
- if (visible) {
- calculatePlacement();
- }
+ if (!visible || !triggerElementRef.current) return () => {};
+
+ const resizeObserver = new ResizeObserver(() => {
+ requestAnimationFrame(() => {
+ calculatePlacement();
+ });
+ });
+
+ const intersectionObserver = new IntersectionObserver(
+ entries => {
+ entries.forEach(entry => {
+ if (entry.isIntersecting) {
+ calculatePlacement();
+ }
+ });
+ },
+ { threshold: [0, 0.25, 0.5, 0.75, 1] },
+ );
+
+ resizeObserver.observe(
+ triggerElementRef.current.parentElement || document.body,
+ );
+ intersectionObserver.observe(triggerElementRef.current);
+
+ return () => {
+ resizeObserver.disconnect();
+ intersectionObserver.disconnect();
+ };
}, [visible, calculatePlacement]);
return (
@@ -171,6 +201,7 @@ const ControlPopover: FC = ({
onOpenChange={handleOnVisibleChange}
getPopupContainer={handleGetPopupContainer}
destroyTooltipOnHide={destroyTooltipOnHide}
+ afterOpenChange={handleAfterOpenChange}
/>
);
};
diff --git a/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/DatasetPanel.test.tsx b/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/DatasetPanel.test.tsx
index cbcc35b88d4..fbd6c5ea9af 100644
--- a/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/DatasetPanel.test.tsx
+++ b/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/DatasetPanel.test.tsx
@@ -140,12 +140,12 @@ describe('DatasetPanel', () => {
},
);
expect(await screen.findByText(tableName)).toBeVisible();
- expect(screen.getByText(COLUMN_TITLE)).toBeVisible();
+ expect(screen.getByTitle(COLUMN_TITLE)).toBeVisible();
expect(
- screen.getByText(tableColumnDefinition[0].title as string),
+ screen.getByLabelText(tableColumnDefinition[0].title as string),
).toBeInTheDocument();
expect(
- screen.getByText(tableColumnDefinition[1].title as string),
+ screen.getByLabelText(tableColumnDefinition[1].title as string),
).toBeInTheDocument();
exampleColumns.forEach(row => {
expect(screen.getByText(row.name)).toBeInTheDocument();
diff --git a/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/DatasetPanel.tsx b/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/DatasetPanel.tsx
index 8655cf84508..c1f33053568 100644
--- a/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/DatasetPanel.tsx
+++ b/superset-frontend/src/features/datasets/AddDataset/DatasetPanel/DatasetPanel.tsx
@@ -277,7 +277,7 @@ const DatasetPanel = ({
if (!loading && tableName && hasColumns && !hasError) {
component = (
<>
- {COLUMN_TITLE}
+ {COLUMN_TITLE}
{tableWithDataset ? (
diff --git a/superset-frontend/src/features/roles/RoleListEditModal.test.tsx b/superset-frontend/src/features/roles/RoleListEditModal.test.tsx
index 7e9bfb6e791..2c4d88dc9b3 100644
--- a/superset-frontend/src/features/roles/RoleListEditModal.test.tsx
+++ b/superset-frontend/src/features/roles/RoleListEditModal.test.tsx
@@ -203,9 +203,9 @@ describe('RoleListEditModal', () => {
const usersTab = screen.getByRole('tab', { name: 'Users' });
fireEvent.click(usersTab);
- expect(screen.getByText('First Name')).toBeInTheDocument();
- expect(screen.getByText('Last Name')).toBeInTheDocument();
- expect(screen.getByText('User Name')).toBeInTheDocument();
- expect(screen.getByText('Email')).toBeInTheDocument();
+ expect(screen.getByTitle('First Name')).toBeInTheDocument();
+ expect(screen.getByTitle('Last Name')).toBeInTheDocument();
+ expect(screen.getByTitle('User Name')).toBeInTheDocument();
+ expect(screen.getByTitle('Email')).toBeInTheDocument();
});
});
diff --git a/superset-frontend/src/pages/AlertReportList/AlertReportList.test.jsx b/superset-frontend/src/pages/AlertReportList/AlertReportList.test.jsx
index 1748afe028c..6aab05ec375 100644
--- a/superset-frontend/src/pages/AlertReportList/AlertReportList.test.jsx
+++ b/superset-frontend/src/pages/AlertReportList/AlertReportList.test.jsx
@@ -247,31 +247,31 @@ describe('AlertList', () => {
renderAlertList();
await screen.findByTestId('alerts-list-view');
- expect(screen.getByText('Last run')).toBeInTheDocument();
+ expect(screen.getByTitle('Last run')).toBeInTheDocument();
expect(
screen.getByRole('columnheader', { name: /name/i }),
).toBeInTheDocument();
- expect(screen.getByText('Schedule')).toBeInTheDocument();
- expect(screen.getByText('Notification method')).toBeInTheDocument();
- expect(screen.getByText('Owners')).toBeInTheDocument();
- expect(screen.getByText('Last modified')).toBeInTheDocument();
- expect(screen.getByText('Active')).toBeInTheDocument();
- expect(screen.getByText('Actions')).toBeInTheDocument();
+ expect(screen.getByTitle('Schedule')).toBeInTheDocument();
+ expect(screen.getByTitle('Notification method')).toBeInTheDocument();
+ expect(screen.getByTitle('Owners')).toBeInTheDocument();
+ expect(screen.getByTitle('Last modified')).toBeInTheDocument();
+ expect(screen.getByTitle('Active')).toBeInTheDocument();
+ expect(screen.getByTitle('Actions')).toBeInTheDocument();
}, 15000);
test('renders correct column headers for reports', async () => {
renderAlertList({ isReportEnabled: true });
await screen.findByTestId('alerts-list-view');
- expect(screen.getByText('Last run')).toBeInTheDocument();
+ expect(screen.getByTitle('Last run')).toBeInTheDocument();
expect(
screen.getByRole('columnheader', { name: /name/i }),
).toBeInTheDocument();
- expect(screen.getByText('Schedule')).toBeInTheDocument();
- expect(screen.getByText('Notification method')).toBeInTheDocument();
- expect(screen.getByText('Owners')).toBeInTheDocument();
- expect(screen.getByText('Last modified')).toBeInTheDocument();
- expect(screen.getByText('Active')).toBeInTheDocument();
- expect(screen.getByText('Actions')).toBeInTheDocument();
+ expect(screen.getByTitle('Schedule')).toBeInTheDocument();
+ expect(screen.getByTitle('Notification method')).toBeInTheDocument();
+ expect(screen.getByTitle('Owners')).toBeInTheDocument();
+ expect(screen.getByTitle('Last modified')).toBeInTheDocument();
+ expect(screen.getByTitle('Active')).toBeInTheDocument();
+ expect(screen.getByTitle('Actions')).toBeInTheDocument();
}, 15000);
});
diff --git a/superset-frontend/src/pages/ChartList/ChartList.listview.test.tsx b/superset-frontend/src/pages/ChartList/ChartList.listview.test.tsx
index 6589b77ecfb..65264947b16 100644
--- a/superset-frontend/src/pages/ChartList/ChartList.listview.test.tsx
+++ b/superset-frontend/src/pages/ChartList/ChartList.listview.test.tsx
@@ -219,7 +219,7 @@ describe('ChartList - List View Tests', () => {
// Verify all expected headers are present
expectedHeaders.forEach(headerText => {
- expect(within(table).getByText(headerText)).toBeInTheDocument();
+ expect(within(table).getByTitle(headerText)).toBeInTheDocument();
});
});
@@ -231,11 +231,15 @@ describe('ChartList - List View Tests', () => {
});
const table = screen.getByTestId('listview-table');
- const sortableHeaders = table.querySelectorAll('.ant-table-column-sorters');
+ const allHeaders = table.querySelectorAll('.ant-table-column-sorters');
+
+ const sortableHeaders = Array.from(allHeaders).filter(
+ header => !header.closest('.ant-table-measure-cell-content'),
+ );
expect(sortableHeaders).toHaveLength(3);
- const nameHeader = within(table).getByText('Name');
+ const nameHeader = within(table).getByTitle('Name');
fireEvent.click(nameHeader);
await waitFor(() => {
@@ -248,7 +252,7 @@ describe('ChartList - List View Tests', () => {
expect(sortCalls).toHaveLength(1);
});
- const typeHeader = within(table).getByText('Type');
+ const typeHeader = within(table).getByTitle('Type');
fireEvent.click(typeHeader);
await waitFor(() => {
@@ -261,7 +265,7 @@ describe('ChartList - List View Tests', () => {
expect(typeSortCalls).toHaveLength(1);
});
- const lastModifiedHeader = within(table).getByText('Last modified');
+ const lastModifiedHeader = within(table).getByTitle('Last modified');
fireEvent.click(lastModifiedHeader);
await waitFor(() => {
@@ -602,7 +606,7 @@ describe('ChartList - List View Tests', () => {
const testChart = mockCharts[0];
const table = screen.getByTestId('listview-table');
- expect(within(table).getByText('Tags')).toBeInTheDocument();
+ expect(within(table).getByTitle('Tags')).toBeInTheDocument();
await waitFor(() => {
expect(within(table).getByText(testChart.slice_name)).toBeInTheDocument();
@@ -651,7 +655,7 @@ describe('ChartList - List View Tests', () => {
});
// Use the header checkbox to select all
- const selectAllCheckbox = screen.getByLabelText('Select all');
+ const selectAllCheckbox = screen.getAllByLabelText('Select all')[0];
expect(selectAllCheckbox).not.toBeChecked();
fireEvent.click(selectAllCheckbox);
@@ -714,7 +718,7 @@ describe('ChartList - List View Tests', () => {
});
// Use select all to select multiple charts
- const selectAllCheckbox = screen.getByLabelText('Select all');
+ const selectAllCheckbox = screen.getAllByLabelText('Select all')[0];
fireEvent.click(selectAllCheckbox);
await waitFor(() => {
@@ -763,7 +767,7 @@ describe('ChartList - List View Tests', () => {
});
// Use select all to select multiple charts
- const selectAllCheckbox = screen.getByLabelText('Select all');
+ const selectAllCheckbox = screen.getAllByLabelText('Select all')[0];
fireEvent.click(selectAllCheckbox);
await waitFor(() => {
diff --git a/superset-frontend/src/pages/ChartList/ChartList.permissions.test.tsx b/superset-frontend/src/pages/ChartList/ChartList.permissions.test.tsx
index bb3445de615..d1730538e67 100644
--- a/superset-frontend/src/pages/ChartList/ChartList.permissions.test.tsx
+++ b/superset-frontend/src/pages/ChartList/ChartList.permissions.test.tsx
@@ -199,7 +199,7 @@ describe('ChartList - Permission-based UI Tests', () => {
expect(screen.getByTestId('bulk-select')).toBeInTheDocument();
// Verify Actions column is visible
- expect(screen.getByText('Actions')).toBeInTheDocument();
+ expect(screen.getByTitle('Actions')).toBeInTheDocument();
// Verify favorite stars are rendered for each chart
const favoriteStars = screen.getAllByTestId('fave-unfave-icon');
@@ -231,7 +231,7 @@ describe('ChartList - Permission-based UI Tests', () => {
await renderWithPermissions(PERMISSIONS.ADMIN);
await screen.findByTestId('chart-list-view');
- expect(screen.getByText('Actions')).toBeInTheDocument();
+ expect(screen.getByTitle('Actions')).toBeInTheDocument();
// Wait for table to load with charts data
await waitFor(() => {
@@ -264,7 +264,7 @@ describe('ChartList - Permission-based UI Tests', () => {
await renderWithPermissions(PERMISSIONS.WRITE_ONLY);
await screen.findByTestId('chart-list-view');
- expect(screen.getByText('Actions')).toBeInTheDocument();
+ expect(screen.getByTitle('Actions')).toBeInTheDocument();
// Wait for table to load with charts data
await waitFor(() => {
@@ -297,7 +297,7 @@ describe('ChartList - Permission-based UI Tests', () => {
await renderWithPermissions(PERMISSIONS.ADMIN, 1, { tagging: true });
await screen.findByTestId('chart-list-view');
- expect(screen.getByText('Tags')).toBeInTheDocument();
+ expect(screen.getByTitle('Tags')).toBeInTheDocument();
});
test('hides Tags column when TAGGING_SYSTEM feature flag is disabled', async () => {
@@ -311,7 +311,7 @@ describe('ChartList - Permission-based UI Tests', () => {
await renderWithPermissions(PERMISSIONS.READ_ONLY, 1, { tagging: true });
await screen.findByTestId('chart-list-view');
- expect(screen.getByText('Tags')).toBeInTheDocument();
+ expect(screen.getByTitle('Tags')).toBeInTheDocument();
});
test('shows bulk select button for users with admin permissions', async () => {
@@ -383,7 +383,7 @@ describe('ChartList - Permission-based UI Tests', () => {
await screen.findByTestId('chart-list-view');
// Actions column should be visible
- expect(screen.getByText('Actions')).toBeInTheDocument();
+ expect(screen.getByTitle('Actions')).toBeInTheDocument();
// Wait for table to load with charts data
await waitFor(() => {
@@ -418,7 +418,7 @@ describe('ChartList - Permission-based UI Tests', () => {
await screen.findByTestId('chart-list-view');
// Actions column should be visible (requires can_write)
- expect(screen.getByText('Actions')).toBeInTheDocument();
+ expect(screen.getByTitle('Actions')).toBeInTheDocument();
// Wait for table to load
await waitFor(() => {
@@ -441,7 +441,7 @@ describe('ChartList - Permission-based UI Tests', () => {
expect(favoriteStars).toHaveLength(mockCharts.length);
// Tags column should be visible (feature flag enabled)
- expect(screen.getByText('Tags')).toBeInTheDocument();
+ expect(screen.getByTitle('Tags')).toBeInTheDocument();
// Bulk select should be visible (user has can_export)
expect(screen.getByTestId('bulk-select')).toBeInTheDocument();
@@ -461,8 +461,8 @@ describe('ChartList - Permission-based UI Tests', () => {
await screen.findByTestId('chart-list-view');
// All permission-based elements should be hidden
- expect(screen.queryByText('Actions')).not.toBeInTheDocument();
- expect(screen.queryByText('Tags')).not.toBeInTheDocument();
+ expect(screen.queryByTitle('Actions')).not.toBeInTheDocument();
+ expect(screen.queryByTitle('Tags')).not.toBeInTheDocument();
expect(screen.queryByTestId('bulk-select')).not.toBeInTheDocument();
expect(
screen.queryByRole('button', { name: /chart/i }),
diff --git a/superset-frontend/src/pages/GroupsList/GroupsList.test.tsx b/superset-frontend/src/pages/GroupsList/GroupsList.test.tsx
index 78570133f92..abcfe984655 100644
--- a/superset-frontend/src/pages/GroupsList/GroupsList.test.tsx
+++ b/superset-frontend/src/pages/GroupsList/GroupsList.test.tsx
@@ -110,7 +110,7 @@ describe('GroupsList', () => {
test('renders actions column for admin', async () => {
await renderComponent();
- expect(screen.getByText('Actions')).toBeInTheDocument();
+ expect(screen.getAllByText('Actions')[0]).toBeInTheDocument();
});
test('renders the filters correctly', async () => {
@@ -128,9 +128,9 @@ describe('GroupsList', () => {
await renderComponent();
const table = screen.getByRole('table');
- expect(await within(table).findByText('Name')).toBeInTheDocument();
- expect(await within(table).findByText('Label')).toBeInTheDocument();
- expect(await within(table).findByText('Roles')).toBeInTheDocument();
+ expect(await within(table).findByTitle('Name')).toBeInTheDocument();
+ expect(await within(table).findByTitle('Label')).toBeInTheDocument();
+ expect(await within(table).findByTitle('Roles')).toBeInTheDocument();
});
test('opens add group modal on button click', async () => {
diff --git a/superset-frontend/src/pages/RolesList/RolesList.test.tsx b/superset-frontend/src/pages/RolesList/RolesList.test.tsx
index 92106fc1adb..6990fe8bda1 100644
--- a/superset-frontend/src/pages/RolesList/RolesList.test.tsx
+++ b/superset-frontend/src/pages/RolesList/RolesList.test.tsx
@@ -159,8 +159,8 @@ describe('RolesList', () => {
const table = screen.getByRole('table');
expect(table).toBeInTheDocument();
- const nameColumn = await within(table).findByText('Name');
- const actionsColumn = await within(table).findByText('Actions');
+ const nameColumn = await within(table).findByTitle('Name');
+ const actionsColumn = await within(table).findByTitle('Actions');
expect(nameColumn).toBeInTheDocument();
expect(actionsColumn).toBeInTheDocument();
diff --git a/superset-frontend/src/pages/RowLevelSecurityList/RowLevelSecurityList.test.tsx b/superset-frontend/src/pages/RowLevelSecurityList/RowLevelSecurityList.test.tsx
index bd6ae6fc320..fefb58cec19 100644
--- a/superset-frontend/src/pages/RowLevelSecurityList/RowLevelSecurityList.test.tsx
+++ b/superset-frontend/src/pages/RowLevelSecurityList/RowLevelSecurityList.test.tsx
@@ -174,12 +174,12 @@ describe('RuleList RTL', () => {
const table = screen.getByRole('table');
expect(table).toBeInTheDocument();
- const nameColumn = await within(table).findByText('Name');
- const filterTypeColumn = await within(table).findByText('Filter Type');
- const groupKeyColumn = await within(table).findByText('Group Key');
- const clauseColumn = await within(table).findByText('Clause');
- const modifiedColumn = await within(table).findByText('Last modified');
- const actionsColumn = await within(table).findByText('Actions');
+ const nameColumn = await within(table).findByTitle('Name');
+ const filterTypeColumn = await within(table).findByTitle('Filter Type');
+ const groupKeyColumn = await within(table).findByTitle('Group Key');
+ const clauseColumn = await within(table).findByTitle('Clause');
+ const modifiedColumn = await within(table).findByTitle('Last modified');
+ const actionsColumn = await within(table).findByTitle('Actions');
expect(nameColumn).toBeInTheDocument();
expect(filterTypeColumn).toBeInTheDocument();
diff --git a/superset-frontend/src/pages/UsersList/UsersList.test.tsx b/superset-frontend/src/pages/UsersList/UsersList.test.tsx
index 2f9d56c7926..5edc25b62d4 100644
--- a/superset-frontend/src/pages/UsersList/UsersList.test.tsx
+++ b/superset-frontend/src/pages/UsersList/UsersList.test.tsx
@@ -152,13 +152,13 @@ describe('UsersList', () => {
const table = screen.getByRole('table');
expect(table).toBeInTheDocument();
- const fnameColumn = await within(table).findByText('First name');
- const lnameColumn = await within(table).findByText('Last name');
- const usernameColumn = await within(table).findByText('Username');
- const emailColumn = await within(table).findByText('Email');
- const rolesColumn = await within(table).findByText('Roles');
- const actionsColumn = await within(table).findByText('Actions');
- const activeColumn = await within(table).findByText('Is active?');
+ const fnameColumn = await within(table).findByTitle('First name');
+ const lnameColumn = await within(table).findByTitle('Last name');
+ const usernameColumn = await within(table).findByTitle('Username');
+ const emailColumn = await within(table).findByTitle('Email');
+ const rolesColumn = await within(table).findByTitle('Roles');
+ const actionsColumn = await within(table).findByTitle('Actions');
+ const activeColumn = await within(table).findByTitle('Is active?');
expect(fnameColumn).toBeInTheDocument();
expect(lnameColumn).toBeInTheDocument();
diff --git a/superset-frontend/src/visualizations/TimeTable/TimeTable.test.tsx b/superset-frontend/src/visualizations/TimeTable/TimeTable.test.tsx
index c4f1d59fedf..b973e3b2fe0 100644
--- a/superset-frontend/src/visualizations/TimeTable/TimeTable.test.tsx
+++ b/superset-frontend/src/visualizations/TimeTable/TimeTable.test.tsx
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { render, screen } from '@superset-ui/core/spec';
+import { render, screen, within } from '@superset-ui/core/spec';
import TimeTable from './TimeTable';
const mockData = {
@@ -80,10 +80,25 @@ test('should render TimeTable component', () => {
test('should render table headers', () => {
render();
- expect(screen.getByText('Metric')).toBeInTheDocument();
- expect(screen.getByText('Time series columns')).toBeInTheDocument();
-});
+ const table = screen.getByRole('table');
+
+ const metricHeader = within(table).getByTitle('Metric');
+ expect(metricHeader).toBeInTheDocument();
+
+ const allTimeSeriesHeaders = within(table).getAllByText(
+ 'Time series columns',
+ );
+
+ const visibleTimeSeriesHeaders = allTimeSeriesHeaders.filter(
+ el => !el.closest('.ant-table-measure-cell-content'),
+ );
+
+ expect(visibleTimeSeriesHeaders.length).toBe(1);
+ visibleTimeSeriesHeaders.forEach(header => {
+ expect(header).toBeInTheDocument();
+ });
+});
test('should render table with data rows', () => {
render();
@@ -151,11 +166,23 @@ test('should render with multiple metrics', () => {
test('should handle column type sparkline correctly', () => {
render();
- const columnHeaders = screen.getAllByRole('columnheader');
+ const table = screen.getByRole('table');
+ expect(table).toBeInTheDocument();
- expect(screen.getByRole('table')).toBeInTheDocument();
+ const columnHeaders = screen.getAllByRole('columnheader');
expect(columnHeaders).toHaveLength(2);
- expect(screen.getByText('Time series columns')).toBeInTheDocument();
+
+ const allTimeSeriesElements = within(table).getAllByText(
+ 'Time series columns',
+ );
+ const visibleTimeSeriesColumns = allTimeSeriesElements.filter(
+ el => !el.closest('.ant-table-measure-cell-content'),
+ );
+
+ expect(visibleTimeSeriesColumns.length).toBeGreaterThan(0);
+ visibleTimeSeriesColumns.forEach(el => {
+ expect(el).toBeInTheDocument();
+ });
});
test('should not render empty table due to missing column id property', () => {