Browse Source

ui: fix mulitple checkbox usage

- pass name on token creation
- fix onChange handler
- add specs

Fixes JAVASCRIPT-26J
David Cramer 7 years ago
parent
commit
b27ee2d847

+ 3 - 2
src/sentry/static/sentry/app/components/forms/booleanField.jsx

@@ -28,8 +28,9 @@ export default class BooleanField extends InputField {
   }
 
   render() {
+    let error = this.getError();
     let className = this.getClassName();
-    if (this.props.error) {
+    if (error) {
       className += ' has-error';
     }
     return (
@@ -45,7 +46,7 @@ export default class BooleanField extends InputField {
               </span>}
           </label>
           {defined(this.props.help) && <p className="help-block">{this.props.help}</p>}
-          {this.props.error && <p className="error">{this.props.error}</p>}
+          {error && <p className="error">{error}</p>}
         </div>
       </div>
     );

+ 24 - 23
src/sentry/static/sentry/app/components/forms/multipleCheckboxField.jsx

@@ -4,11 +4,9 @@ import ReactDOM from 'react-dom';
 import FormField from './formField';
 
 export default class MultipleCheckboxField extends FormField {
-  constructor(props) {
-    super(props);
-
-    this.state.value = new Set(props.value || []);
-  }
+  static propTypes = {
+    choices: React.PropTypes.array.isRequired
+  };
 
   // XXX(dcramer): this comes from TooltipMixin
   componentDidMount() {
@@ -28,22 +26,24 @@ export default class MultipleCheckboxField extends FormField {
     jQuery('.tip', ReactDOM.findDOMNode(this)).tooltip('destroy');
   }
 
-  onChange(value, e) {
-    if (e.target.checked) this.state.value.add(value);
-    else this.state.value.delete(value);
-    this.setState(
-      {
-        value: this.state.value
-      },
-      () => {
-        this.props.onChange(Array.from(this.state.value.keys()));
+  onChange = (value, e) => {
+    let allValues = this.state.value;
+    if (e.target.checked) {
+      if (allValues) {
+        allValues = [...allValues, value];
+      } else {
+        allValues = [value];
       }
-    );
-  }
+    } else {
+      allValues = allValues.filter(v => v !== value);
+    }
+    this.setValue(allValues);
+  };
 
   render() {
+    let error = this.getError();
     let className = 'control-group';
-    if (this.props.error) {
+    if (error) {
       className += ' has-error';
     }
     return (
@@ -57,18 +57,19 @@ export default class MultipleCheckboxField extends FormField {
             </span>}
         </label>
         {this.props.help && <p className="help-block">{this.props.help}</p>}
+        {error && <p className="error">{error}</p>}
         <div className="controls control-list">
-          {this.props.choices.map(choice => {
+          {this.props.choices.map(([value, label]) => {
             return (
-              <label className="checkbox" key={choice[0]}>
+              <label className="checkbox" key={value}>
                 <input
                   type="checkbox"
-                  value={choice[0]}
-                  onChange={this.onChange.bind(this, choice[0])}
+                  value={value}
+                  onChange={this.onChange.bind(this, value)}
                   disabled={this.props.disabled}
-                  checked={this.state.value.has(choice[0])}
+                  checked={this.state.value.indexOf(value) !== -1}
                 />
-                {choice[1]}
+                {label}
               </label>
             );
           })}

+ 1 - 0
src/sentry/static/sentry/app/views/apiNewToken.jsx

@@ -109,6 +109,7 @@ const TokenForm = React.createClass({
         <fieldset>
           <MultipleCheckboxField
             key="scopes"
+            name="scopes"
             choices={Array.from(SCOPES.keys()).map(s => [s, s])}
             label={t('Scopes')}
             value={this.state.formData.scopes}

+ 1 - 1
src/sentry/static/sentry/app/views/apiTokens.jsx

@@ -64,7 +64,7 @@ const ApiTokenRow = React.createClass({
             <small><AutoSelectText>{token.token}</AutoSelectText></small>
           </div>
           <div style={{marginBottom: 5}}>
-            <small>Created <DateTime value={token.dateCreated} /></small>
+            <small>Created <DateTime date={token.dateCreated} /></small>
           </div>
           <div>
             <small style={{color: '#999'}}>{token.scopes.join(', ')}</small>

+ 77 - 0
tests/js/spec/components/forms/__snapshots__/multipleCheckboxField.spec.jsx.snap

@@ -0,0 +1,77 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`MultipleCheckboxField render() renders with form context 1`] = `
+<div
+  className="control-group"
+>
+  <label
+    className="control-label"
+  />
+  <div
+    className="controls control-list"
+  >
+    <label
+      className="checkbox"
+    >
+      <input
+        checked={true}
+        disabled={false}
+        onChange={[Function]}
+        type="checkbox"
+        value="1"
+      />
+      On
+    </label>
+    <label
+      className="checkbox"
+    >
+      <input
+        checked={false}
+        disabled={false}
+        onChange={[Function]}
+        type="checkbox"
+        value="2"
+      />
+      Off
+    </label>
+  </div>
+</div>
+`;
+
+exports[`MultipleCheckboxField render() renders without form context 1`] = `
+<div
+  className="control-group"
+>
+  <label
+    className="control-label"
+  />
+  <div
+    className="controls control-list"
+  >
+    <label
+      className="checkbox"
+    >
+      <input
+        checked={true}
+        disabled={false}
+        onChange={[Function]}
+        type="checkbox"
+        value="1"
+      />
+      On
+    </label>
+    <label
+      className="checkbox"
+    >
+      <input
+        checked={false}
+        disabled={false}
+        onChange={[Function]}
+        type="checkbox"
+        value="2"
+      />
+      Off
+    </label>
+  </div>
+</div>
+`;

+ 37 - 0
tests/js/spec/components/forms/multipleCheckboxField.spec.jsx

@@ -0,0 +1,37 @@
+import React from 'react';
+import {shallow} from 'enzyme';
+import toJson from 'enzyme-to-json';
+
+import {MultipleCheckboxField} from 'app/components/forms';
+
+describe('MultipleCheckboxField', function() {
+  describe('render()', function() {
+    it('renders without form context', function() {
+      let wrapper = shallow(
+        <MultipleCheckboxField
+          name="fieldName"
+          choices={[['1', 'On'], ['2', 'Off']]}
+          value={['1']}
+        />
+      );
+      expect(toJson(wrapper)).toMatchSnapshot();
+    });
+
+    it('renders with form context', function() {
+      let wrapper = shallow(
+        <MultipleCheckboxField name="fieldName" choices={[['1', 'On'], ['2', 'Off']]} />,
+        {
+          context: {
+            form: {
+              data: {
+                fieldName: ['1']
+              },
+              errors: {}
+            }
+          }
+        }
+      );
+      expect(toJson(wrapper)).toMatchSnapshot();
+    });
+  });
+});