Search code examples
javascriptreactjsmaterial-uimaterial-table

MUI - Material-table/core - Provided selection does not work on checkbox click


EDIT : This issue also concerns Material Ui's Data table. I tried using the same example as given by the documentation but the same problem occurs.

I have been battling with an issue regarding the provided selection feature where, when I click on a select, the box ripples, but does not get selected (no checkmark). The weird thing is that when I tried to create a code sandbox using the same components, everything worked just fine. I'm unsure how to debug this, but it seems that the environment is the cause.

I have forked the material-table-core/core project, and I am planning to do some debugging on there, but don't necessarily know where to look. Thought maybe some bored soul could offer some guidance here ?

It should be noted that if I try to control the checked state from "outside" of the table like instructed in this issue, the selection works just fine.

Here is a gif of the behavior :

enter image description here

The website is a PHP/contao legacy app on a Linux remote server with a lot of jquery all over the place, we recently decided to start using react and deprecating our jquery code gradually. I am currently using a fork of @material-table/[email protected]. But no changes to the fork have been made yet. I just want to use it as a debugging tool. As you can assume, react/webpack and babel have been manually installed and configured (not using CRA).

To understand how react is loaded, take a look at the following files (stripped of all irrelevant code for clarity) :

Index.html5 : (most of these script tags are needed for the old legacy app)

<!DOCTYPE html>
<html>
<head>
    <link rel="preconnect" href="https://fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,600;0,700;0,800;1,300;1,400;1,600;1,700;1,800&display=swap" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css?family=Material+Icons|Material+Icons+Outlined|Material+Icons+Two+Tone|Material+Icons+Round|Material+Icons+Sharp" rel="stylesheet">
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="https://code.jquery.com/jquery-migrate-3.3.2<?= $prodOrDevMode ?>.js"></script>
    <script src="https://code.jquery.com/ui/1.13.0/jquery-ui.min.js"></script> <!-- //! upgraded --> 
    <script src="files/script/tag-it/js/tag-it.js" type="text/javascript" charset="utf-8"></script>
    <script src="files/script/bootstrap-tagsinput/dist/bootstrap-tagsinput<?= $prodOrDevMode ?>.js" type="text/javascript" charset="utf-8"></script> <!-- //! upgraded -->
    <script src="files/script/erp.js?v=<?= date('ymdh'); ?>" type="text/javascript"></script>
    <script src="files/script/cms.js?v=<?= date('ymdh'); ?>" type="text/javascript"></script>
    <script src="files/script/typeahead.js/dist/typeahead.bundle.js?v=<?= date('ymdh'); ?>" type="text/javascript"></script>
    <script src="/files/script/dropzone.js"></script>
    <script src="files/script/tinyMCE/tinymce.min.js" type="text/javascript"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js" type="text/javascript"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>
    <script>Chart.plugins.unregister(ChartDataLabels);</script>
    <script src="files/bootstrap/js/bootstrap.bundle.min.js" type="text/javascript"></script>
    <script src="files/script/tableExport.jquery.plugin-master/tableExport.min.js"></script>
    <script src="files/script/tableExport.jquery.plugin-master/libs/jsPDF/jspdf.min.js"></script>
    <script src="files/script/tableExport.jquery.plugin-master/libs/jsPDF-AutoTable/jspdf.plugin.autotable.js"></script>
    <script src="node_modules/bootstrap-table/dist/bootstrap-table.js" type="text/javascript"></script>
    <script src="node_modules/bootstrap-table/dist/locale/bootstrap-table-it-IT.js"></script>
    <script src="node_modules/bootstrap-table/dist/locale/bootstrap-table-es-ES.js"></script>
    <script src="node_modules/bootstrap-table/dist/locale/bootstrap-table-fr-FR.js"></script>
    <script src="node_modules/bootstrap-table/dist/locale/bootstrap-table-en-US.js"></script>
    <script src="node_modules/bootstrap-table/dist/locale/bootstrap-table-de-DE.min.js"></script>
    <script src="node_modules/bootstrap-table/dist/extensions/sticky-header/bootstrap-table-sticky-header.js"></script>
    <script src="node_modules/bootstrap-table/dist/extensions/export/bootstrap-table-export.min.js"></script>
    <script src="node_modules/bootstrap-table/dist/extensions/toolbar/bootstrap-table-toolbar.min.js"></script>
    <script src="node_modules/bootstrap-table/dist/extensions/filter-control/bootstrap-table-filter-control.min.js"></script>
    <script src="node_modules/bootstrap-table/dist/extensions/mobile/bootstrap-table-mobile.min.js"></script>
    <script src="node_modules/bootstrap-table/dist/extensions/fixed-columns/bootstrap-table-fixed-columns.min.js"></script>
    <script src="files/script/moment.js"></script>
    <script src="files/script/select2/dist/js/select2.min.js"></script>
    <script src="files/script/select2/dist/js/i18n/de.js"></script>   
    <script src="files/script/bootstrap-datetimepicker.min.js"></script>
    <script src="files/script/bootstrap-colorpicker-2.5.2/dist/js/bootstrap-colorpicker.min.js"></script>
    <script src="files/script/myDropzone.js"></script>
    <script src="files/script/jquery.mark.min.js"></script>
    <script src="files/script/bootstrap-select-1.13.14/dist/js/bootstrap-select.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.16.2/xlsx.full.min.js"></script>
    <?php include_once 'dist/styles.html5'; ?> <--- css build loads here
</head>
<body id="top" >
    ..html/scripts
  <!-- Load our Bundled js files. Unbundled files can be found in : ./react/src/Index.js -->
  <?php include_once 'dist/main.html5'; ?> <--- react build loads here
</body>
</html>

Index.js :

import React, { StrictMode } from "react";
import { render } from "react-dom";
import "./Index.css";
import TimeTrackingTable from "./TimeTrackingTable";

const timeTrackingTable = document.getElementById(
  "time_tracking_react_component"
);

if (timeTrackingTable) {
  // console.log("timeTrackingTable", timeTrackingTable);
  render(
    <StrictMode>
      {/* <Provider store={store}> */}
      <TimeTrackingTable {...timeTrackingTable.dataset} />
      {/* </Provider> */}
    </StrictMode>,
    timeTrackingTable
  );
}

Timetracking component :

import React, { useState, forwardRef } from "react";
import MaterialTable from "@material-table/core";

import { Switch } from "@mui/material";
import {
  AddBox,
  ArrowUpward,
  Check,
  ChevronLeft,
  ChevronRight,
  Clear,
  DeleteOutline,
  Edit,
  FilterList,
  FirstPage,
  LastPage,
  Remove,
  SaveAlt,
  Search,
  ViewColumn,
} from "@mui/icons-material";

const tableIcons = {
  Add: forwardRef((props, ref) => <AddBox {...props} ref={ref} />),
  Check: forwardRef((props, ref) => <Check {...props} ref={ref} />),
  Clear: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
  Delete: forwardRef((props, ref) => <DeleteOutline {...props} ref={ref} />),
  DetailPanel: forwardRef((props, ref) => (
    <ChevronRight {...props} ref={ref} />
  )),
  Edit: forwardRef((props, ref) => <Edit {...props} ref={ref} />),
  Export: forwardRef((props, ref) => <SaveAlt {...props} ref={ref} />),
  Filter: forwardRef((props, ref) => <FilterList {...props} ref={ref} />),
  FirstPage: forwardRef((props, ref) => <FirstPage {...props} ref={ref} />),
  LastPage: forwardRef((props, ref) => <LastPage {...props} ref={ref} />),
  NextPage: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
  PreviousPage: forwardRef((props, ref) => (
    <ChevronLeft {...props} ref={ref} />
  )),
  ResetSearch: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
  Search: forwardRef((props, ref) => <Search {...props} ref={ref} />),
  SortArrow: forwardRef((props, ref) => <ArrowUpward {...props} ref={ref} />),
  ThirdStateCheck: forwardRef((props, ref) => <Remove {...props} ref={ref} />),
  ViewColumn: forwardRef((props, ref) => <ViewColumn {...props} ref={ref} />),
};

export default function TimeTrackingTable() {
  return (
    <>
      <MaterialTable
        title="$11.50 Menu"
        icons={tableIcons}
        columns={[{ title: "Section", field: "name" }]}
        data={menuSections}
        options={{
          selection: true,
          detailPanelColumnAlignment: "right",
          sorting: false,
          draggable: false,
          paging: true,
          pageSize: 5, // make initial page size
          emptyRowsWhenPaging: false, // To avoid of having empty rows
          tableLayout: "fixed",
        }}
        onSelectionChange={(rows, e) => {
          console.log("rows", rows);
          console.log("e", e);
        }}
        detailPanel={({ rowData }) => {
          return <RenderDetails rowData={rowData} />;
        }}
      />
      <SelectOutsideOfTable />{" "}
    </>
  );
}
function RenderDetails({ rowData }) {
  const [detailDataRow, setDetailDataRow] = useState({ ...rowData });

  return (
    <MaterialTable
      icons={tableIcons}
      title=""
      columns={[
        {
          title: "Item",
          field: "name",
          headerStyle: { backgroundColor: "royalblue", color: "white" },
        },
        {
          title: "Active",
          field: "active",
          headerStyle: { backgroundColor: "royalblue", color: "white" },
          render: ({ active }) => <Switch checked={active} />,
        },
      ]}
      data={detailDataRow.PartnerMenuItems}
      style={{
        backgroundColor: "aliceblue",
      }}
      options={{
        selection: true,
      }}
      onSelectionChange={(rows) =>
        alert("You selected " + rows.length + " rows")
      }
    />
  );
}

const menuSections = [
  {
    name: "Salads",
    PartnerMenuItems: [
      {
        name: "Farmers Salad",
        active: true,
      },
      {
        name: "Goat Cheese",
        active: true,
      },
      {
        name: "Champagne Vinaigrette",
        active: true,
      },
      {
        name: "Romaine Salad",
        active: true,
      },
      {
        name: "Cilantro Parmesan Dressing",
        active: true,
      },
      {
        name: "Asian Inspired Salad",
        active: true,
      },
      {
        name: "Cilantro Ginger Vinaigrette",
        active: true,
      },
      {
        name: "Beet Citrus Salad",
        active: true,
      },
      {
        name: "Maple Thyme Vinaigrette",
        active: true,
      },
    ],
  },
  .. etc i wont include all data here so not make the post too long 
];

function SelectOutsideOfTable() {
  const [data, setData] = useState([
    {
      fruit: "Apple",
      id: 0,
    },
    {
      fruit: "Orange",
      id: 1,
    },
  ]);

  const toggleRowChecked = (row) => {
    return row.tableData && row.tableData.checked
      ? !row.tableData.checked
      : true;
  };

  const toggleSelectAll = () => {
    const newData = data.map((row) => ({
      ...row,
      tableData: {
        checked: toggleRowChecked(row),
      },
    }));
    setData(newData);
  };

  const columns = [
    {
      title: "Fruit",
      field: "fruit",
    },
    {
      title: "ID",
      field: "id",
    },
  ];

  return (
    <div>
      <button onClick={toggleSelectAll}>Toggle Selection</button>
      <MaterialTable
        title="Select All External"
        columns={columns}
        data={data}
        options={{
          selection: true,
        }}
      />
    </div>
  );
}

Package.json dependencies :

  "devDependencies": {
    "@babel/core": "^7.15.8",
    "@babel/preset-env": "^7.15.8",
    "@babel/preset-react": "^7.9.4",
    "babel-loader": "^8.2.2",
    "clean-webpack-plugin": "^4.0.0",
    "css-loader": "^6.4.0",
    "css-minimizer-webpack-plugin": "^3.1.1",
    "html-webpack-plugin": "^5.4.0",
    "html-webpack-skip-assets-plugin": "^1.0.3",
    "jquery-migrate": "^3.3.2",
    "mini-css-extract-plugin": "^2.4.3",
    "react-json-view": "^1.21.3",
    "style-loader": "^3.3.1",
    "webpack": "^5.59.1",
    "webpack-bundle-analyzer": "^4.5.0",
    "webpack-cli": "^4.9.1",
    "webpack-merge": "^5.8.0"
  },
  "dependencies": {
    "@emotion/react": "^11.7.1",
    "@emotion/styled": "^11.6.0",
    "@material-table/core": "https://github.com/HRpuls/core.git#dist",
    "@material-ui/core": "^4.12.3",
    "@material-ui/data-grid": "^4.0.0-alpha.37",
    "@material-ui/styles": "^4.11.4",
    "@mui/icons-material": "^5.1.0",
    "@mui/lab": "^5.0.0-alpha.56",
    "@mui/material": "^5.2.8",
    "@mui/styled-engine-sc": "^5.1.0",
    "@mui/styles": "^5.2.3",
    "@mui/x-data-grid": "^5.2.2",
    "axios": "^0.24.0",
    "bootstrap-table": "^1.19.1",
    "chart.js": "^3.6.2",
    "chartjs-plugin-datalabels": "^2.0.0",
    "date-fns": "^2.27.0",
    "jquery": "^3.6.0",
    "jquery-ui": "^1.13.0",
    "jspdf": "^2.5.0",
    "jspdf-autotable": "^3.5.23",
    "moment": "^2.29.1",
    "moment-range": "^4.0.2",
    "react": "^17.0.2",
    "react-bootstrap": "^2.0.2",
    "react-chartjs-2": "^4.0.0",
    "react-csv": "^2.2.1",
    "react-dom": "^17.0.2",
    "react-highlight-words": "^0.17.0",
    "react-hook-form": "^7.21.0",
    "react-player": "^2.9.0",
    "react-redux": "^7.2.6",
    "react-router-dom": "^6.1.1",
    "react-spinners": "^0.11.0",
    "react-swipeable-views": "^0.14.0",
    "react-table": "^7.7.0",
    "react-video-js-player": "^1.1.1",
    "redux": "^4.1.2",
    "redux-logger": "^3.0.6",
    "redux-promise-middleware": "^6.1.2",
    "redux-thunk": "^2.4.1",
    "signature_pad": "^4.0.1",
    "styled-components": "^5.3.3",
    "use-debounce": "^7.0.1"
  }

Solution

  • The problem was caused by css. Found some legacy code that gave all checkboxes inputs the following :

    [type="checkbox"]:not(:checked),
    [type="checkbox"]:checked {
      left: -99999px !important;
    }
    
    input[type="checkbox"],
    input[type="radio"] {
      width: auto;
    }
    

    So I just needed to set those values to their initial state and that did the trick.