This commit is contained in:
Stuart Colville 2016-08-24 14:55:15 +01:00
Родитель eb6a1fc9db
Коммит 4b373d38f8
34 изменённых файлов: 103 добавлений и 37 удалений

Просмотреть файл

@ -61,7 +61,13 @@
"react/prefer-stateless-function": "off",
"react/jsx-indent-props": "off",
"react/jsx-closing-bracket-location": "off",
"react/jsx-first-prop-new-line": "off"
"react/jsx-first-prop-new-line": "off",
// FIXME: Use funcs for refs https://github.com/mozilla/addons-frontend/issues/970
"react/no-string-refs": "off",
// FIXME: Deprecate use of findDOMNode https://github.com/mozilla/addons-frontend/issues/968
"react/no-find-dom-node": "off",
// FIXME: Switch using .jsx for jsx files https://github.com/mozilla/addons-frontend/issues/969.
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }]
},
"settings": {
"import/ignore": [

Просмотреть файл

@ -196,7 +196,7 @@
"eslint-config-airbnb": "10.0.1",
"eslint-plugin-import": "1.14.0",
"eslint-plugin-jsx-a11y": "2.1.0",
"eslint-plugin-react": "5.2.2",
"eslint-plugin-react": "6.1.2",
"file-loader": "0.9.0",
"glob": "7.0.5",
"json-loader": "0.5.4",

Просмотреть файл

@ -61,11 +61,11 @@ class AddonDetail extends React.Component {
<LikeButton />
</div>
<div className="title">
<h1 dangerouslySetInnerHTML={sanitizeHTML(title, ['a', 'span'])}></h1>
<h1 dangerouslySetInnerHTML={sanitizeHTML(title, ['a', 'span'])} />
<InstallButton slug={addon.slug} />
</div>
<div className="description">
<p dangerouslySetInnerHTML={sanitizeHTML(addon.summary)}></p>
<p dangerouslySetInnerHTML={sanitizeHTML(addon.summary)} />
</div>
</header>
@ -86,8 +86,7 @@ class AddonDetail extends React.Component {
<section className="about">
<h2>{i18n.gettext('About this extension')}</h2>
<div dangerouslySetInnerHTML={sanitizeHTML(nl2br(addon.description),
allowedDescriptionTags)}>
</div>
allowedDescriptionTags)} />
</section>
</div>
);

Просмотреть файл

@ -1,3 +1,5 @@
/* eslint-disable jsx-a11y/href-no-hash */
import React, { PropTypes } from 'react';
import translate from 'core/i18n/translate';

Просмотреть файл

@ -1,3 +1,5 @@
/* global fetch */
import url from 'url';
import 'isomorphic-fetch';

Просмотреть файл

@ -1 +1,2 @@
/* global window */
export default window;

Просмотреть файл

@ -1,3 +1,5 @@
/* global document */
import 'babel-polyfill';
import config from 'config';
import Jed from 'jed';

Просмотреть файл

@ -9,6 +9,7 @@ export class ClientConfig {
constructor(objData) {
// This Object.assign keeps the config data private.
Object.assign(this, {
// eslint-disable-next-line no-prototype-builtins
has: (key) => objData.hasOwnProperty(key),
get: (key) => {

Просмотреть файл

@ -1,4 +1,6 @@
/* eslint-disable no-console */
/* global window */
import config from 'config';
/*

Просмотреть файл

@ -114,7 +114,7 @@ export class InstallButtonBase extends React.Component {
ref="themeData"
type="checkbox" />
<label htmlFor={identifier}>
{isDownloading ? <div className="progress"></div> : null}
{isDownloading ? <div className="progress" /> : null}
<span className="visually-hidden">{this.getLabel()}</span>
</label>
</div>

Просмотреть файл

@ -43,10 +43,10 @@ export default class Paginate extends React.Component {
return makePageNumbers({ start: 1, end: pageCount });
// If we are showing less on the right than we should, define the start by the end.
} else if (end - currentPage < showExtra) {
return makePageNumbers({ start: end - showPages + 1, end });
return makePageNumbers({ start: (end - showPages) + 1, end });
// If we are showing less on the left than we should, define the end by the start.
} else if (currentPage - start < showExtra) {
return makePageNumbers({ start, end: start + showPages - 1 });
return makePageNumbers({ start, end: (start + showPages) - 1 });
}
// We're showing the maximum number of pages on each side, start and end are correct.
return makePageNumbers({ start, end });

Просмотреть файл

@ -45,7 +45,7 @@ export default class ServerHtml extends Component {
key={type + index}
rel="stylesheet" type="text/css" />);
case 'js':
return <script key={type + index} src={filePath} {...sriProps}></script>;
return <script key={type + index} src={filePath} {...sriProps} />;
default:
throw new Error('Unknown static type');
}
@ -56,7 +56,7 @@ export default class ServerHtml extends Component {
getAnalytics() {
if (this.props.trackingEnabled) {
return <script async src="https://www.google-analytics.com/analytics.js"></script>;
return <script async src="https://www.google-analytics.com/analytics.js" />;
}
return null;
}

Просмотреть файл

@ -73,6 +73,7 @@ export function sanitizeLanguage(langOrLocale) {
let language = normalizeLang(langOrLocale);
// Only look in the un-mapped lang list.
if (!isValidLang(language)) {
// eslint-disable-next-line no-prototype-builtins
language = langMap.hasOwnProperty(language) ? langMap[language] : defaultLang;
}
return language;

Просмотреть файл

@ -1,3 +1,5 @@
/* eslint-disable import/prefer-default-export */
import config from 'config';
import { getClientApp, isValidClientApp } from 'core/utils';

Просмотреть файл

@ -7,9 +7,9 @@ export default purify;
purify.addHook('afterSanitizeAttributes', (node) => {
// Set all elements owning target to target=_blank
// and add rel="noreferrer".
// and add rel="noopener noreferrer".
if ('target' in node) {
node.setAttribute('target', '_blank');
node.setAttribute('rel', 'noreferrer');
node.setAttribute('rel', 'noopener noreferrer');
}
});

Просмотреть файл

@ -23,15 +23,13 @@ export default function addon(state = initialState, action) {
type: THEME_TYPE,
};
delete newState[key].theme_data;
} else if (thisAddon.current_version && thisAddon.current_version.files.length > 0) {
newState[key] = {
...thisAddon,
installURL: thisAddon.current_version.files[0].url,
};
} else {
if (thisAddon.current_version && thisAddon.current_version.files.length > 0) {
newState[key] = {
...thisAddon,
installURL: thisAddon.current_version.files[0].url,
};
} else {
newState[key] = thisAddon;
}
newState[key] = thisAddon;
}
});
return newState;

Просмотреть файл

@ -1,3 +1,6 @@
/* global window */
/* eslint-disable import/prefer-default-export */
import { applyMiddleware, compose } from 'redux';
import createLogger from 'redux-logger';
import config from 'config';

Просмотреть файл

@ -1,4 +1,6 @@
/* global window */
/* eslint-disable no-underscore-dangle */
import config from 'config';
import { convertBoolean } from 'core/utils';

Просмотреть файл

@ -1,3 +1,5 @@
/* eslint-disable import/prefer-default-export */
export function discoResults(results) {
return {
type: 'DISCO_RESULTS',

Просмотреть файл

@ -1,3 +1,5 @@
/* global window */
import log from 'core/logger';
import {
globalEvents,
@ -59,6 +61,7 @@ export function addChangeListeners(callback, mozAddonManager) {
function handleChangeEvent(e) {
const { id, type, needsRestart } = e;
log.info('Event received', { type, id, needsRestart });
// eslint-disable-next-line no-prototype-builtins
if (globalEventStatusMap.hasOwnProperty(type)) {
return callback({ guid: id, status: globalEventStatusMap[type], needsRestart });
}

Просмотреть файл

@ -1,3 +1,5 @@
/* global document, CustomEvent */
import classNames from 'classnames';
import { sprintf } from 'jed';
import React, { PropTypes } from 'react';
@ -98,6 +100,7 @@ export class AddonBase extends React.Component {
return status === ERROR ? (<div className="notification error" key="error-overlay">
<p className="message">{this.errorMessage()}</p>
{error && !error.startsWith('FATAL') ?
// eslint-disable-next-line jsx-a11y/href-no-hash
<a className="close" href="#" onClick={this.closeError}>{i18n.gettext('Close')}</a> : null}
</div>) : null;
}
@ -119,6 +122,7 @@ export class AddonBase extends React.Component {
getThemeImage() {
const { i18n, name, previewURL } = this.props;
if (this.props.type === THEME_TYPE) {
// eslint-disable-next-line jsx-a11y/href-no-hash
return (<a href="#" className="theme-image"
data-browsertheme={this.getBrowserThemeData()}
onBlur={this.resetPreviewTheme}
@ -267,7 +271,7 @@ export function makeProgressHandler(dispatch, guid) {
return (addonInstall, e) => {
if (addonInstall.state === 'STATE_DOWNLOADING') {
const downloadProgress = parseInt(
100 * addonInstall.progress / addonInstall.maxProgress, 10);
(100 * addonInstall.progress) / addonInstall.maxProgress, 10);
dispatch({ type: DOWNLOAD_PROGRESS, payload: { guid, downloadProgress } });
} else if (e.type === 'onDownloadFailed') {
dispatch({ type: INSTALL_ERROR, payload: { guid, error: DOWNLOAD_FAILED } });

Просмотреть файл

@ -1,4 +1,5 @@
/* eslint-disable max-len */
/* global navigator */
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { asyncConnect } from 'redux-async-connect';
@ -118,7 +119,7 @@ export class DiscoPaneBase extends React.Component {
{results.map((item) => <AddonComponent {...camelCaseProps(item)} key={item.guid} />)}
<div className="amo-link">
<a href="https://addons.mozilla.org/" target="_blank"
rel="noreferrer" onClick={this.showMoreAddons}>
rel="noopener noreferrer" onClick={this.showMoreAddons}>
{i18n.gettext('See more add-ons!')}
</a>
</div>

Просмотреть файл

@ -1,3 +1,5 @@
/* global document */
import { validThemeActions } from 'core/constants';
export default function themeAction(node, action, _doc = document) {

Просмотреть файл

@ -9,7 +9,7 @@ import JsonData from 'search/components/JsonData';
import './style.scss';
function siteLink(url, text) {
return <a href={url} target="_blank">{text}</a>;
return <a href={url} rel="noopener noreferrer" target="_blank">{text}</a>;
}
class AddonPage extends React.Component {
@ -32,7 +32,8 @@ class AddonPage extends React.Component {
];
if (addon.homepage) {
items.push([
<a href={addon.homepage} rel="external" target="_blank">{_('View homepage')}</a>,
<a href={addon.homepage}
rel="external noopener noreferrer" target="_blank">{_('View homepage')}</a>,
'homepage',
]);
}
@ -44,7 +45,8 @@ class AddonPage extends React.Component {
}
if (addon.support_url) {
items.push([
<a href={addon.support_url} rel="external" target="_blank">{_('View support site')}</a>,
<a href={addon.support_url}
rel="external noopener noreferrer" target="_blank">{_('View support site')}</a>,
'support_url',
]);
}

Просмотреть файл

@ -1,3 +1,4 @@
import React from 'react';
import { findDOMNode } from 'react-dom';
import {
@ -12,6 +13,7 @@ import InstallButton from 'core/components/InstallButton';
import { getFakeI18nInst } from 'tests/client/helpers';
// eslint-disable-next-line import/prefer-default-export
export const fakeAddon = {
name: 'Chill Out',
slug: 'chill-out',

Просмотреть файл

@ -1,3 +1,5 @@
/* global window */
import * as api from 'core/api';
import { unexpectedSuccess } from 'tests/client/helpers';

Просмотреть файл

@ -1,3 +1,5 @@
/* global document, Node */
import React from 'react';
import { render } from 'react-dom';
import { renderIntoDocument } from 'react-addons-test-utils';

Просмотреть файл

@ -1,3 +1,5 @@
/* global window */
import { Tracking, getAction } from 'core/tracking';
import { EXTENSION_TYPE, THEME_TYPE } from 'core/constants';

Просмотреть файл

@ -210,7 +210,7 @@ describe('<Addon />', () => {
heading: 'This is <span>an <a href="https://addons.mozilla.org">add-on</a>/span>',
});
const link = root.refs.heading.querySelector('a');
assert.equal(link.getAttribute('rel'), 'noreferrer');
assert.equal(link.getAttribute('rel'), 'noopener noreferrer');
assert.equal(link.getAttribute('target'), '_blank');
});
@ -329,8 +329,13 @@ describe('<Addon />', () => {
const preventDefault = sinon.stub();
Simulate.click(themeImage, { preventDefault });
const installTheme = sinon.stub();
const data = { ...result, type: THEME_TYPE, themeAction,
status: UNINSTALLED, installTheme };
const data = {
...result,
type: THEME_TYPE,
themeAction,
status: UNINSTALLED,
installTheme,
};
root = renderAddon(data);
themeImage = findDOMNode(root).querySelector('.theme-image');
Simulate.click(themeImage, { currentTarget: themeImage, preventDefault });
@ -342,8 +347,13 @@ describe('<Addon />', () => {
const preventDefault = sinon.stub();
Simulate.click(themeImage, { preventDefault });
const installTheme = sinon.stub();
const data = { ...result, type: THEME_TYPE, themeAction,
status: DISABLED, installTheme };
const data = {
...result,
type: THEME_TYPE,
themeAction,
status: DISABLED,
installTheme,
};
root = renderAddon(data);
themeImage = findDOMNode(root).querySelector('.theme-image');
Simulate.click(themeImage, { currentTarget: themeImage, preventDefault });
@ -354,8 +364,13 @@ describe('<Addon />', () => {
it('does not try to install theme if not UNINSTALLED', () => {
const preventDefault = sinon.stub();
const installTheme = sinon.stub();
const data = { ...result, type: THEME_TYPE, themeAction,
status: INSTALLED, installTheme };
const data = {
...result,
type: THEME_TYPE,
themeAction,
status: INSTALLED,
installTheme,
};
root = renderAddon(data);
themeImage = findDOMNode(root).querySelector('.theme-image');
Simulate.click(themeImage, { currentTarget: themeImage, preventDefault });

Просмотреть файл

@ -1,3 +1,5 @@
/* global document */
import React from 'react';
import { Simulate, renderIntoDocument } from 'react-addons-test-utils';
import ReactDOM, { findDOMNode } from 'react-dom';
@ -88,15 +90,15 @@ describe('Clicking outside <InfoDialog />', () => {
<I18nProvider i18n={getFakeI18nInst()}>
<div>
{getInfoDialog()}
<div ref="outsideComponent" onClick={(e) => e.stopPropagation()} />
<div id="outside-component" onClick={(e) => e.stopPropagation()} />
</div>
</I18nProvider>
);
}
}
const rootComponent = ReactDOM.render(<FakeContainer />, mountNode);
const outsideNode = rootComponent.refs.outsideComponent;
ReactDOM.render(<FakeContainer />, mountNode);
const outsideNode = document.getElementById('outside-component');
simulateClick(outsideNode);
assert.ok(closeAction.called, 'closeAction stub was called');
});

Просмотреть файл

@ -55,6 +55,6 @@ export function getFakeI18nInst() {
export class MockedSubComponent extends React.Component {
render() {
return <div></div>;
return <div />;
}
}

Просмотреть файл

@ -1,3 +1,5 @@
/* global window */
const realSinon = sinon;
window.sinon = realSinon.sandbox.create();
window.sinon.createStubInstance = realSinon.createStubInstance;

Просмотреть файл

@ -1,3 +1,5 @@
/* eslint-disable no-continue */
import fs from 'fs';
import path from 'path';

Просмотреть файл

@ -1,3 +1,5 @@
/* eslint-disable import/prefer-default-export */
import cheerio from 'cheerio';
import { assert } from 'chai';