Skip to content
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

Support Bootstrap 5 #622

Closed
3 tasks done
ericgio opened this issue Feb 22, 2021 · 22 comments
Closed
3 tasks done

Support Bootstrap 5 #622

ericgio opened this issue Feb 22, 2021 · 22 comments

Comments

@ericgio
Copy link
Owner

ericgio commented Feb 22, 2021

Bootstrap 5 is currently in beta, so I'm creating this issue to track the changes this library will need to make to support the new version out of the box. Please comment if you come across anything not working.

As a general workaround, the typeahead's render props and child render function can be used to customize individual components in a way that better supports BS5.

@rkleine
Copy link

rkleine commented Feb 23, 2021

@dwaynelavon
Copy link

dwaynelavon commented Mar 3, 2021

@filipkis
Copy link

Bootstrap 5 is now released. Any ETA on when support could be expected?

@belinde
Copy link

belinde commented Jul 12, 2021

Not exactly sure it's a Bootstrap5 exclusive issue, but I'm using it with the 2.0.0-beta.2 react-bootstrap. Elements with class invalid-feedback are shown only if they're adjacent to an element with class is-invalid. The Typeahead component has the Input element with the correct class, but it's wrapped in two div's, so the standard CSS from Bootstrap doesn't show the error messages. Putting the is-invalid class in the outermost div resolves the issue.

@ericgio
Copy link
Owner Author

ericgio commented Jul 13, 2021

Support for visually-hidden classname added in v5.2.0.

@filipkis: I don't have a timeline on when full, out of the box support will happen. I'll try to add things incrementally, but the close button and floating label changes are both non-trivial.

@frankv12
Copy link

BS 5.0.2 | RBT 5.2.0 | Remove button styling is missing: https://prnt.sc/1d683mt @ericgio

@fkrauthan
Copy link

Any ETA for this? On initial testing it seems like the loading indicator shows text+the spinning animation and the close/delete button seem to be missing (as it shows text only too)

@geoffbarcalow
Copy link

Multiselect close buttons not rendering correctly.
image

@rcugut
Copy link

rcugut commented Sep 20, 2021

There's been quite a change between BS4 and BS5 in regard how the "close" icon/button is implemented.

In BS4, it was using a <span> with the html character "times" inside a <button>:

<button type="button" class="close" aria-label="Close">
  <span aria-hidden="true">&times;</span>
</button>

And in BS5, this changed to just using a <button> (which uses a SVG background for rendering the actual "X" icon):

<button type="button" class="btn-close" aria-label="Close"></button>

And it looks like the current v5.2.0 lib code is still generating the underlying component code to be compatible to BS4, which includes the child <span>:
https://github.com/ericgio/react-bootstrap-typeahead/blob/v5.2.0/src/components/ClearButton.js#L71

However, I solved this in our project, where we use BS5, by adding this SCSS snippet. It's overwriting a the .rbt-close class.

// somewhere you import bootstrap and react-bootstrap-typeahead
@import "~bootstrap/scss/bootstrap";
@import "~react-bootstrap-typeahead/css/Typeahead";


// the class override "hack" for BS5

.rbt-aux {
    & .rbt-close {
        margin-top: unset;
        @extend .btn-close; // inherit all the properties of the bootstrap class `.btn-close`
        pointer-events: auto; /* Override pointer-events: none; above */
        span {
            display: none;  // hide the BS4 compatible span element
        }
    }
}

I didn't have enough time to think of an elegant and simple solution from the library code point of view, to keep both BS4 and BS5 compatibility.
My conclusion was that the library must be made aware of which Bootstrap version is using.

@ericgio
Copy link
Owner Author

ericgio commented Oct 18, 2021

My conclusion was that the library must be made aware of which Bootstrap version is using.

That sounds right. When BS4 came out I added a separate CSS file with BS4 overrides that consumers of the library needed to import as well as the main file. This is most likely the approach I'll need to take for BS5 as well.

The switch to using a background image for the close button is pretty unfortunate from a flexibility point of view, since it makes it much more difficult to customize, eg: applying a different color.

Still no set timeline for complete out of the box support, but I'd like to include it with v6.0, currently in alpha.

@waltervi

This comment has been minimized.

@Jorl17
Copy link

Jorl17 commented Dec 8, 2021

In case someone wants an alternative solution to the SCSS fix, I believe this fix works in plain CSS:

.rbt-token .rbt-token-remove-button {
  margin-right: -10px;
}

.rbt-input-wrapper div:nth-child(n+2) {
  padding-left: 10px;
}

It's far from good, but it does the job for my use case. We basically pad the button 10px to the right and adjust everything (cursor position, etc) accordingly -- but only if there is more than one item actually present in the div.

@cesarvarela
Copy link

Something quick and dirty:

const StyledTypeahead = styled(Typeahead)`
  .rbt-close {
    border: none;
    background: transparent;
  }
`

@ericgio
Copy link
Owner Author

ericgio commented Feb 4, 2022

The BS5 clear button is now supported as of v6.0.0-alpha.7. Along with the existing Typeahead.css file, you'll also need to add the Typeahead.bs5.css file that is now included with the package.

@ericgio
Copy link
Owner Author

ericgio commented Feb 11, 2022

As of v6.0.0-alpha.8 you can use floating labels with the typeahead as follows:

<Typeahead
  ...
  renderInput={({ inputRef, referenceElementRef, ...inputProps }) => (
    <Hint>
      <FloatingLabel controlId="name-input" label="Name">
          <Form.Control
            {...inputProps}
            ref={(node) => {
              inputRef(node);
              referenceElementRef(node);
            }}
          />
      </FloatingLabel>
    </Hint>
  )}
/>

@ericgio
Copy link
Owner Author

ericgio commented Sep 5, 2022

Bootstrap v5 officially supported as of v6.0.0

@ericgio ericgio closed this as completed Sep 5, 2022
@nikischin
Copy link

nikischin commented Aug 23, 2023

@ericgio

As of v6.0.0-alpha.8 you can use floating labels with the typeahead as follows:

<Typeahead
  ...
  renderInput={({ inputRef, referenceElementRef, ...inputProps }) => (
    <Hint>
      <FloatingLabel controlId="name-input" label="Name">
          <Form.Control
            {...inputProps}
            ref={(node) => {
              inputRef(node);
              referenceElementRef(node);
            }}
          />
      </FloatingLabel>
    </Hint>
  )}
/>

I tried this implementation provided and this does work. Thank you!

However I feel like this is a rather ugly implementation. Couldn't some behavior like this be provided like a property or something, so maybe we could open a feature request?

Also I don't really understand what the <Hint> in this example is needed for as it has some hardcoded style tag which destroys the width behavior of the element. Seems to work also without the <Hint> though I am not sure if this will rather be a problem for some features I haven't yet tested.

@ericgio
Copy link
Owner Author

ericgio commented Aug 23, 2023

@nikischin

I feel like this is a rather ugly implementation. Couldn't some behavior like this be provided like a property or something

I agree that customizing the input this way is a bit more difficult for the developer than simply setting some prop to true. On the other hand, introducing that prop adds complexity to the code and makes the package a bit more heavyweight and ugly for everyone (especially me 😄). I'd like to support floating labels in the component, but I'm not sure they're common enough to warrant the additional complexity of being baked in. The approach above feels like the right compromise to me and provides enough flexibility for developers to tailor the implementation to their needs.

@nikischin
Copy link

nikischin commented Aug 23, 2023

@ericgio thank you so much for the fast reply! Generally speaking I can totally understand! Though from my perspective as one of those using the floating labels it's rather not so satisfying hah. Though what to support and what not is in the end up to you and I totally do understand your argumentation.

Talking about the second part of my comment (which partly got lost in Github formatting) could you give some information why the <Hint> is needed in the example and why it needs to have a hardcoded style tag like position: relative; flex: 1 1 0%;... as for my implementation this does effect the size of the input and makes it not behave like the standard Bootstrap <Form.Controls>

Also I guess there is no chance of implementing the floating label for a multi input? :)

@nikischin
Copy link

nikischin commented Aug 24, 2023

I was trying to create a custom implementation for the floatingLabel on a multi typeahead, though, it does not work as expected, not sure what I am doing wrong, though I cannot see any of the selected options as the selected is always undefined.

    <Typeahead
            id={'th' + formId}
            inputProps={{ className: 'left-icon icon-tag' }}
            type='text'
            placeholder='Tags'
            options={[ '2023', 'Indoor', 'Outdoor' ]}
            defaultSelected={[ '2023' ]}
            multiple={true}
            renderInput={({ inputRef, referenceElementRef, onRemove, selected, ...inputProps })=> (
                <Hint className='rbt-hint'>
                    {selected?.map((option, idx)=> (
                        <Token
                            key={idx}
                            onRemove={onRemove}
                            option={option}
                            className="medal-token"
                        >
                            {option.medal}
                        </Token>
                    ))}
                    <FloatingLabel controlId={formId} label={inputProps.placeholder}>
                        <FormControl
                            {...inputProps}
                            ref={(node)=> {
                                inputRef(node);
                                referenceElementRef(node);
                            }}
                        />
                    </FloatingLabel>
                </Hint>
            )}
        />

Not yet sure where exactly to place the Token for the optimal appearance, though I cannot see any Token in neither the dom nor the props or anything.

Also I noticed the className provided by inputProps={{ className: 'left-icon icon-tag' }} is translated into inputclassname on multiselect, while it is translated into class/className in normal Typeahead without multi set to true. Seems like a bug to me if I am not mistaken?

@ericgio
Copy link
Owner Author

ericgio commented Aug 29, 2023

I guess there is no chance of implementing the floating label for a multi input? :)

The multi-input unfortunately does not play well with Bootstrap's floating label due to the way it uses CSS selectors.

I cannot see any of the selected options as the selected is always undefined.

It looks like you're trying to de-structure selected from the first argument in renderInput but it doesn't exist there. There's a second argument that provides the component's internal state, including selected.

the className provided by inputProps is translated into inputclassname on multiselect, while it is translated into class/className in normal Typeahead without multi set to true

That's right. The multi-select input component accepts both a className and inputClassName because the input element is nested within containing elements. So the latter is for the input element, while the former is for the overall containing element. Note that inputProps is exposed mainly to help customize the default implementation. Because you're using a custom implementation with renderInput, you can just apply those classnames directly (eg: to the FormControl)

@superfaz
Copy link

superfaz commented Feb 2, 2024

Hi, If that can help anyone: here is the css styles that I use to have RBT working with Floating Label:

/**
 * Extend Typeahead to support floating labels.
 */
.form-floating > .rbt .form-control {
  height: calc(3.5rem + calc(var(--bs-border-width) * 2));
  min-height: calc(3.5rem + calc(var(--bs-border-width) * 2));
  line-height: 1.25;
}

.form-floating > .rbt .form-control:focus,
.form-floating > .rbt .form-control:not(:placeholder-shown) {
  padding-top: 1.625rem;
  padding-bottom: 0.625rem;
}

.form-floating > .rbt:has(.form-control:focus) ~ label,
.form-floating > .rbt:has(.form-control:not(:placeholder-shown)) ~ label {
  color: rgba(var(--bs-body-color-rgb), 0.65);
  transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
}

.form-floating > .rbt:has(.form-control:not(:focus):placeholder-shown) ~ label {
  display: none;
}

Then my JSX looks as simple as expected:

  <Form.FloatingLabel controlId="floatingSelect1" label="Floating label">
    <Typeahead options={options} clearButton={true} allowNew={true} placeholder="Choose an option..." />
  </Form.FloatingLabel>

Testing with react-bootstrap and confirmed with the following options:

  • with or without placeholder
  • with or without clear button
  • with or without allow new

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests