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
24 changes: 17 additions & 7 deletions core/src/components/UnifiedSearch/UnifiedSearchModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@
ref="resultsContainer"
class="unified-search-modal__results"
:class="{ 'unified-search-modal__results--held': heldHeight !== null }"
:style="heldHeight !== null ? { blockSize: `${heldHeight}px`, boxSizing: 'border-box' } : undefined">
:style="heldHeight !== null ? { minBlockSize: `${heldHeight}px`, boxSizing: 'border-box' } : undefined">
<h3 class="hidden-visually">
{{ t('core', 'Results') }}
</h3>
Expand Down Expand Up @@ -1807,20 +1807,30 @@ export default defineComponent({
min-height: 0;
overflow: hidden auto;

// The placeholders deliberately overfill, so the bottom fades out over the cut.
// The reserved height is a floor, not a size. Column layout so the placeholders can
// take what the results leave.
&--held {
display: flex;
flex-direction: column;
flex-grow: 0;
overflow: clip;
// Capped, so a short box does not spend a third of itself fading.
mask-image: linear-gradient(to bottom, #000 calc(100% - min(2lh, 25%)), transparent);

> *:not(.search-result-skeleton) {
flex: none;
}
}
// Adjust padding to match container but keep the scrollbar on the very end
padding-inline: calc(var(--default-grid-baseline) * 4);
padding-block: 0 calc(var(--default-grid-baseline) * 4);

// Matches the gap a category title keeps above itself.
.search-result-skeleton {
// Matches the gap a category title keeps above itself.
margin-block-start: 14px;
// The placeholders deliberately overfill, so the bottom fades out over the cut.
flex: 1 1 0;
min-block-size: 0;
overflow: clip;
// Capped, so a short box does not spend a third of itself fading.
mask-image: linear-gradient(to bottom, #000 calc(100% - min(2lh, 25%)), transparent);
}

.result {
Expand Down Expand Up @@ -1891,7 +1901,7 @@ export default defineComponent({

// Ensure modal is accessible on small devices
@media only screen and (max-height: 400px) {
.unified-search-modal__results:not(.unified-search-modal__results--held) {
.unified-search-modal__results {
overflow: unset;
}
}
Expand Down
24 changes: 23 additions & 1 deletion core/src/tests/components/UnifiedSearchModal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1267,13 +1267,35 @@ describe('UnifiedSearchModal loading skeleton', () => {
expect(wrapper.vm.skeletonRows * 60).toBeGreaterThanOrEqual(300)
})

// isBusy stays true until the last provider answers, so the box carries real results meanwhile.
it('reserves the height as a floor, so landed results are never cut off', async () => {
const wrapper = await withResults()
measureResultsAt(wrapper, 300)
wrapper.vm.searchQuery = 'querying'
await wrapper.vm.$nextTick()
wrapper.vm.find('querying')

searchStates.value = {
files: loaded([{ resourceUrl: '/a' }, { resourceUrl: '/b' }, { resourceUrl: '/c' }]),
talk: { status: 'loading', entries: [], cursor: null, hasMore: false, loadMoreFailed: false },
}
await wrapper.vm.$nextTick()

expect(wrapper.vm.isBusy).toBe(true)
expect(wrapper.vm.hasVisibleResults).toBe(true)

const style = wrapper.find('.unified-search-modal__results').attributes('style')
expect(style).toContain('min-block-size: 300px')
expect(style).not.toMatch(/(^|[^-])block-size: 300px/)
})

it('holds the same height through repeated keystrokes rather than creeping taller', async () => {
const wrapper = await withResults()
const box = wrapper.vm.$refs.resultsContainer as HTMLElement
// Stand in for the browser: report the padding on top of whatever height is set.
const padding = 16
vi.spyOn(box, 'getBoundingClientRect').mockImplementation(() => ({
height: (parseFloat(box.style.blockSize) || 300) + (box.style.boxSizing === 'border-box' ? 0 : padding),
height: (parseFloat(box.style.minBlockSize) || 300) + (box.style.boxSizing === 'border-box' ? 0 : padding),
}) as DOMRect)

wrapper.vm.searchQuery = 'q1'
Expand Down
Loading