Skip to content

Commit 40e8f07

Browse files
authored
ref(preprod): Sidebar polish (#110571)
Fixes EME-924. This was a bit tricky as emotion styling didn't work on `Disclosure.Title` as there seems to be a bug where `...rest` is passed to the child button rather than the top-level `Flex`. This required me to essentially implement a custom `Disclosure.Title` with a Button, but works pretty well overall. Before: <img width="371" height="382" alt="Screenshot 2026-03-12 at 11 20 10 AM" src="https://github.com/user-attachments/assets/141c7d8b-5d20-4f32-9714-a8b925b8087c" /> After: <img width="376" height="268" alt="Screenshot 2026-03-12 at 12 51 32 PM" src="https://github.com/user-attachments/assets/24c5834d-e67e-4323-82f5-7873f18521ad" /> <img width="382" height="306" alt="Screenshot 2026-03-12 at 12 51 26 PM" src="https://github.com/user-attachments/assets/7144d856-8b11-45b5-9730-7de5c6c08e1a" /> Designs (after matches): https://www.figma.com/design/noaq47E7GgvQhcRNxj5OAF/Emerge-Merge?node-id=3298-2462&t=YVvX3NErHrgtNHpV-1
1 parent 50f5d89 commit 40e8f07

File tree

1 file changed

+108
-104
lines changed

1 file changed

+108
-104
lines changed
Lines changed: 108 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {useMemo, useState} from 'react';
22
import styled from '@emotion/styled';
33

4+
import {Button} from '@sentry/scraps/button';
45
import {Disclosure} from '@sentry/scraps/disclosure';
56
import {InputGroup} from '@sentry/scraps/input';
67
import {Flex, Stack} from '@sentry/scraps/layout';
@@ -9,6 +10,7 @@ import {Text} from '@sentry/scraps/text';
910
import {
1011
IconAdd,
1112
IconCheckmark,
13+
IconChevron,
1214
IconCopy,
1315
IconEdit,
1416
IconSearch,
@@ -112,8 +114,8 @@ export function SnapshotSidebarContent({
112114
const isGroupedDiff = isDiffMode && groupedItems !== null;
113115

114116
return (
115-
<Flex direction="column" gap="md" height="100%" width="100%">
116-
<Flex padding="xl" paddingBottom="0">
117+
<Stack height="100%" width="100%">
118+
<Stack gap="xl" padding="xl" borderBottom="primary">
117119
<InputGroup style={{flex: 1}}>
118120
<InputGroup.LeadingItems disablePointerEvents>
119121
<IconSearch size="sm" />
@@ -125,94 +127,93 @@ export function SnapshotSidebarContent({
125127
onChange={e => onSearchChange(e.target.value)}
126128
/>
127129
</InputGroup>
128-
</Flex>
129-
<Stack overflow="auto" flex="1">
130-
{isGroupedDiff
131-
? SECTION_ORDER.map(section => {
132-
const sectionItems = groupedItems.get(section.type);
133-
if (!sectionItems || sectionItems.length === 0) {
134-
return null;
135-
}
136-
const isExpanded = isSearching || expandedSections[section.type];
137-
return (
138-
<SectionWrapper key={section.type}>
139-
<Disclosure
140-
size="xs"
141-
expanded={isExpanded}
142-
onExpandedChange={expanded =>
143-
handleExpandedChange(section.type, expanded)
144-
}
145-
>
146-
<Disclosure.Title
147-
trailingItems={
148-
<Flex align="center" gap="xs">
149-
<Text size="xs" bold>
150-
{sectionItems.length}
151-
</Text>
152-
{section.icon}
153-
</Flex>
154-
}
155-
>
156-
<Text size="xs" bold uppercase>
130+
</Stack>
131+
<Stack overflow="auto" flex="1" paddingRight="0">
132+
{isGroupedDiff &&
133+
SECTION_ORDER.map(section => {
134+
const sectionItems = groupedItems.get(section.type);
135+
if (!sectionItems || sectionItems.length === 0) {
136+
return null;
137+
}
138+
const isExpanded = isSearching || expandedSections[section.type];
139+
return (
140+
<Disclosure key={section.type} size="md" expanded={isExpanded}>
141+
<SidebarSectionTitle
142+
priority="transparent"
143+
size="md"
144+
onClick={() => handleExpandedChange(section.type, !isExpanded)}
145+
>
146+
<Flex align="center" justify="between" width="100%">
147+
<Flex align="center" gap="xs">
148+
<IconChevron direction={isExpanded ? 'down' : 'right'} size="sm" />
149+
<Text size="sm" bold uppercase>
157150
{section.label}
158151
</Text>
159-
</Disclosure.Title>
160-
<Disclosure.Content>
161-
{sectionItems.map(item => (
162-
<SidebarItemRow
163-
key={item.name}
164-
isSelected={item.name === currentItemName}
165-
onClick={() => onSelectItem(item.name)}
166-
>
167-
<Flex align="center" gap="sm" flex="1" minWidth="0">
168-
<Text
169-
size="md"
170-
variant={item.name === currentItemName ? 'accent' : 'muted'}
171-
bold={item.name === currentItemName}
172-
ellipsis
173-
>
174-
{item.name}
175-
</Text>
176-
</Flex>
177-
{item.type === 'changed' && item.pair.diff !== null && (
178-
<Text variant="muted" size="xs">
179-
{`${(item.pair.diff * 100).toFixed(1)}%`}
180-
</Text>
181-
)}
182-
</SidebarItemRow>
183-
))}
184-
</Disclosure.Content>
185-
</Disclosure>
186-
</SectionWrapper>
187-
);
188-
})
189-
: items.map(item => {
190-
const isSelected = item.name === currentItemName;
191-
192-
return (
193-
<SidebarItemRow
194-
key={item.name}
195-
isSelected={isSelected}
196-
onClick={() => onSelectItem(item.name)}
197-
>
198-
<Flex align="center" gap="sm" flex="1" minWidth="0">
199-
<Text
200-
size="md"
201-
variant={isSelected ? 'accent' : 'muted'}
202-
bold={isSelected}
203-
ellipsis
204-
>
205-
{item.name}
206-
</Text>
152+
</Flex>
153+
<Flex align="center" gap="xs">
154+
<Text size="sm">{sectionItems.length}</Text>
155+
{section.icon}
156+
</Flex>
207157
</Flex>
208-
{item.type === 'solo' && item.images.length > 1 && (
209-
<Text variant="muted" size="xs">
210-
{item.images.length}
211-
</Text>
212-
)}
213-
</SidebarItemRow>
214-
);
215-
})}
158+
</SidebarSectionTitle>
159+
{isExpanded && (
160+
<SidebarSectionContent>
161+
{sectionItems.map(item => (
162+
<SidebarItemRow
163+
key={item.name}
164+
isSelected={item.name === currentItemName}
165+
onClick={() => onSelectItem(item.name)}
166+
>
167+
<Flex align="center" gap="sm" flex="1" minWidth="0">
168+
<Text
169+
size="md"
170+
variant={item.name === currentItemName ? 'accent' : 'muted'}
171+
bold={item.name === currentItemName}
172+
ellipsis
173+
>
174+
{item.name}
175+
</Text>
176+
</Flex>
177+
{item.type === 'changed' && item.pair.diff !== null && (
178+
<Text variant="muted" size="xs">
179+
{`${(item.pair.diff * 100).toFixed(1)}%`}
180+
</Text>
181+
)}
182+
</SidebarItemRow>
183+
))}
184+
</SidebarSectionContent>
185+
)}
186+
</Disclosure>
187+
);
188+
})}
189+
{!isGroupedDiff &&
190+
items.map(item => {
191+
const isSelected = item.name === currentItemName;
192+
193+
return (
194+
<SidebarItemRow
195+
key={item.name}
196+
isSelected={isSelected}
197+
onClick={() => onSelectItem(item.name)}
198+
>
199+
<Flex align="center" gap="sm" flex="1" minWidth="0">
200+
<Text
201+
size="md"
202+
variant={isSelected ? 'accent' : 'muted'}
203+
bold={isSelected}
204+
ellipsis
205+
>
206+
{item.name}
207+
</Text>
208+
</Flex>
209+
{item.type === 'solo' && item.images.length > 1 && (
210+
<Text variant="muted" size="xs">
211+
{item.images.length}
212+
</Text>
213+
)}
214+
</SidebarItemRow>
215+
);
216+
})}
216217
{items.length === 0 && (
217218
<Flex align="center" justify="center" padding="lg">
218219
<Text variant="muted" size="sm">
@@ -221,44 +222,47 @@ export function SnapshotSidebarContent({
221222
</Flex>
222223
)}
223224
</Stack>
224-
</Flex>
225+
</Stack>
225226
);
226227
}
227228

228-
const SectionWrapper = styled('div')`
229-
&:not(:first-child) {
230-
border-top: 1px solid ${p => p.theme.tokens.border.primary};
231-
}
229+
const SidebarSectionTitle = styled(Button)`
230+
padding: ${p => p.theme.space.lg} ${p => p.theme.space.xl};
231+
display: block;
232+
width: 100%;
233+
border-radius: 0;
234+
border-bottom: 1px solid ${p => p.theme.tokens.border.secondary};
232235
233-
[data-disclosure] > :not(:first-child) {
234-
padding-left: 0;
235-
padding-right: 0;
236+
&:hover {
237+
background: ${p => p.theme.tokens.background.secondary};
236238
}
239+
`;
237240

238-
[data-disclosure] > div > button {
239-
min-width: 0;
240-
overflow: hidden;
241-
}
241+
const SidebarSectionContent = styled('div')`
242+
width: 100%;
242243
243-
[data-disclosure] > div > button ~ * {
244-
flex-shrink: 0;
245-
padding-right: ${p => p.theme.space.md};
244+
&:last-child {
245+
border-bottom: 1px solid ${p => p.theme.tokens.border.secondary};
246246
}
247247
`;
248248

249249
const SidebarItemRow = styled('div')<{isSelected: boolean}>`
250250
display: flex;
251251
align-items: center;
252-
justify-content: space-between;
253-
gap: ${p => p.theme.space.sm};
254252
padding: ${p => p.theme.space.lg} ${p => p.theme.space.xl};
253+
gap: ${p => p.theme.space.sm};
255254
cursor: pointer;
256255
border-right: 3px solid
257256
${p => (p.isSelected ? p.theme.tokens.border.accent.vibrant : 'transparent')};
257+
border-bottom: 1px solid ${p => p.theme.tokens.border.secondary};
258258
background: ${p =>
259259
p.isSelected ? p.theme.tokens.background.transparent.accent.muted : 'transparent'};
260260
261261
&:hover {
262262
background: ${p => p.theme.tokens.background.secondary};
263263
}
264+
265+
&:last-child {
266+
border-bottom: none;
267+
}
264268
`;

0 commit comments

Comments
 (0)