Skip to content

fix(DateInput): pass the picked date to onClose when controlled - #1347

Draft
elbaar wants to merge 1 commit into
masterfrom
dateinput-controlled-onclose
Draft

elbaar wants to merge 1 commit into
masterfrom
dateinput-controlled-onclose

Conversation

@elbaar

@elbaar elbaar commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

What's wrong

With a controlled value prop, DateInput's onClose never sees a date picked from the calendar. It always gets the value from before the pick.

Picking a day calls setDate, which sets state.value and open: false in one setState. componentDidUpdate sees the close and fires onClose right away, but it reads this.props.value whenever that's defined. The parent hasn't re-rendered with the new value yet (onChange runs in the setState callback, after componentDidUpdate), so onClose gets the old one.

Uncontrolled callers aren't affected because they already read state.value.

The fix

  • In componentDidUpdate, use state.value for onClose when it's uncontrolled or when state.value changed in the update that closed the picker (a pick or Today). Otherwise keep using the value prop. That way, if the parent changes value on its own (a "clear filters" reset, say) and the user then opens and closes the picker without picking, onClose still reports the parent's value and not an old pick left in state.
  • Add onClose to the props pulled out in render so it isn't spread onto the <input> as an unknown DOM prop.

Tests

New onClose specs in DateInput.spec.js:

  • controlled, pick a day → onClose gets the picked date (failed before the fix)
  • uncontrolled, pick a day → onClose gets the picked date (regression guard)
  • controlled, parent changes value, open then Esc → onClose gets the new prop value (guards the case above)
  • onClose isn't passed to the <input> (failed before the fix)

yarn lint, yarn format and yarn test pass locally.

Why now

This came up in https://github.com/appfolio/apm_bundle/pull/126484 (owners Advanced Filters date inputs). The filter needed to act when the picker closed, but onClose with a controlled value always got the old date, so that PR had to avoid onClose entirely. The only other onClose callers in apm_bundle work only because they're uncontrolled.

🤖 Generated with Claude Code

[GROWN-45]

Picking a day sets state.value and closes the picker in the same update,
before the parent re-renders with the new value prop. onClose read the
value prop whenever it was defined, so controlled callers always got the
pre-selection value. Prefer state.value when it changed in the closing
update; otherwise keep using the value prop so external changes still win.

Also stop spreading onClose onto the <input> as an unknown DOM prop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Released prerelease version 8.19.7-dateinput-controlled-onclose-5392c4f.0.
You may now run npm install @appfolio/react-gears@dateinput-controlled-onclose

@elbaar

elbaar commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

One decision needed that impacts user experience:

  1. onClose always reads this.state.value. (lambo's solution, shown left)
  2. onClose reads state.value only when it changed in the same update that closed the picker. Otherwise it keeps reading the value prop. (this PR, shown right)
Screen.Recording.2026-10-20.at.11.07.02.AM.mov

Steps:
Pick a day in From.
Click Reset to Default.
Click into From and click away without picking anything.

Expect From to stay empty on 1, and the old date to come back on 2.

@mike-mike-mike-mike-mike mike-mike-mike-mike-mike 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.

Let's try to get this merged!

This branch has not been deployed

No deployments
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.

2 participants