Skip to content

fix: handle new card creation from board footer - #8928

Open
tollenceld wants to merge 3 commits into
AppFlowy-IO:mainfrom
tollenceld:fix/8922-board-footer-card-creation
Open

tollenceld wants to merge 3 commits into
AppFlowy-IO:mainfrom
tollenceld:fix/8922-board-footer-card-creation

Conversation

@tollenceld

@tollenceld tollenceld commented Aug 6, 2026

Copy link
Copy Markdown

Fixes #8922

Problem

On desktop, in a board's column footer, clicking + New, typing a title, and then clicking outside the input closed the input without creating a card but kept the typed draft. Reopening + New showed the stale draft, and pressing Enter could then create a card with an empty title (reported as "Untitled"). Escape also left the old draft in place for the next attempt.

Root cause

In _BoardColumnFooterState (frontend/appflowy_flutter/lib/plugins/database/board/presentation/board_page.dart):

  • Losing focus only set _isCreating = false; the TextEditingController was never cleared, so the draft survived.
  • Escape only called _focusNode.unfocus(), which went through the same blur path and left the draft intact.
  • onSubmitted had no empty-title guard, so pressing Enter with an empty title dispatched BoardEvent.createRow with an empty name, producing a card with an empty title. Because the input stayed focused after submit and the controller was cleared immediately after dispatch, a second Enter submitted the now-empty title.

Behavior

Consistent with the board's other trailing create input (_BoardTrailingState, "Add a new group", which cancels and clears on Escape and on blur), the footer now follows:

  • Enter with a non-empty title → create the card (existing behavior, kept).
  • Empty title + Enter → no-op (no empty/"Untitled" card).
  • Blur (click outside) → cancel creation and clear the draft.
  • Escape → cancel creation and clear the draft.
  • Reopening + New always starts with a clean, empty input.

Changes

  • _BoardColumnFooterState:
    • Added _cancelCreating(), which clears the controller and exits the creating state.
    • The focus-loss listener now calls _cancelCreating(), so blur and Escape share one cancellation path (Escape just unfocuses, and the listener performs the single cancellation).
    • onSubmitted ignores empty/whitespace-only titles.

Tests

Extended frontend/appflowy_flutter/integration_test/desktop/board/board_add_row_test.dart with five footer scenarios: empty-title submit, click-outside cancel + draft clear, Escape cancel + draft clear, double Enter, and submit-then-click-outside.

Local results:

  • flutter analyze (full project): No issues found.
  • dart format --output=none --set-exit-if-changed on changed files: clean.
  • Board integration tests (board_add_row_test.dart, macOS local): 4 of 7 passed (from header, from footer, empty title submit, clicking outside). The escape test could not complete: sendKeyEvent(escape) hangs in the macOS live binding (key-event channel round trip). The same sendKeyEvent(escape) pattern is already used by existing board tests (board_row_test.dart, board_hide_groups_test.dart) on the repo's Linux CI, so this appears to be a local macOS tooling quirk rather than a product regression. The remaining two tests were not run locally.

Not run locally / pending: full integration-test pass on Linux (repo CI runs board tests via integration_test/desktop_runner_3.dart) and Windows behavior.

Manual verification

The contributor manually verified the current fix on a real device and did not observe any issues. The specific device and platform are not claimed here, and this does not replace automated cross-platform CI coverage.

CI and limitations

  • Ninja i18n check passes. license/cla is pending (the contributor must sign the CLA via the CLA assistant link; this cannot be done from the CLI).
  • The Flutter-CI workflow is not triggered while the PR is a draft, and external-contributor workflows may require a maintainer to approve them; action_required on the workflow means "awaiting maintainer approval", not a test failure.
  • Windows and other platforms have not been tested locally; the change is platform-neutral Flutter code.

Draft status

Still a draft. Waiting on:

Summary by Sourcery

Ensure board column footer new-card input cancels cleanly and does not create unintended cards when blurred, escaped, or submitted with empty text.

Bug Fixes:

  • Prevent creating cards with empty or whitespace-only titles from the board column footer input.
  • Clear any draft text and exit creation mode when the footer input loses focus or Escape is pressed, avoiding stale drafts and accidental submissions.

Tests:

  • Extend desktop board integration tests to cover footer behaviors for empty submit, blur cancel, Escape cancel, double Enter, and submit-then-blur without creating duplicates.

Clear the card draft when creation is cancelled (Escape or focus loss) so
reopening '+ New' starts with an empty input, and ignore empty titles on
submit so a blank Enter cannot create an 'Untitled' card.

Fixes AppFlowy-IO#8922
@CLAassistant

CLAassistant commented Aug 6, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@sourcery-ai

sourcery-ai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Updates the board column footer’s new-card creation flow to cancel and clear drafts on blur/Escape, prevent empty-title submissions, and adds integration tests to lock in the corrected behaviors.

Sequence diagram for updated board footer card creation interactions

sequenceDiagram
  actor User
  participant BoardColumnFooter as _BoardColumnFooterState
  participant FocusNode as _focusNode
  participant BoardBloc

  User->>BoardColumnFooter: tap_plus_New()
  BoardColumnFooter->>BoardColumnFooter: setState(_isCreating = true)

  User->>BoardColumnFooter: type_title_in_textField()

  alt blur or Escape
    User->>FocusNode: unfocus()
    FocusNode-->>BoardColumnFooter: hasFocus == false (listener)
    BoardColumnFooter->>BoardColumnFooter: _cancelCreating()
    BoardColumnFooter->>BoardColumnFooter: _textController.clear()
    BoardColumnFooter->>BoardColumnFooter: setState(_isCreating = false)
  end

  alt submit non_empty_title
    User->>BoardColumnFooter: onSubmitted(name)
    BoardColumnFooter->>BoardColumnFooter: [name.trim().isNotEmpty]
    BoardColumnFooter->>BoardBloc: BoardEvent.createRow(columnId, name)
    BoardColumnFooter->>BoardColumnFooter: _textController.clear()
  else submit empty_or_whitespace_title
    User->>BoardColumnFooter: onSubmitted(name)
    BoardColumnFooter->>BoardColumnFooter: [name.trim().isEmpty]
    BoardColumnFooter-->>User: no_op (no card created)
  end
Loading

File-Level Changes

Change Details Files
Unify footer cancel behavior so blur/Escape both exit creating mode and clear the draft.
  • Wrap footer cancel logic in a new _cancelCreating() helper that clears the text controller and toggles _isCreating to false.
  • Update the FocusNode listener to invoke _cancelCreating() instead of only flipping _isCreating, so losing focus always clears the draft.
frontend/appflowy_flutter/lib/plugins/database/board/presentation/board_page.dart
Prevent creating cards with empty or whitespace-only titles from the column footer input.
  • Add a guard in the footer TextField onSubmitted callback that returns early when the submitted name is empty or all whitespace before dispatching BoardEvent.createRow.
frontend/appflowy_flutter/lib/plugins/database/board/presentation/board_page.dart
Add integration tests covering footer new-card creation edge cases and interactions.
  • Introduce a helper Finder _footerTextField() that targets the footer TextField of the second column.
  • Import flutter/services.dart to access LogicalKeyboardKey.escape in tests.
  • Add tests for empty-title submit doing nothing, click-outside cancel + draft clear, Escape cancel + draft clear, double Enter creating only one card, and submit-then-click-outside not creating a duplicate card.
frontend/appflowy_flutter/integration_test/desktop/board/board_add_row_test.dart

Assessment against linked issues

Issue Objective Addressed Explanation
#8922 When adding a card from the board column footer, clicking outside the input should create a card with the typed title instead of hiding or canceling it. The PR explicitly changes blur behavior to cancel creation and clear the draft (_cancelCreating() on focus loss) rather than creating a card. This fixes the hidden/stale draft bug but does not implement the requested behavior where clicking outside should create the card.
#8922 Prevent creation of cards with empty or "Untitled" titles when submitting via Enter from the column footer input.
#8922 Ensure that when card creation is cancelled (e.g., by clicking outside or pressing Escape), any typed draft title is cleared so reopening "+ New" shows an empty, unselected input.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Escape now relies on the shared focus-loss cancellation path instead of
cancelling twice in a row, and the PR description is corrected to match
the actual behavior.

The contributor manually verified the current fix on a real device and did
not observe any issues. This does not replace pending cross-platform CI.
@tollenceld
tollenceld marked this pull request as ready for review August 6, 2026 15:19

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The new tests rely on find.byType(BoardColumnFooter).at(1) for the footer, which is brittle if column ordering or counts change; consider using a more robust finder (e.g., by key or text) to target the intended footer.
  • There is a lot of repeated setup across the new footer tests (initialize app, sign in, create board page, tap footer); extracting a shared helper or setUp routine would make the tests easier to maintain and adjust in the future.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new tests rely on `find.byType(BoardColumnFooter).at(1)` for the footer, which is brittle if column ordering or counts change; consider using a more robust finder (e.g., by key or text) to target the intended footer.
- There is a lot of repeated setup across the new footer tests (initialize app, sign in, create board page, tap footer); extracting a shared helper or `setUp` routine would make the tests easier to maintain and adjust in the future.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Replace the index-based footer lookup with a finder that targets the column
by its group name, and extract shared board/footer test setup helpers.

No product behavior was changed.

The contributor manually verified the current fix on a real device and did
not observe any issues. This does not replace pending cross-platform CI.
@tollenceld

Copy link
Copy Markdown
Author

Addressed the Sourcery feedback by replacing the index-based footer lookup with a stable finder and extracting shared board/footer test setup helpers. No product behavior was changed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Add a new card at the bottom

2 participants