selvagsz / react-power-select

A highly composable & reusable select/autocomplete components

Home Page:https://selvagsz.github.io/react-power-select/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

RPS Next

selvagsz opened this issue · comments

commented

Problem

We've quite a few problems with the current implementation of RPS

  1. Currently, disabling a particular option is done via disabled flag in the option. But, what if you need some conditional logic to disable the option like invoice.status === 'void. Same applies for optgroup.

  2. Another main problem is generating a uuid while rendering the options list #17 . Right now, the key is index-based which is an anti-pattern. I could foresee 2 options to solve this problem
    a. introduce optionValuePath. (Unnecessary increase in api surface)
    b. implement/use a guuid counter to generate the uuids. (But why when the consumer can dictate the unique field ?)

Proposal

The best option is to offload the looping of options/optgroups to the consumer end like we do for html selects.

In a better world, we can make use of the render props & children as function patterns to achieve the utmost composability without increasing the api surface.

// Basic - Plain Array
<PowerSelect
  options={items}
  selected={this.state.selectedItem}
  onChange={this.handleChange}
>  
  {
    ({ results, getOptionProps }) => (
      results.map(item => <Option key={item} ...getOptionProps({ option: item })>{item}</Option>)
    )
  }
</PowerSelect>


// Basic - Array of objects
<PowerSelect
  options={items}
  selected={this.state.selectedItem}
  selectedOptionComponent={({ option }) => option.name}
  searchFields={['name']}
  onChange={this.handleChange}
>  
  {
    ({ results, getOptionProps }) => (
      results.map(item => <Option key={item.id} ...getOptionProps({ option: item, disabled: item.status === 'void' })>{item.name}</Option>)
    )
  }
</PowerSelect>

// OptGroup
<PowerSelect
  options={items}
  selected={this.state.selectedItem}
  selectedOptionComponent={({ option }) => option.name}
  searchFields={['name']}
  onChange={this.handleChange}
>  
  {
    ({ results, getOptionProps, getOptGroupProps }) => (
      results.map(item => (
        <OptGroup key={item.groupId} ...getOptGroupProps({ label: item.name, disabled: item.continent === 'Asia' })>
          {
            item.options.map(option => <Option key={option.id} ...getOptionProps({ option: option })>{option.name}</Option>)
          }
        </OptGroup>
      ))
    )
  }
</PowerSelect>


// With all configurations
<PowerSelect
  options={items}
  selected={this.state.selectedItem}
  selectedOptionComponent={({ option }) => option.name}
  onChange={this.handleChange}
  searchFields={['name']}
  selectedOptionComponent={}
  triggerLHSComponent={}
  triggerRHSComponent={}
  beforeOptionsComponent={}
  afterOptionsComponent={}
>
  {
    ({ results, getOptionProps }) => (
      results.map(item => <Option key={item.id} ...getOptionProps({ option: item, disabled=item.status === 'paid' })>{item.name}</Option>)
    )
  }
</PowerSelect>

With the above approach, we can deprecate optionLabelPath, optionComponent completely thereby achieving more composability with decreased api surface 🎉

commented

Seems like we need to add optionValuePath option even in this approach

Updated the code in the issue description ^^