Browse Source

Fix missing keys / cleanup React warnings

Ben Vinegar 9 years ago
parent
commit
5aea9adc94

+ 1 - 0
.eslintrc

@@ -123,6 +123,7 @@
      */
     "react/display-name": 0,         // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md
     "react/no-multi-comp": [1, {"ignoreStateless": true}], // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md
+    "react/jsx-key": 2,              // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-key.md
     "react/jsx-no-undef": 2,         // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md
     "react/jsx-no-duplicate-props": 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-duplicate-props.md
     "react/jsx-uses-react": 2,       // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md

+ 1 - 1
package.json

@@ -12,7 +12,7 @@
     "crypto-js": "3.1.5",
     "css-loader": "0.18.0",
     "eslint": "1.9.0",
-    "eslint-plugin-react": "3.8.0",
+    "eslint-plugin-react": "3.11.2",
     "extract-text-webpack-plugin": "0.8.2",
     "file-loader": "0.8.4",
     "history": "1.13.0",

+ 3 - 3
src/sentry/static/sentry/app/components/contextData.jsx

@@ -110,7 +110,7 @@ const ContextData = React.createClass({
       } else if (typeof value === 'string' || value instanceof String) {
         let valueInfo = analyzeStringForRepr(value);
 
-        let out = [<span className={
+        let out = [<span key="value" className={
             (valueInfo.isString ? 'val-string' : 'val-repr') +
             (valueInfo.isStripped ? ' val-stripped' : '') +
             (valueInfo.isMultiLine ? ' val-string-multiline' : '')}>{
@@ -118,7 +118,7 @@ const ContextData = React.createClass({
 
         if (valueInfo.isString && isUrl(value)) {
           out.push(
-            <a href={value} className="external-icon">
+            <a key="external" href={value} className="external-icon">
               <em className="icon-open" />
             </a>
           );
@@ -201,4 +201,4 @@ const ContextData = React.createClass({
   }
 });
 
-export default ContextData;
+export default ContextData;

+ 2 - 2
src/sentry/static/sentry/app/components/events/interfaces/keyValueList.jsx

@@ -27,14 +27,14 @@ const KeyValueList = React.createClass({
         {data.map(([key, value]) => {
           if (this.props.isContextData) {
             return [
-              <tr>
+              <tr key={key}>
                 <td className="key">{key}</td>
                 <td className="value"><ContextData data={value}/></td>
               </tr>
             ];
           } else {
             return [
-              <tr>
+              <tr key={key}>
                 <td className="key">{key}</td>
                 <td className="value"><pre>{'' + value || ' '}</pre></td>
               </tr>

+ 3 - 3
src/sentry/static/sentry/app/components/events/interfaces/request.jsx

@@ -62,7 +62,7 @@ const RequestInterface = React.createClass({
 
     if (!this.isPartial()) {
       children.push(
-        <div className="pull-right">
+        <div key="action-buttons" className="pull-right">
           {!this.props.isShare &&
             <RequestActions organization={this.context.organization}
                             project={this.context.project}
@@ -70,7 +70,7 @@ const RequestInterface = React.createClass({
                             event={evt} />
           }
         </div>,
-        <div className="btn-group">
+        <div key="view-buttons" className="btn-group">
           <a className={(view === 'rich' ? 'active' : '') + ' btn btn-default btn-sm'}
             onClick={this.toggleView.bind(this, 'rich')}>{
               /* Translators: this means "rich" rendering (fancy tables) */
@@ -82,7 +82,7 @@ const RequestInterface = React.createClass({
     }
 
     children.push(
-      <h3>
+      <h3 key="title">
         <strong>{data.method || 'GET'} <a href={fullUrl}>{parsedUrl.pathname}</a></strong>
         <small style={{marginLeft: 20}}>{parsedUrl.hostname}</small>
       </h3>

+ 2 - 2
src/sentry/static/sentry/app/views/groupActivity/index.jsx

@@ -117,7 +117,7 @@ const GroupActivity = React.createClass({
             <TimeSince date={item.dateCreated} />
             <div className="activity-item-content">
               {this.formatActivity(
-                <span>
+                <span key={`${itemIdx}-author`}>
                   {author.avatar}
                   <span className="activity-author">{author.name}</span>
                 </span>,
@@ -135,7 +135,7 @@ const GroupActivity = React.createClass({
         <div className="col-md-9">
           <div className="activity-container">
             <ul className="activity">
-              <li className="activity-note">
+              <li className="activity-note" key="activity-note">
                 <Gravatar email={me.email} size={64} className="avatar" />
                 <div className="activity-bubble">
                   <NoteInput group={group} />

+ 28 - 24
src/sentry/static/sentry/app/views/groupDetails/actions.jsx

@@ -114,30 +114,34 @@ const GroupActions = React.createClass({
               </a>
             )
           :
-            [<a className={resolveClassName}
-               title={t('Resolve')}
-               onClick={this.onUpdate.bind(this, {status: 'resolved'})}>
-              Resolve
-            </a>,
-            <DropdownLink
-              caret={true}
-              className={resolveClassName}
-              topLevelClasses={resolveDropdownClasses}
-              title="">
-              <MenuItem noAnchor={true}>
-                {hasRelease ?
-                  <a onClick={this.onUpdate.bind(this, {status: 'resolvedInNextRelease'})}>
-                    <strong>{t('Resolved in next release')}</strong>
-                    <div className="help-text">{t('Snooze notifications until this issue reoccurs in a future release.')}</div>
-                  </a>
-                :
-                  <a className="disabled tip" title="You need to send release data to Sentry in order to use this feature.">
-                    <strong>{t('Resolved in next release.')}</strong>
-                    <div className="help-text">{t('Snooze notifications until this issue reoccurs in a future release.')}</div>
-                  </a>
-                }
-              </MenuItem>
-            </DropdownLink>]
+            [
+              <a key="resolve-button"
+                 className={resolveClassName}
+                 title={t('Resolve')}
+                 onClick={this.onUpdate.bind(this, {status: 'resolved'})}>
+                Resolve
+              </a>,
+              <DropdownLink
+                key="resolve-dropdown"
+                caret={true}
+                className={resolveClassName}
+                topLevelClasses={resolveDropdownClasses}
+                title="">
+                <MenuItem noAnchor={true}>
+                  {hasRelease ?
+                    <a onClick={this.onUpdate.bind(this, {status: 'resolvedInNextRelease'})}>
+                      <strong>{t('Resolved in next release')}</strong>
+                      <div className="help-text">{t('Snooze notifications until this issue reoccurs in a future release.')}</div>
+                    </a>
+                  :
+                    <a className="disabled tip" title="You need to send release data to Sentry in order to use this feature.">
+                      <strong>{t('Resolved in next release.')}</strong>
+                      <div className="help-text">{t('Snooze notifications until this issue reoccurs in a future release.')}</div>
+                    </a>
+                  }
+                </MenuItem>
+              </DropdownLink>
+            ]
           }
         </div>
         <div className="btn-group">