Skip to content

Commit

Permalink
ref(issue-details): Rework the shared issue details context (#82838)
Browse files Browse the repository at this point in the history
Refactoring the shared issue details context. The only real change is
that the sidebar now always appears open when you land on the page
(rather than using local storage)
- Name change `useEventDetails` -> `useIssueDetails`
- Rather than a generic `updateState` action, now has a specific
`UpdateNavScrollMargin` action to avoid messing with other state keys
- Adds an `UpdateSidebarAction`instead of the local storage for sharing
the state.
  • Loading branch information
leeandher authored Jan 2, 2025
1 parent 6717367 commit 31b9b6a
Show file tree
Hide file tree
Showing 11 changed files with 108 additions and 73 deletions.
4 changes: 2 additions & 2 deletions static/app/components/events/interfaces/nativeFrame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import type {PlatformKey} from 'sentry/types/project';
import {defined} from 'sentry/utils';
import {useSyncedLocalStorageState} from 'sentry/utils/useSyncedLocalStorageState';
import withSentryAppComponents from 'sentry/utils/withSentryAppComponents';
import {SectionKey, useEventDetails} from 'sentry/views/issueDetails/streamline/context';
import {SectionKey, useIssueDetails} from 'sentry/views/issueDetails/streamline/context';
import {getFoldSectionKey} from 'sentry/views/issueDetails/streamline/foldSection';
import {useHasStreamlinedUI} from 'sentry/views/issueDetails/utils';

Expand Down Expand Up @@ -103,7 +103,7 @@ function NativeFrame({
}: Props) {
const traceEventDataSectionContext = useContext(TraceEventDataSectionContext);

const {sectionData} = useEventDetails();
const {sectionData} = useIssueDetails();
const debugSectionConfig = sectionData[SectionKey.DEBUGMETA];
const [_isCollapsed, setIsCollapsed] = useSyncedLocalStorageState(
getFoldSectionKey(SectionKey.DEBUGMETA),
Expand Down
91 changes: 58 additions & 33 deletions static/app/views/issueDetails/streamline/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,57 +86,77 @@ export interface SectionConfig {
initialCollapse?: boolean;
}

export interface EventDetailsContextType extends EventDetailsState {
dispatch: Dispatch<EventDetailsActions>;
export interface IssueDetailsContextType extends IssueDetailsState {
dispatch: Dispatch<IssueDetailsActions>;
}

export const EventDetailsContext = createContext<EventDetailsContextType>({
export const IssueDetailsContext = createContext<IssueDetailsContextType>({
sectionData: {},
isSidebarOpen: true,
navScrollMargin: 0,
eventCount: 0,
dispatch: () => {},
});

export function useEventDetails() {
return useContext(EventDetailsContext);
export function useIssueDetails() {
return useContext(IssueDetailsContext);
}

export interface EventDetailsState {
export interface IssueDetailsState {
/**
* Allows updating the event count based on the date/time/environment filters.
*/
eventCount: number;
/**
* Controls whether the sidebar is open.
*/
isSidebarOpen: boolean;
/**
* The margin to add to the 'Jump To' nav (accounts for the main app sidebar on small screen sizes).
*/
navScrollMargin: number;
/**
* Controls the state of each section.
*/
sectionData: {
[key in SectionKey]?: SectionConfig;
};
eventCount?: number;
navScrollMargin?: number;
}

type UpdateSectionAction = {
type UpdateEventSectionAction = {
key: SectionKey;
type: 'UPDATE_SECTION';
type: 'UPDATE_EVENT_SECTION';
config?: Partial<SectionConfig>;
};

type UpdateDetailsAction = {
type: 'UPDATE_DETAILS';
state?: Omit<EventDetailsState, 'sectionData'>;
type UpdateNavScrollMarginAction = {
margin: number;
type: 'UPDATE_NAV_SCROLL_MARGIN';
};

type UpdateEventCountAction = {
count: number;
type: 'UPDATE_EVENT_COUNT';
};

export type EventDetailsActions =
| UpdateSectionAction
| UpdateDetailsAction
| UpdateEventCountAction;
type UpdateSidebarAction = {
isOpen: boolean;
type: 'UPDATE_SIDEBAR_STATE';
};

export type IssueDetailsActions =
| UpdateEventSectionAction
| UpdateNavScrollMarginAction
| UpdateEventCountAction
| UpdateSidebarAction;

function updateSection(
state: EventDetailsState,
function updateEventSection(
state: IssueDetailsState,
sectionKey: SectionKey,
updatedConfig: Partial<SectionConfig>
): EventDetailsState {
): IssueDetailsState {
const existingConfig = state.sectionData[sectionKey] ?? {key: sectionKey};
const nextState: EventDetailsState = {
const nextState: IssueDetailsState = {
...state,
sectionData: {
...state.sectionData,
Expand All @@ -147,21 +167,26 @@ function updateSection(
}

/**
* If trying to use the current state of the event page, you likely want to use `useEventDetails`
* instead. This hook is just meant to create state for the provider.
* If trying to use the current state of the issue/event page, you likely want to use
* `useIssueDetails` instead. This hook is just meant to create state for the provider.
*/
export function useEventDetailsReducer() {
const initialState: EventDetailsState = {
export function useIssueDetailsReducer() {
const initialState: IssueDetailsState = {
sectionData: {},
isSidebarOpen: true,
eventCount: 0,
navScrollMargin: 0,
};

const reducer: Reducer<EventDetailsState, EventDetailsActions> = useCallback(
(state, action): EventDetailsState => {
const reducer: Reducer<IssueDetailsState, IssueDetailsActions> = useCallback(
(state, action): IssueDetailsState => {
switch (action.type) {
case 'UPDATE_SECTION':
return updateSection(state, action.key, action.config ?? {});
case 'UPDATE_DETAILS':
return {...state, ...action.state};
case 'UPDATE_SIDEBAR_STATE':
return {...state, isSidebarOpen: action.isOpen};
case 'UPDATE_NAV_SCROLL_MARGIN':
return {...state, navScrollMargin: action.margin};
case 'UPDATE_EVENT_SECTION':
return updateEventSection(state, action.key, action.config ?? {});
case 'UPDATE_EVENT_COUNT':
return {...state, eventCount: action.count};
default:
Expand All @@ -171,10 +196,10 @@ export function useEventDetailsReducer() {
[]
);

const [eventDetails, dispatch] = useReducer(reducer, initialState);
const [issueDetails, dispatch] = useReducer(reducer, initialState);

return {
eventDetails,
issueDetails,
dispatch,
};
}
9 changes: 4 additions & 5 deletions static/app/views/issueDetails/streamline/eventDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
EventDetailsContent,
type EventDetailsContentProps,
} from 'sentry/views/issueDetails/groupEventDetails/groupEventDetailsContent';
import {useEventDetails} from 'sentry/views/issueDetails/streamline/context';
import {useIssueDetails} from 'sentry/views/issueDetails/streamline/context';
import {EventMissingBanner} from 'sentry/views/issueDetails/streamline/eventMissingBanner';
import {EventTitle} from 'sentry/views/issueDetails/streamline/eventTitle';

Expand Down Expand Up @@ -45,18 +45,17 @@ function StickyEventNav({event, group}: {event: Event; group: Group}) {
const [nav, setNav] = useState<HTMLDivElement | null>(null);
const isStuck = useIsStuck(nav);
const isScreenMedium = useMedia(`(max-width: ${theme.breakpoints.medium})`);
const {dispatch} = useEventDetails();
const {dispatch} = useIssueDetails();

useLayoutEffect(() => {
if (!nav) {
return;
}

const navHeight = nav.offsetHeight ?? 0;
const sidebarHeight = isScreenMedium ? theme.sidebar.mobileHeightNumber : 0;
dispatch({
type: 'UPDATE_DETAILS',
state: {navScrollMargin: navHeight + sidebarHeight},
type: 'UPDATE_NAV_SCROLL_MARGIN',
margin: navHeight + sidebarHeight,
});
}, [nav, isScreenMedium, dispatch, theme.sidebar.mobileHeightNumber]);

Expand Down
4 changes: 2 additions & 2 deletions static/app/views/issueDetails/streamline/eventGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {useLocalStorageState} from 'sentry/utils/useLocalStorageState';
import {useLocation} from 'sentry/utils/useLocation';
import useOrganization from 'sentry/utils/useOrganization';
import {getBucketSize} from 'sentry/views/dashboards/widgetCard/utils';
import {useEventDetails} from 'sentry/views/issueDetails/streamline/context';
import {useIssueDetails} from 'sentry/views/issueDetails/streamline/context';
import {useCurrentEventMarklineSeries} from 'sentry/views/issueDetails/streamline/hooks/useEventMarkLineSeries';
import useFlagSeries from 'sentry/views/issueDetails/streamline/hooks/useFlagSeries';
import {
Expand Down Expand Up @@ -75,7 +75,7 @@ export function EventGraph({group, event, ...styleProps}: EventGraphProps) {
);
const eventView = useIssueDetailsEventView({group});
const config = getConfigForIssueType(group, group.project);
const {dispatch} = useEventDetails();
const {dispatch} = useIssueDetails();

const {
data: groupStats = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {initializeOrg} from 'sentry-test/initializeOrg';
import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';

import * as useMedia from 'sentry/utils/useMedia';
import {SectionKey, useEventDetails} from 'sentry/views/issueDetails/streamline/context';
import {SectionKey, useIssueDetails} from 'sentry/views/issueDetails/streamline/context';
import {Tab, TabPaths} from 'sentry/views/issueDetails/types';

import {IssueEventNavigation} from './eventNavigation';
Expand Down Expand Up @@ -38,12 +38,15 @@ describe('EventNavigation', () => {

beforeEach(() => {
jest.resetAllMocks();
jest.mocked(useEventDetails).mockReturnValue({
jest.mocked(useIssueDetails).mockReturnValue({
sectionData: {
highlights: {key: SectionKey.HIGHLIGHTS},
tags: {key: SectionKey.TAGS},
replay: {key: SectionKey.REPLAY},
},
eventCount: 0,
isSidebarOpen: true,
navScrollMargin: 0,
dispatch: jest.fn(),
});
MockApiClient.addMockResponse({
Expand Down
4 changes: 2 additions & 2 deletions static/app/views/issueDetails/streamline/eventNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import useOrganization from 'sentry/utils/useOrganization';
import {useParams} from 'sentry/utils/useParams';
import {hasDatasetSelector} from 'sentry/views/dashboards/utils';
import {useGroupEventAttachments} from 'sentry/views/issueDetails/groupEventAttachments/useGroupEventAttachments';
import {useEventDetails} from 'sentry/views/issueDetails/streamline/context';
import {useIssueDetails} from 'sentry/views/issueDetails/streamline/context';
import {useIssueDetailsEventView} from 'sentry/views/issueDetails/streamline/hooks/useIssueDetailsDiscoverQuery';
import {Tab, TabPaths} from 'sentry/views/issueDetails/types';
import {useGroupDetailsRoute} from 'sentry/views/issueDetails/useGroupDetailsRoute';
Expand Down Expand Up @@ -76,7 +76,7 @@ export function IssueEventNavigation({event, group}: IssueEventNavigationProps)
const [shouldPreload, setShouldPreload] = useState({next: false, previous: false});
const environments = useEnvironmentsFromUrl();
const eventView = useIssueDetailsEventView({group});
const {eventCount} = useEventDetails();
const {eventCount} = useIssueDetails();
const issueTypeConfig = getConfigForIssueType(group, group.project);

const hideDropdownButton =
Expand Down
12 changes: 9 additions & 3 deletions static/app/views/issueDetails/streamline/eventTitle.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {GroupFixture} from 'sentry-fixture/group';

import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';

import {SectionKey, useEventDetails} from 'sentry/views/issueDetails/streamline/context';
import {SectionKey, useIssueDetails} from 'sentry/views/issueDetails/streamline/context';

import {EventTitle} from './eventTitle';

Expand All @@ -30,12 +30,15 @@ describe('EventNavigation', () => {

beforeEach(() => {
jest.resetAllMocks();
jest.mocked(useEventDetails).mockReturnValue({
jest.mocked(useIssueDetails).mockReturnValue({
sectionData: {
highlights: {key: SectionKey.HIGHLIGHTS},
tags: {key: SectionKey.TAGS},
replay: {key: SectionKey.REPLAY},
},
eventCount: 0,
isSidebarOpen: true,
navScrollMargin: 0,
dispatch: jest.fn(),
});
Object.assign(navigator, {
Expand All @@ -53,8 +56,11 @@ describe('EventNavigation', () => {
});

it('does not show jump to sections by default', () => {
jest.mocked(useEventDetails).mockReturnValue({
jest.mocked(useIssueDetails).mockReturnValue({
sectionData: {},
eventCount: 0,
isSidebarOpen: true,
navScrollMargin: 0,
dispatch: jest.fn(),
});
render(<EventTitle {...defaultProps} />);
Expand Down
4 changes: 2 additions & 2 deletions static/app/views/issueDetails/streamline/eventTitle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import EventCreatedTooltip from 'sentry/views/issueDetails/eventCreatedTooltip';
import {
type SectionConfig,
SectionKey,
useEventDetails,
useIssueDetails,
} from 'sentry/views/issueDetails/streamline/context';
import {getFoldSectionKey} from 'sentry/views/issueDetails/streamline/foldSection';

Expand Down Expand Up @@ -64,7 +64,7 @@ export const EventTitle = forwardRef<HTMLDivElement, EventNavigationProps>(
const organization = useOrganization();
const theme = useTheme();

const {sectionData} = useEventDetails();
const {sectionData} = useIssueDetails();
const eventSectionConfigs = Object.values(sectionData ?? {}).filter(
config => sectionLabels[config.key]
);
Expand Down
10 changes: 7 additions & 3 deletions static/app/views/issueDetails/streamline/foldSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import mergeRefs from 'sentry/utils/mergeRefs';
import useOrganization from 'sentry/utils/useOrganization';
import {useSyncedLocalStorageState} from 'sentry/utils/useSyncedLocalStorageState';
import type {SectionKey} from 'sentry/views/issueDetails/streamline/context';
import {useEventDetails} from 'sentry/views/issueDetails/streamline/context';
import {useIssueDetails} from 'sentry/views/issueDetails/streamline/context';

export function getFoldSectionKey(key: SectionKey) {
return `'issue-details-fold-section-collapse:${key}`;
Expand Down Expand Up @@ -60,7 +60,7 @@ export const FoldSection = forwardRef<HTMLElement, FoldSectionProps>(function Fo
forwardedRef
) {
const organization = useOrganization();
const {sectionData, navScrollMargin, dispatch} = useEventDetails();
const {sectionData, navScrollMargin, dispatch} = useIssueDetails();
// Does not control open/close state. Controls what state is persisted to local storage
const [isCollapsed, setIsCollapsed] = useSyncedLocalStorageState(
getFoldSectionKey(sectionKey),
Expand Down Expand Up @@ -94,7 +94,11 @@ export const FoldSection = forwardRef<HTMLElement, FoldSectionProps>(function Fo

useLayoutEffect(() => {
if (!sectionData.hasOwnProperty(sectionKey)) {
dispatch({type: 'UPDATE_SECTION', key: sectionKey, config: {initialCollapse}});
dispatch({
type: 'UPDATE_EVENT_SECTION',
key: sectionKey,
config: {initialCollapse},
});
}
}, [sectionData, dispatch, sectionKey, initialCollapse]);

Expand Down
19 changes: 10 additions & 9 deletions static/app/views/issueDetails/streamline/groupDetailsLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ import type {Project} from 'sentry/types/project';
import {getConfigForIssueType} from 'sentry/utils/issueTypeConfig';
import theme from 'sentry/utils/theme';
import useMedia from 'sentry/utils/useMedia';
import {useSyncedLocalStorageState} from 'sentry/utils/useSyncedLocalStorageState';
import {
EventDetailsContext,
useEventDetailsReducer,
IssueDetailsContext,
useIssueDetailsReducer,
} from 'sentry/views/issueDetails/streamline/context';
import {EventDetailsHeader} from 'sentry/views/issueDetails/streamline/eventDetailsHeader';
import {IssueEventNavigation} from 'sentry/views/issueDetails/streamline/eventNavigation';
Expand All @@ -33,22 +32,24 @@ export function GroupDetailsLayout({
project,
children,
}: GroupDetailsLayoutProps) {
const {eventDetails, dispatch} = useEventDetailsReducer();
const [sidebarOpen] = useSyncedLocalStorageState('issue-details-sidebar-open', true);
const {issueDetails, dispatch} = useIssueDetailsReducer();
const isScreenSmall = useMedia(`(max-width: ${theme.breakpoints.large})`);
const shouldDisplaySidebar = sidebarOpen || isScreenSmall;
const shouldDisplaySidebar = issueDetails.isSidebarOpen || isScreenSmall;
const issueTypeConfig = getConfigForIssueType(group, group.project);
const groupReprocessingStatus = getGroupReprocessingStatus(group);

return (
<EventDetailsContext.Provider value={{...eventDetails, dispatch}}>
<IssueDetailsContext.Provider value={{...issueDetails, dispatch}}>
<StreamlinedGroupHeader
group={group}
event={event ?? null}
project={project}
groupReprocessingStatus={groupReprocessingStatus}
/>
<StyledLayoutBody data-test-id="group-event-details" sidebarOpen={sidebarOpen}>
<StyledLayoutBody
data-test-id="group-event-details"
sidebarOpen={issueDetails.isSidebarOpen}
>
<div>
<EventDetailsHeader event={event} group={group} project={project} />
<GroupContent>
Expand All @@ -68,7 +69,7 @@ export function GroupDetailsLayout({
<StreamlinedSidebar group={group} event={event} project={project} />
) : null}
</StyledLayoutBody>
</EventDetailsContext.Provider>
</IssueDetailsContext.Provider>
);
}

Expand Down
Loading

0 comments on commit 31b9b6a

Please sign in to comment.