Repository: jbetancur/react-data-table-component (ts)

Issue:
Radio selection for selectableRows
## Feature Check list

- [x] Agree to the [Code of Conduct](https://github.com/jbetancur/react-data-table-component/blob/master/CODE-OF-CONDUCT.md)
- [x] Read the README to ensure the feature is not already present
- [x] You read [Creating Issues, Features and Pull Requests](https://github.com/jbetancur/react-data-table-component/issues/387)
- [x] Considered the value versus complexity for all users of the library as well as library maintenance
- [x] Considered if this can be a storybook or documentation example

## 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


Pull request description:
allow deselect when selectableRowsSingle
fixes #497 

Patch:
diff --git a/package.json b/package.json
index f989566..6ea7743 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "react-data-table-component",
-  "version": "7.0.0-alpha-14",
+  "version": "7.0.0-alpha-15",
   "description": "A declarative react based data table",
   "main": "dist/index.cjs.js",
   "module": "dist/index.es.js",
diff --git a/src/DataTable/tableReducer.ts b/src/DataTable/tableReducer.ts
index a4d6e0b..0674a15 100644
--- a/src/DataTable/tableReducer.ts
+++ b/src/DataTable/tableReducer.ts
@@ -40,16 +40,26 @@ export function tableReducer<T extends RowRecord>(state: TableState<T>, action:
 		case 'SELECT_SINGLE_ROW': {
 			const { keyField, row, isSelected, rowCount, singleSelect } = action;
 
-			//  single select mode allows only 1 row to be selected at a time
+			// handle single select mode
 			if (singleSelect) {
+				if (isSelected) {
+					return {
+						...state,
+						selectedCount: 0,
+						allSelected: false,
+						selectedRows: [],
+					};
+				}
+
 				return {
 					...state,
-					selectedCount: 0, // this also has the effect of disabling the context menu
+					selectedCount: 1,
 					allSelected: false,
 					selectedRows: insertItem([], row),
 				};
 			}
 
+			// handlesmulti select mode
 			if (isSelected) {
 				return {
 					...state,
diff --git a/stories/DataTable/Basic/1BasicKitchenSink.stories.js b/stories/DataTable/Basic/1BasicKitchenSink.stories.js
index 27c01b2..f4d4320 100644
--- a/stories/DataTable/Basic/1BasicKitchenSink.stories.js
+++ b/stories/DataTable/Basic/1BasicKitchenSink.stories.js
@@ -13,6 +13,15 @@ import TextField from '@material-ui/core/TextField';
 import data from '../constants/sampleMovieData';
 import DataTable from '../../../src/index';
 
+const subHeaderComponent = (
+	<div style={{ display: 'flex', alignItems: 'center' }}>
+		<TextField id="outlined-basic" label="Search" variant="outlined" size="small" style={{ margin: '5px' }} />
+		<Icon1 style={{ margin: '5px' }} color="action" />
+		<Icon2 style={{ margin: '5px' }} color="action" />
+		<Icon3 style={{ margin: '5px' }} color="action" />
+	</div>
+);
+
 const columns = [
 	{
 		name: 'Title',
@@ -37,6 +46,7 @@ const KitchenSink = () => {
 	const [selectableRowsVisibleOnly, setSelectableRowsVisibleOnly] = React.useState(false);
 	const [selectableRowsHighlight, setSelectableRowsHighlight] = React.useState(false);
 	const [selectableRowsSingle, setSelectableRowsSingle] = React.useState(false);
+	const [selectableRowsRadio, setSelectableRowsRadio] = React.useState(false);
 	const [expandableRows, setExpandableRows] = React.useState(false);
 	const [expandOnRowClick, setExpandOnRowClick] = React.useState(false);
 	const [pagination, setPagination] = React.useState(true);
@@ -55,6 +65,13 @@ const KitchenSink = () => {
 	const [direction, setDirection] = React.useState(false);
 	const [directionValue, setDirectionValue] = React.useState('auto');
 
+	const selectableRowsComponentProps = React.useMemo(
+		() => ({
+			type: selectableRowsRadio ? 'radio' : 'checkbox',
+		}),
+		[selectableRowsRadio],
+	);
+
 	return (
 		<div>
 			<FormControlLabel
@@ -108,6 +125,17 @@ const KitchenSink = () => {
 							label="Single Select Mode"
 						/>
 
+						<FormControlLabel
+							control={
+								<Checkbox
+									size="small"
+									checked={selectableRowsRadio}
+									onChange={() => setSelectableRowsRadio(!selectableRowsRadio)}
+								/>
+							}
+							label="Radio Button"
+						/>
+
 						<FormControlLabel
 							control={<Checkbox size="small" checked={tableHead} onChange={() => setNoHead(!tableHead)} />}
 							label="No Table Head"
@@ -240,7 +268,7 @@ const KitchenSink = () => {
 				data={data}
 				defaultSortField="title"
 				selectableRows={selectableRows}
-				selectableRowsComponentProps={{ type: 'radio' }}
+				selectableRowsComponentProps={selectableRowsComponentProps}
 				selectableRowsNoSelectAll={noSelectAll}
 				selectableRowsHighlight={selectableRowsHighlight}
 				selectableRowsSingle={selectableRowsSingle}
@@ -257,14 +285,8 @@ const KitchenSink = () => {
 				progressPending={loading}
 				noHeader={noHeader}
 				subHeader={subHeader}
-				subHeaderComponent={
-					<div style={{ display: 'flex', alignItems: 'center' }}>
-						<TextField id="outlined-basic" label="Search" variant="outlined" size="small" style={{ margin: '5px' }} />
-						<Icon1 style={{ margin: '5px' }} color="action" />
-						<Icon2 style={{ margin: '5px' }} color="action" />
-						<Icon3 style={{ margin: '5px' }} color="action" />
-					</div>
-				}
+				noContextMenu={noContextMenu}
+				subHeaderComponent={subHeaderComponent}
 				subHeaderAlign={subHeaderAlign}
 				fixedHeader={fixedHeader}
 				fixedHeaderScrollHeight="300px"


Test patch:
diff --git a/src/DataTable/__tests__/DataTable.test.tsx b/src/DataTable/__tests__/DataTable.test.tsx
index a038eb8..9bc434e 100644
--- a/src/DataTable/__tests__/DataTable.test.tsx
+++ b/src/DataTable/__tests__/DataTable.test.tsx
@@ -861,6 +861,22 @@ describe('DataTable::selectableRows', () => {
 		expect(container.firstChild).toMatchSnapshot();
 	});
 
+	test('should de-select a single row when clicked again', () => {
+		const mock = dataMock();
+		const { container } = render(
+			<DataTable data={mock.data} columns={mock.columns} selectableRows selectableRowsSingle />,
+		);
+
+		const rowCheck = container.querySelector('input[name="select-row-1"]') as HTMLInputElement;
+
+		fireEvent.click(rowCheck);
+		expect(rowCheck.checked).toBe(true);
+
+		fireEvent.click(rowCheck);
+
+		expect(rowCheck.checked).toBe(false);
+	});
+
 	test('should clear all rows selectableRowsSingle is changed', () => {
 		const mock = dataMock();
 		const { container, rerender } = render(<DataTable data={mock.data} columns={mock.columns} selectableRows />);