Compare commits

...

120 Commits

Author SHA1 Message Date
Enzo Martellucci
e3d5aeba2b refactor(components): Replace button, form, tag with antd5 components 2025-04-28 14:13:57 +02:00
Enzo Martellucci
a5fc4cad09 refactor(superset-ui-chart-controls): replace Ant Design imports with AntD v5 imports 2025-04-28 11:53:16 +02:00
Enzo Martellucci
50459ee3d5 Merge branch 'template_less' into enxdev/refactor/add-exports-to-components-index 2025-04-28 11:35:22 +02:00
Enzo Martellucci
b040d52c2d refactor(Components): Use named imports - P.2 (#33249) 2025-04-25 15:11:52 -07:00
Enzo Martellucci
2e44c0135e fix Cy test 2025-04-25 20:13:12 +02:00
Enzo Martellucci
5085e8973d Merge branch 'template_less' into enxdev/refactor/add-exports-to-components-index 2025-04-25 19:51:50 +02:00
Enzo Martellucci
944d1901cd clean up 2025-04-25 19:48:13 +02:00
Enzo Martellucci
a28ea4f82d refactor(Components): Use named imports 2025-04-25 18:53:22 +02:00
Mehmet Salih Yavuz
a7b7e6319c refactor(Table): Use Table instead of html <table> in CollectionTable (#33159) 2025-04-25 09:52:04 -07:00
Enzo Martellucci
c217f56aea refactor(components): Replace native HTML elements with Ant Design v5 components (#33090)
This PR introduces a refactor that replaces native HTML elements with their Ant Design v5 equivalents across the codebase. The goal is to ensure a more consistent UI, better accessibility, and improved maintainability by leveraging standardized components from Ant Design.
2025-04-25 09:48:12 +02:00
Mehmet Salih Yavuz
d43657ed90 fix: post merge 2025-04-24 20:57:53 +03:00
Mehmet Salih Yavuz
6c01173c21 Merge branch 'master' into template_less 2025-04-24 18:23:33 +03:00
Mehmet Salih Yavuz
4709eb0153 fix(Select): Merge conflicts and Select bug (#33165) 2025-04-24 17:00:00 +02:00
Maxime Beauchemin
51f719f8d4 set colorLink == colorPrimary 2025-04-16 13:17:00 -07:00
Damian Pendrak
50535a92a0 fix: upgrade tabs in database modal (#33038)
Co-authored-by: Maxime Beauchemin <maximebeauchemin@gmail.com>
Co-authored-by: Mehmet Salih Yavuz <salih.yavuz@proton.me>
2025-04-16 21:39:49 +03:00
Alexandru Soare
3fe5db1e72 refactor(Tabs): Tabs to use items instead of TabPanel (#33057)
Co-authored-by: Mehmet Salih Yavuz <salih.yavuz@proton.me>
2025-04-16 15:44:23 +03:00
Kamil Gabryjelski
08c9d28545 Fix changes in DatasourcePanelItem after merge 2025-04-16 14:04:40 +02:00
Mehmet Salih Yavuz
86d5537a7f Merge branch 'master' into template_less 2025-04-16 13:22:21 +03:00
Mehmet Salih Yavuz
18c3f0adb6 fix(theming): Fix various ci issues (#33147) 2025-04-16 12:46:57 +03:00
Mehmet Salih Yavuz
5e8cd7a6ee fix: provide id's 2025-04-16 00:05:04 +03:00
Maxime Beauchemin
dcea6c09ca fix imports 2025-04-15 13:30:45 -07:00
Maxime Beauchemin
dff7c1b50d post merge conflicts 2025-04-15 11:49:19 -07:00
Maxime Beauchemin
4f93c2d2e7 Merge branch 'master' into template_less 2025-04-15 11:45:12 -07:00
Maxime Beauchemin
334aa1a672 fix a unit test 2025-04-14 10:41:03 -07:00
Enzo Martellucci
2667a14678 Merge branch 'master' into template_less 2025-04-14 11:39:05 +02:00
Maxime Beauchemin
29ba5adf21 fix scripts/change_detector.py 2025-04-12 13:13:00 -07:00
Maxime Beauchemin
f18455cc2d fix a python build issue 2025-04-12 13:02:32 -07:00
Maxime Beauchemin
4123d25873 Merge branch 'master' into template_less 2025-04-12 12:57:53 -07:00
Geido
662f33b7de refactor(Components): Use named imports - P.1 (#33081) 2025-04-11 18:45:55 +03:00
Enzo Martellucci
c14dcecd8f refactor(breadcrumb): Upgrade Breadcrumb component from AntD v4 to AntD v5 (#32905) 2025-04-11 14:28:10 +03:00
Maxime Beauchemin
6a4730bbbe Merge branch 'master' into template_less 2025-04-09 12:50:09 -07:00
Pius Iniobong
739caa19cb refactor(Checkbox): Upgrade component to Ant Design v5 (#32980)
Co-authored-by: Diego Pucci <diegopucci.me@gmail.com>
2025-04-09 14:48:02 +03:00
Maxime Beauchemin
8bb02e2958 fix: use Input & TextArea instead of native <input> and <textarea> (#32989) 2025-04-08 11:33:12 -07:00
Maxime Beauchemin
7c2fd55104 restyling page header and buttons 2025-04-08 11:19:21 -07:00
Maxime Beauchemin
6c8e72b889 Fix theming in Explore west panel - Search Input 2025-04-08 11:19:21 -07:00
Mehmet Salih Yavuz
3b198ab656 refactor(Table): Use our custom Table component in CRUD views (#32964)
Co-authored-by: Geido <60598000+geido@users.noreply.github.com>
2025-04-08 19:05:58 +03:00
Maxime Beauchemin
c993abe58c Merge branch 'master' into template_less 2025-04-08 08:33:41 -07:00
Maxime Beauchemin
a9bc4655a4 use colorBorder in AddSliceCard 2025-04-08 08:33:09 -07:00
Mehmet Salih Yavuz
b835478514 refactor(Select): Migrate Select component to Ant Design 5 (#32514)
Co-authored-by: Maxime Beauchemin <maximebeauchemin@gmail.com>
Co-authored-by: Diego Pucci <diegopucci.me@gmail.com>
2025-04-08 15:14:15 +03:00
Damian Pendrak
3950cf065e refactor(Collapse): Upgrade Collapse to Antd5 (#32959)
Co-authored-by: Maxime Beauchemin <maximebeauchemin@gmail.com>
2025-04-07 14:35:39 -07:00
Maxime Beauchemin
c7d2881d04 Merge branch 'master' into template_less 2025-04-07 13:02:10 -07:00
Alexandru Soare
33febb669e refactor(Tabs): Upgrade Tabs to Antd5 (#32810)
Co-authored-by: Maxime Beauchemin <maximebeauchemin@gmail.com>
2025-04-07 18:01:37 +03:00
Enzo Martellucci
6254db34cd refactor(Components): Create wrappers for Ant Design 5 direct exports (#32705)
Co-authored-by: Diego Pucci <diegopucci.me@gmail.com>
Co-authored-by: Geido <60598000+geido@users.noreply.github.com>
Co-authored-by: Mehmet Salih Yavuz <salih.yavuz@proton.me>
2025-04-07 14:48:18 +03:00
Maxime Beauchemin
e6df194201 fix merge-related issues 2025-04-02 18:01:05 -07:00
Maxime Beauchemin
2580a8ba78 Merge branch 'master' into template_less 2025-04-02 17:59:35 -07:00
Maxime Beauchemin
6b58ef155e feat: Theme to include brand logo configuration 2025-04-02 17:55:50 -07:00
Mehmet Salih Yavuz
6f73e58b25 refactor(theming): Fixes to previously migrated components (#32845) 2025-04-02 17:55:12 -07:00
Maxime Beauchemin
bc85a118ba fix some type issues 2025-04-02 13:21:42 -07:00
Maxime Beauchemin
70a5925b03 theming the viz picker 2025-04-02 10:26:43 -07:00
Maxime Beauchemin
d266835820 remove console.log 2025-04-01 13:59:58 -07:00
Maxime Beauchemin
952658ee63 fix links and bring GlobalStyles to superset-ui/core 2025-04-01 13:54:08 -07:00
Maxime Beauchemin
27d723fba1 force colorLink in GlobalStyles 2025-04-01 13:03:23 -07:00
Mehmet Salih Yavuz
971715931b refactor(form): Migrate Form component to Ant Design 5 (#32729) 2025-04-01 09:29:17 -07:00
Maxime Beauchemin
e3342bb731 Merge branch 'master' into template_less 2025-04-01 00:45:31 -07:00
Maxime Beauchemin
506c8387fc lint 2025-03-29 15:32:02 -07:00
Maxime Beauchemin
9dedb588ba altering comment for THEME_OVERRIDES in superset/config.py 2025-03-29 15:30:35 -07:00
Maxime Beauchemin
8b69958f19 centralize theming stuff in AsyncAceEditor 2025-03-29 15:14:00 -07:00
Maxime Beauchemin
1dd8a76113 set Global body bg color to colorBgBase 2025-03-28 17:40:30 -07:00
Maxime Beauchemin
cde1da6285 Merge branch 'master' into template_less 2025-03-28 17:24:20 -07:00
Enzo Martellucci
3665ebcb4b refactor(table): Upgrade table component from antd4 to antd5 (#32378)
Co-authored-by: Diego Pucci <diegopucci.me@gmail.com>
Co-authored-by: Mehmet Salih Yavuz <salih.yavuz@proton.me>
2025-03-28 15:11:25 -07:00
Maxime Beauchemin
c31d70dd12 one minor edit to fix a cypress test 2025-03-27 16:07:53 -07:00
Maxime Beauchemin
f217865435 oopsy daisy 2025-03-27 15:16:25 -07:00
Maxime Beauchemin
501874980e fix cypress test 2025-03-27 14:30:46 -07:00
Maxime Beauchemin
e7b2b586b6 Merge branch 'master' into template_less 2025-03-27 13:59:33 -07:00
Maxime Beauchemin
6a15aaf562 ThemeEditor 2025-03-27 13:48:45 -07:00
Maxime Beauchemin
f5b680699f ThemeEditor 2025-03-27 12:17:06 -07:00
Maxime Beauchemin
3c289a927d a theme editor 2025-03-26 22:57:47 -07:00
Maxime Beauchemin
5805f242d0 minor color tweak 2025-03-26 21:54:22 -07:00
Maxime Beauchemin
4f0a4454ec styling the AsyncAceEditor 2025-03-26 21:51:30 -07:00
Maxime Beauchemin
d752b0f06a fixing themes in Storybook 2025-03-26 18:51:20 -07:00
Maxime Beauchemin
06d737ec9f add theming support to Storybook 2025-03-26 18:02:49 -07:00
Maxime Beauchemin
04729794c8 fix test in dashboard/actions.test.js 2025-03-25 20:56:18 -07:00
Maxime Beauchemin
68ea9ac4d0 fix ErrorMessageWithStackTrace 2025-03-25 20:38:29 -07:00
Maxime Beauchemin
1afa4971d1 fix SavedQueries test 2025-03-25 20:28:44 -07:00
Maxime Beauchemin
5418f09864 fix test for BasicErrorAlert.tsx 2025-03-25 20:24:56 -07:00
Maxime Beauchemin
aabeefb761 fix types in Gauge/transformProps.test.ts 2025-03-25 20:17:59 -07:00
Maxime Beauchemin
023c7da07b fix another test 2025-03-25 20:02:14 -07:00
Maxime Beauchemin
7af32d4c70 fix theme test 2025-03-25 19:43:23 -07:00
Maxime Beauchemin
58724b1c5c fix test 2025-03-25 19:39:38 -07:00
Maxime Beauchemin
f9494128bc fix some tests 2025-03-25 19:15:22 -07:00
Maxime Beauchemin
cebff5e726 improve styles in HighlightedSql 2025-03-25 12:54:38 -07:00
Maxime Beauchemin
bcb6da18ef Merge branch 'master' into template_less 2025-03-25 12:03:09 -07:00
Maxime Beauchemin
344c8f5c37 theming react-code-highlighter or whatev it's called 2025-03-25 09:44:17 -07:00
Maxime Beauchemin
d3f450fca0 fix theme for ag-grid in SQL LAB 2025-03-24 19:24:03 -07:00
Maxime Beauchemin
11a29b1610 fix Collapse color content 2025-03-24 17:17:13 -07:00
Maxime Beauchemin
54d67b679b fix table viz colors 2025-03-24 17:12:30 -07:00
Maxime Beauchemin
64c480a8f1 aligning some colors tokens 2025-03-24 17:01:41 -07:00
Maxime Beauchemin
cf6816064d going vanila on tooltips 2025-03-24 16:41:50 -07:00
Maxime Beauchemin
59e402ac68 fix icon on Explore's cached label 2025-03-24 13:57:41 -07:00
Maxime Beauchemin
5042248ed7 Fix table row separator in TableCollection 2025-03-24 13:26:06 -07:00
Maxime Beauchemin
56e3d165dd theme toast icons and various cosmetic updates 2025-03-24 12:54:01 -07:00
Maxime Beauchemin
8ce144983d fix CopyOutlined icon in dataset view to align with sizing 2025-03-24 10:44:07 -07:00
Maxime Beauchemin
4afbfd11e0 Merge branch 'master' into template_less 2025-03-24 09:18:21 -07:00
Maxime Beauchemin
31eb10590e touchups 2025-03-24 09:17:45 -07:00
Maxime Beauchemin
358633e98d some theming fixes 2025-03-20 08:40:15 -07:00
Maxime Beauchemin
b7bc1113ac Merge branch 'master' into template_less 2025-03-20 07:33:55 -07:00
Maxime Beauchemin
11bc4965e3 echarts theming 2025-03-19 20:09:00 -07:00
Maxime Beauchemin
1ca0f34210 more button background 2025-03-19 17:02:57 -07:00
Maxime Beauchemin
9cb6c3b039 color 2025-03-19 16:57:58 -07:00
Maxime Beauchemin
b9be692e55 fix MoreOutlined button background 2025-03-19 16:28:10 -07:00
Maxime Beauchemin
e0d86df5a5 merging 2025-03-19 12:58:37 -07:00
Maxime Beauchemin
2f80ebb3e8 minor progress 2025-03-18 19:02:59 -07:00
Maxime Beauchemin
83f47d3ca1 fixing vizes 2025-03-18 18:38:21 -07:00
Maxime Beauchemin
48df49d89c fix BIG number 2025-03-18 18:32:54 -07:00
Maxime Beauchemin
f16600ee86 fix sankey theme 2025-03-18 18:32:43 -07:00
Maxime Beauchemin
7dbe05f6d8 fix treemap 2025-03-18 18:32:35 -07:00
Maxime Beauchemin
2d461deb68 echarts 2025-03-18 18:32:26 -07:00
Maxime Beauchemin
dbc7db981c rebased 2025-03-17 14:02:25 -07:00
Maxime Beauchemin
5faf0189e8 switch primary color, keep old hex as comment in case we want to rollback 2025-03-17 13:55:17 -07:00
Maxime Beauchemin
4b55a928c9 set DARK ff to false 2025-03-17 13:55:16 -07:00
Maxime Beauchemin
d15c6d361b fixing storybook 2025-03-17 13:55:16 -07:00
Maxime Beauchemin
6b5d53ad39 fix dashboard left panel background-color 2025-03-17 13:55:16 -07:00
Maxime Beauchemin
b575aa6aac make secondary filled/primary 2025-03-17 13:55:16 -07:00
Maxime Beauchemin
d131c29f3b fix a few buttons 2025-03-17 13:55:16 -07:00
Maxime Beauchemin
22cbec1d95 ts-ignore 2025-03-17 13:55:16 -07:00
Maxime Beauchemin
b2b7b899a3 fix unit test 2025-03-17 13:55:16 -07:00
Maxime Beauchemin
ac81eefe3f fix types/tests 2025-03-17 13:55:16 -07:00
Maxime Beauchemin
8d361205f6 adjusting some buttons 2025-03-17 13:55:15 -07:00
Maxime Beauchemin
352aa36823 make Cancel secondary 2025-03-17 13:55:15 -07:00
Maxime Beauchemin
336763f0c9 feat: messing with the theme 2025-03-17 13:55:12 -07:00
959 changed files with 26106 additions and 14220 deletions

1
.gitattributes vendored
View File

@@ -1,3 +1,4 @@
docker/**/*.sh text eol=lf
*.svg binary
*.ipynb binary
*.geojson binary

View File

@@ -26,6 +26,7 @@ assists people when migrating to a new version.
- [33116](https://github.com/apache/superset/pull/33116) In Echarts Series charts (e.g. Line, Area, Bar, etc.) charts, the `x_axis_sort_series` and `x_axis_sort_series_ascending` form data items have been renamed with `x_axis_sort` and `x_axis_sort_asc`.
There's a migration added that can potentially affect a significant number of existing charts.
- [32317](https://github.com/apache/superset/pull/32317) The horizontal filter bar feature is now out of testing/beta development and its feature flag `HORIZONTAL_FILTER_BAR` has been removed.
- [31590](https://github.com/apache/superset/pull/31590) Marks the begining of intricate work around supporting dynamic Theming, and breaks support for [THEME_OVERRIDES](https://github.com/apache/superset/blob/732de4ac7fae88e29b7f123b6cbb2d7cd411b0e4/superset/config.py#L671) in favor of a new theming system based on AntD V5. Likely this will be in disrepair until settling over the 5.x lifecycle.
- [31976](https://github.com/apache/superset/pull/31976) Removed the `DISABLE_LEGACY_DATASOURCE_EDITOR` feature flag. The previous value of the feature flag was `True` and now the feature is permanently removed.
- [31959](https://github.com/apache/superset/pull/32000) Removes CSV_UPLOAD_MAX_SIZE config, use your web server to control file upload size.
- [31959](https://github.com/apache/superset/pull/31959) Removes the following endpoints from data uploads: `/api/v1/database/<id>/<file type>_upload` and `/api/v1/database/<file type>_metadata`, in favour of new one (Details on the PR). And simplifies permissions.

View File

@@ -373,7 +373,7 @@ module.exports = {
'fixtures.*',
'cypress-base/cypress/**/*',
'Stories.tsx',
'packages/superset-ui-core/src/style/index.tsx',
'packages/superset-ui-core/src/theme/index.tsx',
],
rules: {
'theme-colors/no-literal-colors': 0,

View File

@@ -26,5 +26,7 @@ CHANGELOG/
*-topo.json
storybook-static/
*.snap
**/*.less
**/*.less.hbs
/.nx/workspace-data

View File

@@ -17,31 +17,75 @@
* under the License.
*/
import { withJsx } from '@mihkeleidast/storybook-addon-source';
import { supersetTheme, ThemeProvider } from '@superset-ui/core';
import { AntdThemeProvider } from '../src/components/AntdThemeProvider';
import { themeObject, css, exampleThemes } from '@superset-ui/core';
import { combineReducers, createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import reducerIndex from 'spec/helpers/reducerIndex';
import { GlobalStyles } from '../src/GlobalStyles';
import { Global } from '@emotion/react';
import { App, Layout, Space, Content } from 'antd-v5';
import 'src/theme.ts';
import './storybook.css';
export const GlobalStylesOverrides = () => (
<Global
styles={css`
html,
body,
#storybook-root {
margin: 0 !important;
padding: 0 !important;
min-height: 100vh !important;
}
.antd5-app {
min-height: 100vh !important;
}
`}
/>
);
const store = createStore(
combineReducers(reducerIndex),
{},
compose(applyMiddleware(thunk)),
);
const themeDecorator = Story => (
<ThemeProvider theme={supersetTheme}>
<AntdThemeProvider>
<GlobalStyles />
<Story />
</AntdThemeProvider>
</ThemeProvider>
);
export const globalTypes = {
theme: {
name: 'Theme',
description: 'Global theme for components',
defaultValue: 'superset',
toolbar: {
icon: 'paintbrush',
items: Object.keys(exampleThemes),
},
},
};
const themeDecorator = (Story, context) => {
const themeKey = context.globals.theme || 'superset';
themeObject.setConfig(exampleThemes[themeKey]);
return (
<themeObject.SupersetThemeProvider>
<App>
<GlobalStylesOverrides />
<Layout
style={{
minHeight: '100vh',
width: '100%',
padding: 24,
backgroundColor: themeObject.theme.colorBgBase,
}}
>
<Story {...context} />
</Layout>
</App>
</themeObject.SupersetThemeProvider>
);
};
const providerDecorator = Story => (
<Provider store={store}>
@@ -81,5 +125,5 @@ export const parameters = {
],
},
},
controls: { expanded: true, sort: 'alpha' },
controls: { expanded: true, sort: 'alpha', disableSaveFromUI: true },
};

View File

@@ -1,3 +1,2 @@
body {
background: transparent;
}

View File

@@ -84,9 +84,9 @@ describe('Charts list', () => {
it.only('should show the newly added dashboards in a tooltip', () => {
interceptDashboardGet();
visitSampleChartFromList('1 - Sample chart');
saveChartToDashboard('1 - Sample dashboard');
saveChartToDashboard('2 - Sample dashboard');
saveChartToDashboard('3 - Sample dashboard');
saveChartToDashboard('1 - Sample chart', '1 - Sample dashboard');
saveChartToDashboard('1 - Sample chart', '2 - Sample dashboard');
saveChartToDashboard('1 - Sample chart', '3 - Sample dashboard');
visitChartList();
cy.getBySel('count-crosslinks').should('be.visible');
@@ -122,8 +122,8 @@ describe('Charts list', () => {
it('should bulk select in list mode', () => {
toggleBulkSelect();
cy.get('#header-toggle-all').click();
cy.get('[aria-label="checkbox-on"]').should('have.length', 26);
cy.getBySel('header-toggle-all').click();
cy.get('input[type="checkbox"]:checked').should('have.length', 26);
cy.getBySel('bulk-select-copy').contains('25 Selected');
cy.getBySel('bulk-select-action')
.should('have.length', 2)
@@ -132,7 +132,7 @@ describe('Charts list', () => {
expect($btns).to.contain('Export');
});
cy.getBySel('bulk-select-deselect-all').click();
cy.get('[aria-label="checkbox-on"]').should('have.length', 0);
cy.get('input[type="checkbox"]:checked').should('have.length', 0);
cy.getBySel('bulk-select-copy').contains('0 Selected');
cy.getBySel('bulk-select-action').should('not.exist');
});

View File

@@ -62,6 +62,6 @@ describe('Dashboard actions', () => {
// Verify the color of the outlined star (gray)
cy.get('@starIconOutlinedAfter')
.should('have.css', 'color')
.and('eq', 'rgb(178, 178, 178)');
.and('eq', 'rgb(133, 133, 133)');
});
});

View File

@@ -130,7 +130,7 @@ const testEchart = (
.should('contain', chartName);
cy.get('@drillByModal')
.find('.ant-breadcrumb')
.find('.antd5-breadcrumb')
.should('be.visible')
.and('contain', 'gender (boy)')
.and('contain', '/')
@@ -185,7 +185,7 @@ const testEchart = (
// undo - back to drill by state
interceptV1ChartData('drillByUndo');
cy.get('@drillByModal')
.find('.ant-breadcrumb')
.find('.antd5-breadcrumb')
.should('be.visible')
.and('contain', 'gender (boy)')
.and('contain', '/')
@@ -210,7 +210,7 @@ const testEchart = (
});
cy.get('@drillByModal')
.find('.ant-breadcrumb')
.find('.antd5-breadcrumb')
.should('be.visible')
.and('contain', 'gender (boy)')
.and('contain', '/')
@@ -269,7 +269,7 @@ describe('Drill by modal', () => {
.should('be.visible');
cy.get('@drillByModal')
.find('.ant-breadcrumb')
.find('.antd5-breadcrumb')
.should('be.visible')
.and('contain', 'gender (boy)')
.and('contain', '/')
@@ -318,7 +318,7 @@ describe('Drill by modal', () => {
interceptV1ChartData('drillByUndo');
interceptFormDataKey();
cy.get('@drillByModal')
.find('.ant-breadcrumb')
.find('.antd5-breadcrumb')
.should('be.visible')
.and('contain', 'gender (boy)')
.and('contain', '/')
@@ -350,7 +350,7 @@ describe('Drill by modal', () => {
.and('contain', 'sum__num');
cy.get('@drillByModal')
.find('.ant-breadcrumb')
.find('.antd5-breadcrumb')
.should('be.visible')
.and('contain', 'gender (boy)')
.and('contain', '/')
@@ -414,7 +414,7 @@ describe('Drill by modal', () => {
.should('contain', 'Drill by: Pivot Table');
cy.get('@drillByModal')
.find('.ant-breadcrumb')
.find('.antd5-breadcrumb')
.should('be.visible')
.and('contain', 'gender (boy)')
.and('contain', '/')
@@ -465,7 +465,7 @@ describe('Drill by modal', () => {
interceptV1ChartData('drillByUndo');
cy.get('@drillByModal')
.find('.ant-breadcrumb')
.find('.antd5-breadcrumb')
.should('be.visible')
.and('contain', 'gender (boy)')
.and('contain', '/')
@@ -499,7 +499,7 @@ describe('Drill by modal', () => {
.and('contain', 'sum__num');
cy.get('@drillByModal')
.find('.ant-breadcrumb')
.find('.antd5-breadcrumb')
.should('be.visible')
.and('contain', 'gender (boy)')
.and('contain', '/')
@@ -636,7 +636,7 @@ describe('Drill by modal', () => {
.should('contain', 'Mixed Chart');
cy.get('@drillByModal')
.find('.ant-breadcrumb')
.find('.antd5-breadcrumb')
.should('be.visible')
.and('contain', 'gender (boy)')
.and('contain', '/')
@@ -672,7 +672,7 @@ describe('Drill by modal', () => {
// undo - back to drill by state
interceptV1ChartData('drillByUndo');
cy.get('@drillByModal')
.find('.ant-breadcrumb')
.find('.antd5-breadcrumb')
.should('be.visible')
.and('contain', 'gender (boy)')
.and('contain', '/')
@@ -692,7 +692,7 @@ describe('Drill by modal', () => {
});
cy.get('@drillByModal')
.find('.ant-breadcrumb')
.find('.antd5-breadcrumb')
.should('be.visible')
.and('contain', 'gender (boy)')
.and('contain', '/')

View File

@@ -146,7 +146,7 @@ describe('Drill to detail modal', () => {
it('refreshes the data', () => {
openModalFromMenu('big_number_total');
// move to the last page
cy.get('.ant-pagination-item').eq(5).click();
cy.get('.antd5-pagination-item').eq(5).click();
// skips error on pagination
cy.on('uncaught:exception', () => false);
cy.wait('@samples');
@@ -154,7 +154,7 @@ describe('Drill to detail modal', () => {
cy.get("[aria-label='Reload']").click();
cy.wait('@samples');
// make sure it started back from first page
cy.get('.ant-pagination-item-active').should('contain', '1');
cy.get('.antd5-pagination-item-active').should('contain', '1');
});
it('paginates', () => {
@@ -165,13 +165,13 @@ describe('Drill to detail modal', () => {
expect($rows).to.contain('Amy');
});
// checking the paginated data
cy.get('.ant-pagination-item')
cy.get('.antd5-pagination-item')
.should('have.length', 6)
.should($pages => {
expect($pages).to.contain('1');
expect($pages).to.contain('1514');
});
cy.get('.ant-pagination-item').eq(4).click();
cy.get('.antd5-pagination-item').eq(4).click();
// skips error on pagination
cy.on('uncaught:exception', () => false);
cy.wait('@samples');
@@ -184,7 +184,7 @@ describe('Drill to detail modal', () => {
cy.get('.virtual-grid').contains('Juan').should('not.be.visible');
cy.get('.ant-pagination-item').eq(0).click();
cy.get('.antd5-pagination-item').eq(0).click();
cy.get('.virtual-grid').contains('Aaron').should('be.visible');
});
@@ -455,7 +455,7 @@ describe('Drill to detail modal', () => {
// checking the filter
cy.getBySel('filter-val').should('contain', 'boy');
cy.getBySel('row-count-label').should('contain', '39.2k rows');
cy.get('.ant-pagination-item')
cy.get('.antd5-pagination-item')
.should('have.length', 6)
.then($pages => {
expect($pages).to.contain('1');
@@ -466,8 +466,8 @@ describe('Drill to detail modal', () => {
cy.getBySel('filter-col').find("[aria-label='close']").click();
cy.wait('@samples');
cy.getBySel('row-count-label').should('contain', '75.7k rows');
cy.get('.ant-pagination-item-active').should('contain', '1');
cy.get('.ant-pagination-item')
cy.get('.antd5-pagination-item-active').should('contain', '1');
cy.get('.antd5-pagination-item')
.should('have.length', 6)
.then($pages => {
expect($pages).to.contain('1');

View File

@@ -17,7 +17,12 @@
* under the License.
*/
import { SAMPLE_DASHBOARD_1, TABBED_DASHBOARD } from 'cypress/utils/urls';
import { drag, resize, waitForChartLoad } from 'cypress/utils';
import {
drag,
resize,
setSelectSearchInput,
waitForChartLoad,
} from 'cypress/utils';
import { edit } from 'brace';
import {
interceptExploreUpdate,
@@ -34,21 +39,12 @@ function editDashboard() {
cy.getBySel('edit-dashboard-button').click();
}
function closeModal() {
cy.getBySel('properties-modal-cancel-button').click({ force: true });
}
function openProperties() {
cy.get('body').then($body => {
if ($body.find('[data-test="properties-modal-cancel-button"]').length) {
closeModal();
}
cy.getBySel('actions-trigger').click({ force: true });
cy.getBySel('header-actions-menu')
.contains('Edit properties')
.click({ force: true });
cy.get('.antd5-modal-body').should('be.visible');
});
cy.getBySel('actions-trigger').click({ force: true });
cy.getBySel('header-actions-menu')
.contains('Edit properties')
.click({ force: true });
cy.get('.antd5-modal-body').should('be.visible');
}
function assertMetadata(text: string) {
@@ -150,12 +146,10 @@ function selectColorScheme(
target = 'dashboard-edit-properties-form',
) {
cy.get(`[data-test="${target}"] input[aria-label="Select color scheme"]`)
.first()
.should('exist')
.then($input => {
cy.wrap($input).click({ force: true });
cy.wrap($input).type(color.slice(0, 5), { force: true });
setSelectSearchInput($input, color.slice(0, 5));
});
cy.getBySel(color).click({ force: true });
}
function saveAndGo(dashboard = 'Tabbed Dashboard') {
@@ -163,7 +157,7 @@ function saveAndGo(dashboard = 'Tabbed Dashboard') {
cy.getBySel('query-save-button').click();
cy.getBySel('save-modal-body').then($modal => {
cy.wrap($modal)
.find("div[aria-label='Select a dashboard'] .ant-select-selection-item")
.find("div[aria-label='Select a dashboard'] .antd5-select-selection-item")
.should('have.text', dashboard);
cy.getBySel('save-overwrite-radio').should('not.be.disabled');
cy.getBySel('save-overwrite-radio').click();
@@ -1130,7 +1124,7 @@ describe('Dashboard edit', () => {
it('should filter charts', () => {
interceptCharts();
cy.get('[role="checkbox"]').click();
cy.get('input[type="checkbox"]').click();
cy.getBySel('dashboard-charts-filter-search-input').type('Unicode');
cy.wait('@filtering');
cy.getBySel('chart-card')
@@ -1140,8 +1134,8 @@ describe('Dashboard edit', () => {
});
// TODO fix this test! This was the #1 flaky test as of 4/21/23 according to cypress dashboard.
xit('should disable the Save button when undoing', () => {
cy.get('[role="checkbox"]').click();
it.skip('should disable the Save button when undoing', () => {
cy.get('input[type="checkbox"]').click();
dragComponent('Unicode Cloud', 'card-title', false);
cy.getBySel('header-save-button').should('be.enabled');
discardChanges();
@@ -1155,13 +1149,13 @@ describe('Dashboard edit', () => {
});
it('should add charts', () => {
cy.get('[role="checkbox"]').click();
cy.get('input[type="checkbox"]').click();
dragComponent();
cy.getBySel('dashboard-component-chart-holder').should('have.length', 1);
});
it.skip('should remove added charts', () => {
cy.get('[role="checkbox"]').click();
cy.get('input[type="checkbox"]').click();
dragComponent('Unicode Cloud');
cy.getBySel('dashboard-component-chart-holder').should('have.length', 1);
cy.getBySel('dashboard-delete-component-button').click();
@@ -1204,7 +1198,7 @@ describe('Dashboard edit', () => {
});
it('should save', () => {
cy.get('[role="checkbox"]').click();
cy.get('input[type="checkbox"]').click();
dragComponent();
cy.getBySel('header-save-button').should('be.enabled');
saveChanges();

View File

@@ -157,11 +157,11 @@ describe('Horizontal FilterBar', () => {
cy.get('.filter-item-wrapper').should('have.length', 3);
openMoreFilters();
cy.getBySel('form-item-value').should('have.length', 12);
cy.getBySel('filter-control-name').contains('test_10').should('be.visible');
cy.getBySel('filter-control-name').contains('test_9').should('be.visible');
cy.getBySel('filter-control-name')
.contains('test_12')
.should('not.be.visible');
cy.get('.antd5-popover-inner').scrollTo('bottom');
cy.getBySel('filter-control-name').contains('test_12').scrollIntoView();
cy.getBySel('filter-control-name').contains('test_12').should('be.visible');
});
@@ -228,7 +228,7 @@ describe('Horizontal FilterBar', () => {
});
cy.getBySel('filter-status-popover').contains('test_9').click();
cy.getBySel('dropdown-content').should('be.visible');
cy.get('.ant-select-focused').should('be.visible');
cy.get('.antd5-select-focused').should('be.visible');
});
it.skip('should show tag count and one plain tag on focus and only count on blur in select ', () => {
@@ -238,13 +238,13 @@ describe('Horizontal FilterBar', () => {
setFilterBarOrientation('horizontal');
enterNativeFilterEditModal();
inputNativeFilterDefaultValue('Albania');
cy.get('.ant-select-selection-search-input').clear({ force: true });
cy.get('.antd5-select-selection-search-input').clear({ force: true });
inputNativeFilterDefaultValue('Algeria', true);
saveNativeFilterSettings([SAMPLE_CHART]);
cy.getBySel('filter-bar').within(() => {
cy.get(nativeFilters.filterItem).contains('Albania').should('be.visible');
cy.get(nativeFilters.filterItem).contains('+ 1 ...').should('be.visible');
cy.get('.ant-select-selection-search-input').click();
cy.get('.antd5-select-selection-search-input').click();
cy.get(nativeFilters.filterItem).contains('+ 2 ...').should('be.visible');
});
});

View File

@@ -199,14 +199,16 @@ describe('Native filters', () => {
.should('be.visible')
.click();
cy.get('[data-test="range-filter-from-input"]').type('{selectall}5');
cy.get('[data-test="range-filter-from-input"]').type('{selectall}40');
cy.get('[data-test="range-filter-to-input"]')
.should('be.visible')
.click();
cy.get('[data-test="range-filter-to-input"]').type('{selectall}50');
cy.get(nativeFilters.applyFilter).click();
cy.get(nativeFilters.applyFilter).click({
force: true,
});
// Assert that the URL contains 'native_filters'
cy.url().then(u => {
@@ -215,7 +217,7 @@ describe('Native filters', () => {
cy.get('[data-test="range-filter-from-input"]')
.invoke('val')
.should('equal', '5');
.should('equal', '40');
// Assert that the "To" input has the correct value
cy.get('[data-test="range-filter-to-input"]')

View File

@@ -293,7 +293,9 @@ describe('Native filters', () => {
it('Verify setting options and tooltips for value filter', () => {
enterNativeFilterEditModal(false);
cy.contains('Filter value is required').should('be.visible').click();
cy.contains('Filter value is required').should('be.visible').click({
force: true,
});
checkNativeFilterTooltip(0, nativeFilterTooltips.preFilter);
checkNativeFilterTooltip(1, nativeFilterTooltips.defaultValue);
cy.get(nativeFilters.modal.container).should('be.visible');

View File

@@ -33,7 +33,7 @@ const TABLE = { name: 'Names Sorted by Num in California', viz: 'table' };
function topLevelTabs() {
cy.getBySel('dashboard-component-tabs')
.first()
.find('[data-test="nav-list"] .ant-tabs-nav-list > .ant-tabs-tab')
.find('[data-test="nav-list"] .antd5-tabs-nav-list > .antd5-tabs-tab')
.as('top-level-tabs');
}
@@ -60,20 +60,20 @@ describe('Dashboard tabs', () => {
cy.get('@top-level-tabs').first().click();
cy.get('@top-level-tabs')
.first()
.should('have.class', 'ant-tabs-tab-active');
.should('have.class', 'antd5-tabs-tab-active');
cy.get('@top-level-tabs')
.last()
.should('not.have.class', 'ant-tabs-tab-active');
.should('not.have.class', 'antd5-tabs-tab-active');
cy.get('[data-test-chart-name="Box plot"]').should('not.exist');
cy.get('[data-test-chart-name="Trends"]').should('not.exist');
cy.get('@top-level-tabs').last().click();
cy.get('@top-level-tabs')
.last()
.should('have.class', 'ant-tabs-tab-active');
.should('have.class', 'antd5-tabs-tab-active');
cy.get('@top-level-tabs')
.first()
.should('not.have.class', 'ant-tabs-tab-active');
.should('not.have.class', 'antd5-tabs-tab-active');
waitForChartLoad(BOX_PLOT);
cy.get('[data-test-chart-name="Box plot"]').should('exist');
@@ -83,7 +83,7 @@ describe('Dashboard tabs', () => {
// click row level tab, see 1 more chart
cy.getBySel('dashboard-component-tabs')
.eq(2)
.find('[data-test="nav-list"] .ant-tabs-nav-list > .ant-tabs-tab')
.find('[data-test="nav-list"] .antd5-tabs-nav-list > .antd5-tabs-tab')
.as('row-level-tabs');
cy.get('@row-level-tabs').last().click();
@@ -118,7 +118,7 @@ describe('Dashboard tabs', () => {
cy.intercept('**/superset/explore_json/?*').as('legacyChartData');
// click row level tab, send 1 more query
cy.get('.ant-tabs-tab').contains('row tab 2').click();
cy.get('.antd5-tabs-tab').contains('row tab 2').click();
cy.wait('@legacyChartData').then(({ request }) => {
const requestBody = parsePostForm(request.body);
@@ -134,7 +134,7 @@ describe('Dashboard tabs', () => {
cy.intercept('POST', '**/api/v1/chart/data?*').as('v1ChartData');
// click top level tab, send 1 more query
cy.get('.ant-tabs-tab').contains('Tab B').click();
cy.get('.antd5-tabs-tab').contains('Tab B').click();
cy.wait('@v1ChartData').then(({ request }) => {
expect(request.body.queries[0].filters[0]).deep.eq({
@@ -146,8 +146,8 @@ describe('Dashboard tabs', () => {
getChartAliasBySpec(BOX_PLOT).then(boxPlotAlias => {
// navigate to filter and clear filter
cy.get('.ant-tabs-tab').contains('Tab A').click();
cy.get('.ant-tabs-tab').contains('row tab 1').click();
cy.get('.antd5-tabs-tab').contains('Tab A').click();
cy.get('.antd5-tabs-tab').contains('row tab 1').click();
cy.get('.Select__clear-indicator').click();
cy.get('.filter button:not(:disabled)').contains('Apply').click();
@@ -170,7 +170,7 @@ describe('Dashboard tabs', () => {
cy.get('@top-level-tabs').last().click();
cy.get('@top-level-tabs')
.last()
.should('have.class', 'ant-tabs-tab-active');
.should('have.class', 'antd5-tabs-tab-active');
expandFilterOnLeftPanel();
@@ -179,7 +179,7 @@ describe('Dashboard tabs', () => {
cy.get('@top-level-tabs').first().click();
cy.get('@top-level-tabs')
.first()
.should('have.class', 'ant-tabs-tab-active');
.should('have.class', 'antd5-tabs-tab-active');
cy.wait(1000);

View File

@@ -18,7 +18,11 @@
*/
import { dashboardView, nativeFilters } from 'cypress/support/directories';
import { ChartSpec, waitForChartLoad } from 'cypress/utils';
import {
ChartSpec,
setSelectSearchInput,
waitForChartLoad,
} from 'cypress/utils';
export const WORLD_HEALTH_CHARTS = [
{ name: '% Rural', viz: 'world_map' },
@@ -264,10 +268,11 @@ export function fillNativeFilterForm(
dataset?: string,
filterColumn?: string,
) {
cy.get(nativeFilters.filtersPanel.filterTypeInput)
.find(nativeFilters.filtersPanel.filterTypeItem)
.click({ multiple: true, force: true });
cy.get(`[label="${type}"]`).click({ multiple: true, force: true });
cy.get(nativeFilters.filtersPanel.filterTypeInput).within(() => {
cy.get('input').then($input => {
setSelectSearchInput($input, type);
});
});
cy.get(nativeFilters.modal.container)
.find(nativeFilters.filtersPanel.filterName)
.last()
@@ -280,31 +285,23 @@ export function fillNativeFilterForm(
.find(nativeFilters.filtersPanel.filterName)
.last()
.type(name, { scrollBehavior: false, force: true });
if (dataset) {
cy.get(nativeFilters.modal.container)
.find(nativeFilters.filtersPanel.datasetName)
.last()
.click({ force: true, scrollBehavior: false });
cy.get(nativeFilters.modal.container)
.find(nativeFilters.filtersPanel.datasetName)
.type(`${dataset}`, { scrollBehavior: false });
cy.get('div[aria-label="Dataset"]').within(() => {
cy.get('input').then($input => {
setSelectSearchInput($input, dataset, true);
});
});
cy.get(nativeFilters.silentLoading).should('not.exist');
cy.get(`[label="${dataset}"]`).click({ multiple: true, force: true });
}
cy.get(nativeFilters.silentLoading).should('not.exist');
if (filterColumn) {
cy.get(nativeFilters.filtersPanel.filterInfoInput)
.last()
.click({ force: true });
cy.get(nativeFilters.filtersPanel.filterInfoInput)
.last()
.type(filterColumn);
cy.get(nativeFilters.filtersPanel.inputDropdown)
.should('be.visible', { timeout: 20000 })
.last()
.click();
cy.get('div[aria-label="Column select"]').within(() => {
cy.get('input').then($input => {
setSelectSearchInput($input, filterColumn, true);
});
});
}
cy.get(nativeFilters.silentLoading).should('not.exist');
}
/** ************************************************************************
@@ -442,6 +439,9 @@ export function checkNativeFilterTooltip(index: number, value: string) {
.eq(index)
.trigger('mouseover');
cy.contains(`${value}`);
cy.get(nativeFilters.filterConfigurationSections.infoTooltip)
.eq(index)
.trigger('mouseout');
}
/** ************************************************************************
@@ -456,7 +456,7 @@ export function applyAdvancedTimeRangeFilterOnDashboard(
endRange?: string,
) {
cy.get('.control-label').contains('RANGE TYPE').should('be.visible');
cy.get('.antd5-popover-content .ant-select-selector')
cy.get('.antd5-popover-content .antd5-select-selector')
.should('be.visible')
.click();
cy.get(`[label="Advanced"]`).should('be.visible').click();
@@ -499,7 +499,7 @@ export function inputNativeFilterDefaultValue(
)
.eq(1)
.within(() => {
cy.get('.ant-select-selection-search-input').type(
cy.get('.antd5-select-selection-search-input').type(
`${defaultValue}{enter}`,
{ force: true },
);
@@ -507,8 +507,10 @@ export function inputNativeFilterDefaultValue(
});
} else {
cy.getBySel('default-input').within(() => {
cy.get('.ant-select-selection-search-input').click();
cy.get('.ant-select-item-option-content').contains(defaultValue).click();
cy.get('.antd5-select-selection-search-input').click();
cy.get('.antd5-select-item-option-content')
.contains(defaultValue)
.click();
});
}
}

View File

@@ -79,16 +79,20 @@ describe('Dashboards list', () => {
it('should sort correctly in list mode', () => {
cy.getBySel('sort-header').eq(1).click();
cy.getBySel('loading-indicator').should('not.exist');
cy.getBySel('table-row').first().contains('Supported Charts Dashboard');
cy.getBySel('sort-header').eq(1).click();
cy.getBySel('loading-indicator').should('not.exist');
cy.getBySel('table-row').first().contains("World Bank's Data");
cy.getBySel('sort-header').eq(1).click();
});
it('should bulk select in list mode', () => {
toggleBulkSelect();
cy.get('#header-toggle-all').click();
cy.get('[aria-label="checkbox-on"]').should('have.length', 6);
cy.get('[aria-label="Select all"]').click();
cy.get('.antd5-checkbox-input')
.should('be.checked')
.should('have.length', 6);
cy.getBySel('bulk-select-copy').contains('5 Selected');
cy.getBySel('bulk-select-action')
.should('have.length', 2)
@@ -97,7 +101,7 @@ describe('Dashboards list', () => {
expect($btns).to.contain('Export');
});
cy.getBySel('bulk-select-deselect-all').click();
cy.get('[aria-label="checkbox-on"]').should('have.length', 0);
cy.get('input[type="checkbox"]:checked').should('have.length', 0);
cy.getBySel('bulk-select-copy').contains('0 Selected');
cy.getBySel('bulk-select-action').should('not.exist');
});
@@ -195,6 +199,8 @@ describe('Dashboards list', () => {
cy.get('[data-test="table-row"] input[type="checkbox"]').eq(1).click();
cy.getBySel('bulk-select-action').eq(0).contains('Delete').click();
confirmDelete(true);
cy.getBySel('loading-indicator').should('exist');
cy.getBySel('loading-indicator').should('not.exist');
cy.getBySel('table-row')
.eq(0)
.should('not.contain', '3 - Sample dashboard');

View File

@@ -66,7 +66,7 @@ describe('Add database', () => {
cy.focused().type('badhost', { force: true });
cy.get('input[name="port"]').focus();
cy.focused().type('5432', { force: true });
cy.get('.ant-form-item-explain-error').contains(
cy.get('.antd5-form-item-explain-error').contains(
"The hostname provided can't be resolved",
);
});
@@ -79,6 +79,6 @@ describe('Add database', () => {
cy.get('input[name="port"]').focus();
cy.focused().type('123', { force: true });
cy.get('input[name="database"]').focus();
cy.get('.ant-form-item-explain-error').contains('The port is closed');
cy.get('.antd5-form-item-explain-error').contains('The port is closed');
});
});

View File

@@ -44,8 +44,8 @@ describe.skip('AdhocFilters', () => {
// antd tabs do lazy loading, so we need to click on tab with ace editor
cy.get('#filter-edit-popover').within(() => {
cy.get('.ant-tabs-tab').contains('Custom SQL').click();
cy.get('.ant-tabs-tab').contains('Simple').click();
cy.get('.antd5-tabs-tab').contains('Custom SQL').click();
cy.get('.antd5-tabs-tab').contains('Simple').click();
});
cy.get('script').then(nodes => {

View File

@@ -29,11 +29,11 @@ describe('Advanced analytics', () => {
cy.visitChartByName('Num Births Trend');
cy.verifySliceSuccess({ waitAlias: '@v1Data' });
cy.get('.ant-collapse-header')
cy.get('.antd5-collapse-header')
.contains('Advanced analytics')
.click({ force: true });
cy.get('[data-test=time_compare]').find('.ant-select').click();
cy.get('[data-test=time_compare]').find('.antd5-select').click();
cy.get('[data-test=time_compare]')
.find('input[type=search]')
.type('28 days{enter}');
@@ -52,14 +52,14 @@ describe('Advanced analytics', () => {
waitAlias: '@v1Data',
});
cy.wait('@getExplore');
cy.get('.ant-collapse-header')
cy.get('.antd5-collapse-header')
.contains('Advanced analytics')
.click({ force: true });
cy.get('[data-test=time_compare]')
.find('.ant-select-selector')
.find('.antd5-select-selector')
.contains('28 days');
cy.get('[data-test=time_compare]')
.find('.ant-select-selector')
.find('.antd5-select-selector')
.contains('1 year');
});
});

View File

@@ -30,7 +30,7 @@ import {
const SAMPLE_DASHBOARDS_INDEXES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
function openDashboardsAddedTo() {
cy.getBySel('actions-trigger').click();
cy.getBySel('actions-trigger').should('be.visible').click();
cy.get('.antd5-dropdown-menu-submenu-title')
.contains('On dashboards')
.trigger('mouseover', { force: true });
@@ -80,8 +80,8 @@ function verifyMetabar(text) {
cy.getBySel('metadata-bar').contains(text);
}
function saveAndVerifyDashboard(number) {
saveChartToDashboard(`${number} - Sample dashboard`);
function saveAndVerifyDashboard(chartName, number) {
saveChartToDashboard(chartName, `${number} - Sample dashboard`);
verifyMetabar(
number > 1 ? `Added to ${number} dashboards` : 'Added to 1 dashboard',
);
@@ -106,17 +106,17 @@ describe('Cross-referenced dashboards', () => {
openDashboardsAddedTo();
verifyDashboardsSubmenuItem('None');
saveAndVerifyDashboard('1');
saveAndVerifyDashboard('2');
saveAndVerifyDashboard('3');
saveAndVerifyDashboard('4');
saveAndVerifyDashboard('5');
saveAndVerifyDashboard('6');
saveAndVerifyDashboard('7');
saveAndVerifyDashboard('8');
saveAndVerifyDashboard('9');
saveAndVerifyDashboard('10');
saveAndVerifyDashboard('11');
saveAndVerifyDashboard('1 - Sample chart', '1');
saveAndVerifyDashboard('1 - Sample chart', '2');
saveAndVerifyDashboard('1 - Sample chart', '3');
saveAndVerifyDashboard('1 - Sample chart', '4');
saveAndVerifyDashboard('1 - Sample chart', '5');
saveAndVerifyDashboard('1 - Sample chart', '6');
saveAndVerifyDashboard('1 - Sample chart', '7');
saveAndVerifyDashboard('1 - Sample chart', '8');
saveAndVerifyDashboard('1 - Sample chart', '9');
saveAndVerifyDashboard('1 - Sample chart', '10');
saveAndVerifyDashboard('1 - Sample chart', '11');
verifyDashboardSearch();
verifyDashboardLink();

View File

@@ -19,7 +19,7 @@
// ***********************************************
// Tests for setting controls in the UI
// ***********************************************
import { interceptChart } from 'cypress/utils';
import { interceptChart, setSelectSearchInput } from 'cypress/utils';
describe('Datasource control', () => {
const newMetricName = `abc${Date.now()}`;
@@ -40,15 +40,11 @@ describe('Datasource control', () => {
// create new metric
cy.get('[data-test="crud-add-table-item"]', { timeout: 10000 }).click();
cy.wait(1000);
cy.get(
'[data-test="table-content-rows"] [data-test="editable-title-input"]',
)
cy.get('.antd5-table-body [data-test="textarea-editable-title-input"]')
.first()
.click();
cy.get(
'[data-test="table-content-rows"] [data-test="editable-title-input"]',
)
cy.get('.antd5-table-body [data-test="textarea-editable-title-input"]')
.first()
.focus();
cy.focused().clear({ force: true });
@@ -61,9 +57,12 @@ describe('Datasource control', () => {
.contains('Drop columns/metrics here or click')
.click();
cy.get('input[aria-label="Select saved metrics"]').type(
`${newMetricName}{enter}`,
);
cy.get('input[aria-label="Select saved metrics"]')
.should('exist')
.then($input => {
setSelectSearchInput($input, newMetricName);
});
// delete metric
cy.get('[data-test="datasource-menu-trigger"]').click();
cy.get('[data-test="edit-dataset"]').click();
@@ -72,7 +71,8 @@ describe('Datasource control', () => {
.contains('Metrics')
.click();
});
cy.get(`input[value="${newMetricName}"]`)
cy.get(`[data-test="textarea-editable-title-input"]`)
.contains(newMetricName)
.closest('tr')
.find('[data-test="crud-delete-icon"]')
.click();
@@ -91,21 +91,30 @@ describe('Color scheme control', () => {
});
it('should show color options with and without tooltips', () => {
cy.get('#controlSections-tab-display').click();
cy.get('.ant-select-selection-item .color-scheme-label').contains(
cy.get('#controlSections-tab-CUSTOMIZE').click();
cy.get('.antd5-select-selection-item .color-scheme-label').contains(
'Superset Colors',
);
cy.get('.ant-select-selection-item .color-scheme-label').trigger(
cy.get('.antd5-select-selection-item .color-scheme-label').trigger(
'mouseover',
);
cy.get('.color-scheme-tooltip').should('be.visible');
cy.get('.color-scheme-tooltip').contains('Superset Colors');
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
cy.get('.color-scheme-label')
.contains('Superset Colors')
.trigger('mouseover');
cy.get('.color-scheme-label')
.contains('Superset Colors')
.trigger('mouseout');
cy.focused().type('lyftColors');
cy.getBySel('lyftColors').should('exist');
cy.getBySel('lyftColors').trigger('mouseover');
cy.get('.color-scheme-tooltip').should('not.exist');
cy.getBySel('lyftColors').trigger('mouseover', { force: true });
cy.get('.color-scheme-tooltip').should('not.be.visible');
});
});
describe('VizType control', () => {
@@ -150,7 +159,7 @@ describe('Test datatable', () => {
).as('Samples');
cy.contains('Samples').click();
cy.wait('@Samples');
cy.get('.ant-tabs-tab-active').contains('Samples');
cy.get('.antd5-tabs-tab-active').contains('Samples');
cy.get('[data-test="row-count-label"]').contains('1k rows');
cy.get('.ant-empty-description').should('not.exist');
});

View File

@@ -124,7 +124,7 @@ describe('Test explore links', () => {
.find('input[aria-label="Select a dashboard"]')
.type(`${dashboardTitle}`, { force: true });
cy.get(`.ant-select-item[label="${dashboardTitle}"]`).click({
cy.get(`.antd5-select-item[title="${dashboardTitle}"]`).click({
force: true,
});
@@ -157,7 +157,7 @@ describe('Test explore links', () => {
.find('input[aria-label="Select a dashboard"]')
.type(`${dashboardTitle}{enter}`, { force: true });
cy.get(`.ant-select-item[label="${dashboardTitle}"]`).click({
cy.get(`.antd5-select-item[title="${dashboardTitle}"]`).click({
force: true,
});

View File

@@ -67,7 +67,7 @@ export function setFilter(filter: string, option: string) {
cy.wait('@filtering');
}
export function saveChartToDashboard(dashboardName: string) {
export function saveChartToDashboard(chartName: string, dashboardName: string) {
interceptDashboardGet();
interceptUpdate();
interceptExploreGet();
@@ -75,23 +75,30 @@ export function saveChartToDashboard(dashboardName: string) {
cy.getBySel('query-save-button')
.should('be.enabled')
.should('not.be.disabled')
.click();
cy.getBySelLike('chart-modal').should('be.visible');
cy.get(
'[data-test="save-chart-modal-select-dashboard-form"] [aria-label="Select a dashboard"]',
)
.first()
.click();
cy.get(
'.ant-select-selection-search-input[aria-label="Select a dashboard"]',
).type(dashboardName, { force: true });
cy.get(`.ant-select-item-option[title="${dashboardName}"]`).click();
cy.getBySel('btn-modal-save').click();
.click({ force: true });
cy.wait('@update');
cy.getBySel('save-modal-body')
.should('be.visible')
.then($modal => {
cy.wait(500);
cy.wrap($modal)
.find(
'.antd5-select-selection-search-input[aria-label="Select a dashboard"]',
)
.type(dashboardName, { force: true });
cy.wrap($modal)
.find(`.antd5-select-item-option[title="${dashboardName}"]`)
.click();
cy.getBySel('btn-modal-save').click();
cy.wait('@update');
});
cy.getBySel('save-modal-body').should('not.exist');
cy.getBySel('query-save-button').should('be.disabled');
cy.wait('@get');
cy.wait('@getExplore');
cy.contains(`was added to dashboard [${dashboardName}]`);
cy.contains(`Chart [${chartName}] has been overwritten`);
cy.getBySel('query-save-button').should('be.enabled');
}
export function visitSampleChartFromList(chartName: string) {

View File

@@ -49,12 +49,12 @@ describe('Visualization > Box Plot', () => {
it('should allow type to search color schemes', () => {
verify(BOX_PLOT_FORM_DATA);
cy.get('#controlSections-tab-display').click();
cy.get('#controlSections-tab-CUSTOMIZE').click();
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
cy.focused().type('supersetColors{enter}');
cy.get(
'.Control[data-test="color_scheme"] .ant-select-selection-item [data-test="supersetColors"]',
'.Control[data-test="color_scheme"] .antd5-select-selection-item [data-test="supersetColors"]',
).should('exist');
});
});

View File

@@ -91,7 +91,7 @@ describe('Visualization > Bubble', () => {
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
cy.focused().type('supersetColors{enter}');
cy.get(
'.Control[data-test="color_scheme"] .ant-select-selection-item [data-test="supersetColors"]',
'.Control[data-test="color_scheme"] .antd5-select-selection-item [data-test="supersetColors"]',
).should('exist');
cy.get('[data-test=run-query-button]').click();
cy.get('.bubble .nv-legend .nv-legend-symbol').should(

View File

@@ -89,12 +89,12 @@ describe('Visualization > Compare', () => {
it('should allow type to search color schemes and apply the scheme', () => {
verify(COMPARE_FORM_DATA);
cy.get('#controlSections-tab-display').click();
cy.get('#controlSections-tab-CUSTOMIZE').click();
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
cy.focused().type('supersetColors{enter}');
cy.get(
'.Control[data-test="color_scheme"] .ant-select-selection-item [data-test="supersetColors"]',
'.Control[data-test="color_scheme"] .antd5-select-selection-item [data-test="supersetColors"]',
).should('exist');
});
});

View File

@@ -64,12 +64,12 @@ describe('Visualization > Gauge', () => {
it('should allow type to search color schemes', () => {
verify(GAUGE_FORM_DATA);
cy.get('#controlSections-tab-display').click();
cy.get('#controlSections-tab-CUSTOMIZE').click();
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
cy.focused().type('bnbColors{enter}');
cy.get(
'.Control[data-test="color_scheme"] .ant-select-selection-item [data-test="bnbColors"]',
'.Control[data-test="color_scheme"] .antd5-select-selection-item [data-test="bnbColors"]',
).should('exist');
});
});

View File

@@ -80,12 +80,12 @@ describe('Visualization > Graph', () => {
it('should allow type to search color schemes', () => {
verify(GRAPH_FORM_DATA);
cy.get('#controlSections-tab-display').click();
cy.get('#controlSections-tab-CUSTOMIZE').click();
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
cy.focused().type('bnbColors{enter}');
cy.get(
'.Control[data-test="color_scheme"] .ant-select-selection-item [data-test="bnbColors"]',
'.Control[data-test="color_scheme"] .antd5-select-selection-item [data-test="bnbColors"]',
).should('exist');
});
});

View File

@@ -71,12 +71,12 @@ describe('Visualization > Pie', () => {
it('should allow type to search color schemes', () => {
verify(PIE_FORM_DATA);
cy.get('#controlSections-tab-display').click();
cy.get('#controlSections-tab-CUSTOMIZE').click();
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
cy.focused().type('supersetColors{enter}');
cy.get(
'.Control[data-test="color_scheme"] .ant-select-selection-item [data-test="supersetColors"]',
'.Control[data-test="color_scheme"] .antd5-select-selection-item [data-test="supersetColors"]',
).should('exist');
});
});

View File

@@ -86,12 +86,12 @@ describe('Visualization > Sunburst', () => {
it('should allow type to search color schemes', () => {
verify(SUNBURST_FORM_DATA);
cy.get('#controlSections-tab-display').click();
cy.get('#controlSections-tab-CUSTOMIZE').click();
cy.get('.Control[data-test="color_scheme"]').scrollIntoView();
cy.get('.Control[data-test="color_scheme"] input[type="search"]').focus();
cy.focused().type('supersetColors{enter}');
cy.get(
'.Control[data-test="color_scheme"] .ant-select-selection-item [data-test="supersetColors"]',
'.Control[data-test="color_scheme"] .antd5-select-selection-item [data-test="supersetColors"]',
).should('exist');
});
});

View File

@@ -89,7 +89,7 @@ describe('Visualization > World Map', () => {
).focus();
cy.focused().type('greens{enter}');
cy.get(
'.Control[data-test="linear_color_scheme"] .ant-select-selection-item [data-test="greens"]',
'.Control[data-test="linear_color_scheme"] .antd5-select-selection-item [data-test="greens"]',
).should('exist');
});
});

View File

@@ -165,7 +165,7 @@ describe('SqlLab query panel', () => {
cy.wait('@queryFinished');
cy.get(
'.SouthPane .ant-tabs-content > .ant-tabs-tabpane-active > div button:first',
'.SouthPane .antd5-tabs-content > .antd5-tabs-tabpane-active > div button:first',
{ timeout: 10000 },
).click();

View File

@@ -22,7 +22,7 @@ describe('SqlLab query tabs', () => {
});
const tablistSelector = '[data-test="sql-editor-tabs"] > [role="tablist"]';
const tabSelector = `${tablistSelector} [role="tab"]`;
const tabSelector = `${tablistSelector} [role="tab"]:not([type="button"])`;
it('allows you to create and close a tab', () => {
cy.get(tabSelector).then(tabs => {

View File

@@ -41,8 +41,8 @@ export const pageHeader = {
};
export const profile = {
activeTab: '.ant-tabs-tab-active',
inactiveTab: '.ant-tabs-tab',
activeTab: '.antd5-tabs-tab-active',
inactiveTab: '.antd5-tabs-tab',
emptyFavoritedPlaceholder: '.ant-empty-normal',
tableRow: '.table-row',
favoritesSpace: '#rc-tabs-0-panel-2',
@@ -52,16 +52,16 @@ export const securityAccess = {
};
export const homePage = {
homeSection: {
sectionArea: '.ant-collapse-content-box',
sectionArea: '.antd5-collapse-content-box',
sectionElement: '.antd5-card-meta-title',
},
sections: {
expandedSection: '.ant-collapse-item-active',
expandedSection: '.antd5-collapse-item-active',
expandedSectionHeader: '[aria-expanded="true"]',
collapseExpandButton: '.ant-collapse-arrow',
collapsedSection: '[class="ant-collapse-item"]',
collapseExpandButton: '.antd5-collapse-arrow',
collapsedSection: '[class="antd5-collapse-item"]',
collapsedSectionHeader: '[aria-expanded="false"]',
section: '[class^="ant-collapse-item"]',
section: '[class^="antd5-collapse-item"]',
sectionsMenuContainer: "[role='navigation']",
sectionsMenuItem: "[role='menuitem']",
card: dataTestLocator('styled-card'),
@@ -91,9 +91,9 @@ export const databasesPage = {
preferredBlockSheets: '.preferred > :nth-child(6)',
supportedDatabasesText: '.control-label',
orChoose: '.available-label',
dbDropdown: '[class="ant-select-selection-search-input"]',
dbDropdown: '[class="antd5-select-selection-search-input"]',
dbDropdownMenu: '.rc-virtual-list-holder-inner',
dbDropdownMenuItem: '[class="ant-select-item-option-content"]',
dbDropdownMenuItem: '[class="antd5-select-item-option-content"]',
infoAlert: '.antd5-alert',
serviceAccountInput: '[name="credentials_info"]',
connectionStep: {
@@ -110,9 +110,9 @@ export const databasesPage = {
additionalParameters: '[name="query_input"]',
sqlAlchemyUriInput: dataTestLocator('sqlalchemy-uri-input'),
advancedTab: '#rc-tabs-0-tab-2',
activeTab: '.ant-tabs-tab-active',
activeTab: '.antd5-tabs-tab-active',
securitySubMenu:
':nth-child(3) > .ant-collapse-header > .anticon > svg > path',
':nth-child(3) > .antd5-collapse-header > .anticon > svg > path',
aceTextInput: '.ace_text-input',
aceContent: '.ace_content',
connectButton: '.css-16i3wh7',
@@ -136,15 +136,15 @@ export const sqlLabView = {
},
databaseInput: '[data-test=DatabaseSelector] > :nth-child(1)',
emptyMenuOptionsPlaceholder: '[class="ant-empty-img-simple"]',
removeTabButton: '.ant-tabs-tab-remove > .anticon > svg',
tabsNavList: "[class='ant-tabs-nav-list']",
tab: "[class='ant-tabs-tab-btn']",
removeTabButton: '.antd5-tabs-tab-remove > .anticon > svg',
tabsNavList: "[class='antd5-tabs-nav-list']",
tab: "[class='antd5-tabs-tab-btn']",
addTabButton: dataTestLocator('add-tab-icon'),
tooltip: '.antd5-tooltip-content',
tabName: '.css-1suejie',
schemaInput: '[data-test=DatabaseSelector] > :nth-child(2)',
loadingIndicator: '.Select__loading-indicator',
menuItem: '[class="ant-select-item-option-content"]',
menuItem: '[class="antd5-select-item-option-content"]',
examplesMenuItem: '[title="examples"]',
tableInput: ':nth-child(4) > .select > :nth-child(1)',
sqlEditor: '#brace-editor textarea',
@@ -275,9 +275,9 @@ export const chartListView = {
bulkSelect: dataTestLocator('bulk-select'),
},
header: {
cardView: '[aria-label="appstore"]',
listView: '[aria-label="unordered-list"]',
sort: '[class="ant-select-selection-search-input"][aria-label="Sort"]',
cardView: '[aria-label="card-view"]',
listView: '[aria-label="list-view"]',
sort: '[class="antd5-select-selection-search-input"][aria-label="Sort"]',
sortRecentlyModifiedMenuOption: '[label="Recently modified"]',
sortAlphabeticalMenuOption: '[label="Alphabetical"]',
sortDropdown: '.Select__menu',
@@ -294,8 +294,8 @@ export const chartListView = {
},
table: {
bulkSelect: {
checkboxOff: '[aria-label="checkbox-off"]',
checkboxOn: '[aria-label="checkbox-on"]',
checkboxOff: 'input[type="checkbox"]:checked',
checkboxOn: 'input[type="checkbox"]:not(:checked)',
action: dataTestLocator('bulk-select-action'),
},
tableList: dataTestLocator('listview-table'),
@@ -326,32 +326,32 @@ export const nativeFilters = {
alertXUnsavedFilters: '.antd5-alert-message',
tabsList: {
filterItemsContainer: dataTestLocator('filter-title-container'),
tabsContainer: '[class="ant-tabs-nav-list"]',
tab: '.ant-tabs-tab',
tabsContainer: '[class="antd5-tabs-nav-list"]',
tab: '.antd5-tabs-tab',
removeTab: '[aria-label="delete"]',
},
addFilter: dataTestLocator('add-filter-button'),
defaultValueCheck: '.ant-checkbox-checked',
defaultValueCheck: '.antd5-checkbox-checked',
addNewFilterButton: dataTestLocator('add-new-filter-button'),
addNewDividerButton: dataTestLocator('add-new-divider-button'),
},
filtersPanel: {
filterName: dataTestLocator('filters-config-modal__name-input'),
datasetName: dataTestLocator('filters-config-modal__datasource-input'),
filterInfoInput: '.ant-select-selection-search-input',
inputDropdown: '.ant-select-item-option-content',
columnEmptyInput: '.ant-select-selection-placeholder',
filterInfoInput: '.antd5-select-selection-search-input',
inputDropdown: '.antd5-select-item-option-content',
columnEmptyInput: '.antd5-select-selection-placeholder',
filterTypeInput: dataTestLocator('filters-config-modal__filter-type'),
fieldInput: dataTestLocator('field-input'),
filterTypeItem: '.ant-select-selection-item',
filterTypeItem: '.antd5-select-selection-item',
filterGear: dataTestLocator('filterbar-orientation-icon'),
},
filterFromDashboardView: {
filterValueInput: '[class="ant-select-selection-search-input"]',
filterValueInput: '[class="antd5-select-selection-search-input"]',
expand: dataTestLocator('filter-bar__expand-button'),
collapse: dataTestLocator('filter-bar__collapse-button'),
filterName: dataTestLocator('filter-control-name'),
filterContent: '.ant-select-selection-item',
filterContent: '.antd5-select-selection-item',
createFilterButton: dataTestLocator('filter-bar__create-filter'),
timeRangeFilterContent: dataTestLocator('time-range-trigger'),
},
@@ -359,24 +359,24 @@ export const nativeFilters = {
removeFilter: '[aria-label="remove"]',
silentLoading: '.loading inline-centered css-101mkpk',
filterConfigurationSections: {
sectionHeader: '.ant-collapse-header',
sectionHeader: '.antd5-collapse-header',
displayedSection: 'div[style="height: 100%; overflow-y: auto;"]',
collapseExpandButton: '.ant-collapse-arrow',
checkedCheckbox: '.ant-checkbox-wrapper-checked',
collapseExpandButton: '.antd5-collapse-arrow',
checkedCheckbox: '.antd5-checkbox-wrapper-checked',
infoTooltip: '[aria-label="Show info tooltip"]',
parentFilterInput: dataTestLocator('parent-filter-input'),
filterPlaceholder: '.ant-select-selection-placeholder',
collapsedSectionContainer: '[class="ant-collapse-content-box"]',
filterPlaceholder: '.antd5-select-selection-placeholder',
collapsedSectionContainer: '[class="antd5-collapse-content-box"]',
},
filtersList: {
list: '.ant-tabs-nav-list',
listItemNotActive: '[class="ant-tabs-tab ant-tabs-tab-with-remove"]',
list: '.antd5-tabs-nav-list',
listItemNotActive: '[class="antd5-tabs-tab antd5-tabs-tab-with-remove"]',
listItemActive:
'[class="ant-tabs-tab ant-tabs-tab-with-remove ant-tabs-tab-active"]',
'[class="antd5-tabs-tab antd5-tabs-tab-with-remove antd5-tabs-tab-active"]',
removeIcon: '[aria-label="delete"]',
},
filterItem: dataTestLocator('form-item-value'),
filterItemDropdown: '.ant-select-selection-search',
filterItemDropdown: '.antd5-select-selection-search',
applyFilter: dataTestLocator('filter-bar__apply-button'),
defaultInput: dataTestLocator('default-input'),
filterIcon: dataTestLocator('filter-icon'),
@@ -460,7 +460,7 @@ export const dashboardListView = {
};
export const exploreView = {
openDatasourceMenu: dataTestLocator('open-datasource-tab'),
sectionsHeader: '.ant-collapse-header',
sectionsHeader: '.antd5-collapse-header',
datasourceMenuThreeDots: dataTestLocator('datasource-menu-trigger'),
threeDotsMenuDropdown: {
editDataset: dataTestLocator('edit-dataset'),
@@ -484,7 +484,7 @@ export const exploreView = {
saveModal: {
modal: '.antd5-modal-content',
chartNameInput: dataTestLocator('new-chart-name'),
dashboardNameInput: '.ant-select-selection-search-input',
dashboardNameInput: '.antd5-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-content-box',
categoryArea: '.antd5-collapse-content-box',
dragField: dataTestLocator('datasource'),
metricsField: dataTestLocator('metrics'),
optionField: dataTestLocator('option-label'),
@@ -583,7 +583,7 @@ export const exploreView = {
saveButton: dataTestLocator('datasource-modal-save'),
metricsTab: {
addItem: dataTestLocator('crud-add-table-item'),
rowsContainer: dataTestLocator('table-content-rows'),
rowsContainer: '.antd5-table-body',
},
confirmModal: {
okButton: '.antd5-modal-confirm-btns .antd5-btn-primary',
@@ -594,8 +594,8 @@ export const exploreView = {
},
};
export const createChartView = {
chooseDatasetInput: '.ant-select-selection-search-input',
chooseDatasetOption: '.ant-select-item-option-content',
chooseDatasetInput: '.antd5-select-selection-search-input',
chooseDatasetOption: '.antd5-select-item-option-content',
chooseDatasetList: '.rc-virtual-list-holder-inner',
tableVizType: "[alt='Table']",
};
@@ -606,8 +606,8 @@ export const editDashboardView = {
discardChanges: dataTestLocator('discard-changes-button'),
chartBox: dataTestLocator('chart-grid-component'),
tabsList: {
tabsContainer: '[class="ant-tabs-nav-list"]',
tab: '.ant-tabs-tab',
tabsContainer: '[class="antd5-tabs-nav-list"]',
tab: '.antd5-tabs-tab',
},
};
export const dashboardView = {
@@ -641,7 +641,7 @@ export const dashboardView = {
secondTabSalesDashboard: dataTestLocator('dragdroppable-object'),
},
timeRangeModal: {
rangeTypeField: '.ant-select-selection-item',
rangeTypeField: '.antd5-select-selection-item',
startTimeInputNumber: '.ant-input-number-input',
datePicker: '.ant-picker-input',
applyButton: dataTestLocator('date-filter-control__apply-button'),
@@ -672,7 +672,7 @@ export const dashboardView = {
tabsList: {
tabsContainer: dataTestLocator('dashboard-component-tabs'),
tabsNavList: dataTestLocator('nav-list'),
tabs: '.ant-tabs-nav-list',
tab: '.ant-tabs-tab',
tabs: '.antd5-tabs-nav-list',
tab: '.antd5-tabs-tab',
},
};

View File

@@ -41,8 +41,8 @@ export function toggleBulkSelect() {
export function clearAllInputs() {
cy.get('body').then($body => {
if ($body.find('.ant-select-clear').length) {
cy.get('.ant-select-clear').click({ multiple: true, force: true });
if ($body.find('.antd5-select-clear').length) {
cy.get('.antd5-select-clear').click({ multiple: true, force: true });
}
});
}
@@ -143,3 +143,29 @@ export function resize(selector: string) {
},
};
}
export const setSelectSearchInput = (
$input: any,
value: string,
async = false,
) => {
// Ant Design 5 Select crashes Chromium with type/click events when showSearch is true.
// This copies the value directly to the input element as a workaround.
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
'value',
)?.set;
nativeInputValueSetter?.call($input[0], value);
// Trigger the input and change events
if (async) {
$input[0].dispatchEvent(new Event('mousedown', { bubbles: true }));
}
$input[0].dispatchEvent(new Event('input', { bubbles: true }));
$input[0].dispatchEvent(new Event('change', { bubbles: true }));
cy.get('.antd5-select-item-option-content').should('exist').first().click({
force: true,
});
};

File diff suppressed because it is too large Load Diff

View File

@@ -44,6 +44,7 @@
"build-storybook": "storybook build",
"build-translation": "scripts/po2json.sh",
"bundle-stats": "cross-env BUNDLE_ANALYZER=true npm run build && npx open-cli ../superset/static/stats/statistics.html",
"compile-less": "tsx ./scripts/compileLess.ts",
"core:cover": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=4096\" jest --coverage --coverageThreshold='{\"global\":{\"statements\":100,\"branches\":100,\"functions\":100,\"lines\":100}}' --collectCoverageFrom='[\"packages/**/src/**/*.{js,ts}\", \"!packages/superset-ui-demo/**/*\"]' packages",
"cover": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=4096\" jest --coverage",
"dev": "webpack --mode=development --color --watch",
@@ -83,7 +84,7 @@
"dependencies": {
"@ant-design/icons": "^5.2.6",
"@emotion/cache": "^11.4.0",
"@emotion/react": "^11.13.3",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.3.0",
"@fontsource/fira-code": "^5.0.18",
"@fontsource/inter": "^5.0.20",
@@ -126,7 +127,7 @@
"ag-grid-community": "33.1.1",
"ag-grid-react": "33.1.1",
"antd": "4.10.3",
"antd-v5": "npm:antd@^5.18.0",
"antd-v5": "npm:antd@^5.24.6",
"bootstrap": "^3.4.1",
"brace": "^0.11.1",
"chrono-node": "^2.7.8",
@@ -150,6 +151,8 @@
"geostyler-style": "^7.5.0",
"geostyler-wfs-parser": "^2.0.3",
"googleapis": "^130.0.0",
"handlebars": "^4.7.8",
"html-webpack-plugin": "^5.6.3",
"immer": "^10.1.1",
"interweave": "^13.1.0",
"jquery": "^3.7.1",
@@ -262,12 +265,13 @@
"@types/dom-to-image": "^2.6.7",
"@types/enzyme": "^3.10.18",
"@types/fetch-mock": "^7.3.2",
"@types/jest": "^29.5.12",
"@types/jest": "^29.5.14",
"@types/jquery": "^3.5.8",
"@types/js-levenshtein": "^1.1.3",
"@types/json-bigint": "^1.0.4",
"@types/math-expression-evaluator": "^1.3.3",
"@types/mousetrap": "^1.6.15",
"@types/node": "^22.12.0",
"@types/react": "^17.0.83",
"@types/react-dom": "^17.0.26",
"@types/react-gravatar": "^2.6.14",
@@ -286,6 +290,7 @@
"@types/redux-mock-store": "^1.0.6",
"@types/rison": "0.1.0",
"@types/sinon": "^17.0.3",
"@types/testing-library__jest-dom": "^5.14.9",
"@types/tinycolor2": "^1.4.3",
"@types/yargs": "12 - 18",
"@typescript-eslint/eslint-plugin": "^5.62.0",
@@ -304,6 +309,8 @@
"css-minimizer-webpack-plugin": "^7.0.2",
"enzyme": "^3.11.0",
"enzyme-matchers": "^7.1.2",
"esbuild": "^0.20.0",
"esbuild-loader": "^4.2.2",
"eslint": "^8.56.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^7.2.0",
@@ -330,7 +337,9 @@
"html-webpack-plugin": "^5.6.3",
"imports-loader": "^5.0.0",
"jest": "^29.7.0",
"jest-environment-enzyme": "^7.1.2",
"jest-environment-jsdom": "^29.7.0",
"jest-enzyme": "^7.1.2",
"jest-html-reporter": "^3.10.2",
"jest-websocket-mock": "^2.5.0",
"jsdom": "^26.0.0",
@@ -355,6 +364,7 @@
"ts-jest": "^29.2.5",
"ts-loader": "^9.5.1",
"tscw-config": "^1.1.2",
"tsx": "^4.19.2",
"typescript": "5.1.6",
"vm-browserify": "^1.1.2",
"webpack": "^5.98.0",
@@ -374,7 +384,8 @@
"d3-color": "^3.1.0",
"puppeteer": "^22.4.1",
"underscore": "^1.13.7",
"jspdf": "^3.0.1"
"jspdf": "^3.0.1",
"nwsapi": "^2.2.13"
},
"readme": "ERROR: No README data found!",
"scarfSettings": {

View File

@@ -25,29 +25,14 @@ import { <%= packageLabel %>Props, <%= packageLabel %>StylesProps } from './type
// Theming variables are provided for your use via a ThemeProvider
// imported from @superset-ui/core. For variables available, please visit
// https://github.com/apache-superset/superset-ui/blob/master/packages/superset-ui-core/src/style/index.ts
// https://github.com/apache-superset/superset-ui/blob/master/packages/superset-ui-core/src/theme/index.ts
const Styles = styled.div<<%= packageLabel %>StylesProps>`
background-color: ${({ theme }) => theme.colors.secondary.light2};
padding: ${({ theme }) => theme.gridUnit * 4}px;
border-radius: ${({ theme }) => theme.gridUnit * 2}px;
background-color: ${({ theme }) => theme.colors.primary.light2};
padding: ${({ theme }) => theme.sizeUnit * 4}px;
border-radius: ${({ theme }) => theme.sizeUnit * 2}px;
height: ${({ height }) => height}px;
width: ${({ width }) => width}px;
h3 {
/* You can use your props to control CSS! */
margin-top: 0;
margin-bottom: ${({ theme }) => theme.gridUnit * 3}px;
font-size: ${({ theme, headerFontSize }) =>
theme.typography.sizes[headerFontSize]}px;
font-weight: ${({ theme, boldText }) =>
theme.typography.weights[boldText ? 'bold' : 'normal']};
}
pre {
height: ${({ theme, headerFontSize, height }) =>
height - theme.gridUnit * 12 - theme.typography.sizes[headerFontSize]}px;
}
`;
/**

View File

@@ -27,7 +27,7 @@ interface CertifiedIconWithTooltipProps {
}
const StyledDiv = styled.div`
margin-bottom: ${({ theme }) => theme.gridUnit * 2}px;
margin-bottom: ${({ theme }) => theme.sizeUnit * 2}px;
`;
function CertifiedIconWithTooltip({
@@ -58,7 +58,7 @@ function CertifiedIconWithTooltip({
>
<g>
<path
fill={theme.colors.primary.base}
fill={theme.colorPrimary}
d="M23,12l-2.44-2.79l0.34-3.69l-3.61-0.82L15.4,1.5L12,2.96L8.6,1.5L6.71,4.69L3.1,5.5L3.44,9.2L1,12l2.44,2.79l-0.34,3.7 l3.61,0.82L8.6,22.5l3.4-1.47l3.4,1.46l1.89-3.19l3.61-0.82l-0.34-3.69L23,12z M9.38,16.01L7,13.61c-0.39-0.39-0.39-1.02,0-1.41 l0.07-0.07c0.39-0.39,1.03-0.39,1.42,0l1.61,1.62l5.15-5.16c0.39-0.39,1.03-0.39,1.42,0l0.07,0.07c0.39,0.39,0.39,1.02,0,1.41 l-5.92,5.94C10.41,16.4,9.78,16.4,9.38,16.01z"
/>
</g>

View File

@@ -40,7 +40,7 @@ const StyleOverrides = styled.span`
display: flex;
align-items: center;
svg {
margin-right: ${({ theme }) => theme.gridUnit}px;
margin-right: ${({ theme }) => theme.sizeUnit}px;
}
`;
@@ -82,7 +82,7 @@ export function ColumnOption({
<span
className="option-label column-option-label"
css={(theme: SupersetTheme) => css`
margin-right: ${theme.gridUnit}px;
margin-right: ${theme.sizeUnit}px;
`}
ref={labelRef}
>

View File

@@ -39,9 +39,9 @@ const TypeIconWrapper = styled.div`
display: flex;
justify-content: center;
align-items: center;
width: ${theme.gridUnit * 6}px;
height: ${theme.gridUnit * 6}px;
margin-right: ${theme.gridUnit}px;
width: ${theme.sizeUnit * 6}px;
height: ${theme.sizeUnit * 6}px;
margin-right: ${theme.sizeUnit}px;
&& svg {
margin-right: 0;

View File

@@ -20,9 +20,9 @@ import { styled, css } from '@superset-ui/core';
export const ControlSubSectionHeader = styled.div`
${({ theme }) => css`
font-weight: ${theme.typography.weights.bold};
font-size: ${theme.typography.sizes.s};
margin-bottom: ${theme.gridUnit}px;
font-weight: ${theme.fontWeightStrong};
font-size: ${theme.fontSizeSM};
margin-bottom: ${theme.sizeUnit}px;
`}
`;
export default ControlSubSectionHeader;

View File

@@ -17,5 +17,4 @@
* under the License.
*/
export { Dropdown } from 'antd';
export type { DropDownProps } from 'antd/lib/dropdown';
export { Dropdown, type DropDownProps } from 'antd-v5';

View File

@@ -17,5 +17,4 @@
* under the License.
*/
export { Menu } from 'antd';
export type { MenuProps } from 'antd/lib/menu';
export { Menu, type MenuProps } from 'antd-v5';

View File

@@ -37,7 +37,7 @@ const FlexRowContainer = styled.div`
display: flex;
> svg {
margin-right: ${({ theme }) => theme.gridUnit}px;
margin-right: ${({ theme }) => theme.sizeUnit}px;
}
`;
@@ -73,7 +73,7 @@ export function MetricOption({
<span
className="option-label metric-option-label"
css={(theme: SupersetTheme) => css`
margin-right: ${theme.gridUnit}px;
margin-right: ${theme.sizeUnit}px;
`}
ref={labelRef}
>

View File

@@ -19,17 +19,17 @@
import { useEffect, useState } from 'react';
import { Popover } from 'antd-v5';
import type ReactAce from 'react-ace';
import type { PopoverProps } from 'antd-v5/lib/popover';
import type { PopoverProps } from 'antd-v5/es/popover';
import { CalculatorOutlined } from '@ant-design/icons';
import { css, styled, useTheme, t } from '@superset-ui/core';
const StyledCalculatorIcon = styled(CalculatorOutlined)`
${({ theme }) => css`
color: ${theme.colors.grayscale.base};
font-size: ${theme.typography.sizes.s}px;
font-size: ${theme.fontSizeSM}px;
& svg {
margin-left: ${theme.gridUnit}px;
margin-right: ${theme.gridUnit}px;
margin-left: ${theme.sizeUnit}px;
margin-right: ${theme.sizeUnit}px;
}
`}
`;
@@ -66,8 +66,8 @@ export const SQLPopover = (props: PopoverProps & { sqlExpression: string }) => {
wrapEnabled
style={{
border: `1px solid ${theme.colors.grayscale.light2}`,
background: theme.colors.secondary.light5,
maxWidth: theme.gridUnit * 100,
background: theme.colorPrimaryBg,
maxWidth: theme.sizeUnit * 100,
}}
/>
}

View File

@@ -17,7 +17,10 @@
* under the License.
*/
import { useState, ReactNode } from 'react';
import AntdSelect, { SelectProps as AntdSelectProps } from 'antd/lib/select';
import {
Select as AntdSelect,
type SelectProps as AntdSelectProps,
} from 'antd-v5';
export const { Option }: any = AntdSelect;
@@ -35,7 +38,7 @@ export type SelectProps<VT> = Omit<AntdSelectProps<VT>, 'options'> & {
export default function Select<VT extends string | number>({
creatable,
onSearch,
dropdownMatchSelectWidth = false,
popupMatchSelectWidth = false,
minWidth = '100%',
showSearch: showSearch_ = true,
onChange,
@@ -73,7 +76,7 @@ export default function Select<VT extends string | number>({
return (
<AntdSelect<VT>
dropdownMatchSelectWidth={dropdownMatchSelectWidth}
popupMatchSelectWidth={popupMatchSelectWidth}
showSearch={showSearch}
onSearch={handleSearch}
onChange={handleChange}

View File

@@ -22,7 +22,7 @@ import { Tooltip as BaseTooltip } from 'antd-v5';
import {
TooltipProps as BaseTooltipProps,
TooltipPlacement as BaseTooltipPlacement,
} from 'antd-v5/lib/tooltip';
} from 'antd-v5/es/tooltip';
export type TooltipProps = BaseTooltipProps;
export type TooltipPlacement = BaseTooltipPlacement;
@@ -38,10 +38,10 @@ export const Tooltip = ({
<BaseTooltip
styles={{
root: {
fontSize: theme.typography.sizes.s,
fontSize: theme.fontSizeSM,
lineHeight: '1.6',
maxWidth: theme.gridUnit * 62,
minWidth: theme.gridUnit * 30,
maxWidth: theme.sizeUnit * 62,
minWidth: theme.sizeUnit * 30,
...overlayStyle,
},
}}

View File

@@ -29,18 +29,18 @@ const TooltipSectionWrapper = styled.div`
overflow: hidden;
text-overflow: ellipsis;
font-size: ${theme.typography.sizes.s}px;
font-size: ${theme.fontSizeSM}px;
line-height: 1.2;
&:not(:last-of-type) {
margin-bottom: ${theme.gridUnit * 2}px;
margin-bottom: ${theme.sizeUnit * 2}px;
}
`}
`;
const TooltipSectionLabel = styled.span`
${({ theme }) => css`
font-weight: ${theme.typography.weights.bold};
font-weight: ${theme.fontWeightStrong};
`}
`;

View File

@@ -55,17 +55,17 @@ export default function RadioButtonControl({
},
'.control-label': {
color: theme.colors.grayscale.base,
marginBottom: theme.gridUnit,
marginBottom: theme.sizeUnit,
},
'.control-label + .btn-group': {
marginTop: '1px',
},
'.btn-group .btn-default': {
color: theme.colors.grayscale.dark1,
color: theme.colorText,
},
'.btn-group .btn.active': {
background: theme.colors.grayscale.light4,
fontWeight: theme.typography.weights.bold,
fontWeight: theme.fontWeightStrong,
boxShadow: 'none',
},
}}

View File

@@ -70,6 +70,7 @@
"timezone-mock": "1.3.6"
},
"peerDependencies": {
"antd-v5": "npm:antd@^5.18.0",
"@emotion/cache": "^11.4.0",
"@emotion/react": "^11.4.1",
"@emotion/styled": "^11.3.0",

View File

@@ -18,7 +18,7 @@
*/
import { t } from '@superset-ui/core';
import { SupersetTheme } from '../../style';
import { SupersetTheme } from '../..';
import { FallbackPropsWithDimension } from './SuperChart';
export type Props = FallbackPropsWithDimension;

View File

@@ -18,7 +18,7 @@
*/
import { CSSProperties } from 'react';
import { css, styled } from '../../style';
import { css, styled } from '../../theme';
import { t } from '../../translation';
const MESSAGE_STYLES: CSSProperties = { maxWidth: 800 };
@@ -36,16 +36,16 @@ const Container = styled.div<{
text-align: center;
height: ${height}px;
width: ${width}px;
padding: ${theme.gridUnit * 4}px;
padding: ${theme.sizeUnit * 4}px;
& .no-results-title {
font-size: ${theme.typography.sizes.l}px;
font-weight: ${theme.typography.weights.bold};
padding-bottom: ${theme.gridUnit * 2};
font-size: ${theme.fontSizeLG}px;
font-weight: ${theme.fontWeightStrong};
padding-bottom: ${theme.sizeUnit * 2};
}
& .no-results-body {
font-size: ${theme.typography.sizes.m}px;
font-size: ${theme.fontSize}px;
}
`}
`;

View File

@@ -18,7 +18,7 @@
*/
// eslint-disable-next-line no-restricted-syntax -- whole React import is required for `reactify.test.tsx` Jest test passing.
import React, { Component, ComponentClass, WeakValidationMap } from 'react';
import { Component, ComponentClass, WeakValidationMap } from 'react';
// TODO: Note that id and className can collide between Props and ReactifyProps
// leading to (likely) unexpected behaviors. We should either require Props to not

View File

@@ -37,7 +37,7 @@ import {
SetDataMaskHook,
} from '../types/Base';
import { QueryData, DataRecordFilters } from '..';
import { SupersetTheme } from '../../style';
import { SupersetTheme } from '../../theme';
// TODO: more specific typing for these fields of ChartProps
type AnnotationData = PlainObject;

View File

@@ -28,7 +28,7 @@ export * from './number-format';
export * from './time-format';
export * from './dimension';
export * from './color';
export * from './style';
export * from './theme';
export * from './validator';
export * from './chart';
export * from './chart-composition';

View File

@@ -1,167 +0,0 @@
/**
* 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 emotionStyled from '@emotion/styled';
import { useTheme as useThemeBasic } from '@emotion/react';
import createCache from '@emotion/cache';
export {
css,
keyframes,
jsx,
ThemeProvider,
CacheProvider as EmotionCacheProvider,
withTheme,
type SerializedStyles,
} from '@emotion/react';
export { default as createEmotionCache } from '@emotion/cache';
declare module '@emotion/react' {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface Theme extends SupersetTheme {}
}
export function useTheme() {
const theme = useThemeBasic();
// in the case there is no theme, useTheme returns an empty object
if (Object.keys(theme).length === 0 && theme.constructor === Object) {
throw new Error(
'useTheme() could not find a ThemeContext. The <ThemeProvider/> component is likely missing from the app.',
);
}
return theme;
}
export const emotionCache = createCache({
key: 'superset',
});
export const styled = emotionStyled;
const defaultTheme = {
borderRadius: 4,
colors: {
text: {
label: '#879399',
help: '#737373',
},
primary: {
base: '#20A7C9',
dark1: '#1A85A0',
dark2: '#156378',
light1: '#79CADE',
light2: '#A5DAE9',
light3: '#D2EDF4',
light4: '#E9F6F9',
light5: '#F3F8FA',
},
secondary: {
base: '#444E7C',
dark1: '#363E63',
dark2: '#282E4A',
dark3: '#1B1F31',
light1: '#8E94B0',
light2: '#B4B8CA',
light3: '#D9DBE4',
light4: '#ECEEF2',
light5: '#F5F5F8',
},
grayscale: {
base: '#666666',
dark1: '#323232',
dark2: '#000000',
light1: '#B2B2B2',
light2: '#E0E0E0',
light3: '#F0F0F0',
light4: '#F7F7F7',
light5: '#FFFFFF',
},
error: {
base: '#E04355',
dark1: '#A7323F',
dark2: '#6F212A',
light1: '#EFA1AA',
light2: '#FAEDEE',
},
warning: {
base: '#FCC700',
dark1: '#BC9501',
dark2: '#7D6300',
light1: '#FDE380',
light2: '#FEF9E6',
},
success: {
base: '#5AC189',
dark1: '#439066',
dark2: '#2B6144',
light1: '#ACE1C4',
light2: '#EEF8F3',
},
info: {
base: '#66BCFE',
dark1: '#4D8CBE',
dark2: '#315E7E',
light1: '#B3DEFE',
light2: '#EFF8FE',
},
},
opacity: {
light: '10%',
mediumLight: '35%',
mediumHeavy: '60%',
heavy: '80%',
},
typography: {
families: {
sansSerif: `'Inter', Helvetica, Arial`,
serif: `Georgia, 'Times New Roman', Times, serif`,
monospace: `'Fira Code', 'Courier New', monospace`,
},
weights: {
light: 200,
normal: 400,
medium: 500,
bold: 600,
},
sizes: {
xxs: 9,
xs: 10,
s: 12,
m: 14,
l: 16,
xl: 21,
xxl: 28,
},
},
zIndex: {
aboveDashboardCharts: 10,
dropdown: 11,
max: 3000,
},
transitionTiming: 0.3,
gridUnit: 4,
brandIconMaxWidth: 37,
};
export type SupersetTheme = typeof defaultTheme;
export interface SupersetThemeProps {
theme: SupersetTheme;
}
export const supersetTheme = defaultTheme;

View File

@@ -0,0 +1,77 @@
/**
* 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 { css, useTheme, Global } from '@emotion/react';
export const GlobalStyles = () => {
const theme = useTheme();
return (
<Global
key={`global-${theme.colorLink}`}
styles={css`
body {
background-color: ${theme.colorBgBase};
color: ${theme.colorText};
}
a {
color: ${theme.colorLink};
}
h1,
h2,
h3,
h4,
h5,
h6,
strong,
th {
font-weight: ${theme.fontWeightStrong};
}
.echarts-tooltip[style*='visibility: hidden'] {
display: none !important;
}
.antd5-dropdown,
.ant-dropdown,
.ant-select-dropdown,
.antd5-modal-wrap,
.antd5-modal-mask,
.antd5-picker-dropdown,
.ant-popover,
.antd5-popover {
z-index: ${theme.zIndexPopupBase} !important;
}
.column-config-popover {
& .antd5-input-number {
width: 100%;
}
&& .btn-group svg {
line-height: 0;
top: 0;
}
& .btn-group > .btn {
padding: 5px 10px 6px;
}
}
`}
/>
);
};

View File

@@ -0,0 +1,224 @@
/**
* 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 { theme as antdThemeImport } from 'antd-v5';
import { Theme } from './Theme';
import { AnyThemeConfig } from './types';
// Mock emotion's cache to avoid actual DOM operations
jest.mock('@emotion/cache', () => ({
__esModule: true,
default: jest.fn().mockReturnValue({}),
}));
describe('Theme', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('json', () => {
it('serializes the theme configuration to a JSON string', () => {
const theme = Theme.fromConfig({
token: {
colorPrimary: '#ff0000',
},
algorithm: antdThemeImport.darkAlgorithm,
});
const jsonString = theme.json();
const parsedJson = JSON.parse(jsonString);
expect(parsedJson.token?.colorPrimary).toBe('#ff0000');
expect(parsedJson.algorithm).toBe('dark');
});
});
describe('fromConfig', () => {
it('creates a theme with default tokens when no config is provided', () => {
const theme = Theme.fromConfig();
// Verify default primary color is set
expect(theme.theme.colorPrimary).toBe('#2893b3');
// Verify default font family is set
expect(theme.theme.fontFamily).toContain('Inter');
// Verify the theme is initialized with colors
expect(theme.theme.colors).toBeDefined();
});
it('creates a theme with custom tokens when provided', () => {
const customConfig: AnyThemeConfig = {
token: {
colorPrimary: '#ff0000',
fontFamily: 'CustomFont, sans-serif',
},
};
const theme = Theme.fromConfig(customConfig);
// Verify custom primary color is set
expect(theme.theme.colorPrimary).toBe('#ff0000');
// Verify custom font family is set
expect(theme.theme.fontFamily).toBe('CustomFont, sans-serif');
// But default tokens should still be preserved for unspecified values
expect(theme.theme.colorError).toBe('#e04355');
});
it('creates a theme with dark mode when dark algorithm is specified', () => {
const darkConfig: AnyThemeConfig = {
algorithm: antdThemeImport.darkAlgorithm,
};
const theme = Theme.fromConfig(darkConfig);
// Verify dark mode by using the serialized config from the public method
const serialized = theme.toSerializedConfig();
expect(serialized.algorithm).toBe('dark');
});
});
describe('setConfig', () => {
it('updates theme with a new configuration', () => {
const theme = Theme.fromConfig();
const initialPrimaryColor = theme.theme.colorPrimary;
// Update with new config
theme.setConfig({
token: {
colorPrimary: '#0000ff',
},
});
// Verify the theme was updated
expect(theme.theme.colorPrimary).toBe('#0000ff');
expect(theme.theme.colorPrimary).not.toBe(initialPrimaryColor);
});
it('preserves default tokens when updating with partial config', () => {
const theme = Theme.fromConfig();
const initialErrorColor = theme.theme.colorError;
// Update with new config that doesn't specify error color
theme.setConfig({
token: {
colorPrimary: '#0000ff',
},
});
// Verify the error color is preserved
expect(theme.theme.colorError).toBe(initialErrorColor);
});
it('correctly applies algorithm changes', () => {
const theme = Theme.fromConfig();
// Change to dark mode via config
theme.setConfig({
algorithm: antdThemeImport.darkAlgorithm,
});
// Verify the algorithm was updated
const serialized = theme.toSerializedConfig();
expect(serialized.algorithm).toBe('dark');
});
});
describe('toggleDarkMode', () => {
it('switches to dark algorithm when toggling dark mode on', () => {
const theme = Theme.fromConfig();
// Toggle dark mode on
theme.toggleDarkMode(true);
// Verify dark algorithm is used
const serialized = theme.toSerializedConfig();
expect(serialized.algorithm).toBe('dark');
});
it('switches to default algorithm when toggling dark mode off', () => {
// Start with dark theme
const theme = Theme.fromConfig({
algorithm: antdThemeImport.darkAlgorithm,
});
// Toggle dark mode off
theme.toggleDarkMode(false);
// Verify default algorithm is used
const serialized = theme.toSerializedConfig();
expect(serialized.algorithm).toBe('default');
});
it('preserves other algorithms when toggling dark mode', () => {
// Start with compact and dark algorithms
const theme = Theme.fromConfig({
algorithm: [
antdThemeImport.compactAlgorithm,
antdThemeImport.darkAlgorithm,
],
});
// Toggle dark mode off
theme.toggleDarkMode(false);
// Verify default algorithm replaces dark but compact is preserved
const serialized = theme.toSerializedConfig();
expect(Array.isArray(serialized.algorithm)).toBe(true);
expect(serialized.algorithm).toContain('default');
expect(serialized.algorithm).toContain('compact');
expect(serialized.algorithm).not.toContain('dark');
});
});
describe('getFontSize', () => {
it('returns correct font size for given key', () => {
const theme = Theme.fromConfig();
// Test different font size keys
expect(theme.getFontSize('xs')).toBe('8');
expect(theme.getFontSize('m')).toBeTruthy();
expect(theme.getFontSize('xxl')).toBe('28');
});
it('defaults to medium font size when no key is provided', () => {
const theme = Theme.fromConfig();
const mediumSize = theme.getFontSize('m');
expect(theme.getFontSize()).toBe(mediumSize);
});
});
describe('toSerializedConfig', () => {
it('serializes theme config correctly', () => {
const theme = Theme.fromConfig({
token: {
colorPrimary: '#ff0000',
},
algorithm: antdThemeImport.darkAlgorithm,
});
const serialized = theme.toSerializedConfig();
expect(serialized.token?.colorPrimary).toBe('#ff0000');
expect(serialized.algorithm).toBe('dark');
});
});
});

View File

@@ -0,0 +1,312 @@
/**
* 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.
*/
/* eslint-disable react-prefer-function-component/react-prefer-function-component */
// eslint-disable-next-line no-restricted-syntax
import React from 'react';
import { theme as antdThemeImport, ConfigProvider } from 'antd-v5';
import tinycolor from 'tinycolor2';
import {
ThemeProvider as EmotionThemeProvider,
CacheProvider as EmotionCacheProvider,
} from '@emotion/react';
import createCache from '@emotion/cache';
import { GlobalStyles } from './GlobalStyles';
import {
AntdThemeConfig,
AnyThemeConfig,
SerializableThemeConfig,
SupersetTheme,
allowedAntdTokens,
SharedAntdTokens,
ColorVariants,
DeprecatedThemeColors,
FontSizeKey,
} from './types';
import {
normalizeThemeConfig,
serializeThemeConfig,
getSystemColors,
getDeprecatedColors,
} from './utils';
/* eslint-disable theme-colors/no-literal-colors */
export class Theme {
theme: SupersetTheme;
private static readonly defaultTokens = {
// Brand
brandLogoAlt: 'Apache Superset',
brandLogoUrl: '/static/assets/images/superset-logo-horiz.png',
brandLogoMargin: '18px',
brandLogoHref: 'https:/supserset.apache.org',
brandLogoHeight: '24px',
// Default colors
colorPrimary: '#2893B3', // NOTE: previous lighter primary color was #20a7c9
colorLink: '#2893B3',
colorError: '#e04355',
colorWarning: '#fcc700',
colorSuccess: '#5ac189',
colorInfo: '#66bcfe',
// Forcing some default tokens
fontFamily: `'Inter', Helvetica, Arial`,
fontFamilyCode: `'Fira Code', 'Courier New', monospace`,
// Extra tokens
transitionTiming: 0.3,
brandIconMaxWidth: 37,
fontSizeXS: '8',
fontSizeXXL: '28',
fontWeightNormal: '400',
fontWeightLight: '300',
fontWeightMedium: '500',
};
private antdConfig: AntdThemeConfig;
private static readonly sizeMap: Record<FontSizeKey, string> = {
xs: 'fontSizeXS',
s: 'fontSizeSM',
m: 'fontSize',
l: 'fontSizeLG',
xl: 'fontSizeXL',
xxl: 'fontSizeXXL',
};
private constructor({ config }: { config?: AnyThemeConfig }) {
this.SupersetThemeProvider = this.SupersetThemeProvider.bind(this);
// Create a new config object with default tokens
const newConfig: AnyThemeConfig = config ? { ...config } : {};
// Ensure token property exists with defaults
newConfig.token = {
...Theme.defaultTokens,
...(config?.token || {}),
};
this.setConfig(newConfig);
}
/**
* Create a theme from any theme configuration
* Automatically handles both AntdThemeConfig and SerializableThemeConfig
* If simple tokens are provided as { token: {...} }, they will be applied with defaults
* If no config is provided, uses default tokens
* Dark mode can be set via the algorithm property in the config
*/
static fromConfig(config?: AnyThemeConfig): Theme {
return new Theme({ config });
}
private static getFilteredAntdTheme(
antdConfig: AntdThemeConfig,
): SharedAntdTokens {
// This method generates all antd tokens and filters out the ones not allowed
// in Superset
const theme = Theme.getAntdTokens(antdConfig);
return Object.fromEntries(
allowedAntdTokens.map(key => [key, (theme as Record<string, any>)[key]]),
) as SharedAntdTokens;
}
private static getAntdTokens(
antdConfig: AntdThemeConfig,
): Record<string, any> {
return antdThemeImport.getDesignToken(antdConfig);
}
/**
* Update the theme using any theme configuration
* Automatically handles both AntdThemeConfig and SerializableThemeConfig
* Dark mode should be specified via the algorithm property in the config
*/
setConfig(config: AnyThemeConfig): void {
const antdConfig = normalizeThemeConfig(config);
// Apply default tokens to token property
antdConfig.token = {
...Theme.defaultTokens,
...(antdConfig.token || {}),
};
// First phase: Let Ant Design compute the tokens
const tokens = Theme.getFilteredAntdTheme(antdConfig);
// Set the base theme properties
this.antdConfig = antdConfig;
this.theme = {
...Theme.defaultTokens,
...antdConfig.token, // Passing through the extra, superset-specific tokens
...tokens,
colors: {} as DeprecatedThemeColors, // Placeholder that will be filled in the second phase
};
// Second phase: Now that theme is initialized, we can determine if it's dark
// and generate the legacy colors correctly
const systemColors = getSystemColors(tokens);
const isDark = this.isThemeDark(); // Now we can safely call this
this.theme.colors = getDeprecatedColors(systemColors, isDark);
// Update the providers with the fully formed theme
this.updateProviders(
this.theme,
this.antdConfig,
createCache({ key: 'superset' }),
);
}
/**
* Export the current theme as a serializable configuration
*/
toSerializedConfig(): SerializableThemeConfig {
return serializeThemeConfig(this.antdConfig);
}
private getToken(token: string): any {
return (this.theme as Record<string, any>)[token];
}
public getFontSize(size?: FontSizeKey): string {
const fontSizeKey = Theme.sizeMap[size || 'm'];
return this.getToken(fontSizeKey) || this.getToken('fontSize');
}
/**
* Check if the current theme is dark based on background color
*/
isThemeDark(): boolean {
return tinycolor(this.theme.colorBgContainer).isDark();
}
toggleDarkMode(isDark: boolean): void {
// Create a new config based on the current one
const newConfig = { ...this.antdConfig };
// Determine the new algorithm based on isDark
const newAlgorithm = isDark
? antdThemeImport.darkAlgorithm
: antdThemeImport.defaultAlgorithm;
// Handle the case where algorithm is an array
if (Array.isArray(newConfig.algorithm)) {
// Filter out any existing dark/default algorithms
const otherAlgorithms = newConfig.algorithm.filter(
alg =>
alg !== antdThemeImport.darkAlgorithm &&
alg !== antdThemeImport.defaultAlgorithm,
);
// Add the new algorithm to the front of the array
newConfig.algorithm = [newAlgorithm, ...otherAlgorithms];
} else {
// Simple case: just replace the algorithm
newConfig.algorithm = newAlgorithm;
}
// Update the theme with the new configuration
this.setConfig(newConfig);
}
json(): string {
return JSON.stringify(serializeThemeConfig(this.antdConfig), null, 2);
}
getColorVariants(color: string): ColorVariants {
const firstLetterCapped = color.charAt(0).toUpperCase() + color.slice(1);
if (color === 'default' || color === 'grayscale') {
const isDark = this.isThemeDark();
const flipBrightness = (baseColor: string): string => {
if (!isDark) return baseColor;
const { r, g, b } = tinycolor(baseColor).toRgb();
const invertedColor = tinycolor({ r: 255 - r, g: 255 - g, b: 255 - b });
return invertedColor.toHexString();
};
return {
active: flipBrightness('#222'),
textActive: flipBrightness('#444'),
text: flipBrightness('#555'),
textHover: flipBrightness('#666'),
hover: flipBrightness('#888'),
borderHover: flipBrightness('#AAA'),
border: flipBrightness('#CCC'),
bgHover: flipBrightness('#DDD'),
bg: flipBrightness('#F4F4F4'),
};
}
const theme = this.getToken.bind(this);
return {
active: theme(`color${firstLetterCapped}Active`),
textActive: theme(`color${firstLetterCapped}TextActive`),
text: theme(`color${firstLetterCapped}Text`),
textHover: theme(`color${firstLetterCapped}TextHover`),
hover: theme(`color${firstLetterCapped}Hover`),
borderHover: theme(`color${firstLetterCapped}BorderHover`),
border: theme(`color${firstLetterCapped}Border`),
bgHover: theme(`color${firstLetterCapped}BgHover`),
bg: theme(`color${firstLetterCapped}Bg`),
};
}
private updateProviders(
theme: SupersetTheme,
antdConfig: AntdThemeConfig,
emotionCache: any,
): void {
// Overridden at runtime by SupersetThemeProvider using setThemeState
}
SupersetThemeProvider({ children }: { children: React.ReactNode }) {
if (!this.theme || !this.antdConfig) {
throw new Error('Theme is not initialized.');
}
// eslint-disable-next-line react-hooks/rules-of-hooks
const [themeState, setThemeState] = React.useState({
theme: this.theme,
antdConfig: this.antdConfig,
emotionCache: createCache({ key: 'superset' }),
});
this.updateProviders = (theme, antdConfig, emotionCache) => {
setThemeState({ theme, antdConfig, emotionCache });
};
return (
<EmotionCacheProvider value={themeState.emotionCache}>
<EmotionThemeProvider theme={themeState.theme}>
<GlobalStyles />
<ConfigProvider theme={themeState.antdConfig} prefixCls="antd5">
{children}
</ConfigProvider>
</EmotionThemeProvider>
</EmotionCacheProvider>
);
}
}
/* eslint-enable theme-colors/no-literal-colors */

View File

@@ -0,0 +1,61 @@
/**
* 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.
*/
/* eslint-disable theme-colors/no-literal-colors */
import { SerializableThemeConfig } from './types';
const exampleThemes: Record<string, SerializableThemeConfig> = {
superset: {
token: {
colorBgElevated: '#fafafa',
},
},
supersetDark: {
token: {},
algorithm: 'dark',
},
supersetCompact: {
token: {},
algorithm: 'compact',
},
funky: {
token: {
colorPrimary: '#f759ab', // hot pink
colorSuccess: '#52c41a',
colorWarning: '#faad14',
colorError: '#ff4d4f',
colorInfo: '#40a9ff',
borderRadius: 12,
fontFamily: 'Comic Sans MS, cursive',
},
algorithm: 'default',
},
funkyDark: {
token: {
colorPrimary: '#f759ab', // hot pink
colorSuccess: '#52c41a',
colorWarning: '#faad14',
colorError: '#ff4d4f',
colorInfo: '#40a9ff',
borderRadius: 12,
fontFamily: 'Comic Sans MS, cursive',
},
algorithm: 'dark',
},
};
export default exampleThemes;

View File

@@ -0,0 +1,61 @@
/**
* 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 emotionStyled from '@emotion/styled';
import { useTheme as useThemeBasic } from '@emotion/react';
// import { theme as antdThemeImport } from 'antd-v5';
import { Theme } from './Theme';
import type { SupersetTheme, SerializableThemeConfig } from './types';
export {
css,
keyframes,
jsx,
ThemeProvider,
CacheProvider as EmotionCacheProvider,
withTheme,
} from '@emotion/react';
export { default as createEmotionCache } from '@emotion/cache';
export { default as exampleThemes } from './exampleThemes';
declare module '@emotion/react' {
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface Theme extends SupersetTheme {}
}
export function useTheme() {
const theme = useThemeBasic();
// in the case there is no theme, useTheme returns an empty object
if (Object.keys(theme).length === 0 && theme.constructor === Object) {
throw new Error(
'useTheme() could not find a ThemeContext. The <ThemeProvider/> component is likely missing from the app.',
);
}
return theme;
}
const styled = emotionStyled;
// launching in in dark mode for now while iterating
const themeObject = Theme.fromConfig({});
const { theme } = themeObject;
const supersetTheme = theme;
export { Theme, themeObject, styled, theme, supersetTheme };
export type { SupersetTheme, SerializableThemeConfig };

View File

@@ -0,0 +1,367 @@
/**
* 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.
*/
/* eslint-disable @typescript-eslint/no-unused-vars */
// eslint-disable-next-line no-restricted-syntax
import { theme as antdThemeImport } from 'antd-v5';
/**
* Get AntdThemeConfig type from the theme object
*/
import type { ThemeConfig } from 'antd-v5';
/**
* Grab all antd tokens via getDesignToken(...).
* (Same as in the original file.)
*/
export type AntdTokens = ReturnType<typeof antdThemeImport.getDesignToken>;
export type AntdThemeConfig = ThemeConfig;
/**
* A serializable version of Ant Design's ThemeConfig
* Compatible with theme editor exports
*/
export type SerializableThemeConfig = {
token?: Record<string, any>;
components?: Record<string, any>;
algorithm?:
| 'default'
| 'dark'
| 'compact'
| ('default' | 'dark' | 'compact')[];
hashed?: boolean;
inherit?: boolean;
};
/**
* A combined type that can be either a regular AntdThemeConfig or a SerializableThemeConfig
*/
export type AnyThemeConfig = AntdThemeConfig | SerializableThemeConfig;
/** Minimal color system references. */
export type FontSizeKey = 'xs' | 's' | 'm' | 'l' | 'xl' | 'xxl';
export interface SystemColors {
colorPrimary: string;
colorError: string;
colorWarning: string;
colorSuccess: string;
colorInfo: string;
}
export interface ColorVariants {
bg: string;
border: string;
hover: string;
active: string;
textHover: string;
text: string;
borderHover: string;
bgHover: string;
textActive: string;
}
export interface DeprecatedColorVariations {
base: string;
light1: string;
light2: string;
light3: string;
light4: string;
light5: string;
dark1: string;
dark2: string;
dark3: string;
dark4: string;
dark5: string;
}
export interface DeprecatedThemeColors {
primary: DeprecatedColorVariations;
error: DeprecatedColorVariations;
warning: DeprecatedColorVariations;
success: DeprecatedColorVariations;
info: DeprecatedColorVariations;
grayscale: DeprecatedColorVariations;
}
export interface LegacySupersetTheme {
// Old colors structure with light/dark semantics still heavily referenced in code base
// TODO: replace/realign with antd-type tokens
colors: DeprecatedThemeColors;
transitionTiming: number;
}
export interface SupersetSpecificTokens {
// Brand-related
brandIconMaxWidth: number;
// Font-related
fontSizeXS: string;
fontSizeXXL: string;
fontWeightNormal: string;
fontWeightLight: string;
fontWeightMedium: string;
brandLogoAlt: string;
brandLogoUrl: string;
brandLogoMargin: string;
brandLogoHref: string;
brandLogoHeight: string;
}
/**
* This array is used to define which keys from the full Antd token set
* we actually allow in the SupersetTheme.
*/
export const allowedAntdTokens = [
'borderRadius',
'borderRadiusLG',
'borderRadiusOuter',
'borderRadiusSM',
'borderRadiusXS',
'boxShadow',
'boxShadowDrawerLeft',
'boxShadowDrawerRight',
'boxShadowDrawerUp',
'boxShadowPopoverArrow',
'boxShadowSecondary',
'boxShadowTabsOverflowBottom',
'boxShadowTabsOverflowLeft',
'boxShadowTabsOverflowRight',
'boxShadowTabsOverflowTop',
'boxShadowTertiary',
'colorError',
'colorErrorActive',
'colorErrorBg',
'colorErrorBgActive',
'colorErrorBgHover',
'colorErrorBorder',
'colorErrorBorderHover',
'colorErrorHover',
'colorErrorOutline',
'colorErrorText',
'colorErrorTextActive',
'colorErrorTextHover',
'colorPrimary',
'colorPrimaryActive',
'colorPrimaryBg',
'colorPrimaryBgHover',
'colorPrimaryBorder',
'colorPrimaryBorderHover',
'colorPrimaryHover',
'colorPrimaryText',
'colorPrimaryTextActive',
'colorPrimaryTextHover',
'colorSuccess',
'colorSuccessActive',
'colorSuccessBg',
'colorSuccessBgHover',
'colorSuccessBorder',
'colorSuccessBorderHover',
'colorSuccessHover',
'colorSuccessText',
'colorSuccessTextActive',
'colorSuccessTextHover',
'colorBgBase',
'colorBgBlur',
'colorBgContainer',
'colorBgContainerDisabled',
'colorBgElevated',
'colorBgLayout',
'colorBgMask',
'colorBgSpotlight',
'colorBgTextActive',
'colorBgTextHover',
'colorBorder',
'colorBorderBg',
'colorBorderSecondary',
'colorFill',
'colorFillAlter',
'colorFillContent',
'colorFillContentHover',
'colorFillQuaternary',
'colorFillSecondary',
'colorFillTertiary',
'colorHighlight',
'colorIcon',
'colorIconHover',
'colorInfo',
'colorInfoActive',
'colorInfoBg',
'colorInfoBgHover',
'colorInfoBorder',
'colorInfoBorderHover',
'colorInfoHover',
'colorInfoText',
'colorInfoTextActive',
'colorInfoTextHover',
'colorLink',
'colorLinkActive',
'colorLinkHover',
'colorSplit',
'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',
] as const;
/** We build a narrower type from these allowed keys. */
export type AllowedAntdTokenKeys = Extract<
(typeof allowedAntdTokens)[number],
keyof AntdTokens
>;
export type SharedAntdTokens = Pick<AntdTokens, AllowedAntdTokenKeys>;
/** The final shape for our custom theme object, combining old theme + shared antd + superset specifics. */
export type SupersetTheme = LegacySupersetTheme &
SharedAntdTokens &
SupersetSpecificTokens;

View File

@@ -0,0 +1,349 @@
/**
* 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 { theme as antdThemeImport } from 'antd-v5';
import {
isSerializableConfig,
deserializeThemeConfig,
serializeThemeConfig,
normalizeThemeConfig,
getAntdConfig,
getSystemColors,
getDeprecatedColors,
genDeprecatedColorVariations,
} from './utils';
import {
AnyThemeConfig,
SerializableThemeConfig,
AntdThemeConfig,
} from './types';
// Mock tinycolor2 for consistent testing
jest.mock('tinycolor2', () => {
const mockMix = jest.fn().mockImplementation(() => ({
toHexString: jest.fn().mockReturnValue('#mixed-color'),
}));
return {
mix: mockMix,
};
});
describe('Theme utilities', () => {
describe('isSerializableConfig', () => {
it('returns true when algorithm is undefined', () => {
const config: AnyThemeConfig = {
token: { colorPrimary: '#ff0000' },
};
expect(isSerializableConfig(config)).toBe(true);
});
it('returns true when algorithm is a string', () => {
const config: AnyThemeConfig = {
token: { colorPrimary: '#ff0000' },
algorithm: 'dark',
};
expect(isSerializableConfig(config)).toBe(true);
});
it('returns true when algorithm is an array of strings', () => {
const config: AnyThemeConfig = {
token: { colorPrimary: '#ff0000' },
algorithm: ['dark', 'compact'],
};
expect(isSerializableConfig(config)).toBe(true);
});
it('returns false when algorithm is a function', () => {
const config: AnyThemeConfig = {
token: { colorPrimary: '#ff0000' },
algorithm: antdThemeImport.darkAlgorithm,
};
expect(isSerializableConfig(config)).toBe(false);
});
it('returns false when algorithm is an array containing a function', () => {
const config: AnyThemeConfig = {
token: { colorPrimary: '#ff0000' },
// @ts-ignore
algorithm: [antdThemeImport.darkAlgorithm, 'compact'],
};
expect(isSerializableConfig(config)).toBe(false);
});
});
describe('deserializeThemeConfig', () => {
it('converts string algorithm to function reference', () => {
const config: SerializableThemeConfig = {
token: { colorPrimary: '#ff0000' },
algorithm: 'dark',
};
const result = deserializeThemeConfig(config);
expect(result.algorithm).toBe(antdThemeImport.darkAlgorithm);
});
it('converts array of string algorithms to function references', () => {
const config: SerializableThemeConfig = {
token: { colorPrimary: '#ff0000' },
algorithm: ['dark', 'compact'],
};
const result = deserializeThemeConfig(config);
expect(Array.isArray(result.algorithm)).toBe(true);
expect(result.algorithm).toContain(antdThemeImport.darkAlgorithm);
expect(result.algorithm).toContain(antdThemeImport.compactAlgorithm);
});
it('preserves other configuration properties', () => {
const config: SerializableThemeConfig = {
token: { colorPrimary: '#ff0000' },
algorithm: 'dark',
hashed: true,
};
const result = deserializeThemeConfig(config);
expect(result.token).toEqual({ colorPrimary: '#ff0000' });
expect(result.hashed).toBe(true);
});
it('handles undefined algorithm', () => {
const config: SerializableThemeConfig = {
token: { colorPrimary: '#ff0000' },
};
const result = deserializeThemeConfig(config);
expect(result.algorithm).toBeUndefined();
});
it('converts default algorithm string to function reference', () => {
const config: SerializableThemeConfig = {
token: { colorPrimary: '#ff0000' },
algorithm: 'default',
};
const result = deserializeThemeConfig(config);
expect(result.algorithm).toBe(antdThemeImport.defaultAlgorithm);
});
it('converts compact algorithm string to function reference', () => {
const config: SerializableThemeConfig = {
token: { colorPrimary: '#ff0000' },
algorithm: 'compact',
};
const result = deserializeThemeConfig(config);
expect(result.algorithm).toBe(antdThemeImport.compactAlgorithm);
});
});
describe('serializeThemeConfig', () => {
it('converts function algorithm to string', () => {
const config: AntdThemeConfig = {
token: { colorPrimary: '#ff0000' },
algorithm: antdThemeImport.darkAlgorithm,
};
const result = serializeThemeConfig(config);
expect(result.algorithm).toBe('dark');
});
it('converts array of function algorithms to strings', () => {
const config: AntdThemeConfig = {
token: { colorPrimary: '#ff0000' },
algorithm: [
antdThemeImport.darkAlgorithm,
antdThemeImport.compactAlgorithm,
],
};
const result = serializeThemeConfig(config);
expect(Array.isArray(result.algorithm)).toBe(true);
expect(result.algorithm).toContain('dark');
expect(result.algorithm).toContain('compact');
});
it('preserves other configuration properties', () => {
const config: AntdThemeConfig = {
token: { colorPrimary: '#ff0000' },
algorithm: antdThemeImport.darkAlgorithm,
hashed: true,
};
const result = serializeThemeConfig(config);
expect(result.token).toEqual({ colorPrimary: '#ff0000' });
expect(result.hashed).toBe(true);
});
it('handles undefined algorithm', () => {
const config: AntdThemeConfig = {
token: { colorPrimary: '#ff0000' },
};
const result = serializeThemeConfig(config);
expect(result.algorithm).toBeUndefined();
});
it('defaults to "default" for unknown algorithms', () => {
const unknownAlgorithm = () => ({});
const config: AntdThemeConfig = {
token: { colorPrimary: '#ff0000' },
// @ts-ignore
algorithm: unknownAlgorithm,
};
const result = serializeThemeConfig(config);
expect(result.algorithm).toBe('default');
});
it('converts default algorithm function to string', () => {
const config: AntdThemeConfig = {
token: { colorPrimary: '#ff0000' },
algorithm: antdThemeImport.defaultAlgorithm,
};
const result = serializeThemeConfig(config);
expect(result.algorithm).toBe('default');
});
it('converts compact algorithm function to string', () => {
const config: AntdThemeConfig = {
token: { colorPrimary: '#ff0000' },
algorithm: antdThemeImport.compactAlgorithm,
};
const result = serializeThemeConfig(config);
expect(result.algorithm).toBe('compact');
});
it('defaults each unknown algorithm in array to "default"', () => {
const unknownAlgorithm = () => ({});
const config: AntdThemeConfig = {
token: { colorPrimary: '#ff0000' },
// @ts-ignore
algorithm: [antdThemeImport.darkAlgorithm, unknownAlgorithm],
};
const result = serializeThemeConfig(config);
expect(Array.isArray(result.algorithm)).toBe(true);
expect(result.algorithm).toEqual(['dark', 'default']);
});
it('handles mixed known and unknown algorithms in array', () => {
const unknownAlgorithm1 = () => ({});
const unknownAlgorithm2 = () => ({});
const config: AntdThemeConfig = {
token: { colorPrimary: '#ff0000' },
algorithm: [
antdThemeImport.darkAlgorithm,
// @ts-ignore
unknownAlgorithm1,
antdThemeImport.compactAlgorithm,
// @ts-ignore
unknownAlgorithm2,
],
};
const result = serializeThemeConfig(config);
expect(Array.isArray(result.algorithm)).toBe(true);
expect(result.algorithm).toEqual([
'dark',
'default',
'compact',
'default',
]);
});
});
describe('normalizeThemeConfig', () => {
it('returns the same config for non-serializable configs', () => {
const config: AntdThemeConfig = {
token: { colorPrimary: '#ff0000' },
algorithm: antdThemeImport.darkAlgorithm,
};
const result = normalizeThemeConfig(config);
expect(result).toBe(config);
});
it('deserializes serializable configs', () => {
const config: SerializableThemeConfig = {
token: { colorPrimary: '#ff0000' },
algorithm: 'dark',
};
const result = normalizeThemeConfig(config);
expect(result.algorithm).toBe(antdThemeImport.darkAlgorithm);
});
});
describe('getAntdConfig', () => {
it('returns config with default algorithm for light mode', () => {
const seed = { colorPrimary: '#ff0000' };
const result = getAntdConfig(seed, false);
expect(result.token).toBe(seed);
expect(result.algorithm).toBe(antdThemeImport.defaultAlgorithm);
});
it('returns config with dark algorithm for dark mode', () => {
const seed = { colorPrimary: '#ff0000' };
const result = getAntdConfig(seed, true);
expect(result.token).toBe(seed);
expect(result.algorithm).toBe(antdThemeImport.darkAlgorithm);
});
});
describe('getSystemColors', () => {
it('extracts system colors from tokens', () => {
const tokens = {
colorPrimary: '#primary',
colorError: '#error',
colorWarning: '#warning',
colorSuccess: '#success',
colorInfo: '#info',
otherToken: 'ignore-me',
};
const result = getSystemColors(tokens);
expect(result).toEqual({
colorPrimary: '#primary',
colorError: '#error',
colorWarning: '#warning',
colorSuccess: '#success',
colorInfo: '#info',
});
});
});
describe('genDeprecatedColorVariations', () => {
it('generates color variations for light mode', () => {
const result = genDeprecatedColorVariations('#base-color', false);
expect(result.base).toBe('#base-color');
expect(result.light1).toBe('#mixed-color');
expect(result.dark1).toBe('#mixed-color');
});
it('generates color variations for dark mode', () => {
const result = genDeprecatedColorVariations('#base-color', true);
expect(result.base).toBe('#base-color');
expect(result.light1).toBe('#mixed-color');
expect(result.dark1).toBe('#mixed-color');
});
});
describe('getDeprecatedColors', () => {
it('generates deprecated colors from system colors', () => {
const systemColors = {
colorPrimary: '#primary',
colorError: '#error',
colorWarning: '#warning',
colorSuccess: '#success',
colorInfo: '#info',
};
const result = getDeprecatedColors(systemColors, false);
expect(result.primary.base).toBe('#primary');
expect(result.error.base).toBe('#error');
expect(result.warning.base).toBe('#warning');
expect(result.success.base).toBe('#success');
expect(result.info.base).toBe('#info');
expect(result.grayscale.base).toBe('#666');
});
});
});

View File

@@ -0,0 +1,189 @@
/**
* 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 { theme as antdThemeImport } from 'antd-v5';
import tinycolor from 'tinycolor2';
import {
AntdThemeConfig,
AnyThemeConfig,
SerializableThemeConfig,
DeprecatedColorVariations,
DeprecatedThemeColors,
SystemColors,
SupersetTheme,
} from './types';
/**
* Check if a theme config is serializable by detecting string-based algorithm
*/
export function isSerializableConfig(
config: AnyThemeConfig,
): config is SerializableThemeConfig {
const { algorithm } = config;
if (algorithm === undefined) return true;
if (Array.isArray(algorithm)) {
return (algorithm as unknown[]).every(alg => typeof alg === 'string');
}
return typeof algorithm === 'string';
}
/**
* Converts a serializable theme config to an Ant Design ThemeConfig
*/
export function deserializeThemeConfig(
config: SerializableThemeConfig,
): AntdThemeConfig {
const { algorithm, ...rest } = config;
const algorithmMap: Record<string, any> = {
default: antdThemeImport.defaultAlgorithm,
dark: antdThemeImport.darkAlgorithm,
compact: antdThemeImport.compactAlgorithm,
};
let resolvedAlgorithm;
if (Array.isArray(algorithm)) {
resolvedAlgorithm = algorithm.map(alg => algorithmMap[alg]);
} else if (algorithm) {
resolvedAlgorithm = algorithmMap[algorithm];
}
return {
...rest,
algorithm: resolvedAlgorithm,
};
}
/**
* Converts an Ant Design ThemeConfig to a serializable format
*/
export function serializeThemeConfig(
config: AntdThemeConfig,
): SerializableThemeConfig {
const { algorithm, ...rest } = config;
let serializedAlgorithm: SerializableThemeConfig['algorithm'];
if (Array.isArray(algorithm)) {
serializedAlgorithm = algorithm.map(alg => {
if (alg === antdThemeImport.defaultAlgorithm) return 'default';
if (alg === antdThemeImport.darkAlgorithm) return 'dark';
if (alg === antdThemeImport.compactAlgorithm) return 'compact';
return 'default'; // Fallback
}) as ('default' | 'dark' | 'compact')[];
} else if (algorithm) {
if (algorithm === antdThemeImport.defaultAlgorithm)
serializedAlgorithm = 'default';
else if (algorithm === antdThemeImport.darkAlgorithm)
serializedAlgorithm = 'dark';
else if (algorithm === antdThemeImport.compactAlgorithm)
serializedAlgorithm = 'compact';
else serializedAlgorithm = 'default'; // Fallback
}
return {
...rest,
algorithm: serializedAlgorithm,
};
}
/**
* Normalize any theme config to a standard AntdThemeConfig
* This automatically detects and converts serializable configs
*/
export function normalizeThemeConfig(config: AnyThemeConfig): AntdThemeConfig {
if (isSerializableConfig(config)) {
return deserializeThemeConfig(config);
}
return config as AntdThemeConfig;
}
/**
* Creates default Ant Design configuration from tokens and dark mode preference
*/
export function getAntdConfig(
seed: Partial<SupersetTheme>,
isDark: boolean,
): AntdThemeConfig {
const algorithm = isDark
? antdThemeImport.darkAlgorithm
: antdThemeImport.defaultAlgorithm;
return {
token: seed,
algorithm,
};
}
/**
* Generate deprecated color variations from a base color
*/
export function genDeprecatedColorVariations(
color: string,
isDark: boolean,
): DeprecatedColorVariations {
const bg = isDark ? '#FFF' : '#000';
const fg = isDark ? '#000' : '#FFF';
const adjustColor = (c: string, perc: number, tgt: string): string =>
tinycolor.mix(c, tgt, perc).toHexString();
return {
base: color,
light1: adjustColor(color, 20, fg),
light2: adjustColor(color, 45, fg),
light3: adjustColor(color, 70, fg),
light4: adjustColor(color, 90, fg),
light5: adjustColor(color, 95, fg),
dark1: adjustColor(color, 10, bg),
dark2: adjustColor(color, 20, bg),
dark3: adjustColor(color, 40, bg),
dark4: adjustColor(color, 60, bg),
dark5: adjustColor(color, 80, bg),
};
}
/**
* Generate deprecated theme colors from system colors
*/
export function getDeprecatedColors(
systemColors: SystemColors,
isDark: boolean,
): DeprecatedThemeColors {
const sc = systemColors;
return {
primary: genDeprecatedColorVariations(sc.colorPrimary, isDark),
error: genDeprecatedColorVariations(sc.colorError, isDark),
warning: genDeprecatedColorVariations(sc.colorWarning, isDark),
success: genDeprecatedColorVariations(sc.colorSuccess, isDark),
info: genDeprecatedColorVariations(sc.colorInfo, isDark),
grayscale: genDeprecatedColorVariations('#666', isDark),
};
}
/**
* Extract system colors from Ant Design tokens
*/
export function getSystemColors(antdTokens: Record<string, any>): SystemColors {
return {
colorPrimary: antdTokens.colorPrimary,
colorError: antdTokens.colorError,
colorWarning: antdTokens.colorWarning,
colorSuccess: antdTokens.colorSuccess,
colorInfo: antdTokens.colorInfo,
};
}

View File

@@ -58,6 +58,7 @@ export enum FeatureFlag {
SlackEnableAvatars = 'SLACK_ENABLE_AVATARS',
EnableDashboardScreenshotEndpoints = 'ENABLE_DASHBOARD_SCREENSHOT_ENDPOINTS',
EnableDashboardDownloadWebDriverScreenshot = 'ENABLE_DASHBOARD_DOWNLOAD_WEBDRIVER_SCREENSHOT',
DarkThemeSwitch = 'DARK_THEME_SWITCH',
}
export type ScheduleQueriesProps = {

View File

@@ -20,7 +20,7 @@
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { FallbackProps } from 'react-error-boundary';
import { ThemeProvider, supersetTheme } from '../../../src/style';
import { ThemeProvider, supersetTheme } from '../../../src/theme';
import FallbackComponent from '../../../src/chart/components/FallbackComponent';

View File

@@ -19,7 +19,7 @@
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { ThemeProvider, supersetTheme } from '../../../src/style';
import { ThemeProvider, supersetTheme } from '../../../src/theme';
import NoResultsComponent from '../../../src/chart/components/NoResultsComponent';
const renderNoResultsComponent = () =>

View File

@@ -106,19 +106,12 @@ describe('reactify(renderFn)', () => {
describe('propTypes', () => {
it('has propTypes if renderFn.propTypes is defined', () => {
/* eslint-disable-next-line react/forbid-foreign-prop-types */
expect(Object.keys(TheChart.propTypes ?? {})).toEqual([
'id',
'className',
'content',
]);
expect(Object.keys(TheChart.propTypes ?? {})).toEqual(['content']);
});
it('does not have propTypes if renderFn.propTypes is not defined', () => {
const AnotherChart = reactify(() => {});
/* eslint-disable-next-line react/forbid-foreign-prop-types */
expect(Object.keys(AnotherChart.propTypes ?? {})).toEqual([
'id',
'className',
]);
expect(Object.keys(AnotherChart.propTypes ?? {})).toEqual([]);
});
});
describe('defaultProps', () => {

View File

@@ -1,72 +0,0 @@
/*
* 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 {
styled,
supersetTheme,
SupersetThemeProps,
useTheme,
ThemeProvider,
EmotionCacheProvider,
emotionCache,
} from '@superset-ui/core';
import { render } from '@testing-library/react';
describe('@superset-ui/style package', () => {
it('exports a theme', () => {
expect(typeof supersetTheme).toBe('object');
});
it('exports styled component templater', () => {
expect(typeof styled.div).toBe('function');
});
it('exports SupersetThemeProps', () => {
const props: SupersetThemeProps = {
theme: supersetTheme,
};
expect(typeof props).toBe('object');
});
describe('useTheme()', () => {
it('returns the theme', () => {
function ThemeUser() {
expect(useTheme()).toStrictEqual(supersetTheme);
return <div>test</div>;
}
render(<ThemeUser />, {
wrapper: ({ children }) => (
<EmotionCacheProvider value={emotionCache}>
<ThemeProvider theme={supersetTheme}>{children}</ThemeProvider>
</EmotionCacheProvider>
),
});
});
it('throws when a theme is not present', () => {
function ThemeUser() {
expect(useTheme).toThrow(/could not find a ThemeContext/);
return <div>test</div>;
}
render(<ThemeUser />, {
wrapper: ({ children }) => <div>{children}</div>,
});
});
});
});

View File

@@ -18,6 +18,7 @@
*/
import { Component, ReactNode } from 'react';
import { Button } from 'antd-v5';
export type Props = {
children: ReactNode;
@@ -45,13 +46,9 @@ export default class Expandable extends Component<Props, State> {
return (
<div>
<button
type="button"
className="btn btn-primary btn-sm"
onClick={this.handleToggle}
>
<Button type="primary" size="small" onClick={this.handleToggle}>
{`${open ? 'Hide' : 'Show'} ${expandableWhat}`}
</button>
</Button>
<br />
<br />
{open ? children : null}

View File

@@ -25,6 +25,7 @@ import {
SupersetApiError,
t,
} from '@superset-ui/core';
import { Button } from 'antd-v5';
import ErrorMessage from './ErrorMessage';
export type Props = {
@@ -117,13 +118,9 @@ export default class VerifyCORS extends Component<Props, State> {
3) click below to verify authentication. You may debug CORS further
using the `@superset-ui/connection` story. <br />
<br />
<button
type="button"
className="btn btn-primary btn-sm"
onClick={this.handleVerify}
>
<Button type="primary" size="small" onClick={this.handleVerify}>
{t('Verify')}
</button>
</Button>
<br />
<br />
</div>

View File

@@ -0,0 +1,91 @@
/**
* 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 {
Layout,
Menu,
Button,
Card,
Alert,
Input,
Table,
Space,
} from 'antd-v5';
// eslint-disable-next-line import/no-extraneous-dependencies
import { Icons } from 'src/components/Icons';
const { Header, Content, Sider } = Layout;
export default {
title: 'Example/ExampleApp',
};
export const KitchenSink = () => {
const columns = [
{ title: 'Name', dataIndex: 'name' },
{ title: 'Age', dataIndex: 'age' },
{ title: 'Address', dataIndex: 'address' },
];
const data = [
{ key: 1, name: 'John Brown', age: 32, address: 'New York' },
{ key: 2, name: 'Jim Green', age: 42, address: 'London' },
{ key: 3, name: 'Joe Black', age: 28, address: 'Sydney' },
];
return (
<Layout style={{ minHeight: '100vh' }}>
<Header style={{ color: 'white', fontSize: 18 }}>My App</Header>
<Layout>
<Sider width={200}>
<Menu
mode="inline"
defaultSelectedKeys={['1']}
style={{ height: '100%' }}
items={[
{ key: '1', icon: <Icons.UserOutlined />, label: 'Users' },
{ key: '2', icon: <Icons.BookOutlined />, label: 'Devices' },
{ key: '3', icon: <Icons.CheckCircleFilled />, label: 'Alerts' },
]}
/>
</Sider>
<Layout style={{ padding: '24px' }}>
<Content>
<Space direction="vertical" size="large" style={{ width: '100%' }}>
<Alert
message="Welcome"
description="You are logged in."
type="info"
/>
<Card title="Quick Actions">
<Space>
<Button type="primary">Create</Button>
<Button>Settings</Button>
<Input placeholder="Search..." />
</Space>
</Card>
<Card title="User Table">
<Table columns={columns} dataSource={data} pagination={false} />
</Card>
</Space>
</Content>
</Layout>
</Layout>
</Layout>
);
};

View File

@@ -5539,5 +5539,5 @@ export const generateData = (theme: SupersetTheme) => ({
],
renderWhileDragging: true,
tooltip: null,
color: theme.colors.primary.base,
color: theme.colorPrimary,
});

View File

@@ -81,7 +81,7 @@ export const payload = theme => ({
data: {
features: [
{
color: theme.colors.primary.base,
color: theme.colorPrimary,
path: [
[-122.3535851, 37.9360513],
[-122.3179784, 37.9249513],
@@ -108,10 +108,10 @@ export const payload = theme => ({
[-122.3876274, 37.5993171],
],
__timestamp: null,
extraProps: { color: theme.colors.primary.base },
extraProps: { color: theme.colorPrimary },
},
{
color: theme.colors.warning.base,
color: theme.colorWarning,
path: [
[-122.353165, 37.936887],
[-122.317269, 37.925655],
@@ -133,10 +133,10 @@ export const payload = theme => ({
[-121.9772135, 37.5567286],
],
__timestamp: null,
extraProps: { color: theme.colors.warning.base },
extraProps: { color: theme.colorWarning },
},
{
color: theme.colors.error.base,
color: theme.colorError,
path: [
[-121.945154, 38.018914],
[-122.024597, 38.003275],
@@ -166,10 +166,10 @@ export const payload = theme => ({
[-122.38666, 37.599787],
],
__timestamp: null,
extraProps: { color: theme.colors.error.base },
extraProps: { color: theme.colorError },
},
{
color: theme.colors.success.base,
color: theme.colorSuccess,
path: [
[-121.900367, 37.701695],
[-121.928099, 37.699759],
@@ -191,10 +191,10 @@ export const payload = theme => ({
[-122.469081, 37.706121],
],
__timestamp: null,
extraProps: { color: theme.colors.success.base },
extraProps: { color: theme.colorSuccess },
},
{
color: theme.colors.warning.base,
color: theme.colorWarning,
path: [
[-121.9764, 37.557355],
[-122.017867, 37.591208],
@@ -217,7 +217,7 @@ export const payload = theme => ({
[-122.4683093, 37.705461],
],
__timestamp: null,
extraProps: { color: theme.colors.warning.base },
extraProps: { color: theme.colorWarning },
},
],
mapboxApiKey:

View File

@@ -1,127 +0,0 @@
/*
* 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 { supersetTheme, SupersetTheme } from '@superset-ui/core';
type ColorCategory = keyof SupersetTheme['colors'];
export default {
title: 'Core Packages/@superset-ui-style',
};
export const ThemeColors = () => {
const { colors } = supersetTheme;
// Define tones to be displayed in columns
const tones = [
'dark2',
'dark1',
'base',
'light1',
'light2',
'light3',
'light4',
'light5',
];
const colorTypes = [
'primary',
'secondary',
'grayscale',
'error',
'warning',
'alert',
'success',
'info',
];
return (
<div>
<h1>Theme Colors</h1>
<table
style={{ borderCollapse: 'collapse', width: '100%', textAlign: 'left' }}
>
<thead>
<tr>
<th style={{ border: '1px solid #ddd', padding: '8px' }}>
Category
</th>
{tones.map(tone => (
<th
key={tone}
style={{ border: '1px solid #ddd', padding: '8px' }}
>
{tone}
</th>
))}
</tr>
</thead>
<tbody>
{colorTypes.map(category => (
<tr key={category}>
<td style={{ border: '1px solid #ddd', padding: '8px' }}>
<strong>{category}</strong>
</td>
{tones.map(tone => {
const categoryColors = colors[category as ColorCategory];
const color =
typeof categoryColors === 'object' &&
categoryColors !== null &&
tone in categoryColors
? (categoryColors as Record<string, string>)[tone]
: undefined;
return (
<td
key={tone}
style={{
border: '1px solid #ddd',
padding: '8px',
backgroundColor: color || '#fff',
}}
>
{color ? <code>{color}</code> : '-'}
</td>
);
})}
</tr>
))}
</tbody>
</table>
<h3>
text.label: <code>{colors.text.label}</code>
</h3>
<div style={{ color: `#${colors.text.label}` }}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat.
</div>
<h3>
text.help: <code>{colors.text.help}</code>
</h3>
<div style={{ color: `#${colors.text.help}` }}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat.
</div>
<h3>The supersetTheme object</h3>
<code>
<pre>{JSON.stringify(supersetTheme, null, 2)}</pre>
</code>
</div>
);
};

View File

@@ -0,0 +1,176 @@
/*
* 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 { supersetTheme, themeObject } from '@superset-ui/core';
const colorTypes = [
'primary',
'error',
'warning',
'success',
'info',
'grayscale',
];
const AntDFunctionalColors = ({ antdTheme }) => {
const { antd } = supersetTheme;
// Define color types and variants dynamically
const variants = [
'active',
'textActive',
'text',
'textHover',
'hover',
'borderHover',
'border',
'bgHover',
'bg',
];
const { colors } = supersetTheme;
return (
<table
style={{ borderCollapse: 'collapse', width: '100%', textAlign: 'left' }}
>
<thead>
<tr>
<th style={{ border: '1px solid #ddd', padding: '8px' }}>Type</th>
{variants.map(variant => (
<th
key={variant}
style={{ border: '1px solid #ddd', padding: '8px' }}
>
{variant}
</th>
))}
</tr>
</thead>
<tbody>
{colorTypes.map(type => {
const typeKey = `color${type}`;
return (
<tr key={type}>
<td style={{ border: '1px solid #ddd', padding: '8px' }}>
<strong>{type}</strong>
</td>
{variants.map(variant => {
const color = themeObject.getColorVariants(type)[variant];
return (
<td
key={variant}
style={{
border: '1px solid #ddd',
padding: '8px',
backgroundColor: color || 'transparent',
color: [`color${type}${variant}`],
}}
>
{color ? <code>{color}</code> : '-'}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
);
};
export const ThemeColors = () => {
const { colors } = supersetTheme;
// Define tones to be displayed in columns
const tones = [
'dark5',
'dark4',
'dark3',
'dark1',
'base',
'light1',
'light2',
'light3',
'light4',
'light5',
];
return (
<div>
<h1>Theme Colors</h1>
<h2>Legacy Theme Colors</h2>
<table
style={{ borderCollapse: 'collapse', width: '100%', textAlign: 'left' }}
>
<thead>
<tr>
<th style={{ border: '1px solid #ddd', padding: '8px' }}>
Category
</th>
{tones.map(tone => (
<th
key={tone}
style={{ border: '1px solid #ddd', padding: '8px' }}
>
{tone}
</th>
))}
</tr>
</thead>
<tbody>
{colorTypes.map(category => (
<tr key={category}>
<td style={{ border: '1px solid #ddd', padding: '8px' }}>
<strong>{category}</strong>
</td>
{tones.map(tone => {
const color = colors[category][tone];
return (
<td
key={tone}
style={{
border: '1px solid #ddd',
padding: '8px',
backgroundColor: color || '#fff',
}}
>
{color ? <code>{color}</code> : '-'}
</td>
);
})}
</tr>
))}
</tbody>
</table>
<h2>Ant Design Theme Colors</h2>
<h3>Functional Colors</h3>
<AntDFunctionalColors antdTheme={supersetTheme.antd} />
<h2>The supersetTheme object</h2>
<pre>
<code>{JSON.stringify(supersetTheme, null, 2)}</code>
</pre>
</div>
);
};
/*
* */
export default {
title: 'Core Packages/@superset-ui-theme',
};
export const Default = () => <ThemeColors />;

View File

@@ -31,19 +31,19 @@ const Calendar = ({ className, ...otherProps }) => {
styles={css`
.d3-tip {
line-height: 1;
padding: ${theme.gridUnit * 3}px;
padding: ${theme.sizeUnit * 3}px;
background: ${theme.colors.grayscale.dark2};
color: ${theme.colors.grayscale.light5};
border-radius: 4px;
pointer-events: none;
z-index: 1000;
font-size: ${theme.typography.sizes.s}px;
font-size: ${theme.fontSizeSM}px;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: ${theme.typography.sizes.xs};
font-size: ${theme.fontSizeXS};
width: 100%;
line-height: 1;
color: ${theme.colors.grayscale.dark2};
@@ -53,7 +53,7 @@ const Calendar = ({ className, ...otherProps }) => {
/* Northward tooltips */
.d3-tip.n:after {
content: '\\25BC';
margin: -${theme.gridUnit}px 0 0 0;
margin: -${theme.sizeUnit}px 0 0 0;
top: 100%;
left: 0;
text-align: center;
@@ -61,22 +61,22 @@ const Calendar = ({ className, ...otherProps }) => {
/* Eastward tooltips */
.d3-tip.e:after {
content: '\\25C0';
margin: -${theme.gridUnit}px 0 0 0;
margin: -${theme.sizeUnit}px 0 0 0;
top: 50%;
left: -${theme.gridUnit * 2}px;
left: -${theme.sizeUnit * 2}px;
}
/* Southward tooltips */
.d3-tip.s:after {
content: '\\25B2';
margin: 0;
top: -${theme.gridUnit * 2}px;
top: -${theme.sizeUnit * 2}px;
left: 0;
text-align: center;
}
/* Westward tooltips */
.d3-tip.w:after {
content: '\\25B6';
margin: -${theme.gridUnit}px 0 0 0px;
margin: -${theme.sizeUnit}px 0 0 0px;
top: 50%;
left: 100%;
}
@@ -99,19 +99,19 @@ Calendar.propTypes = {
export default styled(Calendar)`
${({ theme }) => `
.superset-legacy-chart-calendar {
padding: ${theme.gridUnit * 3}px;
padding: ${theme.sizeUnit * 3}px;
position: static !important;
overflow: auto !important;
}
.superset-legacy-chart-calendar .ch-tooltip {
margin-left: ${theme.gridUnit * 5}px;
margin-top: ${theme.gridUnit}px;
margin-left: ${theme.sizeUnit * 5}px;
margin-top: ${theme.sizeUnit}px;
}
.superset-legacy-chart-calendar .d3-tip {
line-height: 1;
padding: ${theme.gridUnit * 3}px;
padding: ${theme.sizeUnit * 3}px;
background: ${theme.colors.grayscale.dark2};
color: ${theme.colors.grayscale.light5};
border-radius: ${theme.borderRadius}px;
@@ -125,7 +125,7 @@ export default styled(Calendar)`
.cal-heatmap-container .graph-label {
fill: ${theme.colors.grayscale.base};
font-size: ${theme.typography.sizes.xs}px;
font-size: ${theme.fontSizeXS}px;
}
.cal-heatmap-container .graph,
@@ -143,7 +143,7 @@ export default styled(Calendar)`
}
.cal-heatmap-container .subdomain-text {
font-size: ${theme.typography.sizes.xs}px;
font-size: ${theme.fontSizeXS}px;
pointer-events: none;
}
@@ -172,8 +172,8 @@ export default styled(Calendar)`
}
.cal-heatmap-container .q4 {
background-color: ${theme.colors.success.base};
fill: ${theme.colors.success.base};
background-color: ${theme.colorSuccess};
fill: ${theme.colorSuccess};
}
.cal-heatmap-container .q5 {
@@ -182,21 +182,21 @@ export default styled(Calendar)`
}
.cal-heatmap-container rect.highlight {
stroke: ${theme.colors.grayscale.dark1};
stroke: ${theme.colorText};
stroke-width: 1;
}
.cal-heatmap-container text.highlight {
fill: ${theme.colors.grayscale.dark1};
fill: ${theme.colorText};
}
.cal-heatmap-container rect.highlight-now {
stroke: ${theme.colors.error.base};
stroke: ${theme.colorError};
}
.cal-heatmap-container text.highlight-now {
fill: ${theme.colors.error.base};
font-weight: ${theme.typography.weights.bold};
fill: ${theme.colorError};
font-weight: ${theme.fontWeightStrong};
}
.cal-heatmap-container .domain-background {
@@ -205,10 +205,10 @@ export default styled(Calendar)`
}
.ch-tooltip {
padding: ${theme.gridUnit * 2}px;
background: ${theme.colors.grayscale.dark1};
padding: ${theme.sizeUnit * 2}px;
background: ${theme.colorText};
color: ${theme.colors.grayscale.light1};
font-size: ${theme.typography.sizes.s}px;
font-size: ${theme.fontSizeSM}px;
line-height: 1.4;
width: 140px;
position: absolute;
@@ -229,11 +229,11 @@ export default styled(Calendar)`
content: '';
padding: 0;
display: block;
bottom: -${theme.gridUnit}px;
bottom: -${theme.sizeUnit}px;
left: 50%;
margin-left: -${theme.gridUnit}px;
border-width: ${theme.gridUnit}px ${theme.gridUnit}px 0;
border-top-color: ${theme.colors.grayscale.dark1};
margin-left: -${theme.sizeUnit}px;
border-width: ${theme.sizeUnit}px ${theme.sizeUnit}px 0;
border-top-color: ${theme.colorSplit};
}
`}
`;

View File

@@ -44,14 +44,14 @@ export default styled(Chord)`
pointer-events: all;
}
.superset-legacy-chart-chord svg .group path {
fill-opacity: ${theme.opacity.mediumHeavy};
fill-opacity: 60%;
}
.superset-legacy-chart-chord svg path.chord {
stroke: ${theme.colors.grayscale.dark2};
stroke: ${theme.colorText};
stroke-width: 0.25px;
}
.superset-legacy-chart-chord svg #circle:hover path.fade {
opacity: ${theme.opacity.light};
opacity: 10%;
}
`}
`;

View File

@@ -52,19 +52,19 @@ export default styled(CountryMap)`
}
.superset-legacy-chart-country-map .text-layer {
color: ${theme.colors.grayscale.dark1};
color: ${theme.colorText};
text-anchor: middle;
pointer-events: none;
}
.superset-legacy-chart-country-map text.result-text {
font-weight: ${theme.typography.weights.light};
font-size: ${theme.typography.sizes.xl}px;
font-weight: ${theme.fontWeightLight};
font-size: ${theme.fontSizeXL}px;
}
.superset-legacy-chart-country-map text.big-text {
font-weight: ${theme.typography.weights.bold};
font-size: ${theme.typography.sizes.l}px;
font-weight: ${theme.fontWeightStrong};
font-size: ${theme.fontSizeLG}px;
}
.superset-legacy-chart-country-map path.region {

View File

@@ -73,8 +73,8 @@ const StyledDiv = styled.div`
.superset-legacy-chart-horizon .horizon-row span.title {
position: absolute;
color: ${theme.colors.grayscale.dark1};
font-size: ${theme.typography.sizes.s}px;
color: ${theme.colorText};
font-size: ${theme.fontSizeSM}px;
margin: 0;
}
`}

View File

@@ -46,8 +46,8 @@ const StyledDiv = styled.div`
}
.paired-ttest-table .scrollbar-content {
padding-left: ${theme.gridUnit}px;
padding-right: ${theme.gridUnit}px;
padding-left: ${theme.sizeUnit}px;
padding-right: ${theme.sizeUnit}px;
margin-bottom: 0;
}
@@ -56,7 +56,7 @@ const StyledDiv = styled.div`
}
.paired-ttest-table h1 {
margin-left: ${theme.gridUnit}px;
margin-left: ${theme.sizeUnit}px;
}
.reactable-data tr {
@@ -74,19 +74,19 @@ const StyledDiv = styled.div`
}
.reactable-data tr .false {
color: ${theme.colors.error.base};
color: ${theme.colorError};
}
.reactable-data tr .true {
color: ${theme.colors.success.base};
color: ${theme.colorSuccess};
}
.reactable-data tr .control {
color: ${theme.colors.primary.base};
color: ${theme.colorPrimary};
}
.reactable-data tr .invalid {
color: ${theme.colors.warning.base};
color: ${theme.colorWarning};
}
.reactable-data .control td {
@@ -104,13 +104,13 @@ const StyledDiv = styled.div`
.reactable-header-sort-asc:after {
content: '\\25bc';
position: absolute;
right: ${theme.gridUnit * 3}px;
right: ${theme.sizeUnit * 3}px;
}
.reactable-header-sort-desc:after {
content: '\\25b2';
position: absolute;
right: ${theme.gridUnit * 3}px;
right: ${theme.sizeUnit * 3}px;
}
`}
`;

View File

@@ -46,7 +46,7 @@ export default styled(ParallelCoordinates)`
}
.parcoords svg,
.parcoords canvas {
font-size: ${theme.typography.sizes.s}px;
font-size: ${theme.fontSizeSM}px;
position: absolute;
}
.parcoords > canvas {
@@ -55,7 +55,7 @@ export default styled(ParallelCoordinates)`
.parcoords text.label {
font: 100%;
font-size: ${theme.typography.sizes.s}px;
font-size: ${theme.fontSizeSM}px;
cursor: drag;
}
.parcoords rect.background {
@@ -74,7 +74,7 @@ export default styled(ParallelCoordinates)`
.parcoords .axis line,
.parcoords .axis path {
fill: none;
stroke: ${theme.colors.grayscale.dark1};
stroke: ${theme.colorText};
shape-rendering: crispEdges;
}
.parcoords canvas {
@@ -84,7 +84,7 @@ export default styled(ParallelCoordinates)`
-o-transition: opacity 0.3s;
}
.parcoords canvas.faded {
opacity: ${theme.opacity.mediumLight};
opacity: 35%;
}
.parcoords {
-webkit-touch-callout: none;
@@ -100,7 +100,7 @@ export default styled(ParallelCoordinates)`
.parcoords .row,
.parcoords .header {
clear: left;
font-size: ${theme.typography.sizes.s}px;
font-size: ${theme.fontSizeSM}px;
line-height: 18px;
height: 18px;
margin: 0px;
@@ -109,7 +109,7 @@ export default styled(ParallelCoordinates)`
background: ${addAlpha(theme.colors.grayscale.dark2, 0.05)};
}
.parcoords .header {
font-weight: ${theme.typography.weights.bold};
font-weight: ${theme.fontWeightStrong};
}
.parcoords .cell {
float: left;

View File

@@ -36,13 +36,13 @@ export default styled(Partition)`
.superset-legacy-chart-partition .chart {
display: block;
margin: auto;
font-size: ${theme.typography.sizes.s}px;
font-size: ${theme.fontSizeSM}px;
}
.superset-legacy-chart-partition rect {
stroke: ${theme.colors.grayscale.light2};
fill: ${theme.colors.grayscale.light1};
fill-opacity: ${theme.opacity.heavy};
fill-opacity: 80%;
transition: fill-opacity 180ms linear;
cursor: pointer;
}
@@ -52,8 +52,8 @@ export default styled(Partition)`
}
.superset-legacy-chart-partition g text {
font-weight: ${theme.typography.weights.bold};
fill: ${theme.colors.grayscale.dark1};
font-weight: ${theme.fontWeightStrong};
fill: ${theme.colorText};
}
.superset-legacy-chart-partition g:hover text {
@@ -65,15 +65,15 @@ export default styled(Partition)`
top: 0;
left: 0;
opacity: 0;
padding: ${theme.gridUnit}px;
padding: ${theme.sizeUnit}px;
pointer-events: none;
background-color: ${theme.colors.grayscale.dark2};
border-radius: ${theme.gridUnit}px;
border-radius: ${theme.sizeUnit}px;
}
.partition-tooltip td {
padding-left: ${theme.gridUnit}px;
font-size: ${theme.typography.sizes.s}px;
padding-left: ${theme.sizeUnit}px;
font-size: ${theme.fontSizeSM}px;
color: ${theme.colors.grayscale.light5};
}
`}

View File

@@ -28,13 +28,13 @@ const Rose = ({ className, ...otherProps }) => (
styles={theme => css`
.tooltip {
line-height: 1;
padding: ${theme.gridUnit * 3}px;
padding: ${theme.sizeUnit * 3}px;
background: ${theme.colors.grayscale.dark2};
color: ${theme.colors.grayscale.light5};
border-radius: 4px;
pointer-events: none;
z-index: 1000;
font-size: ${theme.typography.sizes.s}px;
font-size: ${theme.fontSizeSM}px;
}
`}
/>
@@ -53,9 +53,8 @@ export default styled(Rose)`
}
.superset-legacy-chart-rose text {
font-weight: ${theme.typography.weights.normal};
font-size: ${theme.typography.sizes.s}px;
font-family: ${theme.typography.families.sansSerif};
font-size: ${theme.fontSizeSM}px;
font-family: ${theme.fontFamily};
pointer-events: none;
}

View File

@@ -265,7 +265,7 @@ function WorldMap(element, props) {
countryFeature =>
!filterState.selectedValues.includes(countryFeature.id),
)
.style('fill-opacity', theme.opacity.mediumLight);
.style('fill-opacity', 0.35);
// hack to ensure that the clicked country's color is preserved
// sometimes the fill color would get default grey value after applying cross filter

View File

@@ -24,12 +24,12 @@ import { formatNumber, styled } from '@superset-ui/core';
const StyledLegend = styled.div`
${({ theme }) => `
font-size: ${theme.typography.sizes.s}px;
font-size: ${theme.fontSizeSM}px;
position: absolute;
background: ${theme.colors.grayscale.light5};
box-shadow: 0 0 ${theme.gridUnit}px ${theme.colors.grayscale.light2};
margin: ${theme.gridUnit * 6}px;
padding: ${theme.gridUnit * 3}px ${theme.gridUnit * 5}px;
box-shadow: 0 0 ${theme.sizeUnit}px ${theme.colors.grayscale.light2};
margin: ${theme.sizeUnit * 6}px;
padding: ${theme.sizeUnit * 3}px ${theme.sizeUnit * 5}px;
outline: none;
overflow-y: scroll;
max-height: 200px;
@@ -43,10 +43,10 @@ const StyledLegend = styled.div`
display: flex;
color: ${theme.colors.grayscale.base};
text-decoration: none;
padding: ${theme.gridUnit}px 0;
padding: ${theme.sizeUnit}px 0;
& span {
margin-right: ${theme.gridUnit}px;
margin-right: ${theme.sizeUnit}px;
}
}
}

View File

@@ -36,12 +36,12 @@ const StyledDiv = styled.div<{ top: number; left: number }>`
position: absolute;
top: ${top}px;
left: ${left}px;
padding: ${theme.gridUnit * 2}px;
margin: ${theme.gridUnit * 2}px;
padding: ${theme.sizeUnit * 2}px;
margin: ${theme.sizeUnit * 2}px;
background: ${theme.colors.grayscale.dark2};
color: ${theme.colors.grayscale.light5};
maxWidth: 300px;
fontSize: ${theme.typography.sizes.s}px;
fontSize: ${theme.fontSizeSM}px;
zIndex: 9;
pointerEvents: none;
`}

View File

@@ -49,23 +49,23 @@ export default styled(NVD3)`
svg {
&.nvd3-svg {
width: auto;
font-size: ${({ theme }) => theme.typography.sizes.m};
font-size: ${({ theme }) => theme.fontSize};
}
}
}
.superset-legacy-chart-nvd3 {
nv-x text {
font-size: ${({ theme }) => theme.typography.sizes.m};
font-size: ${({ theme }) => theme.fontSize};
}
g.superset path {
stroke-dasharray: 5, 5;
}
.nvtooltip tr.highlight td {
font-weight: ${({ theme }) => theme.typography.weights.bold};
font-size: ${({ theme }) => theme.typography.sizes.m}px !important;
font-weight: ${({ theme }) => theme.fontWeightStrong};
font-size: ${({ theme }) => theme.fontSize}px !important;
}
text.nv-axislabel {
font-size: ${({ theme }) => theme.typography.sizes.m} !important;
font-size: ${({ theme }) => theme.fontSize} !important;
}
g.solid path,
line.solid {
@@ -134,22 +134,22 @@ export default styled(NVD3)`
stroke-dasharray: 5, 5, 1, 5;
}
.nv-noData.body {
font-size: ${({ theme }) => theme.typography.sizes.m};
font-weight: ${({ theme }) => theme.typography.weights.normal};
font-size: ${({ theme }) => theme.fontSize};
font-weight: ${({ theme }) => theme.fontWeightNormal};
}
}
.superset-legacy-chart-nvd3-tr-highlight {
border-top: 1px solid;
border-bottom: 1px solid;
font-weight: ${({ theme }) => theme.typography.weights.bold};
font-weight: ${({ theme }) => theme.fontWeightStrong};
}
.superset-legacy-chart-nvd3-tr-total {
font-weight: ${({ theme }) => theme.typography.weights.bold};
font-weight: ${({ theme }) => theme.fontWeightStrong};
}
.nvtooltip {
.tooltip-header {
white-space: nowrap;
font-weight: ${({ theme }) => theme.typography.weights.bold};
font-weight: ${({ theme }) => theme.fontWeightStrong};
}
tbody tr:not(.tooltip-header) td:nth-of-type(2) {
word-break: break-word;
@@ -161,12 +161,12 @@ export default styled(NVD3)`
border-radius: 2px;
background-color: ${({ theme }) => theme.colors.grayscale.base};
fill-opacity: 0.6;
margin: ${({ theme }) => theme.gridUnit * 2}px;
padding: ${({ theme }) => theme.gridUnit * 2}px;
margin: ${({ theme }) => theme.sizeUnit * 2}px;
padding: ${({ theme }) => theme.sizeUnit * 2}px;
color: ${({ theme }) => theme.colors.grayscale.light5};
&:after {
content: '\\25BC';
font-size: ${({ theme }) => theme.typography.sizes.m};
font-size: ${({ theme }) => theme.fontSize};
color: ${({ theme }) => theme.colors.grayscale.base};
position: absolute;
bottom: -14px;

View File

@@ -47,10 +47,10 @@ const NumbersContainer = styled.div`
const ComparisonValue = styled.div<PopKPIComparisonValueStyleProps>`
${({ theme, subheaderFontSize }) => `
font-weight: ${theme.typography.weights.light};
font-weight: ${theme.fontWeightLight};
display: flex;
justify-content: center;
font-size: ${subheaderFontSize || 20}px;
font-size: ${String(subheaderFontSize) || 20}px;
flex: 1 1 0px;
`}
`;
@@ -59,9 +59,9 @@ const SymbolWrapper = styled.span<PopKPIComparisonSymbolStyleProps>`
${({ theme, backgroundColor, textColor }) => `
background-color: ${backgroundColor};
color: ${textColor};
padding: ${theme.gridUnit}px ${theme.gridUnit * 2}px;
border-radius: ${theme.gridUnit * 2}px;
margin-right: ${theme.gridUnit}px;
padding: ${theme.sizeUnit}px ${theme.sizeUnit * 2}px;
border-radius: ${theme.sizeUnit * 2}px;
margin-right: ${theme.sizeUnit}px;
`}
`;
@@ -124,9 +124,9 @@ export default function PopKPI(props: PopKPIProps) {
}, [currentTimeRangeFilter, shift, startDateOffset, dashboardTimeRange]);
const theme = useTheme();
const flexGap = theme.gridUnit * 5;
const flexGap = theme.sizeUnit * 5;
const wrapperDivStyles = css`
font-family: ${theme.typography.families.sansSerif};
font-family: ${theme.fontFamily};
display: flex;
justify-content: center;
align-items: center;
@@ -136,19 +136,19 @@ export default function PopKPI(props: PopKPIProps) {
`;
const bigValueContainerStyles = css`
font-size: ${headerFontSize || 60}px;
font-weight: ${theme.typography.weights.normal};
font-size: ${String(headerFontSize) || 60}px;
font-weight: ${theme.fontWeightNormal};
text-align: center;
margin-bottom: ${theme.gridUnit * 4}px;
margin-bottom: ${theme.sizeUnit * 4}px;
`;
const SubtitleText = styled.div`
${({ theme }) => `
font-family: ${theme.typography.families.sansSerif};
font-weight: ${theme.typography.weights.medium};
font-family: ${theme.fontFamily};
font-weight: ${theme.fontWeightNormal};
text-align: center;
margin-top: -10px;
margin-bottom: ${theme.gridUnit * 4}px;
margin-bottom: ${theme.sizeUnit * 4}px;
`}
`;
@@ -160,21 +160,21 @@ export default function PopKPI(props: PopKPIProps) {
if (percentDifferenceNumber > 0) {
// Positive difference
return comparisonColorScheme === ColorSchemeEnum.Green
? theme.colors.success.base
: theme.colors.error.base;
? theme.colorSuccess
: theme.colorError;
}
// Negative difference
return comparisonColorScheme === ColorSchemeEnum.Red
? theme.colors.success.base
: theme.colors.error.base;
? theme.colorSuccess
: theme.colorError;
};
const arrowIndicatorStyle = css`
color: ${getArrowIndicatorColor()};
margin-left: ${theme.gridUnit}px;
margin-left: ${theme.sizeUnit}px;
`;
const defaultBackgroundColor = theme.colors.grayscale.light4;
const defaultBackgroundColor = theme.colorBgContainer;
const defaultTextColor = theme.colors.grayscale.base;
const { backgroundColor, textColor } = useMemo(() => {
let bgColor = defaultBackgroundColor;
@@ -190,9 +190,7 @@ export default function PopKPI(props: PopKPIProps) {
bgColor = useSuccess
? theme.colors.success.light2
: theme.colors.error.light2;
txtColor = useSuccess
? theme.colors.success.base
: theme.colors.error.base;
txtColor = useSuccess ? theme.colorSuccess : theme.colorError;
}
return {

View File

@@ -18,17 +18,18 @@
*/
import {
QueryFormData,
supersetTheme,
TimeseriesDataRecord,
Metric,
SimpleAdhocFilter,
} from '@superset-ui/core';
export type FontSizeOptions = 'xs' | 's' | 'm' | 'l' | 'xl' | 'xxl';
export interface PopKPIStylesProps {
height: number;
width: number;
headerFontSize: keyof typeof supersetTheme.typography.sizes;
subheaderFontSize: keyof typeof supersetTheme.typography.sizes;
headerFontSize: FontSizeOptions;
subheaderFontSize: FontSizeOptions;
boldText: boolean;
comparisonColorEnabled: boolean;
}
@@ -44,7 +45,7 @@ interface PopKPICustomizeProps {
}
export interface PopKPIComparisonValueStyleProps {
subheaderFontSize?: keyof typeof supersetTheme.typography.sizes;
subheaderFontSize?: FontSizeOptions;
}
export interface PopKPIComparisonSymbolStyleProps {

View File

@@ -26,6 +26,7 @@ import {
BRAND_COLOR,
styled,
BinaryQueryObjectFilterClause,
themeObject,
} from '@superset-ui/core';
import Echart from '../components/Echart';
import { BigNumberVizProps } from './types';
@@ -138,6 +139,7 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> {
const hasThresholdColorFormatter =
Array.isArray(colorThresholdFormatters) &&
colorThresholdFormatters.length > 0;
const { theme } = themeObject;
let numberColor;
if (hasThresholdColorFormatter) {
@@ -150,7 +152,7 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> {
}
});
} else {
numberColor = 'black';
numberColor = theme.colorText;
}
const container = this.createTemporaryContainer();
@@ -316,7 +318,7 @@ class BigNumberVis extends PureComponent<BigNumberVizProps> {
export default styled(BigNumberVis)`
${({ theme }) => `
font-family: ${theme.typography.families.sansSerif};
font-family: ${theme.fontFamily};
position: relative;
display: flex;
flex-direction: column;
@@ -333,11 +335,11 @@ export default styled(BigNumberVis)`
justify-content: center;
align-items: flex-start;
.alert {
font-size: ${theme.typography.sizes.s};
font-size: ${theme.fontSizeSM};
margin: -0.5em 0 0.4em;
line-height: 1;
padding: ${theme.gridUnit}px;
border-radius: ${theme.gridUnit}px;
padding: ${theme.sizeUnit}px;
border-radius: ${theme.sizeUnit}px;
}
}
@@ -350,7 +352,7 @@ export default styled(BigNumberVis)`
position: relative;
line-height: 1em;
white-space: nowrap;
margin-bottom:${theme.gridUnit * 2}px;
margin-bottom:${theme.sizeUnit * 2}px;
span {
position: absolute;
bottom: 0;
@@ -371,7 +373,7 @@ export default styled(BigNumberVis)`
.kicker,
.header-line,
.subheader-line {
opacity: ${theme.opacity.mediumHeavy};
opacity: 60%;
}
}
`}

View File

@@ -227,7 +227,9 @@ export default function transformProps(
const defaultLabel = {
formatter,
show: showLabels,
color: theme.colors.grayscale.dark2,
color: theme.colorText,
textBorderColor: theme.colorBgBase,
textBorderWidth: 1,
};
const series: FunnelSeriesOption[] = [
@@ -245,7 +247,6 @@ export default function transformProps(
label: {
...defaultLabel,
position: labelLine ? 'outer' : 'inner',
textBorderColor: 'transparent',
},
emphasis: {
label: {

View File

@@ -24,25 +24,25 @@ export const defaultGaugeSeriesOption = (
): GaugeSeriesOption => ({
splitLine: {
lineStyle: {
color: theme.colors.primary.base,
color: theme.colorPrimary,
},
},
axisLine: {
lineStyle: {
color: [[1, theme.colors.primary.light4]],
color: [[1, theme.colorSplit]],
},
},
axisLabel: {
color: theme.colors.grayscale.dark1,
color: theme.colorText,
},
axisTick: {
lineStyle: {
width: 2,
color: theme.colors.primary.base,
color: theme.colorPrimary,
},
},
detail: {
color: 'auto',
color: theme.colorText,
},
});

View File

@@ -183,6 +183,7 @@ export default function transformProps(
`${index * titleOffsetFromTitle + OFFSETS.titleFromCenter}%`,
],
fontSize,
color: theme.colorTextSecondary,
},
detail: {
offsetCenter: [
@@ -194,6 +195,7 @@ export default function transformProps(
}%`,
],
fontSize: FONT_SIZE_MULTIPLIERS.detailFontSize * fontSize,
color: theme.colorText,
},
};
if (

View File

@@ -325,7 +325,10 @@ export default function transformProps(
selectedMode,
...getChartPadding(showLegend, legendOrientation, legendMargin),
animation: DEFAULT_GRAPH_SERIES_OPTION.animation,
label: DEFAULT_GRAPH_SERIES_OPTION.label,
label: {
...DEFAULT_GRAPH_SERIES_OPTION.label,
color: theme.colorText,
},
lineStyle: DEFAULT_GRAPH_SERIES_OPTION.lineStyle,
emphasis: DEFAULT_GRAPH_SERIES_OPTION.emphasis,
},

View File

@@ -252,7 +252,6 @@ export default function transformProps(
label: {
show: true,
fontWeight: 'bold',
backgroundColor: theme.colors.grayscale.light5,
},
},
data: transformedData,
@@ -276,6 +275,23 @@ export default function transformProps(
radar: {
shape: isCircle ? 'circle' : 'polygon',
indicator,
splitLine: {
show: true,
lineStyle: {
color: theme.colorSplit,
},
},
splitArea: {
show: true,
areaStyle: {
color: [theme.colorBgLayout, theme.colorBgContainer],
},
},
axisLine: {
lineStyle: {
color: theme.colorSplit,
},
},
},
};

View File

@@ -26,6 +26,7 @@ import {
getMetricLabel,
getNumberFormatter,
tooltipHtml,
themeObject,
} from '@superset-ui/core';
import { SankeyChartProps, SankeyTransformedProps } from './types';
import { Refs } from '../types';
@@ -62,6 +63,7 @@ export default function transformProps(
value,
});
});
const { theme } = themeObject;
const seriesData: NonNullable<SankeySeriesOption['data']> = Array.from(
set,
@@ -70,6 +72,10 @@ export default function transformProps(
itemStyle: {
color: colorFn(name, sliceId),
},
label: {
color: theme.colorText,
textShadow: theme.colorBgBase,
},
}));
// stores a map with the total values for each node considering the links

View File

@@ -220,10 +220,10 @@ export default function transformProps(
});
const minShowLabelAngle = (showLabelsThreshold || 0) * 3.6;
const padding = {
top: theme.gridUnit * 3,
right: theme.gridUnit,
bottom: theme.gridUnit * 3,
left: theme.gridUnit,
top: theme.sizeUnit * 3,
right: theme.sizeUnit,
bottom: theme.sizeUnit * 3,
left: theme.sizeUnit,
};
const containerWidth = width;
const containerHeight = height;
@@ -274,7 +274,11 @@ export default function transformProps(
} else {
linearColorScale(totalSecondaryValue / totalValue);
}
const labelProps = {
color: theme.colorText,
textBorderColor: theme.colorBgBase,
textBorderWidth: 1,
};
const traverse = (
treeNodes: TreeNode[],
path: string[],
@@ -316,7 +320,7 @@ export default function transformProps(
opacity: OpacityEnum.SemiTransparent,
},
label: {
color: `rgba(0, 0, 0, ${OpacityEnum.SemiTransparent})`,
...labelProps,
},
};
}
@@ -356,10 +360,10 @@ export default function transformProps(
},
},
label: {
...labelProps,
width: (radius * 0.6) / (columns.length || 1),
show: showLabels,
formatter,
color: theme.colors.grayscale.dark2,
minAngle: minShowLabelAngle,
overflow: 'breakAll',
},
@@ -381,7 +385,6 @@ export default function transformProps(
}
: null,
};
return {
formData,
width,

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