fix(input): support floating labels with slotted content - #31309
fix(input): support floating labels with slotted content#31309brandyscarney wants to merge 25 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
I renamed these screenshots from input-slots to input-slot to match the folder name.
|
|
||
| configs().forEach(({ title, screenshot, config }) => { | ||
| test.describe(title('input: start and end slots (visual checks)'), () => { | ||
| test.describe(title('input: slot'), () => { |
There was a problem hiding this comment.
This was updated to match the folder name, following how we title other tests.
There was a problem hiding this comment.
This is the correct height and label position for Material Design filled inputs. See: https://m2.material.io/components/text-fields
There was a problem hiding this comment.
This 1px shift was caused by the removal of this rule:
ionic-framework/core/src/components/input/input.scss
Lines 562 to 569 in 048788f
However, this is actually more aligned than before:
There was a problem hiding this comment.
This 1px shift was caused by the removal of this rule:
ionic-framework/core/src/components/input/input.scss
Lines 562 to 569 in 048788f
There was a problem hiding this comment.
This 1px shift was caused by the removal of this rule:
ionic-framework/core/src/components/input/input.scss
Lines 562 to 569 in 048788f
There was a problem hiding this comment.
This 1px shift was caused by the removal of this rule:
ionic-framework/core/src/components/input/input.scss
Lines 562 to 569 in 048788f
ShaneK
left a comment
There was a problem hiding this comment.
This is looking really great! I had some feedback on a few issues and some minor things, but really looking good!
| * Only applies to inputs with `md` mode and `fill="outline"`. | ||
| * In RTL mode, the adjustment is positive; in LTR mode, it's negative. | ||
| */ | ||
| private getStartSlotAdjustment(): string { |
There was a problem hiding this comment.
Calling getBoundingClientRect() from inside render() means the value only refreshes when something triggers a re-render, and it forces a sync layout on every render, including focus, blur and value changes that have nothing to do with slots.
The part that doesn't recover is a start slot changing width without a DOM mutation. Nothing re-measures and the label just stays wrong. Both of these have the same start slot width, the bottom one only got there after mount, which is what a late loading web font does:
There's a smaller version of this on first paint too, where .input-start doesn't exist yet so the first render gets an empty string and the label slides about 32px into place over the 150ms transition. That one's easy to miss while the form is still appearing, so the stale case is the one I'd worry about.
The NotchController already solves this shape for the notch: it defers with raf(), measures, then writes with style.setProperty(), driven from componentDidRender() which this component already calls. A ResizeObserver on .input-start would cover the width change case as well.
| * The solid and outline fills are only supported by `md` mode. | ||
| */ | ||
| configs({ modes: ['md'] }).forEach(({ title, screenshot, config }) => { | ||
| test.describe(title('input: slot'), () => { |
There was a problem hiding this comment.
The two functionality tests came out and nothing replaced them, so this file is all screenshots now. hasValue is covered eight ways but hasFocus isn't covered at all, and floating on focus is the headline behaviour of the fix.
A few other gaps: end slot only, which is exactly what the removed test covered, stacked, and the runtime add/remove slot path that skipLabelTransition exists for. Both test/color and test/highlight set class="has-focus" on the host for deterministic focus screenshots if that helps.
Since the contract inverted rather than disappeared, could the old assertion come back the other way round, something like not.toHaveClass(/label-floating/) when the slots are populated and the input is empty? A screenshot diff tells you something moved, but not which rule broke.
| --border-radius: 4px; | ||
| --padding-start: 16px; | ||
| --padding-end: 16px; | ||
| --start-slot-adjustment: 0px; |
There was a problem hiding this comment.
Should this get the same private API comment as --highlight-color? It's sitting right next to --border-radius and --padding-start which are both @prop documented, so as it is now it looks like something people can set.
Also wondering about a fallback on the var(). Without one a missing property invalidates the whole transform, so the label would lose its scale() too and not just the translate.
There was a problem hiding this comment.
Since we decided we would mark internal css variables with --internal for modular Ionic I chose to do that instead of adding a comment. I also don't think the fallback should be reached since it is set by default here, but I added it anyway.
Input fix: 4aedde9
Textarea fix: a595a64
Select fix: ffe8326
| */ | ||
| this.skipLabelTransition = true; | ||
|
|
||
| requestAnimationFrame(() => { |
There was a problem hiding this comment.
There's a raf() helper in @utils/helpers that's the zone-patched wrapper, and it's what the notch controller this component already depends on uses. Any reason not to use it here?
This also isn't cancelled in disconnectedCallback(), so a slot mutation landing in the same frame the input is removed will set state on a torn down component. Minor, but the other controllers are cleaned up there already.

Issue number: resolves #29449 resolves #28665
What is the current behavior?
Inputs with a floating label and a start or end slot always display the label in the floated state, regardless of whether the input contains a value:
What is the new behavior?
mdspecification.mdspecification.Does this introduce a breaking change?
Internal DOM Structure Changes
New wrapper elements have been added to the component's internal DOM structure to support floating labels with slotted start and end content. Additionally, the structure of the component has been reorganized, with some elements now grouped differently than before. This may introduce breaking changes for developers who rely on the component's internal DOM structure or apply custom styling to internal elements.
The following internal wrapper elements have been added:
<div class="input-start">wrapper for the start slot<div class="input-control">wrapper for the label and native control<div class="input-end">wrapper for the end slot and clear buttonWhile the public API has not changed, selectors or style overrides targeting the previous markup may need to be updated to reference the new wrapper elements and their organization. If you have custom CSS targeting the internal structure of input, update your selectors to account for these structural changes.
Other information
Preview