Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 7 additions & 19 deletions static/app/components/core/link/link.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import {
Link as RouterLink,
type LinkProps as ReactRouterLinkProps,
} from 'react-router-dom';
import {type LinkProps as ReactRouterLinkProps} from 'react-router-dom';
import isPropValid from '@emotion/is-prop-valid';
import {css, type Theme} from '@emotion/react';
import styled from '@emotion/styled';
import type {LocationDescriptor} from 'history';

import {locationDescriptorToTo} from 'sentry/utils/reactRouter6Compat/location';
import normalizeUrl from 'sentry/utils/url/normalizeUrl';
import {useLocation} from 'sentry/utils/useLocation';
import {useLinkBehavior} from './linkBehaviorContext';

export interface LinkProps
extends React.RefAttributes<HTMLAnchorElement>,
Expand Down Expand Up @@ -61,26 +56,19 @@ const getLinkStyles = ({
`;

const Anchor = styled('a', {
shouldForwardProp: prop =>
typeof prop === 'string' && isPropValid(prop) && prop !== 'disabled',
shouldForwardProp: prop => isPropValid(prop) && prop !== 'disabled',
})<{disabled?: LinkProps['disabled']}>`
${getLinkStyles}
`;

/**
* A context-aware version of Link (from react-router) that falls
* back to <a> if there is no router present
*/
export const Link = styled(({disabled, to, ...props}: LinkProps) => {
const location = useLocation();
Comment on lines -70 to -75
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@evanpurkhiser do you maybe know why we have this “fall back to <a> if there is no router present. When would that trigger? Does useLocation ever return null / undefined?

I would honestly love to just remove that if possible

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this actually dies at runtime so why do we have it 😅 ?

router.js:422 Uncaught Error: useLocation() may be used only in the context of a <Router> component.
    at invariant (router.js:422:15)
    at useLocation (index.js:204:104)
    at useLocation (useLocation.tsx:25:90)
    at SentryLinkBehaviorProvider (link.tsx:25:91)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is probably pretty legacy. This might have been needed at one point for one of these things

const COMPONENT_MAP = {
[SentryInitRenderReactComponent.INDICATORS]: () =>
import(/* webpackChunkName: "Indicators" */ 'sentry/components/indicators'),
[SentryInitRenderReactComponent.SYSTEM_ALERTS]: () =>
import(/* webpackChunkName: "SystemAlerts" */ 'sentry/views/app/systemAlerts'),
[SentryInitRenderReactComponent.SETUP_WIZARD]: () =>
import(/* webpackChunkName: "SetupWizard" */ 'sentry/views/setupWizard'),
[SentryInitRenderReactComponent.WEB_AUTHN_ASSSERT]: () =>
import(
/* webpackChunkName: "WebAuthnAssert" */ 'sentry/components/webAuthn/webAuthnAssert'
),
[SentryInitRenderReactComponent.SU_STAFF_ACCESS_FORM]: () =>
import(
/* webpackChunkName: "SuperuserStaffAccessForm" */ 'sentry/components/superuserStaffAccessForm'
),
};
, components that get rendered in Django views

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I doubt we need this yeah.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay, I think this is what we want then: f1625c5

the design-system uses ’a’ per default unless specified by the context, which both the sentry app and our tests are doing.

export const Link = styled((props: LinkProps) => {
const {Component, behavior} = useLinkBehavior(props);

if (disabled || !location) {
if (props.disabled || Component === 'a') {
return <Anchor {...props} />;
}

return (
<RouterLink to={locationDescriptorToTo(normalizeUrl(to, location))} {...props} />
);
return <Component {...behavior()} />;
})`
${getLinkStyles}
`;
Expand Down
19 changes: 19 additions & 0 deletions static/app/components/core/link/linkBehaviorContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {createContext, useContext, type FunctionComponent} from 'react';

import type {LinkProps} from './link';

const LinkBehaviorContext = createContext<{
behavior: (props: LinkProps) => LinkProps;
component: FunctionComponent<LinkProps> | 'a';
}>({
component: 'a',
behavior: props => props,
});

export const LinkBehaviorContextProvider = LinkBehaviorContext.Provider;

export const useLinkBehavior = (props: LinkProps) => {
const {component, behavior} = useContext(LinkBehaviorContext);

return {Component: component, behavior: () => behavior(props)};
};
13 changes: 5 additions & 8 deletions static/app/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {FrontendVersionProvider} from 'sentry/components/frontendVersionContext'
import {ThemeAndStyleProvider} from 'sentry/components/themeAndStyleProvider';
import {SENTRY_RELEASE_VERSION, USE_REACT_QUERY_DEVTOOL} from 'sentry/constants';
import {routes} from 'sentry/routes';
import {SentryTrackingProvider} from 'sentry/tracking';
import {DANGEROUS_SET_REACT_ROUTER_6_HISTORY} from 'sentry/utils/browserHistory';

function buildRouter() {
Expand All @@ -28,13 +27,11 @@ function Main() {
<AppQueryClientProvider>
<FrontendVersionProvider releaseVersion={SENTRY_RELEASE_VERSION ?? null}>
<ThemeAndStyleProvider>
<SentryTrackingProvider>
<NuqsAdapter defaultOptions={{shallow: false}}>
<CommandPaletteProvider>
<RouterProvider router={router} />
</CommandPaletteProvider>
</NuqsAdapter>
</SentryTrackingProvider>
<NuqsAdapter defaultOptions={{shallow: false}}>
<CommandPaletteProvider>
<RouterProvider router={router} />
</CommandPaletteProvider>
</NuqsAdapter>
{USE_REACT_QUERY_DEVTOOL && (
<ReactQueryDevtools initialIsOpen={false} buttonPosition="bottom-left" />
)}
Expand Down
9 changes: 8 additions & 1 deletion static/app/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import memoize from 'lodash/memoize';

import {EXPERIMENTAL_SPA} from 'sentry/constants';
import {t} from 'sentry/locale';
import {ScrapsProviders} from 'sentry/scrapsProviders';
import HookStore from 'sentry/stores/hookStore';
import type {HookName} from 'sentry/types/hooks';
import errorHandler from 'sentry/utils/errorHandler';
Expand Down Expand Up @@ -3066,7 +3067,13 @@ function buildRoutes(): RouteObject[] {
};

const appRoutes: SentryRouteObject = {
component: ProvideAriaRouter,
component: ({children}: {children: React.ReactNode}) => {
return (
<ProvideAriaRouter>
<ScrapsProviders>{children}</ScrapsProviders>
</ProvideAriaRouter>
);
},
deprecatedRouteProps: true,
children: [
experimentalSpaRoutes,
Expand Down
10 changes: 10 additions & 0 deletions static/app/scrapsProviders/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import {SentryLinkBehaviorProvider} from './link';
import {SentryTrackingProvider} from './tracking';

export function ScrapsProviders({children}: {children: React.ReactNode}) {
return (
<SentryTrackingProvider>
<SentryLinkBehaviorProvider>{children}</SentryLinkBehaviorProvider>
</SentryTrackingProvider>
);
}
34 changes: 34 additions & 0 deletions static/app/scrapsProviders/link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {useMemo} from 'react';
import {Link as RouterLink} from 'react-router-dom';

import type {LinkProps} from '@sentry/scraps/link';
import {LinkBehaviorContextProvider} from '@sentry/scraps/link/linkBehaviorContext';

import {locationDescriptorToTo} from 'sentry/utils/reactRouter6Compat/location';
import normalizeUrl from 'sentry/utils/url/normalizeUrl';
import {useLocation} from 'sentry/utils/useLocation';

export function SentryLinkBehaviorProvider({children}: {children: React.ReactNode}) {
const location = useLocation();

return (
<LinkBehaviorContextProvider
value={useMemo(
() => ({
component: RouterLink,
behavior: ({to, ...props}: LinkProps) => {
const normalizedTo = locationDescriptorToTo(normalizeUrl(to, location));

return {
to: normalizedTo,
...props,
};
},
}),
[location]
)}
>
{children}
</LinkBehaviorContextProvider>
);
}
File renamed without changes.
12 changes: 7 additions & 5 deletions tests/js/sentry-test/reactTestingLibrary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ import * as qs from 'query-string';
import {LocationFixture} from 'sentry-fixture/locationFixture';
import {ThemeFixture} from 'sentry-fixture/theme';

import {makeTestQueryClient} from 'sentry-test/queryClient';

import {CommandPaletteProvider} from 'sentry/components/commandPalette/context';
import {GlobalDrawer} from 'sentry/components/globalDrawer';
import GlobalModal from 'sentry/components/globalModal';
Expand All @@ -43,6 +41,8 @@ import {instrumentUserEvent} from '../instrumentedEnv/userEventIntegration';

import {initializeOrg} from './initializeOrg';
import {SentryNuqsTestingAdapter} from './nuqsTestingAdapter';
import {makeTestQueryClient} from './queryClient';
import {ScrapsTestingProviders} from './scrapsTestingProviders';

interface ProviderOptions {
/**
Expand Down Expand Up @@ -218,9 +218,11 @@ function makeAllTheProviders(options: ProviderOptions) {
<CacheProvider value={{...cache, compat: true}}>
<QueryClientProvider client={makeTestQueryClient()}>
<SentryNuqsTestingAdapter defaultOptions={{shallow: false}}>
<CommandPaletteProvider>
<ThemeProvider theme={ThemeFixture()}>{wrappedContent}</ThemeProvider>
</CommandPaletteProvider>
<ScrapsTestingProviders>
<CommandPaletteProvider>
<ThemeProvider theme={ThemeFixture()}>{wrappedContent}</ThemeProvider>
</CommandPaletteProvider>
</ScrapsTestingProviders>
</SentryNuqsTestingAdapter>
</QueryClientProvider>
</CacheProvider>
Expand Down
5 changes: 5 additions & 0 deletions tests/js/sentry-test/scrapsTestingProviders.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {SentryLinkBehaviorProvider} from 'sentry/scrapsProviders/link';

export function ScrapsTestingProviders({children}: {children: React.ReactNode}) {
return <SentryLinkBehaviorProvider>{children}</SentryLinkBehaviorProvider>;
}
Loading