Repository: react-materialize/react-materialize (js)

Issue:
Select / Deselect Checkboxes Not Updating In React
### Select / Deselect Checkboxes Not Updating In React

We have a React component that lists checkboxes with user names. At the top of the list is a Select and Deselect All button. We have validated that our Redux store is updating and returning the appropriate props to the component so it should re-render the checkbox list either selected or deselected accordingly. The issue though is that each checkbox does not update, but remains its initial value.

### Steps to Reproduce

1. Create a React Application using Create-React-App
2. Map through an array of users and rendering the React-Materialize Checkbox component with the 'checked' parameter dynamic based on the users.isChecked value


**Expected behavior:** All checkboxes should be selected or deselected upon the click action of the corresponding button.

**Actual behavior:** Each checkbox does not update, but remains its initial value.

### Versions

**react-materialize:** 3.0.5
**materialize-css:** 1.0.0
**react:** 16.8.2

#### Thanks for helping out!

### Screenshot
![image](https://user-images.githubusercontent.com/29242164/56145780-ebe3dc00-5f72-11e9-8c3a-2fe8391423a2.png)



Pull request description:
fix: dynamically update `checked` props
# Description

Similar to #792 (resolved with #797), it was impossible for a `Checkbox` to be "controlled".
This should fix #823 

I also took the occasion to add the possibility to pass down `className`s and other properties to the `input` element.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

# How Has This Been Tested?

Added a new test to make sure to preserve `filled-in` materialize-css class when passing down other `className`s.

# Checklist:

- [x] I have commented my code, particularly in hard-to-understand areas
- [x] My changes generate no new warnings
- [x] I have not generated a new package version. (the maintainers will handle that)


Patch:
diff --git a/src/Checkbox.js b/src/Checkbox.js
index ed134ff..529c6b2 100644
--- a/src/Checkbox.js
+++ b/src/Checkbox.js
@@ -1,9 +1,10 @@
-import React, { Component } from 'react';
+import React from 'react';
 import PropTypes from 'prop-types';
 import cx from 'classnames';
 import idgen from './idgen';
 
 const Checkbox = ({
+  className,
   checked,
   onChange,
   filledIn,
@@ -11,27 +12,24 @@ const Checkbox = ({
   disabled,
   value,
   id,
-  indeterminate
+  indeterminate,
+  ...other
 }) => {
-  let computedId;
-  if (indeterminate) {
-    computedId = 'indeterminate-checkbox';
-  } else if (id) {
-    computedId = id;
-  } else {
-    computedId = idgen();
-  }
+  const computedId =
+    id ||
+    (indeterminate ? `indeterminate-checkbox${idgen()}` : `checkbox${idgen()}`);
 
   return (
     <label htmlFor={computedId}>
       <input
         id={computedId}
-        className={cx({ 'filled-in': filledIn })}
+        className={cx({ 'filled-in': filledIn }, className)}
         disabled={disabled}
         onChange={onChange}
         type="checkbox"
-        defaultChecked={checked}
+        checked={checked}
         value={value}
+        {...other}
       />
       <span>{label}</span>
     </label>
@@ -39,6 +37,7 @@ const Checkbox = ({
 };
 
 Checkbox.propTypes = {
+  className: PropTypes.string,
   /*
    * checkbox value
    */
@@ -69,7 +68,7 @@ Checkbox.propTypes = {
    */
   disabled: PropTypes.bool,
   /*
-   * initialise checkbox checked
+   * A Boolean attribute indicating whether or not this checkbox is checked
    */
   checked: PropTypes.bool
 };
diff --git a/src/Dropdown.js b/src/Dropdown.js
index c838aed..40b026d 100644
--- a/src/Dropdown.js
+++ b/src/Dropdown.js
@@ -1,4 +1,4 @@
-import React, { Component, Fragment, Children, cloneElement } from 'react';
+import React, { Component } from 'react';
 import PropTypes from 'prop-types';
 import idgen from './idgen';
 import cx from 'classnames';
@@ -6,24 +6,22 @@ import cx from 'classnames';
 class Dropdown extends Component {
   constructor(props) {
     super(props);
-    this.idx = props.id || `dropdown${idgen()}`;
+    this.idx = 'dropdown_' + idgen();
     this.renderTrigger = this.renderTrigger.bind(this);
     this.renderItems = this.renderItems.bind(this);
   }
 
   componentDidMount() {
-    const { options } = this.props;
+    const options = this.props.options || {};
 
     if (typeof M !== undefined) {
-      this.instance = M.Dropdown.init(
-        document.querySelectorAll(this._trigger),
-        options
-      )[0];
+      const selector = document.querySelectorAll(this._trigger);
+      this.instance = M.Dropdown.init(selector, options)[0];
     }
   }
 
   componentWillUnmount() {
-    if (this.instance) {
+    if (typeof M !== undefined) {
       this.instance.destroy();
     }
   }
@@ -34,7 +32,7 @@ class Dropdown extends Component {
     delete props.options;
 
     return (
-      <Fragment>
+      <React.Fragment>
         {this.renderTrigger()}
         <ul
           {...props}
@@ -43,14 +41,14 @@ class Dropdown extends Component {
         >
           {this.renderItems()}
         </ul>
-      </Fragment>
+      </React.Fragment>
     );
   }
 
   renderTrigger() {
     const { trigger } = this.props;
 
-    return cloneElement(trigger, {
+    return React.cloneElement(trigger, {
       'data-target': this.idx,
       ref: t => (this._trigger = `[data-target=${this.idx}]`),
       className: cx(trigger.props.className, 'dropdown-trigger')
@@ -60,9 +58,9 @@ class Dropdown extends Component {
   renderItems() {
     const { children } = this.props;
 
-    return Children.map(children, element => {
+    return React.Children.map(children, element => {
       if (element.type.name === 'Divider') {
-        return <li key={idgen()} className="divider" tabIndex="-1" />;
+        return <li className="divider" tabIndex="-1" />;
       } else {
         return <li key={idgen()}>{element}</li>;
       }
@@ -71,7 +69,6 @@ class Dropdown extends Component {
 }
 
 Dropdown.propTypes = {
-  id: PropTypes.string,
   /**
    * The node to trigger the dropdown
    */
@@ -100,22 +97,4 @@ Dropdown.propTypes = {
   })
 };
 
-Dropdown.defaultProps = {
-  options: {
-    alignment: 'left',
-    autoTrigger: true,
-    constrainWidth: true,
-    container: null,
-    coverTrigger: true,
-    closeOnClick: true,
-    hover: false,
-    inDuration: 150,
-    outDuration: 250,
-    onOpenStart: null,
-    onOpenEnd: null,
-    onCloseStart: null,
-    onCloseEnd: null
-  }
-};
-
 export default Dropdown;
diff --git a/src/Modal.js b/src/Modal.js
index f1e1922..aba4fb9 100644
--- a/src/Modal.js
+++ b/src/Modal.js
@@ -1,4 +1,4 @@
-import React, { Component, Fragment } from 'react';
+import React, { Component } from 'react';
 import ReactDOM from 'react-dom';
 import PropTypes from 'prop-types';
 import cx from 'classnames';
@@ -21,7 +21,7 @@ class Modal extends Component {
 
   componentDidMount() {
     if (typeof M !== 'undefined') {
-      const { options, open } = this.props;
+      const { trigger, options, open } = this.props;
 
       this.instance = M.Modal.init(this._modal, options);
 
@@ -70,26 +70,27 @@ class Modal extends Component {
       className
     );
 
-    return (
-      this.modalRoot &&
-      ReactDOM.createPortal(
-        <div
-          {...other}
-          className={classes}
-          id={this.modalID}
-          ref={div => {
-            this._modal = div;
-          }}
-        >
-          <div className="modal-content">
-            <h4>{header}</h4>
-            {children}
-          </div>
-          <div className="modal-footer">{React.Children.toArray(actions)}</div>
-        </div>,
-        this.modalRoot
-      )
-    );
+    return this.modalRoot
+      ? ReactDOM.createPortal(
+          <div
+            {...other}
+            className={classes}
+            id={this.modalID}
+            ref={div => {
+              this._modal = div;
+            }}
+          >
+            <div className="modal-content">
+              <h4>{header}</h4>
+              {children}
+            </div>
+            <div className="modal-footer">
+              {React.Children.toArray(actions)}
+            </div>
+          </div>,
+          this.modalRoot
+        )
+      : null;
   }
 
   showModal(e) {
@@ -108,10 +109,10 @@ class Modal extends Component {
     const { trigger } = this.props;
 
     return (
-      <Fragment>
+      <div>
         {trigger && React.cloneElement(trigger, { onClick: this.showModal })}
         {this.renderModalPortal()}
-      </Fragment>
+      </div>
     );
   }
 }
@@ -123,8 +124,8 @@ Modal.propTypes = {
    */
   options: PropTypes.shape({
     /*
-     * Opacity of the modal overlay.
-     */
+    * Opacity of the modal overlay.
+    */
     opacity: PropTypes.number,
     /*
      * Transition in duration in milliseconds.
diff --git a/stories/Dropdown.stories.js b/stories/Dropdown.stories.js
index 841e9e1..966e345 100644
--- a/stories/Dropdown.stories.js
+++ b/stories/Dropdown.stories.js
@@ -3,7 +3,6 @@ import { storiesOf } from '@storybook/react';
 import Dropdown from '../src/Dropdown';
 import Divider from '../src/Divider';
 import Button from '../src/Button';
-import Icon from '../src/Icon';
 
 const stories = storiesOf('Javascript|Dropdown', module);
 
@@ -16,16 +15,9 @@ stories.addParameters({
 });
 
 stories.add('Default', () => (
-  <Dropdown trigger={<Button>Drop Me!</Button>}>
-    <a href="#">one</a>
-    <a href="#">two</a>
+  <Dropdown trigger={<Button>open</Button>}>
+    <a href="#">test</a>
     <Divider />
-    <a href="#">three</a>
-    <a href="#">
-      <Icon>view_module</Icon>four
-    </a>
-    <a href="#">
-      <Icon>cloud</Icon> five
-    </a>
+    <a href="#">test</a>
   </Dropdown>
 ));


Test patch:
diff --git a/test/Checkbox.spec.js b/test/Checkbox.spec.js
index 90d3a00..b7d79a8 100644
--- a/test/Checkbox.spec.js
+++ b/test/Checkbox.spec.js
@@ -1,5 +1,5 @@
 import React from 'react';
-import { shallow } from 'enzyme';
+import { shallow, mount } from 'enzyme';
 import Checkbox from '../src/Checkbox';
 import idgen from '../src/idgen';
 
@@ -36,7 +36,13 @@ describe('<Checkbox />', () => {
 
   test('with indeterminate', () => {
     wrapper = shallow(<Checkbox value="red" label="red" indeterminate />);
-    expect(idgen).not.toHaveBeenCalled();
+    expect(wrapper).toMatchSnapshot();
+  });
+
+  test('does not overwrite materialize-css classnames', () => {
+    wrapper = mount(
+      <Checkbox value="red" label="red" filledIn className="my-class" />
+    );
     expect(wrapper).toMatchSnapshot();
   });
 });
diff --git a/test/DropdownButton.spec.js b/test/DropdownButton.spec.js
index 1fa9c41..0db2491 100644
--- a/test/DropdownButton.spec.js
+++ b/test/DropdownButton.spec.js
@@ -58,25 +58,6 @@ describe('<Dropdown />', () => {
       restore();
     });
 
-    test('with default options if none are given', () => {
-      wrapper = shallow(<Dropdown trigger={<button>Drop me!</button>} />);
-      expect(dropdownInitMock).toHaveBeenCalledWith({
-        alignment: 'left',
-        autoTrigger: true,
-        constrainWidth: true,
-        container: null,
-        coverTrigger: true,
-        closeOnClick: true,
-        hover: false,
-        inDuration: 150,
-        outDuration: 250,
-        onOpenStart: null,
-        onOpenEnd: null,
-        onCloseStart: null,
-        onCloseEnd: null
-      });
-    });
-
     test('handles options prop', () => {
       const options = { constrainWidth: true };
       wrapper = shallow(
diff --git a/test/__snapshots__/Checkbox.spec.js.snap b/test/__snapshots__/Checkbox.spec.js.snap
index 090dd63..82795bb 100644
--- a/test/__snapshots__/Checkbox.spec.js.snap
+++ b/test/__snapshots__/Checkbox.spec.js.snap
@@ -1,12 +1,35 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`<Checkbox /> does not overwrite materialize-css classnames 1`] = `
+<Checkbox
+  className="my-class"
+  filledIn={true}
+  label="red"
+  value="red"
+>
+  <label
+    htmlFor="checkboxmockId"
+  >
+    <input
+      className="filled-in my-class"
+      id="checkboxmockId"
+      type="checkbox"
+      value="red"
+    />
+    <span>
+      red
+    </span>
+  </label>
+</Checkbox>
+`;
+
 exports[`<Checkbox /> filled-in 1`] = `
 <label
-  htmlFor="mockId"
+  htmlFor="checkboxmockId"
 >
   <input
     className="filled-in"
-    id="mockId"
+    id="checkboxmockId"
     type="checkbox"
     value="red"
   />
@@ -18,11 +41,11 @@ exports[`<Checkbox /> filled-in 1`] = `
 
 exports[`<Checkbox /> renders 1`] = `
 <label
-  htmlFor="mockId"
+  htmlFor="checkboxmockId"
 >
   <input
     className=""
-    id="mockId"
+    id="checkboxmockId"
     type="checkbox"
     value="red"
   />
@@ -34,12 +57,12 @@ exports[`<Checkbox /> renders 1`] = `
 
 exports[`<Checkbox /> renders disabled 1`] = `
 <label
-  htmlFor="mockId"
+  htmlFor="checkboxmockId"
 >
   <input
     className=""
     disabled={true}
-    id="mockId"
+    id="checkboxmockId"
     type="checkbox"
     value="red"
   />
@@ -67,11 +90,11 @@ exports[`<Checkbox /> with id 1`] = `
 
 exports[`<Checkbox /> with indeterminate 1`] = `
 <label
-  htmlFor="indeterminate-checkbox"
+  htmlFor="indeterminate-checkboxmockId"
 >
   <input
     className=""
-    id="indeterminate-checkbox"
+    id="indeterminate-checkboxmockId"
     type="checkbox"
     value="red"
   />
diff --git a/test/__snapshots__/DropdownButton.spec.js.snap b/test/__snapshots__/DropdownButton.spec.js.snap
index 96ad5d2..84ae3e8 100644
--- a/test/__snapshots__/DropdownButton.spec.js.snap
+++ b/test/__snapshots__/DropdownButton.spec.js.snap
@@ -4,13 +4,13 @@ exports[`<Dropdown /> renders 1`] = `
 <Fragment>
   <button
     className="dropdown-trigger"
-    data-target="dropdownmockId"
+    data-target="dropdown_mockId"
   >
     Drop me!
   </button>
   <ul
     className="dropdown-content"
-    id="dropdownmockId"
+    id="dropdown_mockId"
   />
 </Fragment>
 `;
diff --git a/test/__snapshots__/Modal.spec.js.snap b/test/__snapshots__/Modal.spec.js.snap
index a4dbb07..f3d8c46 100644
--- a/test/__snapshots__/Modal.spec.js.snap
+++ b/test/__snapshots__/Modal.spec.js.snap
@@ -29,79 +29,81 @@ exports[`<Modal /> renders a modal has children 1`] = `
     </button>
   }
 >
-  <button
-    onClick={[Function]}
-  >
-    click
-  </button>
-  <Portal
-    containerInfo={
-      <div>
-        <div
-          class="modal"
-          id="modal_mockId"
-        >
+  <div>
+    <button
+      onClick={[Function]}
+    >
+      click
+    </button>
+    <Portal
+      containerInfo={
+        <div>
           <div
-            class="modal-content"
+            class="modal"
+            id="modal_mockId"
           >
-            <h4>
-              Modal header
-            </h4>
-            <div>
-              <h1>
-                Hello
-              </h1>
+            <div
+              class="modal-content"
+            >
+              <h4>
+                Modal header
+              </h4>
+              <div>
+                <h1>
+                  Hello
+                </h1>
+              </div>
             </div>
-          </div>
-          <div
-            class="modal-footer"
-          >
-            <button
-              class="waves-effect waves-green btn-flat modal-close"
+            <div
+              class="modal-footer"
             >
-              Close
-            </button>
+              <button
+                class="waves-effect waves-green btn-flat modal-close"
+              >
+                Close
+              </button>
+            </div>
           </div>
         </div>
-      </div>
-    }
-  >
-    <div
-      className="modal"
-      id="modal_mockId"
+      }
     >
       <div
-        className="modal-content"
+        className="modal"
+        id="modal_mockId"
       >
-        <h4>
-          Modal header
-        </h4>
-        <div>
-          <h1>
-            Hello
-          </h1>
+        <div
+          className="modal-content"
+        >
+          <h4>
+            Modal header
+          </h4>
+          <div>
+            <h1>
+              Hello
+            </h1>
+          </div>
         </div>
-      </div>
-      <div
-        className="modal-footer"
-      >
-        <Button
-          flat={true}
-          key=".0"
-          modal="close"
-          node="button"
-          waves="green"
+        <div
+          className="modal-footer"
         >
-          <button
-            className="waves-effect waves-green btn-flat modal-close"
-            disabled={false}
+          <Button
+            flat={true}
+            key=".0"
+            modal="close"
+            node="button"
+            waves="green"
           >
-            Close
-          </button>
-        </Button>
+            <button
+              className="waves-effect waves-green btn-flat modal-close"
+              disabled={false}
+            >
+              Close
+            </button>
+          </Button>
+        </div>
       </div>
-    </div>
-  </Portal>
+    </Portal>
+  </div>
 </Modal>
 `;
 
@@ -134,79 +136,81 @@ exports[`<Modal /> renders a trigger renders 1`] = `
     </button>
   }
 >
-  <button
-    onClick={[Function]}
-  >
-    click
-  </button>
-  <Portal
-    containerInfo={
-      <div>
-        <div
-          class="modal"
-          id="modal_mockId"
-        >
+  <div>
+    <button
+      onClick={[Function]}
+    >
+      click
+    </button>
+    <Portal
+      containerInfo={
+        <div>
           <div
-            class="modal-content"
+            class="modal"
+            id="modal_mockId"
           >
-            <h4>
-              Modal header
-            </h4>
-            <div>
-              <h1>
-                Hello
-              </h1>
+            <div
+              class="modal-content"
+            >
+              <h4>
+                Modal header
+              </h4>
+              <div>
+                <h1>
+                  Hello
+                </h1>
+              </div>
             </div>
-          </div>
-          <div
-            class="modal-footer"
-          >
-            <button
-              class="waves-effect waves-green btn-flat modal-close"
+            <div
+              class="modal-footer"
             >
-              Close
-            </button>
+              <button
+                class="waves-effect waves-green btn-flat modal-close"
+              >
+                Close
+              </button>
+            </div>
           </div>
         </div>
-      </div>
-    }
-  >
-    <div
-      className="modal"
-      id="modal_mockId"
+      }
     >
       <div
-        className="modal-content"
+        className="modal"
+        id="modal_mockId"
       >
-        <h4>
-          Modal header
-        </h4>
-        <div>
-          <h1>
-            Hello
-          </h1>
+        <div
+          className="modal-content"
+        >
+          <h4>
+            Modal header
+          </h4>
+          <div>
+            <h1>
+              Hello
+            </h1>
+          </div>
         </div>
-      </div>
-      <div
-        className="modal-footer"
-      >
-        <Button
-          flat={true}
-          key=".0"
-          modal="close"
-          node="button"
-          waves="green"
+        <div
+          className="modal-footer"
         >
-          <button
-            className="waves-effect waves-green btn-flat modal-close"
-            disabled={false}
+          <Button
+            flat={true}
+            key=".0"
+            modal="close"
+            node="button"
+            waves="green"
           >
-            Close
-          </button>
-        </Button>
+            <button
+              className="waves-effect waves-green btn-flat modal-close"
+              disabled={false}
+            >
+              Close
+            </button>
+          </Button>
+        </div>
       </div>
-    </div>
-  </Portal>
+    </Portal>
+  </div>
 </Modal>
 `;
 
@@ -233,69 +237,71 @@ exports[`<Modal /> without a trigger renders 1`] = `
     }
   }
 >
-  <Portal
-    containerInfo={
-      <div>
-        <div
-          class="modal"
-          id="modal_mockId"
-        >
+  <div>
+    <Portal
+      containerInfo={
+        <div>
           <div
-            class="modal-content"
+            class="modal"
+            id="modal_mockId"
           >
-            <h4 />
-            <div>
-              <h1>
-                Hello
-              </h1>
+            <div
+              class="modal-content"
+            >
+              <h4 />
+              <div>
+                <h1>
+                  Hello
+                </h1>
+              </div>
             </div>
-          </div>
-          <div
-            class="modal-footer"
-          >
-            <button
-              class="waves-effect waves-green btn-flat modal-close"
+            <div
+              class="modal-footer"
             >
-              Close
-            </button>
+              <button
+                class="waves-effect waves-green btn-flat modal-close"
+              >
+                Close
+              </button>
+            </div>
           </div>
         </div>
-      </div>
-    }
-  >
-    <div
-      className="modal"
-      id="modal_mockId"
+      }
     >
       <div
-        className="modal-content"
+        className="modal"
+        id="modal_mockId"
       >
-        <h4 />
-        <div>
-          <h1>
-            Hello
-          </h1>
+        <div
+          className="modal-content"
+        >
+          <h4 />
+          <div>
+            <h1>
+              Hello
+            </h1>
+          </div>
         </div>
-      </div>
-      <div
-        className="modal-footer"
-      >
-        <Button
-          flat={true}
-          key=".0"
-          modal="close"
-          node="button"
-          waves="green"
+        <div
+          className="modal-footer"
         >
-          <button
-            className="waves-effect waves-green btn-flat modal-close"
-            disabled={false}
+          <Button
+            flat={true}
+            key=".0"
+            modal="close"
+            node="button"
+            waves="green"
           >
-            Close
-          </button>
-        </Button>
+            <button
+              className="waves-effect waves-green btn-flat modal-close"
+              disabled={false}
+            >
+              Close
+            </button>
+          </Button>
+        </div>
       </div>
-    </div>
-  </Portal>
+    </Portal>
+  </div>
 </Modal>
 `;