Skip to content

feat(input): add start and end slots #28397

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Nov 27, 2023
Merged

feat(input): add start and end slots #28397

merged 13 commits into from
Nov 27, 2023

Conversation

averyjohnston
Copy link
Contributor

@averyjohnston averyjohnston commented Oct 23, 2023

Issue number: Part of #26297


What is the current behavior?

With the modern form control syntax, it is not possible to add icon buttons or other decorators to the sides of ion-input, as you can with ion-item.

What is the new behavior?

start and end slots added. While making this change, I also tweaked the CSS selectors responsible for translating the label above the input with "stacked" and "floating" placements, merging this logic into a single class managed by the TSX. I needed to add a new class for whether slot content is present, so the selectors were getting unwieldy otherwise.

Docs PR TBA; I plan on knocking out all three components at once when the features are all complete, to make dev builds easier to manage.

Does this introduce a breaking change?

  • Yes
  • No

Other information

@github-actions github-actions bot added the package: core @ionic/core package label Oct 23, 2023
@averyjohnston averyjohnston marked this pull request as ready for review October 23, 2023 19:41
@averyjohnston averyjohnston requested review from a team and brandyscarney and removed request for a team October 23, 2023 19:42
'has-focus': this.hasFocus,
'has-value': hasValue,
'has-focus': hasFocus,
'label-floating': labelShouldFloat,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open to alternative ideas for the name of this class.

Copy link
Member

@brandyscarney brandyscarney Nov 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this name makes sense. We tend to do <component>-<status> when naming them. Like the following:

select-disabled
select-expanded
radio-checked
radio-disabled

If you really wanted to you could add input before it since we have the following in other components (radio, select) when talking about the label placement:

[`radio-label-placement-${labelPlacement}`]: true,

Comment on lines 696 to 714
const hasStartEndSlots = el.querySelector('[slot="start"], [slot="end"]') !== null;

/**
* If the label is stacked, it should always sit above the input.
* For floating labels, the label should move above the input if
* the input has a value, is focused, or has anything in either
* the start or end slot.
*
* If there is content in the start slot, the label would overlap
* it if not forced to float. This is also applied to the end slot
* because with the default or solid fills, the input is not
* vertically centered in the container, but the label is. This
* causes the slots and label to appear vertically offset from each
* other when the label isn't floating above the input. This doesn't
* apply to the outline fill, but this was not accounted for to keep
* things consistent.
*/
const labelShouldFloat =
labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-floating when there's anything in the start slot is taken from MUI: https://stackblitz.com/edit/react-7vghat?file=Demo.js

The end slot behavior was a compromise and does not follow native behavior. I spent a bit of time trying to rearrange the CSS/markup to vertically center the input when the label isn't raised, without breaking existing behavior, but in the end decided it would be best to push that scope to a future ticket since the feature works as-is and there are still two components to go.

Let me know if you feel that allowing the label to lower with content in the end slot should be required for this feature, and I can take a closer look. Alternatively, I can allow that only for fill="outline" for now and fix up the other fills in a separate ticket.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Material Design components for the web seem to have the floating label overlay the input even when using a start slot: https://material-components.github.io/material-components-web-catalog/#/component/text-field However, it sounds like that would significantly increase the amount of work required to implement. Given that this is an experimental feature, I'm fine going without this behavior.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MUI demo looks like it only floats the label when there is a start slot but not when there is only an end slot. Should we match that? Or is that what you meant was a compromise?

Also, just adding that it seems the latest Material Design components for the web also overlay the input with a start slot: https://material-web.dev/components/text-field/#icons

(I modified the first md-outlined-field to include label="Messages":

floating-labels-slots.mov

Their implementation doesn't look complete though judging by the zero padding between the icons and edge of the input lol. I'm also okay with not implementing this, but maybe we should make a tech debt ticket to look into it more?

Copy link
Contributor Author

@averyjohnston averyjohnston Oct 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MUI demo looks like it only floats the label when there is a start slot but not when there is only an end slot. Should we match that? Or is that what you meant was a compromise?

That's what I meant, yeah. I can definitely make a tech debt ticket if we're in agreement, I just wanted to make sure we were okay proceeding without this piece for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@liamdebeasi liamdebeasi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed to pause my review, but wanted to get these comments out now so you can start addressing them. Overall the implementation looks solid! Mostly nitpick things

Comment on lines 696 to 714
const hasStartEndSlots = el.querySelector('[slot="start"], [slot="end"]') !== null;

/**
* If the label is stacked, it should always sit above the input.
* For floating labels, the label should move above the input if
* the input has a value, is focused, or has anything in either
* the start or end slot.
*
* If there is content in the start slot, the label would overlap
* it if not forced to float. This is also applied to the end slot
* because with the default or solid fills, the input is not
* vertically centered in the container, but the label is. This
* causes the slots and label to appear vertically offset from each
* other when the label isn't floating above the input. This doesn't
* apply to the outline fill, but this was not accounted for to keep
* things consistent.
*/
const labelShouldFloat =
labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Material Design components for the web seem to have the floating label overlay the input even when using a start slot: https://material-components.github.io/material-components-web-catalog/#/component/text-field However, it sounds like that would significantly increase the amount of work required to implement. Given that this is an experimental feature, I'm fine going without this behavior.

// ----------------------------------------------------------------

::slotted([slot="start"]) {
margin-inline-end: $form-control-label-margin;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The focus state looks a bit odd on the button:
image

Maybe we need to add some padding?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh actually it looks like the padding was removed in the test template. Any particular reason for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just thought it looked nicer 😆 With the default padding added to the margins, it looks like there's a ton of empty space around the icons. I'll revert the padding so the behavior is more clear.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see why you added it 😂

image

Yeah that does look strange. We might want to consider adding some internal styles to account for this. cc @brandyscarney might have ideas on how best to achieve this. I'd expect the start button to line up with the stacked label at the very least.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked into this a bit and is there a reason we are recommending buttons for both slots? In the Material Design web catalog they use plain icons. The latest Material Design web components does include a button in the end slot for their "Password" field, although their padding is nonexistent, but I suspect it just hasn't been properly designed yet.

Maybe we should add some styles for slotted icons to make them appear larger and match? Here is what I see with an ion-icon in the start slot and an ion-button in the end slot:

localhost_3333_src_components_input_test_slot(Pixel 5)

In addition, maybe we could style the .button-has-icon-only like this:

:host(.button-has-icon-only) .button-native {
  @include padding(0);

  aspect-ratio: 1;
  border-radius: 50%;
}

Changing the icon in the start slot back to a button, with the above styles, gives me:

localhost_3333_src_components_input_test_slot(Pixel 5) (3)
localhost_3333_src_components_input_test_slot(Pixel 5) (2)

This still looks a bit off because of the left padding, but if we reduce that it also moves the notch. We could remove the left margin for button and get it a bit closer:

localhost_3333_src_components_input_test_slot(Pixel 5)

I think we should focus more on aligning icons in the start slot with the label though. It doesn't seem to be a typical pattern to have a button slotted at the start. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point that a button in the start slot isn't going to be a common use case. I didn't make the test page with recommended patterns in mind -- the buttons were mostly to check the focus behavior -- but I agree that the icons should be the priority, at least as far as the start slot goes.

That said, I'm hesitant to add too much built-in styling since I don't want devs to have to deal with potentially unwanted extra "magic," especially since they can add their own styling to the slotted content. For example, if we removed the padding from icon-only buttons by default, that would make non-clear buttons look odd:

non-clear buttons

Maybe we just adjust the test to make it seem less like I'm trying to recommend using clear buttons in both slots?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should still look fine with the CSS I added above:

localhost_3333_src_components_input_test_slot(Pixel 5) (1)

but I understand if we don't want to add custom styles just for this. I do think we should consider it in the future though, because Material Design 3 has a section dedicated to icon buttons that looks like the above: https://m3.material.io/components/icon-buttons/overview

Maybe this is something we could add for our Material Design 3 work.

Their spec doesn't show buttons in an input at all so yeah I am fine with pushing this off: https://m3.material.io/components/text-fields/specs

We can also push off styling icons in an input but I think we should revisit that for MD3 too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We decided to handle this as part of a separate ticket, starting with this spike: https://ionic-cloud.atlassian.net/browse/FW-5645

Copy link
Contributor

@liamdebeasi liamdebeasi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall looks good! I have one lingering comment about the slotted button padding, but I think the general approach makes sense.

@Devolick
Copy link

Devolick commented Oct 31, 2023

Hi, approximately how long until this will be available? Is it possible to add a collection from *ngFor of via slot=start to make multiselect with floating label and popover?

Best,
Dzmitry

@averyjohnston
Copy link
Contributor Author

@Devolick We do not have a planned timeline for this feature; work is ongoing and there are other components that need updating as well before this can be shipped.

For your use case, I would instead recommend using a Select, or a Popover that opens underneath the input according to your desired behavior. For questions on the implementation, I would recommend posting in the Ionic forum or Discord server.

Copy link
Member

@brandyscarney brandyscarney left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good I just added a couple comments about the naming and button/icon styles!

Base automatically changed from FW-2853-base to FW-2853 November 27, 2023 15:44
@averyjohnston averyjohnston merged commit 3648520 into FW-2853 Nov 27, 2023
@averyjohnston averyjohnston deleted the FW-2853-input branch November 27, 2023 15:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
package: core @ionic/core package
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants