Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(insights): Allow picking chart layouts in Insights > Session Health #88502

Merged
merged 11 commits into from
Apr 3, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
type TimeSeriesWidgetVisualizationProps,
} from 'sentry/views/dashboards/widgets/timeSeriesWidget/timeSeriesWidgetVisualization';
import {Widget} from 'sentry/views/dashboards/widgets/widget/widget';
import type {WidgetTitleProps} from 'sentry/views/dashboards/widgets/widget/widgetTitle';

import {
AVG_COLOR,
Expand All @@ -32,15 +33,15 @@ import {INGESTION_DELAY} from '../../settings';
import type {DiscoverSeries} from '../queries/useDiscoverSeries';
import {convertSeriesToTimeseries} from '../utils/convertSeriesToTimeseries';

export interface InsightsTimeSeriesWidgetProps {
export interface InsightsTimeSeriesWidgetProps extends WidgetTitleProps {
error: Error | null;
isLoading: boolean;
series: DiscoverSeries[];
title: string;
visualizationType: 'line' | 'area' | 'bar';
aliases?: Record<string, string>;
description?: React.ReactNode;
height?: string | number;
interactiveTitle?: () => React.ReactNode;
legendSelection?: LegendSelection | undefined;
onLegendSelectionChange?: ((selection: LegendSelection) => void) | undefined;
stacked?: boolean;
Expand Down Expand Up @@ -76,7 +77,11 @@ export function InsightsTimeSeriesWidget(props: InsightsTimeSeriesWidgetProps) {
}),
};

const Title = <Widget.WidgetTitle title={props.title} />;
const Title = props.interactiveTitle ? (
props.interactiveTitle()
) : (
<Widget.WidgetTitle title={props.title} />
);

// TODO: Instead of using `ChartContainer`, enforce the height from the parent layout
if (props.isLoading) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ const Header = styled('div')`

const StyledCompactSelect = styled(CompactSelect)`
/* Reset font-weight set by HeaderTitleLegend, buttons are already bold and
* setting this higher up causes it to trickle into the menues */
* setting this higher up causes it to trickle into the menus */
font-weight: ${p => p.theme.fontWeightNormal};
margin: -${space(0.5)} -${space(1)} -${space(0.25)};
min-width: 0;
Expand Down
36 changes: 23 additions & 13 deletions static/app/views/insights/sessions/charts/chartWithIssues.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,35 @@ import type {LegendSelection} from 'sentry/views/dashboards/widgets/common/types
import type {Plottable} from 'sentry/views/dashboards/widgets/timeSeriesWidget/plottables/plottable';
import {TimeSeriesWidgetVisualization} from 'sentry/views/dashboards/widgets/timeSeriesWidget/timeSeriesWidgetVisualization';
import {Widget} from 'sentry/views/dashboards/widgets/widget/widget';
import type {WidgetTitleProps} from 'sentry/views/dashboards/widgets/widget/widgetTitle';
import type {DiscoverSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries';
import {ModalChartContainer} from 'sentry/views/insights/pages/backend/laravel/styles';
import {WidgetVisualizationStates} from 'sentry/views/insights/pages/backend/laravel/widgetVisualizationStates';
import useRecentIssues from 'sentry/views/insights/sessions/queries/useRecentIssues';
import {SESSION_HEALTH_CHART_HEIGHT} from 'sentry/views/insights/sessions/utils/sessions';

export default function ChartWithIssues({
project,
series,
plottables,
title,
description,
isPending,
error,
legendSelection,
}: {
interface Props extends WidgetTitleProps {
description: string;
error: Error | null;
isPending: boolean;
plottables: Plottable[];
project: Project;
series: DiscoverSeries[];
title: string;
interactiveTitle?: () => React.ReactNode;
legendSelection?: LegendSelection | undefined;
}) {
}

export default function ChartWithIssues({
description,
error,
interactiveTitle,
isPending,
legendSelection,
plottables,
project,
series,
title,
}: Props) {
const {recentIssues, isPending: isPendingRecentIssues} = useRecentIssues({
projectId: project.id,
});
Expand All @@ -66,6 +70,12 @@ export default function ChartWithIssues({
);
}

const Title = interactiveTitle ? (
interactiveTitle()
) : (
<Widget.WidgetTitle title={title} />
);

const visualization = (
<WidgetVisualizationStates
isEmpty={!hasData}
Expand All @@ -92,7 +102,7 @@ export default function ChartWithIssues({

return (
<Widget
Title={<Widget.WidgetTitle title={title} />}
Title={Title}
height={SESSION_HEALTH_CHART_HEIGHT}
Visualization={visualization}
Actions={
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import ExternalLink from 'sentry/components/links/externalLink';
import {t, tct} from 'sentry/locale';
import {tct} from 'sentry/locale';
import {formatSeriesName} from 'sentry/views/dashboards/widgets/timeSeriesWidget/formatters/formatSeriesName';
import {InsightsLineChartWidget} from 'sentry/views/insights/common/components/insightsLineChartWidget';
import ChartSelectionTitle from 'sentry/views/insights/sessions/components/chartSelectionTitle';
import useCrashFreeSessions from 'sentry/views/insights/sessions/queries/useCrashFreeSessions';
import {CHART_TITLES} from 'sentry/views/insights/sessions/settings';
import {SESSION_HEALTH_CHART_HEIGHT} from 'sentry/views/insights/sessions/utils/sessions';

export default function CrashFreeSessionsChart() {
Expand All @@ -17,7 +19,10 @@ export default function CrashFreeSessionsChart() {

return (
<InsightsLineChartWidget
title={t('Crash Free Sessions')}
title={CHART_TITLES.CrashFreeSessionsChart}
interactiveTitle={() => (
<ChartSelectionTitle title={CHART_TITLES.CrashFreeSessionsChart} />
)}
height={SESSION_HEALTH_CHART_HEIGHT}
description={tct(
'The percent of sessions terminating without a crash. See [link:session status].',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import ExternalLink from 'sentry/components/links/externalLink';
import {t, tct} from 'sentry/locale';
import {InsightsLineChartWidget} from 'sentry/views/insights/common/components/insightsLineChartWidget';
import ChartSelectionTitle from 'sentry/views/insights/sessions/components/chartSelectionTitle';
import useErrorFreeSessions from 'sentry/views/insights/sessions/queries/useErrorFreeSessions';
import {CHART_TITLES} from 'sentry/views/insights/sessions/settings';
import {SESSION_HEALTH_CHART_HEIGHT} from 'sentry/views/insights/sessions/utils/sessions';

export default function ErrorFreeSessionsChart() {
Expand All @@ -13,7 +15,10 @@ export default function ErrorFreeSessionsChart() {

return (
<InsightsLineChartWidget
title={t('Error Free Sessions')}
title={CHART_TITLES.ErrorFreeSessionsChart}
interactiveTitle={() => (
<ChartSelectionTitle title={CHART_TITLES.ErrorFreeSessionsChart} />
)}
height={SESSION_HEALTH_CHART_HEIGHT}
description={tct(
'The percent of sessions terminating without a single error occurring. See [link:session status].',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,21 @@ import type {Project} from 'sentry/types/project';
import {Bars} from 'sentry/views/dashboards/widgets/timeSeriesWidget/plottables/bars';
import {convertSeriesToTimeseries} from 'sentry/views/insights/common/utils/convertSeriesToTimeseries';
import ChartWithIssues from 'sentry/views/insights/sessions/charts/chartWithIssues';
import ChartSelectionTitle from 'sentry/views/insights/sessions/components/chartSelectionTitle';
import useNewAndResolvedIssues from 'sentry/views/insights/sessions/queries/useNewAndResolvedIssues';
import {CHART_TITLES} from 'sentry/views/insights/sessions/settings';

export default function NewAndResolvedIssueChart({
type,
project,
}: {
project: Project;
type: 'issue' | 'feedback';
}) {
const {series, isPending, error} = useNewAndResolvedIssues({type});
export default function NewAndResolvedIssueChart({project}: {project: Project}) {
const {series, isPending, error} = useNewAndResolvedIssues({type: 'issue'});
const theme = useTheme();

const aliases = {
new_issues_count: `new_${type}s`,
resolved_issues_count: `resolved_${type}s`,
new_issues_count: 'new_issues',
resolved_issues_count: 'resolved_issues',
};

const colorPalette = theme.chart.getColorPalette(series.length - 2);
const title = type === 'issue' ? t('Issues') : t('User Feedback');

const plottables = series.map(
(ts, index) =>
new Bars(convertSeriesToTimeseries(ts), {
Expand All @@ -34,10 +30,13 @@ export default function NewAndResolvedIssueChart({

return (
<ChartWithIssues
title={CHART_TITLES.NewAndResolvedIssueChart}
interactiveTitle={() => (
<ChartSelectionTitle title={CHART_TITLES.NewAndResolvedIssueChart} />
)}
project={project}
series={series}
title={title}
description={t('New and resolved %s counts over time.', type)}
description={t('New and resolved issue counts over time.')}
plottables={plottables}
isPending={isPending}
error={error}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import type {Project} from 'sentry/types/project';
import {Line} from 'sentry/views/dashboards/widgets/timeSeriesWidget/plottables/line';
import {convertSeriesToTimeseries} from 'sentry/views/insights/common/utils/convertSeriesToTimeseries';
import ChartWithIssues from 'sentry/views/insights/sessions/charts/chartWithIssues';
import ChartSelectionTitle from 'sentry/views/insights/sessions/components/chartSelectionTitle';
import useReleaseNewIssues from 'sentry/views/insights/sessions/queries/useReleaseNewIssues';
import {CHART_TITLES} from 'sentry/views/insights/sessions/settings';

export default function ReleaseNewIssuesChart({project}: {project: Project}) {
const {series, isPending, error} = useReleaseNewIssues();
Expand All @@ -22,9 +24,12 @@ export default function ReleaseNewIssuesChart({project}: {project: Project}) {

return (
<ChartWithIssues
title={CHART_TITLES.ReleaseNewIssuesChart}
interactiveTitle={() => (
<ChartSelectionTitle title={CHART_TITLES.ReleaseNewIssuesChart} />
)}
project={project}
series={series}
title={t('New Issues by Release')}
description={t('New issue counts over time, grouped by release.')}
isPending={isPending}
error={error}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {t} from 'sentry/locale';
import {formatSeriesName} from 'sentry/views/dashboards/widgets/timeSeriesWidget/formatters/formatSeriesName';
import {InsightsLineChartWidget} from 'sentry/views/insights/common/components/insightsLineChartWidget';
import ChartSelectionTitle from 'sentry/views/insights/sessions/components/chartSelectionTitle';
import useReleaseSessionCounts from 'sentry/views/insights/sessions/queries/useReleaseSessionCounts';
import {CHART_TITLES} from 'sentry/views/insights/sessions/settings';
import {SESSION_HEALTH_CHART_HEIGHT} from 'sentry/views/insights/sessions/utils/sessions';

export default function ReleaseSessionCountChart() {
Expand All @@ -14,7 +16,10 @@ export default function ReleaseSessionCountChart() {

return (
<InsightsLineChartWidget
title={t('Total Sessions by Release')}
title={CHART_TITLES.ReleaseSessionCountChart}
interactiveTitle={() => (
<ChartSelectionTitle title={CHART_TITLES.ReleaseSessionCountChart} />
)}
height={SESSION_HEALTH_CHART_HEIGHT}
description={t(
'The total number of sessions per release. The 5 most recent releases are shown.'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {t} from 'sentry/locale';
import {formatSeriesName} from 'sentry/views/dashboards/widgets/timeSeriesWidget/formatters/formatSeriesName';
import {InsightsAreaChartWidget} from 'sentry/views/insights/common/components/insightsAreaChartWidget';
import ChartSelectionTitle from 'sentry/views/insights/sessions/components/chartSelectionTitle';
import useReleaseSessionPercentage from 'sentry/views/insights/sessions/queries/useReleaseSessionPercentage';
import {CHART_TITLES} from 'sentry/views/insights/sessions/settings';
import {SESSION_HEALTH_CHART_HEIGHT} from 'sentry/views/insights/sessions/utils/sessions';

export default function ReleaseSessionPercentageChart() {
Expand All @@ -14,7 +16,10 @@ export default function ReleaseSessionPercentageChart() {

return (
<InsightsAreaChartWidget
title={t('Release Adoption')}
title={CHART_TITLES.ReleaseSessionPercentageChart}
interactiveTitle={() => (
<ChartSelectionTitle title={CHART_TITLES.ReleaseSessionPercentageChart} />
)}
height={SESSION_HEALTH_CHART_HEIGHT}
description={t(
'The percentage of total sessions that each release accounted for. The 5 most recent releases are shown.'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import ExternalLink from 'sentry/components/links/externalLink';
import {t, tct} from 'sentry/locale';
import {tct} from 'sentry/locale';
import {InsightsLineChartWidget} from 'sentry/views/insights/common/components/insightsLineChartWidget';
import ChartSelectionTitle from 'sentry/views/insights/sessions/components/chartSelectionTitle';
import useSessionHealthBreakdown from 'sentry/views/insights/sessions/queries/useSessionHealthBreakdown';
import {CHART_TITLES} from 'sentry/views/insights/sessions/settings';
import {SESSION_HEALTH_CHART_HEIGHT} from 'sentry/views/insights/sessions/utils/sessions';

export default function SessionHealthCountChart() {
Expand All @@ -16,8 +18,11 @@ export default function SessionHealthCountChart() {

return (
<InsightsLineChartWidget
title={CHART_TITLES.SessionHealthCountChart}
interactiveTitle={() => (
<ChartSelectionTitle title={CHART_TITLES.SessionHealthCountChart} />
)}
height={SESSION_HEALTH_CHART_HEIGHT}
title={t('Session Counts')}
description={tct(
'The count of sessions with each health status. See [link:session status].',
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import ExternalLink from 'sentry/components/links/externalLink';
import {t, tct} from 'sentry/locale';
import {tct} from 'sentry/locale';
import {InsightsAreaChartWidget} from 'sentry/views/insights/common/components/insightsAreaChartWidget';
import ChartSelectionTitle from 'sentry/views/insights/sessions/components/chartSelectionTitle';
import useSessionHealthBreakdown from 'sentry/views/insights/sessions/queries/useSessionHealthBreakdown';
import {CHART_TITLES} from 'sentry/views/insights/sessions/settings';
import {SESSION_HEALTH_CHART_HEIGHT} from 'sentry/views/insights/sessions/utils/sessions';

export default function SessionHealthRateChart() {
Expand All @@ -16,7 +18,10 @@ export default function SessionHealthRateChart() {

return (
<InsightsAreaChartWidget
title={t('Session Health')}
title={CHART_TITLES.SessionHealthRateChart}
interactiveTitle={() => (
<ChartSelectionTitle title={CHART_TITLES.SessionHealthRateChart} />
)}
height={SESSION_HEALTH_CHART_HEIGHT}
description={tct(
'The percent of sessions with each health status. See [link:session status].',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import ExternalLink from 'sentry/components/links/externalLink';
import {t, tct} from 'sentry/locale';
import {tct} from 'sentry/locale';
import {InsightsLineChartWidget} from 'sentry/views/insights/common/components/insightsLineChartWidget';
import ChartSelectionTitle from 'sentry/views/insights/sessions/components/chartSelectionTitle';
import useUserHealthBreakdown from 'sentry/views/insights/sessions/queries/useUserHealthBreakdown';
import {CHART_TITLES} from 'sentry/views/insights/sessions/settings';
import {SESSION_HEALTH_CHART_HEIGHT} from 'sentry/views/insights/sessions/utils/sessions';

export default function UserHealthCountChart() {
Expand All @@ -16,8 +18,11 @@ export default function UserHealthCountChart() {

return (
<InsightsLineChartWidget
title={CHART_TITLES.UserHealthCountChart}
interactiveTitle={() => (
<ChartSelectionTitle title={CHART_TITLES.UserHealthCountChart} />
)}
height={SESSION_HEALTH_CHART_HEIGHT}
title={t('User Counts')}
description={tct(
'Breakdown of total [linkUsers:users], grouped by [linkStatus:health status].',
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import ExternalLink from 'sentry/components/links/externalLink';
import {t, tct} from 'sentry/locale';
import {tct} from 'sentry/locale';
import {InsightsAreaChartWidget} from 'sentry/views/insights/common/components/insightsAreaChartWidget';
import ChartSelectionTitle from 'sentry/views/insights/sessions/components/chartSelectionTitle';
import useUserHealthBreakdown from 'sentry/views/insights/sessions/queries/useUserHealthBreakdown';
import {CHART_TITLES} from 'sentry/views/insights/sessions/settings';
import {SESSION_HEALTH_CHART_HEIGHT} from 'sentry/views/insights/sessions/utils/sessions';

export default function UserHealthRateChart() {
Expand All @@ -16,7 +18,10 @@ export default function UserHealthRateChart() {

return (
<InsightsAreaChartWidget
title={t('User Health')}
title={CHART_TITLES.UserHealthRateChart}
interactiveTitle={() => (
<ChartSelectionTitle title={CHART_TITLES.UserHealthRateChart} />
)}
height={SESSION_HEALTH_CHART_HEIGHT}
description={tct(
'The percent of [linkUsers:users] with each [linkStatus:health status].',
Expand Down
Loading
Loading