From ca02adf7d02e793062dbf52fc116194b3310b69b Mon Sep 17 00:00:00 2001 From: Yevhenii Ushakov Date: Thu, 24 Oct 2024 15:48:06 +0300 Subject: [PATCH] feat(map): map details --- .../src/MapBox.jsx | 31 ++++++++++++++++ .../src/ScatterPlotGlowOverlay.jsx | 4 +- .../src/controlPanel.ts | 14 +++++++ .../src/transformProps.js | 4 ++ .../src/components/Chart/ChartRenderer.jsx | 2 + .../dashboard/components/DashboardGrid.jsx | 1 + .../components/gridComponents/Chart.jsx | 4 ++ .../components/gridComponents/ChartHolder.tsx | 37 ++++++++++++++++++- .../components/gridComponents/Column.jsx | 1 + .../components/gridComponents/Row.jsx | 1 + .../components/gridComponents/Tab.jsx | 2 + .../components/gridComponents/Tabs.jsx | 7 ++++ .../src/dashboard/containers/Chart.jsx | 5 ++- 13 files changed, 109 insertions(+), 4 deletions(-) diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.jsx b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.jsx index f04e3db5fb6dc..68418ad03b187 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.jsx +++ b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/MapBox.jsx @@ -81,6 +81,7 @@ class MapBox extends React.Component { this.handleViewportChange = this.handleViewportChange.bind(this); this.zoomIn = this.zoomIn.bind(this); this.zoomOut = this.zoomOut.bind(this); + this.handleViewDetail = this.handleViewDetail.bind(this); } zoomIn() { @@ -107,6 +108,35 @@ class MapBox extends React.Component { onViewportChange(viewport); } + handleViewDetail(value) { + const dataMask = { + id: this.props?.filterIdForDetails, + extraFormData: { + filters: [ + { + col: 'hospital_name', + op: 'IN', + val: [value], + }, + ], + }, + filterState: { + validateMessage: false, + label: value, + value: [value], + }, + ownState: {}, + }; + + this?.props?.onChangeParentTab(4); + this?.props?.handleApply(dataMask, this.props?.filterIdForDetails, () => { + window.scrollTo({ + top: 0, + behavior: 'smooth', + }); + }); + } + render() { const { width, @@ -181,6 +211,7 @@ class MapBox extends React.Component { namesDisappearZoomLevel={namesDisappearZoomLevel} globalOpacity={globalOpacity} compositeOperation="screen" + handleViewDetail={this.handleViewDetail} renderWhileDragging={renderWhileDragging} aggregation={hasCustomMetric ? aggregatorName : null} lngLatAccessor={location => { diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/ScatterPlotGlowOverlay.jsx b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/ScatterPlotGlowOverlay.jsx index df48f0d884cae..5326136f34243 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/ScatterPlotGlowOverlay.jsx +++ b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/ScatterPlotGlowOverlay.jsx @@ -276,7 +276,9 @@ class ScatterPlotGlowOverlay extends React.PureComponent { diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanel.ts b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanel.ts index 2865811678b6f..a447a4b3c4d52 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanel.ts +++ b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanel.ts @@ -75,6 +75,20 @@ const config: ControlPanelConfig = { }, }, ], + [ + { + name: 'filterIdForDetails', + config: { + type: 'TextControl', + label: t('Filter ID for item details'), + default: '', + isFloat: false, + description: t( + 'Filter id for item details. Which will take us to the details tab', + ), + }, + }, + ], ], }, { diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/transformProps.js b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/transformProps.js index 13ef13ee77914..269a1c01c0757 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/transformProps.js +++ b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/transformProps.js @@ -36,6 +36,7 @@ export default function transformProps(chartProps) { pointRadiusUnit, renderWhileDragging, namesDisappearZoomLevel, + filterIdForDetails, } = formData; // Validate mapbox color @@ -96,5 +97,8 @@ export default function transformProps(chartProps) { renderWhileDragging, rgb, namesDisappearZoomLevel, + filterIdForDetails, + onChangeParentTab: hooks?.onChangeParentTab, + handleApply: hooks?.handleApply, }; } diff --git a/superset-frontend/src/components/Chart/ChartRenderer.jsx b/superset-frontend/src/components/Chart/ChartRenderer.jsx index 5f29840dde898..d542d298ee3ae 100644 --- a/superset-frontend/src/components/Chart/ChartRenderer.jsx +++ b/superset-frontend/src/components/Chart/ChartRenderer.jsx @@ -119,6 +119,8 @@ class ChartRenderer extends React.Component { setDataMask: dataMask => { this.props.actions?.updateDataMask(this.props.chartId, dataMask); }, + onChangeParentTab: this.props?.onChangeParentTab, + handleApply: this.props?.handleApply, }; // TODO: queriesResponse comes from Redux store but it's being edited by diff --git a/superset-frontend/src/dashboard/components/DashboardGrid.jsx b/superset-frontend/src/dashboard/components/DashboardGrid.jsx index 6509596304779..481c3854b8715 100644 --- a/superset-frontend/src/dashboard/components/DashboardGrid.jsx +++ b/superset-frontend/src/dashboard/components/DashboardGrid.jsx @@ -313,6 +313,7 @@ class DashboardGrid extends React.PureComponent { onResize={this.handleResize} onResizeStop={this.handleResizeStop} onChangeTab={this.handleChangeTab} + onChangeParentTab={this.props?.onChangeParentTab} isCurrentPartChartsLoading={isCurrentPartChartsLoading} /> {/* make the area below components droppable */} diff --git a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx b/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx index aee061955f1f9..b088034db68c2 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx @@ -415,6 +415,8 @@ class Chart extends React.Component { isInView, emitCrossFilters, logEvent, + onChangeParentTab, + handleApply, } = this.props; const { width } = this.state; @@ -530,6 +532,8 @@ class Chart extends React.Component { postTransformProps={postTransformProps} datasetsStatus={datasetsStatus} isInView={isInView} + handleApply={handleApply} + onChangeParentTab={onChangeParentTab} emitCrossFilters={emitCrossFilters} /> diff --git a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx index bcc58d0691dab..ab39f1dc356cc 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx +++ b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx @@ -1,3 +1,4 @@ +/* eslint-disable import/no-unresolved */ /** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -19,8 +20,9 @@ import React, { useState, useMemo, useCallback, useEffect } from 'react'; import { ResizeCallback, ResizeStartCallback } from 're-resizable'; import cx from 'classnames'; -import { useSelector } from 'react-redux'; -import { css } from '@superset-ui/core'; +import { useDispatch, useSelector } from 'react-redux'; +import { updateDataMask } from 'src/dataMask/actions'; +import { css, DataMask, DataMaskStateWithId } from '@superset-ui/core'; import { LayoutItem, RootState } from 'src/dashboard/types'; import AnchorLink from 'src/dashboard/components/AnchorLink'; import Chart from 'src/dashboard/containers/Chart'; @@ -37,6 +39,9 @@ import { GRID_MIN_COLUMN_COUNT, GRID_MIN_ROW_UNITS, } from 'src/dashboard/util/constants'; +import { logEvent } from 'src/logger/actions'; +import { LOG_ACTIONS_CHANGE_DASHBOARD_FILTER } from 'src/logger/LogUtils'; +import { useNativeFiltersDataMask } from '../nativeFilters/FilterBar/state'; export const CHART_MARGIN = 32; @@ -47,6 +52,7 @@ interface ChartHolderProps { component: LayoutItem; parentComponent: LayoutItem; getComponentById?: (id?: string) => LayoutItem | undefined; + onChangeParentTab?: (tabIndex: number) => void; index: number; depth: number; editMode: boolean; @@ -100,10 +106,35 @@ const ChartHolder: React.FC = ({ handleComponentDrop, setFullSizeChartId, isInView, + onChangeParentTab, }) => { + const dispatch = useDispatch(); const { chartId } = component.meta; const isFullSize = fullSizeChartId === chartId; + const dataMaskApplied: DataMaskStateWithId = useNativeFiltersDataMask(); + + const handleApply = useCallback( + async ( + dataMask: DataMask, + filterIdForDetails: string, + callback: () => void, + ) => { + if (dataMask && filterIdForDetails) { + dispatch(logEvent(LOG_ACTIONS_CHANGE_DASHBOARD_FILTER, {})); + + if (dataMaskApplied[filterIdForDetails]) { + await dispatch(updateDataMask(filterIdForDetails, dataMask)); + } + + if (callback) { + callback(); + } + } + }, + [dataMaskApplied, dispatch], + ); + const focusHighlightStyles = useFilterFocusHighlightStyles(chartId); const dashboardState = useSelector( (state: RootState) => state.dashboardState, @@ -315,6 +346,8 @@ const ChartHolder: React.FC = ({ setControlValue={handleExtraControl} extraControls={extraControls} isInView={isInView} + onChangeParentTab={onChangeParentTab} + handleApply={handleApply} /> {editMode && ( diff --git a/superset-frontend/src/dashboard/components/gridComponents/Column.jsx b/superset-frontend/src/dashboard/components/gridComponents/Column.jsx index 98f2b84e8d920..45c6af350b5f6 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Column.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Column.jsx @@ -280,6 +280,7 @@ class Column extends React.PureComponent { onResizeStop={onResizeStop} isComponentVisible={isComponentVisible} onChangeTab={onChangeTab} + onChangeParentTab={this.props?.onChangeParentTab} /> {editMode && ( {editMode && ( diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx index fa72d8ea5a6ba..8549a2ad0f78f 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx @@ -183,6 +183,7 @@ class Tab extends React.PureComponent { setEditMode, dashboardId, isChild, + onChangeParentTab, } = this.props; const shouldDisplayEmptyState = tabComponent.children.length === 0; @@ -263,6 +264,7 @@ class Tab extends React.PureComponent { onResizeStop={onResizeStop} isComponentVisible={isComponentVisible} onChangeTab={this.handleChangeTab} + onChangeParentTab={onChangeParentTab} /> {/* Make bottom of tab droppable */} {editMode && ( diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx b/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx index 9cff499c84e44..bf0527d5ba712 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx @@ -357,6 +357,7 @@ export class Tabs extends React.PureComponent { nativeFilters, isChild, isCurrentPartChartsLoading, + onChangeParentTab, } = this.props; const { children: tabIds } = tabsComponent; @@ -436,6 +437,9 @@ export class Tabs extends React.PureComponent { onHoverTab={() => this.handleClickTab(tabIndex)} isFocused={activeKey === tabId} isChild={isChild} + onChangeParentTab={ + isChild ? onChangeParentTab : this.handleClickTab + } isHighlighted={ activeKey !== tabId && tabsToHighlight?.includes(tabId) } @@ -449,6 +453,9 @@ export class Tabs extends React.PureComponent { parentId={tabsComponent.id} depth={depth} // see isValidChild.js for why tabs don't increment child depth index={tabIndex} + onChangeParentTab={ + isChild ? onChangeParentTab : this.handleClickTab + } isCurrentPartChartsLoading={isCurrentPartChartsLoading} renderType={RENDER_TAB_CONTENT} availableColumnCount={availableColumnCount} diff --git a/superset-frontend/src/dashboard/containers/Chart.jsx b/superset-frontend/src/dashboard/containers/Chart.jsx index 1e50602b06299..6fd24fcb6fa9f 100644 --- a/superset-frontend/src/dashboard/containers/Chart.jsx +++ b/superset-frontend/src/dashboard/containers/Chart.jsx @@ -54,7 +54,8 @@ function mapStateToProps( }, ownProps, ) { - const { id, extraControls, setControlValue } = ownProps; + const { id, extraControls, setControlValue, onChangeParentTab, handleApply } = + ownProps; const chart = chartQueries[id] || EMPTY_OBJECT; const datasource = (chart && chart.form_data && datasources[chart.form_data.datasource]) || @@ -101,6 +102,8 @@ function mapStateToProps( setControlValue, datasetsStatus, emitCrossFilters: !!dashboardInfo.crossFiltersEnabled, + onChangeParentTab, + handleApply, }; }