Skip to content

Mood Logging — Feature & Testing Guide

Overview

Mood logging allows users to record how they're feeling with a quick daily check-in. Each mood log captures one of six mood states, an optional text note (max 300 characters), and an optional link to a specific goal. Mood data helps users track emotional patterns alongside their goal progress.


Mood Check-In Flow

Opening the Mood Check-In

The mood check-in modal can be accessed from:

  1. Dashboard — In the "Today's practice" section, when no mood has been logged yet, a "How's today feeling?" prompt appears as a tappable row. Tapping it opens the mood check-in modal in goal-less mode, allowing users to log a general daily mood. Once a mood is logged, the prompt is replaced with a confirmation row that reads "Mood logged today."
  2. Goal Detail Page — when logging an update, a mood selector is embedded in the GoalEventForm

Step-by-Step Flow

  1. Modal opens with "How are you feeling?" header
  2. Select a mood from the Mood Selector (required):
MoodEmojiValue
Amazing😄amazing
Happy😊happy
Calm😌calm
Meh😐meh
Tired😴tired
Low😞low
  1. Optionally link to a goal via the Goal Selector dropdown
    • Shows "No goal" as the first option (null value)
    • Lists all active goals (label/value pairs)
  2. Optionally add a Note (max 300 characters)
    • Auto-grow textarea with "What's on your mind?" placeholder
    • Character counter shows X/300 at bottom
  3. Tap Log Check-In to submit

What Gets Stored

  • mood: Required enum — one of amazing, happy, calm, meh, tired, low
  • note: Optional string, max 300 characters (trimmed before submission)
  • goal_id: Optional FK linking the mood to a specific goal
  • user_id: Auto-set from authenticated user
  • created_at: Timestamp of the check-in

Key Behaviors

  • Mood is required — the submit button is disabled until a mood is selected
  • Note and goal are optional — only included in the mutation if provided
  • The form resets after successful submission
  • The modal emits a closed event and calls the onSuccess callback on submit

Mood Selector Component

The MoodSelector is a reusable component used in both MoodCheckIn and GoalEventForm:

  • Displays 6 mood options in a horizontal row
  • Each option is a circular button with emoji + label
  • Selected mood gets a highlight ring and slightly enlarged styling
  • Emits the selected mood value to the parent component
  • Visual feedback on tap/click

Where Mood Data Appears

  1. Goal Events — When a mood is selected during a goal update, it's attached to the GoalEvent record and displayed as a mood emoji badge on the timeline event card
  2. User Profile / Stats — Mood history contributes to wellness tracking metrics
  3. Coach Insights — The Coach may reference mood patterns when generating insights

UI Components

MoodCheckIn Modal

  • Header: "How are you feeling?" with close button (X)
  • Body: MoodSelector + Goal dropdown + Note textarea with counter
  • Footer: "Log Check-In" button (disabled until mood selected)
  • Loading state: button shows spinner during submission

MoodSelector

  • Horizontal flex row of 6 emoji buttons
  • Each button: emoji icon + mood label text
  • Selected state: primary-colored ring, subtle scale transform
  • Accessible: buttons have aria labels

Testing the Feature

Manual Testing Checklist

1. Open Mood Check-In

  • [ ] Navigate to Dashboard
  • [ ] Open the mood check-in modal
  • [ ] Verify "How are you feeling?" header displays
  • [ ] Verify 6 mood options are shown

2. Select a Mood

  • [ ] Tap "Amazing" → verify it highlights with selection ring
  • [ ] Tap "Meh" → verify Amazing deselects and Meh highlights
  • [ ] Verify only one mood can be selected at a time
  • [ ] Verify "Log Check-In" button becomes enabled after selection

3. Submit Without Optional Fields

  • [ ] Select a mood
  • [ ] Leave note empty and goal unselected
  • [ ] Tap Log Check-In
  • [ ] Verify submission succeeds
  • [ ] Verify modal closes

4. Submit With Note

  • [ ] Select a mood
  • [ ] Type a note: "Feeling good about my progress"
  • [ ] Verify character counter shows correct count (e.g., "35/300")
  • [ ] Submit → verify success

5. Note Character Limit

  • [ ] Type a note approaching 300 characters
  • [ ] Verify counter updates in real-time
  • [ ] Verify input stops at exactly 300 characters (maxlength enforced)
  • [ ] Verify counter shows "300/300"
  • [ ] Select a mood
  • [ ] Open the goal dropdown
  • [ ] Verify "No goal" is the first option
  • [ ] Select a goal from the list
  • [ ] Submit → verify the mood log is linked to the goal

7. Submit with All Fields

  • [ ] Select "Happy" mood
  • [ ] Link to a goal
  • [ ] Add a note: "Great session today"
  • [ ] Submit → verify all fields are saved
  • [ ] Verify the form resets after submission

8. Validation

  • [ ] Open the modal without selecting a mood
  • [ ] Verify "Log Check-In" button is disabled
  • [ ] Verify button text is not "Saving..."
  • [ ] Select a mood → verify button enables

9. Mood in Goal Events

  • [ ] Navigate to a goal detail page
  • [ ] Tap Add Update
  • [ ] Verify MoodSelector appears in the event form
  • [ ] Select a mood, add content, submit
  • [ ] Verify the mood emoji appears on the event card in the timeline

Smoke Tests

Automated smoke test:

  • ionic_frontend/tests/smoke-playwright/specs/mood-lifecycle/06-mood-log.spec.ts — Logs a mood check-in

Run locally:

bash
cd ionic_frontend
npm run smoke:playwright:staging

Backend Unit Tests

bash
cd rails_api

# Model specs
bundle exec rspec spec/models/mood_log_spec.rb

# Interaction specs
bundle exec rspec spec/interactions/user_identity/add_mood_log_spec.rb

# GraphQL specs
bundle exec rspec spec/requests/mutations/add_mood_log_spec.rb

Frontend Unit Tests

bash
cd ionic_frontend

npm run test:unit -- --run -t "MoodCheckIn"
npm run test:unit -- --run -t "MoodSelector"

GraphQL API Reference

Queries

Mood logs are returned as part of the user query:

graphql
query UserQuery($id: ID!) {
  user(id: $id) {
    moodLogs {
      publicId
      mood            # "amazing" | "happy" | "calm" | "meh" | "tired" | "low"
      note            # Optional string (max 300 chars)
      createdAtTime   # Timestamp
      goal { publicId name }
    }
  }
}

Mutations

graphql
mutation AddMoodLog($mood: String!, $note: String, $goalId: ID) {
  addMoodLog(mood: $mood, note: $note, goalId: $goalId) {
    moodLog {
      publicId
      mood
      note
      createdAtTime
      goal { publicId name }
    }
    errors
  }
}

Known Behaviors & Edge Cases

  1. No daily limit: Users can log multiple mood check-ins per day. Each creates a separate MoodLog record. There is no deduplication.

  2. Mood enum validation: The backend validates mood is one of the 6 allowed values. Invalid values return an error.

  3. Note trimming: The note is trimmed before submission. Leading/trailing whitespace is removed. An all-whitespace note becomes null.

  4. Goal linking is optional: If goalId is not provided (or "No goal" is selected), the mood log is saved without a goal association.

  5. Mood in goal events: When a mood is selected in the GoalEventForm, it's stored on the GoalEvent record (not as a separate MoodLog). These are different data paths — the mood check-in modal creates a MoodLog, while the goal update form stores mood on the GoalEvent.

  6. No edit/delete: Mood logs cannot be edited or deleted through the UI. They are immutable once created.

  7. Character counter UX: The counter only appears below the textarea, not above. It shows {current}/300 format and does not turn red at the limit — the maxlength attribute prevents overflow.

Last updated: 2026-05-22

Loading…