From 2a75e4c3c37ec6dddab5ecdec8908a0f18ed391d Mon Sep 17 00:00:00 2001 From: Jinghuayao <81597121+jinghua-qa@users.noreply.github.com> Date: Fri, 1 Apr 2022 01:06:48 -0700 Subject: [PATCH 01/31] test(native filter): add new test for dependent filter (#19392) * add new test for dependent filter --- docker/docker-init.sh | 2 +- .../integration/dashboard/dashboard.helper.ts | 120 ++++++++++++++++++ .../dashboard/nativeFilters.test.ts | 97 ++++++++++---- .../cypress-base/cypress/support/index.d.ts | 14 ++ .../cypress-base/cypress/support/index.ts | 86 +++++++++++++ superset-frontend/package-lock.json | 2 +- 6 files changed, 292 insertions(+), 29 deletions(-) diff --git a/docker/docker-init.sh b/docker/docker-init.sh index 07830694048a7..c98f49881ada7 100755 --- a/docker/docker-init.sh +++ b/docker/docker-init.sh @@ -41,7 +41,7 @@ ADMIN_PASSWORD="admin" # If Cypress run – overwrite the password for admin and export env variables if [ "$CYPRESS_CONFIG" == "true" ]; then ADMIN_PASSWORD="general" - export SUPERSET_CONFIG=tests.superset_test_config + export SUPERSET_CONFIG=tests.integration_tests.superset_test_config export SUPERSET_TESTENV=true export SUPERSET__SQLALCHEMY_DATABASE_URI=postgresql+psycopg2://superset:superset@db:5432/superset fi diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/dashboard.helper.ts b/superset-frontend/cypress-base/cypress/integration/dashboard/dashboard.helper.ts index 1458fc7d5982d..9822f7035952d 100644 --- a/superset-frontend/cypress-base/cypress/integration/dashboard/dashboard.helper.ts +++ b/superset-frontend/cypress-base/cypress/integration/dashboard/dashboard.helper.ts @@ -1,4 +1,9 @@ import { getChartAlias, Slice } from 'cypress/utils/vizPlugins'; +import { + dashboardView, + editDashboardView, + nativeFilters, +} from 'cypress/support/directories'; /** * Licensed to the Apache Software Foundation (ASF) under one @@ -25,7 +30,13 @@ export const testItems = { dashboard: 'Cypress Sales Dashboard', dataset: 'Vehicle Sales', chart: 'Cypress chart', + newChart: 'New Cypress Chart', + createdDashboard: 'New Dashboard', defaultNameDashboard: '[ untitled dashboard ]', + newDashboardTitle: `Test dashboard [NEW TEST]`, + bulkFirstNameDashboard: 'First Dash', + bulkSecondNameDashboard: 'Second Dash', + worldBanksDataCopy: `World Bank's Data [copy]`, }; export const CHECK_DASHBOARD_FAVORITE_ENDPOINT = @@ -133,3 +144,112 @@ export function resize(selector: string) { }, }; } + +export function cleanUp() { + cy.deleteDashboardByName(testItems.dashboard); + cy.deleteDashboardByName(testItems.defaultNameDashboard); + cy.deleteDashboardByName(''); + cy.deleteDashboardByName(testItems.newDashboardTitle); + cy.deleteDashboardByName(testItems.bulkFirstNameDashboard); + cy.deleteDashboardByName(testItems.bulkSecondNameDashboard); + cy.deleteDashboardByName(testItems.createdDashboard); + cy.deleteDashboardByName(testItems.worldBanksDataCopy); + cy.deleteChartByName(testItems.chart); + cy.deleteChartByName(testItems.newChart); +} + +/** ************************************************************************ + * Clicks on new filter button + * @returns {None} + * @summary helper for adding new filter + ************************************************************************* */ +export function clickOnAddFilterInModal() { + return cy + .get(nativeFilters.addFilterButton.button) + .first() + .click() + .then(() => { + cy.get(nativeFilters.addFilterButton.dropdownItem) + .contains('Filter') + .click({ force: true }); + }); +} + +/** ************************************************************************ + * Fills value native filter form with basic information + * @param {string} name name for filter + * @param {string} dataset which dataset should be used + * @param {string} filterColumn which column should be used + * @returns {None} + * @summary helper for filling value native filter form + ************************************************************************* */ +export function fillValueNativeFilterForm( + name: string, + dataset: string, + filterColumn: string, +) { + cy.get(nativeFilters.modal.container) + .find(nativeFilters.filtersPanel.filterName) + .last() + .click({ scrollBehavior: false }) + .type(name, { scrollBehavior: false }); + cy.get(nativeFilters.modal.container) + .find(nativeFilters.filtersPanel.datasetName) + .last() + .click({ scrollBehavior: false }) + .type(`${dataset}{enter}`, { scrollBehavior: false }); + cy.get(nativeFilters.silentLoading).should('not.exist'); + cy.get(nativeFilters.filtersPanel.filterInfoInput) + .last() + .should('be.visible') + .click({ force: true }); + cy.get(nativeFilters.filtersPanel.filterInfoInput).last().type(filterColumn); + cy.get(nativeFilters.filtersPanel.inputDropdown) + .should('be.visible', { timeout: 20000 }) + .last() + .click(); +} +/** ************************************************************************ + * Get native filter placeholder e.g 9 options + * @param {number} index which input it fills + * @returns cy object for assertions + * @summary helper for getting placeholder value + ************************************************************************* */ +export function getNativeFilterPlaceholderWithIndex(index: number) { + return cy.get(nativeFilters.filtersPanel.columnEmptyInput).eq(index); +} + +/** ************************************************************************ + * Apply native filter value from dashboard view + * @param {number} index which input it fills + * @param {string} value what is filter value + * @returns {null} + * @summary put value to nth native filter input in view + ************************************************************************* */ +export function applyNativeFilterValueWithIndex(index: number, value: string) { + cy.get(nativeFilters.filterFromDashboardView.filterValueInput) + .eq(index) + .parent() + .should('be.visible', { timeout: 10000 }) + .type(`${value}{enter}`); + // click the title to dismiss shown options + cy.get(nativeFilters.filterFromDashboardView.filterName).eq(index).click(); +} + +/** ************************************************************************ + * Fills parent filter input + * @param {number} index which input it fills + * @param {string} value on which filter it depends on + * @returns {null} + * @summary takes first or second input and modify the depends on filter value + ************************************************************************* */ +export function addParentFilterWithValue(index: number, value: string) { + return cy + .get(nativeFilters.filterConfigurationSections.displayedSection) + .within(() => { + cy.get('input[aria-label="Limit type"]') + .eq(index) + .click({ force: true }) + .type(`${value}{enter}`, { delay: 30, force: true }); + }); +} diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts b/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts index 433b4a65e33cb..2177c9c4fe4cd 100644 --- a/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts +++ b/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts @@ -22,7 +22,17 @@ import { nativeFilters, exploreView, } from 'cypress/support/directories'; -import { testItems } from './dashboard.helper'; +import { + cleanUp, + testItems, + WORLD_HEALTH_CHARTS, + waitForChartLoad, + clickOnAddFilterInModal, + fillValueNativeFilterForm, + getNativeFilterPlaceholderWithIndex, + addParentFilterWithValue, + applyNativeFilterValueWithIndex, +} from './dashboard.helper'; import { DASHBOARD_LIST } from '../dashboard_list/dashboard_list.helper'; import { CHART_LIST } from '../chart_list/chart_list.helper'; import { FORM_DATA_DEFAULTS } from '../explore/visualizations/shared.helper'; @@ -39,21 +49,27 @@ const milliseconds = new Date().getTime(); const dashboard = `Test Dashboard${milliseconds}`; describe('Nativefilters Sanity test', () => { - before(() => { + beforeEach(() => { cy.login(); + cleanUp(); cy.intercept('/api/v1/dashboard/?q=**').as('dashboardsList'); cy.intercept('POST', '**/copy_dash/*').as('copy'); cy.intercept('/api/v1/dashboard/*').as('dashboard'); - cy.request( - 'api/v1/dashboard/?q=(order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:100)', - ).then(xhr => { - const dashboards = xhr.body.result; + cy.intercept('GET', '**/api/v1/dataset/**').as('datasetLoad'); + cy.intercept('**/api/v1/dashboard/?q=**').as('dashboardsList'); + cy.visit('dashboard/list/'); + cy.contains('Actions'); + cy.wait('@dashboardsList').then(xhr => { + const dashboards = xhr.response?.body.result; + /* eslint-disable no-unused-expressions */ + expect(dashboards).not.to.be.undefined; const worldBankDashboard = dashboards.find( (d: { dashboard_title: string }) => d.dashboard_title === "World Bank's Data", ); cy.visit(worldBankDashboard.url); }); + WORLD_HEALTH_CHARTS.forEach(waitForChartLoad); cy.get(dashboardView.threeDotsMenuIcon).should('be.visible').click(); cy.get(dashboardView.saveAsMenuOption).should('be.visible').click(); cy.get(dashboardView.saveModal.dashboardNameInput) @@ -65,19 +81,10 @@ describe('Nativefilters Sanity test', () => { .its('response.statusCode') .should('eq', 200); }); - beforeEach(() => { - cy.login(); - cy.request( - 'api/v1/dashboard/?q=(order_column:changed_on_delta_humanized,order_direction:desc,page:0,page_size:100)', - ).then(xhr => { - const dashboards = xhr.body.result; - const testDashboard = dashboards.find( - (d: { dashboard_title: string }) => - d.dashboard_title === testItems.dashboard, - ); - cy.visit(testDashboard.url); - }); + afterEach(() => { + cleanUp(); }); + it('User can expand / retract native filter sidebar on a dashboard', () => { cy.get(nativeFilters.createFilterButton).should('not.exist'); cy.get(nativeFilters.filterFromDashboardView.expand) @@ -390,15 +397,6 @@ describe('Nativefilters Sanity test', () => { cy.get('.line').within(() => { cy.contains('United States').should('be.visible'); }); - - // clean up the default setting - cy.get(nativeFilters.filterFromDashboardView.expand).click({ force: true }); - cy.get(nativeFilters.filterFromDashboardView.createFilterButton).click(); - cy.contains('Filter has default value').click(); - cy.get(nativeFilters.modal.footer) - .find(nativeFilters.modal.saveButton) - .should('be.visible') - .click({ force: true }); }); it('User can create a time grain filter', () => { @@ -565,6 +563,51 @@ describe('Nativefilters Sanity test', () => { .should('be.visible', { timeout: 40000 }) .contains('country_name'); }); + + it('User can create parent filters using "Values are dependent on other filters"', () => { + cy.get(nativeFilters.filterFromDashboardView.expand) + .should('be.visible') + .click({ force: true }); + cy.get(nativeFilters.filterFromDashboardView.createFilterButton).click(); + // Create parent filter 'region'. + fillValueNativeFilterForm('region', 'wb_health_population', 'region'); + // Create filter 'country_name' depend on region filter. + clickOnAddFilterInModal(); + fillValueNativeFilterForm( + 'country_name', + 'wb_health_population', + 'country_name', + ); + cy.get(nativeFilters.filterConfigurationSections.displayedSection).within( + () => { + cy.contains('Values are dependent on other filters') + .should('be.visible') + .click(); + }, + ); + addParentFilterWithValue(0, 'region'); + cy.wait(1000); + cy.get(nativeFilters.modal.footer) + .contains('Save') + .should('be.visible') + .click(); + // Validate both filter in dashboard view. + WORLD_HEALTH_CHARTS.forEach(waitForChartLoad); + ['region', 'country_name'].forEach(it => { + cy.get(nativeFilters.filterFromDashboardView.filterName) + .contains(it) + .should('be.visible'); + }); + getNativeFilterPlaceholderWithIndex(1) + .invoke('text') + .should('equal', '214 options', { timeout: 20000 }); + // apply first filter value and validate 2nd filter is depden on 1st filter. + applyNativeFilterValueWithIndex(0, 'East Asia & Pacific'); + + getNativeFilterPlaceholderWithIndex(0).should('have.text', '36 options', { + timeout: 20000, + }); + }); }); xdescribe('Nativefilters', () => { diff --git a/superset-frontend/cypress-base/cypress/support/index.d.ts b/superset-frontend/cypress-base/cypress/support/index.d.ts index fdacf3232ba15..eca68a7ced7fe 100644 --- a/superset-frontend/cypress-base/cypress/support/index.d.ts +++ b/superset-frontend/cypress-base/cypress/support/index.d.ts @@ -47,6 +47,20 @@ declare namespace Cypress { querySubstring?: string | RegExp; chartSelector?: JQuery.Selector; }): cy; + + /** + * Get + */ + getDashboards(): cy; + getCharts(): cy; + + /** + * Delete + */ + deleteDashboard(id: number): cy; + deleteDashboardByName(name: string): cy; + deleteChartByName(name: string): cy; + deleteChart(id: number): cy; } } diff --git a/superset-frontend/cypress-base/cypress/support/index.ts b/superset-frontend/cypress-base/cypress/support/index.ts index e22f69975e96f..905d00df9f81c 100644 --- a/superset-frontend/cypress-base/cypress/support/index.ts +++ b/superset-frontend/cypress-base/cypress/support/index.ts @@ -19,6 +19,7 @@ import '@cypress/code-coverage/support'; const BASE_EXPLORE_URL = '/superset/explore/?form_data='; +const TokenName = Cypress.env('TOKEN_NAME'); /* eslint-disable consistent-return */ Cypress.on('uncaught:exception', err => { @@ -102,3 +103,88 @@ Cypress.Commands.add( return cy; }, ); + +Cypress.Commands.add('deleteDashboardByName', (name: string) => + cy.getDashboards().then((dashboards: any) => { + dashboards?.forEach((element: any) => { + if (element.dashboard_title === name) { + const elementId = element.id; + cy.deleteDashboard(elementId); + } + }); + }), +); + +Cypress.Commands.add('deleteDashboard', (id: number) => + cy + .request({ + method: 'DELETE', + url: `api/v1/dashboard/${id}`, + headers: { + Cookie: `csrf_access_token=${window.localStorage.getItem( + 'access_token', + )}`, + 'Content-Type': 'application/json', + Authorization: `Bearer ${TokenName}`, + 'X-CSRFToken': `${window.localStorage.getItem('access_token')}`, + Referer: `${Cypress.config().baseUrl}/`, + }, + }) + .then(resp => resp), +); + +Cypress.Commands.add('getDashboards', () => + cy + .request({ + method: 'GET', + url: `api/v1/dashboard/`, + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${TokenName}`, + }, + }) + .then(resp => resp.body.result), +); + +Cypress.Commands.add('deleteChart', (id: number) => + cy + .request({ + method: 'DELETE', + url: `api/v1/chart/${id}`, + headers: { + Cookie: `csrf_access_token=${window.localStorage.getItem( + 'access_token', + )}`, + 'Content-Type': 'application/json', + Authorization: `Bearer ${TokenName}`, + 'X-CSRFToken': `${window.localStorage.getItem('access_token')}`, + Referer: `${Cypress.config().baseUrl}/`, + }, + failOnStatusCode: false, + }) + .then(resp => resp), +); + +Cypress.Commands.add('getCharts', () => + cy + .request({ + method: 'GET', + url: `api/v1/chart/`, + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${TokenName}`, + }, + }) + .then(resp => resp.body.result), +); + +Cypress.Commands.add('deleteChartByName', (name: string) => + cy.getCharts().then((slices: any) => { + slices?.forEach((element: any) => { + if (element.slice_name === name) { + const elementId = element.id; + cy.deleteChart(elementId); + } + }); + }), +); diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 59582524ea16a..1518dc16471aa 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -275,7 +275,7 @@ }, "engines": { "node": "^16.9.1", - "npm": "^7.5.4" + "npm": "^7.5.4 || ^8.1.2" } }, "buildtools/eslint-plugin-theme-colors": { From d684ad073d63953d803cf05b4b208484f1fe7e51 Mon Sep 17 00:00:00 2001 From: Ville Brofeldt <33317356+villebro@users.noreply.github.com> Date: Fri, 1 Apr 2022 11:32:36 +0300 Subject: [PATCH 02/31] fix: request samples with default row limit (#19456) * fix: request samples with default row limit * lodashLint * fix cypress test --- .../integration/explore/control.test.ts | 2 +- .../src/explore/exploreUtils/index.js | 10 ++++++- superset/common/query_actions.py | 26 ++++++++++--------- superset/common/query_object.py | 4 +-- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts b/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts index 1b723ec65d406..271f86e9ed3b8 100644 --- a/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts +++ b/superset-frontend/cypress-base/cypress/integration/explore/control.test.ts @@ -129,7 +129,7 @@ describe('Test datatable', () => { it('Datapane loads view samples', () => { cy.get('[data-test="data-tab"]').click(); cy.contains('View samples').click(); - cy.get('[data-test="row-count-label"]').contains('10k rows retrieved'); + cy.get('[data-test="row-count-label"]').contains('1k rows retrieved'); cy.get('.ant-empty-description').should('not.exist'); }); }); diff --git a/superset-frontend/src/explore/exploreUtils/index.js b/superset-frontend/src/explore/exploreUtils/index.js index 679902243b4df..79bcb1a36241e 100644 --- a/superset-frontend/src/explore/exploreUtils/index.js +++ b/superset-frontend/src/explore/exploreUtils/index.js @@ -26,6 +26,7 @@ import { getChartBuildQueryRegistry, getChartMetadataRegistry, } from '@superset-ui/core'; +import { omit } from 'lodash'; import { availableDomains } from 'src/utils/hostNamesConfig'; import { safeStringify } from 'src/utils/safeStringify'; import { URL_PARAMS } from 'src/constants'; @@ -215,7 +216,7 @@ export const buildV1ChartDataPayload = ({ ...baseQueryObject, }, ])); - return buildQuery( + const payload = buildQuery( { ...formData, force, @@ -229,6 +230,13 @@ export const buildV1ChartDataPayload = ({ }, }, ); + if (resultType === 'samples') { + // remove row limit and offset to fall back to defaults + payload.queries = payload.queries.map(query => + omit(query, ['row_limit', 'row_offset']), + ); + } + return payload; }; export const getLegacyEndpointType = ({ resultType, resultFormat }) => diff --git a/superset/common/query_actions.py b/superset/common/query_actions.py index 2b85125b0e98a..5899757d528d8 100644 --- a/superset/common/query_actions.py +++ b/superset/common/query_actions.py @@ -14,6 +14,8 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +from __future__ import annotations + import copy from typing import Any, Callable, cast, Dict, List, Optional, TYPE_CHECKING @@ -41,13 +43,13 @@ def _get_datasource( - query_context: "QueryContext", query_obj: "QueryObject" + query_context: QueryContext, query_obj: QueryObject ) -> BaseDatasource: return query_obj.datasource or query_context.datasource def _get_columns( - query_context: "QueryContext", query_obj: "QueryObject", _: bool + query_context: QueryContext, query_obj: QueryObject, _: bool ) -> Dict[str, Any]: datasource = _get_datasource(query_context, query_obj) return { @@ -63,7 +65,7 @@ def _get_columns( def _get_timegrains( - query_context: "QueryContext", query_obj: "QueryObject", _: bool + query_context: QueryContext, query_obj: QueryObject, _: bool ) -> Dict[str, Any]: datasource = _get_datasource(query_context, query_obj) return { @@ -79,8 +81,8 @@ def _get_timegrains( def _get_query( - query_context: "QueryContext", - query_obj: "QueryObject", + query_context: QueryContext, + query_obj: QueryObject, _: bool, ) -> Dict[str, Any]: datasource = _get_datasource(query_context, query_obj) @@ -93,8 +95,8 @@ def _get_query( def _get_full( - query_context: "QueryContext", - query_obj: "QueryObject", + query_context: QueryContext, + query_obj: QueryObject, force_cached: Optional[bool] = False, ) -> Dict[str, Any]: datasource = _get_datasource(query_context, query_obj) @@ -140,7 +142,7 @@ def _get_full( def _get_samples( - query_context: "QueryContext", query_obj: "QueryObject", force_cached: bool = False + query_context: QueryContext, query_obj: QueryObject, force_cached: bool = False ) -> Dict[str, Any]: datasource = _get_datasource(query_context, query_obj) query_obj = copy.copy(query_obj) @@ -155,14 +157,14 @@ def _get_samples( def _get_results( - query_context: "QueryContext", query_obj: "QueryObject", force_cached: bool = False + query_context: QueryContext, query_obj: QueryObject, force_cached: bool = False ) -> Dict[str, Any]: payload = _get_full(query_context, query_obj, force_cached) return payload _result_type_functions: Dict[ - ChartDataResultType, Callable[["QueryContext", "QueryObject", bool], Dict[str, Any]] + ChartDataResultType, Callable[[QueryContext, QueryObject, bool], Dict[str, Any]] ] = { ChartDataResultType.COLUMNS: _get_columns, ChartDataResultType.TIMEGRAINS: _get_timegrains, @@ -179,8 +181,8 @@ def _get_results( def get_query_results( result_type: ChartDataResultType, - query_context: "QueryContext", - query_obj: "QueryObject", + query_context: QueryContext, + query_obj: QueryObject, force_cached: bool, ) -> Dict[str, Any]: """ diff --git a/superset/common/query_object.py b/superset/common/query_object.py index 78a76fc3cdee7..40d37041b9166 100644 --- a/superset/common/query_object.py +++ b/superset/common/query_object.py @@ -100,7 +100,7 @@ class QueryObject: # pylint: disable=too-many-instance-attributes orderby: List[OrderBy] post_processing: List[Dict[str, Any]] result_type: Optional[ChartDataResultType] - row_limit: int + row_limit: Optional[int] row_offset: int series_columns: List[Column] series_limit: int @@ -127,7 +127,7 @@ def __init__( # pylint: disable=too-many-locals order_desc: bool = True, orderby: Optional[List[OrderBy]] = None, post_processing: Optional[List[Optional[Dict[str, Any]]]] = None, - row_limit: int, + row_limit: Optional[int], row_offset: Optional[int] = None, series_columns: Optional[List[Column]] = None, series_limit: int = 0, From d954c3df8604dc4b6a2459a17dd39450a1d57638 Mon Sep 17 00:00:00 2001 From: Yongjie Zhao Date: Fri, 1 Apr 2022 16:40:50 +0800 Subject: [PATCH 03/31] fix: can't sync temporal flag on virtual table (#19366) --- .../cypress/integration/sqllab/query.test.ts | 4 +- .../src/SqlLab/components/ResultSet/index.tsx | 3 +- superset-frontend/src/SqlLab/fixtures.ts | 38 +++--- superset-frontend/src/SqlLab/types.ts | 4 +- superset/connectors/sqla/models.py | 3 +- superset/connectors/sqla/utils.py | 3 +- superset/db_engine_specs/base.py | 7 +- superset/db_engine_specs/presto.py | 34 ++++-- superset/result_set.py | 8 +- superset/superset_typing.py | 10 ++ superset/views/core.py | 2 +- tests/integration_tests/celery_tests.py | 4 +- .../db_engine_specs/presto_tests.py | 114 +++++++++++++----- tests/integration_tests/result_set_tests.py | 20 +-- tests/integration_tests/sqllab_tests.py | 8 +- 15 files changed, 170 insertions(+), 92 deletions(-) diff --git a/superset-frontend/cypress-base/cypress/integration/sqllab/query.test.ts b/superset-frontend/cypress-base/cypress/integration/sqllab/query.test.ts index ea43c66f97a61..f5033313fcaa2 100644 --- a/superset-frontend/cypress-base/cypress/integration/sqllab/query.test.ts +++ b/superset-frontend/cypress-base/cypress/integration/sqllab/query.test.ts @@ -37,8 +37,8 @@ describe('SqlLab query panel', () => { const sampleResponse = { status: 'success', data: [{ '?column?': 1 }], - columns: [{ name: '?column?', type: 'INT', is_date: false }], - selected_columns: [{ name: '?column?', type: 'INT', is_date: false }], + columns: [{ name: '?column?', type: 'INT', is_dttm: false }], + selected_columns: [{ name: '?column?', type: 'INT', is_dttm: false }], expanded_columns: [], }; diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx index dbde25ef2b4f2..b81b4714628ce 100644 --- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx +++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx @@ -280,7 +280,8 @@ export default class ResultSet extends React.PureComponent< sql, results.selected_columns.map(d => ({ column_name: d.name, - is_dttm: d.is_date, + type: d.type, + is_dttm: d.is_dttm, })), datasetToOverwrite.owners.map((o: DatasetOwner) => o.id), true, diff --git a/superset-frontend/src/SqlLab/fixtures.ts b/superset-frontend/src/SqlLab/fixtures.ts index 5725bcf75e1f1..5b12ee29213ee 100644 --- a/superset-frontend/src/SqlLab/fixtures.ts +++ b/superset-frontend/src/SqlLab/fixtures.ts @@ -221,24 +221,24 @@ export const queries = [ results: { columns: [ { - is_date: true, + is_dttm: true, name: 'ds', type: 'STRING', }, { - is_date: false, + is_dttm: false, name: 'gender', type: 'STRING', }, ], selected_columns: [ { - is_date: true, + is_dttm: true, name: 'ds', type: 'STRING', }, { - is_date: false, + is_dttm: false, name: 'gender', type: 'STRING', }, @@ -313,24 +313,24 @@ export const queryWithNoQueryLimit = { results: { columns: [ { - is_date: true, + is_dttm: true, name: 'ds', type: 'STRING', }, { - is_date: false, + is_dttm: false, name: 'gender', type: 'STRING', }, ], selected_columns: [ { - is_date: true, + is_dttm: true, name: 'ds', type: 'STRING', }, { - is_date: false, + is_dttm: false, name: 'gender', type: 'STRING', }, @@ -350,57 +350,57 @@ export const queryWithBadColumns = { data: queries[0].results?.data, selected_columns: [ { - is_date: true, + is_dttm: true, name: 'COUNT(*)', type: 'STRING', }, { - is_date: false, + is_dttm: false, name: 'this_col_is_ok', type: 'STRING', }, { - is_date: false, + is_dttm: false, name: 'a', type: 'STRING', }, { - is_date: false, + is_dttm: false, name: '1', type: 'STRING', }, { - is_date: false, + is_dttm: false, name: '123', type: 'STRING', }, { - is_date: false, + is_dttm: false, name: 'CASE WHEN 1=1 THEN 1 ELSE 0 END', type: 'STRING', }, { - is_date: true, + is_dttm: true, name: '_TIMESTAMP', type: 'TIMESTAMP', }, { - is_date: true, + is_dttm: true, name: '__TIME', type: 'TIMESTAMP', }, { - is_date: false, + is_dttm: false, name: 'my_dupe_col__2', type: 'STRING', }, { - is_date: true, + is_dttm: true, name: '__timestamp', type: 'TIMESTAMP', }, { - is_date: true, + is_dttm: true, name: '__TIMESTAMP', type: 'TIMESTAMP', }, diff --git a/superset-frontend/src/SqlLab/types.ts b/superset-frontend/src/SqlLab/types.ts index f1e088f003ee1..d5dfddbe2bbd5 100644 --- a/superset-frontend/src/SqlLab/types.ts +++ b/superset-frontend/src/SqlLab/types.ts @@ -21,9 +21,11 @@ import { CtasEnum } from 'src/SqlLab/actions/sqlLab'; import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; import { ToastType } from 'src/components/MessageToasts/types'; +// same as superset.result_set.ResultSetColumnType export type Column = { name: string; - is_date?: boolean; + type: string | null; + is_dttm: boolean; }; export type QueryState = diff --git a/superset/connectors/sqla/models.py b/superset/connectors/sqla/models.py index 0600f3b10a582..d6a7d3e3a2645 100644 --- a/superset/connectors/sqla/models.py +++ b/superset/connectors/sqla/models.py @@ -687,8 +687,9 @@ def sql_url(self) -> str: return self.database.sql_url + "?table_name=" + str(self.table_name) def external_metadata(self) -> List[Dict[str, str]]: + # todo(yongjie): create a pysical table column type in seprated PR if self.sql: - return get_virtual_table_metadata(dataset=self) + return get_virtual_table_metadata(dataset=self) # type: ignore return get_physical_table_metadata( database=self.database, table_name=self.table_name, diff --git a/superset/connectors/sqla/utils.py b/superset/connectors/sqla/utils.py index 766b74e57c004..14b9071d1da10 100644 --- a/superset/connectors/sqla/utils.py +++ b/superset/connectors/sqla/utils.py @@ -34,6 +34,7 @@ from superset.models.core import Database from superset.result_set import SupersetResultSet from superset.sql_parse import has_table_query, insert_rls, ParsedQuery, Table +from superset.superset_typing import ResultSetColumnType from superset.tables.models import Table as NewTable if TYPE_CHECKING: @@ -91,7 +92,7 @@ def get_physical_table_metadata( return cols -def get_virtual_table_metadata(dataset: "SqlaTable") -> List[Dict[str, str]]: +def get_virtual_table_metadata(dataset: "SqlaTable") -> List[ResultSetColumnType]: """Use SQLparser to get virtual dataset metadata""" if not dataset.sql: raise SupersetGenericDBErrorException( diff --git a/superset/db_engine_specs/base.py b/superset/db_engine_specs/base.py index 200c7c8eac83c..e60ce78d72b10 100644 --- a/superset/db_engine_specs/base.py +++ b/superset/db_engine_specs/base.py @@ -62,6 +62,7 @@ from superset.models.sql_lab import Query from superset.models.sql_types.base import literal_dttm_type_factory from superset.sql_parse import ParsedQuery, Table +from superset.superset_typing import ResultSetColumnType from superset.utils import core as utils from superset.utils.core import ColumnSpec, GenericDataType from superset.utils.hashing import md5_sha_from_str @@ -566,8 +567,10 @@ def fetch_data( @classmethod def expand_data( - cls, columns: List[Dict[Any, Any]], data: List[Dict[Any, Any]] - ) -> Tuple[List[Dict[Any, Any]], List[Dict[Any, Any]], List[Dict[Any, Any]]]: + cls, columns: List[ResultSetColumnType], data: List[Dict[Any, Any]] + ) -> Tuple[ + List[ResultSetColumnType], List[Dict[Any, Any]], List[ResultSetColumnType] + ]: """ Some engines support expanding nested fields. See implementation in Presto spec for details. diff --git a/superset/db_engine_specs/presto.py b/superset/db_engine_specs/presto.py index 60cb9c7acaca6..77c3d4c2e3383 100644 --- a/superset/db_engine_specs/presto.py +++ b/superset/db_engine_specs/presto.py @@ -53,6 +53,7 @@ ) from superset.result_set import destringify from superset.sql_parse import ParsedQuery +from superset.superset_typing import ResultSetColumnType from superset.utils import core as utils from superset.utils.core import ColumnSpec, GenericDataType @@ -85,24 +86,26 @@ logger = logging.getLogger(__name__) -def get_children(column: Dict[str, str]) -> List[Dict[str, str]]: +def get_children(column: ResultSetColumnType) -> List[ResultSetColumnType]: """ Get the children of a complex Presto type (row or array). For arrays, we return a single list with the base type: - >>> get_children(dict(name="a", type="ARRAY(BIGINT)")) - [{"name": "a", "type": "BIGINT"}] + >>> get_children(dict(name="a", type="ARRAY(BIGINT)", is_dttm=False)) + [{"name": "a", "type": "BIGINT", "is_dttm": False}] For rows, we return a list of the columns: - >>> get_children(dict(name="a", type="ROW(BIGINT,FOO VARCHAR)")) - [{'name': 'a._col0', 'type': 'BIGINT'}, {'name': 'a.foo', 'type': 'VARCHAR'}] + >>> get_children(dict(name="a", type="ROW(BIGINT,FOO VARCHAR)", is_dttm=False)) + [{'name': 'a._col0', 'type': 'BIGINT', 'is_dttm': False}, {'name': 'a.foo', 'type': 'VARCHAR', 'is_dttm': False}] # pylint: disable=line-too-long :param column: dictionary representing a Presto column :return: list of dictionaries representing children columns """ pattern = re.compile(r"(?P\w+)\((?P.*)\)") + if not column["type"]: + raise ValueError match = pattern.match(column["type"]) if not match: raise Exception(f"Unable to parse column type {column['type']}") @@ -111,7 +114,7 @@ def get_children(column: Dict[str, str]) -> List[Dict[str, str]]: type_ = group["type"].upper() children_type = group["children"] if type_ == "ARRAY": - return [{"name": column["name"], "type": children_type}] + return [{"name": column["name"], "type": children_type, "is_dttm": False}] if type_ == "ROW": nameless_columns = 0 @@ -125,7 +128,12 @@ def get_children(column: Dict[str, str]) -> List[Dict[str, str]]: name = f"_col{nameless_columns}" type_ = parts[0] nameless_columns += 1 - columns.append({"name": f"{column['name']}.{name.lower()}", "type": type_}) + _column: ResultSetColumnType = { + "name": f"{column['name']}.{name.lower()}", + "type": type_, + "is_dttm": False, + } + columns.append(_column) return columns raise Exception(f"Unknown type {type_}!") @@ -779,8 +787,10 @@ def get_all_datasource_names( @classmethod def expand_data( # pylint: disable=too-many-locals - cls, columns: List[Dict[Any, Any]], data: List[Dict[Any, Any]] - ) -> Tuple[List[Dict[Any, Any]], List[Dict[Any, Any]], List[Dict[Any, Any]]]: + cls, columns: List[ResultSetColumnType], data: List[Dict[Any, Any]] + ) -> Tuple[ + List[ResultSetColumnType], List[Dict[Any, Any]], List[ResultSetColumnType] + ]: """ We do not immediately display rows and arrays clearly in the data grid. This method separates out nested fields and data values to help clearly display @@ -808,7 +818,7 @@ def expand_data( # pylint: disable=too-many-locals # process each column, unnesting ARRAY types and # expanding ROW types into new columns to_process = deque((column, 0) for column in columns) - all_columns: List[Dict[str, Any]] = [] + all_columns: List[ResultSetColumnType] = [] expanded_columns = [] current_array_level = None while to_process: @@ -828,7 +838,7 @@ def expand_data( # pylint: disable=too-many-locals name = column["name"] values: Optional[Union[str, List[Any]]] - if column["type"].startswith("ARRAY("): + if column["type"] and column["type"].startswith("ARRAY("): # keep processing array children; we append to the right so that # multiple nested arrays are processed breadth-first to_process.append((get_children(column)[0], level + 1)) @@ -862,7 +872,7 @@ def expand_data( # pylint: disable=too-many-locals i += 1 - if column["type"].startswith("ROW("): + if column["type"] and column["type"].startswith("ROW("): # expand columns; we append them to the left so they are added # immediately after the parent expanded = get_children(column) diff --git a/superset/result_set.py b/superset/result_set.py index 19035b6d23788..82b0a313935e3 100644 --- a/superset/result_set.py +++ b/superset/result_set.py @@ -26,7 +26,7 @@ import pyarrow as pa from superset.db_engine_specs import BaseEngineSpec -from superset.superset_typing import DbapiDescription, DbapiResult +from superset.superset_typing import DbapiDescription, DbapiResult, ResultSetColumnType from superset.utils import core as utils logger = logging.getLogger(__name__) @@ -210,17 +210,17 @@ def size(self) -> int: return self.table.num_rows @property - def columns(self) -> List[Dict[str, Any]]: + def columns(self) -> List[ResultSetColumnType]: if not self.table.column_names: return [] columns = [] for col in self.table.schema: db_type_str = self.data_type(col.name, col.type) - column = { + column: ResultSetColumnType = { "name": col.name, "type": db_type_str, - "is_date": self.is_temporal(db_type_str), + "is_dttm": self.is_temporal(db_type_str), } columns.append(column) diff --git a/superset/superset_typing.py b/superset/superset_typing.py index 253d2b63551a8..1af04494d0c95 100644 --- a/superset/superset_typing.py +++ b/superset/superset_typing.py @@ -57,6 +57,16 @@ class AdhocColumn(TypedDict, total=False): sqlExpression: Optional[str] +class ResultSetColumnType(TypedDict): + """ + Superset virtual dataset column interface + """ + + name: str + type: Optional[str] + is_dttm: bool + + CacheConfig = Dict[str, Any] DbapiDescriptionRow = Tuple[ str, str, Optional[str], Optional[str], Optional[int], Optional[int], bool diff --git a/superset/views/core.py b/superset/views/core.py index c806470e8f1de..9c52c3c0f51af 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -2109,7 +2109,7 @@ def sqllab_viz(self) -> FlaskResponse: # pylint: disable=no-self-use column_name=column_name, filterable=True, groupby=True, - is_dttm=config_.get("is_date", False), + is_dttm=config_.get("is_dttm", False), type=config_.get("type", False), ) cols.append(col) diff --git a/tests/integration_tests/celery_tests.py b/tests/integration_tests/celery_tests.py index 802684ba3bd07..3d4ba5e901f08 100644 --- a/tests/integration_tests/celery_tests.py +++ b/tests/integration_tests/celery_tests.py @@ -130,8 +130,8 @@ def cta_result(ctas_method: CtasMethod): if backend() != "presto": return [], [] if ctas_method == CtasMethod.TABLE: - return [{"rows": 1}], [{"name": "rows", "type": "BIGINT", "is_date": False}] - return [{"result": True}], [{"name": "result", "type": "BOOLEAN", "is_date": False}] + return [{"rows": 1}], [{"name": "rows", "type": "BIGINT", "is_dttm": False}] + return [{"result": True}], [{"name": "result", "type": "BOOLEAN", "is_dttm": False}] # TODO(bkyryliuk): quote table and schema names for all databases diff --git a/tests/integration_tests/db_engine_specs/presto_tests.py b/tests/integration_tests/db_engine_specs/presto_tests.py index 558f4322a0e5d..17c7c2d900e7f 100644 --- a/tests/integration_tests/db_engine_specs/presto_tests.py +++ b/tests/integration_tests/db_engine_specs/presto_tests.py @@ -207,8 +207,8 @@ def test_presto_get_fields(self): ) def test_presto_expand_data_with_simple_structural_columns(self): cols = [ - {"name": "row_column", "type": "ROW(NESTED_OBJ VARCHAR)"}, - {"name": "array_column", "type": "ARRAY(BIGINT)"}, + {"name": "row_column", "type": "ROW(NESTED_OBJ VARCHAR)", "is_dttm": False}, + {"name": "array_column", "type": "ARRAY(BIGINT)", "is_dttm": False}, ] data = [ {"row_column": ["a"], "array_column": [1, 2, 3]}, @@ -218,9 +218,9 @@ def test_presto_expand_data_with_simple_structural_columns(self): cols, data ) expected_cols = [ - {"name": "row_column", "type": "ROW(NESTED_OBJ VARCHAR)"}, - {"name": "row_column.nested_obj", "type": "VARCHAR"}, - {"name": "array_column", "type": "ARRAY(BIGINT)"}, + {"name": "row_column", "type": "ROW(NESTED_OBJ VARCHAR)", "is_dttm": False}, + {"name": "row_column.nested_obj", "type": "VARCHAR", "is_dttm": False}, + {"name": "array_column", "type": "ARRAY(BIGINT)", "is_dttm": False}, ] expected_data = [ @@ -232,7 +232,9 @@ def test_presto_expand_data_with_simple_structural_columns(self): {"array_column": 6, "row_column": "", "row_column.nested_obj": ""}, ] - expected_expanded_cols = [{"name": "row_column.nested_obj", "type": "VARCHAR"}] + expected_expanded_cols = [ + {"name": "row_column.nested_obj", "type": "VARCHAR", "is_dttm": False} + ] self.assertEqual(actual_cols, expected_cols) self.assertEqual(actual_data, expected_data) self.assertEqual(actual_expanded_cols, expected_expanded_cols) @@ -247,6 +249,7 @@ def test_presto_expand_data_with_complex_row_columns(self): { "name": "row_column", "type": "ROW(NESTED_OBJ1 VARCHAR, NESTED_ROW ROW(NESTED_OBJ2 VARCHAR))", + "is_dttm": False, } ] data = [{"row_column": ["a1", ["a2"]]}, {"row_column": ["b1", ["b2"]]}] @@ -257,10 +260,19 @@ def test_presto_expand_data_with_complex_row_columns(self): { "name": "row_column", "type": "ROW(NESTED_OBJ1 VARCHAR, NESTED_ROW ROW(NESTED_OBJ2 VARCHAR))", + "is_dttm": False, + }, + {"name": "row_column.nested_obj1", "type": "VARCHAR", "is_dttm": False}, + { + "name": "row_column.nested_row", + "type": "ROW(NESTED_OBJ2 VARCHAR)", + "is_dttm": False, + }, + { + "name": "row_column.nested_row.nested_obj2", + "type": "VARCHAR", + "is_dttm": False, }, - {"name": "row_column.nested_obj1", "type": "VARCHAR"}, - {"name": "row_column.nested_row", "type": "ROW(NESTED_OBJ2 VARCHAR)"}, - {"name": "row_column.nested_row.nested_obj2", "type": "VARCHAR"}, ] expected_data = [ { @@ -278,9 +290,17 @@ def test_presto_expand_data_with_complex_row_columns(self): ] expected_expanded_cols = [ - {"name": "row_column.nested_obj1", "type": "VARCHAR"}, - {"name": "row_column.nested_row", "type": "ROW(NESTED_OBJ2 VARCHAR)"}, - {"name": "row_column.nested_row.nested_obj2", "type": "VARCHAR"}, + {"name": "row_column.nested_obj1", "type": "VARCHAR", "is_dttm": False}, + { + "name": "row_column.nested_row", + "type": "ROW(NESTED_OBJ2 VARCHAR)", + "is_dttm": False, + }, + { + "name": "row_column.nested_row.nested_obj2", + "type": "VARCHAR", + "is_dttm": False, + }, ] self.assertEqual(actual_cols, expected_cols) self.assertEqual(actual_data, expected_data) @@ -296,6 +316,7 @@ def test_presto_expand_data_with_complex_row_columns_and_null_values(self): { "name": "row_column", "type": "ROW(NESTED_ROW ROW(NESTED_OBJ VARCHAR))", + "is_dttm": False, } ] data = [ @@ -311,9 +332,18 @@ def test_presto_expand_data_with_complex_row_columns_and_null_values(self): { "name": "row_column", "type": "ROW(NESTED_ROW ROW(NESTED_OBJ VARCHAR))", + "is_dttm": False, + }, + { + "name": "row_column.nested_row", + "type": "ROW(NESTED_OBJ VARCHAR)", + "is_dttm": False, + }, + { + "name": "row_column.nested_row.nested_obj", + "type": "VARCHAR", + "is_dttm": False, }, - {"name": "row_column.nested_row", "type": "ROW(NESTED_OBJ VARCHAR)"}, - {"name": "row_column.nested_row.nested_obj", "type": "VARCHAR"}, ] expected_data = [ { @@ -339,8 +369,16 @@ def test_presto_expand_data_with_complex_row_columns_and_null_values(self): ] expected_expanded_cols = [ - {"name": "row_column.nested_row", "type": "ROW(NESTED_OBJ VARCHAR)"}, - {"name": "row_column.nested_row.nested_obj", "type": "VARCHAR"}, + { + "name": "row_column.nested_row", + "type": "ROW(NESTED_OBJ VARCHAR)", + "is_dttm": False, + }, + { + "name": "row_column.nested_row.nested_obj", + "type": "VARCHAR", + "is_dttm": False, + }, ] self.assertEqual(actual_cols, expected_cols) self.assertEqual(actual_data, expected_data) @@ -353,10 +391,11 @@ def test_presto_expand_data_with_complex_row_columns_and_null_values(self): ) def test_presto_expand_data_with_complex_array_columns(self): cols = [ - {"name": "int_column", "type": "BIGINT"}, + {"name": "int_column", "type": "BIGINT", "is_dttm": False}, { "name": "array_column", "type": "ARRAY(ROW(NESTED_ARRAY ARRAY(ROW(NESTED_OBJ VARCHAR))))", + "is_dttm": False, }, ] data = [ @@ -367,16 +406,22 @@ def test_presto_expand_data_with_complex_array_columns(self): cols, data ) expected_cols = [ - {"name": "int_column", "type": "BIGINT"}, + {"name": "int_column", "type": "BIGINT", "is_dttm": False}, { "name": "array_column", "type": "ARRAY(ROW(NESTED_ARRAY ARRAY(ROW(NESTED_OBJ VARCHAR))))", + "is_dttm": False, }, { "name": "array_column.nested_array", "type": "ARRAY(ROW(NESTED_OBJ VARCHAR))", + "is_dttm": False, + }, + { + "name": "array_column.nested_array.nested_obj", + "type": "VARCHAR", + "is_dttm": False, }, - {"name": "array_column.nested_array.nested_obj", "type": "VARCHAR"}, ] expected_data = [ { @@ -432,8 +477,13 @@ def test_presto_expand_data_with_complex_array_columns(self): { "name": "array_column.nested_array", "type": "ARRAY(ROW(NESTED_OBJ VARCHAR))", + "is_dttm": False, + }, + { + "name": "array_column.nested_array.nested_obj", + "type": "VARCHAR", + "is_dttm": False, }, - {"name": "array_column.nested_array.nested_obj", "type": "VARCHAR"}, ] self.assertEqual(actual_cols, expected_cols) self.assertEqual(actual_data, expected_data) @@ -545,12 +595,12 @@ def test_query_cost_formatter(self): ) def test_presto_expand_data_array(self): cols = [ - {"name": "event_id", "type": "VARCHAR", "is_date": False}, - {"name": "timestamp", "type": "BIGINT", "is_date": False}, + {"name": "event_id", "type": "VARCHAR", "is_dttm": False}, + {"name": "timestamp", "type": "BIGINT", "is_dttm": False}, { "name": "user", "type": "ROW(ID BIGINT, FIRST_NAME VARCHAR, LAST_NAME VARCHAR)", - "is_date": False, + "is_dttm": False, }, ] data = [ @@ -564,16 +614,16 @@ def test_presto_expand_data_array(self): cols, data ) expected_cols = [ - {"name": "event_id", "type": "VARCHAR", "is_date": False}, - {"name": "timestamp", "type": "BIGINT", "is_date": False}, + {"name": "event_id", "type": "VARCHAR", "is_dttm": False}, + {"name": "timestamp", "type": "BIGINT", "is_dttm": False}, { "name": "user", "type": "ROW(ID BIGINT, FIRST_NAME VARCHAR, LAST_NAME VARCHAR)", - "is_date": False, + "is_dttm": False, }, - {"name": "user.id", "type": "BIGINT"}, - {"name": "user.first_name", "type": "VARCHAR"}, - {"name": "user.last_name", "type": "VARCHAR"}, + {"name": "user.id", "type": "BIGINT", "is_dttm": False}, + {"name": "user.first_name", "type": "VARCHAR", "is_dttm": False}, + {"name": "user.last_name", "type": "VARCHAR", "is_dttm": False}, ] expected_data = [ { @@ -586,9 +636,9 @@ def test_presto_expand_data_array(self): } ] expected_expanded_cols = [ - {"name": "user.id", "type": "BIGINT"}, - {"name": "user.first_name", "type": "VARCHAR"}, - {"name": "user.last_name", "type": "VARCHAR"}, + {"name": "user.id", "type": "BIGINT", "is_dttm": False}, + {"name": "user.first_name", "type": "VARCHAR", "is_dttm": False}, + {"name": "user.last_name", "type": "VARCHAR", "is_dttm": False}, ] self.assertEqual(actual_cols, expected_cols) diff --git a/tests/integration_tests/result_set_tests.py b/tests/integration_tests/result_set_tests.py index bd44661d8d4f0..626468fc5aae0 100644 --- a/tests/integration_tests/result_set_tests.py +++ b/tests/integration_tests/result_set_tests.py @@ -48,9 +48,9 @@ def test_get_columns_basic(self): self.assertEqual( results.columns, [ - {"is_date": False, "type": "STRING", "name": "a"}, - {"is_date": False, "type": "STRING", "name": "b"}, - {"is_date": False, "type": "STRING", "name": "c"}, + {"is_dttm": False, "type": "STRING", "name": "a"}, + {"is_dttm": False, "type": "STRING", "name": "b"}, + {"is_dttm": False, "type": "STRING", "name": "c"}, ], ) @@ -61,8 +61,8 @@ def test_get_columns_with_int(self): self.assertEqual( results.columns, [ - {"is_date": False, "type": "STRING", "name": "a"}, - {"is_date": False, "type": "INT", "name": "b"}, + {"is_dttm": False, "type": "STRING", "name": "a"}, + {"is_dttm": False, "type": "INT", "name": "b"}, ], ) @@ -76,11 +76,11 @@ def test_get_columns_type_inference(self): self.assertEqual( results.columns, [ - {"is_date": False, "type": "FLOAT", "name": "a"}, - {"is_date": False, "type": "INT", "name": "b"}, - {"is_date": False, "type": "STRING", "name": "c"}, - {"is_date": True, "type": "DATETIME", "name": "d"}, - {"is_date": False, "type": "BOOL", "name": "e"}, + {"is_dttm": False, "type": "FLOAT", "name": "a"}, + {"is_dttm": False, "type": "INT", "name": "b"}, + {"is_dttm": False, "type": "STRING", "name": "c"}, + {"is_dttm": True, "type": "DATETIME", "name": "d"}, + {"is_dttm": False, "type": "BOOL", "name": "e"}, ], ) diff --git a/tests/integration_tests/sqllab_tests.py b/tests/integration_tests/sqllab_tests.py index 5a98ddebfa945..49c6a771e5ad3 100644 --- a/tests/integration_tests/sqllab_tests.py +++ b/tests/integration_tests/sqllab_tests.py @@ -477,8 +477,8 @@ def test_sqllab_viz(self): "datasourceName": f"test_viz_flow_table_{random()}", "schema": "superset", "columns": [ - {"is_date": False, "type": "STRING", "name": f"viz_type_{random()}"}, - {"is_date": False, "type": "OBJECT", "name": f"ccount_{random()}"}, + {"is_dttm": False, "type": "STRING", "name": f"viz_type_{random()}"}, + {"is_dttm": False, "type": "OBJECT", "name": f"ccount_{random()}"}, ], "sql": """\ SELECT * @@ -507,8 +507,8 @@ def test_sqllab_viz_bad_payload(self): "chartType": "dist_bar", "schema": "superset", "columns": [ - {"is_date": False, "type": "STRING", "name": f"viz_type_{random()}"}, - {"is_date": False, "type": "OBJECT", "name": f"ccount_{random()}"}, + {"is_dttm": False, "type": "STRING", "name": f"viz_type_{random()}"}, + {"is_dttm": False, "type": "OBJECT", "name": f"ccount_{random()}"}, ], "sql": """\ SELECT * From a2e921bd035ff1af54c22ecc8e4203af68c6baa8 Mon Sep 17 00:00:00 2001 From: Yongjie Zhao Date: Fri, 1 Apr 2022 16:47:56 +0800 Subject: [PATCH 04/31] fix: flaky test for dashboard changed on (#19483) --- tests/integration_tests/charts/api_tests.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/integration_tests/charts/api_tests.py b/tests/integration_tests/charts/api_tests.py index 3c92caceead73..6b8d625d567e3 100644 --- a/tests/integration_tests/charts/api_tests.py +++ b/tests/integration_tests/charts/api_tests.py @@ -17,11 +17,9 @@ # isort:skip_file """Unit tests for Superset""" import json -from datetime import datetime from io import BytesIO from zipfile import is_zipfile, ZipFile -import humanize import prison import pytest import yaml @@ -803,7 +801,6 @@ def test_get_charts_changed_on(self): Dashboard API: Test get charts changed on """ admin = self.get_user("admin") - start_changed_on = datetime.now() chart = self.insert_chart("foo_a", [admin.id], 1, description="ZY_bar") self.login(username="admin") @@ -817,9 +814,9 @@ def test_get_charts_changed_on(self): rv = self.get_assert_metric(uri, "get_list") self.assertEqual(rv.status_code, 200) data = json.loads(rv.data.decode("utf-8")) - self.assertEqual( - data["result"][0]["changed_on_delta_humanized"], - humanize.naturaltime(datetime.now() - start_changed_on), + assert data["result"][0]["changed_on_delta_humanized"] in ( + "now", + "a second ago", ) # rollback changes From 11bf0d09cb854b07492947bb8d02dde55e0b8d04 Mon Sep 17 00:00:00 2001 From: Ville Brofeldt <33317356+villebro@users.noreply.github.com> Date: Fri, 1 Apr 2022 13:05:03 +0300 Subject: [PATCH 05/31] chore(plugin-chart-echarts): upgrade echarts 5.3.2 (#19481) --- superset-frontend/package-lock.json | 29 +++++++++---------- .../plugins/plugin-chart-echarts/package.json | 2 +- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 1518dc16471aa..de2d1a60f81f4 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -32151,9 +32151,9 @@ } }, "node_modules/echarts": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.3.1.tgz", - "integrity": "sha512-nWdlbgX3OVY0hpqncSvp0gDt1FRSKWn7lsWEH+PHmfCuvE0QmSw17pczQvm8AvawnLEkmf1Cts7YwQJZNC0AEQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.3.2.tgz", + "integrity": "sha512-LWCt7ohOKdJqyiBJ0OGBmE9szLdfA9sGcsMEi+GGoc6+Xo75C+BkcT/6NNGRHAWtnQl2fNow05AQjznpap28TQ==", "dependencies": { "tslib": "2.3.0", "zrender": "5.3.1" @@ -59028,6 +59028,7 @@ "prop-types": "^15.6.2" }, "peerDependencies": { + "@emotion/react": "^11.4.1", "@superset-ui/chart-controls": "*", "@superset-ui/core": "*", "react": "^16.13.1" @@ -59116,8 +59117,10 @@ "prop-types": "^15.6.2" }, "peerDependencies": { + "@emotion/react": "^11.4.1", "@superset-ui/chart-controls": "*", - "@superset-ui/core": "*" + "@superset-ui/core": "*", + "react": "^16.13.1" } }, "plugins/legacy-plugin-chart-histogram": { @@ -59536,7 +59539,7 @@ "license": "Apache-2.0", "dependencies": { "d3-array": "^1.2.0", - "echarts": "^5.3.1", + "echarts": "^5.3.2", "lodash": "^4.17.15", "moment": "^2.26.0" }, @@ -59782,9 +59785,6 @@ "version": "1.0.0", "dev": true, "license": "Apache-2.0", - "dependencies": { - "lodash": "^4.17.21" - }, "engines": { "node": "^16.9.1", "npm": "^7.5.4" @@ -76931,7 +76931,7 @@ "version": "file:plugins/plugin-chart-echarts", "requires": { "d3-array": "^1.2.0", - "echarts": "^5.3.1", + "echarts": "^5.3.2", "lodash": "^4.17.15", "moment": "^2.26.0" } @@ -85138,9 +85138,9 @@ } }, "echarts": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.3.1.tgz", - "integrity": "sha512-nWdlbgX3OVY0hpqncSvp0gDt1FRSKWn7lsWEH+PHmfCuvE0QmSw17pczQvm8AvawnLEkmf1Cts7YwQJZNC0AEQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.3.2.tgz", + "integrity": "sha512-LWCt7ohOKdJqyiBJ0OGBmE9szLdfA9sGcsMEi+GGoc6+Xo75C+BkcT/6NNGRHAWtnQl2fNow05AQjznpap28TQ==", "requires": { "tslib": "2.3.0", "zrender": "5.3.1" @@ -86468,10 +86468,7 @@ } }, "eslint-plugin-theme-colors": { - "version": "file:tools/eslint-plugin-theme-colors", - "requires": { - "lodash": "^4.17.21" - } + "version": "file:tools/eslint-plugin-theme-colors" }, "eslint-scope": { "version": "5.1.1", diff --git a/superset-frontend/plugins/plugin-chart-echarts/package.json b/superset-frontend/plugins/plugin-chart-echarts/package.json index 8d1e559258b8e..0c5cbcf595ae0 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/package.json +++ b/superset-frontend/plugins/plugin-chart-echarts/package.json @@ -27,7 +27,7 @@ }, "dependencies": { "d3-array": "^1.2.0", - "echarts": "^5.3.1", + "echarts": "^5.3.2", "lodash": "^4.17.15", "moment": "^2.26.0" }, From fc8cb223761ef78f888d5d4dbf038caa1028b277 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Fri, 1 Apr 2022 15:23:00 +0200 Subject: [PATCH 06/31] feat(dashboard): Implement empty states for empty tabs (#19408) * feat(dashboard): Implement empty states in empty tabs * Change button to in text link * Add edit dashboard button to dashboard empty state * Add tests * Fix test --- .../src/components/EmptyState/index.tsx | 9 ++ .../DashboardBuilder/DashboardBuilder.tsx | 7 +- .../dashboard/components/DashboardGrid.jsx | 83 +++++++++++++++---- .../components/gridComponents/Tab.jsx | 67 ++++++++++++++- .../components/gridComponents/Tab.test.tsx | 77 +++++++++++++++-- .../dashboard/containers/DashboardGrid.jsx | 6 +- .../src/dashboard/stylesheets/builder.less | 1 + 7 files changed, 220 insertions(+), 30 deletions(-) diff --git a/superset-frontend/src/components/EmptyState/index.tsx b/superset-frontend/src/components/EmptyState/index.tsx index d3e0c5701a1eb..02c1d7c4a2959 100644 --- a/superset-frontend/src/components/EmptyState/index.tsx +++ b/superset-frontend/src/components/EmptyState/index.tsx @@ -58,6 +58,15 @@ const EmptyStateContainer = styled.div` & .ant-empty-image svg { width: auto; } + + & a, + & span[role='button'] { + color: inherit; + text-decoration: underline; + &:hover { + color: ${theme.colors.grayscale.base}; + } + } `} `; diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx index 4f7ef563ce55f..ab1e95b9a1b47 100644 --- a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx +++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx @@ -34,7 +34,10 @@ import getDirectPathToTabIndex from 'src/dashboard/util/getDirectPathToTabIndex' import { URL_PARAMS } from 'src/constants'; import { getUrlParam } from 'src/utils/urlUtils'; import { DashboardLayout, RootState } from 'src/dashboard/types'; -import { setDirectPathToChild } from 'src/dashboard/actions/dashboardState'; +import { + setDirectPathToChild, + setEditMode, +} from 'src/dashboard/actions/dashboardState'; import { useElementOnScreen } from 'src/hooks/useElementOnScreen'; import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags'; import { @@ -398,6 +401,8 @@ const DashboardBuilder: FC = () => { 'Go to the edit mode to configure the dashboard and add charts', ) } + buttonText={canEdit && t('Edit the dashboard')} + buttonAction={() => dispatch(setEditMode(true))} image="dashboard.svg" /> )} diff --git a/superset-frontend/src/dashboard/components/DashboardGrid.jsx b/superset-frontend/src/dashboard/components/DashboardGrid.jsx index 5a9d2ff812786..4be8d6bc05d0f 100644 --- a/superset-frontend/src/dashboard/components/DashboardGrid.jsx +++ b/superset-frontend/src/dashboard/components/DashboardGrid.jsx @@ -24,6 +24,7 @@ import { componentShape } from '../util/propShapes'; import DashboardComponent from '../containers/DashboardComponent'; import DragDroppable from './dnd/DragDroppable'; import { GRID_GUTTER_SIZE, GRID_COLUMN_COUNT } from '../util/constants'; +import { TAB_TYPE } from '../util/componentTypes'; const propTypes = { depth: PropTypes.number.isRequired, @@ -137,9 +138,11 @@ class DashboardGrid extends React.PureComponent { gridComponent, handleComponentDrop, depth, - editMode, width, isComponentVisible, + editMode, + canEdit, + setEditMode, } = this.props; const columnPlusGutterWidth = (width + GRID_GUTTER_SIZE) / GRID_COLUMN_COUNT; @@ -147,26 +150,70 @@ class DashboardGrid extends React.PureComponent { const columnWidth = columnPlusGutterWidth - GRID_GUTTER_SIZE; const { isResizing, rowGuideTop } = this.state; + const shouldDisplayEmptyState = gridComponent?.children?.length === 0; + const shouldDisplayTopLevelTabEmptyState = + shouldDisplayEmptyState && gridComponent.type === TAB_TYPE; + + const dashboardEmptyState = editMode && ( + + + {t('Create a new chart')} + + } + buttonAction={() => { + window.open('/chart/add', '_blank', 'noopener noreferrer'); + }} + image="chart.svg" + /> + ); + + const topLevelTabEmptyState = editMode ? ( + + + {t('Create a new chart')} + + } + buttonAction={() => { + window.open('/chart/add', '_blank', 'noopener noreferrer'); + }} + image="chart.svg" + /> + ) : ( + { + setEditMode(true); + }) + } + image="chart.svg" + /> + ); + return width < 100 ? null : ( <> - {editMode && gridComponent?.children?.length === 0 && ( + {shouldDisplayEmptyState && ( - - - {t('Create a new chart')} - - } - buttonAction={() => { - window.location.assign('/chart/add'); - }} - image="chart.svg" - /> + {shouldDisplayTopLevelTabEmptyState + ? topLevelTabEmptyState + : dashboardEmptyState} )}
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx index cf051baefbb90..77156278504d6 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx @@ -18,8 +18,12 @@ */ import React from 'react'; import PropTypes from 'prop-types'; -import { styled } from '@superset-ui/core'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import { styled, t } from '@superset-ui/core'; +import { EmptyStateMedium } from 'src/components/EmptyState'; +import { setEditMode } from 'src/dashboard/actions/dashboardState'; import DashboardComponent from '../../containers/DashboardComponent'; import DragDroppable from '../dnd/DragDroppable'; import EditableTitle from '../../../components/EditableTitle'; @@ -40,6 +44,7 @@ const propTypes = { renderType: PropTypes.oneOf([RENDER_TAB, RENDER_TAB_CONTENT]).isRequired, onDropOnTab: PropTypes.func, editMode: PropTypes.bool.isRequired, + canEdit: PropTypes.bool.isRequired, filters: PropTypes.object.isRequired, // grid related @@ -53,6 +58,7 @@ const propTypes = { handleComponentDrop: PropTypes.func.isRequired, updateComponents: PropTypes.func.isRequired, setDirectPathToChild: PropTypes.func.isRequired, + setEditMode: PropTypes.func.isRequired, }; const defaultProps = { @@ -85,7 +91,7 @@ const renderDraggableContentTop = dropProps =>
); -export default class Tab extends React.PureComponent { +class Tab extends React.PureComponent { constructor(props) { super(props); this.handleChangeText = this.handleChangeText.bind(this); @@ -143,8 +149,11 @@ export default class Tab extends React.PureComponent { onResizeStop, editMode, isComponentVisible, + canEdit, + setEditMode, } = this.props; + const shouldDisplayEmptyState = tabComponent.children.length === 0; return (
{/* Make top of tab droppable */} @@ -162,6 +171,43 @@ export default class Tab extends React.PureComponent { {renderDraggableContentTop} )} + {shouldDisplayEmptyState && ( + + {t('You can')}{' '} + + {t('create a new chart')} + {' '} + {t('or use existing ones from the panel on the right')} + + ) : ( + + {t('You can add the components in the')}{' '} + setEditMode(true)} + > + {t('edit mode')} + + + )) + } + image="chart.svg" + /> + )} {tabComponent.children.map((componentId, componentIndex) => ( ); }), ); +jest.mock('src/dashboard/actions/dashboardState', () => ({ + setEditMode: jest.fn(() => ({ + type: 'SET_EDIT_MODE', + })), +})); -const creteProps = () => ({ +const createProps = () => ({ id: 'TAB-YT6eNksV-', parentId: 'TABS-L-d9eyOE-b', depth: 2, @@ -98,7 +104,7 @@ beforeEach(() => { }); test('Render tab (no content)', () => { - const props = creteProps(); + const props = createProps(); props.renderType = 'RENDER_TAB'; render(, { useRedux: true, useDnd: true }); expect(screen.getByText('🚀 Aspiring Developers')).toBeInTheDocument(); @@ -107,7 +113,7 @@ test('Render tab (no content)', () => { }); test('Render tab (no content) editMode:true', () => { - const props = creteProps(); + const props = createProps(); props.editMode = true; props.renderType = 'RENDER_TAB'; render(, { useRedux: true, useDnd: true }); @@ -117,7 +123,7 @@ test('Render tab (no content) editMode:true', () => { }); test('Edit table title', () => { - const props = creteProps(); + const props = createProps(); props.editMode = true; props.renderType = 'RENDER_TAB'; render(, { useRedux: true, useDnd: true }); @@ -131,7 +137,7 @@ test('Edit table title', () => { }); test('Render tab (with content)', () => { - const props = creteProps(); + const props = createProps(); props.isFocused = true; render(, { useRedux: true, useDnd: true }); expect(DashboardComponent).toBeCalledTimes(2); @@ -174,8 +180,39 @@ test('Render tab (with content)', () => { expect(DragDroppable).toBeCalledTimes(0); }); +test('Render tab content with no children', () => { + const props = createProps(); + props.component.children = []; + render(, { + useRedux: true, + useDnd: true, + }); + expect( + screen.getByText('There are no components added to this tab'), + ).toBeVisible(); + expect(screen.getByAltText('empty')).toBeVisible(); + expect(screen.queryByText('edit mode')).not.toBeInTheDocument(); +}); + +test('Render tab content with no children, canEdit: true', () => { + const props = createProps(); + props.component.children = []; + render(, { + useRedux: true, + useDnd: true, + initialState: { + dashboardInfo: { + dash_edit_perm: true, + }, + }, + }); + expect(screen.getByText('edit mode')).toBeVisible(); + userEvent.click(screen.getByRole('button', { name: 'edit mode' })); + expect(setEditMode).toHaveBeenCalled(); +}); + test('Render tab (with content) editMode:true', () => { - const props = creteProps(); + const props = createProps(); props.isFocused = true; props.editMode = true; render(, { useRedux: true, useDnd: true }); @@ -220,7 +257,7 @@ test('Render tab (with content) editMode:true', () => { }); test('Should call "handleDrop" and "handleTopDropTargetDrop"', () => { - const props = creteProps(); + const props = createProps(); props.isFocused = true; props.editMode = true; render(, { useRedux: true, useDnd: true }); @@ -233,3 +270,29 @@ test('Should call "handleDrop" and "handleTopDropTargetDrop"', () => { expect(props.onDropOnTab).toBeCalledTimes(1); expect(props.handleComponentDrop).toBeCalledTimes(2); }); + +test('Render tab content with no children, editMode: true, canEdit: true', () => { + const props = createProps(); + props.editMode = true; + // props.canEdit = true; + props.component.children = []; + render(, { + useRedux: true, + useDnd: true, + initialState: { + dashboardInfo: { + dash_edit_perm: true, + }, + }, + }); + expect( + screen.getByText('Drag and drop components to this tab'), + ).toBeVisible(); + expect(screen.getByAltText('empty')).toBeVisible(); + expect( + screen.getByRole('link', { name: 'create a new chart' }), + ).toBeVisible(); + expect( + screen.getByRole('link', { name: 'create a new chart' }), + ).toHaveAttribute('href', '/chart/add'); +}); diff --git a/superset-frontend/src/dashboard/containers/DashboardGrid.jsx b/superset-frontend/src/dashboard/containers/DashboardGrid.jsx index cbec708e54fbd..96688476112cd 100644 --- a/superset-frontend/src/dashboard/containers/DashboardGrid.jsx +++ b/superset-frontend/src/dashboard/containers/DashboardGrid.jsx @@ -24,11 +24,12 @@ import { handleComponentDrop, resizeComponent, } from '../actions/dashboardLayout'; -import { setDirectPathToChild } from '../actions/dashboardState'; +import { setDirectPathToChild, setEditMode } from '../actions/dashboardState'; -function mapStateToProps({ dashboardState }) { +function mapStateToProps({ dashboardState, dashboardInfo }) { return { editMode: dashboardState.editMode, + canEdit: dashboardInfo.dash_edit_perm, }; } @@ -38,6 +39,7 @@ function mapDispatchToProps(dispatch) { handleComponentDrop, resizeComponent, setDirectPathToChild, + setEditMode, }, dispatch, ); diff --git a/superset-frontend/src/dashboard/stylesheets/builder.less b/superset-frontend/src/dashboard/stylesheets/builder.less index 1512e4c6fa08a..422d455622a40 100644 --- a/superset-frontend/src/dashboard/stylesheets/builder.less +++ b/superset-frontend/src/dashboard/stylesheets/builder.less @@ -22,6 +22,7 @@ flex-grow: 1; display: flex; flex-direction: column; + height: 100%; } /* only top-level tabs have popover, give it more padding to match header + tabs */ From 08aca83f6cba81d37d6d70cfddc7980ae95a7bb5 Mon Sep 17 00:00:00 2001 From: Geido <60598000+geido@users.noreply.github.com> Date: Fri, 1 Apr 2022 17:57:36 +0300 Subject: [PATCH 07/31] chore: Remove index.less from showSavedQuery (#19440) * Remove index less * Revert change --- .../src/showSavedQuery/index.jsx | 25 +++++++++++++++---- .../src/showSavedQuery/index.less | 25 ------------------- 2 files changed, 20 insertions(+), 30 deletions(-) delete mode 100644 superset-frontend/src/showSavedQuery/index.less diff --git a/superset-frontend/src/showSavedQuery/index.jsx b/superset-frontend/src/showSavedQuery/index.jsx index 12259cb0d4d47..6b8adcd60d7a9 100644 --- a/superset-frontend/src/showSavedQuery/index.jsx +++ b/superset-frontend/src/showSavedQuery/index.jsx @@ -20,7 +20,7 @@ import React from 'react'; import ReactDom from 'react-dom'; import Form from 'react-jsonschema-form'; import { interpolate } from 'src/showSavedQuery/utils'; -import './index.less'; +import { styled } from '@superset-ui/core'; const scheduleInfoContainer = document.getElementById('schedule-info'); const bootstrapData = JSON.parse( @@ -31,12 +31,27 @@ const { query } = bootstrapData.common; const scheduleInfo = query.extra_json.schedule_info; const linkback = config.linkback ? interpolate(config.linkback, query) : null; +const StyledSavedQueryContainer = styled.div` + .btn-add { + display: none; + } +`; + +const StyledLinkBack = styled.div` + ${({ theme }) => ` + padding-top: 0; + padding-right: ${theme.gridUnit * 2 + 2}px; + padding-bottom: ${theme.gridUnit * 5}px; + padding-left: ${theme.gridUnit / 2}px; +`} +`; + if (scheduleInfo && config) { // hide instructions when showing schedule info config.JSONSCHEMA.description = ''; ReactDom.render( -
+
{linkback && ( - + )} -
, + , scheduleInfoContainer, ); } diff --git a/superset-frontend/src/showSavedQuery/index.less b/superset-frontend/src/showSavedQuery/index.less deleted file mode 100644 index 7008bec9ea8a3..0000000000000 --- a/superset-frontend/src/showSavedQuery/index.less +++ /dev/null @@ -1,25 +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. - */ -.btn-add { - display: none; -} - -.linkback { - padding: 0 10px 20px 2px; -} From 90c9daea08cd59ba7261c13e1ce4e80a72f84b48 Mon Sep 17 00:00:00 2001 From: Stephen Liu <750188453@qq.com> Date: Fri, 1 Apr 2022 23:42:30 +0800 Subject: [PATCH 08/31] feat(color): support analogous colors to prevent color conflict (#19325) * feat(color): support analogous colors * fix test * fix range * add some comment --- .../src/color/CategoricalColorScale.ts | 16 ++++++++++ .../src/color/SharedLabelColorSingleton.ts | 19 +++--------- .../superset-ui-core/src/color/utils.ts | 22 ++++++++++++++ .../test/color/CategoricalColorScale.test.ts | 29 +++++-------------- 4 files changed, 50 insertions(+), 36 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/src/color/CategoricalColorScale.ts b/superset-frontend/packages/superset-ui-core/src/color/CategoricalColorScale.ts index d34960dac0973..c6f37e4ff771f 100644 --- a/superset-frontend/packages/superset-ui-core/src/color/CategoricalColorScale.ts +++ b/superset-frontend/packages/superset-ui-core/src/color/CategoricalColorScale.ts @@ -23,6 +23,7 @@ import { ExtensibleFunction } from '../models'; import { ColorsLookup } from './types'; import stringifyAndTrim from './stringifyAndTrim'; import getSharedLabelColor from './SharedLabelColorSingleton'; +import { getAnalogousColors } from './utils'; // Use type augmentation to correct the fact that // an instance of CategoricalScale is also a function @@ -31,6 +32,8 @@ interface CategoricalColorScale { } class CategoricalColorScale extends ExtensibleFunction { + originColors: string[]; + colors: string[]; scale: ScaleOrdinal<{ toString(): string }, string>; @@ -39,6 +42,8 @@ class CategoricalColorScale extends ExtensibleFunction { forcedColors: ColorsLookup; + multiple: number; + /** * Constructor * @param {*} colors an array of colors @@ -48,11 +53,13 @@ class CategoricalColorScale extends ExtensibleFunction { constructor(colors: string[], parentForcedColors?: ColorsLookup) { super((value: string, sliceId?: number) => this.getColor(value, sliceId)); + this.originColors = colors; this.colors = colors; this.scale = scaleOrdinal<{ toString(): string }, string>(); this.scale.range(colors); this.parentForcedColors = parentForcedColors; this.forcedColors = {}; + this.multiple = 0; } getColor(value?: string, sliceId?: number) { @@ -72,6 +79,15 @@ class CategoricalColorScale extends ExtensibleFunction { return forcedColor; } + const multiple = Math.floor( + this.domain().length / this.originColors.length, + ); + if (multiple > this.multiple) { + this.multiple = multiple; + const newRange = getAnalogousColors(this.originColors, multiple); + this.range(this.originColors.concat(newRange)); + } + const color = this.scale(cleanedValue); sharedLabelColor.addSlice(cleanedValue, color, sliceId); diff --git a/superset-frontend/packages/superset-ui-core/src/color/SharedLabelColorSingleton.ts b/superset-frontend/packages/superset-ui-core/src/color/SharedLabelColorSingleton.ts index 227b565276a94..d2a59ac7c292a 100644 --- a/superset-frontend/packages/superset-ui-core/src/color/SharedLabelColorSingleton.ts +++ b/superset-frontend/packages/superset-ui-core/src/color/SharedLabelColorSingleton.ts @@ -17,9 +17,9 @@ * under the License. */ -import tinycolor from 'tinycolor2'; import { CategoricalColorNamespace } from '.'; import makeSingleton from '../utils/makeSingleton'; +import { getAnalogousColors } from './utils'; export class SharedLabelColor { sliceLabelColorMap: Record>; @@ -39,27 +39,16 @@ export class SharedLabelColor { CategoricalColorNamespace.getNamespace(colorNamespace); const colors = categoricalNamespace.getScale(colorScheme).range(); const sharedLabels = this.getSharedLabels(); - const generatedColors: tinycolor.Instance[] = []; + let generatedColors: string[] = []; let sharedLabelMap; if (sharedLabels.length) { const multiple = Math.ceil(sharedLabels.length / colors.length); - const ext = 5; - const analogousColors = colors.map(color => { - const result = tinycolor(color).analogous(multiple + ext); - return result.slice(ext); - }); - - // [[A, AA, AAA], [B, BB, BBB]] => [A, B, AA, BB, AAA, BBB] - while (analogousColors[analogousColors.length - 1]?.length) { - analogousColors.forEach(colors => - generatedColors.push(colors.shift() as tinycolor.Instance), - ); - } + generatedColors = getAnalogousColors(colors, multiple); sharedLabelMap = sharedLabels.reduce( (res, label, index) => ({ ...res, - [label.toString()]: generatedColors[index]?.toHexString(), + [label.toString()]: generatedColors[index], }), {}, ); diff --git a/superset-frontend/packages/superset-ui-core/src/color/utils.ts b/superset-frontend/packages/superset-ui-core/src/color/utils.ts index 47a936aaa6187..0ce64d049012c 100644 --- a/superset-frontend/packages/superset-ui-core/src/color/utils.ts +++ b/superset-frontend/packages/superset-ui-core/src/color/utils.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import tinycolor from 'tinycolor2'; const rgbRegex = /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/; export function getContrastingColor(color: string, thresholds = 186) { @@ -51,3 +52,24 @@ export function getContrastingColor(color: string, thresholds = 186) { return r * 0.299 + g * 0.587 + b * 0.114 > thresholds ? '#000' : '#FFF'; } + +export function getAnalogousColors(colors: string[], results: number) { + const generatedColors: string[] = []; + // This is to solve the problem that the first three values generated by tinycolor.analogous + // may have the same or very close colors. + const ext = 3; + const analogousColors = colors.map(color => { + const result = tinycolor(color).analogous(results + ext); + return result.slice(ext); + }); + + // [[A, AA, AAA], [B, BB, BBB]] => [A, B, AA, BB, AAA, BBB] + while (analogousColors[analogousColors.length - 1]?.length) { + analogousColors.forEach(colors => { + const color = colors.shift() as tinycolor.Instance; + generatedColors.push(color.toHexString()); + }); + } + + return generatedColors; +} diff --git a/superset-frontend/packages/superset-ui-core/test/color/CategoricalColorScale.test.ts b/superset-frontend/packages/superset-ui-core/test/color/CategoricalColorScale.test.ts index f080b6fc84e5d..1d47cf760e326 100644 --- a/superset-frontend/packages/superset-ui-core/test/color/CategoricalColorScale.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/color/CategoricalColorScale.test.ts @@ -62,28 +62,15 @@ describe('CategoricalColorScale', () => { expect(c2).not.toBe(c3); expect(c3).not.toBe(c1); }); - it('recycles colors when number of items exceed available colors', () => { - const colorSet: { [key: string]: number } = {}; + it('get analogous colors when number of items exceed available colors', () => { const scale = new CategoricalColorScale(['blue', 'red', 'green']); - const colors = [ - scale.getColor('pig'), - scale.getColor('horse'), - scale.getColor('cat'), - scale.getColor('cow'), - scale.getColor('donkey'), - scale.getColor('goat'), - ]; - colors.forEach(color => { - if (colorSet[color]) { - colorSet[color] += 1; - } else { - colorSet[color] = 1; - } - }); - expect(Object.keys(colorSet)).toHaveLength(3); - ['blue', 'red', 'green'].forEach(color => { - expect(colorSet[color]).toBe(2); - }); + scale.getColor('pig'); + scale.getColor('horse'); + scale.getColor('cat'); + scale.getColor('cow'); + scale.getColor('donkey'); + scale.getColor('goat'); + expect(scale.range()).toHaveLength(6); }); }); describe('.setColor(value, forcedColor)', () => { From 8cd03181ac0d324851d814443f4bd7f14924153d Mon Sep 17 00:00:00 2001 From: "Michael S. Molina" <70410625+michael-s-molina@users.noreply.github.com> Date: Fri, 1 Apr 2022 13:13:16 -0300 Subject: [PATCH 09/31] refactor: Removes the CSS files from the DeckGL plugin (#19465) --- .../src/DeckGLContainer.jsx | 1 - .../src/components/BootstrapSliderWrapper.css | 25 ---------- .../src/components/BootstrapSliderWrapper.jsx | 17 +++++-- .../src/components/Legend.css | 44 ------------------ .../src/components/Legend.jsx | 39 +++++++++++++--- .../src/components/PlaySlider.css | 46 ------------------- .../src/components/PlaySlider.jsx | 38 +++++++++++++-- .../src/css/deckgl.css | 22 --------- 8 files changed, 81 insertions(+), 151 deletions(-) delete mode 100644 superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/BootstrapSliderWrapper.css delete mode 100644 superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/Legend.css delete mode 100644 superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/PlaySlider.css delete mode 100644 superset-frontend/plugins/legacy-preset-chart-deckgl/src/css/deckgl.css diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/DeckGLContainer.jsx b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/DeckGLContainer.jsx index 53c6f73a2e09f..9bc963c9fce3d 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/DeckGLContainer.jsx +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/DeckGLContainer.jsx @@ -28,7 +28,6 @@ import DeckGL from 'deck.gl'; import { styled } from '@superset-ui/core'; import Tooltip from './components/Tooltip'; import 'mapbox-gl/dist/mapbox-gl.css'; -import './css/deckgl.css'; const TICK = 250; // milliseconds diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/BootstrapSliderWrapper.css b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/BootstrapSliderWrapper.css deleted file mode 100644 index dc54046e4c258..0000000000000 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/BootstrapSliderWrapper.css +++ /dev/null @@ -1,25 +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. - */ -.BootstrapSliderWrapper .slider-selection { - background: #efefef; -} - -.BootstrapSliderWrapper .slider-handle { - background: #b3b3b3; -} diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/BootstrapSliderWrapper.jsx b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/BootstrapSliderWrapper.jsx index 51698b5dfae04..0ff45f5ea2c54 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/BootstrapSliderWrapper.jsx +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/BootstrapSliderWrapper.jsx @@ -19,12 +19,23 @@ import React from 'react'; import ReactBootstrapSlider from 'react-bootstrap-slider'; import 'bootstrap-slider/dist/css/bootstrap-slider.min.css'; -import './BootstrapSliderWrapper.css'; +import { styled } from '@superset-ui/core'; + +const StyledSlider = styled.div` + ${({ theme }) => ` + .slider-selection { + background: ${theme.colors.grayscale.light2}; + } + .slider-handle { + background: ${theme.colors.grayscale.light1}; + } + `} +`; export default function BootstrapSliderWrapper(props) { return ( - + - + ); } diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/Legend.css b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/Legend.css deleted file mode 100644 index 706dfaf0cd23b..0000000000000 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/Legend.css +++ /dev/null @@ -1,44 +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. - */ -div.legend { - font-size: 90%; - position: absolute; - background: #fff; - box-shadow: 0 0 4px rgba(0, 0, 0, 0.15); - margin: 24px; - padding: 12px 20px; - outline: none; - overflow-y: scroll; - max-height: 200px; -} - -ul.categories { - list-style: none; - padding-left: 0; - margin: 0; -} - -ul.categories li a { - color: rgb(51, 51, 51); - text-decoration: none; -} - -ul.categories li a span { - margin-right: 10px; -} diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/Legend.jsx b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/Legend.jsx index bb18626b699f6..40f13bb514d5b 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/Legend.jsx +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/Legend.jsx @@ -21,9 +21,36 @@ */ import React from 'react'; import PropTypes from 'prop-types'; -import { formatNumber } from '@superset-ui/core'; - -import './Legend.css'; +import { formatNumber, styled } from '@superset-ui/core'; + +const StyledLegend = styled.div` + ${({ theme }) => ` + font-size: ${theme.typography.sizes.s}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; + outline: none; + overflow-y: scroll; + max-height: 200px; + + & ul { + list-style: none; + padding-left: 0; + margin: 0; + + & li a { + color: ${theme.colors.grayscale.base}; + text-decoration: none; + + & span { + margin-right: ${theme.gridUnit * 3}px; + } + } + } + `} +`; const categoryDelimiter = ' - '; @@ -106,9 +133,9 @@ export default class Legend extends React.PureComponent { }; return ( -
-
    {categories}
-
+ +
    {categories}
+
); } } diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/PlaySlider.css b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/PlaySlider.css deleted file mode 100644 index f4b6f5de2eea1..0000000000000 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/PlaySlider.css +++ /dev/null @@ -1,46 +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. - */ -.play-slider { - display: flex; - height: 40px; - width: 100%; - margin: 0; -} - -.play-slider-controls { - flex: 0 0 80px; - text-align: middle; -} - -.play-slider-scrobbler { - flex: 1; -} - -.slider.slider-horizontal { - width: 100% !important; -} - -.slider-button { - color: #b3b3b3; - margin-right: 5px; -} - -div.slider > div.tooltip.tooltip-main.top.in { - margin-left: 0 !important; -} diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/PlaySlider.jsx b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/PlaySlider.jsx index d0ec3199d4c81..eda7803f21754 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/PlaySlider.jsx +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/components/PlaySlider.jsx @@ -26,9 +26,39 @@ import React from 'react'; import PropTypes from 'prop-types'; import Mousetrap from 'mousetrap'; -import { t } from '@superset-ui/core'; +import { t, styled } from '@superset-ui/core'; import BootrapSliderWrapper from './BootstrapSliderWrapper'; -import './PlaySlider.css'; + +const StyledSlider = styled.div` + ${({ theme }) => ` + display: flex; + height: 40px; + width: 100%; + margin: 0; + + .play-slider-controls { + flex: 0 0 80px; + text-align: middle; + } + + .play-slider-scrobbler { + flex: 1; + } + + .slider.slider-horizontal { + width: 100% !important; + } + + .slider-button { + color: ${theme.colors.grayscale.light1}; + margin-right: ${theme.gridUnit}px; + } + + div.slider > div.tooltip.tooltip-main.top.in { + margin-left: 0 !important; + } + `} +`; const propTypes = { start: PropTypes.number.isRequired, @@ -167,7 +197,7 @@ export default class PlaySlider extends React.PureComponent { this.props; return ( -
+
-
+ ); } } diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/css/deckgl.css b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/css/deckgl.css deleted file mode 100644 index f836c27f08781..0000000000000 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/css/deckgl.css +++ /dev/null @@ -1,22 +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. - */ -.deckgl-tooltip > div { - overflow: hidden; - text-overflow: ellipsis; -} From 7594a02d42940ff01e4c2200864a76ce942d9e8e Mon Sep 17 00:00:00 2001 From: "Michael S. Molina" <70410625+michael-s-molina@users.noreply.github.com> Date: Fri, 1 Apr 2022 13:13:33 -0300 Subject: [PATCH 10/31] chore: Removes hard-coded colors from legacy-plugin-chart-world-map (#19466) --- .../plugins/legacy-plugin-chart-world-map/src/ReactWorldMap.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset-frontend/plugins/legacy-plugin-chart-world-map/src/ReactWorldMap.jsx b/superset-frontend/plugins/legacy-plugin-chart-world-map/src/ReactWorldMap.jsx index d0f23e1844376..52b20f1bdafab 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-world-map/src/ReactWorldMap.jsx +++ b/superset-frontend/plugins/legacy-plugin-chart-world-map/src/ReactWorldMap.jsx @@ -37,7 +37,7 @@ export default styled(WorldMapComponent)` .superset-legacy-chart-world-map { position: relative; svg { - background-color: #feffff; + background-color: ${({ theme }) => theme.colors.grayscale.light5}; } } `; From 5fed10dae2723bbcab46e54c9eb6ca55272c34d3 Mon Sep 17 00:00:00 2001 From: "Michael S. Molina" <70410625+michael-s-molina@users.noreply.github.com> Date: Fri, 1 Apr 2022 13:13:43 -0300 Subject: [PATCH 11/31] refactor: Removes the CSS files from the Rose plugin (#19473) --- .../legacy-plugin-chart-rose/package.json | 34 ++++----- .../legacy-plugin-chart-rose/src/ReactRose.js | 22 ------ .../src/ReactRose.jsx | 75 +++++++++++++++++++ .../legacy-plugin-chart-rose/src/Rose.css | 43 ----------- .../legacy-plugin-chart-rose/src/Rose.js | 2 +- 5 files changed, 92 insertions(+), 84 deletions(-) delete mode 100644 superset-frontend/plugins/legacy-plugin-chart-rose/src/ReactRose.js create mode 100644 superset-frontend/plugins/legacy-plugin-chart-rose/src/ReactRose.jsx delete mode 100644 superset-frontend/plugins/legacy-plugin-chart-rose/src/Rose.css diff --git a/superset-frontend/plugins/legacy-plugin-chart-rose/package.json b/superset-frontend/plugins/legacy-plugin-chart-rose/package.json index 1541f323fe418..20deaa3bdb4d0 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-rose/package.json +++ b/superset-frontend/plugins/legacy-plugin-chart-rose/package.json @@ -2,39 +2,37 @@ "name": "@superset-ui/legacy-plugin-chart-rose", "version": "0.18.25", "description": "Superset Legacy Chart - Nightingale Rose Diagram", - "sideEffects": [ - "*.css" - ], - "main": "lib/index.js", - "module": "esm/index.js", - "files": [ - "esm", - "lib" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/apache-superset/superset-ui.git" - }, "keywords": [ "superset" ], - "author": "Superset", - "license": "Apache-2.0", + "homepage": "https://github.com/apache-superset/superset-ui#readme", "bugs": { "url": "https://github.com/apache-superset/superset-ui/issues" }, - "homepage": "https://github.com/apache-superset/superset-ui#readme", - "publishConfig": { - "access": "public" + "repository": { + "type": "git", + "url": "git+https://github.com/apache-superset/superset-ui.git" }, + "license": "Apache-2.0", + "author": "Superset", + "main": "lib/index.js", + "module": "esm/index.js", + "files": [ + "esm", + "lib" + ], "dependencies": { "d3": "^3.5.17", "nvd3-fork": "^2.0.5", "prop-types": "^15.6.2" }, "peerDependencies": { + "@emotion/react": "^11.4.1", "@superset-ui/chart-controls": "*", "@superset-ui/core": "*", "react": "^16.13.1" + }, + "publishConfig": { + "access": "public" } } diff --git a/superset-frontend/plugins/legacy-plugin-chart-rose/src/ReactRose.js b/superset-frontend/plugins/legacy-plugin-chart-rose/src/ReactRose.js deleted file mode 100644 index bfdd152460689..0000000000000 --- a/superset-frontend/plugins/legacy-plugin-chart-rose/src/ReactRose.js +++ /dev/null @@ -1,22 +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 { reactify } from '@superset-ui/core'; -import Component from './Rose'; - -export default reactify(Component); diff --git a/superset-frontend/plugins/legacy-plugin-chart-rose/src/ReactRose.jsx b/superset-frontend/plugins/legacy-plugin-chart-rose/src/ReactRose.jsx new file mode 100644 index 0000000000000..33d9cd4f9c8fd --- /dev/null +++ b/superset-frontend/plugins/legacy-plugin-chart-rose/src/ReactRose.jsx @@ -0,0 +1,75 @@ +/** + * 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 React from 'react'; +import { reactify, styled, css } from '@superset-ui/core'; +import { Global } from '@emotion/react'; +import Component from './Rose'; + +const ReactComponent = reactify(Component); + +const Rose = ({ className, ...otherProps }) => ( +
+ css` + .tooltip { + line-height: 1; + padding: ${theme.gridUnit * 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; + } + `} + /> + +
+); + +export default styled(Rose)` + ${({ theme }) => ` + .superset-legacy-chart-rose path { + transition: fill-opacity 180ms linear; + stroke: ${theme.colors.grayscale.light5}; + stroke-width: 1px; + stroke-opacity: 1; + fill-opacity: 0.75; + } + + .superset-legacy-chart-rose text { + font-weight: ${theme.typography.weights.normal}; + font-size: ${theme.typography.sizes.s}px; + font-family: ${theme.typography.families.sansSerif}; + pointer-events: none; + } + + .superset-legacy-chart-rose .clickable path { + cursor: pointer; + } + + .superset-legacy-chart-rose .hover path { + fill-opacity: 1; + } + + .nv-legend .nv-series { + cursor: pointer; + } + `} +`; diff --git a/superset-frontend/plugins/legacy-plugin-chart-rose/src/Rose.css b/superset-frontend/plugins/legacy-plugin-chart-rose/src/Rose.css deleted file mode 100644 index 441e95c5d4cc0..0000000000000 --- a/superset-frontend/plugins/legacy-plugin-chart-rose/src/Rose.css +++ /dev/null @@ -1,43 +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. - */ - -.superset-legacy-chart-rose path { - transition: fill-opacity 180ms linear; - stroke: #fff; - stroke-width: 1px; - stroke-opacity: 1; - fill-opacity: 0.75; -} - -.superset-legacy-chart-rose text { - font: 400 12px Arial, sans-serif; - pointer-events: none; -} - -.superset-legacy-chart-rose .clickable path { - cursor: pointer; -} - -.superset-legacy-chart-rose .hover path { - fill-opacity: 1; -} - -.nv-legend .nv-series { - cursor: pointer; -} diff --git a/superset-frontend/plugins/legacy-plugin-chart-rose/src/Rose.js b/superset-frontend/plugins/legacy-plugin-chart-rose/src/Rose.js index 4d7ef2b8ed995..2dfa2ffdd70a2 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-rose/src/Rose.js +++ b/superset-frontend/plugins/legacy-plugin-chart-rose/src/Rose.js @@ -27,7 +27,6 @@ import { getNumberFormatter, CategoricalColorNamespace, } from '@superset-ui/core'; -import './Rose.css'; const propTypes = { // Data is an object hashed by numeric value, perhaps timestamp @@ -137,6 +136,7 @@ function Rose(element, props) { legendWrap.datum(legendData(datum)).call(legend); tooltip.headerFormatter(timeFormat).valueFormatter(format); + tooltip.classes('tooltip'); // Compute max radius, which the largest value will occupy const roseHeight = height - legend.height(); From 90dbe8d340f227e3ffe374984a202516d779d5d8 Mon Sep 17 00:00:00 2001 From: Jesse Yang Date: Fri, 1 Apr 2022 16:44:41 -0700 Subject: [PATCH 12/31] perf: speed up db migration for deprecating time_range_endpoints (#19495) --- ...0b36b94_rm_time_range_endpoints_from_qc.py | 22 +++++++++---------- ...9d86e695_deprecate_time_range_endpoints.py | 4 ++-- ...dd_saved_query_foreign_key_to_tab_state.py | 1 + .../versions/e866bd2d4976_smaller_grid.py | 1 + 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/superset/migrations/versions/2ed890b36b94_rm_time_range_endpoints_from_qc.py b/superset/migrations/versions/2ed890b36b94_rm_time_range_endpoints_from_qc.py index 34b833dabd200..42d73bc33d335 100644 --- a/superset/migrations/versions/2ed890b36b94_rm_time_range_endpoints_from_qc.py +++ b/superset/migrations/versions/2ed890b36b94_rm_time_range_endpoints_from_qc.py @@ -30,7 +30,6 @@ import sqlalchemy as sa from alembic import op -from sqlalchemy.dialects import postgresql from sqlalchemy.ext.declarative import declarative_base from superset import db @@ -47,16 +46,17 @@ class Slice(Base): def upgrade(): bind = op.get_bind() session = db.Session(bind=bind) - for slc in session.query(Slice): - if slc.query_context: - try: - query_context = json.loads(slc.query_context) - except json.decoder.JSONDecodeError: - continue - queries = query_context.get("queries") - for query in queries: - query.get("extras", {}).pop("time_range_endpoints", None) - slc.queries = json.dumps(queries) + for slc in session.query(Slice).filter( + Slice.query_context.like("%time_range_endpoints%") + ): + try: + query_context = json.loads(slc.query_context) + except json.decoder.JSONDecodeError: + continue + queries = query_context.get("queries") + for query in queries: + query.get("extras", {}).pop("time_range_endpoints", None) + slc.queries = json.dumps(queries) session.commit() session.close() diff --git a/superset/migrations/versions/ab9a9d86e695_deprecate_time_range_endpoints.py b/superset/migrations/versions/ab9a9d86e695_deprecate_time_range_endpoints.py index 3f149d3ff0c2a..2bd65996140d7 100644 --- a/superset/migrations/versions/ab9a9d86e695_deprecate_time_range_endpoints.py +++ b/superset/migrations/versions/ab9a9d86e695_deprecate_time_range_endpoints.py @@ -46,8 +46,8 @@ def upgrade(): bind = op.get_bind() session = db.Session(bind=bind) - for slc in session.query(Slice): - params = json.loads(slc.params or "{}") + for slc in session.query(Slice).filter(Slice.params.like("%time_range_endpoints%")): + params = json.loads(slc.params) params.pop("time_range_endpoints", None) slc.params = json.dumps(params) diff --git a/superset/migrations/versions/c53bae8f08dd_add_saved_query_foreign_key_to_tab_state.py b/superset/migrations/versions/c53bae8f08dd_add_saved_query_foreign_key_to_tab_state.py index a70b95af0ad42..2a2d66a1fd19c 100644 --- a/superset/migrations/versions/c53bae8f08dd_add_saved_query_foreign_key_to_tab_state.py +++ b/superset/migrations/versions/c53bae8f08dd_add_saved_query_foreign_key_to_tab_state.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. """add_saved_query_foreign_key_to_tab_state + Revision ID: c53bae8f08dd Revises: bb38f40aa3ff Create Date: 2021-12-15 15:05:21.845777 diff --git a/superset/migrations/versions/e866bd2d4976_smaller_grid.py b/superset/migrations/versions/e866bd2d4976_smaller_grid.py index b674fd603073b..286be8a5fc9c6 100644 --- a/superset/migrations/versions/e866bd2d4976_smaller_grid.py +++ b/superset/migrations/versions/e866bd2d4976_smaller_grid.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. """smaller_grid + Revision ID: e866bd2d4976 Revises: 21e88bc06c02 Create Date: 2018-02-13 08:07:40.766277 From 60dcd651f44b7e1aa1b030e0cd5c64334a346e60 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Sun, 3 Apr 2022 13:26:56 +0200 Subject: [PATCH 13/31] feat(explore): SQL popover in datasource panel (#19308) * feat(explore): SQL popover in datasource panel * Fix acequire not defined * Rebase and fix tests * Disable highlighting gutter * Use ace-build acequire instead of brace --- superset-frontend/package-lock.json | 23 +++--- superset-frontend/package.json | 1 + .../superset-ui-chart-controls/package.json | 7 +- .../src/components/ColumnOption.tsx | 14 +--- .../src/components/MetricOption.tsx | 10 +-- .../src/components/SQLPopover.tsx | 70 +++++++++++++++++++ .../test/components/ColumnOption.test.tsx | 12 ++-- .../test/components/MetricOption.test.tsx | 11 +-- .../TemplateParamsEditor.test.tsx | 3 - .../components/TemplateParamsEditor/index.tsx | 1 - .../src/components/AsyncAceEditor/index.tsx | 6 +- 11 files changed, 110 insertions(+), 48 deletions(-) create mode 100644 superset-frontend/packages/superset-ui-chart-controls/src/components/SQLPopover.tsx diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index de2d1a60f81f4..2e845ffab4f53 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -51,6 +51,7 @@ "@superset-ui/switchboard": "file:./packages/superset-ui-switchboard", "@vx/responsive": "^0.0.195", "abortcontroller-polyfill": "^1.1.9", + "ace-builds": "^1.4.14", "antd": "^4.9.4", "array-move": "^2.2.1", "babel-plugin-typescript-to-proptypes": "^2.0.0", @@ -24403,9 +24404,9 @@ } }, "node_modules/ace-builds": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.13.tgz", - "integrity": "sha512-SOLzdaQkY6ecPKYRDDg+MY1WoGgXA34cIvYJNNoBMGGUswHmlauU2Hy0UL96vW0Fs/LgFbMUjD+6vqzWTldIYQ==" + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.14.tgz", + "integrity": "sha512-NBOQlm9+7RBqRqZwimpgquaLeTJFayqb9UEPtTkpC3TkkwDnlsT/TwsCC0svjt9kEZ6G9mH5AEOHSz6Q/HrzQQ==" }, "node_modules/acorn": { "version": "7.1.1", @@ -58467,6 +58468,8 @@ "license": "Apache-2.0", "dependencies": { "@react-icons/all-files": "^4.1.0", + "@types/enzyme": "^3.10.5", + "@types/react": "*", "lodash": "^4.17.15", "prop-types": "^15.7.2" }, @@ -58479,10 +58482,11 @@ "@testing-library/react": "^11.2.0", "@testing-library/react-hooks": "^5.0.3", "@testing-library/user-event": "^12.7.0", - "@types/enzyme": "^3.10.5", - "@types/react": "*", + "ace-builds": "^1.4.14", "antd": "^4.9.4", + "brace": "^0.11.1", "react": "^16.13.1", + "react-ace": "^9.4.4", "react-dom": "^16.13.1" } }, @@ -59324,6 +59328,7 @@ "prop-types": "^15.6.2" }, "peerDependencies": { + "@emotion/react": "^11.4.1", "@superset-ui/chart-controls": "*", "@superset-ui/core": "*", "react": "^16.13.1" @@ -76177,6 +76182,8 @@ "version": "file:packages/superset-ui-chart-controls", "requires": { "@react-icons/all-files": "^4.1.0", + "@types/enzyme": "^3.10.5", + "@types/react": "*", "lodash": "^4.17.15", "prop-types": "^15.7.2" } @@ -79091,9 +79098,9 @@ } }, "ace-builds": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.13.tgz", - "integrity": "sha512-SOLzdaQkY6ecPKYRDDg+MY1WoGgXA34cIvYJNNoBMGGUswHmlauU2Hy0UL96vW0Fs/LgFbMUjD+6vqzWTldIYQ==" + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.14.tgz", + "integrity": "sha512-NBOQlm9+7RBqRqZwimpgquaLeTJFayqb9UEPtTkpC3TkkwDnlsT/TwsCC0svjt9kEZ6G9mH5AEOHSz6Q/HrzQQ==" }, "acorn": { "version": "7.1.1", diff --git a/superset-frontend/package.json b/superset-frontend/package.json index f122d09464390..edf122a24992c 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -111,6 +111,7 @@ "@superset-ui/switchboard": "file:./packages/superset-ui-switchboard", "@vx/responsive": "^0.0.195", "abortcontroller-polyfill": "^1.1.9", + "ace-builds": "^1.4.14", "antd": "^4.9.4", "array-move": "^2.2.1", "babel-plugin-typescript-to-proptypes": "^2.0.0", diff --git a/superset-frontend/packages/superset-ui-chart-controls/package.json b/superset-frontend/packages/superset-ui-chart-controls/package.json index bdb6be4daf846..1890a5e38a08b 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/package.json +++ b/superset-frontend/packages/superset-ui-chart-controls/package.json @@ -24,6 +24,8 @@ ], "dependencies": { "@react-icons/all-files": "^4.1.0", + "@types/enzyme": "^3.10.5", + "@types/react": "*", "lodash": "^4.17.15", "prop-types": "^15.7.2" }, @@ -36,10 +38,11 @@ "@testing-library/react": "^11.2.0", "@testing-library/react-hooks": "^5.0.3", "@testing-library/user-event": "^12.7.0", - "@types/enzyme": "^3.10.5", - "@types/react": "*", + "ace-builds": "^1.4.14", "antd": "^4.9.4", + "brace": "^0.11.1", "react": "^16.13.1", + "react-ace": "^9.4.4", "react-dom": "^16.13.1" }, "publishConfig": { diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/components/ColumnOption.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/components/ColumnOption.tsx index dd7775ec4dd06..fce2e8ff2ad07 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/components/ColumnOption.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/components/ColumnOption.tsx @@ -20,10 +20,10 @@ import React, { useState, ReactNode, useLayoutEffect } from 'react'; import { css, styled, SupersetTheme } from '@superset-ui/core'; import { Tooltip } from './Tooltip'; import { ColumnTypeLabel } from './ColumnTypeLabel/ColumnTypeLabel'; -import InfoTooltipWithTrigger from './InfoTooltipWithTrigger'; import CertifiedIconWithTooltip from './CertifiedIconWithTooltip'; import { ColumnMeta } from '../types'; import { getColumnLabelText, getColumnTooltipNode } from './labelUtils'; +import { SQLPopover } from './SQLPopover'; export type ColumnOptionProps = { column: ColumnMeta; @@ -69,17 +69,7 @@ export function ColumnOption({ {getColumnLabelText(column)} - - {hasExpression && ( - - )} - + {hasExpression && } {column.is_certified && ( - {showFormula && ( - + {showFormula && metric.expression && ( + )} {metric.is_certified && ( css` + color: ${theme.colors.grayscale.base}; + font-size: ${theme.typography.sizes.s}px; + & svg { + margin-left: ${theme.gridUnit}px; + margin-right: ${theme.gridUnit}px; + } + `} +`; + +export const SQLPopover = (props: PopoverProps & { sqlExpression: string }) => { + const theme = useTheme(); + return ( + + } + placement="bottomLeft" + arrowPointAtCenter + title={t('SQL expression')} + {...props} + > + + + ); +}; diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/components/ColumnOption.test.tsx b/superset-frontend/packages/superset-ui-chart-controls/test/components/ColumnOption.test.tsx index cc0106e9d650c..b1fb4b26535bf 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/test/components/ColumnOption.test.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/test/components/ColumnOption.test.tsx @@ -20,12 +20,8 @@ import React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; import { GenericDataType } from '@superset-ui/core'; -import { - ColumnOption, - ColumnOptionProps, - ColumnTypeLabel, - InfoTooltipWithTrigger, -} from '../../src'; +import { ColumnOption, ColumnOptionProps, ColumnTypeLabel } from '../../src'; +import { SQLPopover } from '../../src/components/SQLPopover'; describe('ColumnOption', () => { const defaultProps: ColumnOptionProps = { @@ -53,8 +49,8 @@ describe('ColumnOption', () => { expect(lbl).toHaveLength(1); expect(lbl.first().text()).toBe('Foo'); }); - it('shows 1 InfoTooltipWithTrigger', () => { - expect(wrapper.find(InfoTooltipWithTrigger)).toHaveLength(1); + it('shows SQL Popover trigger', () => { + expect(wrapper.find(SQLPopover)).toHaveLength(1); }); it('shows a label with column_name when no verbose_name', () => { delete props.column.verbose_name; diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/components/MetricOption.test.tsx b/superset-frontend/packages/superset-ui-chart-controls/test/components/MetricOption.test.tsx index e71882bd3ee14..59ba64c7bfe6f 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/test/components/MetricOption.test.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/test/components/MetricOption.test.tsx @@ -51,18 +51,21 @@ describe('MetricOption', () => { expect(lbl).toHaveLength(1); expect(lbl.first().text()).toBe('Foo'); }); - it('shows 2 InfoTooltipWithTrigger', () => { - expect(wrapper.find('InfoTooltipWithTrigger')).toHaveLength(2); + it('shows a InfoTooltipWithTrigger', () => { + expect(wrapper.find('InfoTooltipWithTrigger')).toHaveLength(1); + }); + it('shows SQL Popover trigger', () => { + expect(wrapper.find('SQLPopover')).toHaveLength(1); }); it('shows a label with metric_name when no verbose_name', () => { props.metric.verbose_name = ''; wrapper = shallow(factory(props)); expect(wrapper.find('.option-label').first().text()).toBe('foo'); }); - it('shows only 1 InfoTooltipWithTrigger when no warning', () => { + it('doesnt show InfoTooltipWithTrigger when no warning', () => { props.metric.warning_text = ''; wrapper = shallow(factory(props)); - expect(wrapper.find('InfoTooltipWithTrigger')).toHaveLength(1); + expect(wrapper.find('InfoTooltipWithTrigger')).toHaveLength(0); }); it('sets target="_blank" when openInNewWindow is true', () => { props.url = 'https://github.com/apache/incubator-superset'; diff --git a/superset-frontend/src/SqlLab/components/TemplateParamsEditor/TemplateParamsEditor.test.tsx b/superset-frontend/src/SqlLab/components/TemplateParamsEditor/TemplateParamsEditor.test.tsx index e663704ba21b8..bc04030d28c8e 100644 --- a/superset-frontend/src/SqlLab/components/TemplateParamsEditor/TemplateParamsEditor.test.tsx +++ b/superset-frontend/src/SqlLab/components/TemplateParamsEditor/TemplateParamsEditor.test.tsx @@ -24,7 +24,6 @@ import { getByText, waitFor, } from 'spec/helpers/testing-library'; -import brace from 'brace'; import { ThemeProvider, supersetTheme } from '@superset-ui/core'; import TemplateParamsEditor from 'src/SqlLab/components/TemplateParamsEditor'; @@ -48,8 +47,6 @@ describe('TemplateParamsEditor', () => { { wrapper: ThemeWrapper }, ); fireEvent.click(getByText(container, 'Parameters')); - const spy = jest.spyOn(brace, 'acequire'); - spy.mockReturnValue({ setCompleters: () => 'foo' }); await waitFor(() => { expect(baseElement.querySelector('#ace-editor')).toBeInTheDocument(); }); diff --git a/superset-frontend/src/SqlLab/components/TemplateParamsEditor/index.tsx b/superset-frontend/src/SqlLab/components/TemplateParamsEditor/index.tsx index 4bedbfcecce31..62d0a7209de1c 100644 --- a/superset-frontend/src/SqlLab/components/TemplateParamsEditor/index.tsx +++ b/superset-frontend/src/SqlLab/components/TemplateParamsEditor/index.tsx @@ -74,7 +74,6 @@ function TemplateParamsEditor({ syntax.

import('brace/mode/sql'), @@ -101,7 +102,6 @@ export default function AsyncAceEditor( }: AsyncAceEditorOptions = {}, ) { return AsyncEsmComponent(async () => { - const { default: ace } = await import('brace'); const { default: ReactAceEditor } = await import('react-ace'); await Promise.all(aceModules.map(x => aceModuleLoaders[x]())); @@ -126,7 +126,7 @@ export default function AsyncAceEditor( ref, ) { if (keywords) { - const langTools = ace.acequire('ace/ext/language_tools'); + const langTools = acequire('ace/ext/language_tools'); const completer = { getCompletions: ( editor: AceEditor, From ea708162a559a426c1c44f035883d709fc5dac50 Mon Sep 17 00:00:00 2001 From: Jesse Yang Date: Sun, 3 Apr 2022 16:27:43 -0700 Subject: [PATCH 14/31] feat(CI): clean up Python tests output (#19489) --- .github/workflows/superset-python-unittest.yml | 2 +- scripts/python_tests.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/superset-python-unittest.yml b/.github/workflows/superset-python-unittest.yml index 738c6138574b5..64db4d3e649af 100644 --- a/.github/workflows/superset-python-unittest.yml +++ b/.github/workflows/superset-python-unittest.yml @@ -51,7 +51,7 @@ jobs: - name: Python unit tests if: steps.check.outcome == 'failure' run: | - pytest --durations=0 ./tests/common ./tests/unit_tests --cache-clear + pytest --durations-min=0.5 --cov-report= --cov=superset ./tests/common ./tests/unit_tests --cache-clear - name: Upload code coverage if: steps.check.outcome == 'failure' run: | diff --git a/scripts/python_tests.sh b/scripts/python_tests.sh index 36b2b808025c4..0554f8ca65ad9 100755 --- a/scripts/python_tests.sh +++ b/scripts/python_tests.sh @@ -32,4 +32,4 @@ superset init echo "Running tests" -pytest --durations=0 --maxfail=1 --cov=superset "$@" +pytest --durations-min=2 --maxfail=1 --cov-report= --cov=superset "$@" From 5db36ec81c0e8b6d4ce8435a77c3dce8a036fec6 Mon Sep 17 00:00:00 2001 From: smileydev <47900232+prosdev0107@users.noreply.github.com> Date: Sun, 3 Apr 2022 21:59:38 -0400 Subject: [PATCH 15/31] fix(sqllab): make to hide the delete button of most recent query history (#19358) --- .../QueryHistory/QueryHistory.test.tsx | 1 + .../src/SqlLab/components/QueryHistory/index.tsx | 9 ++++++++- .../components/QueryTable/QueryTable.test.jsx | 1 + .../src/SqlLab/components/QueryTable/index.tsx | 16 ++++++++++------ .../src/SqlLab/components/SouthPane/index.tsx | 1 + 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/QueryHistory/QueryHistory.test.tsx b/superset-frontend/src/SqlLab/components/QueryHistory/QueryHistory.test.tsx index 782b147839186..e63de3fdca869 100644 --- a/superset-frontend/src/SqlLab/components/QueryHistory/QueryHistory.test.tsx +++ b/superset-frontend/src/SqlLab/components/QueryHistory/QueryHistory.test.tsx @@ -31,6 +31,7 @@ const mockedProps = { removeQuery: NOOP, }, displayLimit: 1000, + latestQueryId: 'yhMUZCGb', }; const setup = (overrides = {}) => ( diff --git a/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx b/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx index 7cf9d6ba657dd..bbae677ffe8c4 100644 --- a/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx +++ b/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx @@ -32,6 +32,7 @@ interface QueryHistoryProps { removeQuery: Function; }; displayLimit: number; + latestQueryId: string | undefined; } const StyledEmptyStateWrapper = styled.div` @@ -45,7 +46,12 @@ const StyledEmptyStateWrapper = styled.div` } `; -const QueryHistory = ({ queries, actions, displayLimit }: QueryHistoryProps) => +const QueryHistory = ({ + queries, + actions, + displayLimit, + latestQueryId, +}: QueryHistoryProps) => queries.length > 0 ? ( queries={queries} actions={actions} displayLimit={displayLimit} + latestQueryId={latestQueryId} /> ) : ( diff --git a/superset-frontend/src/SqlLab/components/QueryTable/QueryTable.test.jsx b/superset-frontend/src/SqlLab/components/QueryTable/QueryTable.test.jsx index 5be5a384863f9..f77e631ae2f5c 100644 --- a/superset-frontend/src/SqlLab/components/QueryTable/QueryTable.test.jsx +++ b/superset-frontend/src/SqlLab/components/QueryTable/QueryTable.test.jsx @@ -32,6 +32,7 @@ describe('QueryTable', () => { queries, displayLimit: 100, actions, + latestQueryId: 'ryhMUZCGb', }; it('is valid', () => { expect(React.isValidElement()).toBe(true); diff --git a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx index 142d8a13099da..65189c8ae0155 100644 --- a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx +++ b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx @@ -54,6 +54,7 @@ interface QueryTableProps { onUserClicked?: Function; onDbClicked?: Function; displayLimit: number; + latestQueryId?: string | undefined; } const openQuery = (id: number) => { @@ -68,6 +69,7 @@ const QueryTable = ({ onUserClicked = () => undefined, onDbClicked = () => undefined, displayLimit, + latestQueryId, }: QueryTableProps) => { const theme = useTheme(); @@ -290,12 +292,14 @@ const QueryTable = ({ > - removeQuery(query)} - > - - + {q.id !== latestQueryId && ( + removeQuery(query)} + > + + + )}
); return q; diff --git a/superset-frontend/src/SqlLab/components/SouthPane/index.tsx b/superset-frontend/src/SqlLab/components/SouthPane/index.tsx index afdb13e6f7fb3..767b608f3b7d2 100644 --- a/superset-frontend/src/SqlLab/components/SouthPane/index.tsx +++ b/superset-frontend/src/SqlLab/components/SouthPane/index.tsx @@ -224,6 +224,7 @@ export default function SouthPane({ queries={editorQueries} actions={actions} displayLimit={displayLimit} + latestQueryId={latestQueryId} /> {renderDataPreviewTabs()} From b8891acf4a478da8883bd168272715023d6a3351 Mon Sep 17 00:00:00 2001 From: dudasaron <6893357+dudasaron@users.noreply.github.com> Date: Mon, 4 Apr 2022 09:38:44 +0200 Subject: [PATCH 16/31] fix(dashboard list): do not show favorite star for anonymous users #18210 (#19409) * fix: Only show favorite star on dashboard list if user is logged in #18210 * Fix linter errors --- .../src/components/ListViewCard/index.tsx | 2 +- .../views/CRUD/dashboard/DashboardCard.tsx | 14 ++-- .../CRUD/dashboard/DashboardList.test.jsx | 68 ++++++++++++++-- .../views/CRUD/dashboard/DashboardList.tsx | 79 ++++++++++++------- superset-frontend/src/views/CRUD/utils.tsx | 2 +- 5 files changed, 120 insertions(+), 45 deletions(-) diff --git a/superset-frontend/src/components/ListViewCard/index.tsx b/superset-frontend/src/components/ListViewCard/index.tsx index 79616c86aca59..153b06489be46 100644 --- a/superset-frontend/src/components/ListViewCard/index.tsx +++ b/superset-frontend/src/components/ListViewCard/index.tsx @@ -26,7 +26,7 @@ import CertifiedBadge from '../CertifiedBadge'; const ActionsWrapper = styled.div` width: 64px; display: flex; - justify-content: space-between; + justify-content: flex-end; `; const StyledCard = styled(AntdCard)` diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx index 25a012f86b32b..2da9f45515336 100644 --- a/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx +++ b/superset-frontend/src/views/CRUD/dashboard/DashboardCard.tsx @@ -44,7 +44,7 @@ interface DashboardCardProps { saveFavoriteStatus: (id: number, isStarred: boolean) => void; favoriteStatus: boolean; dashboardFilter?: string; - userId?: number; + userId?: string | number; showThumbnails?: boolean; handleBulkDashboardExport: (dashboardsToExport: Dashboard[]) => void; } @@ -171,11 +171,13 @@ function DashboardCard({ e.preventDefault(); }} > - + {userId && ( + + )} diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardList.test.jsx b/superset-frontend/src/views/CRUD/dashboard/DashboardList.test.jsx index 3561cafdbce6c..e42ba92ff2926 100644 --- a/superset-frontend/src/views/CRUD/dashboard/DashboardList.test.jsx +++ b/superset-frontend/src/views/CRUD/dashboard/DashboardList.test.jsx @@ -36,6 +36,9 @@ import DashboardList from 'src/views/CRUD/dashboard/DashboardList'; import ListView from 'src/components/ListView'; import ListViewCard from 'src/components/ListViewCard'; import PropertiesModal from 'src/dashboard/components/PropertiesModal'; +import FaveStar from 'src/components/FaveStar'; +import TableCollection from 'src/components/TableCollection'; +import CardCollection from 'src/components/ListView/CardCollection'; // store needed for withToasts(DashboardTable) const mockStore = configureStore([thunk]); @@ -104,15 +107,18 @@ describe('DashboardList', () => { }); const mockedProps = {}; - const wrapper = mount( - - - - - , - ); + let wrapper; beforeAll(async () => { + fetchMock.resetHistory(); + wrapper = mount( + + + + + , + ); + await waitForComponentToPaint(wrapper); }); @@ -178,6 +184,18 @@ describe('DashboardList', () => { await waitForComponentToPaint(wrapper); expect(wrapper.find(ConfirmStatusChange)).toExist(); }); + + it('renders the Favorite Star column in list view for logged in user', async () => { + wrapper.find('[aria-label="list-view"]').first().simulate('click'); + await waitForComponentToPaint(wrapper); + expect(wrapper.find(TableCollection).find(FaveStar)).toExist(); + }); + + it('renders the Favorite Star in card view for logged in user', async () => { + wrapper.find('[aria-label="card-view"]').first().simulate('click'); + await waitForComponentToPaint(wrapper); + expect(wrapper.find(CardCollection).find(FaveStar)).toExist(); + }); }); describe('RTL', () => { @@ -222,3 +240,39 @@ describe('RTL', () => { expect(importTooltip).toBeInTheDocument(); }); }); + +describe('DashboardList - anonymous view', () => { + const mockedProps = {}; + const mockUserLoggedOut = {}; + let wrapper; + + beforeAll(async () => { + fetchMock.resetHistory(); + wrapper = mount( + + + + + , + ); + + await waitForComponentToPaint(wrapper); + }); + + afterAll(() => { + cleanup(); + fetch.resetMocks(); + }); + + it('does not render the Favorite Star column in list view for anonymous user', async () => { + wrapper.find('[aria-label="list-view"]').first().simulate('click'); + await waitForComponentToPaint(wrapper); + expect(wrapper.find(TableCollection).find(FaveStar)).not.toExist(); + }); + + it('does not render the Favorite Star in card view for anonymous user', async () => { + wrapper.find('[aria-label="card-view"]').first().simulate('click'); + await waitForComponentToPaint(wrapper); + expect(wrapper.find(CardCollection).find(FaveStar)).not.toExist(); + }); +}); diff --git a/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx b/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx index b3da8ee8e3534..a00ceefbc8761 100644 --- a/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx +++ b/superset-frontend/src/views/CRUD/dashboard/DashboardList.tsx @@ -17,7 +17,7 @@ * under the License. */ import { styled, SupersetClient, t } from '@superset-ui/core'; -import React, { useState, useMemo } from 'react'; +import React, { useState, useMemo, useCallback } from 'react'; import { Link } from 'react-router-dom'; import rison from 'rison'; import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags'; @@ -95,7 +95,11 @@ const Actions = styled.div` `; function DashboardList(props: DashboardListProps) { - const { addDangerToast, addSuccessToast } = props; + const { + addDangerToast, + addSuccessToast, + user: { userId }, + } = props; const { state: { @@ -143,7 +147,6 @@ function DashboardList(props: DashboardListProps) { addSuccessToast(t('Dashboard imported')); }; - const { userId } = props.user; // TODO: Fix usage of localStorage keying on the user id const userKey = dangerouslyGetItemDoNotUse(userId?.toString(), null); @@ -232,27 +235,25 @@ function DashboardList(props: DashboardListProps) { const columns = useMemo( () => [ - ...(props.user.userId - ? [ - { - Cell: ({ - row: { - original: { id }, - }, - }: any) => ( - - ), - Header: '', - id: 'id', - disableSortBy: true, - size: 'xs', - }, - ] - : []), + { + Cell: ({ + row: { + original: { id }, + }, + }: any) => + userId && ( + + ), + Header: '', + id: 'id', + disableSortBy: true, + size: 'xs', + hidden: !userId, + }, { Cell: ({ row: { @@ -422,10 +423,15 @@ function DashboardList(props: DashboardListProps) { }, ], [ + userId, canEdit, canDelete, canExport, - ...(props.user.userId ? [favoriteStatus] : []), + saveFavoriteStatus, + favoriteStatus, + refreshData, + addSuccessToast, + addDangerToast, ], ); @@ -500,7 +506,7 @@ function DashboardList(props: DashboardListProps) { { label: t('Draft'), value: false }, ], }, - ...(props.user.userId ? [favoritesFilter] : []), + ...(userId ? [favoritesFilter] : []), { Header: t('Certified'), id: 'id', @@ -544,8 +550,8 @@ function DashboardList(props: DashboardListProps) { }, ]; - function renderCard(dashboard: Dashboard) { - return ( + const renderCard = useCallback( + (dashboard: Dashboard) => ( - ); - } + ), + [ + addDangerToast, + addSuccessToast, + bulkSelectEnabled, + favoriteStatus, + hasPerm, + loading, + userId, + refreshData, + saveFavoriteStatus, + userKey, + ], + ); const subMenuButtons: SubMenuProps['buttons'] = []; if (canDelete || canExport) { diff --git a/superset-frontend/src/views/CRUD/utils.tsx b/superset-frontend/src/views/CRUD/utils.tsx index 3449d764abfa2..7f069a3e7c712 100644 --- a/superset-frontend/src/views/CRUD/utils.tsx +++ b/superset-frontend/src/views/CRUD/utils.tsx @@ -285,7 +285,7 @@ export function handleDashboardDelete( addSuccessToast: (arg0: string) => void, addDangerToast: (arg0: string) => void, dashboardFilter?: string, - userId?: number, + userId?: string | number, ) { return SupersetClient.delete({ endpoint: `/api/v1/dashboard/${id}`, From 0de03c4b34fd13d4f751710277ede3ca0c9b1033 Mon Sep 17 00:00:00 2001 From: Ville Brofeldt <33317356+villebro@users.noreply.github.com> Date: Mon, 4 Apr 2022 13:40:39 +0300 Subject: [PATCH 17/31] chore(releasing): use node 16 for testing and remove redundant updating note (#19505) * chore(releasing): use node 16 for testing * remove note about required cache --- RELEASING/Dockerfile.from_local_tarball | 2 +- RELEASING/Dockerfile.from_svn_tarball | 2 +- UPDATING.md | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/RELEASING/Dockerfile.from_local_tarball b/RELEASING/Dockerfile.from_local_tarball index be08d6fb951fc..3cd030609b60e 100644 --- a/RELEASING/Dockerfile.from_local_tarball +++ b/RELEASING/Dockerfile.from_local_tarball @@ -34,7 +34,7 @@ RUN apt-get install -y build-essential libssl-dev \ # Install nodejs for custom build # https://nodejs.org/en/download/package-manager/ -RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - \ +RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - \ && apt-get install -y nodejs RUN mkdir -p /home/superset diff --git a/RELEASING/Dockerfile.from_svn_tarball b/RELEASING/Dockerfile.from_svn_tarball index 5f9bde9c3f9ba..482ab474a58e3 100644 --- a/RELEASING/Dockerfile.from_svn_tarball +++ b/RELEASING/Dockerfile.from_svn_tarball @@ -34,7 +34,7 @@ RUN apt-get install -y build-essential libssl-dev \ # Install nodejs for custom build # https://nodejs.org/en/download/package-manager/ -RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - \ +RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - \ && apt-get install -y nodejs RUN mkdir -p /home/superset diff --git a/UPDATING.md b/UPDATING.md index 865b3907d5ff5..e1b14db28f184 100644 --- a/UPDATING.md +++ b/UPDATING.md @@ -39,7 +39,6 @@ assists people when migrating to a new version. - [19231](https://github.com/apache/superset/pull/19231): The `ENABLE_REACT_CRUD_VIEWS` feature flag has been removed (permanently enabled). Any deployments which had set this flag to false will need to verify that the React views support their use case. - [17556](https://github.com/apache/superset/pull/17556): Bumps mysqlclient from v1 to v2 - [19113](https://github.com/apache/superset/pull/19113): The `ENABLE_JAVASCRIPT_CONTROLS` setting has moved from app config to a feature flag. Any deployments who overrode this setting will now need to override the feature flag from here onward. -- [18976](https://github.com/apache/superset/pull/18976): When running the app in debug mode, the app will default to use `SimpleCache` for `FILTER_STATE_CACHE_CONFIG` and `EXPLORE_FORM_DATA_CACHE_CONFIG`. When running in non-debug mode, a cache backend will need to be defined, otherwise the application will fail to start. For installations using Redis or other caching backends, it is recommended to use the same backend for both cache configs. - [17881](https://github.com/apache/superset/pull/17881): Previously simple adhoc filter values on string columns were stripped of enclosing single and double quotes. To fully support literal quotes in filters, both single and double quotes will no longer be removed from filter values. - [17984](https://github.com/apache/superset/pull/17984): Default Flask SECRET_KEY has changed for security reasons. You should always override with your own secret. Set `PREVIOUS_SECRET_KEY` (ex: PREVIOUS_SECRET_KEY = "\2\1thisismyscretkey\1\2\\e\\y\\y\\h") with your previous key and use `superset re-encrypt-secrets` to rotate you current secrets - [15254](https://github.com/apache/superset/pull/15254): Previously `QUERY_COST_FORMATTERS_BY_ENGINE`, `SQL_VALIDATORS_BY_ENGINE` and `SCHEDULED_QUERIES` were expected to be defined in the feature flag dictionary in the `config.py` file. These should now be defined as a top-level config, with the feature flag dictionary being reserved for boolean only values. From b601db78090ba1823ee63c797491bee75f4ee1af Mon Sep 17 00:00:00 2001 From: Geido <60598000+geido@users.noreply.github.com> Date: Mon, 4 Apr 2022 17:10:20 +0300 Subject: [PATCH 18/31] chore: Remove StyledQueryButton.less (#19441) * Remove StyledQueryButton less * Remove unused className --- .../ScheduleQueryButton.less | 39 ------------------- .../components/ScheduleQueryButton/index.tsx | 23 +++++++++-- 2 files changed, 20 insertions(+), 42 deletions(-) delete mode 100644 superset-frontend/src/SqlLab/components/ScheduleQueryButton/ScheduleQueryButton.less diff --git a/superset-frontend/src/SqlLab/components/ScheduleQueryButton/ScheduleQueryButton.less b/superset-frontend/src/SqlLab/components/ScheduleQueryButton/ScheduleQueryButton.less deleted file mode 100644 index 4ae5847227caa..0000000000000 --- a/superset-frontend/src/SqlLab/components/ScheduleQueryButton/ScheduleQueryButton.less +++ /dev/null @@ -1,39 +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. - */ -// ------------------------------------------------------------- -// Glyphicons are not supported and used by react-json-schema -// ------------------------------------------------------------- -.json-schema { - i.glyphicon { - display: none; - } - .btn-add::after { - content: '+'; - } - .array-item-move-up::after { - content: '↑'; - } - .array-item-move-down::after { - content: '↓'; - } - .array-item-remove::after { - content: '-'; - } -} -// ------------------------------------------------------------- diff --git a/superset-frontend/src/SqlLab/components/ScheduleQueryButton/index.tsx b/superset-frontend/src/SqlLab/components/ScheduleQueryButton/index.tsx index 900e34d05a594..e1c7b54d025ea 100644 --- a/superset-frontend/src/SqlLab/components/ScheduleQueryButton/index.tsx +++ b/superset-frontend/src/SqlLab/components/ScheduleQueryButton/index.tsx @@ -24,7 +24,6 @@ import { t, styled } from '@superset-ui/core'; import * as chrono from 'chrono-node'; import ModalTrigger from 'src/components/ModalTrigger'; import { Form, FormItem } from 'src/components/Form'; -import './ScheduleQueryButton.less'; import Button from 'src/components/Button'; const appContainer = document.getElementById('app'); @@ -111,6 +110,24 @@ export const StyledButtonComponent = styled(Button)` } `; +const StyledJsonSchema = styled.div` + i.glyphicon { + display: none; + } + .btn-add::after { + content: '+'; + } + .array-item-move-up::after { + content: '↑'; + } + .array-item-move-down::after { + content: '↓'; + } + .array-item-remove::after { + content: '-'; + } +`; + const ScheduleQueryButton: FunctionComponent = ({ defaultLabel = t('Undefined'), sql, @@ -175,7 +192,7 @@ const ScheduleQueryButton: FunctionComponent = ({ -
+ = ({ Submit -
+
{scheduleQueryWarning && ( From 7914296d8a1fa142337792341f6f500812581893 Mon Sep 17 00:00:00 2001 From: Geido <60598000+geido@users.noreply.github.com> Date: Mon, 4 Apr 2022 17:11:22 +0300 Subject: [PATCH 19/31] chore: Remove FilterbaleTableStyles.less (#19442) * Remove FilterbaleTablesStyles less * Add theme top level --- superset-frontend/src/SqlLab/App.jsx | 1 - .../FilterableTable/FilterableTable.tsx | 108 +++++++++++++++- .../FilterableTableStyles.less | 118 ------------------ 3 files changed, 104 insertions(+), 123 deletions(-) delete mode 100644 superset-frontend/src/components/FilterableTable/FilterableTableStyles.less diff --git a/superset-frontend/src/SqlLab/App.jsx b/superset-frontend/src/SqlLab/App.jsx index e2118e2efd084..02a4df2a6f8d9 100644 --- a/superset-frontend/src/SqlLab/App.jsx +++ b/superset-frontend/src/SqlLab/App.jsx @@ -41,7 +41,6 @@ import setupApp from '../setup/setupApp'; import './main.less'; import '../assets/stylesheets/reactable-pagination.less'; -import '../components/FilterableTable/FilterableTableStyles.less'; import { theme } from '../preamble'; setupApp(); diff --git a/superset-frontend/src/components/FilterableTable/FilterableTable.tsx b/superset-frontend/src/components/FilterableTable/FilterableTable.tsx index 23d68f96850c9..c0b49f8619d33 100644 --- a/superset-frontend/src/components/FilterableTable/FilterableTable.tsx +++ b/superset-frontend/src/components/FilterableTable/FilterableTable.tsx @@ -87,10 +87,110 @@ const JSON_TREE_THEME = { const ONLY_NUMBER_REGEX = /^(NaN|-?((\d*\.\d+|\d+)([Ee][+-]?\d+)?|Infinity))$/; const StyledFilterableTable = styled.div` - height: 100%; - overflow-x: auto; - margin-top: ${({ theme }) => theme.gridUnit * 2}px; - overflow-y: hidden; + ${({ theme }) => ` + height: 100%; + overflow-x: auto; + margin-top: ${theme.gridUnit * 2}px; + overflow-y: hidden; + + .ReactVirtualized__Grid__innerScrollContainer { + border: 1px solid ${theme.colors.grayscale.light2}; + } + + .ReactVirtualized__Table__headerRow { + font-weight: ${theme.typography.weights.bold}; + display: flex; + flex-direction: row; + align-items: center; + border: 1px solid ${theme.colors.grayscale.light2}; + } + + .ReactVirtualized__Table__row { + display: flex; + flex-direction: row; + } + + .ReactVirtualized__Table__headerTruncatedText, + .grid-header-cell { + display: inline-block; + max-width: 100%; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + + .ReactVirtualized__Table__headerColumn, + .ReactVirtualized__Table__rowColumn, + .grid-cell { + min-width: 0px; + border-right: 1px solid ${theme.colors.grayscale.light2}; + align-self: center; + padding: ${theme.gridUnit * 3}px; + font-size: ${theme.typography.sizes.s}px; + } + + .grid-header-cell { + font-weight: ${theme.typography.weights.bold}; + cursor: pointer; + } + + .ReactVirtualized__Table__headerColumn:last-of-type, + .ReactVirtualized__Table__rowColumn:last-of-type { + border-right: 0px; + } + + .ReactVirtualized__Table__headerColumn:focus, + .ReactVirtualized__Table__Grid:focus { + outline: none; + } + + .ReactVirtualized__Table__rowColumn { + text-overflow: ellipsis; + white-space: nowrap; + } + + .ReactVirtualized__Table__sortableHeaderColumn { + cursor: pointer; + } + + .ReactVirtualized__Table__sortableHeaderIconContainer { + display: flex; + align-items: center; + } + + .ReactVirtualized__Table__sortableHeaderIcon { + flex: 0 0 ${theme.gridUnit * 6}px; + height: 1em; + width: 1em; + fill: currentColor; + } + + .even-row { + background: ${theme.colors.grayscale.light4}; + } + + .odd-row { + background: ${theme.colors.grayscale.light5}; + } + + .header-style { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .header-style-disabled { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: ${theme.colors.grayscale.light1}; + } + + .cell-text-for-measuring { + font-family: ${theme.typography.families.sansSerif}; + font-size: ${theme.typography.sizes.s}px; + } + `} `; // when more than MAX_COLUMNS_FOR_TABLE are returned, switch from table to grid view diff --git a/superset-frontend/src/components/FilterableTable/FilterableTableStyles.less b/superset-frontend/src/components/FilterableTable/FilterableTableStyles.less deleted file mode 100644 index d921149ae5fa5..0000000000000 --- a/superset-frontend/src/components/FilterableTable/FilterableTableStyles.less +++ /dev/null @@ -1,118 +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 '../../assets/stylesheets/less/variables.less'; - -.ReactVirtualized__Grid__innerScrollContainer { - border: 1px solid @gray-light; -} - -.ReactVirtualized__Table__headerRow { - font-weight: @font-weight-bold; - display: flex; - flex-direction: row; - align-items: center; - border: 1px solid @gray-light; -} - -.ReactVirtualized__Table__row { - display: flex; - flex-direction: row; -} - -.ReactVirtualized__Table__headerTruncatedText, -.grid-header-cell { - display: inline-block; - max-width: 100%; - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; -} - -.ReactVirtualized__Table__headerColumn, -.ReactVirtualized__Table__rowColumn, -.grid-cell { - min-width: 0px; - border-right: 1px solid @gray-light; - align-self: center; - padding: 12px; - font-size: @font-size-s; -} - -.grid-header-cell { - font-weight: @font-weight-bold; - cursor: pointer; -} - -.ReactVirtualized__Table__headerColumn:last-of-type, -.ReactVirtualized__Table__rowColumn:last-of-type { - border-right: 0px; -} - -.ReactVirtualized__Table__headerColumn:focus, -.ReactVirtualized__Table__Grid:focus { - outline: none; -} - -.ReactVirtualized__Table__rowColumn { - text-overflow: ellipsis; - white-space: nowrap; -} - -.ReactVirtualized__Table__sortableHeaderColumn { - cursor: pointer; -} - -.ReactVirtualized__Table__sortableHeaderIconContainer { - display: flex; - align-items: center; -} - -.ReactVirtualized__Table__sortableHeaderIcon { - flex: 0 0 24px; - height: 1em; - width: 1em; - fill: currentColor; -} - -.even-row { - background: @gray-bg; -} - -.odd-row { - background: @lightest; -} - -.header-style { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.header-style-disabled { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - color: @gray-heading; -} - -.cell-text-for-measuring { - font-family: Helvetica, Arial, sans-serif; - font-size: @font-size-s; -} From 4b922873ae4fc7ee480e28c3b59c6f2b15d891b2 Mon Sep 17 00:00:00 2001 From: Geido <60598000+geido@users.noreply.github.com> Date: Mon, 4 Apr 2022 17:19:54 +0300 Subject: [PATCH 20/31] Add theme color (#19490) --- .../src/ReactParallelCoordinates.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset-frontend/plugins/legacy-plugin-chart-parallel-coordinates/src/ReactParallelCoordinates.jsx b/superset-frontend/plugins/legacy-plugin-chart-parallel-coordinates/src/ReactParallelCoordinates.jsx index 8633607984952..9e0ec30e546e4 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-parallel-coordinates/src/ReactParallelCoordinates.jsx +++ b/superset-frontend/plugins/legacy-plugin-chart-parallel-coordinates/src/ReactParallelCoordinates.jsx @@ -39,7 +39,7 @@ export default styled(ParallelCoordianes)` overflow: auto; div.row { &:hover { - background-color: #ccc; + background-color: ${({ theme }) => theme.colors.grayscale.light2}; } } } From 47308024a3e25f319aee7419fd91d0b5b8e0e1be Mon Sep 17 00:00:00 2001 From: "Michael S. Molina" <70410625+michael-s-molina@users.noreply.github.com> Date: Mon, 4 Apr 2022 13:46:34 -0300 Subject: [PATCH 21/31] refactor: Removes the CSS files from the Sunburst plugin (#19488) --- .../legacy-plugin-chart-sunburst/package.json | 36 +++++----- .../src/ReactSunburst.js | 22 ------ .../src/ReactSunburst.jsx | 66 +++++++++++++++++ .../src/Sunburst.css | 70 ------------------- .../src/Sunburst.js | 1 - 5 files changed, 83 insertions(+), 112 deletions(-) delete mode 100644 superset-frontend/plugins/legacy-plugin-chart-sunburst/src/ReactSunburst.js create mode 100644 superset-frontend/plugins/legacy-plugin-chart-sunburst/src/ReactSunburst.jsx delete mode 100644 superset-frontend/plugins/legacy-plugin-chart-sunburst/src/Sunburst.css diff --git a/superset-frontend/plugins/legacy-plugin-chart-sunburst/package.json b/superset-frontend/plugins/legacy-plugin-chart-sunburst/package.json index db840722fe47a..cdba4664f47e7 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-sunburst/package.json +++ b/superset-frontend/plugins/legacy-plugin-chart-sunburst/package.json @@ -2,37 +2,35 @@ "name": "@superset-ui/legacy-plugin-chart-sunburst", "version": "0.18.25", "description": "Superset Legacy Chart - Sunburst", - "sideEffects": [ - "*.css" - ], - "main": "lib/index.js", - "module": "esm/index.js", - "files": [ - "esm", - "lib" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/apache-superset/superset-ui.git" - }, "keywords": [ "superset" ], - "author": "Superset", - "license": "Apache-2.0", + "homepage": "https://github.com/apache-superset/superset-ui#readme", "bugs": { "url": "https://github.com/apache-superset/superset-ui/issues" }, - "homepage": "https://github.com/apache-superset/superset-ui#readme", - "publishConfig": { - "access": "public" + "repository": { + "type": "git", + "url": "git+https://github.com/apache-superset/superset-ui.git" }, + "license": "Apache-2.0", + "author": "Superset", + "main": "lib/index.js", + "module": "esm/index.js", + "files": [ + "esm", + "lib" + ], "dependencies": { "d3": "^3.5.17", "prop-types": "^15.6.2" }, "peerDependencies": { "@superset-ui/chart-controls": "*", - "@superset-ui/core": "*" + "@superset-ui/core": "*", + "react": "^16.13.1" + }, + "publishConfig": { + "access": "public" } } diff --git a/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/ReactSunburst.js b/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/ReactSunburst.js deleted file mode 100644 index 15303c9eafb62..0000000000000 --- a/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/ReactSunburst.js +++ /dev/null @@ -1,22 +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 { reactify } from '@superset-ui/core'; -import Component from './Sunburst'; - -export default reactify(Component); diff --git a/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/ReactSunburst.jsx b/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/ReactSunburst.jsx new file mode 100644 index 0000000000000..10e959285bb4b --- /dev/null +++ b/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/ReactSunburst.jsx @@ -0,0 +1,66 @@ +/** + * 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 React from 'react'; +import { reactify, styled } from '@superset-ui/core'; +import Component from './Sunburst'; + +const ReactComponent = reactify(Component); + +const Sunburst = ({ className, ...otherProps }) => ( +
+ +
+); + +export default styled(Sunburst)` + ${({ theme }) => ` + .superset-legacy-chart-sunburst text { + text-rendering: optimizeLegibility; + } + .superset-legacy-chart-sunburst path { + stroke: ${theme.colors.grayscale.light2}; + stroke-width: 0.5px; + } + .superset-legacy-chart-sunburst .center-label { + text-anchor: middle; + fill: ${theme.colors.grayscale.dark1}; + pointer-events: none; + } + .superset-legacy-chart-sunburst .path-abs-percent { + font-size: ${theme.typography.sizes.m}px; + font-weight: ${theme.typography.weights.bold}; + } + .superset-legacy-chart-sunburst .path-cond-percent { + font-size: ${theme.typography.sizes.s}px; + } + .superset-legacy-chart-sunburst .path-metrics { + color: ${theme.colors.grayscale.base}; + } + .superset-legacy-chart-sunburst .path-ratio { + color: ${theme.colors.grayscale.base}; + } + + .superset-legacy-chart-sunburst .breadcrumbs text { + font-weight: ${theme.typography.weights.bold}; + font-size: ${theme.typography.sizes.m}px; + text-anchor: middle; + fill: ${theme.colors.grayscale.dark1}; + } + `} +`; diff --git a/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/Sunburst.css b/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/Sunburst.css deleted file mode 100644 index 0afe0a87951cc..0000000000000 --- a/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/Sunburst.css +++ /dev/null @@ -1,70 +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. - */ -.superset-legacy-chart-sunburst text { - text-rendering: optimizeLegibility; -} -.superset-legacy-chart-sunburst path { - stroke: #ddd; - stroke-width: 0.5px; -} -.superset-legacy-chart-sunburst .center-label { - text-anchor: middle; - fill: #333; - pointer-events: none; -} -.superset-legacy-chart-sunburst .path-abs-percent { - font-size: 3em; - font-weight: 700; -} -.superset-legacy-chart-sunburst .path-cond-percent { - font-size: 2em; -} -.superset-legacy-chart-sunburst .path-metrics { - color: #777; -} -.superset-legacy-chart-sunburst .path-ratio { - color: #777; -} - -.superset-legacy-chart-sunburst .breadcrumbs text { - font-weight: 600; - font-size: 1.2em; - text-anchor: middle; - fill: #333; -} - -/* dashboard specific */ -.dashboard-chart.sunburst { - overflow: visible; -} -.superset-legacy-chart-sunburst svg { - overflow: visible; -} -.superset-legacy-chart-sunburst.m text { - font-size: 0.55em; -} -.superset-legacy-chart-sunburst.s text { - font-size: 0.45em; -} -.superset-legacy-chart-sunburst.l text { - font-size: 0.75em; -} -.superset-legacy-chart-sunburst .path-abs-percent { - font-weight: 700; -} diff --git a/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/Sunburst.js b/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/Sunburst.js index 2a9cc56f51fc6..4418f68bbd150 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/Sunburst.js +++ b/superset-frontend/plugins/legacy-plugin-chart-sunburst/src/Sunburst.js @@ -26,7 +26,6 @@ import { getSequentialSchemeRegistry, } from '@superset-ui/core'; import wrapSvgText from './utils/wrapSvgText'; -import './Sunburst.css'; const propTypes = { // Each row is an array of [hierarchy-lvl1, hierarchy-lvl2, metric1, metric2] From 82a653f64b64c15b67072e76083e90709a61e21b Mon Sep 17 00:00:00 2001 From: "Michael S. Molina" <70410625+michael-s-molina@users.noreply.github.com> Date: Mon, 4 Apr 2022 13:46:46 -0300 Subject: [PATCH 22/31] refactor: Removes the CSS files from the Treemap plugin (#19486) --- .../legacy-plugin-chart-treemap/package.json | 36 +++++++++---------- .../src/{ReactTreemap.js => ReactTreemap.jsx} | 33 +++++++++++++++-- .../src/Treemap.css | 36 ------------------- .../src/Treemap.js | 1 - 4 files changed, 48 insertions(+), 58 deletions(-) rename superset-frontend/plugins/legacy-plugin-chart-treemap/src/{ReactTreemap.js => ReactTreemap.jsx} (52%) delete mode 100644 superset-frontend/plugins/legacy-plugin-chart-treemap/src/Treemap.css diff --git a/superset-frontend/plugins/legacy-plugin-chart-treemap/package.json b/superset-frontend/plugins/legacy-plugin-chart-treemap/package.json index afd55b88c18a4..96d582d7859cb 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-treemap/package.json +++ b/superset-frontend/plugins/legacy-plugin-chart-treemap/package.json @@ -2,31 +2,25 @@ "name": "@superset-ui/legacy-plugin-chart-treemap", "version": "0.18.25", "description": "Superset Legacy Chart - Treemap", - "sideEffects": [ - "*.css" - ], - "main": "lib/index.js", - "module": "esm/index.js", - "files": [ - "esm", - "lib" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/apache-superset/superset-ui.git" - }, "keywords": [ "superset" ], - "author": "Superset", - "license": "Apache-2.0", + "homepage": "https://github.com/apache-superset/superset-ui#readme", "bugs": { "url": "https://github.com/apache-superset/superset-ui/issues" }, - "homepage": "https://github.com/apache-superset/superset-ui#readme", - "publishConfig": { - "access": "public" + "repository": { + "type": "git", + "url": "git+https://github.com/apache-superset/superset-ui.git" }, + "license": "Apache-2.0", + "author": "Superset", + "main": "lib/index.js", + "module": "esm/index.js", + "files": [ + "esm", + "lib" + ], "dependencies": { "d3-hierarchy": "^1.1.8", "d3-selection": "^1.4.0", @@ -34,6 +28,10 @@ }, "peerDependencies": { "@superset-ui/chart-controls": "*", - "@superset-ui/core": "*" + "@superset-ui/core": "*", + "react": "^16.13.1" + }, + "publishConfig": { + "access": "public" } } diff --git a/superset-frontend/plugins/legacy-plugin-chart-treemap/src/ReactTreemap.js b/superset-frontend/plugins/legacy-plugin-chart-treemap/src/ReactTreemap.jsx similarity index 52% rename from superset-frontend/plugins/legacy-plugin-chart-treemap/src/ReactTreemap.js rename to superset-frontend/plugins/legacy-plugin-chart-treemap/src/ReactTreemap.jsx index 743115c018798..c00d8b5d17492 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-treemap/src/ReactTreemap.js +++ b/superset-frontend/plugins/legacy-plugin-chart-treemap/src/ReactTreemap.jsx @@ -16,7 +16,36 @@ * specific language governing permissions and limitations * under the License. */ -import { reactify } from '@superset-ui/core'; +import React from 'react'; +import { reactify, styled } from '@superset-ui/core'; import Component from './Treemap'; -export default reactify(Component); +const ReactComponent = reactify(Component); + +const Treemap = ({ className, ...otherProps }) => ( +
+ +
+); + +export default styled(Treemap)` + ${({ theme }) => ` + .superset-legacy-chart-treemap text { + font-size: ${theme.typography.sizes.s}px; + pointer-events: none; + } + + .superset-legacy-chart-treemap tspan:last-child { + font-size: ${theme.typography.sizes.xs}px; + fill-opacity: 0.8; + } + + .superset-legacy-chart-treemap .node rect { + shape-rendering: crispEdges; + } + + .superset-legacy-chart-treemap .node--hover rect { + stroke: ${theme.colors.grayscale.dark2}; + } + `} +`; diff --git a/superset-frontend/plugins/legacy-plugin-chart-treemap/src/Treemap.css b/superset-frontend/plugins/legacy-plugin-chart-treemap/src/Treemap.css deleted file mode 100644 index f49b425f200ba..0000000000000 --- a/superset-frontend/plugins/legacy-plugin-chart-treemap/src/Treemap.css +++ /dev/null @@ -1,36 +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. - */ - -.superset-legacy-chart-treemap text { - font-size: 11px; - pointer-events: none; -} - -.superset-legacy-chart-treemap tspan:last-child { - font-size: 9px; - fill-opacity: 0.8; -} - -.superset-legacy-chart-treemap .node rect { - shape-rendering: crispEdges; -} - -.superset-legacy-chart-treemap .node--hover rect { - stroke: #000; -} diff --git a/superset-frontend/plugins/legacy-plugin-chart-treemap/src/Treemap.js b/superset-frontend/plugins/legacy-plugin-chart-treemap/src/Treemap.js index f218218ec8bbd..e0f4e691220c2 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-treemap/src/Treemap.js +++ b/superset-frontend/plugins/legacy-plugin-chart-treemap/src/Treemap.js @@ -29,7 +29,6 @@ import { getNumberFormatter, CategoricalColorNamespace, } from '@superset-ui/core'; -import './Treemap.css'; // Declare PropTypes for recursive data structures // https://github.com/facebook/react/issues/5676 From c2fae82fa60f75023446a9b0940f437b4933d532 Mon Sep 17 00:00:00 2001 From: "Hugh A. Miles II" Date: Mon, 4 Apr 2022 14:19:46 -0400 Subject: [PATCH 23/31] fix key reference (#19519) --- superset-frontend/src/views/components/MenuRight.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset-frontend/src/views/components/MenuRight.tsx b/superset-frontend/src/views/components/MenuRight.tsx index 6495b62912796..4628a47e2402b 100644 --- a/superset-frontend/src/views/components/MenuRight.tsx +++ b/superset-frontend/src/views/components/MenuRight.tsx @@ -203,7 +203,7 @@ const RightMenu = ({ typeof item !== 'string' && item.name && item.perm ? ( {idx === 2 && } - + {item.url ? ( {item.label} ) : ( From 79abd23f48bcad15f3c879b2ec0713648f066849 Mon Sep 17 00:00:00 2001 From: Phillip Kelley-Dotson Date: Mon, 4 Apr 2022 15:31:53 -0700 Subject: [PATCH 24/31] fix: add back view for report reload error (#19522) * fix: add back view for report reload error * remove print * run black --- superset/initialization/__init__.py | 3 ++- superset/views/alerts.py | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/superset/initialization/__init__.py b/superset/initialization/__init__.py index e600b0f2f5e7b..74b05e168804e 100644 --- a/superset/initialization/__init__.py +++ b/superset/initialization/__init__.py @@ -151,7 +151,7 @@ def init_views(self) -> None: from superset.reports.logs.api import ReportExecutionLogRestApi from superset.security.api import SecurityRestApi from superset.views.access_requests import AccessRequestsModelView - from superset.views.alerts import AlertView + from superset.views.alerts import AlertView, ReportView from superset.views.annotations import ( AnnotationLayerModelView, AnnotationModelView, @@ -445,6 +445,7 @@ def init_views(self) -> None: and self.config["DRUID_METADATA_LINKS_ENABLED"] ), ) + appbuilder.add_view_no_menu(ReportView) appbuilder.add_link( "Refresh Druid Metadata", label=__("Refresh Druid Metadata"), diff --git a/superset/views/alerts.py b/superset/views/alerts.py index b97587ec71855..ad0fefec64044 100644 --- a/superset/views/alerts.py +++ b/superset/views/alerts.py @@ -52,3 +52,8 @@ def log(self, pk: int) -> FlaskResponse: # pylint: disable=unused-argument class AlertView(BaseAlertReportView): route_base = "/alert" class_permission_name = "ReportSchedule" + + +class ReportView(BaseAlertReportView): + route_base = "/report" + class_permission_name = "ReportSchedule" From 5861bd399fa914207e507a4764b5d50b7d783722 Mon Sep 17 00:00:00 2001 From: Inclusive Coding Bot <102100353+inclusive-coding-bot@users.noreply.github.com> Date: Mon, 4 Apr 2022 20:17:34 -0400 Subject: [PATCH 25/31] chore: Switch to gender neutral terms (#19460) * Switch to gender neutral terms * Update countries.md Co-authored-by: inclusive-coding-bot --- tests/integration_tests/druid_tests.py | 2 +- tests/integration_tests/queries/api_tests.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration_tests/druid_tests.py b/tests/integration_tests/druid_tests.py index a9c787ecc8a8d..66f5cc7244fc5 100644 --- a/tests/integration_tests/druid_tests.py +++ b/tests/integration_tests/druid_tests.py @@ -290,7 +290,7 @@ def check(): .one() ) # columns and metrics are not deleted if config is changed as - # user could define his own dimensions / metrics and want to keep them + # user could define their own dimensions / metrics and want to keep them assert set([c.column_name for c in druid_ds.columns]) == set( ["affiliate_id", "campaign", "first_seen", "second_seen"] ) diff --git a/tests/integration_tests/queries/api_tests.py b/tests/integration_tests/queries/api_tests.py index d086e1082108e..eaf4e00576573 100644 --- a/tests/integration_tests/queries/api_tests.py +++ b/tests/integration_tests/queries/api_tests.py @@ -210,7 +210,7 @@ def test_get_query_no_data_access(self): get_example_database().id, gamma2.id, gamma2_client_id ) - # Gamma1 user, only sees his own queries + # Gamma1 user, only sees their own queries self.login(username="gamma_1", password="password") uri = f"api/v1/query/{query_gamma2.id}" rv = self.client.get(uri) @@ -219,7 +219,7 @@ def test_get_query_no_data_access(self): rv = self.client.get(uri) self.assertEqual(rv.status_code, 200) - # Gamma2 user, only sees his own queries + # Gamma2 user, only sees their own queries self.logout() self.login(username="gamma_2", password="password") uri = f"api/v1/query/{query_gamma1.id}" From 1eef923b31005eaf13e4b068a939d0fb27716c90 Mon Sep 17 00:00:00 2001 From: Ville Brofeldt <33317356+villebro@users.noreply.github.com> Date: Tue, 5 Apr 2022 10:10:29 +0300 Subject: [PATCH 26/31] chore: postpone timerange endpoint removal (#19513) --- ...9d86e695_deprecate_time_range_endpoints.py | 27 +-------- ...074e4_deprecate_time_range_endpoints_v2.py | 59 +++++++++++++++++++ 2 files changed, 60 insertions(+), 26 deletions(-) create mode 100644 superset/migrations/versions/b0d0249074e4_deprecate_time_range_endpoints_v2.py diff --git a/superset/migrations/versions/ab9a9d86e695_deprecate_time_range_endpoints.py b/superset/migrations/versions/ab9a9d86e695_deprecate_time_range_endpoints.py index 2bd65996140d7..148804d2588df 100644 --- a/superset/migrations/versions/ab9a9d86e695_deprecate_time_range_endpoints.py +++ b/superset/migrations/versions/ab9a9d86e695_deprecate_time_range_endpoints.py @@ -21,38 +21,13 @@ Create Date: 2022-02-25 08:06:14.835094 """ -import json - -from alembic import op -from sqlalchemy import Column, Integer, Text -from sqlalchemy.ext.declarative import declarative_base - -from superset import db - # revision identifiers, used by Alembic. revision = "ab9a9d86e695" down_revision = "b5a422d8e252" -Base = declarative_base() - - -class Slice(Base): - __tablename__ = "slices" - id = Column(Integer, primary_key=True) - params = Column(Text) - def upgrade(): - bind = op.get_bind() - session = db.Session(bind=bind) - - for slc in session.query(Slice).filter(Slice.params.like("%time_range_endpoints%")): - params = json.loads(slc.params) - params.pop("time_range_endpoints", None) - slc.params = json.dumps(params) - - session.commit() - session.close() + pass def downgrade(): diff --git a/superset/migrations/versions/b0d0249074e4_deprecate_time_range_endpoints_v2.py b/superset/migrations/versions/b0d0249074e4_deprecate_time_range_endpoints_v2.py new file mode 100644 index 0000000000000..90ee62d3f70d9 --- /dev/null +++ b/superset/migrations/versions/b0d0249074e4_deprecate_time_range_endpoints_v2.py @@ -0,0 +1,59 @@ +# 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. +"""deprecate time_range_endpoints v2 + +Revision ID: b0d0249074e4 +Revises: 2ed890b36b94 +Create Date: 2022-04-04 15:04:05.606340 + +""" +import json + +from alembic import op +from sqlalchemy import Column, Integer, Text +from sqlalchemy.ext.declarative import declarative_base + +from superset import db + +# revision identifiers, used by Alembic. +revision = "b0d0249074e4" +down_revision = "2ed890b36b94" + +Base = declarative_base() + + +class Slice(Base): + __tablename__ = "slices" + id = Column(Integer, primary_key=True) + params = Column(Text) + + +def upgrade(): + bind = op.get_bind() + session = db.Session(bind=bind) + + for slc in session.query(Slice).filter(Slice.params.like("%time_range_endpoints%")): + params = json.loads(slc.params) + params.pop("time_range_endpoints", None) + slc.params = json.dumps(params) + + session.commit() + session.close() + + +def downgrade(): + pass From 602afbaa31d72eefd213d85649eee494e72add7a Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Tue, 5 Apr 2022 15:20:29 +0200 Subject: [PATCH 27/31] feat(explore): Move chart header to top of the page (#19529) * Move chart header to top of the page * Implement truncating and dynamic input * fix typing * Prevent cmd+z undoing changes when not in edit mode * Fix tests, add missing types * Show changed title in altered --- .../src/components/FaveStar/index.tsx | 10 +- .../components/DatasourcePanel/index.tsx | 2 +- .../ChartEditableTitle.test.tsx | 68 +++ .../ChartEditableTitle/index.tsx | 213 +++++++++ .../components/ExploreChartHeader/index.jsx | 135 +++--- .../explore/components/ExploreChartPanel.jsx | 51 +-- .../components/ExploreViewContainer/index.jsx | 420 ++++++++++-------- .../controls/DatasourceControl/index.jsx | 3 +- 8 files changed, 602 insertions(+), 300 deletions(-) create mode 100644 superset-frontend/src/explore/components/ExploreChartHeader/ChartEditableTitle/ChartEditableTitle.test.tsx create mode 100644 superset-frontend/src/explore/components/ExploreChartHeader/ChartEditableTitle/index.tsx diff --git a/superset-frontend/src/components/FaveStar/index.tsx b/superset-frontend/src/components/FaveStar/index.tsx index ac5bb6065eab0..5953075854984 100644 --- a/superset-frontend/src/components/FaveStar/index.tsx +++ b/superset-frontend/src/components/FaveStar/index.tsx @@ -18,7 +18,7 @@ */ import React, { useCallback } from 'react'; -import { t, styled } from '@superset-ui/core'; +import { css, t, styled } from '@superset-ui/core'; import { Tooltip } from 'src/components/Tooltip'; import { useComponentDidMount } from 'src/hooks/useComponentDidMount'; import Icons from 'src/components/Icons'; @@ -32,9 +32,11 @@ interface FaveStarProps { } const StyledLink = styled.a` - font-size: ${({ theme }) => theme.typography.sizes.xl}px; - display: flex; - padding: 0 0 0 0.5em; + ${({ theme }) => css` + font-size: ${theme.typography.sizes.xl}px; + display: flex; + padding: 0 0 0 ${theme.gridUnit * 2}px; + `}; `; const FaveStar = ({ diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index ebed661be9e46..c38c1b59ae4fc 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -86,7 +86,7 @@ const DatasourceContainer = styled.div` color: ${theme.colors.grayscale.light1}; } .form-control.input-md { - width: calc(100% - ${theme.gridUnit * 4}px); + width: calc(100% - ${theme.gridUnit * 8}px); height: ${theme.gridUnit * 8}px; margin: ${theme.gridUnit * 2}px auto; } diff --git a/superset-frontend/src/explore/components/ExploreChartHeader/ChartEditableTitle/ChartEditableTitle.test.tsx b/superset-frontend/src/explore/components/ExploreChartHeader/ChartEditableTitle/ChartEditableTitle.test.tsx new file mode 100644 index 0000000000000..dd98518c8c41b --- /dev/null +++ b/superset-frontend/src/explore/components/ExploreChartHeader/ChartEditableTitle/ChartEditableTitle.test.tsx @@ -0,0 +1,68 @@ +/** + * 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 React from 'react'; +import userEvent from '@testing-library/user-event'; +import { render, screen } from 'spec/helpers/testing-library'; +import { ChartEditableTitle } from './index'; + +const createProps = (overrides: Record = {}) => ({ + title: 'Chart title', + placeholder: 'Add the name of the chart', + canEdit: true, + onSave: jest.fn(), + ...overrides, +}); + +describe('Chart editable title', () => { + it('renders chart title', () => { + const props = createProps(); + render(); + expect(screen.getByText('Chart title')).toBeVisible(); + }); + + it('renders placeholder', () => { + const props = createProps({ + title: '', + }); + render(); + expect(screen.getByText('Add the name of the chart')).toBeVisible(); + }); + + it('click, edit and save title', () => { + const props = createProps(); + render(); + const textboxElement = screen.getByRole('textbox'); + userEvent.click(textboxElement); + userEvent.type(textboxElement, ' edited'); + expect(screen.getByText('Chart title edited')).toBeVisible(); + userEvent.type(textboxElement, '{enter}'); + expect(props.onSave).toHaveBeenCalled(); + }); + + it('renders in non-editable mode', () => { + const props = createProps({ canEdit: false }); + render(); + const titleElement = screen.getByLabelText('Chart title'); + expect(screen.queryByRole('textbox')).not.toBeInTheDocument(); + expect(titleElement).toBeVisible(); + userEvent.click(titleElement); + userEvent.type(titleElement, ' edited{enter}'); + expect(props.onSave).not.toHaveBeenCalled(); + }); +}); diff --git a/superset-frontend/src/explore/components/ExploreChartHeader/ChartEditableTitle/index.tsx b/superset-frontend/src/explore/components/ExploreChartHeader/ChartEditableTitle/index.tsx new file mode 100644 index 0000000000000..0e2761b6a9dea --- /dev/null +++ b/superset-frontend/src/explore/components/ExploreChartHeader/ChartEditableTitle/index.tsx @@ -0,0 +1,213 @@ +/** + * 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 React, { + ChangeEvent, + KeyboardEvent, + useCallback, + useEffect, + useLayoutEffect, + useRef, + useState, +} from 'react'; +import { css, styled, t } from '@superset-ui/core'; +import { Tooltip } from 'src/components/Tooltip'; +import { useResizeDetector } from 'react-resize-detector'; + +export type ChartEditableTitleProps = { + title: string; + placeholder: string; + onSave: (title: string) => void; + canEdit: boolean; +}; + +const Styles = styled.div` + ${({ theme }) => css` + display: flex; + font-size: ${theme.typography.sizes.xl}px; + font-weight: ${theme.typography.weights.bold}; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + & .chart-title, + & .chart-title-input { + display: inline-block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + & .chart-title { + cursor: default; + } + & .chart-title-input { + border: none; + padding: 0; + outline: none; + + &::placeholder { + color: ${theme.colors.grayscale.light1}; + } + } + + & .input-sizer { + position: absolute; + left: -9999px; + display: inline-block; + } + `} +`; + +export const ChartEditableTitle = ({ + title, + placeholder, + onSave, + canEdit, +}: ChartEditableTitleProps) => { + const [isEditing, setIsEditing] = useState(false); + const [currentTitle, setCurrentTitle] = useState(title || ''); + const contentRef = useRef(null); + const [showTooltip, setShowTooltip] = useState(false); + + const { width: inputWidth, ref: sizerRef } = useResizeDetector(); + const { width: containerWidth, ref: containerRef } = useResizeDetector({ + refreshMode: 'debounce', + }); + + useEffect(() => { + if (isEditing && contentRef?.current) { + contentRef.current.focus(); + // move cursor and scroll to the end + if (contentRef.current.setSelectionRange) { + const { length } = contentRef.current.value; + contentRef.current.setSelectionRange(length, length); + contentRef.current.scrollLeft = contentRef.current.scrollWidth; + } + } + }, [isEditing]); + + // a trick to make the input grow when user types text + // we make additional span component, place it somewhere out of view and copy input + // then we can measure the width of that span to resize the input element + useLayoutEffect(() => { + if (sizerRef?.current) { + sizerRef.current.innerHTML = (currentTitle || placeholder).replace( + /\s/g, + ' ', + ); + } + }, [currentTitle, placeholder, sizerRef]); + + useEffect(() => { + if ( + contentRef.current && + contentRef.current.scrollWidth > contentRef.current.clientWidth + ) { + setShowTooltip(true); + } else { + setShowTooltip(false); + } + }, [inputWidth, containerWidth]); + + const handleClick = useCallback(() => { + if (!canEdit || isEditing) { + return; + } + setIsEditing(true); + }, [canEdit, isEditing]); + + const handleBlur = useCallback(() => { + if (!canEdit) { + return; + } + const formattedTitle = currentTitle.trim(); + setCurrentTitle(formattedTitle); + if (title !== formattedTitle) { + onSave(formattedTitle); + } + setIsEditing(false); + }, [canEdit, currentTitle, onSave, title]); + + const handleChange = useCallback( + (ev: ChangeEvent) => { + if (!canEdit || !isEditing) { + return; + } + setCurrentTitle(ev.target.value); + }, + [canEdit, isEditing], + ); + + const handleKeyPress = useCallback( + (ev: KeyboardEvent) => { + if (!canEdit) { + return; + } + if (ev.key === 'Enter') { + ev.preventDefault(); + contentRef.current?.blur(); + } + }, + [canEdit], + ); + + return ( + + + {canEdit ? ( + 0 && + css` + width: ${inputWidth}px; + `} + `} + /> + ) : ( + + {currentTitle} + + )} + + + + ); +}; diff --git a/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx b/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx index 665a2512ef273..d9d615dc1f33b 100644 --- a/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx +++ b/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx @@ -22,6 +22,7 @@ import { bindActionCreators } from 'redux'; import PropTypes from 'prop-types'; import { CategoricalColorNamespace, + css, SupersetClient, styled, t, @@ -33,7 +34,6 @@ import { } from 'src/reports/actions/reports'; import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags'; import { chartPropShape } from 'src/dashboard/util/propShapes'; -import EditableTitle from 'src/components/EditableTitle'; import AlteredSliceTag from 'src/components/AlteredSliceTag'; import FaveStar from 'src/components/FaveStar'; import Timer from 'src/components/Timer'; @@ -44,6 +44,7 @@ import CertifiedBadge from 'src/components/CertifiedBadge'; import withToasts from 'src/components/MessageToasts/withToasts'; import RowCountLabel from '../RowCountLabel'; import ExploreAdditionalActionsMenu from '../ExploreAdditionalActionsMenu'; +import { ChartEditableTitle } from './ChartEditableTitle'; const CHART_STATUS_MAP = { failed: 'danger', @@ -53,8 +54,8 @@ const CHART_STATUS_MAP = { const propTypes = { actions: PropTypes.object.isRequired, - can_overwrite: PropTypes.bool.isRequired, - can_download: PropTypes.bool.isRequired, + canOverwrite: PropTypes.bool.isRequired, + canDownload: PropTypes.bool.isRequired, dashboardId: PropTypes.number, isStarred: PropTypes.bool.isRequired, slice: PropTypes.object, @@ -67,37 +68,41 @@ const propTypes = { }; const StyledHeader = styled.div` - display: flex; - flex-direction: row; - align-items: center; - flex-wrap: wrap; - justify-content: space-between; - - span[role='button'] { + ${({ theme }) => css` display: flex; + flex-direction: row; + align-items: center; + flex-wrap: nowrap; + justify-content: space-between; height: 100%; - } - .title-panel { - display: flex; - align-items: center; - } + span[role='button'] { + display: flex; + height: 100%; + } - .right-button-panel { - display: flex; - align-items: center; + .title-panel { + display: flex; + align-items: center; + min-width: 0; + margin-right: ${theme.gridUnit * 6}px; + } + + .right-button-panel { + display: flex; + align-items: center; - > .btn-group { - flex: 0 0 auto; - margin-left: ${({ theme }) => theme.gridUnit}px; + > .btn-group { + flex: 0 0 auto; + margin-left: ${theme.gridUnit}px; + } } - } - .action-button { - color: ${({ theme }) => theme.colors.grayscale.base}; - margin: 0 ${({ theme }) => theme.gridUnit * 1.5}px 0 - ${({ theme }) => theme.gridUnit}px; - } + .action-button { + color: ${theme.colors.grayscale.base}; + margin: 0 ${theme.gridUnit * 1.5}px 0 ${theme.gridUnit}px; + } + `} `; const StyledButtons = styled.span` @@ -173,13 +178,6 @@ export class ExploreChartHeader extends React.PureComponent { .catch(() => {}); } - getSliceName() { - const { sliceName, table_name: tableName } = this.props; - const title = sliceName || t('%s - untitled', tableName); - - return title; - } - postChartFormData() { this.props.actions.postChartFormData( this.props.form_data, @@ -221,22 +219,45 @@ export class ExploreChartHeader extends React.PureComponent { } render() { - const { user, form_data: formData, slice } = this.props; + const { + actions, + chart, + user, + formData, + slice, + canOverwrite, + canDownload, + isStarred, + sliceUpdated, + sliceName, + } = this.props; const { chartStatus, chartUpdateEndTime, chartUpdateStartTime, latestQueryFormData, queriesResponse, - } = this.props.chart; + sliceFormData, + } = chart; // TODO: when will get appropriate design for multi queries use all results and not first only const queryResponse = queriesResponse?.[0]; + const oldSliceName = slice?.slice_name; const chartFinished = ['failed', 'rendered', 'success'].includes( - this.props.chart.chartStatus, + chartStatus, ); return ( - +
+ {slice?.certified_by && ( <> {' '} )} - - - {this.props.slice && ( + {slice && ( {user.userId && ( )} @@ -272,15 +281,15 @@ export class ExploreChartHeader extends React.PureComponent { )} - {this.props.chart.sliceFormData && ( + {sliceFormData && ( )} @@ -306,10 +315,10 @@ export class ExploreChartHeader extends React.PureComponent { status={CHART_STATUS_MAP[chartStatus]} /> diff --git a/superset-frontend/src/explore/components/ExploreChartPanel.jsx b/superset-frontend/src/explore/components/ExploreChartPanel.jsx index 2067d853c7fdf..61d03101bf701 100644 --- a/superset-frontend/src/explore/components/ExploreChartPanel.jsx +++ b/superset-frontend/src/explore/components/ExploreChartPanel.jsx @@ -28,7 +28,6 @@ import { setItem, LocalStorageKeys, } from 'src/utils/localStorageHelpers'; -import ConnectedExploreChartHeader from './ExploreChartHeader'; import { DataTablesPane } from './DataTablesPane'; import { buildV1ChartDataPayload } from '../exploreUtils'; @@ -63,7 +62,6 @@ const GUTTER_SIZE_FACTOR = 1.25; const CHART_PANEL_PADDING_HORIZ = 30; const CHART_PANEL_PADDING_VERTICAL = 15; -const HEADER_PADDING = 15; const INITIAL_SIZES = [90, 10]; const MIN_SIZES = [300, 50]; @@ -78,8 +76,8 @@ const Styles = styled.div` box-shadow: none; height: 100%; - & > div:last-of-type { - flex-basis: 100%; + & > div { + height: 100%; } .gutter { @@ -114,10 +112,6 @@ const ExploreChartPanel = props => { const theme = useTheme(); const gutterMargin = theme.gridUnit * GUTTER_SIZE_FACTOR; const gutterHeight = theme.gridUnit * GUTTER_SIZE_FACTOR; - const { height: hHeight, ref: headerRef } = useResizeDetector({ - refreshMode: 'debounce', - refreshRate: 300, - }); const { width: chartPanelWidth, ref: chartPanelRef } = useResizeDetector({ refreshMode: 'debounce', refreshRate: 300, @@ -156,21 +150,10 @@ const ExploreChartPanel = props => { }, [updateQueryContext]); const calcSectionHeight = useCallback( - percent => { - let headerHeight; - if (props.standalone) { - headerHeight = 0; - } else if (hHeight) { - headerHeight = hHeight + HEADER_PADDING; - } else { - headerHeight = 50; - } - const containerHeight = parseInt(props.height, 10) - headerHeight; - return ( - (containerHeight * percent) / 100 - (gutterHeight / 2 + gutterMargin) - ); - }, - [gutterHeight, gutterMargin, props.height, props.standalone, hHeight], + percent => + (parseInt(props.height, 10) * percent) / 100 - + (gutterHeight / 2 + gutterMargin), + [gutterHeight, gutterMargin, props.height, props.standalone], ); const [tableSectionHeight, setTableSectionHeight] = useState( @@ -283,34 +266,12 @@ const ExploreChartPanel = props => { return standaloneChartBody; } - const header = ( - - ); - const elementStyle = (dimension, elementSize, gutterSize) => ({ [dimension]: `calc(${elementSize}% - ${gutterSize + gutterMargin}px)`, }); return ( -
- {header} -
{props.vizType === 'filter_box' ? ( panelBody ) : ( diff --git a/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx b/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx index 527392275c51c..57437ea99f889 100644 --- a/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx +++ b/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx @@ -60,6 +60,7 @@ import { LOG_ACTIONS_MOUNT_EXPLORER, LOG_ACTIONS_CHANGE_EXPLORE_CONTROLS, } from '../../../logger/LogUtils'; +import ConnectedExploreChartHeader from '../ExploreChartHeader'; const propTypes = { ...ExploreChartPanel.propTypes, @@ -82,69 +83,96 @@ const propTypes = { vizType: PropTypes.string, }; -const Styles = styled.div` - background: ${({ theme }) => theme.colors.grayscale.light5}; - text-align: left; - position: relative; - width: 100%; - max-height: 100%; +const ExploreContainer = styled.div` display: flex; - flex-direction: row; - flex-wrap: nowrap; - flex-basis: 100vh; - align-items: stretch; - border-top: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; - .explore-column { - display: flex; - flex-direction: column; - padding: ${({ theme }) => 2 * theme.gridUnit}px 0; - max-height: 100%; - } - .data-source-selection { - background-color: ${({ theme }) => theme.colors.grayscale.light5}; - padding: ${({ theme }) => 2 * theme.gridUnit}px 0; - border-right: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; - } - .main-explore-content { - flex: 1; - min-width: ${({ theme }) => theme.gridUnit * 128}px; - border-left: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; - .panel { - margin-bottom: 0; + flex-direction: column; + height: 100%; +`; + +const ExploreHeaderContainer = styled.div` + ${({ theme }) => css` + background-color: ${theme.colors.grayscale.light5}; + height: ${theme.gridUnit * 16}px; + padding: 0 ${theme.gridUnit * 4}px; + + .editable-title { + overflow: hidden; + + & > input[type='button'], + & > span { + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; + white-space: nowrap; + } } - } - .controls-column { - align-self: flex-start; - padding: 0; - } - .title-container { + `} +`; + +const ExplorePanelContainer = styled.div` + ${({ theme }) => css` + background: ${theme.colors.grayscale.light5}; + text-align: left; position: relative; + width: 100%; + max-height: 100%; + min-height: 0; display: flex; - flex-direction: row; - padding: 0 ${({ theme }) => 2 * theme.gridUnit}px; - justify-content: space-between; - .horizontal-text { - text-transform: uppercase; - color: ${({ theme }) => theme.colors.grayscale.light1}; - font-size: ${({ theme }) => 4 * theme.typography.sizes.s}; + flex: 1; + flex-wrap: nowrap; + border-top: 1px solid ${theme.colors.grayscale.light2}; + .explore-column { + display: flex; + flex-direction: column; + padding: ${theme.gridUnit * 2}px 0; + max-height: 100%; } - } - .no-show { - display: none; - } - .vertical-text { - writing-mode: vertical-rl; - text-orientation: mixed; - } - .sidebar { - height: 100%; - background-color: ${({ theme }) => theme.colors.grayscale.light4}; - padding: ${({ theme }) => 2 * theme.gridUnit}px; - width: ${({ theme }) => 8 * theme.gridUnit}px; - } - .callpase-icon > svg { - color: ${({ theme }) => theme.colors.primary.base}; - } + .data-source-selection { + background-color: ${theme.colors.grayscale.light5}; + padding: ${theme.gridUnit * 2}px 0; + border-right: 1px solid ${theme.colors.grayscale.light2}; + } + .main-explore-content { + flex: 1; + min-width: ${theme.gridUnit * 128}px; + border-left: 1px solid ${theme.colors.grayscale.light2}; + .panel { + margin-bottom: 0; + } + } + .controls-column { + align-self: flex-start; + padding: 0; + } + .title-container { + position: relative; + display: flex; + flex-direction: row; + padding: 0 ${theme.gridUnit * 4}px; + justify-content: space-between; + .horizontal-text { + text-transform: uppercase; + color: ${theme.colors.grayscale.light1}; + font-size: ${theme.typography.sizes.s * 4}; + } + } + .no-show { + display: none; + } + .vertical-text { + writing-mode: vertical-rl; + text-orientation: mixed; + } + .sidebar { + height: 100%; + background-color: ${theme.colors.grayscale.light4}; + padding: ${theme.gridUnit * 2}px; + width: ${theme.gridUnit * 8}px; + } + .callpase-icon > svg { + color: ${theme.colors.primary.base}; + } + `}; `; const getWindowSize = () => ({ @@ -230,7 +258,7 @@ function ExploreViewContainer(props) { const theme = useTheme(); const width = `${windowSize.width}px`; - const navHeight = props.standalone ? 0 : 90; + const navHeight = props.standalone ? 0 : 120; const height = props.forcedHeight ? `${props.forcedHeight}px` : `${windowSize.height - navHeight}px`; @@ -515,144 +543,164 @@ function ExploreViewContainer(props) { } return ( - - - {showingModal && ( - + + - )} - { - setShouldForceUpdate(d?.width); - setSidebarWidths(LocalStorageKeys.datasource_width, d); - }} - defaultSize={{ - width: getSidebarWidths(LocalStorageKeys.datasource_width), - height: '100%', - }} - minWidth={defaultSidebarsWidth[LocalStorageKeys.datasource_width]} - maxWidth="33%" - enable={{ right: true }} - className={ - isCollapsed ? 'no-show' : 'explore-column data-source-selection' - } - > -
- {t('Dataset')} - - - -
- + + -
- {isCollapsed ? ( -
+ )} + { + setShouldForceUpdate(d?.width); + setSidebarWidths(LocalStorageKeys.datasource_width, d); + }} + defaultSize={{ + width: getSidebarWidths(LocalStorageKeys.datasource_width), + height: '100%', + }} + minWidth={defaultSidebarsWidth[LocalStorageKeys.datasource_width]} + maxWidth="33%" + enable={{ right: true }} + className={ + isCollapsed ? 'no-show' : 'explore-column data-source-selection' + } > - - - + {t('Dataset')} + + - - - +
+ + + {isCollapsed ? ( +
+ + + + + + +
+ ) : null} + + setSidebarWidths(LocalStorageKeys.controls_width, d) + } + defaultSize={{ + width: getSidebarWidths(LocalStorageKeys.controls_width), + height: '100%', + }} + minWidth={defaultSidebarsWidth[LocalStorageKeys.controls_width]} + maxWidth="33%" + enable={{ right: true }} + className="col-sm-3 explore-column controls-column" + > + + + +
+ {renderChartContainer()}
- ) : null} - - setSidebarWidths(LocalStorageKeys.controls_width, d) - } - defaultSize={{ - width: getSidebarWidths(LocalStorageKeys.controls_width), - height: '100%', - }} - minWidth={defaultSidebarsWidth[LocalStorageKeys.controls_width]} - maxWidth="33%" - enable={{ right: true }} - className="col-sm-3 explore-column controls-column" - > - - - -
- {renderChartContainer()} -
-
+ + ); } diff --git a/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx b/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx index f7cda8b752504..b011901eb0306 100644 --- a/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx +++ b/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx @@ -59,7 +59,8 @@ const Styles = styled.div` justify-content: space-between; align-items: center; border-bottom: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; - padding: ${({ theme }) => 2 * theme.gridUnit}px; + padding: ${({ theme }) => 4 * theme.gridUnit}px; + padding-right: ${({ theme }) => 2 * theme.gridUnit}px; } .error-alert { margin: ${({ theme }) => 2 * theme.gridUnit}px; From cccec9a6ab8eadea2ecaac6ee2094c8eb7d6b1f4 Mon Sep 17 00:00:00 2001 From: Roman Pridybailo Date: Tue, 5 Apr 2022 18:46:12 +0300 Subject: [PATCH 28/31] fix: Correct Ukraine map (#19528) * Crimea * Update ukraine.geojson Co-authored-by: Roman Pridybailo --- .../src/countries/ukraine.geojson | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/superset-frontend/plugins/legacy-plugin-chart-country-map/src/countries/ukraine.geojson b/superset-frontend/plugins/legacy-plugin-chart-country-map/src/countries/ukraine.geojson index a4b3c47b99a2c..6d2c9151227c7 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-country-map/src/countries/ukraine.geojson +++ b/superset-frontend/plugins/legacy-plugin-chart-country-map/src/countries/ukraine.geojson @@ -26,6 +26,7 @@ { "type": "Feature", "properties": { "ISO": "UA-12", "NAME_1": "Dnipropetrovs'k" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 34.983942499047089, 49.163717760166492 ], [ 35.169822625413815, 49.147232978419197 ], [ 35.361335483645632, 49.022020982382401 ], [ 35.462466262208238, 48.974892076208334 ], [ 35.579203321576017, 48.975822252195144 ], [ 35.794383986132061, 48.934222724199344 ], [ 35.930654737626014, 48.976390692976054 ], [ 35.952513868819949, 48.968690904461653 ], [ 36.022018670125533, 48.900116279142935 ], [ 35.961970655621258, 48.854899399987289 ], [ 36.072144809835095, 48.829112861069632 ], [ 36.163715447909567, 48.767256171435122 ], [ 36.170691766012169, 48.757747707790429 ], [ 36.132812941064344, 48.723692939577461 ], [ 36.285671827992473, 48.634111842887194 ], [ 36.267533399846513, 48.59780914997225 ], [ 36.307582635130473, 48.569903875763202 ], [ 36.27404463085503, 48.541042588944322 ], [ 36.284276564012202, 48.528252671823338 ], [ 36.374761997368239, 48.541636868146895 ], [ 36.364530064211067, 48.558457547577746 ], [ 36.382358433095192, 48.580549220969772 ], [ 36.477184686224291, 48.646875922485833 ], [ 36.583431430915766, 48.602666734582101 ], [ 36.725438267062373, 48.625921128856987 ], [ 36.738305697649821, 48.601581528964346 ], [ 36.787191603010058, 48.603700263356416 ], [ 36.749312778062233, 48.563961087334292 ], [ 36.751173130035909, 48.529234523754269 ], [ 36.795098097099469, 48.53481557877592 ], [ 36.81917931457366, 48.578249620323675 ], [ 36.883103061756856, 48.57930898751971 ], [ 36.891009555846324, 48.533988756475878 ], [ 36.854215935616935, 48.51652212189839 ], [ 36.858556756289261, 48.492673448420874 ], [ 36.835457390745944, 48.476343696304525 ], [ 36.847239618413653, 48.433969021053429 ], [ 36.808947381416488, 48.413143419533185 ], [ 36.81421837717636, 48.319014798394221 ], [ 36.901086460271927, 48.300307929467351 ], [ 36.913798862127749, 48.263824368499797 ], [ 36.927183057552043, 48.201502590871826 ], [ 36.869770542276683, 48.161039944437846 ], [ 36.892559848558165, 48.067557278445634 ], [ 36.829411248630208, 48.032107245353075 ], [ 36.665080194195582, 48.088072822502738 ], [ 36.589064161881538, 48.078926093164625 ], [ 36.57505984883295, 48.058229681954231 ], [ 36.57661014244411, 47.970715643711969 ], [ 36.61340376177418, 47.95702138902584 ], [ 36.63402265771947, 47.924258531106318 ], [ 36.534442172768024, 47.905034898241922 ], [ 36.53304690878781, 47.883640855041392 ], [ 36.569478793811243, 47.867362778869108 ], [ 36.576145053551386, 47.844211738281047 ], [ 36.39197024862807, 47.825711574929187 ], [ 36.361222772313056, 47.839870916709401 ], [ 36.285981887254252, 47.813309231435881 ], [ 36.214926792337565, 47.830310777120644 ], [ 36.203454623931748, 47.862763577577027 ], [ 36.15224328040307, 47.837907212847597 ], [ 36.078656039944292, 47.845167751790314 ], [ 36.060827671060167, 47.87377065709012 ], [ 36.098396436746157, 47.873357245040779 ], [ 36.117620069610496, 47.938676256204246 ], [ 36.059122348717381, 47.951130276540994 ], [ 36.053076205702325, 47.993039862899309 ], [ 36.021295199713677, 48.004227810465068 ], [ 36.044704623619452, 48.043295192918777 ], [ 35.974579705588894, 48.028644924723494 ], [ 35.959180128560035, 48.090708319933015 ], [ 35.808853387174054, 48.066833808033778 ], [ 35.674132928391884, 48.126985175325558 ], [ 35.514762811354615, 48.079391181158087 ], [ 35.254571974117312, 48.129672350498538 ], [ 35.164551628754623, 48.119130357180211 ], [ 35.118146192992413, 48.129052231974924 ], [ 35.017428827378467, 48.116649889380881 ], [ 34.943118117407153, 48.080993149813992 ], [ 34.886015658695669, 48.080889797925806 ], [ 34.869892612154331, 48.119337062755221 ], [ 34.851444125645855, 48.122747708340057 ], [ 34.835889519885427, 48.108588364761204 ], [ 34.840230339658433, 48.041305649735932 ], [ 34.813410271966461, 48.015519110818275 ], [ 34.842245721262998, 48.006553250432148 ], [ 34.857025180667506, 47.978389593804707 ], [ 34.90839155472645, 47.966684882301479 ], [ 34.916298048815861, 47.947151191074568 ], [ 34.819766473344032, 47.906740221484029 ], [ 34.808139276206589, 47.877543036082216 ], [ 34.901880323717933, 47.83082754195749 ], [ 34.886325717957504, 47.760134182246702 ], [ 34.945288526844024, 47.742770901355982 ], [ 34.895317416765351, 47.597250067736411 ], [ 34.929630568296091, 47.563298652310948 ], [ 34.898262974356783, 47.523249417026932 ], [ 34.594612258049267, 47.568621324014885 ], [ 34.445939162162574, 47.522887681821032 ], [ 34.157481317308907, 47.47389842457261 ], [ 33.901786330374819, 47.514464422894775 ], [ 33.654411248680162, 47.486817532003499 ], [ 33.624335565034301, 47.511622218990169 ], [ 33.584441359381231, 47.51472280531317 ], [ 33.575294630942437, 47.537589626859756 ], [ 33.588782180053556, 47.560043036357001 ], [ 33.562892287449074, 47.575365099020075 ], [ 33.494265985286972, 47.564151313032653 ], [ 33.485274285579806, 47.532732042249904 ], [ 33.365953402927175, 47.51436107010727 ], [ 33.339443393597719, 47.49508576129881 ], [ 33.283426141403311, 47.539320786724886 ], [ 32.994193150193723, 47.595441393505496 ], [ 33.02070315862386, 47.642596137201963 ], [ 32.983754509662845, 47.710008043436403 ], [ 33.086952345774137, 47.731712144999449 ], [ 33.060287305914414, 47.775585436118945 ], [ 33.0827665547327, 47.89593984574725 ], [ 33.031710239036272, 47.919736843280646 ], [ 33.057496778853249, 48.025802720818888 ], [ 33.134287956623893, 48.076962389302821 ], [ 33.2222929221806, 48.090449938413883 ], [ 33.241981642139081, 48.173907375924614 ], [ 33.268801710730372, 48.102723089798758 ], [ 33.348125034043051, 48.185947984212078 ], [ 33.40290205278751, 48.175250963061444 ], [ 33.470546502119362, 48.227986761779619 ], [ 33.513386265363863, 48.238502915776905 ], [ 33.525943637588796, 48.297414048719361 ], [ 33.500002069040249, 48.311211656192938 ], [ 33.488529900634376, 48.403945014251008 ], [ 33.471476678106171, 48.422961941540336 ], [ 33.484499139223885, 48.443916734269862 ], [ 33.461554803311515, 48.497763577027456 ], [ 33.443261346433985, 48.504274807136596 ], [ 33.4454317558708, 48.525307115131227 ], [ 33.580410597071364, 48.559413561086956 ], [ 33.821067743081869, 48.668399155996212 ], [ 33.769701369022926, 48.680543117970501 ], [ 33.756523879173642, 48.697699693286211 ], [ 33.598549025217324, 48.721832586704465 ], [ 33.557776320420828, 48.762295234037822 ], [ 33.572194044619437, 48.787074083502148 ], [ 33.619374626737624, 48.784231878698222 ], [ 33.664798210568961, 48.807615465081597 ], [ 33.70887820816273, 48.782500718833091 ], [ 33.760399610953243, 48.807098701144128 ], [ 33.774817336051171, 48.77898672136007 ], [ 33.801844110217473, 48.796143297575099 ], [ 33.810990837756947, 48.761985174775987 ], [ 33.82571862211671, 48.759453030133216 ], [ 33.919459669628054, 48.865803128511573 ], [ 34.040175816260899, 48.807822171555927 ], [ 34.161977165813539, 48.784386909228431 ], [ 34.237476434190057, 48.746275540283932 ], [ 34.293752068802917, 48.778314927791655 ], [ 34.275872023075408, 48.809992580992798 ], [ 34.315921258359367, 48.839603175745935 ], [ 34.308789910625876, 48.867353421223413 ], [ 34.274321730363567, 48.891227932223273 ], [ 34.277732375049027, 48.922285467800066 ], [ 34.308169793001525, 48.970964666685973 ], [ 34.367907749143285, 48.991066800492433 ], [ 34.34449832523751, 49.008533434170602 ], [ 34.402841017398998, 49.050132962166458 ], [ 34.40826704278976, 49.076539618708409 ], [ 34.72649051292683, 49.145992744069872 ], [ 34.784058057833079, 49.174569810948014 ], [ 34.864776646025348, 49.173407090964474 ], [ 34.867412144354944, 49.144907538452117 ], [ 34.891131625723915, 49.148266507193512 ], [ 34.926219923610574, 49.183044744919016 ], [ 34.983942499047089, 49.163717760166492 ] ] ] } }, { "type": "Feature", "properties": { "ISO": "UA-71", "NAME_1": "Cherkasy" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 32.019213902199795, 50.251454982961093 ], [ 32.094609815990168, 50.236933905974979 ], [ 32.140705194289239, 50.182311916861465 ], [ 32.215842725661219, 50.147533678236641 ], [ 32.269689569318132, 50.156835436306324 ], [ 32.271704950023377, 50.131358953952542 ], [ 32.230570510020982, 50.118594876152599 ], [ 32.255065137745873, 50.062836005477266 ], [ 32.329427524560572, 50.051777249120732 ], [ 32.381207309769536, 49.987026678738232 ], [ 32.415675490031845, 49.96749298841064 ], [ 32.407148879217402, 49.893440659958458 ], [ 32.368649936645227, 49.887497869730851 ], [ 32.372680698055774, 49.86362335873099 ], [ 32.431023390217263, 49.805177313781996 ], [ 32.468902215165087, 49.806572577762211 ], [ 32.503525425058285, 49.78001089069005 ], [ 32.562488233944805, 49.766600856844036 ], [ 32.571479932752652, 49.705881048771346 ], [ 32.692971225741474, 49.671645413405088 ], [ 32.710799594625598, 49.619968980983685 ], [ 32.752089064258882, 49.593820705960809 ], [ 32.754414504225963, 49.547156886880828 ], [ 32.675918003213269, 49.477135322537094 ], [ 32.747593214854987, 49.406131904463791 ], [ 32.789347771582356, 49.333888251141843 ], [ 32.788727654857382, 49.261799628350161 ], [ 32.752554152252287, 49.181287747531485 ], [ 32.789502801213303, 49.146767890425792 ], [ 32.800354851994825, 49.07599701634922 ], [ 32.827174919686797, 49.027705390191556 ], [ 32.853891636389903, 49.021375027235706 ], [ 32.850791050066903, 49.005820421475221 ], [ 32.775240105746263, 48.961714586359051 ], [ 32.689405552324388, 48.987449449332587 ], [ 32.634318475217412, 48.946004950967676 ], [ 32.567759229704677, 48.957942206467635 ], [ 32.547088656915946, 48.929597682686904 ], [ 32.526934848964061, 48.927711494090147 ], [ 32.456189813309209, 49.018429470543595 ], [ 32.452934198254638, 49.056127428338129 ], [ 32.369115024638631, 49.045792141494132 ], [ 32.331907993259279, 49.089768785401077 ], [ 32.288758171652262, 49.070028387699892 ], [ 32.284572380610882, 49.047962550930947 ], [ 32.241422559903185, 49.052044990084198 ], [ 32.192536655442268, 48.999774278460166 ], [ 32.142565545363595, 48.975615545720814 ], [ 32.132953728931398, 48.961146145578141 ], [ 32.143650750082031, 48.935798855332905 ], [ 32.121326531794011, 48.910270697934322 ], [ 32.026190220302396, 48.928047389975063 ], [ 31.886715528798618, 48.85975698549646 ], [ 31.841550327385676, 48.909934801150143 ], [ 31.732306349158648, 48.942645982226281 ], [ 31.683575474328677, 48.943007717432181 ], [ 31.651122673872294, 48.917427883190214 ], [ 31.544100782824842, 48.924197495717806 ], [ 31.529476353051223, 48.904043687765977 ], [ 31.527305942715088, 48.835210680028808 ], [ 31.481210565315337, 48.803481349984281 ], [ 31.462296990813456, 48.761106676531881 ], [ 31.332899203735224, 48.723847968309087 ], [ 31.309128044623549, 48.742813218755032 ], [ 31.223655226407573, 48.753871975111565 ], [ 31.067385694793984, 48.727413641726173 ], [ 30.93814293824596, 48.756223253500309 ], [ 30.820062289942712, 48.749789536857634 ], [ 30.702291700901242, 48.765189113886493 ], [ 30.606845331047168, 48.714520371817684 ], [ 30.599403924051842, 48.659975897969275 ], [ 30.540906203158727, 48.619254869116901 ], [ 30.576097852933572, 48.604320380081447 ], [ 30.567571242119129, 48.561919868207326 ], [ 30.484423862071537, 48.55352244680347 ], [ 30.414712355191, 48.571919257367824 ], [ 30.384946729907654, 48.530526434946978 ], [ 30.247280715332749, 48.47755809223213 ], [ 30.193640578150166, 48.493474433198514 ], [ 30.108426140553945, 48.45332184512705 ], [ 30.036905959442436, 48.473785712340714 ], [ 29.957892693592896, 48.459316311298721 ], [ 30.021661411145203, 48.533627021269979 ], [ 29.963628778245493, 48.609849758259713 ], [ 29.958202751955412, 48.655325018934434 ], [ 29.859087354997428, 48.698319810910505 ], [ 29.857537062285587, 48.71769847340579 ], [ 29.884047071615043, 48.743278306748437 ], [ 29.846323276298165, 48.764827379579856 ], [ 29.772477655219575, 48.769297390562087 ], [ 29.745295851422384, 48.848543199509038 ], [ 29.703592970639079, 48.867482612432582 ], [ 29.722351514610693, 48.89696401597655 ], [ 29.716150343763331, 48.914378973710654 ], [ 29.635948521307171, 48.947038478842671 ], [ 29.672690463793856, 48.983522039810282 ], [ 29.67083011182018, 49.00140208643711 ], [ 29.593108758062726, 49.031477770082972 ], [ 29.6095418638659, 49.062121894509801 ], [ 29.670675083088611, 49.085169583209677 ], [ 29.67486087412999, 49.114625149231244 ], [ 29.707158644056165, 49.131678371759449 ], [ 29.710569288741681, 49.182760524978221 ], [ 29.674550815767532, 49.209709784778738 ], [ 29.697236769261451, 49.227279772143731 ], [ 29.745295851422384, 49.213378810983329 ], [ 29.729482863243504, 49.195214545314968 ], [ 29.761470574807163, 49.174621486892079 ], [ 29.862342970951318, 49.173846341435478 ], [ 29.920065544589193, 49.246245021690356 ], [ 30.038611280885902, 49.285105699468431 ], [ 30.111526726876946, 49.274977119098764 ], [ 30.104395379143398, 49.30128042285321 ], [ 30.15188602142274, 49.329547431368837 ], [ 30.182891880156149, 49.3214859085478 ], [ 30.169249302313403, 49.284123847537501 ], [ 30.349548373658479, 49.258053086880409 ], [ 30.382001174114862, 49.232964179053567 ], [ 30.419879999062687, 49.335955308690529 ], [ 30.437708367946811, 49.348512681814782 ], [ 30.498221470444491, 49.323863023559682 ], [ 30.550207961228409, 49.340657864568755 ], [ 30.592737665211075, 49.329935004097138 ], [ 30.619402704171421, 49.35853791029632 ], [ 30.684566684804622, 49.353835354418038 ], [ 30.725391066444558, 49.320788276557664 ], [ 30.868173048947028, 49.36063080536735 ], [ 30.908015577756714, 49.346135565903637 ], [ 30.950390252108434, 49.414296780072334 ], [ 31.155855747444775, 49.556381131483988 ], [ 31.156785923431585, 49.585190742358861 ], [ 31.129759149265283, 49.595784409822613 ], [ 31.128208855654123, 49.614439601906099 ], [ 31.174614292315709, 49.671748766192593 ], [ 31.169808383649979, 49.695519925304268 ], [ 31.196938510603786, 49.753449205416473 ], [ 31.182055698411716, 49.773809718943369 ], [ 31.198850539420846, 49.804350491481955 ], [ 31.178645053726257, 49.818251450843775 ], [ 31.21202802927013, 49.853959866354728 ], [ 31.335999790058224, 49.896747951856469 ], [ 31.430205925562973, 49.906101385870215 ], [ 31.496920199807334, 49.840033066772605 ], [ 31.5193994477263, 49.866465563534916 ], [ 31.570920851416133, 49.862693182744181 ], [ 31.596552361602164, 49.897626450999894 ], [ 31.613140497036284, 49.858145657396165 ], [ 31.716648389711395, 49.848533840963967 ], [ 31.806772087861532, 49.96020661104626 ], [ 31.810027703815479, 50.00563019487754 ], [ 31.871987746237494, 50.023458563761665 ], [ 31.849611851106033, 50.094978745772494 ], [ 31.91431074374583, 50.147275295818247 ], [ 31.954359979029789, 50.147533678236641 ], [ 31.987536248998651, 50.169289455743751 ], [ 31.995339390300614, 50.186497707902845 ], [ 31.975133904605968, 50.224066474488154 ], [ 32.019213902199795, 50.251454982961093 ] ] ] } }, { "type": "Feature", "properties": { "ISO": "UA-35", "NAME_1": "Kirovohrad" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 32.788727654857382, 49.261799628350161 ], [ 33.038221470044789, 49.18195954020058 ], [ 33.149325799346116, 49.125632228744337 ], [ 33.204102818090576, 49.070906886843318 ], [ 33.297068719245999, 49.064860744727582 ], [ 33.34161380393391, 49.044035143207282 ], [ 33.345954623706916, 49.032666328488233 ], [ 33.31923790880245, 49.025586655799486 ], [ 33.29939415921308, 48.995459296209503 ], [ 33.378820835313263, 48.939958807952621 ], [ 33.425071242343961, 48.950914212420969 ], [ 33.548422886407081, 48.901873277429786 ], [ 33.596998731606163, 48.922388821486891 ], [ 33.651620720719677, 48.899289455943631 ], [ 33.668880649722212, 48.869058743566143 ], [ 33.694615512695748, 48.905800686052771 ], [ 33.778486362255819, 48.92254385021846 ], [ 33.807425165239181, 48.903449409462667 ], [ 33.892019484311732, 48.891744697060119 ], [ 33.919459669628054, 48.865803128511573 ], [ 33.82571862211671, 48.759453030133216 ], [ 33.810990837756947, 48.761985174775987 ], [ 33.801844110217473, 48.796143297575099 ], [ 33.774817336051171, 48.77898672136007 ], [ 33.773732131332793, 48.801414293334972 ], [ 33.754043409575672, 48.807563788238213 ], [ 33.70887820816273, 48.782500718833091 ], [ 33.670844353584016, 48.807408759506643 ], [ 33.619374626737624, 48.784231878698222 ], [ 33.584441359381231, 48.794489651176434 ], [ 33.557569613946498, 48.771984564835748 ], [ 33.598549025217324, 48.721832586704465 ], [ 33.756523879173642, 48.697699693286211 ], [ 33.769701369022926, 48.680543117970501 ], [ 33.821067743081869, 48.668399155996212 ], [ 33.580410597071364, 48.559413561086956 ], [ 33.4454317558708, 48.525307115131227 ], [ 33.443261346433985, 48.504274807136596 ], [ 33.461554803311515, 48.497763577027456 ], [ 33.484499139223885, 48.443916734269862 ], [ 33.471476678106171, 48.422961941540336 ], [ 33.488529900634376, 48.403945014251008 ], [ 33.500002069040249, 48.311211656192938 ], [ 33.525943637588796, 48.297414048719361 ], [ 33.513386265363863, 48.238502915776905 ], [ 33.470546502119362, 48.227986761779619 ], [ 33.40290205278751, 48.175250963061444 ], [ 33.348125034043051, 48.185947984212078 ], [ 33.268801710730372, 48.102723089798758 ], [ 33.241981642139081, 48.173907375924614 ], [ 33.2222929221806, 48.090449938413883 ], [ 33.111291944767402, 48.064198309704182 ], [ 33.057496778853249, 48.025802720818888 ], [ 33.035275913352677, 47.966529853569853 ], [ 32.975692986841864, 48.044509588846438 ], [ 32.888204787021266, 48.032158922196459 ], [ 32.882778761630505, 47.993970037986855 ], [ 32.844434848689275, 47.986218574427653 ], [ 32.829345330922251, 47.966684882301479 ], [ 32.777565544814024, 47.989319159851334 ], [ 32.696071812064474, 47.970302232561949 ], [ 32.681964146228324, 47.943223782451582 ], [ 32.714727004147903, 47.929736233340464 ], [ 32.715760532022898, 47.91521515635435 ], [ 32.662740513363985, 47.900254827997912 ], [ 32.68227420369152, 47.845865382881072 ], [ 32.651991815369968, 47.83733877206663 ], [ 32.644550409273961, 47.800648505524066 ], [ 32.467041864090731, 47.781088975875491 ], [ 32.342915072772371, 47.796178494541891 ], [ 32.287517938202257, 47.822455959874617 ], [ 32.234446241800583, 47.820440579169372 ], [ 32.169127232435812, 47.757705390391436 ], [ 32.138689812684675, 47.747525133178328 ], [ 32.021849399630071, 47.809846909906923 ], [ 31.862324252961912, 47.795842596858336 ], [ 31.839069858687026, 47.845968736567897 ], [ 31.863409457680291, 47.89438955303541 ], [ 31.821241488903581, 47.945187486313444 ], [ 31.768169793401228, 47.952577216465329 ], [ 31.708070102952888, 48.019756577803776 ], [ 31.769254999018983, 48.047041734388529 ], [ 31.758041213031561, 48.09541087491192 ], [ 31.69861331615158, 48.093757229412574 ], [ 31.61655114082248, 48.118742784451911 ], [ 31.591281365842349, 48.109570216692134 ], [ 31.584873487621337, 48.086212470529063 ], [ 31.533662144092659, 48.086780911309972 ], [ 31.511803012898724, 48.055051581265445 ], [ 31.499710727767877, 48.063888251341723 ], [ 31.503741489178367, 48.118096829305216 ], [ 31.414496291070975, 48.107709866517098 ], [ 31.384265577794167, 48.124943956198592 ], [ 31.343906284147693, 48.111792303871709 ], [ 31.250320265367918, 48.120939033209766 ], [ 31.231096633402899, 48.131636054360399 ], [ 31.228151075811468, 48.158895372523375 ], [ 31.189807162870238, 48.171478583170028 ], [ 31.185466343097232, 48.205068264288968 ], [ 31.168568150199974, 48.211682848084934 ], [ 31.041805860552017, 48.21912425418094 ], [ 30.906155225783039, 48.157112534915541 ], [ 30.857114291691175, 48.186258043473913 ], [ 30.816961703619711, 48.159076240576042 ], [ 30.789004754365919, 48.177783107704215 ], [ 30.704772169599948, 48.181581326017351 ], [ 30.619867791265563, 48.145046088206357 ], [ 30.501787143861577, 48.163572089080617 ], [ 30.347532992953234, 48.163572089080617 ], [ 30.322314893917223, 48.134607449474174 ], [ 30.083466423936272, 48.143728339491247 ], [ 30.04780968616808, 48.155200506997801 ], [ 29.996753371370971, 48.220984605255296 ], [ 29.959442987204056, 48.209589952114584 ], [ 29.924096306899003, 48.221553046036206 ], [ 29.882186721440007, 48.18000519488379 ], [ 29.825859409084444, 48.210106716052053 ], [ 29.774803094287336, 48.203104560427107 ], [ 29.786430291424779, 48.250801907382083 ], [ 29.738939650044813, 48.267079983554368 ], [ 29.775423211012367, 48.316896064002151 ], [ 29.779298943691288, 48.35309540502891 ], [ 29.883426954890012, 48.432599596394255 ], [ 29.916964959165512, 48.428310452565313 ], [ 29.940064324708771, 48.375600491369539 ], [ 29.998096957608482, 48.42205760397519 ], [ 30.038766309617472, 48.427690334940962 ], [ 30.076128370627771, 48.455078844313164 ], [ 30.120828484946628, 48.45639659302833 ], [ 30.184752232129767, 48.492492581267584 ], [ 30.247280715332749, 48.47755809223213 ], [ 30.384946729907654, 48.530526434946978 ], [ 30.408821241806834, 48.570213935025038 ], [ 30.456466912817746, 48.570782375805948 ], [ 30.484423862071537, 48.55352244680347 ], [ 30.57408247222827, 48.565149644840233 ], [ 30.576097852933572, 48.604320380081447 ], [ 30.540906203158727, 48.619254869116901 ], [ 30.599403924051842, 48.659975897969275 ], [ 30.606845331047168, 48.714520371817684 ], [ 30.702291700901242, 48.765189113886493 ], [ 30.820062289942712, 48.749789536857634 ], [ 30.93814293824596, 48.756223253500309 ], [ 31.067385694793984, 48.727413641726173 ], [ 31.223655226407573, 48.753871975111565 ], [ 31.309128044623549, 48.742813218755032 ], [ 31.332899203735224, 48.723847968309087 ], [ 31.462296990813456, 48.761106676531881 ], [ 31.481210565315337, 48.803481349984281 ], [ 31.527305942715088, 48.835210680028808 ], [ 31.529476353051223, 48.904043687765977 ], [ 31.544100782824842, 48.924197495717806 ], [ 31.651122673872294, 48.917427883190214 ], [ 31.683575474328677, 48.943007717432181 ], [ 31.732306349158648, 48.942645982226281 ], [ 31.841550327385676, 48.909934801150143 ], [ 31.886715528798618, 48.85975698549646 ], [ 32.026190220302396, 48.928047389975063 ], [ 32.129233025883423, 48.912518621736979 ], [ 32.142565545363595, 48.975615545720814 ], [ 32.192536655442268, 48.999774278460166 ], [ 32.241422559903185, 49.052044990084198 ], [ 32.284572380610882, 49.047962550930947 ], [ 32.288758171652262, 49.070028387699892 ], [ 32.331907993259279, 49.089768785401077 ], [ 32.369115024638631, 49.045792141494132 ], [ 32.452934198254638, 49.056127428338129 ], [ 32.454484490966479, 49.02160757123238 ], [ 32.503525425058285, 48.9730834010781 ], [ 32.51980350123057, 48.929106757171098 ], [ 32.547088656915946, 48.929597682686904 ], [ 32.567759229704677, 48.957942206467635 ], [ 32.634318475217412, 48.946004950967676 ], [ 32.689405552324388, 48.987449449332587 ], [ 32.782371454379074, 48.963729967064296 ], [ 32.855441929101744, 49.018403632121874 ], [ 32.82469445188741, 49.030392564465274 ], [ 32.800354851994825, 49.07599701634922 ], [ 32.789502801213303, 49.146767890425792 ], [ 32.752554152252287, 49.181287747531485 ], [ 32.788727654857382, 49.261799628350161 ] ] ] } }, -{ "type": "Feature", "properties": { "ISO": "UA-30", "NAME_1": "Kiev City" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 30.738579569391618, 50.668153050722708 ], [ 30.834853259770114, 50.63879710530216 ], [ 30.842339119354847, 50.583978551369 ], [ 30.77560546292176, 50.544198930136474 ], [ 30.750571941641795, 50.506299492727408 ], [ 30.768079276837113, 50.488703509559286 ], [ 30.760252259434196, 50.43988011157785 ], [ 30.817012763528169, 50.367226506797806 ], [ 30.846045721033192, 50.353492985013247 ], [ 30.830268158049023, 50.332724846574365 ], [ 30.790160822064252, 50.332587875330148 ], [ 30.791965799182265, 50.292555746950143 ], [ 30.753676885180084, 50.2891579715631 ], [ 30.724343095457471, 50.309563514144827 ], [ 30.718108877222903, 50.262483327843256 ], [ 30.605901274923895, 50.267452462561096 ], [ 30.654383518684426, 50.155983494142902 ], [ 30.639759213017271, 50.12409198125988 ], [ 30.620784347019992, 50.122712035432073 ], [ 30.648197621922691, 50.03369265169448 ], [ 30.585565493652609, 50.014154717466226 ], [ 30.568478920665655, 50.091072387580596 ], [ 30.541697786423697, 50.097418012031596 ], [ 30.536180761333071, 50.145639750973714 ], [ 30.511085473715639, 50.146179644574886 ], [ 30.520806118907217, 50.190074235237716 ], [ 30.476911529143763, 50.199794880429351 ], [ 30.445622347093604, 50.263568562239357 ], [ 30.41059423723425, 50.261989207036265 ], [ 30.413718066535523, 50.303914962305612 ], [ 30.332238312847835, 50.368768432217337 ], [ 30.318757298210699, 50.40827410826347 ], [ 30.238241797620844, 50.414672186572147 ], [ 30.223303712196525, 50.382020633245588 ], [ 30.19660342970451, 50.374352670839073 ], [ 30.185993737221168, 50.387245468603453 ], [ 30.20028059567295, 50.478145037963088 ], [ 30.217438779875749, 50.542431728141707 ], [ 30.258627859088392, 50.565877710388008 ], [ 30.249909840166993, 50.600723265965939 ], [ 30.267873699111931, 50.608004352618764 ], [ 30.285561019225611, 50.588952523540115 ], [ 30.291309143102467, 50.609275474183733 ], [ 30.270164967539188, 50.63339306383881 ], [ 30.333473963352731, 50.637918952397683 ], [ 30.334013856054582, 50.663014239115796 ], [ 30.432651793045807, 50.66244745938269 ], [ 30.437266331376065, 50.634241784630603 ], [ 30.462512034202973, 50.630365880181216 ], [ 30.475767426025868, 50.595864218159136 ], [ 30.548079872987728, 50.548982771237888 ], [ 30.538686942548338, 50.571958808252361 ], [ 30.551813308637236, 50.580783641451887 ], [ 30.647182495975414, 50.575179282297995 ], [ 30.664327235313635, 50.602694650937337 ], [ 30.718544131106739, 50.62352455391499 ], [ 30.71559759865022, 50.651805437171163 ], [ 30.738579569391618, 50.668153050722708 ] ] ] } } +{ "type": "Feature", "properties": { "ISO": "UA-30", "NAME_1": "Kyiv City" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 30.738579569391618, 50.668153050722708 ], [ 30.834853259770114, 50.63879710530216 ], [ 30.842339119354847, 50.583978551369 ], [ 30.77560546292176, 50.544198930136474 ], [ 30.750571941641795, 50.506299492727408 ], [ 30.768079276837113, 50.488703509559286 ], [ 30.760252259434196, 50.43988011157785 ], [ 30.817012763528169, 50.367226506797806 ], [ 30.846045721033192, 50.353492985013247 ], [ 30.830268158049023, 50.332724846574365 ], [ 30.790160822064252, 50.332587875330148 ], [ 30.791965799182265, 50.292555746950143 ], [ 30.753676885180084, 50.2891579715631 ], [ 30.724343095457471, 50.309563514144827 ], [ 30.718108877222903, 50.262483327843256 ], [ 30.605901274923895, 50.267452462561096 ], [ 30.654383518684426, 50.155983494142902 ], [ 30.639759213017271, 50.12409198125988 ], [ 30.620784347019992, 50.122712035432073 ], [ 30.648197621922691, 50.03369265169448 ], [ 30.585565493652609, 50.014154717466226 ], [ 30.568478920665655, 50.091072387580596 ], [ 30.541697786423697, 50.097418012031596 ], [ 30.536180761333071, 50.145639750973714 ], [ 30.511085473715639, 50.146179644574886 ], [ 30.520806118907217, 50.190074235237716 ], [ 30.476911529143763, 50.199794880429351 ], [ 30.445622347093604, 50.263568562239357 ], [ 30.41059423723425, 50.261989207036265 ], [ 30.413718066535523, 50.303914962305612 ], [ 30.332238312847835, 50.368768432217337 ], [ 30.318757298210699, 50.40827410826347 ], [ 30.238241797620844, 50.414672186572147 ], [ 30.223303712196525, 50.382020633245588 ], [ 30.19660342970451, 50.374352670839073 ], [ 30.185993737221168, 50.387245468603453 ], [ 30.20028059567295, 50.478145037963088 ], [ 30.217438779875749, 50.542431728141707 ], [ 30.258627859088392, 50.565877710388008 ], [ 30.249909840166993, 50.600723265965939 ], [ 30.267873699111931, 50.608004352618764 ], [ 30.285561019225611, 50.588952523540115 ], [ 30.291309143102467, 50.609275474183733 ], [ 30.270164967539188, 50.63339306383881 ], [ 30.333473963352731, 50.637918952397683 ], [ 30.334013856054582, 50.663014239115796 ], [ 30.432651793045807, 50.66244745938269 ], [ 30.437266331376065, 50.634241784630603 ], [ 30.462512034202973, 50.630365880181216 ], [ 30.475767426025868, 50.595864218159136 ], [ 30.548079872987728, 50.548982771237888 ], [ 30.538686942548338, 50.571958808252361 ], [ 30.551813308637236, 50.580783641451887 ], [ 30.647182495975414, 50.575179282297995 ], [ 30.664327235313635, 50.602694650937337 ], [ 30.718544131106739, 50.62352455391499 ], [ 30.71559759865022, 50.651805437171163 ], [ 30.738579569391618, 50.668153050722708 ] ] ] } }, +{ "type": "Feature", "properties": { "ISO": "UA-43", "NAME_1": "Crimea" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 33.745771311300061, 44.402323389538829 ], [ 33.852068870481958, 44.431631863719531 ], [ 33.805710906333218, 44.527306810660775 ], [ 33.712994978035795, 44.581555492092264 ], [ 33.709049619844052, 44.666380702207618 ], [ 33.611401992907645, 44.720629383639107 ], [ 33.67452773116986, 44.791645839184127 ], [ 33.588428605039795, 44.842004786558498 ], [ 33.612207031250023, 44.9078125 ], [ 33.601171875, 44.981494140624996 ], [ 33.55517578125, 45.09765625 ], [ 33.392480468750023, 45.187841796874999 ], [ 33.261523437500017, 45.170751953124999 ], [ 33.186914062500023, 45.194775390624997 ], [ 32.918652343750011, 45.34814453125 ], [ 32.772656250000011, 45.358984375 ], [ 32.611328125, 45.328076171874997 ], [ 32.551855468750006, 45.350390625 ], [ 32.508007812500011, 45.40380859375 ], [ 32.828027343750023, 45.593017578125 ], [ 33.142285156250011, 45.74921875 ], [ 33.280078125000017, 45.765234375 ], [ 33.466210937500023, 45.837939453124996 ], [ 33.664843750000017, 45.947070312499996 ], [ 33.63671875, 46.032861328124994 ], [ 33.594140625000023, 46.096240234374996 ], [ 33.654322809145384, 46.146221891615326 ], [ 33.659965189140848, 46.21957283155632 ], [ 33.806667069022836, 46.208288071565399 ], [ 34.026719888845811, 46.106725231647104 ], [ 34.128282728764113, 46.089798091660718 ], [ 34.22420318868695, 46.10108285165164 ], [ 34.353977928582552, 46.061586191683411 ], [ 34.449898388505389, 45.965665731760573 ], [ 34.523249328446383, 45.976950491751495 ], [ 34.68687834831475, 45.976950491751495 ], [ 34.794083568228508, 45.89231479181958 ], [ 34.799725948223973, 45.790751951901285 ], [ 34.946427828105953, 45.728685771951213 ], [ 35.001674239407095, 45.733383205653169 ], [ 35.022851562500023, 45.700976562499996 ], [ 35.260156250000023, 45.446923828124994 ], [ 35.373925781250023, 45.353613281249999 ], [ 35.45751953125, 45.316308593749994 ], [ 35.558007812500023, 45.310888671874999 ], [ 35.7509765625, 45.389355468749997 ], [ 35.83349609375, 45.401611328125 ], [ 36.012890625000011, 45.371679687499999 ], [ 36.0771484375, 45.424121093749996 ], [ 36.170507812500006, 45.453076171874997 ], [ 36.290332031250017, 45.456738281249997 ], [ 36.427050781250017, 45.433251953124994 ], [ 36.575, 45.3935546875 ], [ 36.514257812500006, 45.303759765624996 ], [ 36.45078125, 45.232324218749994 ], [ 36.428417968750011, 45.153271484374997 ], [ 36.393359375000017, 45.065380859374997 ], [ 36.229882812500023, 45.025976562499999 ], [ 36.054785156250006, 45.030810546874996 ], [ 35.8701171875, 45.005322265624997 ], [ 35.803613281250023, 45.039599609374996 ], [ 35.759472656250011, 45.070849609374996 ], [ 35.677539062500017, 45.102001953124997 ], [ 35.569531250000011, 45.119335937499997 ], [ 35.472558593750023, 45.098486328124999 ], [ 35.357812500000023, 44.978417968749994 ], [ 35.15478515625, 44.896337890624999 ], [ 35.087695312500017, 44.802636718749994 ], [ 34.887792968750006, 44.823583984374999 ], [ 34.716894531250006, 44.80712890625 ], [ 34.469921875000011, 44.7216796875 ], [ 34.28173828125, 44.538427734374999 ], [ 34.074414062500011, 44.423828125 ], [ 33.909960937500017, 44.387597656249994 ], [ 33.755664062500017, 44.39892578125 ], [ 33.745771311300061, 44.402323389538829 ] ] ] } } ] } From 03d3eaacafc6ebdad7fdbcef6efa4df553468ba1 Mon Sep 17 00:00:00 2001 From: Kamil Gabryjelski Date: Tue, 5 Apr 2022 18:03:23 +0200 Subject: [PATCH 29/31] feat(explore): Move timer, row counter and cached pills to chart container (#19458) * feat(explore): Move timer, row counter and cached pills to chart container * Hide pills in standalone mode * Take pills out of chart-container --- .../src/explore/components/ChartPills.tsx | 88 +++++++++++++++++++ .../components/ExploreChartHeader/index.jsx | 49 +---------- .../explore/components/ExploreChartPanel.jsx | 39 +++++++- .../RowCountLabel.stories.tsx | 2 +- .../RowCountLabel.test.jsx | 2 +- .../index.tsx} | 0 6 files changed, 128 insertions(+), 52 deletions(-) create mode 100644 superset-frontend/src/explore/components/ChartPills.tsx rename superset-frontend/src/explore/components/{ => RowCountLabel}/RowCountLabel.stories.tsx (97%) rename superset-frontend/src/explore/components/{ => RowCountLabel}/RowCountLabel.test.jsx (96%) rename superset-frontend/src/explore/components/{RowCountLabel.tsx => RowCountLabel/index.tsx} (100%) diff --git a/superset-frontend/src/explore/components/ChartPills.tsx b/superset-frontend/src/explore/components/ChartPills.tsx new file mode 100644 index 0000000000000..09595576dd753 --- /dev/null +++ b/superset-frontend/src/explore/components/ChartPills.tsx @@ -0,0 +1,88 @@ +/** + * 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 React, { forwardRef, RefObject } from 'react'; +import { css, QueryData, SupersetTheme } from '@superset-ui/core'; +import RowCountLabel from 'src/explore/components/RowCountLabel'; +import CachedLabel from 'src/components/CachedLabel'; +import Timer from 'src/components/Timer'; + +enum CHART_STATUS_MAP { + failed = 'danger', + loading = 'warning', + success = 'success', +} + +export type ChartPillsProps = { + queriesResponse: QueryData[]; + chartStatus: keyof typeof CHART_STATUS_MAP; + chartUpdateStartTime: number; + chartUpdateEndTime: number; + refreshCachedQuery: () => void; + rowLimit: string | number; +}; + +export const ChartPills = forwardRef( + ( + { + queriesResponse, + chartStatus, + chartUpdateStartTime, + chartUpdateEndTime, + refreshCachedQuery, + rowLimit, + }: ChartPillsProps, + ref: RefObject, + ) => { + const isLoading = chartStatus === 'loading'; + const firstQueryResponse = queriesResponse?.[0]; + return ( +
+
css` + display: flex; + justify-content: flex-end; + padding-bottom: ${theme.gridUnit * 4}px; + & .ant-tag:last-of-type { + margin: 0; + } + `} + > + {!isLoading && firstQueryResponse && ( + + )} + {!isLoading && firstQueryResponse?.is_cached && ( + + )} + +
+
+ ); + }, +); diff --git a/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx b/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx index d9d615dc1f33b..6c8d3cb799f4a 100644 --- a/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx +++ b/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx @@ -36,22 +36,12 @@ import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags'; import { chartPropShape } from 'src/dashboard/util/propShapes'; import AlteredSliceTag from 'src/components/AlteredSliceTag'; import FaveStar from 'src/components/FaveStar'; -import Timer from 'src/components/Timer'; -import CachedLabel from 'src/components/CachedLabel'; import PropertiesModal from 'src/explore/components/PropertiesModal'; import { sliceUpdated } from 'src/explore/actions/exploreActions'; import CertifiedBadge from 'src/components/CertifiedBadge'; -import withToasts from 'src/components/MessageToasts/withToasts'; -import RowCountLabel from '../RowCountLabel'; import ExploreAdditionalActionsMenu from '../ExploreAdditionalActionsMenu'; import { ChartEditableTitle } from './ChartEditableTitle'; -const CHART_STATUS_MAP = { - failed: 'danger', - loading: 'warning', - success: 'success', -}; - const propTypes = { actions: PropTypes.object.isRequired, canOverwrite: PropTypes.bool.isRequired, @@ -85,7 +75,7 @@ const StyledHeader = styled.div` display: flex; align-items: center; min-width: 0; - margin-right: ${theme.gridUnit * 6}px; + margin-right: ${theme.gridUnit * 12}px; } .right-button-panel { @@ -231,20 +221,8 @@ export class ExploreChartHeader extends React.PureComponent { sliceUpdated, sliceName, } = this.props; - const { - chartStatus, - chartUpdateEndTime, - chartUpdateStartTime, - latestQueryFormData, - queriesResponse, - sliceFormData, - } = chart; - // TODO: when will get appropriate design for multi queries use all results and not first only - const queryResponse = queriesResponse?.[0]; + const { latestQueryFormData, sliceFormData } = chart; const oldSliceName = slice?.slice_name; - const chartFinished = ['failed', 'rendered', 'success'].includes( - chartStatus, - ); return (
@@ -296,24 +274,6 @@ export class ExploreChartHeader extends React.PureComponent { )}
- {chartFinished && queryResponse && ( - - )} - {chartFinished && queryResponse && queryResponse.is_cached && ( - - )} - { refreshMode: 'debounce', refreshRate: 300, }); + const { height: pillsHeight, ref: pillsRef } = useResizeDetector({ + refreshMode: 'debounce', + refreshRate: 1000, + }); const [splitSizes, setSplitSizes] = useState( getItem(LocalStorageKeys.chart_split_sizes, INITIAL_SIZES), ); @@ -150,10 +155,16 @@ const ExploreChartPanel = props => { }, [updateQueryContext]); const calcSectionHeight = useCallback( - percent => - (parseInt(props.height, 10) * percent) / 100 - - (gutterHeight / 2 + gutterMargin), - [gutterHeight, gutterMargin, props.height, props.standalone], + percent => { + let containerHeight = parseInt(props.height, 10); + if (pillsHeight) { + containerHeight -= pillsHeight; + } + return ( + (containerHeight * percent) / 100 - (gutterHeight / 2 + gutterMargin) + ); + }, + [gutterHeight, gutterMargin, pillsHeight, props.height, props.standalone], ); const [tableSectionHeight, setTableSectionHeight] = useState( @@ -179,6 +190,17 @@ const ExploreChartPanel = props => { setSplitSizes(sizes); }; + const refreshCachedQuery = () => { + props.actions.postChartFormData( + props.form_data, + true, + props.timeout, + props.chart.id, + undefined, + props.ownState, + ); + }; + const onCollapseChange = openPanelName => { let splitSizes; if (!openPanelName) { @@ -229,6 +251,15 @@ const ExploreChartPanel = props => { const panelBody = useMemo( () => (
+ {renderChart()}
), diff --git a/superset-frontend/src/explore/components/RowCountLabel.stories.tsx b/superset-frontend/src/explore/components/RowCountLabel/RowCountLabel.stories.tsx similarity index 97% rename from superset-frontend/src/explore/components/RowCountLabel.stories.tsx rename to superset-frontend/src/explore/components/RowCountLabel/RowCountLabel.stories.tsx index 9030b828d8440..716830f9ca32b 100644 --- a/superset-frontend/src/explore/components/RowCountLabel.stories.tsx +++ b/superset-frontend/src/explore/components/RowCountLabel/RowCountLabel.stories.tsx @@ -17,7 +17,7 @@ * under the License. */ import React from 'react'; -import RowCountLabel from './RowCountLabel'; +import RowCountLabel from '.'; export default { title: 'RowCountLabel', diff --git a/superset-frontend/src/explore/components/RowCountLabel.test.jsx b/superset-frontend/src/explore/components/RowCountLabel/RowCountLabel.test.jsx similarity index 96% rename from superset-frontend/src/explore/components/RowCountLabel.test.jsx rename to superset-frontend/src/explore/components/RowCountLabel/RowCountLabel.test.jsx index 128a0329c298f..452f68a745478 100644 --- a/superset-frontend/src/explore/components/RowCountLabel.test.jsx +++ b/superset-frontend/src/explore/components/RowCountLabel/RowCountLabel.test.jsx @@ -21,7 +21,7 @@ import { shallow } from 'enzyme'; import Label from 'src/components/Label'; import { Tooltip } from 'src/components/Tooltip'; -import RowCountLabel from 'src/explore/components/RowCountLabel'; +import RowCountLabel from '.'; describe('RowCountLabel', () => { const defaultProps = { diff --git a/superset-frontend/src/explore/components/RowCountLabel.tsx b/superset-frontend/src/explore/components/RowCountLabel/index.tsx similarity index 100% rename from superset-frontend/src/explore/components/RowCountLabel.tsx rename to superset-frontend/src/explore/components/RowCountLabel/index.tsx From e391a8394747f70a6bfadc149458f2106bf4f5ad Mon Sep 17 00:00:00 2001 From: Geido <60598000+geido@users.noreply.github.com> Date: Tue, 5 Apr 2022 20:04:47 +0300 Subject: [PATCH 30/31] Remove unused less (#19413) --- superset-frontend/src/profile/App.tsx | 1 - superset-frontend/src/profile/main.less | 32 ------------------------- 2 files changed, 33 deletions(-) delete mode 100644 superset-frontend/src/profile/main.less diff --git a/superset-frontend/src/profile/App.tsx b/superset-frontend/src/profile/App.tsx index 6f935ee710f99..1f2cd144afcc1 100644 --- a/superset-frontend/src/profile/App.tsx +++ b/superset-frontend/src/profile/App.tsx @@ -27,7 +27,6 @@ import App from 'src/profile/components/App'; import messageToastReducer from 'src/components/MessageToasts/reducers'; import { initEnhancer } from 'src/reduxUtils'; import setupApp from 'src/setup/setupApp'; -import './main.less'; import { theme } from 'src/preamble'; import ToastContainer from 'src/components/MessageToasts/ToastContainer'; diff --git a/superset-frontend/src/profile/main.less b/superset-frontend/src/profile/main.less deleted file mode 100644 index 7e16c67499745..0000000000000 --- a/superset-frontend/src/profile/main.less +++ /dev/null @@ -1,32 +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 '../assets/stylesheets/less/variables.less'; - -.tab-pane { - min-height: 400px; - background: @lightest; - border: 1px solid @gray-light; - border-top: 0px; -} - -.label { - display: inline-block; - margin-right: 5px; - margin-bottom: 5px; -} From a59718b0941fa73ae126a53ac257d4ab0046a549 Mon Sep 17 00:00:00 2001 From: Geido <60598000+geido@users.noreply.github.com> Date: Tue, 5 Apr 2022 20:05:09 +0300 Subject: [PATCH 31/31] Remove Control less (#19415) --- .../src/explore/components/Control.less | 21 ------------------- .../src/explore/components/Control.tsx | 12 ++++++----- 2 files changed, 7 insertions(+), 26 deletions(-) delete mode 100644 superset-frontend/src/explore/components/Control.less diff --git a/superset-frontend/src/explore/components/Control.less b/superset-frontend/src/explore/components/Control.less deleted file mode 100644 index 87b223c285462..0000000000000 --- a/superset-frontend/src/explore/components/Control.less +++ /dev/null @@ -1,21 +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. - */ -.Control { - padding-bottom: 4px; -} diff --git a/superset-frontend/src/explore/components/Control.tsx b/superset-frontend/src/explore/components/Control.tsx index 0f4820f4a2b5c..a8ade357da72a 100644 --- a/superset-frontend/src/explore/components/Control.tsx +++ b/superset-frontend/src/explore/components/Control.tsx @@ -21,13 +21,11 @@ import { ControlType, ControlComponentProps as BaseControlComponentProps, } from '@superset-ui/chart-controls'; -import { JsonValue, QueryFormData } from '@superset-ui/core'; +import { styled, JsonValue, QueryFormData } from '@superset-ui/core'; import ErrorBoundary from 'src/components/ErrorBoundary'; import { ExploreActions } from 'src/explore/actions/exploreActions'; import controlMap from './controls'; -import './Control.less'; - export type ControlProps = { // the actual action dispatcher (via bindActionCreators) has identical // signature to the original action factory. @@ -52,6 +50,10 @@ export type ControlProps = { export type ControlComponentProps = Omit & BaseControlComponentProps; +const StyledControl = styled.div` + padding-bottom: ${({ theme }) => theme.gridUnit}px; +`; + export default function Control(props: ControlProps) { const { actions: { setControlValue }, @@ -76,7 +78,7 @@ export default function Control(props: ControlProps) { } return ( -
-
+ ); }