Skip to content

Commit 8326350

Browse files
authored
Merge branch 'master' into fix-column-tooltip
2 parents 2783d57 + 9215eb5 commit 8326350

21 files changed

Lines changed: 683 additions & 79 deletions

File tree

superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ const defaultFormData: EchartsTimeseriesFormData & {
158158
xAxisTitle: '',
159159
xAxisTitleMargin: 0,
160160
yAxisTitle: '',
161-
yAxisTitleMargin: 0,
161+
yAxisTitleMargin: 15,
162162
yAxisTitlePosition: '',
163163
time_range: 'No filter',
164164
granularity: undefined,

superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export const DEFAULT_FORM_DATA: EchartsTimeseriesFormData = {
4646
xAxisTitle: '',
4747
xAxisTitleMargin: 0,
4848
yAxisTitle: '',
49-
yAxisTitleMargin: 0,
49+
yAxisTitleMargin: 15,
5050
yAxisTitlePosition: 'Top',
5151
// Now that the weird bug workaround is over, here's the rest...
5252
...DEFAULT_SORT_SERIES_DATA,

superset-frontend/plugins/plugin-chart-echarts/src/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export const DEFAULT_TITLE_FORM_DATA: TitleFormData = {
104104
xAxisTitle: '',
105105
xAxisTitleMargin: 0,
106106
yAxisTitle: '',
107-
yAxisTitleMargin: 0,
107+
yAxisTitleMargin: 15,
108108
yAxisTitlePosition: 'Top',
109109
};
110110

superset-frontend/plugins/plugin-chart-echarts/test/MixedTimeseries/transformProps.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ const formData: EchartsMixedTimeseriesFormData = {
112112
yAxisBounds: [undefined, undefined],
113113
yAxisBoundsSecondary: [undefined, undefined],
114114
yAxisTitle: '',
115-
yAxisTitleMargin: 0,
115+
yAxisTitleMargin: 15,
116116
yAxisTitlePosition: '',
117117
yAxisTitleSecondary: '',
118118
zoomable: false,

superset-frontend/src/dataMask/reducer.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ import {
3636
isChartCustomization,
3737
} from 'src/dashboard/components/nativeFilters/FiltersConfigModal/utils';
3838
import { HYDRATE_DASHBOARD } from 'src/dashboard/actions/hydrate';
39+
import {
40+
HYDRATE_EXPLORE,
41+
HydrateExplore,
42+
} from 'src/explore/actions/hydrateExplore';
3943
import { SaveFilterChangesType } from 'src/dashboard/components/nativeFilters/FiltersConfigModal/types';
4044
import {
4145
migrateChartCustomizationArray,
@@ -195,7 +199,7 @@ function updateDataMaskForFilterChanges(
195199
const dataMaskReducer = produce(
196200
(
197201
draft: DataMaskStateWithId,
198-
action: AnyDataMaskAction | HydrateDashboardAction,
202+
action: AnyDataMaskAction | HydrateDashboardAction | HydrateExplore,
199203
) => {
200204
const cleanState: DataMaskStateWithId = {};
201205
switch (action.type) {
@@ -286,6 +290,20 @@ const dataMaskReducer = produce(
286290

287291
return cleanState;
288292
}
293+
case HYDRATE_EXPLORE: {
294+
const hydrateExploreAction = action as HydrateExplore;
295+
const loadedDataMask = hydrateExploreAction.data.dataMask;
296+
if (loadedDataMask) {
297+
Object.entries(loadedDataMask).forEach(([id, mask]) => {
298+
draft[id] = {
299+
...getInitialDataMask(id),
300+
...draft[id],
301+
...mask,
302+
};
303+
});
304+
}
305+
return draft;
306+
}
289307
case SET_DATA_MASK_FOR_FILTER_CHANGES_COMPLETE:
290308
updateDataMaskForFilterChanges(
291309
action.filterChanges,

superset-frontend/src/explore/actions/exploreActions.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,19 @@ export function setForceQuery(force: boolean) {
153153
};
154154
}
155155

156+
export const UPDATE_EXPLORE_CHART_STATE = 'UPDATE_EXPLORE_CHART_STATE';
157+
export function updateExploreChartState(
158+
chartId: number,
159+
chartState: Record<string, unknown>,
160+
) {
161+
return {
162+
type: UPDATE_EXPLORE_CHART_STATE,
163+
chartId,
164+
chartState,
165+
lastModified: Date.now(),
166+
};
167+
}
168+
156169
export const SET_STASH_FORM_DATA = 'SET_STASH_FORM_DATA';
157170
export function setStashFormData(
158171
isHidden: boolean,

superset-frontend/src/explore/actions/hydrateExplore.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import { getControlsState } from 'src/explore/store';
2828
import { Dispatch } from 'redux';
2929
import {
3030
Currency,
31+
DataMaskStateWithId,
32+
JsonObject,
3133
ensureIsArray,
3234
FeatureFlag,
3335
getCategoricalSchemeRegistry,
@@ -60,7 +62,12 @@ export const hydrateExplore =
6062
dataset,
6163
metadata,
6264
saveAction = null,
63-
}: ExplorePageInitialData) =>
65+
dataMask,
66+
chartStates,
67+
}: ExplorePageInitialData & {
68+
dataMask?: DataMaskStateWithId;
69+
chartStates?: Record<number, JsonObject>;
70+
}) =>
6471
(dispatch: Dispatch, getState: () => ExplorePageState) => {
6572
const { user, datasources, charts, sliceEntities, common, explore } =
6673
getState();
@@ -224,12 +231,13 @@ export const hydrateExplore =
224231
saveModalAlert: null,
225232
isVisible: false,
226233
},
227-
explore: exploreState,
234+
explore: { ...exploreState, chartStates },
235+
dataMask,
228236
},
229237
});
230238
};
231239

232240
export type HydrateExplore = {
233241
type: typeof HYDRATE_EXPLORE;
234-
data: ExplorePageState;
242+
data: ExplorePageState & { dataMask?: DataMaskStateWithId };
235243
};

superset-frontend/src/explore/components/ExploreChartPanel/index.tsx

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* under the License.
1818
*/
1919
import { useState, useEffect, useCallback, useMemo, ReactNode } from 'react';
20+
import { useDispatch, useSelector } from 'react-redux';
2021
import Split from 'react-split';
2122
import { t } from '@apache-superset/core/translation';
2223
import {
@@ -33,6 +34,11 @@ import {
3334
import { Alert } from '@apache-superset/core/components';
3435
import { css, styled, useTheme } from '@apache-superset/core/theme';
3536
import ChartContainer from 'src/components/Chart/ChartContainer';
37+
import { updateExploreChartState } from 'src/explore/actions/exploreActions';
38+
import {
39+
convertChartStateToOwnState,
40+
hasChartStateConverter,
41+
} from 'src/dashboard/util/chartStateConverter';
3642
import {
3743
getItem,
3844
setItem,
@@ -43,6 +49,7 @@ import { getDatasourceAsSaveableDataset } from 'src/utils/datasourceUtils';
4349
import { buildV1ChartDataPayload } from 'src/explore/exploreUtils';
4450
import { getChartRequiredFieldsMissingMessage } from 'src/utils/getChartRequiredFieldsMissingMessage';
4551
import type { ChartState, Datasource } from 'src/explore/types';
52+
import type { ExploreState } from 'src/explore/reducers/exploreReducer';
4653
import type { Slice } from 'src/types/Chart';
4754
import LastQueriedLabel from 'src/components/LastQueriedLabel';
4855
import { DataTablesPane } from '../DataTablesPane';
@@ -126,6 +133,28 @@ const Styles = styled.div<{ showSplite: boolean }>`
126133
}
127134
`;
128135

136+
const EMPTY_OBJECT: Record<string, never> = {};
137+
138+
const createOwnStateWithChartState = (
139+
baseOwnState: JsonObject,
140+
chartState: { state?: JsonObject } | undefined,
141+
vizTypeArg: string,
142+
): JsonObject => {
143+
if (!hasChartStateConverter(vizTypeArg)) {
144+
return baseOwnState;
145+
}
146+
const state = chartState?.state;
147+
if (!state) {
148+
return baseOwnState;
149+
}
150+
const convertedState = convertChartStateToOwnState(vizTypeArg, state);
151+
return {
152+
...baseOwnState,
153+
...convertedState,
154+
chartState: state,
155+
};
156+
};
157+
129158
const ExploreChartPanel = ({
130159
chart,
131160
slice,
@@ -145,8 +174,34 @@ const ExploreChartPanel = ({
145174
can_download: canDownload,
146175
}: ExploreChartPanelProps) => {
147176
const theme = useTheme();
177+
const dispatch = useDispatch();
148178
const gutterMargin = theme.sizeUnit * GUTTER_SIZE_FACTOR;
149179
const gutterHeight = theme.sizeUnit * GUTTER_SIZE_FACTOR;
180+
181+
const chartState = useSelector(
182+
(state: { explore?: ExploreState }) =>
183+
state.explore?.chartStates?.[chart.id],
184+
);
185+
186+
const handleChartStateChange = useCallback(
187+
(chartStateArg: JsonObject) => {
188+
if (hasChartStateConverter(vizType)) {
189+
dispatch(updateExploreChartState(chart.id, chartStateArg));
190+
}
191+
},
192+
[dispatch, chart.id, vizType],
193+
);
194+
195+
const mergedOwnState = useMemo(
196+
() =>
197+
createOwnStateWithChartState(
198+
ownState || EMPTY_OBJECT,
199+
chartState as { state?: JsonObject } | undefined,
200+
vizType,
201+
),
202+
[ownState, chartState, vizType],
203+
);
204+
150205
const {
151206
ref: chartPanelRef,
152207
observerRef: resizeObserverRef,
@@ -259,7 +314,7 @@ const ExploreChartPanel = ({
259314
<ChartContainer
260315
width={Math.floor(chartPanelWidth)}
261316
height={chartPanelHeight}
262-
ownState={ownState}
317+
ownState={mergedOwnState}
263318
annotationData={chart.annotationData}
264319
chartId={chart.id}
265320
triggerRender={triggerRender}
@@ -277,6 +332,7 @@ const ExploreChartPanel = ({
277332
timeout={timeout}
278333
triggerQuery={chart.triggerQuery}
279334
vizType={vizType}
335+
onChartStateChange={handleChartStateChange}
280336
{...(chart.chartAlert && { chartAlert: chart.chartAlert })}
281337
{...(chart.chartStackTrace && {
282338
chartStackTrace: chart.chartStackTrace,
@@ -304,8 +360,9 @@ const ExploreChartPanel = ({
304360
errorMessage,
305361
force,
306362
formData,
363+
handleChartStateChange,
307364
onQuery,
308-
ownState,
365+
mergedOwnState,
309366
timeout,
310367
triggerRender,
311368
vizType,

superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,9 @@ interface ExploreSlice {
172172

173173
interface ExploreState {
174174
charts?: Record<number, ChartState>;
175-
explore?: ExploreSlice;
175+
explore?: ExploreSlice & {
176+
chartStates?: Record<number, JsonObject>;
177+
};
176178
common?: {
177179
conf?: {
178180
CSV_STREAMING_ROW_THRESHOLD?: number;
@@ -221,6 +223,15 @@ export const useExploreAdditionalActionsMenu = (
221223
state.common?.conf?.CSV_STREAMING_ROW_THRESHOLD ||
222224
DEFAULT_CSV_STREAMING_ROW_THRESHOLD,
223225
);
226+
const exploreChartState = useSelector<
227+
ExploreState,
228+
JsonObject | undefined
229+
>(state => {
230+
const chartKey = state.explore ? getChartKey(state.explore) : undefined;
231+
return chartKey != null
232+
? state.explore?.chartStates?.[chartKey]
233+
: undefined;
234+
});
224235

225236
// Streaming export state and handlers
226237
const [isStreamingModalVisible, setIsStreamingModalVisible] = useState(false);
@@ -274,6 +285,9 @@ export const useExploreAdditionalActionsMenu = (
274285
'EXPORT_CURRENT_VIEW' as Behavior,
275286
);
276287

288+
const permalinkChartState = (exploreChartState as { state?: JsonObject })
289+
?.state;
290+
277291
const shareByEmail = useCallback(async () => {
278292
try {
279293
const subject = t('Superset Chart');
@@ -282,6 +296,8 @@ export const useExploreAdditionalActionsMenu = (
282296
}
283297
const result = await getChartPermalink(
284298
latestQueryFormData as Pick<QueryFormData, 'datasource'>,
299+
undefined,
300+
permalinkChartState,
285301
);
286302
if (!result?.url) {
287303
throw new Error('Failed to generate permalink');
@@ -293,7 +309,7 @@ export const useExploreAdditionalActionsMenu = (
293309
} catch (error) {
294310
addDangerToast(t('Sorry, something went wrong. Try again later.'));
295311
}
296-
}, [addDangerToast, latestQueryFormData]);
312+
}, [addDangerToast, latestQueryFormData, permalinkChartState]);
297313

298314
const exportCSV = useCallback(() => {
299315
if (!canDownloadCSV) return null;
@@ -411,6 +427,8 @@ export const useExploreAdditionalActionsMenu = (
411427
await copyTextToClipboard(async () => {
412428
const result = await getChartPermalink(
413429
latestQueryFormData as Pick<QueryFormData, 'datasource'>,
430+
undefined,
431+
permalinkChartState,
414432
);
415433
if (!result?.url) {
416434
throw new Error('Failed to generate permalink');
@@ -421,7 +439,7 @@ export const useExploreAdditionalActionsMenu = (
421439
} catch (error) {
422440
addDangerToast(t('Sorry, something went wrong. Try again later.'));
423441
}
424-
}, [addDangerToast, addSuccessToast, latestQueryFormData]);
442+
}, [addDangerToast, addSuccessToast, latestQueryFormData, permalinkChartState]);
425443

426444
// Minimal client-side CSV builder used for "Current View" when pagination is disabled
427445
const downloadClientCSV = (

superset-frontend/src/explore/reducers/exploreReducer.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,12 @@
1717
* under the License.
1818
*/
1919
/* eslint camelcase: 0 */
20-
import { ensureIsArray, QueryFormData, JsonValue } from '@superset-ui/core';
20+
import {
21+
ensureIsArray,
22+
QueryFormData,
23+
JsonValue,
24+
JsonObject,
25+
} from '@superset-ui/core';
2126
import {
2227
ControlState,
2328
ControlStateMapping,
@@ -66,6 +71,7 @@ export interface ExploreState {
6671
owners?: string[] | null;
6772
};
6873
saveAction?: SaveActionType | null;
74+
chartStates?: Record<number, JsonObject>;
6975
}
7076

7177
// Action type definitions
@@ -165,6 +171,13 @@ interface SetForceQueryAction {
165171
force: boolean;
166172
}
167173

174+
interface UpdateExploreChartStateAction {
175+
type: typeof actions.UPDATE_EXPLORE_CHART_STATE;
176+
chartId: number;
177+
chartState: Record<string, unknown>;
178+
lastModified: number;
179+
}
180+
168181
type ExploreAction =
169182
| DynamicPluginControlsReadyAction
170183
| ToggleFaveStarAction
@@ -183,6 +196,7 @@ type ExploreAction =
183196
| SetStashFormDataAction
184197
| SliceUpdatedAction
185198
| SetForceQueryAction
199+
| UpdateExploreChartStateAction
186200
| HydrateExplore;
187201

188202
// Extended control state for dynamic form controls - uses Record for flexibility
@@ -621,10 +635,25 @@ export default function exploreReducer(
621635
force: typedAction.force,
622636
};
623637
},
638+
[actions.UPDATE_EXPLORE_CHART_STATE]() {
639+
const typedAction = action as UpdateExploreChartStateAction;
640+
return {
641+
...state,
642+
chartStates: {
643+
...state.chartStates,
644+
[typedAction.chartId]: {
645+
chartId: typedAction.chartId,
646+
state: typedAction.chartState,
647+
lastModified: typedAction.lastModified,
648+
},
649+
},
650+
};
651+
},
624652
[HYDRATE_EXPLORE]() {
625653
const typedAction = action as HydrateExplore;
654+
const exploreData = typedAction.data.explore;
626655
return {
627-
...typedAction.data.explore,
656+
...exploreData,
628657
} as ExploreState;
629658
},
630659
};

0 commit comments

Comments
 (0)