material-ui icon indicating copy to clipboard operation
material-ui copied to clipboard

[material-ui][Autocomplete] Custom end adornment placement differs from TextField

Open stevepennings opened this issue 1 year ago • 9 comments

Steps to reproduce

Here's a sandbox of my 'bare-minimum implementation'.

Current behavior

Would it be possible to get the end adornment investigated with the 'standard' and 'filled' variant in mind? The spacing of the InputBase in the TextField is very different when wrapped in the Autocomplete.

If I miss something in my implementation, I'd be very grateful for support.

Expected behavior

The EndAdornment of the TextField having the same vertical placing as the EndAdornment of the Autocomplete.

Context

I've got a custom TextField component extending the TextField component of MUI. I'm trying to make the custom Textfield compatible with the Autocomplete of MUI. The 'filled' and 'standard' are surprisingly not behaving the same as 'outlined'.

Your environment

Search keywords: component:autocomplete

stevepennings avatar Jun 18 '24 14:06 stevepennings

Additionally I'd like to mention that the end adornment of the AutoComplete creates more padding once a value has been selected. When hovered the clear button shows itself <> hidden with the same padding. Consequently, the end adornment also ends up having odd spacing from the right when it's not hovered. Somehow I can't believe I'm the only one facing this issue, but I truly could not find similar issues. Apologies if there are...

steve-pennings avatar Jun 18 '24 15:06 steve-pennings

Just found this issue that may have something to do with this one: https://github.com/mui/material-ui/issues/28465#issuecomment-1278773152

steve-pennings avatar Jun 21 '24 07:06 steve-pennings

Hey, @steve-pennings. Thanks for the report, and sorry for the late reply.

As you mentioned, there's a difference between how the end adornment is handled in the TextField when inside Autocomplete. In this case, I recommend replacing the endAdornment completely and not relying on props.InputProps.endAdornment inside renderInput. Here's an example of what I mean that you can use as a workaround: https://codesandbox.io/p/sandbox/mui-autocomplete-textfield-filled-with-endadornment-forked-535q4g?file=%2Fsrc%2FApp.js

I believe there's room for improvement here, so the behavior is more consistent, so I'm adding it to the next major's milestone.

DiegoAndai avatar Aug 21 '24 19:08 DiegoAndai

Amazing, appreciate your reply and your workaround, thank you! Good luck with the next major milestone.

steve-pennings avatar Aug 22 '24 09:08 steve-pennings

This solution still makes the end adornment overlap the clear icon

TiagoPortfolio avatar Sep 12 '24 15:09 TiagoPortfolio

@TiagoPortfolio right, with the workaround, you would have to re-implement the clear icon. This is something to consider for the future proper solution.

DiegoAndai avatar Oct 02 '24 18:10 DiegoAndai

I also encountered this error. This is our Autocomplete look:

Image

We want our Autocomplete to become a browsable field too (to open a full-featured list dialog with all filtering and sorting and searching capabilities to find and select and item).

Ths is our code:

                    renderInput={params => <TextField
                        {...params}
                        label={t(label)}
                        onBlur={() => {
                            if (!isDirty) {
                                setIsDirty(true)
                            }
                        }}
                        InputProps={{
                            ...additionalProps,
                            ...params.InputProps,
                            endAdornment: <>
                                {
                                    progress
                                        ?
                                        <Progress size={20} />
                                        :
                                        <BrowserIcons onCleared={() => {
                                            removeFilter(property)
                                            setSelectedEntity(null)
                                        }} />
                                }
                            </>,
                        }}
                    />
                    }

That workaround is a bad recommendation. Because it uses hardcoded values, we should be able to keep MUI theme values dynamically to be forward-compatible.

Nefcanto avatar Feb 02 '25 07:02 Nefcanto

I wanna add a suffix in between of the clear icon and the chevron icon, like a currency for example. The placement is in the middle because I want the chevron to stay at the same position, and the clear icon would not cause a misunderstanding that clearing would means deleting both suffix and value.

However, I investigated the prop endAdornment that is forwarded to TextField in renderInput and turns out, those two are grouped together and does not respect my custom suffix. Are there anyway to work this out? Thanks.

Specs: @mui/material: 7.1.1

duynguyen-kun avatar Jun 10 '25 09:06 duynguyen-kun

@duynguyen-kun did you check out https://codesandbox.io/p/sandbox/mui-autocomplete-textfield-filled-with-endadornment-forked-535q4g?file=%2Fsrc%2FApp.js?

I think this workaround should work for you.


I added the waiting for 👍🏼 label so the community can vote for this new feature. Anyone who needs this fix, please add an upvote (👍🏼 emoji) on this issue's description so we can track interest

We might add it to the roadmap if it has enough upvotes, but there's no certainty at the moment.

DiegoAndai avatar Jun 10 '25 21:06 DiegoAndai

@DiegoAndai @duynguyen-kun Unfortunately the workaround in the codesandbox does not work for us because it breaks the dropdown part of the autocomplete. I found a way to make it work without using a custom component and keeping all default features, such as dropdown and clear button:

<Autocomplete        
        renderInput={(params) => (
          <TextField
            {...params}
            slotProps={{
              input: {
                ...params.InputProps,
               endAdornment: (() => {
                  const adornmentBase = params.InputProps.endAdornment;

                  if (inputProps?.endAdornment) {
                    const [dropdownButton, ...otherAdornments]: ReactNode[] =
                      // @ts-expect-error - extract the default MUI adornments at runtime
                      adornmentBase.props.children;

                    return (
                      <>
                        {/*
                         * recreate the position of the default adornment wrapper from adornmentBase
                         * why this doesn't work with the default MUI adornment wrapper is a mystery
                         * ref: https://github.com/mui/material-ui/issues/42680
                         * codesandbox: https://codesandbox.io/p/sandbox/mui-autocomplete-textfield-filled-with-endadornment-forked-535q4g?file=%2Fsrc%2FApp.js
                         */}
                        <InputAdornment
                          position="end"
                          sx={{ position: 'absolute', right: 0 }}
                        >
                          {dropdownButton}
                          {inputProps.endAdornment}
                          {otherAdornments}
                        </InputAdornment>
                      </>
                    );
                  }

                  return adornmentBase;
                })(),
              },
            }}
          />
        )}

This relies on the internal logic of the Autocomplete but it achieves the level of integration that we're looking for

thuringia avatar Jun 30 '25 13:06 thuringia

@thuringia you can achieve this in my workaround by controlling the open state: https://codesandbox.io/p/sandbox/mui-autocomplete-textfield-filled-with-endadornment-forked-535q4g

DiegoAndai avatar Jul 01 '25 20:07 DiegoAndai

@DiegoAndai Not quite, our desire is to add adornments without any impact on other parts of the component. This component is used in our design system, so ease-of-use and consistency are key. A regular TextField allows you to add adornments without the need for setting any additional props, or even adding additional state variables.

I'm not happy that we have to disassemble the component created by Autocomplete at runtime, but we did have to use a similar approach to fix issues with how Autocomplete manages start adornments. In that sense there is a nice symmetry to it. For reference, here's our workaround for the startAdornments prop:

<Autocomplete
  renderInput={(params) => (
    <TextField
      {...params}
      slotProps={{
        input: {
          ...params.InputProps,
          startAdornment: (() => {
            const adornmentBase = params.InputProps.startAdornment;
            if (loading) {
              return (
                <>
                  <CircularProgress size={20} />
                  {adornmentBase}
                  {inputProps?.startAdornment}
                </>
              );
            }

            if (icon !== undefined) {
              if (icon === null) {
                return (
                  <>
                    {adornmentBase}
                    {inputProps?.startAdornment}
                  </>
                );
              }
              return (
                <>
                  {icon}
                  {adornmentBase}
                  {inputProps?.startAdornment}
                </>
              );
            }

            return (
              <>
                <SearchOutlined />
                {adornmentBase}
                {inputProps?.startAdornment}
              </>
            );
          })()
})} />

thuringia avatar Jul 03 '25 08:07 thuringia