Skip to content

Commit a0df4f9

Browse files
committed
fix(stories): virtual scroll with empty state
1 parent d3e2697 commit a0df4f9

File tree

1 file changed

+47
-11
lines changed

1 file changed

+47
-11
lines changed

static/app/stories/view/storySearch.tsx

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {CollectionChildren} from '@react-types/shared';
99
import {ListBox} from '@sentry/scraps/compactSelect';
1010
import {useHotkeys, Hotkey} from '@sentry/scraps/hotkey';
1111
import {InputGroup} from '@sentry/scraps/input';
12+
import {Flex} from '@sentry/scraps/layout';
1213
import {Text} from '@sentry/scraps/text';
1314

1415
import {Overlay} from 'sentry/components/overlay';
@@ -209,7 +210,7 @@ function SearchComboBox(props: SearchComboBoxProps) {
209210
onInputChange: setInputValue,
210211
defaultFilter: filter,
211212
shouldCloseOnBlur: true,
212-
allowsEmptyCollection: false,
213+
allowsEmptyCollection: true,
213214
onSelectionChange: handleSelectionChange,
214215
});
215216

@@ -233,22 +234,51 @@ function SearchComboBox(props: SearchComboBoxProps) {
233234
<SearchInput ref={inputRef} placeholder={props.label} {...inputProps} />
234235
{state.isOpen && (
235236
<StyledOverlay placement="bottom-start" ref={popoverRef}>
236-
<ListBox
237-
size="sm"
238-
listState={state}
239-
hasSearch={!!state.inputValue}
240-
overlayIsOpen={state.isOpen}
241-
{...listBoxProps}
242-
style={{maxHeight: 320, minHeight: 64}}
243-
>
244-
{props.children}
245-
</ListBox>
237+
{state.collection.size === 0 ? (
238+
inputValue.length === 0 ? (
239+
<SearchEmpty />
240+
) : (
241+
<SearchNotFound inputValue={inputValue} />
242+
)
243+
) : (
244+
<ListBox
245+
size="sm"
246+
virtualized
247+
listState={state}
248+
hasSearch={!!state.inputValue}
249+
overlayIsOpen={state.isOpen}
250+
{...listBoxProps}
251+
className="story-search-results"
252+
>
253+
{props.children}
254+
</ListBox>
255+
)}
246256
</StyledOverlay>
247257
)}
248258
</StorySearchContainer>
249259
);
250260
}
251261

262+
function SearchEmpty() {
263+
return (
264+
<Flex align="center" justify="start" padding="lg">
265+
<Text variant="muted" size="sm">
266+
{t('Type to search stories...')}
267+
</Text>
268+
</Flex>
269+
);
270+
}
271+
272+
function SearchNotFound({inputValue}: {inputValue: string}) {
273+
return (
274+
<Flex align="center" justify="start" padding="lg">
275+
<Text variant="muted" size="sm">
276+
{t('No stories match "%s"', inputValue)}
277+
</Text>
278+
</Flex>
279+
);
280+
}
281+
252282
const StorySearchContainer = styled('div')`
253283
position: relative;
254284
width: 320px;
@@ -267,6 +297,12 @@ const StyledOverlay = styled(Overlay)`
267297
p[id][aria-hidden='true'] {
268298
color: ${p => p.theme.tokens.content.primary};
269299
}
300+
301+
.story-search-results {
302+
max-height: 320px;
303+
min-height: 64px;
304+
padding-block-end: calc(${p => p.theme.space.md} + 1px);
305+
}
270306
`;
271307

272308
function getStoryTreeNodeFromKey(

0 commit comments

Comments
 (0)