diff --git a/static/app/routes.tsx b/static/app/routes.tsx index 43c5bc172e41bd..14a5b77d7fddb2 100644 --- a/static/app/routes.tsx +++ b/static/app/routes.tsx @@ -2141,7 +2141,6 @@ function buildRoutes(): RouteObject[] { { path: 'vitaldetail/', component: make(() => import('sentry/views/performance/vitalDetail')), - deprecatedRouteProps: true, }, traceView, ...insightsRedirectObjects, diff --git a/static/app/views/performance/vitalDetail/index.spec.tsx b/static/app/views/performance/vitalDetail/index.spec.tsx index 4ae581ca5b3da3..279fdebc3328b5 100644 --- a/static/app/views/performance/vitalDetail/index.spec.tsx +++ b/static/app/views/performance/vitalDetail/index.spec.tsx @@ -13,45 +13,25 @@ import {textWithMarkupMatcher} from 'sentry-test/utils'; import ProjectsStore from 'sentry/stores/projectsStore'; import TeamStore from 'sentry/stores/teamStore'; -import type {InjectedRouter} from 'sentry/types/legacyReactRouter'; import {WebVital} from 'sentry/utils/fields'; import {Browser} from 'sentry/utils/performance/vitals/constants'; import {DEFAULT_STATS_PERIOD} from 'sentry/views/performance/data'; import VitalDetail from 'sentry/views/performance/vitalDetail'; import {vitalSupportedBrowsers} from 'sentry/views/performance/vitalDetail/utils'; +const mockNavigate = jest.fn(); +jest.mock('sentry/utils/useNavigate', () => ({ + useNavigate: () => mockNavigate, +})); + const organization = OrganizationFixture({ features: ['discover-basic', 'performance-view'], }); -const { - organization: org, - project, - router, -} = initializeOrg({ +const {organization: org, project} = initializeOrg({ organization, - router: { - location: { - query: { - project: '1', - }, - }, - }, }); -function TestComponent(props: {router?: InjectedRouter} = {}) { - return ( - - ); -} - const testSupportedBrowserRendering = (webVital: WebVital) => { Object.values(Browser).forEach(browser => { const browserElement = screen.getByText(browser); @@ -224,10 +204,16 @@ describe('Performance > VitalDetail', () => { }); it('renders basic UI elements', async () => { - render(, { - router, + render(, { organization: org, - deprecatedRouterMocks: true, + initialRouterConfig: { + location: { + pathname: '/performance/vitaldetail/', + query: { + project: '1', + }, + }, + }, }); // It shows a search bar @@ -252,25 +238,34 @@ describe('Performance > VitalDetail', () => { }); it('triggers a navigation on search', async () => { - render(, { - router, + render(, { organization: org, - deprecatedRouterMocks: true, + initialRouterConfig: { + location: { + pathname: '/performance/vitaldetail/', + query: { + project: '1', + }, + }, + }, }); - // Fill out the search box, and submit it. + // Clear any navigation calls from initialization + mockNavigate.mockClear(); + + // Fill out the search box, and submit it await userEvent.click( await screen.findByPlaceholderText('Search for events, users, tags, and more') ); await userEvent.paste('user.email:uhoh*'); - // Check the navigation. + // Check the navigation await waitFor(() => { - expect(router.push).toHaveBeenCalledTimes(1); + expect(mockNavigate).toHaveBeenCalledTimes(1); }); - expect(router.push).toHaveBeenCalledWith({ - pathname: undefined, + expect(mockNavigate).toHaveBeenCalledWith({ + pathname: '/performance/vitaldetail/', query: { project: '1', statsPeriod: '14d', @@ -280,20 +275,16 @@ describe('Performance > VitalDetail', () => { }); it('applies conditions when linking to transaction summary', async () => { - const newRouter = { - ...router, - location: { - ...router.location, - query: { - query: 'sometag:value', + render(, { + organization: org, + initialRouterConfig: { + location: { + pathname: '/performance/vitaldetail/', + query: { + query: 'sometag:value', + }, }, }, - }; - - render(, { - router: newRouter, - organization: org, - deprecatedRouterMocks: true, }); expect( @@ -304,7 +295,7 @@ describe('Performance > VitalDetail', () => { await screen.findByLabelText('See transaction summary of the transaction something') ); - expect(newRouter.push).toHaveBeenCalledWith({ + expect(mockNavigate).toHaveBeenCalledWith({ pathname: `/organizations/${organization.slug}/insights/summary/`, query: { transaction: 'something', @@ -325,30 +316,29 @@ describe('Performance > VitalDetail', () => { }); it('check CLS', async () => { - const newRouter = { - ...router, - location: { - ...router.location, - query: { - query: 'anothertag:value', - vitalName: 'measurements.cls', + render(, { + organization: org, + initialRouterConfig: { + location: { + pathname: '/performance/vitaldetail/', + query: { + query: 'anothertag:value', + vitalName: 'measurements.cls', + }, }, }, - }; - - render(, { - router: newRouter, - organization: org, - deprecatedRouterMocks: true, }); expect(await screen.findByText('Cumulative Layout Shift')).toBeInTheDocument(); + // Check cells are not in ms + expect(screen.getByText('0.215').closest('td')).toBeInTheDocument(); + await userEvent.click( await screen.findByLabelText('See transaction summary of the transaction something') ); - expect(newRouter.push).toHaveBeenCalledWith({ + expect(mockNavigate).toHaveBeenCalledWith({ pathname: `/organizations/${organization.slug}/insights/summary/`, query: { transaction: 'something', @@ -366,29 +356,25 @@ describe('Performance > VitalDetail', () => { trendColumn: undefined, }, }); - - // Check cells are not in ms - expect(screen.getByText('0.215').closest('td')).toBeInTheDocument(); }); it('can switch vitals with dropdown menu', async () => { - const newRouter = { - ...router, - location: { - ...router.location, - query: { - project: 1, - query: 'tag:value', + render(, { + organization: org, + initialRouterConfig: { + location: { + pathname: '/performance/vitaldetail/', + query: { + project: '1', + query: 'tag:value', + }, }, }, - }; - - render(, { - router: newRouter, - organization: org, - deprecatedRouterMocks: true, }); + // Clear any navigation calls from initialization + mockNavigate.mockClear(); + const button = screen.getByRole('button', {name: /web vitals: lcp/i}); expect(button).toBeInTheDocument(); await userEvent.click(button); @@ -397,11 +383,11 @@ describe('Performance > VitalDetail', () => { expect(menuItem).toBeInTheDocument(); await userEvent.click(menuItem); - expect(newRouter.push).toHaveBeenCalledTimes(1); - expect(newRouter.push).toHaveBeenCalledWith({ - pathname: undefined, + expect(mockNavigate).toHaveBeenCalledTimes(1); + expect(mockNavigate).toHaveBeenCalledWith({ + pathname: '/performance/vitaldetail/', query: { - project: 1, + project: '1', query: 'tag:value', vitalName: 'measurements.fcp', }, @@ -409,10 +395,13 @@ describe('Performance > VitalDetail', () => { }); it('renders LCP vital correctly', async () => { - render(, { - router, + render(, { organization: org, - deprecatedRouterMocks: true, + initialRouterConfig: { + location: { + pathname: '/performance/vitaldetail/', + }, + }, }); expect(await screen.findByText('Largest Contentful Paint')).toBeInTheDocument(); @@ -425,10 +414,13 @@ describe('Performance > VitalDetail', () => { }); it('correctly renders which browsers support LCP', async () => { - render(, { - router, + render(, { organization: org, - deprecatedRouterMocks: true, + initialRouterConfig: { + location: { + pathname: '/performance/vitaldetail/', + }, + }, }); expect(await screen.findAllByText(/Largest Contentful Paint/)).toHaveLength(2); @@ -436,20 +428,16 @@ describe('Performance > VitalDetail', () => { }); it('correctly renders which browsers support CLS', async () => { - const newRouter = { - ...router, - location: { - ...router.location, - query: { - vitalName: 'measurements.cls', + render(, { + organization: org, + initialRouterConfig: { + location: { + pathname: '/performance/vitaldetail/', + query: { + vitalName: 'measurements.cls', + }, }, }, - }; - - render(, { - router, - organization: org, - deprecatedRouterMocks: true, }); expect(await screen.findAllByText(/Cumulative Layout Shift/)).toHaveLength(2); @@ -457,25 +445,21 @@ describe('Performance > VitalDetail', () => { }); it('correctly renders which browsers support FCP', async () => { - const newRouter = { - ...router, - location: { - ...router.location, - query: { - vitalName: 'measurements.fcp', - }, - }, - }; - MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/events/`, body: [], }); - render(, { - router, + render(, { organization: org, - deprecatedRouterMocks: true, + initialRouterConfig: { + location: { + pathname: '/performance/vitaldetail/', + query: { + vitalName: 'measurements.fcp', + }, + }, + }, }); expect(await screen.findAllByText(/First Contentful Paint/)).toHaveLength(2); @@ -483,25 +467,21 @@ describe('Performance > VitalDetail', () => { }); it('correctly renders which browsers support FID', async () => { - const newRouter = { - ...router, - location: { - ...router.location, - query: { - vitalName: 'measurements.fid', - }, - }, - }; - MockApiClient.addMockResponse({ url: `/organizations/${organization.slug}/events/`, body: [], }); - render(, { - router, + render(, { organization: org, - deprecatedRouterMocks: true, + initialRouterConfig: { + location: { + pathname: '/performance/vitaldetail/', + query: { + vitalName: 'measurements.fid', + }, + }, + }, }); expect(await screen.findAllByText(/First Input Delay/)).toHaveLength(2); diff --git a/static/app/views/performance/vitalDetail/index.tsx b/static/app/views/performance/vitalDetail/index.tsx index a22add6eca791a..736f688ab8f8b5 100644 --- a/static/app/views/performance/vitalDetail/index.tsx +++ b/static/app/views/performance/vitalDetail/index.tsx @@ -7,9 +7,6 @@ import PageFiltersContainer from 'sentry/components/organizations/pageFilters/co import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle'; import {t} from 'sentry/locale'; import type {PageFilters} from 'sentry/types/core'; -import type {RouteComponentProps} from 'sentry/types/legacyReactRouter'; -import type {Organization} from 'sentry/types/organization'; -import type {Project} from 'sentry/types/project'; import {WebVital} from 'sentry/utils/fields'; import {PerformanceEventViewProvider} from 'sentry/utils/performance/contexts/performanceEventViewContext'; import {decodeScalar} from 'sentry/utils/queryString'; @@ -17,10 +14,11 @@ import useRouteAnalyticsEventNames from 'sentry/utils/routeAnalytics/useRouteAna import useRouteAnalyticsParams from 'sentry/utils/routeAnalytics/useRouteAnalyticsParams'; import normalizeUrl from 'sentry/utils/url/normalizeUrl'; import useApi from 'sentry/utils/useApi'; +import {useLocation} from 'sentry/utils/useLocation'; import {useNavigate} from 'sentry/utils/useNavigate'; -import withOrganization from 'sentry/utils/withOrganization'; -import withPageFilters from 'sentry/utils/withPageFilters'; -import withProjects from 'sentry/utils/withProjects'; +import useOrganization from 'sentry/utils/useOrganization'; +import usePageFilters from 'sentry/utils/usePageFilters'; +import useProjects from 'sentry/utils/useProjects'; import {generatePerformanceVitalDetailView} from 'sentry/views/performance/data'; import { addRoutePerformanceContext, @@ -31,16 +29,13 @@ import { import VitalDetailContent from './vitalDetailContent'; -type Props = RouteComponentProps & { - loadingProjects: boolean; - organization: Organization; - projects: Project[]; - selection: PageFilters; -}; - -function VitalDetail({organization, selection, location, projects, router}: Props) { +export default function VitalDetail() { const api = useApi(); const navigate = useNavigate(); + const location = useLocation(); + const organization = useOrganization(); + const {selection} = usePageFilters(); + const {projects} = useProjects(); useRouteAnalyticsEventNames( 'performance_views.vital_detail.view', @@ -105,7 +100,6 @@ function VitalDetail({organization, selection, location, projects, router}: Prop location={location} organization={organization} eventView={eventView} - router={router} vitalName={vitalName || WebVital.LCP} api={api} /> @@ -115,5 +109,3 @@ function VitalDetail({organization, selection, location, projects, router}: Prop ); } - -export default withPageFilters(withProjects(withOrganization(VitalDetail))); diff --git a/static/app/views/performance/vitalDetail/vitalDetailContent.tsx b/static/app/views/performance/vitalDetail/vitalDetailContent.tsx index 7c286c912ead47..a49206da3dda21 100644 --- a/static/app/views/performance/vitalDetail/vitalDetailContent.tsx +++ b/static/app/views/performance/vitalDetail/vitalDetailContent.tsx @@ -24,11 +24,8 @@ import {TransactionSearchQueryBuilder} from 'sentry/components/performance/trans import {IconCheckmark, IconClose} from 'sentry/icons'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; -import type {InjectedRouter} from 'sentry/types/legacyReactRouter'; import type {Organization} from 'sentry/types/organization'; -import type {Project} from 'sentry/types/project'; import {trackAnalytics} from 'sentry/utils/analytics'; -import {browserHistory} from 'sentry/utils/browserHistory'; import {getUtcToLocalDateObject} from 'sentry/utils/dates'; import type EventView from 'sentry/utils/discover/eventView'; import {WebVital} from 'sentry/utils/fields'; @@ -36,7 +33,8 @@ import {Browser} from 'sentry/utils/performance/vitals/constants'; import {decodeScalar} from 'sentry/utils/queryString'; import Teams from 'sentry/utils/teams'; import {MutableSearch} from 'sentry/utils/tokenizeSearch'; -import withProjects from 'sentry/utils/withProjects'; +import {useNavigate} from 'sentry/utils/useNavigate'; +import useProjects from 'sentry/utils/useProjects'; import {deprecateTransactionAlerts} from 'sentry/views/insights/common/utils/hasEAPAlerts'; import Breadcrumb from 'sentry/views/performance/breadcrumb'; import {getTransactionSearchQuery} from 'sentry/views/performance/utils'; @@ -59,8 +57,6 @@ type Props = { eventView: EventView; location: Location; organization: Organization; - projects: Project[]; - router: InjectedRouter; vitalName: WebVital; }; @@ -71,8 +67,10 @@ function getSummaryConditions(query: string) { return parsed.formatString(); } -function VitalDetailContent(props: Props) { +export default function VitalDetailContent(props: Props) { const theme = useTheme(); + const navigate = useNavigate(); + const {projects} = useProjects(); function handleSearch(query: string) { const {location} = props; @@ -84,14 +82,14 @@ function VitalDetailContent(props: Props) { // do not propagate pagination when making a new search const searchQueryParams = omit(queryParams, 'cursor'); - browserHistory.push({ + navigate({ pathname: location.pathname, query: searchQueryParams, }); } function renderCreateAlertButton() { - const {eventView, organization, projects, vitalName} = props; + const {eventView, organization, vitalName} = props; return ( p.theme.fontSize.md}; margin-bottom: ${space(3)};