Add design docs for achievable status fix and course description popups change artifacts
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-03-27
|
||||
68
openspec/changes/course-description-popups/design.md
Normal file
68
openspec/changes/course-description-popups/design.md
Normal file
@@ -0,0 +1,68 @@
|
||||
## Context
|
||||
|
||||
The app is a React (Vite/TypeScript) single-page app with no external UI library. Courses are rendered as clickable buttons in `CourseSelection.tsx`. There is no existing tooltip or popover infrastructure. The app already handles responsive layout via a `useMediaQuery` hook.
|
||||
|
||||
Course descriptions and instructors come from a static PDF (`ref/J27 Electives-Course Descriptions.pdf`) plus one supplementary markdown file (`ref/inovation-and-design.md`). The data is stable per cohort (J27).
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Show course description and instructor(s) inline via a popover triggered by an info icon
|
||||
- Work identically on desktop and mobile (click/tap, no hover or long-press)
|
||||
- Keep course description data separate from solver data structures
|
||||
|
||||
**Non-Goals:**
|
||||
- Dynamic fetching of descriptions from an API
|
||||
- Editing or updating descriptions at runtime
|
||||
- Changing the Course type or solver logic
|
||||
- Adding a third-party tooltip/popover library
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Separate data file keyed by course ID
|
||||
|
||||
Store descriptions in `app/src/data/courseDescriptions.ts` as a `Record<string, { description: string; instructors: string[] }>` keyed by course ID.
|
||||
|
||||
**Why over extending Course type:** Descriptions are long strings unrelated to solver logic. Keeping them separate preserves readability of `courses.ts` and avoids polluting the `Course` interface used throughout the solver.
|
||||
|
||||
**Why key by course ID (not name):** The same course name can appear in multiple elective sets with different instructors (e.g., "Collaboration, Conflict and Negotiation" — Steve Blader in Spring, Elizabeth Morrison in Summer). Per-ID keying handles this correctly at the cost of some description duplication.
|
||||
|
||||
### 2. Info icon trigger (not hover/long-press)
|
||||
|
||||
An `(i)` icon button next to each course name, clickable on all platforms.
|
||||
|
||||
**Why over CSS hover tooltip:** Hover doesn't work on touch devices. A separate info icon avoids conflicting with the existing click-to-select behavior on the course button.
|
||||
|
||||
**Why over long-press on mobile:** Long-press is discoverable only if users know to try it. An explicit icon is universally obvious.
|
||||
|
||||
### 3. Pure CSS/React popover (no library)
|
||||
|
||||
Build the popover as a positioned `div` managed with React state. Close on: click outside (document listener), re-click icon, or Escape key.
|
||||
|
||||
**Why no library:** The app has zero UI dependencies beyond React. A single popover component doesn't justify adding one. The positioning logic is straightforward since popovers anchor to a known icon element.
|
||||
|
||||
### 4. Popover content layout
|
||||
|
||||
```
|
||||
┌────────────────────────────────┐
|
||||
│ Course Name │
|
||||
│ Instructor(s): Name, Name │
|
||||
│ ────────────────────────────── │
|
||||
│ Description text, scrollable │
|
||||
│ if longer than max-height... │
|
||||
└────────────────────────────────┘
|
||||
```
|
||||
|
||||
- Fixed max-width (~320px), max-height (~300px) with overflow scroll
|
||||
- Instructor list shown as comma-separated
|
||||
- Close button (X) in top-right corner
|
||||
|
||||
### 5. Event handling: stop propagation on info icon
|
||||
|
||||
The `(i)` icon click must call `e.stopPropagation()` to prevent the parent course button's `onClick` (which pins the course) from firing.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Description duplication across sets** — Same description text stored under multiple course IDs (e.g., `spr1-collaboration` and `sum1-collaboration`). Acceptable given the small dataset (~35 entries) and the need for per-ID instructor differentiation.
|
||||
- **Popover positioning at screen edges** — A simple anchored popover could overflow the viewport on narrow screens. Mitigation: on mobile, render as a near-full-width card or use `position: fixed` centered overlay instead of anchored positioning.
|
||||
- **Stale data if courses change** — Descriptions are hardcoded. If the course list changes for a future cohort, both `courses.ts` and `courseDescriptions.ts` need updating. This matches the existing pattern (all course data is static).
|
||||
26
openspec/changes/course-description-popups/proposal.md
Normal file
26
openspec/changes/course-description-popups/proposal.md
Normal file
@@ -0,0 +1,26 @@
|
||||
## Why
|
||||
|
||||
Users selecting elective courses have no way to see course descriptions or instructor information within the app. They must cross-reference a separate PDF document to understand what each course covers. Adding inline course info popups reduces friction and helps users make more informed selections.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add a new data file mapping each course ID to its description text and instructor list, extracted from the J27 Electives PDF and supplementary sources
|
||||
- Add an info icon `(i)` next to each course name in the course selection UI
|
||||
- Clicking/tapping the info icon opens a popover displaying the course description and instructor(s)
|
||||
- Popover closes on click outside, clicking the icon again, or pressing Escape
|
||||
- Works identically on desktop and mobile (no hover/long-press distinction)
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `course-info-popover`: Inline popover UI triggered by an info icon on each course, displaying course description and instructor(s) from a static data source
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
## Impact
|
||||
|
||||
- New file: `app/src/data/courseDescriptions.ts` — static data (~35 course entries with descriptions and instructor arrays)
|
||||
- Modified: `app/src/data/types.ts` — no changes needed (data lives in separate lookup, not on Course type)
|
||||
- Modified: `app/src/components/CourseSelection.tsx` — add info icon and popover component
|
||||
- No solver, state, or worker changes
|
||||
- No new dependencies
|
||||
@@ -0,0 +1,80 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Course description data source
|
||||
The system SHALL maintain a static data file mapping each course ID to its description text and instructor list. Each entry SHALL contain a `description` string and an `instructors` string array. Only courses present in `courses.ts` SHALL have entries.
|
||||
|
||||
#### Scenario: Course with single instructor
|
||||
- **WHEN** looking up course ID `spr2-consumer-behavior`
|
||||
- **THEN** the entry contains a description string and `instructors: ["Radhika Duggal"]`
|
||||
|
||||
#### Scenario: Course with multiple instructors
|
||||
- **WHEN** looking up course ID `spr1-high-stakes`
|
||||
- **THEN** the entry contains `instructors: ["Steve Mellas", "Jim Donofrio"]`
|
||||
|
||||
#### Scenario: Same course name in different sets has per-ID entries
|
||||
- **WHEN** looking up `spr1-collaboration` and `sum1-collaboration`
|
||||
- **THEN** both have description entries, and instructors MAY differ between them
|
||||
|
||||
### Requirement: Info icon display
|
||||
Each course button in the course selection UI SHALL display a clickable info icon next to the course name. The icon SHALL be visible for all non-cancelled, non-disabled courses.
|
||||
|
||||
#### Scenario: Info icon visible on available course
|
||||
- **WHEN** a course is not cancelled and not disabled
|
||||
- **THEN** an info icon is displayed next to the course name
|
||||
|
||||
#### Scenario: Info icon hidden on cancelled course
|
||||
- **WHEN** a course is cancelled
|
||||
- **THEN** no info icon is displayed
|
||||
|
||||
#### Scenario: Info icon hidden on disabled course
|
||||
- **WHEN** a course is disabled (already selected in another set)
|
||||
- **THEN** no info icon is displayed
|
||||
|
||||
### Requirement: Popover opens on info icon click
|
||||
Clicking or tapping the info icon SHALL open a popover displaying the course description and instructor(s). Clicking the info icon SHALL NOT trigger course selection (pin).
|
||||
|
||||
#### Scenario: Open popover on click
|
||||
- **WHEN** user clicks the info icon on a course
|
||||
- **THEN** a popover appears showing the course name, instructor(s), and description
|
||||
- **THEN** the course is NOT pinned/selected
|
||||
|
||||
#### Scenario: Only one popover open at a time
|
||||
- **WHEN** a popover is open and user clicks a different course's info icon
|
||||
- **THEN** the first popover closes and the new one opens
|
||||
|
||||
### Requirement: Popover content layout
|
||||
The popover SHALL display the course name as a heading, instructor(s) as a comma-separated list, and the full description text. If the description exceeds the popover's max height, the content SHALL be scrollable.
|
||||
|
||||
#### Scenario: Display with single instructor
|
||||
- **WHEN** popover opens for a course with one instructor
|
||||
- **THEN** it shows "Instructor: Name"
|
||||
|
||||
#### Scenario: Display with multiple instructors
|
||||
- **WHEN** popover opens for a course with multiple instructors
|
||||
- **THEN** it shows "Instructors: Name1, Name2"
|
||||
|
||||
#### Scenario: Long description scrollable
|
||||
- **WHEN** popover opens for a course with a long description
|
||||
- **THEN** the popover has a max height and the content area is scrollable
|
||||
|
||||
### Requirement: Popover dismissal
|
||||
The popover SHALL close when the user clicks outside it, clicks the info icon again, or presses the Escape key.
|
||||
|
||||
#### Scenario: Close on click outside
|
||||
- **WHEN** a popover is open and user clicks outside of it
|
||||
- **THEN** the popover closes
|
||||
|
||||
#### Scenario: Close on Escape key
|
||||
- **WHEN** a popover is open and user presses Escape
|
||||
- **THEN** the popover closes
|
||||
|
||||
#### Scenario: Close on re-click info icon
|
||||
- **WHEN** a popover is open and user clicks the same info icon
|
||||
- **THEN** the popover closes
|
||||
|
||||
### Requirement: Mobile and desktop parity
|
||||
The popover interaction SHALL work identically on desktop and mobile via click/tap. No hover or long-press interactions are used.
|
||||
|
||||
#### Scenario: Mobile tap opens popover
|
||||
- **WHEN** user taps the info icon on a mobile device
|
||||
- **THEN** the popover opens, same as desktop click behavior
|
||||
21
openspec/changes/course-description-popups/tasks.md
Normal file
21
openspec/changes/course-description-popups/tasks.md
Normal file
@@ -0,0 +1,21 @@
|
||||
## 1. Data Layer
|
||||
|
||||
- [x] 1.1 Create `app/src/data/courseDescriptions.ts` with description and instructors array for each course ID, extracted from the PDF and `ref/inovation-and-design.md`
|
||||
- [x] 1.2 Verify all course IDs in `courses.ts` have corresponding entries (no missing, no extras)
|
||||
|
||||
## 2. Popover Component
|
||||
|
||||
- [x] 2.1 Build a `CourseInfoPopover` component that displays course name, instructor(s), and scrollable description
|
||||
- [x] 2.2 Add dismiss logic: click outside, Escape key, re-click info icon
|
||||
- [x] 2.3 Ensure only one popover is open at a time
|
||||
|
||||
## 3. Info Icon Integration
|
||||
|
||||
- [x] 3.1 Add info icon button next to each course name in `CourseSelection.tsx`
|
||||
- [x] 3.2 Use `stopPropagation` on info icon click to prevent course pinning
|
||||
- [x] 3.3 Hide info icon for cancelled and disabled courses
|
||||
|
||||
## 4. Responsive / Polish
|
||||
|
||||
- [x] 4.1 Handle popover positioning on narrow screens (centered overlay or full-width card on mobile)
|
||||
- [x] 4.2 Verify desktop and mobile parity (click/tap only, no hover/long-press)
|
||||
Reference in New Issue
Block a user