Enhance classic editor timestamp fields with native controls - #12275
Enhance classic editor timestamp fields with native controls#12275poligilad-auto wants to merge 1 commit into
Conversation
|
Hi there! 👋 Thank you for your contribution to WordPress! 💖 It looks like this is your first pull request to No one monitors this repository for new pull requests. Pull requests must be attached to a Trac ticket to be considered for inclusion in WordPress Core. To attach a pull request to a Trac ticket, please include the ticket's full URL in your pull request description. Pull requests are never merged on GitHub. The WordPress codebase continues to be managed through the SVN repository that this GitHub repository mirrors. Please feel free to open pull requests to work on any contribution you are making. More information about how GitHub pull requests can be used to contribute to WordPress can be found in the Core Handbook. Please include automated tests. Including tests in your pull request is one way to help your patch be considered faster. To learn about WordPress' test suites, visit the Automated Testing page in the handbook. If you have not had a chance, please review the Contribute with Code page in the WordPress Core Handbook. The Developer Hub also documents the various coding standards that are followed:
Thank you, |
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Test using WordPress PlaygroundThe changes in this pull request can previewed and tested using a WordPress Playground instance. WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser. Some things to be aware of
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
tyxla
left a comment
There was a problem hiding this comment.
Thanks for this work @poligilad-auto.
I've got some higher-level design and approach questions, but I'll post them on the ticket.
Left a few questions on the code in the meantime. The biggest one is that handling the field rendering with JS is subpar and should ideally be done in the PHP template.
node .context/classic-datetime-smoke.mjs
I wasn't able to where this is coming from
| updateNativeTimestampFields(); | ||
| $timestampdiv.addClass( 'has-native-timestamp-fields' ); | ||
| $timestampwrap.before( $nativeTimestampWrap ).hide(); | ||
| $nativeInput.on( 'change input', updateFieldsFromNativeTimestamp ); |
There was a problem hiding this comment.
Binding to input runs the full-format regex on every keystroke, so while someone is mid-typing a partial value (2026-06-2) it fails the match and adds form-invalid, and the field flashes red as they type. Could we validate on change only (and update the legacy fields there), and avoid flagging invalid on transient input events? If we want live sync, we could debounce, but the invalid styling shouldn't fire until the user commits the value.
There was a problem hiding this comment.
Updated. The native controls now listen on change only, so the full-format validation does not run while a user is still mid-typing. Invalid styling is only applied after the value is committed/saved.
| if ( updateFieldsFromNativeTimestamp && ! updateFieldsFromNativeTimestamp() ) { | ||
| event.preventDefault(); | ||
| return; | ||
| } | ||
|
|
||
| if ( updateText() ) { |
There was a problem hiding this comment.
updateFieldsFromNativeTimestamp() already returns updateText(), and then the existing if ( updateText() ) below calls it again, making it run twice per save. Likely we can lean on the first call's return value instead of re-invoking.
There was a problem hiding this comment.
Updated. updateFieldsFromNativeTimestamp() is now the single save path and the save handler uses its return value instead of calling updateText() again.
| $( '#mm' ).val( matches[2] ); | ||
| $( '#jj' ).val( matches[3] ); | ||
| $( '#hh' ).val( matches[4] ); | ||
| $( '#mn' ).val( matches[5] ); |
There was a problem hiding this comment.
The native control has no seconds and we don't touch #ss here, so the original seconds are carried through to submit. That actually matches the legacy behavior (seconds live in a hidden field there too), so it's not a data bug, but it's worth a one-line comment noting it's intentional, since the visible time no longer reflects the full stored value.
There was a problem hiding this comment.
Updated. Added a comment noting that seconds intentionally remain in the existing hidden #ss field, matching the legacy timestamp UI.
| $timestampdiv.addClass( 'has-native-timestamp-fields' ); | ||
| $timestampwrap.before( $nativeTimestampWrap ).hide(); | ||
| $nativeInput.on( 'change input', updateFieldsFromNativeTimestamp ); | ||
| $timestampwrap.find( 'input, select' ).on( 'change', updateNativeTimestampFields ); |
There was a problem hiding this comment.
Yes. In the enhanced UI those legacy controls are hidden, but they remain the source of truth/fallback and are still synced from the native controls before save.
| $('#aa').val($('#hidden_aa').val()); | ||
| $('#hh').val($('#hidden_hh').val()); | ||
| $('#mn').val($('#hidden_mn').val()); | ||
| if ( updateNativeTimestampFields ) { |
There was a problem hiding this comment.
Those checks may be easy to forget and clutter the code, would be nice to figure something else out, like defaulting them to no-ops.
There was a problem hiding this comment.
Updated. The native timestamp helpers now default to no-op behavior, so the save/cancel handlers do not need repeated existence checks.
| } | ||
|
|
||
| toNativeValue = function() { | ||
| return $( '#aa' ).val() + '-' + |
There was a problem hiding this comment.
Should we pad years to 4 digits too?
There was a problem hiding this comment.
Good point. The native date value is now rendered and synced in the expected YYYY-MM-DD shape.
| margin: 3px 0 0; | ||
| } | ||
|
|
||
| #timestampdiv .timestamp-native-wrap + p { |
There was a problem hiding this comment.
This couples spacing to the sibling DOM order; if the markup around the wrap ever shifts, the spacing silently breaks. Could we target a class on the element we actually want to space instead of + p?
There was a problem hiding this comment.
Updated. The spacing now targets explicit timestamp classes instead of relying on sibling DOM order.
| '<p class="timestamp-native-wrap hide-if-no-js">' + | ||
| '<label for="publish-datetime-native" class="screen-reader-text">' + __( 'Date and time' ) + '</label>' + | ||
| '<input type="datetime-local" id="publish-datetime-native" class="form-required" />' + | ||
| '</p>' |
There was a problem hiding this comment.
The rest of this UI is server-rendered in touch_time(). Building a new labeled field as a concatenated HTML string in JS diverges from that pattern, makes the field invisible to anything inspecting server output, and splits the timestamp UI across two rendering models. I think we should render that on the PHP side instead.
There was a problem hiding this comment.
Updated. The native date/time fields are now rendered server-side in touch_time(), and the JS only activates/syncs them when native date and time inputs are supported.
e7df860 to
64c413f
Compare
31e039f to
76129d2
Compare
76129d2 to
965b41d
Compare
|
Thanks for the review, @tyxla. I updated the PR based on the ticket discussion and this feedback. The approach no longer uses a single I think this addresses the main concerns more directly:
I’ll reply to the code-specific comments inline as well. |
tyxla
left a comment
There was a problem hiding this comment.
Left a review, and from the code perspective, didn't see anything major. Left a few suggestions.
The biggest concerns here remain:
- Ensuring / gathering feedback and confirming this is the design we want to go with
- Ensuring this doesn't break any existing contracts that would cause breakage of existing plugins integrating with the pre-existing controls
| /* translators: Hidden accessibility text. */ | ||
| _e( 'Edit date and time' ); | ||
| ?> | ||
| <span class="timestamp-display"> |
There was a problem hiding this comment.
Noting that some of these markup changes might be necessary below in some of the other meta boxes. Or we need to ensure that our CSS changes affect only this one. But right now the changes may affect the other meta boxes in weird ways because some of those markup changes are missing
| .misc-pub-curtime #timestamp:before { | ||
| content: none; | ||
| } |
| $timestampdiv.find( '.timestamp-wrap, #publish-date-native' ).addClass( 'form-invalid' ); | ||
| return false; | ||
| } else { | ||
| $timestampdiv.find('.timestamp-wrap').removeClass('form-invalid'); | ||
| $timestampdiv.find( '.timestamp-wrap, .timestamp-native-wrap input' ).removeClass( 'form-invalid' ); |
There was a problem hiding this comment.
Should we make these selectors consistent?
| $timezone = wp_timezone_string(); | ||
| if ( preg_match( '/^([+-])(\d{2}):(\d{2})$/', $timezone, $timezone_matches ) ) { | ||
| $timezone = 'UTC' . $timezone_matches[1] . (int) $timezone_matches[2]; | ||
| $timezone .= ( '00' === $timezone_matches[3] ) ? '' : ':' . $timezone_matches[3]; | ||
| } |
There was a problem hiding this comment.
UTC+0 might be weird - should it just be UTC?
Follow-up to the review on PR WordPress#12275 by @tyxla: - Remove a duplicated `.misc-pub-curtime #timestamp:before` rule, keeping the later declaration so it still overrides `.curtime #timestamp:before` (equal specificity, so source order decides). - Align the native date/time invalid-state selectors: mark and clear `form-invalid` on both native inputs via `.timestamp-native-wrap input`, instead of flagging only `#publish-date-native` by ID. - Render a zero UTC offset as `UTC` rather than `UTC+0` in the site-time note. - Size the Publish-box timestamp icon column with `auto` instead of a fixed `27px`, so the row's label lines up with the sibling meta rows. Props poligilad, tyxla. See WordPress#12275.
|
I've opened #13050 as a draft, which carries Poli's commit unchanged and adds a follow-up commit addressing the code comments from @tyxla's last review.
(Thanks Marin!) |
…pat. Wrapping the timestamp display and the Edit link in a `.timestamp-display` span moved `a.edit-timestamp` out from being a sibling of `#timestampdiv`. A scan of the plugin directory found several plugins (e.g. PublishPress Statuses, Media Library Assistant, LH Archived Post Status) that rely on `$( '#timestampdiv' ).siblings( 'a.edit-timestamp' )`, which the wrapper would break. Keep `#timestamp` and `a.edit-timestamp` as direct children of `.misc-pub-curtime` and lay the row out with a three-column grid instead. The rendered result and the row alignment are unchanged. See WordPress#12275.
Summary
dateandtimecontrols as a progressive enhancement for the classic editor publish timestamp UI.touch_time()granular fields as the source of truth and fallback, syncing the native values back toaa,mm,jj,hh, andmn.Trac ticket: https://core.trac.wordpress.org/ticket/61652
Screenshots
Testing
node .context/classic-datetime-smoke.mjs.npx grunt jshint:core.php -l src/wp-admin/includes/template.php.vendor/bin/phpcs --standard=phpcs.xml.dist src/wp-admin/includes/template.php.