fix: keep presenter pairing visible in Q&A
This commit is contained in:
@@ -47,3 +47,26 @@ Passed with no whitespace errors.
|
||||
## Self-Review
|
||||
|
||||
No critical or important findings. The change is limited to the presenter synchronization seam and tests. The untracked active LAN synchronization plan was not staged. The pre-existing intentional `presenter.css` mobile sticky-navigation edit was retained and verified as part of this task.
|
||||
|
||||
## Important Finding Fix: Pairing Persistence On Q&A
|
||||
|
||||
The original Task 7 render condition mounted `PresenterNavigationBar` only when `navigation.note` existed. Valid discussion hashes resolve with `note: null`, so Q&A content remained visible but the pairing controls, session-end action, and ended state disappeared.
|
||||
|
||||
The stable navigation surface now renders for either a valid presenter note or a resolved discussion branch. Discussion routes pass a nullable progress index and display `Q&A`; Previous and Next remain disabled because no note or destination is fabricated. The existing Q&A content, hash parsing, local note navigation, and presenter mobile CSS are unchanged.
|
||||
|
||||
TDD evidence:
|
||||
|
||||
```text
|
||||
RED: pnpm --dir web --filter @lda/console test -- src/presentation/presenter/PresenterRoute.test.tsx
|
||||
1 test file failed; 1 failed and 9 passed (10 total).
|
||||
The regression test could not find the Pair presentation button on #discuss/where-is-ai-agent.
|
||||
|
||||
GREEN: pnpm --dir web --filter @lda/console test -- src/presentation/presenter/PresenterRoute.test.tsx
|
||||
1 test file passed; 10 tests passed.
|
||||
|
||||
FINAL: pnpm --dir web --filter @lda/console test -- src/presentation/presenter/PresenterRoute.test.tsx src/presentation/presenter/PresenterShell.test.tsx
|
||||
2 test files passed; 16 tests passed.
|
||||
|
||||
pnpm --dir web --filter @lda/console typecheck
|
||||
@lda/presentation-sync build and console TypeScript project build passed.
|
||||
```
|
||||
|
||||
@@ -4,7 +4,7 @@ import { PresentationPairingPanel } from "../sync/PresentationPairingPanel.js";
|
||||
import type { PresentationSyncController } from "../sync/presentation-sync-state.js";
|
||||
|
||||
type PresenterNavigationBarProps = {
|
||||
readonly currentIndex: number;
|
||||
readonly currentIndex: number | null;
|
||||
readonly total: number;
|
||||
readonly previous: PresenterBeatNote | null;
|
||||
readonly next: PresenterBeatNote | null;
|
||||
@@ -19,7 +19,7 @@ const DirectionLink = ({ note, children }: { readonly note: PresenterBeatNote |
|
||||
export const PresenterNavigationBar = ({ currentIndex, total, previous, next, syncController }: PresenterNavigationBarProps) => (
|
||||
<nav className="presenter-navigation" aria-label="Presenter note navigation">
|
||||
<DirectionLink note={previous}>← Previous</DirectionLink>
|
||||
<span>{currentIndex + 1} / {total}</span>
|
||||
<span>{currentIndex === null ? "Q&A" : `${currentIndex + 1} / ${total}`}</span>
|
||||
<DirectionLink note={next}>Next →</DirectionLink>
|
||||
<PresentationPairingPanel role="presenter" controller={syncController} />
|
||||
</nav>
|
||||
|
||||
@@ -178,4 +178,21 @@ describe("PresenterRoute", () => {
|
||||
expect(screen.getByText(/Defense Q&A/i).closest("details")).toHaveAttribute("open");
|
||||
expect(screen.getByRole("link", { name: /Where is the AI agent in this thesis/i })).toHaveAttribute("aria-current", "page");
|
||||
});
|
||||
|
||||
it("keeps pairing and ended-session state available on Q&A routes", () => {
|
||||
mockedUsePresentationSync.mockReturnValue({
|
||||
...idleController(),
|
||||
state: { kind: "ended", reason: "presenter_ended" },
|
||||
});
|
||||
window.location.hash = "#discuss/where-is-ai-agent";
|
||||
|
||||
render(<PresenterRoute />);
|
||||
|
||||
expect(screen.getByRole("heading", { name: /Where is the AI agent/i })).toBeInTheDocument();
|
||||
const navigation = screen.getByRole("navigation", { name: /presenter note navigation/i });
|
||||
expect(navigation).toHaveTextContent("Q&A");
|
||||
expect(navigation.querySelectorAll('[aria-disabled="true"]')).toHaveLength(2);
|
||||
expect(screen.getByRole("button", { name: /pair presentation/i })).toBeInTheDocument();
|
||||
expect(screen.getByText("The presenter ended this session.")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -65,9 +65,9 @@ export const PresenterRoute = () => {
|
||||
? () => { moveFromCurrentHash("previous"); }
|
||||
: undefined}
|
||||
>
|
||||
{navigation.note && (
|
||||
{(navigation.note || discussion) && (
|
||||
<PresenterNavigationBar
|
||||
currentIndex={navigation.index}
|
||||
currentIndex={navigation.note ? navigation.index : null}
|
||||
total={presenterNotes.length}
|
||||
previous={navigation.previous}
|
||||
next={navigation.next}
|
||||
|
||||
Reference in New Issue
Block a user