Skip to content

Commit fa9adb3

Browse files
committed
jailable releases hook
1 parent 38c26e9 commit fa9adb3

File tree

3 files changed

+131
-5
lines changed

3 files changed

+131
-5
lines changed

static/app/views/releases/drawer/releasesDrawerList.tsx

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import {useCallback, useRef} from 'react';
1+
import {Fragment, type ReactElement, useCallback, useContext, useRef} from 'react';
2+
import styled from '@emotion/styled';
23
import type {SeriesOption} from 'echarts';
34
import type {MarkLineOption} from 'echarts/types/dist/shared';
45
import type {EChartsInstance} from 'echarts-for-react';
56

7+
import {DateTime} from 'sentry/components/dateTime';
68
import {
79
EventDrawerBody,
810
EventDrawerContainer,
@@ -12,18 +14,37 @@ import {
1214
NavigationCrumbs,
1315
} from 'sentry/components/events/eventDrawer';
1416
import {t, tn} from 'sentry/locale';
17+
import {space} from 'sentry/styles/space';
1518
import type {ReactEchartsRef, SeriesDataUnit} from 'sentry/types/echarts';
19+
import {useLocation} from 'sentry/utils/useLocation';
1620
import usePageFilters from 'sentry/utils/usePageFilters';
1721
import {useReleaseStats} from 'sentry/utils/useReleaseStats';
1822
import {formatVersion} from 'sentry/utils/versions/formatVersion';
23+
import {Widget} from 'sentry/views/dashboards/widgets/widget/widget';
24+
import type {ChartRendererProps} from 'sentry/views/releases/releaseBubbles/types';
25+
26+
type ChartRenderer = (props: ChartRendererProps) => ReactElement;
27+
import {
28+
ReleasesDrawerContext,
29+
ReleasesDrawerProvider,
30+
} from 'sentry/views/releases/drawer/releasesDrawerContext';
1931

2032
import {ReleaseDrawerTable} from './releasesDrawerTable';
2133

2234
interface ReleasesDrawerListProps {
35+
charts: Record<string, ChartRenderer>;
2336
endTs: number;
2437
environments: readonly string[];
2538
projects: readonly number[];
2639
startTs: number;
40+
/**
41+
* A renderer function that returns a chart. It is called with the trimmed
42+
* list of releases and timeSeries. It currently uses the
43+
* `TimeSeriesWidgetVisualization` components props. It's possible we change
44+
* it to make the props more generic, e.g. pass start/end timestamps and do
45+
* the series manipulation when we call the bubble hook.
46+
*/
47+
chartRenderer?: (rendererProps: ChartRendererProps) => ReactElement;
2748
}
2849

2950
type MarkLineDataCallbackFn = (item: SeriesDataUnit) => boolean;
@@ -84,7 +105,9 @@ export function ReleasesDrawerList({
84105
endTs,
85106
projects,
86107
environments,
108+
charts,
87109
}: ReleasesDrawerListProps) {
110+
const location = useLocation();
88111
const start = new Date(startTs);
89112
const end = new Date(endTs);
90113
const pageFilters = usePageFilters();
@@ -95,6 +118,7 @@ export function ReleasesDrawerList({
95118
end: endTs ? new Date(endTs).toISOString() : null,
96119
},
97120
});
121+
// const {getChart} = useContext(ReleasesDrawerContext);
98122
const chartRef = useRef<ReactEchartsRef | null>(null);
99123

100124
const handleMouseOverRelease = useCallback((release: string) => {
@@ -127,6 +151,9 @@ export function ReleasesDrawerList({
127151
label: t('Releases'),
128152
},
129153
];
154+
const chartRenderer = charts.get(String(location.query.rdChartId));
155+
// const chartRenderer = charts[String(location.query.rdChartId)];
156+
// const chartRenderer = getChart(String(location.query.rdChartId));
130157

131158
return (
132159
<EventDrawerContainer>
@@ -137,6 +164,42 @@ export function ReleasesDrawerList({
137164
<Header>{title}</Header>
138165
</EventNavigator>
139166
<EventDrawerBody>
167+
{chartRenderer ? (
168+
<ChartContainer>
169+
<Widget
170+
Title={
171+
<Fragment>
172+
{t('Releases from ')}
173+
<DateTime date={start} /> <span>{t('to')}</span> <DateTime date={end} />
174+
</Fragment>
175+
}
176+
Visualization={chartRenderer?.({
177+
ref: (e: ReactEchartsRef | null) => {
178+
chartRef.current = e;
179+
180+
if (e) {
181+
// When chart is mounted, zoom the chart into the relevant
182+
// bucket
183+
e.getEchartsInstance().dispatchAction({
184+
type: 'dataZoom',
185+
batch: [
186+
{
187+
// data value at starting location
188+
startValue: startTs,
189+
// data value at ending location
190+
endValue: endTs,
191+
},
192+
],
193+
});
194+
}
195+
},
196+
releases,
197+
start,
198+
end,
199+
})}
200+
/>
201+
</ChartContainer>
202+
) : null}
140203
<ReleaseDrawerTable
141204
projects={projects}
142205
environments={environments}
@@ -149,3 +212,7 @@ export function ReleasesDrawerList({
149212
</EventDrawerContainer>
150213
);
151214
}
215+
216+
const ChartContainer = styled('div')`
217+
margin-bottom: ${space(2)};
218+
`;

static/app/views/releases/drawer/useReleasesDrawer.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {useEffect} from 'react';
1+
import {type ReactElement, useCallback, useEffect, useState} from 'react';
22
import omit from 'lodash/omit';
33

44
import useDrawer from 'sentry/components/globalDrawer';
@@ -9,6 +9,7 @@ import {useLocation} from 'sentry/utils/useLocation';
99
import {useNavigate} from 'sentry/utils/useNavigate';
1010
import {ReleasesDrawerDetails} from 'sentry/views/releases/drawer/releasesDrawerDetails';
1111
import {ReleasesDrawerList} from 'sentry/views/releases/drawer/releasesDrawerList';
12+
import type {ChartRendererProps} from 'sentry/views/releases/releaseBubbles/types';
1213

1314
const RELEASES_DRAWER_FIELD_MAP = {
1415
showReleasesDrawer: decodeScalar,
@@ -25,6 +26,10 @@ function cleanLocationQuery(query: Record<string, string[] | string | null | und
2526
return omit(query, RELEASES_DRAWER_FIELDS);
2627
}
2728

29+
type ChartRenderer = (props: ChartRendererProps) => ReactElement;
30+
31+
const initialState = new Map();
32+
2833
export function useReleasesDrawer() {
2934
const {
3035
releaseProjectId,
@@ -37,9 +42,20 @@ export function useReleasesDrawer() {
3742
} = useLocationQuery({
3843
fields: RELEASES_DRAWER_FIELD_MAP,
3944
});
45+
const [charts, setCharts] = useState<Map<string, ChartRenderer>>(initialState);
4046
const navigate = useNavigate();
4147
const location = useLocation();
4248
const {closeDrawer, openDrawer} = useDrawer();
49+
const registerChart = (id: string, chartRenderer: ChartRenderer) => {
50+
setCharts(state => {
51+
state.set(id, chartRenderer);
52+
return state;
53+
});
54+
};
55+
56+
const cleanup = useCallback(() => {
57+
charts.clear();
58+
}, [charts]);
4359

4460
useEffect(() => {
4561
if (showReleasesDrawer !== '1') {
@@ -64,6 +80,7 @@ export function useReleasesDrawer() {
6480
openDrawer(
6581
() => (
6682
<ReleasesDrawerList
83+
charts={charts}
6784
environments={rdEnv}
6885
projects={rdProject.map(Number)}
6986
startTs={Number(rdStart)}
@@ -89,6 +106,7 @@ export function useReleasesDrawer() {
89106
closeDrawer();
90107
};
91108
}, [
109+
charts,
92110
closeDrawer,
93111
openDrawer,
94112
location.query,
@@ -101,4 +119,6 @@ export function useReleasesDrawer() {
101119
releaseProjectId,
102120
showReleasesDrawer,
103121
]);
122+
123+
return {registerChart, cleanup, charts};
104124
}

static/app/views/releases/releaseBubbles/useReleaseBubbles.tsx

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {useMemo, useRef} from 'react';
1+
import {type ReactElement, useContext, useEffect, useMemo, useRef} from 'react';
22
import {type Theme, useTheme} from '@emotion/react';
33
import type {
44
CustomSeriesOption,
@@ -29,16 +29,22 @@ import {type ReactRouter3Navigate, useNavigate} from 'sentry/utils/useNavigate';
2929
import useOrganization from 'sentry/utils/useOrganization';
3030
import usePageFilters from 'sentry/utils/usePageFilters';
3131
import {useUser} from 'sentry/utils/useUser';
32+
import {ReleasesDrawerContext} from 'sentry/views/releases/drawer/releasesDrawerContext';
33+
import {useReleasesDrawer} from 'sentry/views/releases/drawer/useReleasesDrawer';
3234
import {
3335
BUBBLE_AREA_SERIES_ID,
3436
BUBBLE_SERIES_ID,
3537
} from 'sentry/views/releases/releaseBubbles/constants';
3638
import {createReleaseBubbleHighlighter} from 'sentry/views/releases/releaseBubbles/createReleaseBubbleHighlighter';
37-
import type {Bucket} from 'sentry/views/releases/releaseBubbles/types';
39+
import type {
40+
Bucket,
41+
ChartRendererProps,
42+
} from 'sentry/views/releases/releaseBubbles/types';
3843
import {createReleaseBuckets} from 'sentry/views/releases/releaseBubbles/utils/createReleaseBuckets';
3944

4045
interface CreateReleaseBubbleMouseListenersParams {
4146
alignInMiddle: boolean;
47+
chartId: string;
4248
color: string;
4349
environments: readonly string[];
4450
location: LocationDescriptorObject;
@@ -51,6 +57,7 @@ interface CreateReleaseBubbleMouseListenersParams {
5157
* main chart when a release bubble is hovered over.
5258
*/
5359
function createReleaseBubbleMouseListeners({
60+
chartId,
5461
alignInMiddle,
5562
navigate,
5663
color,
@@ -76,6 +83,7 @@ function createReleaseBubbleMouseListeners({
7683
query: {
7784
...location.query,
7885
showReleasesDrawer: 1,
86+
rdChartId: chartId,
7987
rdStart: data.start,
8088
rdEnd: data.end,
8189
rdProject: projects,
@@ -327,6 +335,16 @@ ${t('Click to expand')}
327335
}
328336

329337
interface UseReleaseBubblesParams {
338+
/**
339+
* A unique ID for the chart so that it can be deeplinked to for the releases drawer.
340+
*/
341+
chartId: string;
342+
/**
343+
* Chart rendering function that is used by the Releases Drawer. This is
344+
* used as an "easy" way to show the same chart in the Global Drawer,
345+
* especially in the case of a new page load.
346+
*/
347+
chartRenderer: (props: ChartRendererProps) => ReactElement;
330348
/**
331349
* Align the starting timestamp to the middle of the release bubble (e.g. if
332350
* we want to match ECharts' bar charts), otherwise we draw starting at
@@ -368,6 +386,8 @@ export function useReleaseBubbles({
368386
releases,
369387
minTime,
370388
maxTime,
389+
chartId,
390+
chartRenderer,
371391
datetime,
372392
environments,
373393
projects,
@@ -383,6 +403,8 @@ export function useReleaseBubbles({
383403
const theme = useTheme();
384404
const {options} = useUser();
385405
const {selection} = usePageFilters();
406+
const {cleanup, charts, registerChart} = useReleasesDrawer();
407+
// const {registerChart} = useContext(ReleasesDrawerContext);
386408
// `maxTime` refers to the max time on x-axis for charts.
387409
// There may be the need to include releases that are > maxTime (e.g. in the
388410
// case of relative date selection). This is used for the tooltip to show the
@@ -395,6 +417,22 @@ export function useReleaseBubbles({
395417
const chartRef = useRef<ReactEchartsRef | null>(null);
396418
const hasReleaseBubbles = organization.features.includes('release-bubbles-ui');
397419
const totalBubblePaddingY = bubblePadding * 2;
420+
421+
useEffect(() => {
422+
if (hasReleaseBubbles) {
423+
registerChart(chartId, chartRenderer);
424+
}
425+
426+
return () => {};
427+
}, [chartId, chartRenderer, hasReleaseBubbles, registerChart]);
428+
429+
useEffect(() => {
430+
return () => {
431+
cleanup();
432+
};
433+
}, [cleanup]);
434+
435+
console.log(charts);
398436
const defaultBubbleXAxis = useMemo(
399437
() => ({
400438
axisLine: {onZero: true},
@@ -470,7 +508,7 @@ export function useReleaseBubbles({
470508
return {
471509
connectReleaseBubbleChartRef: () => {},
472510
releaseBubbleEventHandlers: {},
473-
ReleaseBubbleSeries: null,
511+
releaseBubbleSeries: null,
474512
releaseBubbleXAxis: {},
475513
releaseBubbleGrid: {},
476514
};
@@ -483,6 +521,7 @@ export function useReleaseBubbles({
483521
* An object map of ECharts event handlers. These should be spread onto a Chart component
484522
*/
485523
releaseBubbleEventHandlers: createReleaseBubbleMouseListeners({
524+
chartId,
486525
alignInMiddle,
487526
navigate,
488527
location,

0 commit comments

Comments
 (0)