jbetancur / react-data-table-component

A responsive table library with built-in sorting, pagination, selection, expandable rows, and customizable styling.
https://react-data-table-component.netlify.app
Apache License 2.0
2.06k stars 413 forks source link

Radio selection for selectableRows #497

Closed Ownmarc closed 3 years ago

Ownmarc commented 4 years ago

Feature Check list

Is your feature request related to a problem? Please describe

Not a problem

Describe the solution you'd like

Make it possible to enable radio selectable rows. Something similar to this https://www.lightningdesignsystem.com/components/data-tables/#Single-row-selection

Describe alternatives you've considered

Could probably work something with selectableRowsComponent, but I think this should be easier then that.

Additional context

No

jbetancur commented 4 years ago

This would not be hard to do if we add a new prop for the native checkbox type=radio. The only caveat will beindeterminate state with radio buttons won't work.

If you need something more custom then selectableRowsComponent is where it's at.

Ownmarc commented 4 years ago

@jbetancur out of curiosity, how could I do it using selectableRowsComponent ?

I've managed to make the type of the checkbox radio using Material UI Radio component, but not sure how to force only 1 option to be selected at a time. Where should I manage the state and how can I pass it to have this behavior ?

image

jbetancur commented 4 years ago

I may have misunderstood. Do you want to have radio buttons but only be able to select one radio at a time?

This actually would be quite a bit more effort to implement.

Ownmarc commented 4 years ago

Yes, the goal would be to only let the user the choice of selecting 0 or 1 line, but not multiple lines. When a new line is selected, it unselects the previous one (if there was one selected) and selects the new one.

See this for example : https://www.lightningdesignsystem.com/components/data-tables/#Single-row-selection

kishore075 commented 4 years ago

Any update on how to select single radio button, i tried by setting element.allSelected = false; element.selectedCount = 1; element.selectedRows.length = 1; but on second click it doesn't work

Ownmarc commented 4 years ago

@kishore075 I think it is not directly possible from the props. May have to use the onChange of the selected and do something to update the selected rows by yourself. Thats what I plan to do.

kishore075 commented 4 years ago

@Ownmarc thansks for your response i written like onSelectedRowsChange={this.handleRowSelected.bind(this)}

private handleRowSelected(element) { element.setSelectedValue = [element.selectedRows[0]]; element.selectedRows.splice(1)

  element.allSelected = false;
  element.selectedCount = 1;
  element.selectedRows.length = 1;

}

it works for one time and second time when i choose second radio button onchange event is not getting trigger and upon 3rd radio button choosing it triggers and reset to one

Ownmarc commented 4 years ago

Pretty sure the "element" you are changing is a state and should not be updated directly like this. I didn't dive into this just yet, lets keep each other updated here if we find something working.

Also, I think splice can have unexpected behavior on states, better check for that

I still think it would be a greate feature to have for this projet.

kishore075 commented 4 years ago

Yes having single selection option will be very great feature to have in, I understand "element" is state object but how to update that state object in my local file. I thought of updating like above help me with some suggestion if you thought of any I am stuck for 2 day now

Ownmarc commented 4 years ago

@kishore075 i'll check it tonight and update

kishore075 commented 4 years ago

thats great thank you!!!

kishore075 commented 4 years ago

I am using import Radio from "@material-ui/core/Radio"; with DataTable props as below selectableRowsComponent={Radio}

Ownmarc commented 4 years ago

I am using import Radio from "@material-ui/core/Radio"; with DataTable props as below selectableRowsComponent={Radio}

Thats only for the "appearance" of the button to select, it doesn't apply the logic of a radio-group

kishore075 commented 4 years ago

yeah on selected change i have written my logic to reset to one

onSelectedRowsChange={this.handleRowSelected.bind(this)}

private handleRowSelected(element) { element.setSelectedValue = [element.selectedRows[0]]; element.selectedRows.splice(1)

element.allSelected = false; element.selectedCount = 1; element.selectedRows.length = 1; }

this code i placed in above comment too

Ownmarc commented 4 years ago

i'm seeing something possible with selectableRowSelected . I think that the onChange should update your data and that in your data you should be specifying if a row is selected or not. I'll get it to work tonight

Ownmarc commented 4 years ago

Got it, here is how I did it:

My data has a isSelected property, they are all false at first.

Then you need to create a function to handle changes in your DataTable Props and tell it to check for the isSelected like this :

onSelectedRowsChange={handleRowSelected}
selectableRowSelected={(row) => row.isSelected}

Here is my handleRowSelected function :

const [dataForTable, changeDataForTable] = useState(null) // I then fetch my data from S3
const handleRowSelected = (whatsChanging) => {
    if (!isEmpty(whatsChanging.selectedRows[0])) {
      changeDataForTable((prevState) =>
        prevState.map((row) => {
          if (whatsChanging.selectedRows[0].id === row.id) {
            row.isSelected = true
          } else {
            row.isSelected = false
          }
          return row
        })
      )
    } else {
      changeDataForTable((prevState) =>
        prevState.map((row) => {
          row.isSelected = false
          return row
        })
      )
    }
  }

Here is the isEmpty function in case you need it :

export default (obj) => {
  return (
    [Object, Array].includes((obj || {}).constructor) &&
    !Object.entries(obj || {}).length
  )
}

ezgif com-video-to-gif

kishore075 commented 4 years ago

wow thats great, you made my day I will try to implement and let you know if any issues i face.

kishore075 commented 4 years ago

Hi I dont see this property getting imported, do i need to import any controls for this property row.isSelected

I tried to maintain in state but that doesn't work

Ownmarc commented 4 years ago

Its a property of datatable

kishore075 commented 4 years ago

Untitled

Ownmarc commented 4 years ago

Oh it has to be a property you add to your data, it can be anything

kishore075 commented 4 years ago

I am getting properties in screenshot from list and i tried to send RequestID which even i used in sortField above, is that i need to add boolean variable in my list Capture

kishore075 commented 4 years ago

Capture

kishore075 commented 4 years ago

DataTable.txt This is my code, please help if i miss something

prasadregula commented 4 years ago

i am also facing the same type issue. could you please help when we can except the release for this fix

Ownmarc commented 4 years ago

SelectableRowSelected need to be given a true or false for each row. It has to be a property in the data you are passing to the table. Add this property to your data and make them all false

kishore075 commented 4 years ago

Yeah i created as below, now isSelected is working

const columns = [ { name: 'AWARD #', selector: 'RequestID', sortable: true }, { name: 'AWARD GIVER', selector: 'Title', sortable: true }, { name: 'AWARD RECIPIENT', selector: 'RecName', sortable: true }, { name: 'DEPARTMENT', selector: 'ReqDEPT', sortable: true }, { name: 'ATTRIBUTE', selector: 'AwardAttribute', sortable: true }, { name: 'isSelected', selector: 'isSelected', cell: row => false } ];

and this is my rowchange function

private handleRowSelected = (rowChanging) => { try { if (rowChanging.selectedRows[0] != null) { this.state.searchslicedAwarditems.map((item) => { if (rowChanging.selectedRows[0].ID === item.ID) { item.isSelected = true; } else { item.isSelected = false; } return item; }); } }
}

It sets to true based on selection but it doesn't reset to one i checked your code you are using prevstate is that makes difference in my code

still i have multi radio button selected

Ownmarc commented 4 years ago

Your map function returns an array. You need to update the state with this new array. I am wrapping my map function with the changeDataForTable to update the state. The prevState is simply giving me access to the previous state to set the new one.

kishore075 commented 4 years ago

@Ownmarc Thanks for your help but i am really sorry i am not getting the output as expected

Error: TypeError: changeDataForTable is not a function

SingleSelection.txt

Attached my code I am using as you been used. Please review my code and help me

Ownmarc commented 4 years ago

changeDataForTable is my function to update my data that are a state. You must use the function to update your searchsliceAwarditems

kishore075 commented 4 years ago

Capture Capture When I install styled components my app doen't work in IE, any suggestions, i have polly fill installed and all other were working after installing polly fill

anaskhan23 commented 3 years ago

@kishore075 Did You get any solution to select single entry (radio) instead of checkbox?

ivanThePleasant commented 3 years ago

@Ownmarc Thanks for the code! It certainly works for me as intended, however I am confused about the prevState here. Looking at React hooks documentation, they don't support prevState. And yet you are using it. Where is it coming from? Is it the same prevState as in componentDidUpdate lifecycle? Also, I'm slightly confused as to why prevState.map is passed in as an expression (as in it is not wrapped in {} after "(prevState) => " ). If I write above code this way, I get an error "Can't read property map of undefined:

changeDataForTable((prevState) => { // <--- prevState.map((row) => { if (whatsChanging.selectedRows[0].id === row.id) { row.isSelected = true } else { row.isSelected = false } return row }) }) // <--- } else { changeDataForTable((prevState) => { // <--- prevState.map((row) => { row.isSelected = false return row }) }) // <--- }

Would be glad if you could explain this to me, thank you.

feiraelectronica commented 3 years ago

Already removed from To do list? will this be fixed then?

slimandslam commented 3 years ago

This is a high priority for our project. We want to be able to select just one row unambiguously.

feiraelectronica commented 3 years ago

This is a high priority for our project. We want to be able to select just one row unambiguously.

hm same goes to me, really need this asap

jbetancur commented 3 years ago

I hear you guys. I'd like this feature as well. But just as much I'd like to write it for you, I'd like just as much to have the time to implement it 😉.

I'm going to try my very best and move this feature back into alpha ver 7(next). But the implementation is not a trivial task.

I appreciate that you rely on this project but it's just me working on this for the time being unless someone wants to take a shot at a PR that has a sound implementation.

slimandslam commented 3 years ago

I'd rather contribute money as my React chops are not up to this task (probably obvious :-) ). I'd also like to point out that we needed the single row selection so badly, that I was going to find a different table project, but then I realized that we could implement single row selection by using two columns with buttons (each button does something different to the row data). In the future, though (Sept/Oct?), we'll need to move to a radio button with a menu selection.

slimandslam commented 3 years ago

Is there a temporary workaround where you use your current checkboxes, but you just add a prop to your table component that lets the user choose "single selection only", so then your code automatically de-selects all other checkboxes when a user selects a checkbox? So, just a a prop like: checkbox=single || normal (normal is default) You could even add the CSS necessary to make the square checkboxes into circles. As long as it looks and works like a radio button, who cares?

jbetancur commented 3 years ago

Yea, that's sort of implementation at a high level. For the radio button, you can do that already by using selectableRowsComponentProps={{ type: 'radio' }}, or by passing in your own Radio Component from some library.

I actually (finally) had some time this afternoon to take a peek at the implementation and I think it may not be too bad. I should be able to turn something around in a few hours at most. Stay tuned...

jbetancur commented 3 years ago

OK! THis is the basic usage:

<DataTable data={yourData} columns={yourColumns} selectableRows selectableRowsSingle selectableRowsComponentProps={{ type: 'radio' }} />

selectableRowsComponentProps can also be a Radio button from another lib. For example, material-ui's Radio button.

I just need to wrap up the tests and I should be able to cut alpha-14 version tonight.

slimandslam commented 3 years ago

Is v7 backwards compatible to the current version (outside of new features, obvi).

jbetancur commented 3 years ago

@slimandslam about 98%. Please check all the v7 release notes on the releases page for breaking changes.

I don't plan on back-porting to v6 since v7 is pretty close to being done.

jbetancur commented 3 years ago

@slimandslam There is a bit of a bug atm. You can't deselect a single select row. I'm going to push a v7-alpha-15 in a few.

feiraelectronica commented 3 years ago

Hi @jbetancur ,

I tried it, but it not working. i'm not sure what do i do wrong.

here is my code

<DataTable columns={columns} data={decryptMenuAttrib.searchResult.filter((WLRec, index, self) => self.findIndex(t => t.ENTITY_ID === WLRec.ENTITY_ID) === index)} noHeader={true} onSelectedRowsChange={this.updateState} selectableRows selectableRowsSingle={true} keyField="ENTITY_ID" customStyles={customStyles} pagination selectableRowsNoSelectAll paginationPerPage={10} paginationRowsPerPageOptions={[5, 10, 25, 50, 100]}/>

and here is the output

image

and this is my package.json. already updated to the latest version

image

Thank you John

slimandslam commented 3 years ago

Where is this prop? selectableRowsComponentProps={{ type: 'radio' }}

feiraelectronica commented 3 years ago

Where is this prop? selectableRowsComponentProps={{ type: 'radio' }}

you reply to me is it? i also tried to use that. but still

image

image

slimandslam commented 3 years ago

Just a thought, try removing selectableRowsNoSelectAll

feiraelectronica commented 3 years ago

Just a thought, try removing

selectableRowsNoSelectAll

Also tried it 😅 and still. You tried yours? Is it working?

feiraelectronica commented 3 years ago
```js
<DataTable data={yourData} columns={yourColumns} selectableRows selectableRowsSingle selectableRowsComponentProps={{ type: 'radio' }} />

when i copy exactly like this code, it become like this

image