diff --git a/static/app/utils/performance/contexts/performanceDisplayContext.tsx b/static/app/utils/performance/contexts/performanceDisplayContext.tsx new file mode 100644 index 00000000000000..75159ca3143665 --- /dev/null +++ b/static/app/utils/performance/contexts/performanceDisplayContext.tsx @@ -0,0 +1,18 @@ +import {PROJECT_PERFORMANCE_TYPE} from 'app/views/performance/utils'; + +import {createDefinedContext} from './utils'; + +type useCurrentPerformanceView = { + performanceType: PROJECT_PERFORMANCE_TYPE; +}; + +const [PerformanceDisplayProvider, _usePerformanceDisplayType] = + createDefinedContext({ + name: 'CurrentPerformanceViewContext', + }); + +export {PerformanceDisplayProvider}; + +export function usePerformanceDisplayType(): PROJECT_PERFORMANCE_TYPE { + return _usePerformanceDisplayType().performanceType; +} diff --git a/static/app/views/performance/landing/views/allTransactionsView.tsx b/static/app/views/performance/landing/views/allTransactionsView.tsx index 89c0bc1fd54daa..635ac7c8213d53 100644 --- a/static/app/views/performance/landing/views/allTransactionsView.tsx +++ b/static/app/views/performance/landing/views/allTransactionsView.tsx @@ -1,6 +1,8 @@ import {usePageError} from 'app/utils/performance/contexts/pageError'; +import {PerformanceDisplayProvider} from 'app/utils/performance/contexts/performanceDisplayContext'; import Table from '../../table'; +import {PROJECT_PERFORMANCE_TYPE} from '../../utils'; import {DoubleChartRow, TripleChartRow} from '../widgets/components/widgetChartRow'; import {PerformanceWidgetSetting} from '../widgets/widgetDefinitions'; @@ -8,29 +10,31 @@ import {BasePerformanceViewProps} from './types'; export function AllTransactionsView(props: BasePerformanceViewProps) { return ( -
- - - - + +
+ + +
+ + ); } diff --git a/static/app/views/performance/landing/views/frontendOtherView.tsx b/static/app/views/performance/landing/views/frontendOtherView.tsx index dbc14c72e360bf..d6a23a18205f8c 100644 --- a/static/app/views/performance/landing/views/frontendOtherView.tsx +++ b/static/app/views/performance/landing/views/frontendOtherView.tsx @@ -1,18 +1,43 @@ import {usePageError} from 'app/utils/performance/contexts/pageError'; +import {PerformanceDisplayProvider} from 'app/utils/performance/contexts/performanceDisplayContext'; import Table from '../../table'; +import {PROJECT_PERFORMANCE_TYPE} from '../../utils'; import {FRONTEND_OTHER_COLUMN_TITLES} from '../data'; +import {DoubleChartRow, TripleChartRow} from '../widgets/components/widgetChartRow'; +import {PerformanceWidgetSetting} from '../widgets/widgetDefinitions'; import {BasePerformanceViewProps} from './types'; export function FrontendOtherView(props: BasePerformanceViewProps) { return ( -
-
- + +
+ + +
+ + ); } diff --git a/static/app/views/performance/landing/views/frontendPageloadView.tsx b/static/app/views/performance/landing/views/frontendPageloadView.tsx index 7a5bbc66a03f09..de0954197606ef 100644 --- a/static/app/views/performance/landing/views/frontendPageloadView.tsx +++ b/static/app/views/performance/landing/views/frontendPageloadView.tsx @@ -1,6 +1,8 @@ import {usePageError} from 'app/utils/performance/contexts/pageError'; +import {PerformanceDisplayProvider} from 'app/utils/performance/contexts/performanceDisplayContext'; import Table from '../../table'; +import {PROJECT_PERFORMANCE_TYPE} from '../../utils'; import {FRONTEND_PAGELOAD_COLUMN_TITLES} from '../data'; import {DoubleChartRow, TripleChartRow} from '../widgets/components/widgetChartRow'; import {PerformanceWidgetSetting} from '../widgets/widgetDefinitions'; @@ -9,30 +11,34 @@ import {BasePerformanceViewProps} from './types'; export function FrontendPageloadView(props: BasePerformanceViewProps) { return ( -
- - -
- + +
+ + +
+ + ); } diff --git a/static/app/views/performance/landing/widgets/components/widgetContainer.tsx b/static/app/views/performance/landing/widgets/components/widgetContainer.tsx index 3a42b723291c2f..edd080c8242b2f 100644 --- a/static/app/views/performance/landing/widgets/components/widgetContainer.tsx +++ b/static/app/views/performance/landing/widgets/components/widgetContainer.tsx @@ -4,9 +4,11 @@ import styled from '@emotion/styled'; import MenuItem from 'app/components/menuItem'; import {Organization} from 'app/types'; import localStorage from 'app/utils/localStorage'; +import {usePerformanceDisplayType} from 'app/utils/performance/contexts/performanceDisplayContext'; import {useOrganization} from 'app/utils/useOrganization'; import withOrganization from 'app/utils/withOrganization'; import ContextMenu from 'app/views/dashboardsV2/contextMenu'; +import {PROJECT_PERFORMANCE_TYPE} from 'app/views/performance/utils'; import {GenericPerformanceWidgetDataType} from '../types'; import {PerformanceWidgetSetting, WIDGET_DEFINITIONS} from '../widgetDefinitions'; @@ -29,20 +31,38 @@ type Props = { } & ChartRowProps; // Use local storage for chart settings for now. -const getContainerLocalStorageKey = (index: number, height: number) => - `landing-chart-container#${height}#${index}`; +const getContainerLocalStorageObjectKey = 'landing-chart-container'; +const getContainerKey = ( + index: number, + performanceType: PROJECT_PERFORMANCE_TYPE, + height: number +) => `landing-chart-container#${performanceType}#${height}#${index}`; + +function getWidgetStorageObject() { + const localObject = JSON.parse( + localStorage.getItem(getContainerLocalStorageObjectKey) || '{}' + ); + return localObject; +} + +function setWidgetStorageObject(localObject: Record) { + localStorage.setItem(getContainerLocalStorageObjectKey, JSON.stringify(localObject)); +} const getChartSetting = ( index: number, height: number, + performanceType: PROJECT_PERFORMANCE_TYPE, defaultType: PerformanceWidgetSetting, forceDefaultChartSetting?: boolean // Used for testing. ): PerformanceWidgetSetting => { if (forceDefaultChartSetting) { return defaultType; } - const key = getContainerLocalStorageKey(index, height); - const value = localStorage.getItem(key); + const key = getContainerKey(index, performanceType, height); + const localObject = getWidgetStorageObject(); + const value = localObject?.[key]; + if ( value && Object.values(PerformanceWidgetSetting).includes(value as PerformanceWidgetSetting) @@ -55,25 +75,36 @@ const getChartSetting = ( const _setChartSetting = ( index: number, height: number, + performanceType: PROJECT_PERFORMANCE_TYPE, setting: PerformanceWidgetSetting ) => { - const key = getContainerLocalStorageKey(index, height); - localStorage.setItem(key, setting); + const key = getContainerKey(index, performanceType, height); + const localObject = getWidgetStorageObject(); + localObject[key] = setting; + + setWidgetStorageObject(localObject); }; const _WidgetContainer = (props: Props) => { - const {organization, index, chartHeight, ...rest} = props; - const _chartSetting = getChartSetting( + const {organization, index, chartHeight, allowedCharts, ...rest} = props; + const performanceType = usePerformanceDisplayType(); + let _chartSetting = getChartSetting( index, chartHeight, + performanceType, rest.defaultChartSetting, rest.forceDefaultChartSetting ); + + if (!allowedCharts.includes(_chartSetting)) { + _chartSetting = rest.defaultChartSetting; + } + const [chartSetting, setChartSettingState] = useState(_chartSetting); const setChartSetting = (setting: PerformanceWidgetSetting) => { if (!props.forceDefaultChartSetting) { - _setChartSetting(index, chartHeight, setting); + _setChartSetting(index, chartHeight, performanceType, setting); } setChartSettingState(setting); }; diff --git a/static/app/views/performance/landing/widgets/widgetDefinitions.tsx b/static/app/views/performance/landing/widgets/widgetDefinitions.tsx index c181effe5365f0..19a5ea8e0c071e 100644 --- a/static/app/views/performance/landing/widgets/widgetDefinitions.tsx +++ b/static/app/views/performance/landing/widgets/widgetDefinitions.tsx @@ -17,6 +17,7 @@ export interface BaseChartSetting { } export enum PerformanceWidgetSetting { + DURATION_HISTOGRAM = 'duration_histogram', LCP_HISTOGRAM = 'lcp_histogram', FCP_HISTOGRAM = 'fcp_histogram', FID_HISTOGRAM = 'fid_histogram', @@ -43,6 +44,13 @@ export const WIDGET_DEFINITIONS: ({ }: { organization: Organization; }) => ({ + [PerformanceWidgetSetting.DURATION_HISTOGRAM]: { + title: t('Duration Distribution'), + titleTooltip: getTermHelp(organization, PERFORMANCE_TERM.DURATION_DISTRIBUTION), + fields: ['transaction.duration'], + dataType: GenericPerformanceWidgetDataType.histogram, + chartColor: WIDGET_PALETTE[5], + }, [PerformanceWidgetSetting.LCP_HISTOGRAM]: { title: t('LCP Distribution'), titleTooltip: getTermHelp(organization, PERFORMANCE_TERM.DURATION_DISTRIBUTION), diff --git a/tests/js/spec/views/performance/landing/index.spec.tsx b/tests/js/spec/views/performance/landing/index.spec.tsx index c6ed36484f4406..c027103538ca5d 100644 --- a/tests/js/spec/views/performance/landing/index.spec.tsx +++ b/tests/js/spec/views/performance/landing/index.spec.tsx @@ -108,11 +108,11 @@ describe('Performance > Landing > Index', function () { const titles = wrapper.find('div[data-test-id="performance-widget-title"]'); expect(titles).toHaveLength(5); - expect(titles.at(0).text()).toEqual('Transactions Per Minute'); - expect(titles.at(1).text()).toEqual('Most Related Errors'); - expect(titles.at(2).text()).toEqual('p75 LCP'); - expect(titles.at(3).text()).toEqual('LCP Distribution'); - expect(titles.at(4).text()).toEqual('FCP Distribution'); + expect(titles.at(0).text()).toEqual('p75 LCP'); + expect(titles.at(1).text()).toEqual('LCP Distribution'); + expect(titles.at(2).text()).toEqual('FCP Distribution'); + expect(titles.at(3).text()).toEqual('Most Related Errors'); + expect(titles.at(4).text()).toEqual('Most Related Issues'); }); it('renders frontend other view', async function () { diff --git a/tests/js/spec/views/performance/landing/widgets/widgetContainer.spec.jsx b/tests/js/spec/views/performance/landing/widgets/widgetContainer.spec.jsx index b69bf18e0bfdf6..1d0bbb0e993fdd 100644 --- a/tests/js/spec/views/performance/landing/widgets/widgetContainer.spec.jsx +++ b/tests/js/spec/views/performance/landing/widgets/widgetContainer.spec.jsx @@ -1,24 +1,28 @@ import {mountWithTheme} from 'sentry-test/enzyme'; import {initializeData} from 'sentry-test/performance/initializePerformanceData'; +import {PerformanceDisplayProvider} from 'app/utils/performance/contexts/performanceDisplayContext'; import {OrganizationContext} from 'app/views/organizationContext'; import WidgetContainer from 'app/views/performance/landing/widgets/components/widgetContainer'; import {PerformanceWidgetSetting} from 'app/views/performance/landing/widgets/widgetDefinitions'; +import {PROJECT_PERFORMANCE_TYPE} from 'app/views/performance/utils'; const WrappedComponent = ({data, ...rest}) => { return ( - - - + + + + + ); };