feat(textfield, number-field): add tooltip for a11y on truncated input text#6038
feat(textfield, number-field): add tooltip for a11y on truncated input text#6038rise-erpelding wants to merge 24 commits intomainfrom
Conversation
🦋 Changeset detectedLatest commit: edee920 The changes in this PR will be included in the next version bump. This PR includes changesets to release 83 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
a03db36 to
c46b5e3
Compare
📚 Branch Preview Links🔍 First Generation Visual Regression Test ResultsWhen a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:
Deployed to Azure Blob Storage: If the changes are expected, update the |
c6a7d36 to
7d06234
Compare
| el.value = 45; | ||
| expect(el.value).to.equal(45); | ||
| el.focus(); | ||
| await sendKeys({ type: '7' }); // Visible text: EUR 45.007 |
There was a problem hiding this comment.
Just linting fixes in this file below this point
There was a problem hiding this comment.
Liked the approach here. Most of the direction looks sound to me except for a few areas which needs attention. One major blocker is the the password field security issue and the added dependencies on the textfield is a bundle size concern for me, we can discuss this with the team and the eager ResizeObserver allocation will hinder performance. Try to optimize and think of a more performant approach.
Most of the other things looks solid and thanks for handling this a11y compliance. Happy to work with you in resolving the blocker.
nikkimk
left a comment
There was a problem hiding this comment.
I think we might need to discuss our options with this one given the a11y concerns.
5t3ph
left a comment
There was a problem hiding this comment.
In addition to being concerned about the issue for screen readers, I noticed that for number field the tooltip does not reflect real-time edits to the value (deletions or insertions).
The value does update real-time for textfield, but still with a bit of oddness that the tooltip is removed if the value decreases below the threshold (which is a little jarring, possible cognitive issue ex "wait, what just happened? did I do something wrong?"). But, it doesn't re-appear when the value increases above the threshold. Not reappearing might be ok, since the user is actively in the field and their cursor position is still allowing them to see the relevant part that they're editing.
7ccfd84 to
aebf001
Compare
- change name of resize observer so it doesn't clash - add guard for inputElement
- also add aria-hidden to tooltip
- Do not reflect tooltip-placement - Trigger refreshTruncationState from update() only on value change - Run observer setup and refreshTruncationState only when !multiline - Test import remains from '../'
bca6b86 to
fbaeb28
Compare
| this._resizeObserver.disconnect(); | ||
| this._resizeObserver = null; | ||
| } | ||
| this._observerInitialized = false; |
There was a problem hiding this comment.
If the host is disconnected and then reconnected, the observer won't be recreated until hostUpdated() runs. This seems fine for most cases, but only if the host actually triggers an update cycle after reconnection. If it doesn't (e.g. no property changes), the observer won't be re-attached.
| this.truncatedValueTooltipController.refresh(); | ||
| if (justBecameTruncated) { | ||
| this.updateComplete.then(() => { | ||
| this.truncatedValueTooltipController.syncTooltipText( |
There was a problem hiding this comment.
nit: if it justBecameTruncated, we'd be syncing twice (it's already happening above on line 549).
| this.refreshTruncationState(); | ||
| }); | ||
| this._resizeObserver.observe(this.host as unknown as Element); | ||
| if (host.inputElement) { |
There was a problem hiding this comment.
Are we sure this is guaranteed to exist on hostUpdated? If host.inputElement is null on the first hostUpdated() call, only the host element gets observed. On subsequent renders, _observerInitialized is already true so the early return skips observing inputElement even once it exists.
| const overlayWithDescribe = this.overlay as { | ||
| describeTrigger?: 'auto' | 'none'; | ||
| }; |
There was a problem hiding this comment.
nit: couldn't we use the Overlay type directly?
Description
Adds truncated-value tooltip behavior to single-line text inputs in
sp-textfieldandsp-number-field.Updates (2/27/26):
describeTriggerproperty to overlay to prevent overlay from settingaria-describedby, thus preventing screen reader double announcementMotivation and context
Long values in narrow fields can be difficult to review, especially while correcting invalid input.
This change improves usability by exposing the full value on hover/focus only when truncation occurs, while keeping default behavior unchanged when content fits.
Related issue(s)
Screenshots (if appropriate)
Author's checklist
Reviewer's checklist
patch,minor, ormajorfeaturesManual review test cases
Textfield shows tooltip only when visually truncated
Textfield / truncatedValueTooltipstory.Invalid truncated textfield still exposes full value
Number-field truncation behavior works with and without stepper
Number Field / truncatedValueTooltipstory.Regression checks
Updates (2/27/26):
Device review
Accessibility testing checklist
Required: Complete each applicable item and document your testing steps (replace the placeholders with your component-specific instructions).
Keyboard (required — document steps below) — What to test for: Focus order is logical; Tab reaches the component and all interactive descendants; Enter/Space activate where appropriate; arrow keys work for tabs, menus, sliders, etc.; no focus traps; Escape dismisses when applicable; focus indicator is visible.
Screen reader (required — document steps below) — What to test for: Role and name are announced correctly; state changes (e.g. expanded, selected) are announced; labels and relationships are clear; no unnecessary or duplicate announcements.
aria-describedby; addressing this felt a bit out-of-scope