fix(playground): prevent model group option stretching (#6120)

* fix(playground): prevent model group option stretching

- keep group options at a fixed 2rem height and align them to the top.
- organize layout code in a dedicated module and cover layout and scrolling behavior.

* docs(web): strengthen frontend testing requirements

- require regression coverage for behavior changes, bug fixes, and UI states.
- define module-scoped test organization, stable assertions, mocks, and verification rules.
This commit is contained in:
QuentinHsu
2026-07-20 18:03:49 +08:00
committed by GitHub
parent 16bfae1750
commit 2ef4cfff9c
4 changed files with 86 additions and 2 deletions
+1 -1
View File
@@ -65,7 +65,7 @@ import { cn } from '@/lib/utils'
import {
modelGroupSelectorLayoutClasses,
scrollSelectedOptionIntoView,
} from './model-group-selector-layout'
} from './model-group-selector/layout'
interface ModelOption {
label: string
@@ -0,0 +1,68 @@
/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import assert from 'node:assert/strict'
import { describe, test } from 'node:test'
import {
modelGroupSelectorLayoutClasses,
scrollSelectedOptionIntoView,
} from '../layout'
describe('model group selector layout', () => {
test('keeps group options at a fixed height and aligned to the top', () => {
const groupScrollClasses =
modelGroupSelectorLayoutClasses.groupScroll.split(' ')
assert.ok(groupScrollClasses.includes('auto-rows-[2rem]'))
assert.ok(groupScrollClasses.includes('content-start'))
})
test('centers the selected group inside its own scroll container', () => {
const scrollCalls: ScrollToOptions[] = []
const selectedOption = {
offsetHeight: 32,
offsetTop: 160,
scrollIntoView() {},
}
const scrollContainer = {
clientHeight: 200,
scrollTop: 0,
scrollTo(options: ScrollToOptions) {
scrollCalls.push(options)
},
}
scrollSelectedOptionIntoView(selectedOption, scrollContainer)
assert.deepEqual(scrollCalls, [{ top: 76, behavior: 'auto' }])
})
test('falls back to scrollIntoView when no group container is provided', () => {
const scrollCalls: ScrollIntoViewOptions[] = []
const selectedOption = {
scrollIntoView(options?: ScrollIntoViewOptions) {
scrollCalls.push(options ?? {})
},
}
scrollSelectedOptionIntoView(selectedOption)
assert.deepEqual(scrollCalls, [{ block: 'center', inline: 'nearest' }])
})
})
@@ -21,7 +21,8 @@ export const modelGroupSelectorLayoutClasses = {
desktopContent:
'grid h-[min(50vh,28rem)] max-h-[min(50vh,28rem)] min-h-0 gap-3 p-2 md:grid-cols-[9.5rem_minmax(0,1fr)]',
groupColumn: 'flex h-full min-h-0 min-w-0 flex-col overflow-hidden',
groupScroll: 'mt-2 grid min-h-0 flex-1 gap-1 overflow-y-auto pr-1',
groupScroll:
'mt-2 grid min-h-0 flex-1 auto-rows-[2rem] content-start gap-1 overflow-y-auto pr-1',
modelColumn: 'flex h-full min-h-0 min-w-0 overflow-hidden rounded-lg border',
modelCommand: 'min-h-0 flex-1 rounded-lg border-0 bg-transparent p-1',
modelList: