carbon-design-system / carbon

A design system built by IBM
https://www.carbondesignsystem.com
Apache License 2.0
7.76k stars 1.8k forks source link

The Carbon Select widget does not honor the style={ width: 'someWidth' } prop #7259

Closed guigueb closed 3 years ago

guigueb commented 3 years ago

What package(s) are you using?

Detailed description

Describe in detail the issue you're having. The Carbon Select widget does not honor the style={ width: 'someWidth' } prop.

Is this issue related to a specific component? Yes, the Select widget.

What did you expect to happen? What happened instead? What would you like to see changed?

  • expected: The Select widget would resize to the width specified
  • what happened: The width style was ignored
  • what changed: Specifying a width would change the width to the Select widget.

What browser are you working in? Chrome

What version of the Carbon Design System are you using? The latest - I duped the issue on the demo page.

What offering/product do you work on? Any pressing ship or release dates we should be aware of? IBM Cognos products.

Steps to reproduce the issue

  1. using the Carbon Demo page: https://www.carbondesignsystem.com/components/select/usage#live-demo
  2. copy/paste the snippet below
  3. notice that the Select widget is NOT 400px wide

With the parent div size of 400px, when passing a style of width=100%, the Select should be 400px wide.

Additional (similar but different test case): different style widths will produce different odd behavior. Applying a width size of 80% to the Select will render the chevron outside the input, making it unresponsive. image

<div style={{width: ['400px'}}>](url)
<Select
  defaultValue="placeholder-item"
  helperText="Optional helper text"
  id="select-1"
  invalidText="A valid value is required"
  labelText="Select"
  style={{width: '100%'}}
>
  <SelectItem
    text="Choose an option"
    value="placeholder-item"
  />
  <SelectItemGroup
    label="Category 1"
  >
    <SelectItem
      text="Option 1"
      value="option-1"
    />
    <SelectItem
      text="Option 2"
      value="option-2"
    />
  </SelectItemGroup>
  <SelectItemGroup
    label="Category 2"
  >
    <SelectItem
      text="Option 3"
      value="option-3"
    />
    <SelectItem
      text="Option 4"
      value="option-4"
    />
  </SelectItemGroup>
</Select>
</div>
guigueb commented 3 years ago

image

guigueb commented 3 years ago

Closing this was a mistake

tw15egan commented 3 years ago

Can you create a CodeSandbox that recreates the problem you are facing?

guigueb commented 3 years ago

Yes, copy/paste the code that I gave you above and you will see the issue.

This code and image shows the... Additional (similar but different test case): different style widths will produce different odd behavior. Applying a width size of 80% to the Select will render the chevron outside the input, making it unresponsive.

Setting the Select width to 100% show the issue of the Select NOT resizing to 100% of its parent div (400px).

image

import React from 'react';
import { render } from 'react-dom';
import { Select, SelectItem, SelectItemGroup } from 'carbon-components-react';

const App = () => (
  <div style={{width: '400px'}}>
<Select
  defaultValue="placeholder-item"
  helperText="Optional helper text"
  id="select-1"
  invalidText="A valid value is required"
  labelText="Select"
  style={{width: '80%'}}
>
  <SelectItem
    text="Choose an option"
    value="placeholder-item"
  />
  <SelectItemGroup
    label="Category 1"
  >
    <SelectItem
      text="Option 1"
      value="option-1"
    />
    <SelectItem
      text="Option 2"
      value="option-2"
    />
  </SelectItemGroup>
  <SelectItemGroup
    label="Category 2"
  >
    <SelectItem
      text="Option 3"
      value="option-3"
    />
    <SelectItem
      text="Option 4"
      value="option-4"
    />
  </SelectItemGroup>
</Select>
</div>

);

render(<App />, document.getElementById('root'));
tw15egan commented 3 years ago

Do you have an example of the layout you are trying to achieve? It seems like there are some wrapper styles that are preventing the width from being spread to the correct element, and a relative width is not working because its parent elements do not have width: 100%.

For now, you should be able to set .bx--select, .bx--select-input__wrapper { width: 100%; } to have the select element take the entire width of its parent.

As far as the chevron goes, since the arrow is positioned relative to the .bx--select-input__wrapper, and the width: 80% styles are spread to select input itself, it will cause the input to render incorrectly. I believe you'll need to style this via css and target .bx--select-input__wrapper { width: 80%; } (and omitting this selector in the above code block as well).

I can put in a PR to ensure the Select fills the width of its parent container, however, as that is what the rest of our form components do (not sure why this one doesn't 🤔 )

guigueb commented 3 years ago

@tw15egan - Thank you for the suggestions - I already merged changes to address the issue on our end. Close to what you suggested. I can css style the .bx-select and .bx--select-input, but had to set the .bx--select-input__wrapper width pragmatically in order to apply the width users want to the Select.

    // Carbon Select does not honor the style={{ width: 'someWidth' }} prop
    // To address this we need to apply width to a few spots within the Carbon Select widget.
    // Carbon bug: https://github.com/carbon-design-system/carbon/issues/7259
    //
    // <div class="bx-form-item" >
    //  // need to apply width="100%" to this div to stretch the select to match its parents width (done in the scss file)
    //  <div class="bx--select ba-toolkit-select-compat">
    //      <label>
    //      // need to apply width="customWidth" to this div to make select the size the user wants (done here, apply to parent of bx--select-input)
    //      <div class="bx--select-input__wrapper" >
    //          // need to apply width="100%" to this select to stretch the select contents (done in the scss file)
    //          <select class="bx--select-input"
    //          <svg 

    <CarbonSelect>
        ref={el => {
            el && el.parentNode.style.setProperty('width', customWidth);
        }}
    </CarbonSelect>
guigueb commented 3 years ago

@tw15egan - the PR looks good for stretching the Select to fit its parent tag. But I it doesn't address the passing of a style prop containing a width to Select (style={{width: '80%'}}). Will there be another PR for this, or will there be another bug logged to address that?

tw15egan commented 3 years ago

Due to the way the component is constructed, you'll have to set the width via css and target the bx--select-input__wrapper directly.

The style prop is spread via {...other} directly onto the input, so there isn't a way to set inline styles on the wrapper https://github.com/carbon-design-system/carbon/blob/aec8db30597643e8e10ea2da6957d07bfcf3bcc2/packages/react/src/components/Select/Select.js#L74-L86

guigueb commented 3 years ago

@tw15egan Thank you for the description of the problem. But that doesn't change the fact that there is a problem with applying style width to the Carbon Select. Are you saying that there is no intention to fix the problem?

BTW: You could extract width from style and apply it to the bx--select-input__wrapper tag. Basically applying the solution you are suggesting the Carbon clients do. But being in Carbon it can be applied directly to the right spots and not be effected by DOM changes as clients will be.

Sudo code examples... Extract width at the beginning of the input function...

  const width = other.style.width;
  const input = (() => {
    return (
      <>
        <select

And apply it to the tags (in this case bx--select-input__wrapper) as needed.

// where ever the classname `bx--select-input__wrapper` is...
<div className={`${prefix}--select-input__wrapper`} />
// also apply the style
<div className={`${prefix}--select-input__wrapper`} style={width} />
tw15egan commented 3 years ago

We can take a look at this for our next major release, but any change on where the style prop would be placed would be a breaking change for any users currently expecting that to be applied to the select input itself.

As I said, there is a workaround by just applying these styles via css, and not inline on the react component.