Repository navigation
Show the full report field name in a tooltip when it is cut off - #105279
Conversation
Adds an opt-in shouldShowLabelTooltipWhenTruncated prop to the text input. On web, the label reports whether it is cut off (on layout and after its font-size animation settles), and the input container shows the full label in a tooltip on hover. Report fields turn this on for date, list, and text fields. GenericTooltip now keeps its children in the same place in the tree whether or not it has text, so toggling text no longer remounts the wrapped input and drops its focus. Co-authored-by: huult <huult@users.noreply.github.com>
|
Hey! I see that you made changes to our Form component. Make sure to update the docs in FORMS.md accordingly. Cheers! |
|
I created this PR from an upstream branch since I don't have push access to your fork. To take ownership of this branch and be able to push updates, run: Then you can close this PR and open a new one from your fork. |
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
|
🤖 Android test steps verification results: ✅ pass The PR's Tests section is still empty, so I ran the linked issue's steps as a regression check: the tooltip is web-only, and on Android long report field names still cut off cleanly with no tooltip or broken UI. The long-named field stays fully editable and saves normally.
|
|
@MelvinBot Please update the test steps and QA steps to match those in ticket #105158. |
|
I updated the Tests and QA Steps sections to match the expected result in [$175] [CFI] Report fields - Show the full field name on hover when the name is cut off. Both sections now read:
I kept the "no errors in the JS console" check and removed the TODO placeholders. I didn't change the rest of the PR body. |
|
@MelvinBot useAnimatedReaction is registered in every TextInputLabel, including the vast majority that don't pass onTruncationChange. Each time a label scale settles it also calls scheduleOnRN(checkTruncation), which returns immediately when the callback is missing. TextInputLabel is rendered by every text input, so this adds a worklet and a JS-thread hop per input for a feature only report fields use. Could we skip it when there's no callback? Hooks can't be conditional, so the simplest options are either to guard inside the reaction (if (!onTruncationChange) return; before scheduleOnRN, which at least removes the bridge call), or to move the measurement into a small child component that's only rendered when onTruncationChange is set. |
|
@MelvinBot This changes the tree shape for every Tooltip caller whose text is empty: children used to be returned bare and now sit at index 1 of a fragment. For a caller whose text is stable that's invisible, and for one whose text toggles it fixes the remount, which is what this PR needs. Two things worth confirming: Is there any caller that relies on Tooltip with empty text rendering exactly its children, for example in a snapshot or a ref/layout assumption? A quick grep or a run of the Tooltip-related tests would settle it. |
Move the label scale reaction into a child component that renders only when onTruncationChange is set, and skip the onLayout handler otherwise, so other text inputs don't register a worklet or measure their label. Co-authored-by: huult <huult@users.noreply.github.com>
|
Done. I went with the child component, so text inputs without
Checks
|
…ren mounted Co-authored-by: huult <huult@users.noreply.github.com>
|
1. No caller relies on an empty-text
2. I added the empty-text test in Typecheck and ESLint pass. I couldn't run the |
|
@MelvinBot checkTruncation currently runs from onLayout and when the scale animation settles, but not when label changes. If the text changes while the label box keeps its size, neither trigger fires, so the truncation flag goes stale: A label that is now too long gets no tooltip. Could we also measure after label changes? For example: useEffect(() => { |
The input sets the label's width, so new text keeps the same box and onLayout doesn't fire. Check again on label change, so a language switch or a renamed report field updates the tooltip. Co-authored-by: huult <huult@users.noreply.github.com>
|
Done. I added a test for it: tests/ui/components/TextInputLabel.tsx:90. It keeps the box at a fixed width and rerenders the label:
The test fails without the new effect. Checks
|
|
@MelvinBot ready for review |
|
I marked this PR as ready for review. |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Yes, this PR is ready for review. |
|
@MelvinBot could you sync with main? |
…elTooltip Co-authored-by: huult <huult@users.noreply.github.com>
|
@huult I think you need to approve and then @puneetlath will be prompted for review :) |
|
@trjExpensify I’ve already approved this PR. Cc @puneetlath |
puneetlath
left a comment
There was a problem hiding this comment.
Looks good to me, but has conflicts now.
|
@MelvinBot could you sync with main? |
…elTooltip Co-authored-by: huult <huult@users.noreply.github.com> # Conflicts: # tests/unit/GenericTooltipTest.tsx
ed9a03f
|
Synced with There was one conflict in
The mocks are combined so they work for all three. The bubble mock now renders a I couldn't run Jest, ESLint, or Prettier locally in this run, so CI will be the first check on the merged test file. |
|
@MelvinBot Could you take a look at the failing CI checks? |
|
The failing Analysis: Both errors are Evidence: Other unrelated branches fail
|
|
@puneetlath all yours |
|
@MelvinBot could you sync with main? |
…elTooltip Co-authored-by: huult <huult@users.noreply.github.com>
|
@puneetlath Everything looks good now. |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚧 puneetlath has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/puneetlath in version: 9.5.31-0 🚀
|
|
No help site update needed — this usability fix shows full truncated report field names on hover without changing the documented workflow. |
|
🚀 Deployed to staging by https://github.com/puneetlath in version: 9.5.31-0 🚀
|
|
🚀 Deployed to production by https://github.com/blimpich in version: 9.5.31-4 🚀
Bundle Size Analysis (Sentry): |










Explanation of Change
A long report field name is cut off on the report, and you couldn't read the full name anywhere. On web, hovering over a report field whose name is cut off now shows the full name in a tooltip.
TextInputLabel(web): a new optionalonTruncationChangecallback reports whether the label is cut off (scrollWidth > offsetWidth, the same checkTextWithTooltipuses). It checks on layout and again once the label's font-size animation settles. The label keeps its width while its font size changes, soonLayoutalone would miss a label that only fits when shrunk (focused or filled).BaseTextInput: a new opt-inshouldShowLabelTooltipWhenTruncatedprop wraps the input container inTooltipand setstextto the label only while it is cut off. The tooltip wraps the container, not the label, because the label haspointerEventsNoneso clicks reach the input.GenericTooltip: it used to return its children bare whentextwas empty and wrapped them in a fragment otherwise. Togglingtexttherefore remounted the wrapped element, which would drop the input's focus and draft value as the label shrinks to fit. It now renders the same tree either way and only skips the tooltip bubble. The newGenericTooltipTestfails without this change (the child mounts twice).DatePickerforwards the new prop.ReportFieldInlineInputturns it on for date, list, and text/formula/read-only fields.Native has no hover, so it is unchanged.
Fixed Issues
$ #105158
PROPOSAL: #105158 (comment)
Tests
Offline tests
QA Steps
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.AI Tests
npm run typecheck: passed.main.npm run react-compiler-compliance-check checkon changed files:TextInputLabelandGenericTooltipcompile.DatePickerandBaseTextInputalready fail onmainfor existing ref reads during render.cspellon changed files: 0 issues.GenericTooltipTest(new),TextInputLabel,BaseTextInputTest,BaseTextInputImplementationTest,TextInputWebRefAndFocusTest,DatePickerTest, andAvatarTooltipsTestpass (7 suites, 40 tests).Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari