mirror of
https://github.com/apache/superset.git
synced 2026-05-19 06:45:15 +00:00
Compare commits
120 Commits
docs/dashb
...
enxdev/ref
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3d5aeba2b | ||
|
|
a5fc4cad09 | ||
|
|
50459ee3d5 | ||
|
|
b040d52c2d | ||
|
|
2e44c0135e | ||
|
|
5085e8973d | ||
|
|
944d1901cd | ||
|
|
a28ea4f82d | ||
|
|
a7b7e6319c | ||
|
|
c217f56aea | ||
|
|
d43657ed90 | ||
|
|
6c01173c21 | ||
|
|
4709eb0153 | ||
|
|
51f719f8d4 | ||
|
|
50535a92a0 | ||
|
|
3fe5db1e72 | ||
|
|
08c9d28545 | ||
|
|
86d5537a7f | ||
|
|
18c3f0adb6 | ||
|
|
5e8cd7a6ee | ||
|
|
dcea6c09ca | ||
|
|
dff7c1b50d | ||
|
|
4f93c2d2e7 | ||
|
|
334aa1a672 | ||
|
|
2667a14678 | ||
|
|
29ba5adf21 | ||
|
|
f18455cc2d | ||
|
|
4123d25873 | ||
|
|
662f33b7de | ||
|
|
c14dcecd8f | ||
|
|
6a4730bbbe | ||
|
|
739caa19cb | ||
|
|
8bb02e2958 | ||
|
|
7c2fd55104 | ||
|
|
6c8e72b889 | ||
|
|
3b198ab656 | ||
|
|
c993abe58c | ||
|
|
a9bc4655a4 | ||
|
|
b835478514 | ||
|
|
3950cf065e | ||
|
|
c7d2881d04 | ||
|
|
33febb669e | ||
|
|
6254db34cd | ||
|
|
e6df194201 | ||
|
|
2580a8ba78 | ||
|
|
6b58ef155e | ||
|
|
6f73e58b25 | ||
|
|
bc85a118ba | ||
|
|
70a5925b03 | ||
|
|
d266835820 | ||
|
|
952658ee63 | ||
|
|
27d723fba1 | ||
|
|
971715931b | ||
|
|
e3342bb731 | ||
|
|
506c8387fc | ||
|
|
9dedb588ba | ||
|
|
8b69958f19 | ||
|
|
1dd8a76113 | ||
|
|
cde1da6285 | ||
|
|
3665ebcb4b | ||
|
|
c31d70dd12 | ||
|
|
f217865435 | ||
|
|
501874980e | ||
|
|
e7b2b586b6 | ||
|
|
6a15aaf562 | ||
|
|
f5b680699f | ||
|
|
3c289a927d | ||
|
|
5805f242d0 | ||
|
|
4f0a4454ec | ||
|
|
d752b0f06a | ||
|
|
06d737ec9f | ||
|
|
04729794c8 | ||
|
|
68ea9ac4d0 | ||
|
|
1afa4971d1 | ||
|
|
5418f09864 | ||
|
|
aabeefb761 | ||
|
|
023c7da07b | ||
|
|
7af32d4c70 | ||
|
|
58724b1c5c | ||
|
|
f9494128bc | ||
|
|
cebff5e726 | ||
|
|
bcb6da18ef | ||
|
|
344c8f5c37 | ||
|
|
d3f450fca0 | ||
|
|
11a29b1610 | ||
|
|
54d67b679b | ||
|
|
64c480a8f1 | ||
|
|
cf6816064d | ||
|
|
59e402ac68 | ||
|
|
5042248ed7 | ||
|
|
56e3d165dd | ||
|
|
8ce144983d | ||
|
|
4afbfd11e0 | ||
|
|
31eb10590e | ||
|
|
358633e98d | ||
|
|
b7bc1113ac | ||
|
|
11bc4965e3 | ||
|
|
1ca0f34210 | ||
|
|
9cb6c3b039 | ||
|
|
b9be692e55 | ||
|
|
e0d86df5a5 | ||
|
|
2f80ebb3e8 | ||
|
|
83f47d3ca1 | ||
|
|
48df49d89c | ||
|
|
f16600ee86 | ||
|
|
7dbe05f6d8 | ||
|
|
2d461deb68 | ||
|
|
dbc7db981c | ||
|
|
5faf0189e8 | ||
|
|
4b55a928c9 | ||
|
|
d15c6d361b | ||
|
|
6b5d53ad39 | ||
|
|
b575aa6aac | ||
|
|
d131c29f3b | ||
|
|
22cbec1d95 | ||
|
|
b2b7b899a3 | ||
|
|
ac81eefe3f | ||
|
|
8d361205f6 | ||
|
|
352aa36823 | ||
|
|
336763f0c9 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1,3 +1,4 @@
|
||||
docker/**/*.sh text eol=lf
|
||||
*.svg binary
|
||||
*.ipynb binary
|
||||
*.geojson binary
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -26,5 +26,7 @@ CHANGELOG/
|
||||
*-topo.json
|
||||
storybook-static/
|
||||
*.snap
|
||||
**/*.less
|
||||
**/*.less.hbs
|
||||
|
||||
/.nx/workspace-data
|
||||
|
||||
@@ -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 },
|
||||
};
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
body {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
@@ -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)');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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', '/')
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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"]')
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
};
|
||||
|
||||
4268
superset-frontend/package-lock.json
generated
4268
superset-frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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": {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
|
||||
@@ -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,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
}}
|
||||
|
||||
@@ -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};
|
||||
`}
|
||||
`;
|
||||
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
}}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
312
superset-frontend/packages/superset-ui-core/src/theme/Theme.tsx
Normal file
312
superset-frontend/packages/superset-ui-core/src/theme/Theme.tsx
Normal 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 */
|
||||
@@ -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;
|
||||
@@ -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 };
|
||||
367
superset-frontend/packages/superset-ui-core/src/theme/types.ts
Normal file
367
superset-frontend/packages/superset-ui-core/src/theme/types.ts
Normal 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;
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
189
superset-frontend/packages/superset-ui-core/src/theme/utils.ts
Normal file
189
superset-frontend/packages/superset-ui-core/src/theme/utils.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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 = () =>
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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>,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -5539,5 +5539,5 @@ export const generateData = (theme: SupersetTheme) => ({
|
||||
],
|
||||
renderWhileDragging: true,
|
||||
tooltip: null,
|
||||
color: theme.colors.primary.base,
|
||||
color: theme.colorPrimary,
|
||||
});
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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 />;
|
||||
@@ -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};
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
@@ -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%;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
`}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
`}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
`}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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%;
|
||||
}
|
||||
}
|
||||
`}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user