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
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { describe, expect, test } from "bun:test"
import { resolveSkipAction } from "./session-question-dock"

describe("resolveSkipAction", () => {
test("navigates to next unsettled question when one exists after current", () => {
// 3 questions: Q0 settled, Q1 unsettled, Q2 (current) just skipped → now settled
const isSettled = (i: number) => i !== 1
const result = resolveSkipAction(2, isSettled, 3)
expect(result).toEqual({ type: "navigate", tab: 1 })
})

test("navigates to first unsettled overall when nothing after current", () => {
// 3 questions: Q0 unsettled, Q1 settled, Q2 (current) just skipped → settled
const isSettled = (i: number) => i !== 0
const result = resolveSkipAction(2, isSettled, 3)
expect(result).toEqual({ type: "navigate", tab: 0 })
})

test("submits when there is only one question and it was just skipped", () => {
// Single question: Q0 just skipped → settled
const isSettled = () => true
const result = resolveSkipAction(0, isSettled, 1)
expect(result).toEqual({ type: "submit" })
})

test("submits when all questions are settled after skipping the last one", () => {
// 3 questions: all settled (Q0 and Q1 answered, Q2 just skipped)
const isSettled = () => true
const result = resolveSkipAction(2, isSettled, 3)
expect(result).toEqual({ type: "submit" })
})

test("navigates to next unsettled before current when current is not the last", () => {
// 3 questions: Q0 settled, Q1 (current) just skipped → settled, Q2 unsettled
const isSettled = (i: number) => i !== 2
const result = resolveSkipAction(1, isSettled, 3)
expect(result).toEqual({ type: "navigate", tab: 2 })
})
})
40 changes: 31 additions & 9 deletions packages/app/src/pages/session/composer/session-question-dock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,27 @@ type DraftAnswer = QuestionAnswer | undefined

const cache = new Map<string, { tab: number; answers: DraftAnswer[]; custom: string[]; customOn: boolean[] }>()

/**
* After skipping a question (setting its answer to []), decide the next action.
* Returns either the tab to navigate to, or a submit signal when all questions are settled.
*/
export function resolveSkipAction(
currentTab: number,
isSettled: (i: number) => boolean,
total: number,
): { type: "navigate"; tab: number } | { type: "submit" } {
// First, look for an unsettled question after the current tab.
for (let i = currentTab + 1; i < total; i++) {
if (!isSettled(i)) return { type: "navigate", tab: i }
}
// Then, look for any unsettled question before the current tab.
for (let i = 0; i < currentTab; i++) {
if (!isSettled(i)) return { type: "navigate", tab: i }
}
// All settled — time to submit.
return { type: "submit" }
}

function Mark(props: { multi: boolean; picked: boolean; onClick?: (event: MouseEvent) => void }) {
return (
<span data-slot="question-option-check" aria-hidden="true" onClick={props.onClick}>
Expand Down Expand Up @@ -157,8 +178,8 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
store.answers[i] === undefined ? undefined : [...store.answers[i]],
),
// Iterate by total() to avoid leaving stale entries when a tab was never visited.
custom: Array.from({ length: total() }, (_, i) => (customByTab(i) ? store.custom[i] ?? "" : "")),
customOn: Array.from({ length: total() }, (_, i) => (customByTab(i) ? store.customOn[i] ?? false : false)),
custom: Array.from({ length: total() }, (_, i) => (customByTab(i) ? (store.custom[i] ?? "") : "")),
customOn: Array.from({ length: total() }, (_, i) => (customByTab(i) ? (store.customOn[i] ?? false) : false)),
})
})

Expand Down Expand Up @@ -400,15 +421,14 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
setStore("customOn", store.tab, false)
setStore("editing", false)

const nextUnsettled = questions().findIndex((_, i) => i > store.tab && !settled(i))
const target = nextUnsettled >= 0 ? nextUnsettled : firstUnsettled()
if (target >= 0) {
setStore("tab", target)
focus(pickFocus(target))
const action = resolveSkipAction(store.tab, settled, total())
if (action.type === "navigate") {
setStore("tab", action.tab)
focus(pickFocus(action.tab))
return
}

focus(pickFocus(store.tab))
submit()
}

const jump = (tab: number) => {
Expand All @@ -427,7 +447,9 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
<>
<div data-slot="question-header-title">
<span data-slot="question-header-seq">{summary()}</span>
<span data-slot="question-header-separator" aria-hidden="true">·</span>
<span data-slot="question-header-separator" aria-hidden="true">
·
</span>
<span data-slot="question-header-mode">
{multi() ? language.t("ui.question.multiHint") : language.t("ui.question.singleHint")}
</span>
Expand Down
Loading