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
60 changes: 60 additions & 0 deletions apps/web/e2e/mocked/browser-behavior.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,66 @@ test('long Markdown, project paths and descriptions stay usable at a narrow widt
assertNoUnexpected()
})

test('entry Markdown has a roomy scrollable reading and editing area', async ({ page }) => {
const longText = Array.from({ length: 80 }, (_, index) => `Paragraph ${index + 1}: a technical detail.`).join('\n\n')
const assertNoUnexpected = await mockApi(page, (request, url) => {
if (request.method() === 'GET' && url.pathname === `/api/technical-entry/${entryId}`) {
return { body: { ...entry, context: longText, conclusion: longText } }
}
return authenticatedApi(request, url)
})
await page.goto(`/technical-entries/${entryId}`)

for (const label of ['Context', 'Conclusion']) {
const section = page.locator('section').filter({ has: page.getByRole('heading', { name: label, exact: true }) }).first()
const readingArea = section.locator('div.overflow-y-auto').filter({ hasText: 'Paragraph 80' }).first()
await expect(readingArea).toBeVisible()
const readSize = await readingArea.evaluate((element) => ({ height: element.clientHeight, scrollHeight: element.scrollHeight }))
expect(readSize.height).toBeGreaterThan(500)
expect(readSize.scrollHeight).toBeGreaterThan(readSize.height)
expect(await readingArea.evaluate((element) => {
element.scrollTop = element.scrollHeight
return element.scrollTop
})).toBeGreaterThan(0)

await section.getByRole('button', { name: `Edit ${label.toLowerCase()}` }).click()
const editor = section.getByRole('textbox', { name: label })
await expect(editor).toHaveValue(longText)
const editSize = await editor.evaluate((element) => ({ height: element.clientHeight, scrollHeight: element.scrollHeight }))
expect(editSize.height).toBeGreaterThan(500)
expect(editSize.scrollHeight).toBeGreaterThan(editSize.height)
expect(await editor.evaluate((element) => {
element.scrollTop = element.scrollHeight
return element.scrollTop
})).toBeGreaterThan(0)
await section.getByRole('button', { name: 'Cancel' }).click()
}
assertNoUnexpected()
})

test('entry form gives both long-form fields room and stays reachable on a short screen', async ({ page }) => {
await page.setViewportSize({ width: 390, height: 700 })
const assertNoUnexpected = await mockApi(page, authenticatedApi)
await page.goto('/technical-entries')
await page.getByRole('button', { name: 'New entry' }).click()
const dialog = page.getByRole('dialog', { name: 'New technical entry' })

for (const label of ['Context', 'Conclusion']) {
const field = dialog.getByRole('textbox', { name: label })
expect(await field.evaluate((element) => element.clientHeight)).toBeGreaterThan(150)
}
const size = await dialog.evaluate((element) => ({
height: element.getBoundingClientRect().height,
scrollHeight: element.scrollHeight,
clientHeight: element.clientHeight,
}))
expect(size.height).toBeLessThanOrEqual(700)
expect(size.scrollHeight).toBeGreaterThan(size.clientHeight)
await dialog.getByRole('button', { name: 'Cancel' }).click()
await expect(dialog).toBeHidden()
assertNoUnexpected()
})

test('a failed collection has a named error and a keyboard-reachable retry', async ({ page }) => {
const assertNoUnexpected = await mockApi(page, (request, url) => {
if (request.method() === 'GET' && url.pathname === '/api/project') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function TechnicalEntryFormFields<T extends CommonValues>({
<FormControl>
<textarea
{...field}
className="min-h-32 w-full resize-y rounded-lg border border-input bg-transparent px-2.5 py-2 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50"
className="min-h-48 w-full resize-y rounded-lg border border-input bg-transparent px-2.5 py-2 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50"
disabled={disabled}
placeholder="Describe what happened, where it happened, and what you tried."
/>
Expand All @@ -62,7 +62,7 @@ export function TechnicalEntryFormFields<T extends CommonValues>({
<FormControl>
<textarea
{...field}
className="min-h-24 w-full resize-y rounded-lg border border-input bg-transparent px-2.5 py-2 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50"
className="min-h-48 w-full resize-y rounded-lg border border-input bg-transparent px-2.5 py-2 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50"
disabled={disabled}
placeholder="What did you learn or how did you solve it?"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export function TechnicalEntryForm({

return (
<Dialog open={open} onOpenChange={handleOpenChange}>
<DialogContent className="gap-6 border-border/60 bg-card p-7 shadow-2xl ring-0 sm:max-w-xl">
<DialogContent className="max-h-[calc(100dvh-2rem)] gap-6 overflow-y-auto border-border/60 bg-card p-7 shadow-2xl ring-0 sm:max-w-2xl">
<DialogHeader>
<DialogTitle className="text-xl font-semibold tracking-tight">
New technical entry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,10 @@ export function TechnicalEntryInlineContent({
const [validationMessage, setValidationMessage] = useState<string>();
const updateMutation = useUpdateTechnicalEntry();

// Read and edit modes intentionally share the same adaptive viewport rules.
// Short content stays compact; once the larger maximum is reached, the
// content scrolls inside the field instead of expanding the entire page.
// Short Markdown remains compact, while long content gets more reading room
// before scrolling. The editor starts taller so a new draft is useful too.
const contentViewportClassName =
"min-h-24 max-h-110 overflow-y-auto rounded-lg";
"max-h-160 overflow-y-auto rounded-lg";

function handleStartEditing() {
// Re-read the latest server value when editing starts. This avoids
Expand Down Expand Up @@ -111,7 +110,7 @@ export function TechnicalEntryInlineContent({
aria-invalid={Boolean(validationMessage)}
aria-label={label}
autoFocus
className={`${contentViewportClassName} field-sizing-content w-full resize-none border border-input bg-background px-3 py-2 text-sm leading-6 outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50`}
className={`${contentViewportClassName} field-sizing-content min-h-48 w-full resize-none border border-input bg-background px-3 py-2 text-sm leading-6 outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50`}
disabled={updateMutation.isPending}
onChange={(event) => {
setDraft(event.target.value);
Expand Down Expand Up @@ -166,13 +165,13 @@ export function TechnicalEntryInlineContent({
) : null}

{value.trim() ? (
<div className={`${contentViewportClassName} pr-2`}>
<div className={`${contentViewportClassName} min-h-24 pr-2`}>
<Markdown className="max-w-3xl text-card-foreground/80">
{value}
</Markdown>
</div>
) : (
<div className={`${contentViewportClassName} pr-2`}>
<div className={`${contentViewportClassName} min-h-24 pr-2`}>
<p className="text-sm italic leading-6 text-muted-foreground">
{emptyMessage}
</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export function TechnicalEntryStatusDialog({
}}
open={open}
>
<DialogContent className="gap-6 border-border/60 bg-card p-7 shadow-2xl ring-0 sm:max-w-xl">
<DialogContent className="max-h-[calc(100dvh-2rem)] gap-6 overflow-y-auto border-border/60 bg-card p-7 shadow-2xl ring-0 sm:max-w-xl">
<DialogHeader>
<DialogTitle className="text-xl font-semibold tracking-tight">
{isOpen ? "Close this issue" : "Reopen this issue"}
Expand Down Expand Up @@ -114,7 +114,7 @@ export function TechnicalEntryStatusDialog({
<textarea
{...field}
autoFocus
className="min-h-32 w-full resize-y rounded-lg border border-input bg-background px-3 py-2 text-sm leading-6 outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50"
className="min-h-48 w-full resize-y rounded-lg border border-input bg-background px-3 py-2 text-sm leading-6 outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50"
disabled={isPending}
placeholder="What resolved the issue?"
/>
Expand Down
Binary file added docs/images/issue-8-entry-form.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading