Set `tabWidth: 2` in Prettier config (#2438)

* chore: Update Prettier config, setting tabWidth:2

* style: Apply updated Prettier styles

If you need to rebase work past this style change, do as follows:

0. Consider this to be commit `commitA`, replacing that with its id in the following.
1. To make sure mistakes aren't fatal, assign a second branch to your current work.
2. Rebase your branch on the commit immediately before this one, commitA~
3. Run the following command at the root of the repo:

    git rebase --strategy-option=theirs \
      --exec 'npx prettier --write . && git add -u && git commit --amend --no-edit' \
      commitA

That will take a short while esp. if you have multiple commits,
as it runs Prettier on everything for every commit.
If you've deleted files, the rebase may drop down to interactive mode
and have you `git rm` as appropriate, then `git rebase --continue`.

You should end up with just your changes in your branch,
prettily formatted. To validate that,
apply the same Prettier config change to your original branch,
reformat the files with `npm run prettier`,
and then compare the results with the rebased branch.

* chore: Clean up lint configs
This commit is contained in:
Eemeli Aro 2022-03-03 02:46:35 -06:00 коммит произвёл GitHub
Родитель 9fc9430cc7
Коммит 9faea69c20
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
528 изменённых файлов: 30406 добавлений и 31367 удалений

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

@ -1,16 +1,23 @@
**/*.bundle.js
**/*.js.map
**/dist/**
**/build/**
vendor/**
coverage/**
static/*
**/*.min.js
**/js/lib/**/*.js
**/app/error_pages/**/*.js
**/*blockrain*js
assets/*
**/node_modules/**
docs/
.vscode/
tag-admin/dist/
translate/dist/
coverage/
docs/_build/
docs/venv/
package-lock.json
specs/
# Jinja templates
pontoon/base/templates/js/pontoon.js
translate/public/translate.html
**/templates/**/*.html
# Vendored code
error_pages/css/blockrain.css
error_pages/js/
pontoon/base/static/css/boilerplate.css
pontoon/base/static/css/fontawesome-all.css
pontoon/base/static/css/jquery-ui.css
pontoon/base/static/css/nprogress.css
pontoon/base/static/js/lib/
pontoon/in_context/static/

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

@ -1,19 +1,18 @@
/* eslint-env node */
module.exports = {
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
extends: ['eslint:recommended', 'plugin:react/recommended'],
env: {
es6: true,
browser: true,
jest: true,
},
parser: "@babel/eslint-parser",
parser: '@babel/eslint-parser',
parserOptions: {
ecmaVersion: 2017,
ecmaFeatures: {
jsx: true,
experimentalObjectRestSpread: true
experimentalObjectRestSpread: true,
},
sourceType: 'module',
babelOptions: {
@ -56,23 +55,24 @@ module.exports = {
traversalShortcutsHandler: true,
editorShortcutsHandler: true,
},
plugins: [
'react',
],
plugins: ['react'],
rules: {
'react/display-name': 0,
'react/prefer-es6-class': 1,
'react/prefer-stateless-function': 0,
"react/prop-types": 0,
"react/jsx-key": 0,
"react/jsx-uses-react": 1,
'react/prop-types': 0,
'react/jsx-key': 0,
'react/jsx-uses-react': 1,
'react/jsx-uses-vars': 1,
"no-unused-vars": ["error", { "vars": "all", "args": "after-used", "ignoreRestSiblings": true }],
"no-console": 1,
'no-unused-vars': [
'error',
{ vars: 'all', args: 'after-used', ignoreRestSiblings: true },
],
'no-console': 1,
},
settings: {
'react': {
'version': 'detect'
}
}
react: {
version: 'detect',
},
},
};

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

@ -1,21 +1,23 @@
# minified code
*.min.js
*.min.css
translate/dist
tag-admin/dist
.vscode/
tag-admin/dist/
translate/dist/
coverage/
docs/_build/
docs/venv/
package-lock.json
specs/
# libraries
**/base/static/js/lib*
**/base/static/css/boilerplate.css
**/base/static/css/fontawesome-all.css
**/base/static/css/jquery-ui.css
**/base/static/css/nprogress.css
**/base/templates/js/pontoon.js
**/in_context/static/css/agency.css
**/in_context/static/js/agency.js
# Jinja templates
pontoon/base/templates/js/pontoon.js
translate/public/translate.html
**/templates/**/*.html
# Prevent VSCode to reformat these files if "Format On Save" enabled
*.html
*.yml
**/package.json*
# Vendored code
error_pages/css/blockrain.css
error_pages/js/
pontoon/base/static/css/boilerplate.css
pontoon/base/static/css/fontawesome-all.css
pontoon/base/static/css/jquery-ui.css
pontoon/base/static/css/nprogress.css
pontoon/base/static/js/lib/
pontoon/in_context/static/

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

@ -1,4 +1,4 @@
schedule: "every week on monday"
schedule: 'every week on monday'
search: False
update: insecure
requirements:

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

@ -5,6 +5,7 @@ For more details, please read the
[Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).
## How to Report
For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page.
<!--

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

@ -122,13 +122,13 @@ pyupgrade:
"${DC}" run --rm server pyupgrade --exit-zero-even-if-changed --py38-plus *.py `find pontoon -name \*.py`
check-pyupgrade:
"${DC}" run --rm webapp pyupgrade --py38-plus *.py `find pontoon -name \*.py`
"${DC}" run --rm server pyupgrade --py38-plus *.py `find pontoon -name \*.py`
black:
"${DC}" run --rm server black pontoon/
check-black:
"${DC}" run --rm webapp black --check pontoon
"${DC}" run --rm server black --check pontoon
dropdb:
"${DC}" down --volumes postgresql

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

@ -7,7 +7,6 @@ uses version-control systems for storing translations.
[📚 **Documentation**](https://mozilla-pontoon.readthedocs.io/)
## Installing Pontoon
If you are looking to host your own instance of Pontoon, there are several ways to do so.
@ -27,7 +26,6 @@ testing for example, see our
[Developer Setup using Docker](https://mozilla-pontoon.readthedocs.io/en/latest/dev/setup.html).
Please note that you should **not** deploy a production instance with Docker.
## Contributing to Pontoon
Do you want to help us make Pontoon better? We are very glad!
@ -39,14 +37,14 @@ database, run tests, and send your contribution.
If you want to go further, you can:
* Check out development roadmap on the [wiki](https://wiki.mozilla.org/Pontoon)
* Report an [issue](https://github.com/mozilla/pontoon/issues/new)
* Check [existing issues](https://github.com/mozilla/pontoon/issues)
* See Mozilla's Pontoon servers:
* [Staging](https://mozilla-pontoon-staging.herokuapp.com/)
* [Production](https://pontoon.mozilla.org/)
* For discussing Pontoon's development, get in touch with us on [chat.mozilla.org](https://chat.mozilla.org/#/room/#pontoon:mozilla.org)
* For feedback, support, and 3rd party deployments, check out [Discourse](https://discourse.mozilla.org/c/pontoon/)
- Check out development roadmap on the [wiki](https://wiki.mozilla.org/Pontoon)
- Report an [issue](https://github.com/mozilla/pontoon/issues/new)
- Check [existing issues](https://github.com/mozilla/pontoon/issues)
- See Mozilla's Pontoon servers:
- [Staging](https://mozilla-pontoon-staging.herokuapp.com/)
- [Production](https://pontoon.mozilla.org/)
- For discussing Pontoon's development, get in touch with us on [chat.mozilla.org](https://chat.mozilla.org/#/room/#pontoon:mozilla.org)
- For feedback, support, and 3rd party deployments, check out [Discourse](https://discourse.mozilla.org/c/pontoon/)
## License
@ -54,11 +52,10 @@ This software is licensed under the
[New BSD License](https://creativecommons.org/licenses/BSD/). For more
information, read [LICENSE](https://github.com/mozilla/pontoon/blob/master/LICENSE).
## Screenshots
![](docs/img/screenshots/teams-dashboard.png)
*Teams dashboard*
_Teams dashboard_
![](docs/img/screenshots/translation-app.png)
*Translation app*
_Translation app_

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

@ -1,7 +1,14 @@
{
"name": "pontoon",
"description": "In-place localization tool.",
"keywords": ["l10n", "localization", "mozilla", "collaboration", "python", "django"],
"keywords": [
"l10n",
"localization",
"mozilla",
"collaboration",
"python",
"django"
],
"website": "https://pontoon.mozilla.org",
"logo": "https://pontoon.mozilla.org/static/img/logo.svg",
"success_url": "/",

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

@ -1,9 +1,4 @@
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-transform-runtime"
]
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": ["@babel/plugin-transform-runtime"]
}

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

@ -9,10 +9,7 @@
"home": "https://wiki.mozilla.org/Webdev/GetInvolved/pontoon.mozilla.org",
"docs": "https://mozilla-pontoon.readthedocs.io/",
"chat": "https://chat.mozilla.org/#/room/#pontoon:mozilla.org",
"chat-contacts": [
"mathjazz",
"eemeli"
],
"chat-contacts": ["mathjazz", "eemeli"],
"mailing-list": "https://discourse.mozilla.org/c/pontoon"
},
"bugs": {

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

@ -1,7 +1,7 @@
# docker-compose for Pontoon development.
#
# Note: Requires docker-compose 1.10+.
version: "2.3"
version: '2.3'
services:
server:
build:
@ -14,7 +14,7 @@ services:
depends_on:
- postgresql
ports:
- "8000:8000"
- '8000:8000'
volumes:
- ./pontoon:/app/pontoon
- ./requirements:/app/requirements

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

@ -166,27 +166,27 @@ spec:
cpu: 0.5
env:
- name: SECRET_KEY
value: "<the pontoon secret key>"
value: '<the pontoon secret key>'
- name: DJANGO_LOGIN
value: "true"
value: 'true'
- name: DJANGO_DEV
value: "false"
value: 'false'
- name: DJANGO_DEBUG
value: "true"
value: 'true'
- name: CI
value: "true"
value: 'true'
- name: DATABASE_URL
value: "<pontoon database URL>"
value: '<pontoon database URL>'
- name: ALLOWED_HOSTS
value: "<comma seperated list of allowd hosts>"
value: '<comma seperated list of allowd hosts>'
- name: SITE_URL
value: "http://127.0.0.10"
value: 'http://127.0.0.10'
- name: SYNC_INTERVAL
value: "30"
value: '30'
- name: KNOWN_HOSTS
value: "<base64 encoded file content of .ssh/known_hosts>"
value: '<base64 encoded file content of .ssh/known_hosts>'
- name: SSH_KEY
value: "<base64 encoded ssh private key from .ssh/id_rsa>"
value: '<base64 encoded ssh private key from .ssh/id_rsa>'
imagePullSecrets:
- name: sec-dockerhub
---
@ -202,7 +202,7 @@ spec:
ports:
- name: https-web
protocol: TCP
#the port is not really used but mandatory
#the port is not really used but mandatory
port: 2049
targetPort: 8080
nodePort: <your assigned node port!>

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

@ -117,4 +117,3 @@ For a k8s deplyoment example yaml see the k8s-pontoon-example.yaml in this folde
* syncing all projects with writing changes to the soruce code `python manage.py sync_projects`.
This is done by shell script every 30 minutes (evn var SYNC_INTERVAL)
* to see if the sync works look at: http://127.0.0.1:8000/__sync/log/__

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

@ -1,4 +1,5 @@
# Pontoon Error Pages
[Custom Error Pages](https://devcenter.heroku.com/articles/error-pages#customize-pages) for Pontoon deployment on Heroku, featuring Tetris via [blockrain.js](http://aerolab.github.io/blockrain.js/)!
Must be hosted outside the main application, which will be down when these pages are displayed.

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

@ -1,18 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700" rel="stylesheet">
<link href='https://fonts.googleapis.com/css?family=Play:400,700' rel='stylesheet' type='text/css'>
<head>
<link
href="https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css?family=Play:400,700"
rel="stylesheet"
type="text/css"
/>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/blockrain.css">
<link rel="stylesheet" href="css/style.css" />
<link rel="stylesheet" href="css/blockrain.css" />
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
<title>Application Error</title>
</head>
</head>
<body>
<body>
<header>
<h1>Application Error</h1>
<h2>Please check back later. Or play some Tetris.</h2>
@ -35,7 +42,9 @@
</article>
<footer>
<a id="credits" href="https://github.com/Aerolab/blockrain.js">Powered by Blockrain.js</a>
<a id="credits" href="https://github.com/Aerolab/blockrain.js"
>Powered by Blockrain.js</a
>
</footer>
</section>
@ -47,8 +56,8 @@
<script>
var $demo = $('#tetris-demo').blockrain({
theme: 'pontoon',
playText: ''
playText: '',
});
</script>
</body>
</body>
</html>

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -1,18 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700" rel="stylesheet">
<link href='https://fonts.googleapis.com/css?family=Play:400,700' rel='stylesheet' type='text/css'>
<head>
<link
href="https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700"
rel="stylesheet"
/>
<link
href="https://fonts.googleapis.com/css?family=Play:400,700"
rel="stylesheet"
type="text/css"
/>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/blockrain.css">
<link rel="stylesheet" href="css/style.css" />
<link rel="stylesheet" href="css/blockrain.css" />
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
<title>Offline for maintenance</title>
</head>
</head>
<body>
<body>
<header>
<h1>Offline for maintenance</h1>
<h2>Please check back later. Or play some Tetris.</h2>
@ -35,7 +42,9 @@
</article>
<footer>
<a id="credits" href="https://github.com/Aerolab/blockrain.js">Powered by Blockrain.js</a>
<a id="credits" href="https://github.com/Aerolab/blockrain.js"
>Powered by Blockrain.js</a
>
</footer>
</section>
@ -47,8 +56,8 @@
<script>
var $demo = $('#tetris-demo').blockrain({
theme: 'pontoon',
playText: ''
playText: '',
});
</script>
</body>
</body>
</html>

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

@ -20,9 +20,9 @@
"build": "npm run build --workspaces --if-present",
"build:prod": "npm run build:prod --workspaces --if-present",
"heroku-postbuild": "echo Build is taken care of in ./bin/post_compile",
"prettier": "prettier --write '**/translate/**/*.{js,ts,tsx,css}' '**/pontoon/**/*.{js,css}' '**/tag-admin/**/*.{js,css}'",
"check-prettier": "prettier --check '**/translate/**/*.{js,ts,tsx,css}' '**/pontoon/**/*.{js,css}' '**/tag-admin/**/*.{js,css}'",
"eslint": "eslint 'translate/**/*.{js,ts,tsx}' 'pontoon/**/*.js' 'tag-admin/**/*.js'"
"prettier": "prettier --write .",
"check-prettier": "prettier --check .",
"eslint": "eslint ."
},
"workspaces": [
"translate",
@ -92,7 +92,7 @@
"prettier": {
"endOfLine": "lf",
"trailingComma": "all",
"tabWidth": 4,
"tabWidth": 2,
"jsxSingleQuote": true,
"singleQuote": true
},

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

@ -1,12 +1,11 @@
# GraphQL API
Pontoon exposes some of its data via a public API endpoint. The API is
[GraphQL](http://graphql.org/)-based and available at ``/graphql``.
[GraphQL](http://graphql.org/)-based and available at `/graphql`.
## Production Deployments
When run in production (``DEV is False``) the API returns ``application/json``
When run in production (`DEV is False`) the API returns `application/json`
responses to GET and POST requests. In case of GET requests, any whitespace in
the query must be escaped.
@ -22,14 +21,13 @@ An example POST requests may look like this:
$ curl -X POST -d "query={ projects { name } }" https://example.com/graphql
```
## Local Development
In a local development setup (``DEV is True``) the endpoint has two modes of
In a local development setup (`DEV is True`) the endpoint has two modes of
operation: a JSON one and an HTML one.
When a request is sent, without any headers, with ``Accept: application/json`` or
if it explicitly contains a ``raw`` query argument, the endpoint will behave like
When a request is sent, without any headers, with `Accept: application/json` or
if it explicitly contains a `raw` query argument, the endpoint will behave like
a production one, returning JSON responses.
The following query in the CLI will return a JSON response:
@ -38,22 +36,21 @@ The following query in the CLI will return a JSON response:
$ curl --globoff http://localhost:8000/graphql?query={projects{name}}
```
If however a request is sent with ``Accept: text/html`` such as is the case when
If however a request is sent with `Accept: text/html` such as is the case when
accessing the endpoint in a browser, a GUI query editor and explorer,
[GraphiQL](https://github.com/graphql/graphiql), will be served::
http://localhost:8000/graphql?query={projects{name}}
To preview the JSON response in the browser, pass in the ``raw`` query argument::
To preview the JSON response in the browser, pass in the `raw` query argument::
http://localhost:8000/graphql?query={projects{name}}&raw
## Query IDE
The [GraphiQL](https://github.com/graphql/graphiql) query IDE is available at
``http://localhost:8000/graphql`` when running Pontoon locally and the URL is
accessed with the ``Accept: text/html`` header, e.g. using a browser.
`http://localhost:8000/graphql` when running Pontoon locally and the URL is
accessed with the `Accept: text/html` header, e.g. using a browser.
It offers a query editor with:
@ -62,4 +59,4 @@ It offers a query editor with:
- real-time error reporting,
- results folding,
- autogenerated docs on shapes and their fields,
- [introspection](http://docs.graphene-python.org/projects/django/en/latest/debug/) via the ``__debug``.
- [introspection](http://docs.graphene-python.org/projects/django/en/latest/debug/) via the `__debug`.

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

@ -51,9 +51,7 @@ var Pontoon = (function (my) {
1000,
function () {
// Remove inline style and unread mark to make hover work again
unreadNotifications
.removeAttr('style')
.removeAttr('data-unread');
unreadNotifications.removeAttr('style').removeAttr('data-unread');
},
);
},
@ -286,10 +284,7 @@ $(function () {
var $this = $(this);
var loginUrl = $this.prop('href'),
startSign = loginUrl.match(/\?/) ? '&' : '?';
$this.prop(
'href',
loginUrl + startSign + 'next=' + getRedirectUrl(),
);
$this.prop('href', loginUrl + startSign + 'next=' + getRedirectUrl());
},
);
@ -385,16 +380,12 @@ $(function () {
// General keyboard shortcuts
generalShortcutsHandler = function (e) {
const mediaQuery = window.matchMedia(
'(prefers-reduced-motion: reduce)',
);
const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)');
function moveMenu(type) {
var options =
type === 'up' ? ['first', 'last', -1] : ['last', 'first', 1];
var items = menu.find(
'li:visible:not(.horizontal-separator, :has(li))',
);
var items = menu.find('li:visible:not(.horizontal-separator, :has(li))');
var element = null;
if (

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

@ -11,9 +11,7 @@ $(function () {
.siblings('.legend')
.find('li')
.each(function () {
stats[$(this).attr('class')] = $(this)
.find('.value')
.data('value');
stats[$(this).attr('class')] = $(this).find('.value').data('value');
});
stats.all = progress
@ -30,9 +28,7 @@ $(function () {
? stats.missing / stats.all
: 1 /* Draw "empty" progress if no projects enabled */,
},
number = Math.floor(
(fraction.translated + fraction.warnings) * 100,
);
number = Math.floor((fraction.translated + fraction.warnings) * 100);
// Update graph
var canvas = this,

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

@ -32,12 +32,8 @@ $('body')
action = $element.data('action'),
name = $element.data('user-name'),
link = $element.data('user-link'),
date = date_formatter.format(
new Date($element.attr('datetime')),
),
time = time_formatter.format(
new Date($element.attr('datetime')),
);
date = date_formatter.format(new Date($element.attr('datetime'))),
time = time_formatter.format(new Date($element.attr('datetime')));
$element.after(
'<aside class="tooltip">' +
@ -87,10 +83,7 @@ var Pontoon = (function (my) {
* TODO: remove old search code from main.js
*/
filter: (function () {
$('body').on(
'input.filter',
'input.table-filter',
function (e) {
$('body').on('input.filter', 'input.table-filter', function (e) {
if (e.which === 9) {
return;
}
@ -118,8 +111,7 @@ var Pontoon = (function (my) {
)
.parents(item)
.show();
},
);
});
})(),
/*
@ -131,12 +123,8 @@ var Pontoon = (function (my) {
var legend = $(el).find('.progress .legend'),
all = legend.find('.all .value').data('value') || 0,
translated =
legend
.find('.translated .value')
.data('value') / all || 0,
fuzzy =
legend.find('.fuzzy .value').data('value') /
all || 0;
legend.find('.translated .value').data('value') / all || 0,
fuzzy = legend.find('.fuzzy .value').data('value') / all || 0;
if ($(el).find('.progress .not-ready').length) {
return 'not-ready';
@ -174,9 +162,7 @@ var Pontoon = (function (my) {
}
function getNumber(el) {
return parseInt(
$(el).find('span').text().replace(/,/g, ''),
);
return parseInt($(el).find('span').text().replace(/,/g, ''));
}
function getString(el) {
@ -232,14 +218,8 @@ var Pontoon = (function (my) {
var timeA = getTime(a),
timeB = getTime(b);
if (
timeA === defaultTime &&
timeB === defaultTime
) {
return (
getString(a).localeCompare(getString(b)) *
dir
);
if (timeA === defaultTime && timeB === defaultTime) {
return getString(a).localeCompare(getString(b)) * dir;
} else if (timeA === defaultTime) {
return 1 * dir;
} else if (timeB === defaultTime) {
@ -265,9 +245,7 @@ var Pontoon = (function (my) {
// Sort by alphabetical order
} else {
return (
getString(a).localeCompare(getString(b)) * dir
);
return getString(a).localeCompare(getString(b)) * dir;
}
});

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

@ -22,9 +22,7 @@ $(function () {
var isProjectNotification =
$.inArray($(this).data('id'), notifications) > -1;
$(this).toggle(isProjectNotification);
$(this)
.next('.horizontal-separator')
.toggle(isProjectNotification);
$(this).next('.horizontal-separator').toggle(isProjectNotification);
});
$('.right-column .notification-item:visible:last')

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

@ -12,9 +12,7 @@ const Sections = {
},
goToNext() {
this.goTo(
Math.min(this.activeSectionIdx + 1, this.sections.length - 1),
);
this.goTo(Math.min(this.activeSectionIdx + 1, this.sections.length - 1));
},
goToPrev() {

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

@ -17,16 +17,11 @@ var Pontoon = (function (my) {
});
// Select active users period
$('#insights h3 .period-selector .selector').on(
'click',
function () {
$(
'#insights h3 .period-selector .selector',
).removeClass('active');
$('#insights h3 .period-selector .selector').on('click', function () {
$('#insights h3 .period-selector .selector').removeClass('active');
$(this).addClass('active');
Pontoon.insights.renderActiveUsers();
},
);
});
// Set up canvas to be HiDPI display ready
$('#insights canvas.chart').each(function () {
@ -57,9 +52,7 @@ var Pontoon = (function (my) {
// Collect data
var parent = $(this).parents('.active-users-chart');
var id = parent.attr('id');
var period = $('.period-selector .active')
.data('period')
.toString();
var period = $('.period-selector .active').data('period').toString();
var active = $('.active-users').data(period)[id];
var total = $('.active-users').data('total')[id];
@ -96,13 +89,7 @@ var Pontoon = (function (my) {
function plot(start, end, color) {
context.beginPath();
context.arc(
x,
y,
radius,
start * Math.PI,
end * Math.PI,
);
context.arc(x, y, radius, start * Math.PI, end * Math.PI);
context.strokeStyle = color;
context.stroke();
}
@ -149,8 +136,7 @@ var Pontoon = (function (my) {
yPadding: 10,
displayColors: false,
callbacks: {
label: (item) =>
nf.format(item.value) + ' days',
label: (item) => nf.format(item.value) + ' days',
},
},
scales: {
@ -222,9 +208,7 @@ var Pontoon = (function (my) {
{
type: 'line',
label: '12-month average',
data: chart.data(
'time-to-review-suggestions-12-month-avg',
),
data: chart.data('time-to-review-suggestions-12-month-avg'),
borderColor: ['#3e7089'],
borderWidth: 1,
pointBackgroundColor: '#3e7089',
@ -250,8 +234,7 @@ var Pontoon = (function (my) {
yPadding: 10,
callbacks: {
label(items, chart) {
const { label } =
chart.datasets[items.datasetIndex];
const { label } = chart.datasets[items.datasetIndex];
return `${label}: ${items.value} days`;
},
},
@ -376,34 +359,25 @@ var Pontoon = (function (my) {
// Dataset order affects stacking, tooltip and
// legend, but it doesn't work intuitively, so
// we need to manually sort tooltip items.
if (
a.datasetIndex === 2 &&
b.datasetIndex === 1
) {
if (a.datasetIndex === 2 && b.datasetIndex === 1) {
return 1;
}
},
callbacks: {
label: function (items, chart) {
const human =
chart.datasets[1].data[items.index];
const machinery =
chart.datasets[2].data[items.index];
const human = chart.datasets[1].data[items.index];
const machinery = chart.datasets[2].data[items.index];
const label =
chart.datasets[items.datasetIndex]
.label;
const label = chart.datasets[items.datasetIndex].label;
const value = items.yLabel;
const base =
label + ': ' + nf.format(value);
const base = label + ': ' + nf.format(value);
switch (label) {
case 'Completion':
return base + '%';
case 'Human translations':
case 'Machinery translations': {
const pct =
Pontoon.insights.getPercent(
const pct = Pontoon.insights.getPercent(
value,
human + machinery,
);
@ -585,38 +559,28 @@ var Pontoon = (function (my) {
// legend, but it doesn't work intuitively, so
// we need to manually sort tooltip items.
if (
(a.datasetIndex === 3 &&
b.datasetIndex === 2) ||
(a.datasetIndex === 3 &&
b.datasetIndex === 1) ||
(a.datasetIndex === 2 &&
b.datasetIndex === 1)
(a.datasetIndex === 3 && b.datasetIndex === 2) ||
(a.datasetIndex === 3 && b.datasetIndex === 1) ||
(a.datasetIndex === 2 && b.datasetIndex === 1)
) {
return 1;
}
},
callbacks: {
label: function (items, chart) {
const label =
chart.datasets[items.datasetIndex]
.label;
const label = chart.datasets[items.datasetIndex].label;
const value = items.yLabel;
const base =
label + ': ' + nf.format(value);
const base = label + ': ' + nf.format(value);
if (chart.datasets.length < 4) return base;
const peerApproved =
chart.datasets[1].data[items.index];
const selfApproved =
chart.datasets[2].data[items.index];
const rejected =
chart.datasets[3].data[items.index];
const peerApproved = chart.datasets[1].data[items.index];
const selfApproved = chart.datasets[2].data[items.index];
const rejected = chart.datasets[3].data[items.index];
switch (label) {
case 'Self-approved': {
const pct =
Pontoon.insights.getPercent(
const pct = Pontoon.insights.getPercent(
value,
peerApproved + selfApproved,
);
@ -624,8 +588,7 @@ var Pontoon = (function (my) {
}
case 'Peer-approved':
case 'Rejected': {
const pct =
Pontoon.insights.getPercent(
const pct = Pontoon.insights.getPercent(
value,
peerApproved + rejected,
);
@ -709,9 +672,7 @@ var Pontoon = (function (my) {
return chart.data.datasets
.map(function (dataset) {
var disabled = dataset.hidden ? 'disabled' : '';
var color =
dataset.borderColor ||
dataset.backgroundColor;
var color = dataset.borderColor || dataset.backgroundColor;
return (
'<li class="' +

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

@ -18,9 +18,7 @@ $(function () {
var clipboard = new Clipboard('.machinery .machinery li');
clipboard.on('success', function (event) {
var successMessage = $(
'<span class="clipboard-success">Copied!</span>',
),
var successMessage = $('<span class="clipboard-success">Copied!</span>'),
$trigger = $(event.trigger);
$('.clipboard-success').remove();
@ -64,25 +62,17 @@ $(function () {
data.source +
'</span>' +
(data.count
? '<sup title="' +
occurrencesTitle +
'">' +
data.count +
'</sup>'
? '<sup title="' + occurrencesTitle + '">' + data.count + '</sup>'
: '') +
'</a></li>',
);
if (data.quality && sources.siblings('.stress').length === 0) {
sources.prepend(
'<span class="stress">' + data.quality + '</span>',
);
sources.prepend('<span class="stress">' + data.quality + '</span>');
}
} else {
var originalTextForDiff = originalText;
originalText = originalText
? diff(original, originalTextForDiff)
: '';
originalText = originalText ? diff(original, originalTextForDiff) : '';
var li = $(
'<li class="suggestion"' +
@ -107,11 +97,7 @@ $(function () {
data.source +
'</span>' +
(data.count
? '<sup title="' +
occurrencesTitle +
'">' +
data.count +
'</sup>'
? '<sup title="' + occurrencesTitle + '">' + data.count + '</sup>'
: '') +
'</a>' +
'</li>' +
@ -135,8 +121,7 @@ $(function () {
'</li>',
);
ul.append(li);
sourcesMap[data.original + data.translation] =
li.find('.sources');
sourcesMap[data.original + data.translation] = li.find('.sources');
if (data.source === 'Translation memory') {
preferred++;
} else {
@ -173,12 +158,8 @@ $(function () {
listitems.sort(function (a, b) {
var stressA = $(a).find('.stress'),
stressB = $(b).find('.stress'),
valA = stressA.length
? parseInt(stressA.html().split('%')[0])
: 0,
valB = stressB.length
? parseInt(stressB.html().split('%')[0])
: 0,
valA = stressA.length ? parseInt(stressA.html().split('%')[0]) : 0,
valB = stressB.length ? parseInt(stressB.html().split('%')[0]) : 0,
sourceA = getTranslationSource(a),
sourceB = getTranslationSource(b);
@ -202,11 +183,7 @@ $(function () {
sortedItems = sources.sort(function (a, b) {
var sourceA = sourceMap[$(a).find('span').text()],
sourceB = sourceMap[$(b).find('span').text()];
return sourceA > sourceB
? 1
: sourceA < sourceB
? -1
: 0;
return sourceA > sourceB ? 1 : sourceA < sourceB ? -1 : 0;
});
$sourcesList.children('li').remove();
@ -230,7 +207,8 @@ $(function () {
function complete(jqXHR, status) {
if (status !== 'abort') {
requests--;
tab.find('.count')
tab
.find('.count')
.find('.preferred')
.html(preferred)
.toggle(preferred > 0)
@ -441,11 +419,7 @@ $(function () {
*/
function getPlaceableMarkup(title, replacement) {
return (
'<mark class="placeable" title="' +
title +
'">' +
replacement +
'</mark>'
'<mark class="placeable" title="' + title + '">' + replacement + '</mark>'
);
}
@ -468,11 +442,7 @@ $(function () {
/* Special spaces */
// Pontoon.doNotRender() replaces \u00A0 with &nbsp;
string = markPlaceable(string, /&nbsp;/gi, 'Non-breaking space');
string = markPlaceable(
string,
/[\u202F]/gi,
'Narrow non-breaking space',
);
string = markPlaceable(string, /[\u202F]/gi, 'Narrow non-breaking space');
string = markPlaceable(string, /[\u2009]/gi, 'Thin space');
/* Multiple spaces */

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

@ -5,15 +5,11 @@ $(function () {
$form.find('.errors p').css('visibility', 'hidden');
if (!locales) {
$form
.find('.locale-selector .errors p')
.css('visibility', 'visible');
$form.find('.locale-selector .errors p').css('visibility', 'visible');
}
if (!message) {
$form
.find('.message-wrapper .errors p')
.css('visibility', 'visible');
$form.find('.message-wrapper .errors p').css('visibility', 'visible');
}
return locales && message;

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

@ -9,7 +9,6 @@ directly, as well as to write its own changes back.
This document describes that sync process in detail.
## Triggering a Sync
Pontoon is assumed to run a sync once an hour, although this is configurable.
@ -18,7 +17,6 @@ disabled within the admin interface and schedules a sync task for each one.
Sync tasks are executed in parallel, using [Celery](http://www.celeryproject.org/)
to manage the worker queue.
## Syncing a Project
Syncing an individual project is split into two tasks. The first one is syncing
@ -46,7 +44,6 @@ The second step is syncing translations:
changes, no commit is made.
- Clean up leftover information in the database.
## Comparing Entities
The heart of the syncing process is comparing an entity stored in Pontoon's
@ -70,7 +67,6 @@ The actual comparison logic goes something like this:
![](./sync-process-diagram.png)
## Executing Changes
Entity comparison produces a Changeset, which is used to make the necessary
@ -80,24 +76,24 @@ Changesets can perform 4 different operations on an entity:
**Update Pontoon from VCS**
&emsp;Add a translation from VCS to Pontoon if necessary. Existing translations
that match the VCS translation are re-used, and all non-matching translations
are marked as unapproved.
&emsp;Add a translation from VCS to Pontoon if necessary. Existing translations
that match the VCS translation are re-used, and all non-matching translations
are marked as unapproved.
**Update VCS from Pontoon**
&emsp;Add a translation from Pontoon to VCS, overwriting the existing translation
if it exists.
&emsp;Add a translation from Pontoon to VCS, overwriting the existing translation
if it exists.
**Create New Entity in Pontoon**
&emsp;Create a new entity in the Pontoon database, including the VCS translation if
it is present.
&emsp;Create a new entity in the Pontoon database, including the VCS translation if
it is present.
**Obsolete Pontoon Entity**
&emsp;Mark an entity in the database as obsolete, due to it not existing in VCS.
The entity will no longer appear on the website.
&emsp;Mark an entity in the database as obsolete, due to it not existing in VCS.
The entity will no longer appear on the website.
When possible, Changesets perform database operations in bulk in order to speed
up the syncing process.

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

@ -27,15 +27,12 @@ var Pontoon = (function (my) {
'type0-0-1': 'regexp',
'value0-0-1': '^' + locale + ' / ',
resolution: '---',
include_fields:
'id,summary,last_change_time,assigned_to',
include_fields: 'id,summary,last_change_time,assigned_to',
},
success: function (data) {
if (data.bugs.length) {
data.bugs.sort(function (l, r) {
return l.last_change_time < r.last_change_time
? 1
: -1;
return l.last_change_time < r.last_change_time ? 1 : -1;
});
var tbody = $('<tbody>'),
@ -71,9 +68,7 @@ var Pontoon = (function (my) {
$('<td>', {
class: 'last-changed',
datetime: bug.last_change_time,
html: formatter.format(
new Date(bug.last_change_time),
),
html: formatter.format(new Date(bug.last_change_time)),
}).appendTo(tr);
$('<td>', {
@ -106,10 +101,7 @@ var Pontoon = (function (my) {
}
},
error: function (error) {
if (
error.status === 0 &&
error.statusText !== 'abort'
) {
if (error.status === 0 && error.statusText !== 'abort') {
errorCallback(
'Oops, something went wrong. We were unable to load the bugs. Please try again later.',
);
@ -130,14 +122,11 @@ var Pontoon = (function (my) {
}
function getNumber(el) {
return parseInt(
$(el).find('.id').text().replace(/,/g, ''),
);
return parseInt($(el).find('.id').text().replace(/,/g, ''));
}
function getTime(el) {
var date =
$(el).find('.last-changed').attr('datetime') || 0;
var date = $(el).find('.last-changed').attr('datetime') || 0;
return new Date(date).getTime();
}
@ -163,9 +152,7 @@ var Pontoon = (function (my) {
// Sort by alphabetical order
} else {
return (
getString(a).localeCompare(getString(b)) * dir
);
return getString(a).localeCompare(getString(b)) * dir;
}
});

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

@ -28,11 +28,7 @@ $(function () {
if ($(this).parents('.general').length > 0) {
$form.append(
inputHidden(
'general-' + value,
itemId,
'permissions-form-item',
),
inputHidden('general-' + value, itemId, 'permissions-form-item'),
);
} else {
// We have to retrieve an index of parent project locale form
@ -41,9 +37,7 @@ $(function () {
.data('index');
$form.append(
inputHidden(
'project-locale-' +
localeProjectIndex +
'-translators',
'project-locale-' + localeProjectIndex + '-translators',
itemId,
'permissions-form-item',
),
@ -124,15 +118,13 @@ $(function () {
// Copy Translators from the General section
// Reverse selector order to keep presentation order (prepend)
$(
$('.permissions-groups.general .translators li').get().reverse(),
).each(function () {
$($('.permissions-groups.general .translators li').get().reverse()).each(
function () {
$permsForm
.find(
'.user.available li[data-id="' + $(this).data('id') + '"]',
)
.find('.user.available li[data-id="' + $(this).data('id') + '"]')
.click();
});
},
);
// Scroll to the right project locale
$('html, body').animate(

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

@ -58,8 +58,7 @@ var Pontoon = (function (my) {
var show = condition;
if (type === 'locale-projects') {
show =
condition && $('.items td.enabled:visible').length > 0;
show = condition && $('.items td.enabled:visible').length > 0;
} else if (type === 'team') {
show =
condition &&
@ -80,17 +79,10 @@ var Pontoon = (function (my) {
projects: projects,
},
success: function () {
Pontoon.endLoader(
'New ' + type + ' request sent.',
'',
5000,
);
Pontoon.endLoader('New ' + type + ' request sent.', '', 5000);
},
error: function () {
Pontoon.endLoader(
'Oops, something went wrong.',
'error',
);
Pontoon.endLoader('Oops, something went wrong.', 'error');
},
complete: function () {
$('.items td.check').removeClass('enabled');
@ -119,10 +111,7 @@ var Pontoon = (function (my) {
if (res.status === 409) {
Pontoon.endLoader(res.responseText, 'error');
} else {
Pontoon.endLoader(
'Oops, something went wrong.',
'error',
);
Pontoon.endLoader('Oops, something went wrong.', 'error');
}
},
complete: function () {
@ -139,9 +128,7 @@ var Pontoon = (function (my) {
$(function () {
var container = $('#main .container');
var type = $('#server').data('locale-projects')
? 'locale-projects'
: 'team';
var type = $('#server').data('locale-projects') ? 'locale-projects' : 'team';
// Switch between available projects/teams and projects/team to request
container.on('click', '.controls .request-toggle', function (e) {
@ -226,33 +213,19 @@ $(function () {
return $(element).siblings('.name').data('slug');
})
.get();
locale =
$('#server').data('locale') || Pontoon.getSelectedLocale();
locale = $('#server').data('locale') || Pontoon.getSelectedLocale();
Pontoon.requestItem.requestProjects(
locale,
projects,
'projects',
);
Pontoon.requestItem.requestProjects(locale, projects, 'projects');
$(this).removeClass('confirmed').html('Request new projects');
}
// Requesting from project page
else if (
type === 'locale-projects' &&
$('body').hasClass('project')
) {
else if (type === 'locale-projects' && $('body').hasClass('project')) {
var project = $('#server').data('project');
locale = $('.items td.radio.enabled')
.siblings('.name')
.data('slug');
locale = $('.items td.radio.enabled').siblings('.name').data('slug');
Pontoon.requestItem.requestProjects(
locale,
[project],
'language',
);
Pontoon.requestItem.requestProjects(locale, [project], 'language');
$(this).removeClass('confirmed').html('Request new language');
} else if (type === 'team') {

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

@ -23,9 +23,7 @@ test('TagResourceSearch renders select', () => {
test('TagResourceSearch onChange', async () => {
const search = jest.fn();
const type = jest.fn();
const wrapper = mount(
<TagResourceSearch onSearch={search} onType={type} />,
);
const wrapper = mount(<TagResourceSearch onSearch={search} onType={type} />);
wrapper.find('input').simulate('change', { target: { value: 'FOO' } });
wrapper.find('select').simulate('change', { target: { value: 'BAR' } });

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

@ -67,9 +67,7 @@ export function isSameOrigin(url) {
*/
export function post(url, data) {
// this is a bit sketchy but the only afaict way due to session_csrf
const csrf = document.querySelector(
'input[name=csrfmiddlewaretoken]',
).value;
const csrf = document.querySelector('input[name=csrfmiddlewaretoken]').value;
const init = {
body: asFormData(data),

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

@ -41,8 +41,7 @@ export function CheckboxTable({ data, onSubmit, submitMessage }) {
const pruned = prune(checked, visible.current);
// some rows can be empty strings if there are more visible rows than resources
const some = pruned.size > 0;
const all =
some && pruned.size === visible.current.filter(Boolean).length;
const all = some && pruned.size === visible.current.filter(Boolean).length;
return (
<Checkbox
@ -59,11 +58,7 @@ export function CheckboxTable({ data, onSubmit, submitMessage }) {
visible.current[item.viewIndex] = name;
return (
<Checkbox
checked={checked.has(name)}
name={name}
onChange={selectOne}
/>
<Checkbox checked={checked.has(name)} name={name} onChange={selectOne} />
);
};

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

@ -72,9 +72,7 @@ test('CheckboxTable sort change', () => {
// removed if they are no longer visible
const table = mount(
<CheckboxTable
data={[['1'], ['2'], ['3'], ['4'], ['5'], ['6'], ['7']]}
/>,
<CheckboxTable data={[['1'], ['2'], ['3'], ['4'], ['5'], ['6'], ['7']]} />,
);
// Select '2' and '3'
@ -102,9 +100,7 @@ test('CheckboxTable sort change', () => {
test('CheckboxTable page change', () => {
const table = mount(
<CheckboxTable
data={[['1'], ['2'], ['3'], ['4'], ['5'], ['6'], ['7']]}
/>,
<CheckboxTable data={[['1'], ['2'], ['3'], ['4'], ['5'], ['6'], ['7']]} />,
);
expect(table.find('input[name="2"]')).toHaveLength(1);
@ -121,9 +117,7 @@ test('CheckboxTable page change', () => {
test('CheckboxTable resize', () => {
const table = mount(
<CheckboxTable
data={[['1'], ['2'], ['3'], ['4'], ['5'], ['6'], ['7']]}
/>,
<CheckboxTable data={[['1'], ['2'], ['3'], ['4'], ['5'], ['6'], ['7']]} />,
);
// Resize to 10 rows

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

@ -1,4 +1,5 @@
/* global module */
/* eslint-env node */
module.exports = {
root: true,
parser: '@typescript-eslint/parser',

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

@ -45,7 +45,6 @@
</tr>
</table>
## Code architecture
### Where code goes
@ -74,6 +73,7 @@ Of course, more can be added if needed. For example, modules with a high number
To import code from further away than the parent directory,
use paths starting with `~` to refer to the root of the `src/` directory.
For example:
```js
import { SearchBox } from '~/modules/search';
```
@ -111,7 +111,6 @@ As far as we know, it is not possible to make that work in Chrome or Edge. This
If you can't turn on websockets, you will see errors in the console (that's not very impacting) and you'll have to reload your Django server regularly, because polling requests don't close, and after so many web page reloads, the Django process won't be able to accept new requests.
## Dependencies
We manage our JavaScript dependencies with `npm`.
@ -134,7 +133,6 @@ You might want to remove the `translate/node_modules` folder after you've run th
(and the `package.json` and `package-lock.json` files have been updated)
and before rebuilding the image, to reduce the size of the docker context.
## Type checking
Our code uses TypeScript for type-checking the production code. Tests are not type-checked in general, which allows for smaller test fixtures. Visit the [TypeScript documentation](https://www.typescriptlang.org/docs) to learn more about TypeScript.
@ -143,7 +141,6 @@ To check for TypeScript errors locally, run:
$ make types
## Testing
Tests are run using [`jest`](https://facebook.github.io/jest/). We use [`enzyme`](http://airbnb.io/enzyme/docs/api/) for mounting React components and [`sinon`](http://sinonjs.org/) for mocking.
@ -172,7 +169,6 @@ it('does something specific', () => {
We use `jest`'s [`expect`](https://facebook.github.io/jest/docs/en/expect.html) assertion tool.
## Localization
The user interface is localized using [Fluent](https://projectfluent.org/) and the library `fluent-react`. Fluent allows to move the complexity of handling translated content from the developer to the translator. Thus, when using it, you should care only about the English version, and trust that the localizers will know what to do with your string in their language.
@ -186,11 +182,13 @@ That would give:
```js
class Editor extends React.Component {
render() {
return <div>
<Localized id="entitydetails-Editor--button-update">
return (
<div>
<Localized id='entitydetails-Editor--button-update'>
<button>Update</button>
</Localized>
</div>;
</div>
);
}
}
```
@ -206,7 +204,7 @@ Those files use the FTL format. In its simplest form, a string in such a file (c
### Semantic identifiers
Fluent uses the concept of a *social contract* between developer and localizers. This contract is established by the selection of a unique identifier, called `l10n-id`, which carries a promise of being used in a particular place to carry a particular meaning.
Fluent uses the concept of a _social contract_ between developer and localizers. This contract is established by the selection of a unique identifier, called `l10n-id`, which carries a promise of being used in a particular place to carry a particular meaning.
You should consider the `l10n-id` as a variable name. If the meaning of the content changes, then you should also change the ID. This will notify localizers that the content is different from before and that a new translation is needed. However, if you make minor changes (fix a typo, make a change that keeps the same meaning) you should instead keep the same ID.
@ -220,11 +218,10 @@ In Fluent, the developer is not to be bothered with inner logic and complexity t
In order to easily verify that a string is effectively localized, you can turn on pseudo-localization. To do that, add `pseudolocalization=accented` or `pseudolocalization=bidi` to the URL, then refresh the page.
Pseudo-localization turns every supported string into a different version of itself. We support two modes: "accented" (transforms "Accented English" into "Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ") and "bidi" (transforms "Reversed English" into "ᴚǝʌǝɹsǝp Ǝuƃʅı"). Because only strings that are actually localized (they exist in our reference en-US FTL file and they are properly set in a `<Localized>` component) get that transformation, it is easy to spot which strings are *not* properly localized in the interface.
Pseudo-localization turns every supported string into a different version of itself. We support two modes: "accented" (transforms "Accented English" into "Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ") and "bidi" (transforms "Reversed English" into "ᴚǝʌǝɹsǝp Ǝuƃʅı"). Because only strings that are actually localized (they exist in our reference en-US FTL file and they are properly set in a `<Localized>` component) get that transformation, it is easy to spot which strings are _not_ properly localized in the interface.
You can read [more about pseudo-localization on Wikipedia](https://en.wikipedia.org/wiki/Pseudolocalization).
## Development resources
### Integration between Django and React

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

@ -47,9 +47,6 @@
"start": "rollup -c --watch",
"build": "rollup -c",
"build:prod": "rollup -c --environment BUILD:production",
"lint": "eslint 'src/**/*.{js,ts,tsx}'",
"prettier": "prettier --write src/",
"check-prettier": "prettier --check src/",
"test": "jest",
"types": "tsc --noEmit"
}

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

@ -104,17 +104,12 @@ class App extends React.Component<InternalProps> {
<AddonPromotion />
<header>
<Navigation />
<ResourceProgress
stats={state.stats}
parameters={state.parameters}
/>
<ResourceProgress stats={state.stats} parameters={state.parameters} />
<ProjectInfo
projectSlug={state.parameters.project}
project={state.project}
/>
<notification.NotificationPanel
notification={state.notification}
/>
<notification.NotificationPanel notification={state.notification} />
<UserControls />
</header>
<section className='main-content'>

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

@ -38,9 +38,7 @@ export default class APIBase {
isObject: (obj: any) => boolean = function (obj: any) {
return (
obj === Object(obj) &&
!Array.isArray(obj) &&
typeof obj !== 'function'
obj === Object(obj) && !Array.isArray(obj) && typeof obj !== 'function'
);
};
@ -95,9 +93,7 @@ export default class APIBase {
const newObj: any = {};
Object.keys(results).forEach((key) => {
newObj[this.toCamelCase(key)] = this.keysToCamelCase(
results[key],
);
newObj[this.toCamelCase(key)] = this.keysToCamelCase(results[key]);
});
return newObj;

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

@ -127,12 +127,7 @@ export default class EntityAPI extends APIBase {
const headers = new Headers();
headers.append('X-Requested-With', 'XMLHttpRequest');
const results = await this.fetch(
'/get-history/',
'GET',
payload,
headers,
);
const results = await this.fetch('/get-history/', 'GET', payload, headers);
return this.keysToCamelCase(results);
}

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

@ -115,9 +115,7 @@ export default class MachineryAPI extends APIBase {
return [];
}
return [
{ sources: ['google-translate'], original: source, translation },
];
return [{ sources: ['google-translate'], original: source, translation }];
}
/**
@ -171,9 +169,7 @@ export default class MachineryAPI extends APIBase {
return [];
}
return [
{ sources: ['systran-translate'], original: source, translation },
];
return [{ sources: ['systran-translate'], original: source, translation }];
}
/**

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

@ -91,12 +91,7 @@ export default class TranslationAPI extends APIBase {
}
unapprove(id: number, resource: string): Promise<any> {
return this._changeStatus(
'/translations/unapprove/',
id,
resource,
false,
);
return this._changeStatus('/translations/unapprove/', id, resource, false);
}
reject(id: number, resource: string): Promise<any> {
@ -104,12 +99,7 @@ export default class TranslationAPI extends APIBase {
}
unreject(id: number, resource: string): Promise<any> {
return this._changeStatus(
'/translations/unreject/',
id,
resource,
false,
);
return this._changeStatus('/translations/unreject/', id, resource, false);
}
delete(id: number): Promise<any> {

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

@ -93,12 +93,7 @@ export default class UserAPI extends APIBase {
headers.append('X-Requested-With', 'XMLHttpRequest');
headers.append('X-CSRFToken', csrfToken);
return await this.fetch(
'/update-tour-status/',
'POST',
payload,
headers,
);
return await this.fetch('/update-tour-status/', 'POST', payload, headers);
}
/**

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

@ -169,9 +169,7 @@ export default function AddComment(props: Props): React.ReactElement<'div'> {
const commentEditorSpan = document.querySelector(
'.comments-list .add-comment .comment-editor p span',
);
const commentEditorSpanHeight = !(
commentEditorSpan instanceof HTMLElement
)
const commentEditorSpanHeight = !(commentEditorSpan instanceof HTMLElement)
? 0
: commentEditorSpan.offsetHeight;
@ -193,8 +191,7 @@ export default function AddComment(props: Props): React.ReactElement<'div'> {
const suggestionsHeight = el.clientHeight + suggestionsHeightAdjustment;
const teamCommentsOverflow = !teamCommentsRect
? false
: setTop + el.clientHeight - tabIndexHeight >
teamCommentsRect.height;
: setTop + el.clientHeight - tabIndexHeight > teamCommentsRect.height;
if (
(teamCommentsActive && teamCommentsOverflow) ||
@ -278,15 +275,13 @@ export default function AddComment(props: Props): React.ReactElement<'div'> {
switch (event.key) {
case 'ArrowDown': {
event.preventDefault();
const prevIndex =
index >= suggestedUsers.length - 1 ? 0 : index + 1;
const prevIndex = index >= suggestedUsers.length - 1 ? 0 : index + 1;
setIndex(prevIndex);
break;
}
case 'ArrowUp': {
event.preventDefault();
const nextIndex =
index <= 0 ? suggestedUsers.length - 1 : index - 1;
const nextIndex = index <= 0 ? suggestedUsers.length - 1 : index - 1;
setIndex(nextIndex);
break;
}
@ -325,9 +320,7 @@ export default function AddComment(props: Props): React.ReactElement<'div'> {
}
};
const handleMentionsMouseDown = (
event: React.MouseEvent<HTMLDivElement>,
) => {
const handleMentionsMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {
event.preventDefault();
if (target !== null) {
const suggestedUserIndex = suggestedUsers.indexOf(
@ -359,8 +352,7 @@ export default function AddComment(props: Props): React.ReactElement<'div'> {
const wordBefore = Editor.before(editor, start, { unit: 'word' });
const before = wordBefore && Editor.before(editor, wordBefore);
const beforeRange = before && Editor.range(editor, before, start);
const beforeText =
beforeRange && Editor.string(editor, beforeRange);
const beforeText = beforeRange && Editor.string(editor, beforeRange);
// Unicode property escapes allow for matching non-ASCII characters
const beforeMatch =
beforeText && beforeText.match(/^@((\p{L}|\p{N}|\p{P})+)$/u);
@ -434,22 +426,14 @@ export default function AddComment(props: Props): React.ReactElement<'div'> {
const removeStyleForHover = (event: React.MouseEvent<HTMLDivElement>) => {
event.preventDefault();
event.currentTarget.children[index].className =
'mention active-mention';
event.currentTarget.children[index].className = 'mention active-mention';
};
return (
<div className='comment add-comment'>
<UserAvatar
username={user.username}
imageUrl={user.gravatarURLSmall}
/>
<UserAvatar username={user.username} imageUrl={user.gravatarURLSmall} />
<div className='container'>
<Slate
editor={editor}
value={value}
onChange={handleEditorOnChange}
>
<Slate editor={editor} value={value} onChange={handleEditorOnChange}>
<Localized
id='comments-AddComment--input'
attrs={{ placeholder: true }}
@ -460,11 +444,7 @@ export default function AddComment(props: Props): React.ReactElement<'div'> {
dir='auto'
placeholder={`Write a comment…`}
renderElement={renderElement}
onKeyDown={
target
? handleMentionsKeyDown
: handleEditorKeyDown
}
onKeyDown={target ? handleMentionsKeyDown : handleEditorKeyDown}
/>
</Localized>
{target && suggestedUsers.length > 0 && (
@ -479,9 +459,7 @@ export default function AddComment(props: Props): React.ReactElement<'div'> {
<div
key={suggestedUser}
className={
i === index
? 'mention active-mention'
: 'mention'
i === index ? 'mention active-mention' : 'mention'
}
onMouseDown={handleMentionsMouseDown}
>
@ -491,18 +469,14 @@ export default function AddComment(props: Props): React.ReactElement<'div'> {
>
<span className='user-avatar'>
<img
src={getUserGravatar(
suggestedUser,
)}
src={getUserGravatar(suggestedUser)}
alt='User Avatar'
width='22'
height='22'
/>
</span>
</Localized>
<span className='name'>
{suggestedUser}
</span>
<span className='name'>{suggestedUser}</span>
</div>
))}
</div>
@ -575,11 +549,7 @@ const insertMention = (editor, character, users) => {
const MentionElement = ({ attributes, children, element }) => {
return (
<span
{...attributes}
contentEditable={false}
className='mention-element'
>
<span {...attributes} contentEditable={false} className='mention-element'>
@{element.character}
{children}
</span>

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

@ -44,9 +44,7 @@ export default function Comment(props: Props): null | React.ReactElement<'li'> {
href={`/contributors/${comment.username}`}
target='_blank'
rel='noopener noreferrer'
onClick={(e: React.MouseEvent) =>
e.stopPropagation()
}
onClick={(e: React.MouseEvent) => e.stopPropagation()}
>
{comment.author}
</a>

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

@ -64,11 +64,7 @@ export default function CommentsList(props: Props): React.ReactElement<'div'> {
<h2 className='title'>PINNED COMMENTS</h2>
</Localized>
<ul>
{pinnedComments.map((comment) =>
renderComment(comment),
)}
</ul>
<ul>{pinnedComments.map((comment) => renderComment(comment))}</ul>
{!hideAllComments ? (
<Localized id='comments-CommentsList--all-comments'>
<h2 className='title'>ALL COMMENTS</h2>
@ -77,9 +73,7 @@ export default function CommentsList(props: Props): React.ReactElement<'div'> {
</section>
) : null}
<section className='all-comments'>
<ul>
{unpinnedComments.map((comment) => renderComment(comment))}
</ul>
<ul>{unpinnedComments.map((comment) => renderComment(comment))}</ul>
{!canComment ? null : (
<AddComment
parameters={parameters}

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

@ -275,16 +275,12 @@ export function sendTranslation(
// The translation that was provided is the same as an existing
// translation for that entity.
dispatch(
notification.actions.add(
notification.messages.SAME_TRANSLATION,
),
notification.actions.add(notification.messages.SAME_TRANSLATION),
);
} else if (content.status) {
// Notify the user of the change that happened.
dispatch(
notification.actions.add(
notification.messages.TRANSLATION_SAVED,
),
notification.actions.add(notification.messages.TRANSLATION_SAVED),
);
// Ignore existing unsavedchanges because they are saved now.

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

@ -28,9 +28,7 @@ describe('<EditorMainAction>', () => {
const updateStatusMock = sinon.spy();
sinon.stub(history.actions, 'updateStatus').returns(updateStatusMock);
sinon.stub(user.selectors, 'isTranslator').returns(true);
sinon
.stub(editor.selectors, 'sameExistingTranslation')
.returns({ pk: 1 });
sinon.stub(editor.selectors, 'sameExistingTranslation').returns({ pk: 1 });
const [wrapper] = createComponent();

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

@ -109,11 +109,7 @@ export default function EditorMainAction(
}
return (
<Localized
id={btn.id}
attrs={{ title: true }}
elems={{ glyph: btn.glyph }}
>
<Localized id={btn.id} attrs={{ title: true }} elems={{ glyph: btn.glyph }}>
<button
className={btn.className}
onClick={btn.action}

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

@ -78,10 +78,7 @@ function MenuContent(props: Props) {
<KeyboardShortcuts />
{props.translationLengthHook}
<div className='actions'>
<Localized
id='editor-EditorMenu--button-copy'
attrs={{ title: true }}
>
<Localized id='editor-EditorMenu--button-copy' attrs={{ title: true }}>
<button
className='action-copy'
onClick={props.copyOriginalIntoEditor}
@ -90,10 +87,7 @@ function MenuContent(props: Props) {
COPY
</button>
</Localized>
<Localized
id='editor-EditorMenu--button-clear'
attrs={{ title: true }}
>
<Localized id='editor-EditorMenu--button-clear' attrs={{ title: true }}>
<button
className='action-clear'
onClick={props.clearEditor}

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

@ -39,8 +39,7 @@ export function EditorSettings({
>
<li
className={
'check-box' +
(settings.runQualityChecks ? ' enabled' : '')
'check-box' + (settings.runQualityChecks ? ' enabled' : '')
}
title='Run Translate Toolkit checks before submitting translations'
onClick={() => toggleSetting('runQualityChecks')}
@ -56,8 +55,7 @@ export function EditorSettings({
>
<li
className={
'check-box' +
(settings.forceSuggestions ? ' enabled' : '')
'check-box' + (settings.forceSuggestions ? ' enabled' : '')
}
title='Save suggestions instead of translations'
onClick={() => toggleSetting('forceSuggestions')}

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

@ -136,10 +136,7 @@ describe('<FailedChecks>', () => {
const [wrapper, store] = createFailedChecks();
store.dispatch(
editor.actions.updateFailedChecks(
{ pndbWarnings: ['a warning'] },
'',
),
editor.actions.updateFailedChecks({ pndbWarnings: ['a warning'] }, ''),
);
wrapper.update();

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

@ -52,10 +52,7 @@ export default function FailedChecks(
return (
<div className='failed-checks'>
<Localized
id='editor-FailedChecks--close'
attrs={{ ariaLabel: true }}
>
<Localized id='editor-FailedChecks--close' attrs={{ ariaLabel: true }}>
<button
aria-label='Close failed checks popup'
className='close'

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

@ -62,9 +62,7 @@ function KeyboardShortcuts({ onDiscard }: KeyboardShortcutsProps) {
mod1: <span />,
}}
>
<td>
{'<mod1>Shift</mod1> + <accel>Enter</accel>'}
</td>
<td>{'<mod1>Shift</mod1> + <accel>Enter</accel>'}</td>
</Localized>
</tr>
<tr>
@ -108,9 +106,7 @@ function KeyboardShortcuts({ onDiscard }: KeyboardShortcutsProps) {
}}
>
<td>
{
'<mod1>Ctrl</mod1> + <mod2>Shift</mod2> + <accel>C</accel>'
}
{'<mod1>Ctrl</mod1> + <mod2>Shift</mod2> + <accel>C</accel>'}
</td>
</Localized>
</tr>
@ -146,9 +142,7 @@ function KeyboardShortcuts({ onDiscard }: KeyboardShortcutsProps) {
}}
>
<td>
{
'<mod1>Ctrl</mod1> + <mod2>Shift</mod2> + <accel>F</accel>'
}
{'<mod1>Ctrl</mod1> + <mod2>Shift</mod2> + <accel>F</accel>'}
</td>
</Localized>
</tr>
@ -165,9 +159,7 @@ function KeyboardShortcuts({ onDiscard }: KeyboardShortcutsProps) {
}}
>
<td>
{
'<mod1>Ctrl</mod1> + <mod2>Shift</mod2> + <accel>A</accel>'
}
{'<mod1>Ctrl</mod1> + <mod2>Shift</mod2> + <accel>A</accel>'}
</td>
</Localized>
</tr>
@ -184,9 +176,7 @@ function KeyboardShortcuts({ onDiscard }: KeyboardShortcutsProps) {
}}
>
<td>
{
'<mod1>Ctrl</mod1> + <mod2>Shift</mod2> + <accel>Up</accel>'
}
{'<mod1>Ctrl</mod1> + <mod2>Shift</mod2> + <accel>Up</accel>'}
</td>
</Localized>
</tr>
@ -203,9 +193,7 @@ function KeyboardShortcuts({ onDiscard }: KeyboardShortcutsProps) {
}}
>
<td>
{
'<mod1>Ctrl</mod1> + <mod2>Shift</mod2> + <accel>Down</accel>'
}
{'<mod1>Ctrl</mod1> + <mod2>Shift</mod2> + <accel>Down</accel>'}
</td>
</Localized>
</tr>

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

@ -27,15 +27,15 @@ describe('<TranslationLength>', () => {
);
expect(wrapper.find('.countdown')).toHaveLength(0);
expect(
wrapper.find('.translation-vs-original').childAt(0).text(),
).toEqual('7');
expect(
wrapper.find('.translation-vs-original').childAt(1).text(),
).toEqual('|');
expect(
wrapper.find('.translation-vs-original').childAt(2).text(),
).toEqual('5');
expect(wrapper.find('.translation-vs-original').childAt(0).text()).toEqual(
'7',
);
expect(wrapper.find('.translation-vs-original').childAt(1).text()).toEqual(
'|',
);
expect(wrapper.find('.translation-vs-original').childAt(2).text()).toEqual(
'5',
);
});
it('shows translation length and plural original string length', () => {
@ -48,9 +48,9 @@ describe('<TranslationLength>', () => {
/>,
);
expect(
wrapper.find('.translation-vs-original').childAt(2).text(),
).toEqual('6');
expect(wrapper.find('.translation-vs-original').childAt(2).text()).toEqual(
'6',
);
});
it('shows countdown if MAX_LENGTH provided in LANG entity comment', () => {
@ -104,8 +104,8 @@ describe('<TranslationLength>', () => {
/>,
);
expect(
wrapper.find('.translation-vs-original').childAt(0).text(),
).toEqual('19');
expect(wrapper.find('.translation-vs-original').childAt(0).text()).toEqual(
'19',
);
});
});

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

@ -28,10 +28,7 @@ export default class TranslationLength extends React.Component<Props> {
if (parts[0].startsWith('MAX_LENGTH')) {
try {
return parseInt(
parts[0].split('MAX_LENGTH: ')[1].split(' ')[0],
10,
);
return parseInt(parts[0].split('MAX_LENGTH: ')[1].split(' ')[0], 10);
} catch (e) {
// Catch unexpected comment structure
}
@ -69,8 +66,7 @@ export default class TranslationLength extends React.Component<Props> {
</div>
) : (
<div className='translation-vs-original'>
<span>{translation.length}</span>|
<span>{original.length}</span>
<span>{translation.length}</span>|<span>{original.length}</span>
</div>
)}
</div>

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

@ -23,10 +23,7 @@ export default function useCopyMachineryTranslation(
const updateMachinerySources = useCallback(
(machinerySources: Array<SourceType>, machineryTranslation: string) => {
dispatch(
actions.updateMachinerySources(
machinerySources,
machineryTranslation,
),
actions.updateMachinerySources(machinerySources, machineryTranslation),
);
},
[dispatch],
@ -64,10 +61,7 @@ export default function useCopyMachineryTranslation(
else {
updateTranslation(translation.translation, 'machinery');
}
updateMachinerySources(
translation.sources,
translation.translation,
);
updateMachinerySources(translation.sources, translation.translation);
},
[
isFluentTranslationMessage,

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

@ -43,10 +43,7 @@ export default function useHandleShortcuts(): (
return (
event: React.KeyboardEvent<HTMLTextAreaElement>,
sendTranslation: (
ignoreWarnings?: boolean,
translation?: string,
) => void,
sendTranslation: (ignoreWarnings?: boolean, translation?: string) => void,
clearEditorCustom?: () => void,
copyOriginalIntoEditorCustom?: () => void,
) => {
@ -80,10 +77,7 @@ export default function useHandleShortcuts(): (
// Approve anyway.
else if (typeof source === 'number') {
updateTranslationStatus(source, 'approve', ignoreWarnings);
} else if (
sameExistingTranslation &&
!sameExistingTranslation.approved
) {
} else if (sameExistingTranslation && !sameExistingTranslation.approved) {
updateTranslationStatus(
sameExistingTranslation.pk,
'approve',

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

@ -19,10 +19,7 @@ export default function useReplaceSelectionContent(
// the content differently for each Editor type. Thus each Editor
// must use this hook and pass it a function specific to its needs.
if (selectionReplacementContent) {
updateTranslationSelectionWith(
selectionReplacementContent,
changeSource,
);
updateTranslationSelectionWith(selectionReplacementContent, changeSource);
dispatch(editor.actions.resetSelection());
}
}, [

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

@ -62,11 +62,5 @@ export default function useUpdateUnsavedChanges(richEditor: boolean) {
dispatch(unsavedchanges.actions.hide());
}
prevTranslation.current = translation;
}, [
richEditor,
translation,
prevTranslation,
unsavedChangesShown,
dispatch,
]);
}, [richEditor, translation, prevTranslation, unsavedChangesShown, dispatch]);
}

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

@ -149,14 +149,8 @@ export default function reducer(
case UPDATE_FAILED_CHECKS:
return {
...state,
errors: extractFailedChecksOfType(
action.failedChecks,
'Errors',
),
warnings: extractFailedChecksOfType(
action.failedChecks,
'Warnings',
),
errors: extractFailedChecksOfType(action.failedChecks, 'Errors'),
warnings: extractFailedChecksOfType(action.failedChecks, 'Warnings'),
source: action.source,
};
case UPDATE_SELECTION:

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

@ -118,9 +118,7 @@ describe('sameExistingTranslation', () => {
{
...EDITOR_FLUENT,
translation: fluent.flattenMessage(
fluent.parser.parseEntry(
HISTORY_FLUENT.translations[0].string,
),
fluent.parser.parseEntry(HISTORY_FLUENT.translations[0].string),
),
},
ACTIVE_TRANSLATION,
@ -134,9 +132,7 @@ describe('sameExistingTranslation', () => {
{
...EDITOR_FLUENT,
translation: fluent.flattenMessage(
fluent.parser.parseEntry(
HISTORY_FLUENT.translations[1].string,
),
fluent.parser.parseEntry(HISTORY_FLUENT.translations[1].string),
),
},
ACTIVE_TRANSLATION,

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

@ -66,8 +66,7 @@ export function _getPreviousEntity(
return undefined;
}
const previous =
currentIndex === 0 ? entities.length - 1 : currentIndex - 1;
const previous = currentIndex === 0 ? entities.length - 1 : currentIndex - 1;
return entities[previous];
}

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

@ -75,10 +75,7 @@ export function get(locales: ReadonlyArray<string>) {
// We know this is English, let's make it weird before bundling it.
if (usePseudoLocalization) {
bundleOptions = {
transform:
PSEUDO_STRATEGIES[
urlParams.get('pseudolocalization')
],
transform: PSEUDO_STRATEGIES[urlParams.get('pseudolocalization')],
};
}

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

@ -49,8 +49,8 @@ describe('<AppLocalizationProvider>', () => {
AppLocalizationProviderBase,
);
expect(
wrapper.find('#content-test-AppLocalizationProvider'),
).toHaveLength(1);
expect(wrapper.find('#content-test-AppLocalizationProvider')).toHaveLength(
1,
);
});
});

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

@ -50,13 +50,13 @@ const ACCENTED_MAP = {
const FLIPPED_MAP = {
// ∀ԐↃᗡƎℲ⅁HIſӼ⅂WNOԀÒᴚS⊥∩ɅMX⅄Z
caps: [
8704, 1296, 8579, 5601, 398, 8498, 8513, 72, 73, 383, 1276, 8514, 87,
78, 79, 1280, 210, 7450, 83, 8869, 8745, 581, 77, 88, 8516, 90,
8704, 1296, 8579, 5601, 398, 8498, 8513, 72, 73, 383, 1276, 8514, 87, 78,
79, 1280, 210, 7450, 83, 8869, 8745, 581, 77, 88, 8516, 90,
],
// ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz
small: [
592, 113, 596, 112, 477, 607, 387, 613, 305, 638, 670, 645, 623, 117,
111, 100, 98, 633, 115, 647, 110, 652, 653, 120, 654, 122,
592, 113, 596, 112, 477, 607, 387, 613, 305, 638, 670, 645, 623, 117, 111,
100, 98, 633, 115, 647, 110, 652, 653, 120, 654, 122,
],
};

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

@ -37,8 +37,7 @@ describe('Linkify', () => {
it('leaves existing links alone', () => {
const wrapper = shallow(
<Linkify>
more <a href='https://example.com'>pontoon.mozilla.org</a>{' '}
content
more <a href='https://example.com'>pontoon.mozilla.org</a> content
</Linkify>,
);
const links = wrapper.find('a');

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

@ -8,11 +8,7 @@ export default function SkeletonLoader(props) {
const list = Array.from(Array(itemCount).keys());
return (
<ul
className={`skeleton-loader entities ${
firstLoad ? null : 'scroll'
}`}
>
<ul className={`skeleton-loader entities ${firstLoad ? null : 'scroll'}`}>
{list.map((i) => {
const classes = `entity missing ${
i === 0 && firstLoad ? 'selected' : null

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

@ -57,9 +57,7 @@ export function get(code: string) {
const locale = {
...data,
direction: data.direction.toLowerCase(),
cldrPlurals: data.cldrPlurals
.split(',')
.map((i) => parseInt(i, 10)),
cldrPlurals: data.cldrPlurals.split(',').map((i) => parseInt(i, 10)),
};
dispatch(receive(locale));
};

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

@ -27,9 +27,7 @@ describe('getPluralExamples', () => {
const res = getPluralExamples(locale);
const expected = { 0: 1, 1: 2 };
expect(res).toEqual(expected);
expect(spy).toHaveBeenCalledWith(
'Unable to generate plural examples.',
);
expect(spy).toHaveBeenCalledWith('Unable to generate plural examples.');
} finally {
spy.mockRestore();
}

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

@ -9,9 +9,7 @@ export type NotificationType =
export type NotificationMessage = {
readonly type: NotificationType;
readonly content:
| string
| React.ReactElement<React.ComponentProps<any>, any>;
readonly content: string | React.ReactElement<React.ComponentProps<any>, any>;
readonly key?: string;
};

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

@ -15,9 +15,7 @@ describe('<NotificationPanel>', () => {
};
it('returns an empty element when there is no notification', () => {
const wrapper = shallow(
<NotificationPanel notification={EMPTY_NOTIF} />,
);
const wrapper = shallow(<NotificationPanel notification={EMPTY_NOTIF} />);
expect(wrapper.children()).toHaveLength(1);
expect(wrapper.find('span').text()).toEqual('');
});
@ -31,9 +29,7 @@ describe('<NotificationPanel>', () => {
jest.useFakeTimers();
// Create a NotificationPanel with no message.
const wrapper = shallow(
<NotificationPanel notification={EMPTY_NOTIF} />,
);
const wrapper = shallow(<NotificationPanel notification={EMPTY_NOTIF} />);
expect(wrapper.find('span').text()).toEqual('');

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

@ -61,10 +61,7 @@ export default class NotificationPanel extends React.Component<Props, State> {
const notif = notification.message;
return (
<div
className={'notification-panel' + hideClass}
onClick={this.hide}
>
<div className={'notification-panel' + hideClass} onClick={this.hide}>
{!notif ? (
<span />
) : (

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

@ -157,9 +157,7 @@ const messages: Record<string, NotificationMessage> = {
},
COMMENT_ADDED: {
content: (
<Localized id='notification--comment-added'>
Comment added
</Localized>
<Localized id='notification--comment-added'>Comment added</Localized>
),
type: 'info',
},

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

@ -7,9 +7,7 @@ describe('<WithPlaceablesNoLeadingSpace>', () => {
it('matches newlines in a string', () => {
const content = 'Hello\nworld';
const wrapper = shallow(
<WithPlaceablesNoLeadingSpace>
{content}
</WithPlaceablesNoLeadingSpace>,
<WithPlaceablesNoLeadingSpace>{content}</WithPlaceablesNoLeadingSpace>,
);
expect(wrapper.find('mark')).toHaveLength(1);
@ -19,9 +17,7 @@ describe('<WithPlaceablesNoLeadingSpace>', () => {
it('does not match spaces at the beginning of a string', () => {
const content = ' Hello world';
const wrapper = shallow(
<WithPlaceablesNoLeadingSpace>
{content}
</WithPlaceablesNoLeadingSpace>,
<WithPlaceablesNoLeadingSpace>{content}</WithPlaceablesNoLeadingSpace>,
);
expect(wrapper.text()).toEqual(content);

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

@ -16,10 +16,7 @@ const altAttribute = {
rule: /(alt=".*?")/i as RegExp,
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-altAttribute'
attrs={{ title: true }}
>
<Localized id='placeable-parser-altAttribute' attrs={{ title: true }}>
<mark
className='placeable'
title="'alt' attribute inside XML tag"

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

@ -18,10 +18,7 @@ const camelCaseString = {
matchIndex: 0,
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-camelCaseString'
attrs={{ title: true }}
>
<Localized id='placeable-parser-camelCaseString' attrs={{ title: true }}>
<mark className='placeable' title='Camel case string' dir='ltr'>
{x}
</mark>

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

@ -18,10 +18,7 @@ const emailPattern = {
matchIndex: 0,
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-emailPattern'
attrs={{ title: true }}
>
<Localized id='placeable-parser-emailPattern' attrs={{ title: true }}>
<mark className='placeable' title='Email' dir='ltr'>
{x}
</mark>

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

@ -8,10 +8,7 @@ const escapeSequence = {
rule: '\\',
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-escapeSequence'
attrs={{ title: true }}
>
<Localized id='placeable-parser-escapeSequence' attrs={{ title: true }}>
<mark className='placeable' title='Escape sequence' dir='ltr'>
{x}
</mark>

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

@ -18,10 +18,7 @@ const filePattern = {
matchIndex: 2,
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-filePattern'
attrs={{ title: true }}
>
<Localized id='placeable-parser-filePattern' attrs={{ title: true }}>
<mark className='placeable' title='File location' dir='ltr'>
{x}
</mark>

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

@ -16,10 +16,7 @@ const fluentFunction = {
rule: /({ ?[A-W0-9\-_]+[^}]* ?})/ as RegExp,
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-fluentFunction'
attrs={{ title: true }}
>
<Localized id='placeable-parser-fluentFunction' attrs={{ title: true }}>
<mark className='placeable' title='Fluent function' dir='ltr'>
{x}
</mark>

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

@ -8,10 +8,7 @@ import fluentParametrizedTerm from './fluentParametrizedTerm';
describe('fluentParametrizedTerm', () => {
each([
['{-brand(case: "test")}', 'Hello {-brand(case: "test")}'],
[
'{ -brand(case: "what ever") }',
'Hello { -brand(case: "what ever") }',
],
['{ -brand(case: "what ever") }', 'Hello { -brand(case: "what ever") }'],
[
'{ -brand-name(foo-bar: "now that\'s a value!") }',
'Hello { -brand-name(foo-bar: "now that\'s a value!") }',

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

@ -21,11 +21,7 @@ const fluentParametrizedTerm = {
id='placeable-parser-fluentParametrizedTerm'
attrs={{ title: true }}
>
<mark
className='placeable'
title='Fluent parametrized term'
dir='ltr'
>
<mark className='placeable' title='Fluent parametrized term' dir='ltr'>
{x}
</mark>
</Localized>

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

@ -18,11 +18,7 @@ describe('fluentString', () => {
});
each([
[
'{ "hello!" }',
'{ "world!" }',
'Hello { "hello!" } from { "world!" }',
],
['{ "hello!" }', '{ "world!" }', 'Hello { "hello!" } from { "world!" }'],
]).it('marks `%s` and `%s` in `%s`', (mark1, mark2, content) => {
const Marker = createMarker([fluentString]);
const wrapper = shallow(<Marker>{content}</Marker>);

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

@ -15,15 +15,8 @@ const fluentString = {
rule: /({ ?"[^}]*" ?})/ as RegExp,
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-fluentString'
attrs={{ title: true }}
>
<mark
className='placeable'
title='Fluent string expression'
dir='ltr'
>
<Localized id='placeable-parser-fluentString' attrs={{ title: true }}>
<mark className='placeable' title='Fluent string expression' dir='ltr'>
{x}
</mark>
</Localized>

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

@ -16,10 +16,7 @@ const jsonPlaceholder = {
rule: /(\$[A-Z0-9_]+\$)/ as RegExp,
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-jsonPlaceholder'
attrs={{ title: true }}
>
<Localized id='placeable-parser-jsonPlaceholder' attrs={{ title: true }}>
<mark className='placeable' title='JSON placeholder' dir='ltr'>
{x}
</mark>

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

@ -12,10 +12,7 @@ const leadingSpace = {
rule: /(^ +)/ as RegExp,
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-leadingSpace'
attrs={{ title: true }}
>
<Localized id='placeable-parser-leadingSpace' attrs={{ title: true }}>
<mark className='placeable' title='Leading space' dir='ltr'>
{x}
</mark>

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

@ -8,10 +8,7 @@ const multipleSpaces = {
rule: /( +)/ as RegExp,
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-multipleSpaces'
attrs={{ title: true }}
>
<Localized id='placeable-parser-multipleSpaces' attrs={{ title: true }}>
<mark
className='placeable'
title='Multiple spaces'

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

@ -12,11 +12,7 @@ const narrowNonBreakingSpace = {
id='placeable-parser-narrowNonBreakingSpace'
attrs={{ title: true }}
>
<mark
className='placeable'
title='Narrow non-breaking space'
dir='ltr'
>
<mark className='placeable' title='Narrow non-breaking space' dir='ltr'>
{x}
</mark>
</Localized>

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

@ -8,10 +8,7 @@ const newlineCharacter = {
rule: '\n',
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-newlineCharacter'
attrs={{ title: true }}
>
<Localized id='placeable-parser-newlineCharacter' attrs={{ title: true }}>
<mark
className='placeable'
title='Newline character'

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

@ -8,10 +8,7 @@ const newlineEscape = {
rule: '\\n',
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-newlineEscape'
attrs={{ title: true }}
>
<Localized id='placeable-parser-newlineEscape' attrs={{ title: true }}>
<mark className='placeable' title='Escaped newline' dir='ltr'>
{x}
</mark>

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

@ -8,15 +8,8 @@ const nonBreakingSpace = {
rule: '\u00A0',
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-nonBreakingSpace'
attrs={{ title: true }}
>
<mark
className='placeable'
title='Non-breaking space'
dir='ltr'
>
<Localized id='placeable-parser-nonBreakingSpace' attrs={{ title: true }}>
<mark className='placeable' title='Non-breaking space' dir='ltr'>
{x}
</mark>
</Localized>

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

@ -14,10 +14,7 @@ const nsisVariable = {
matchIndex: 2,
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-nsisVariable'
attrs={{ title: true }}
>
<Localized id='placeable-parser-nsisVariable' attrs={{ title: true }}>
<mark className='placeable' title='NSIS variable' dir='ltr'>
{x}
</mark>

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

@ -19,10 +19,7 @@ const numberString = {
matchIndex: 0,
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-numberString'
attrs={{ title: true }}
>
<Localized id='placeable-parser-numberString' attrs={{ title: true }}>
<mark className='placeable' title='Number' dir='ltr'>
{x}
</mark>

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

@ -17,15 +17,8 @@ const optionPattern = {
matchIndex: 0,
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-optionPattern'
attrs={{ title: true }}
>
<mark
className='placeable'
title='Command line option'
dir='ltr'
>
<Localized id='placeable-parser-optionPattern' attrs={{ title: true }}>
<mark className='placeable' title='Command line option' dir='ltr'>
{x}
</mark>
</Localized>

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

@ -27,10 +27,7 @@ const punctuation = {
matchIndex: 0,
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-punctuation'
attrs={{ title: true }}
>
<Localized id='placeable-parser-punctuation' attrs={{ title: true }}>
<mark className='placeable' title='Punctuation' dir='ltr'>
{x}
</mark>

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

@ -17,11 +17,7 @@ const pythonFormatNamedString = {
id='placeable-parser-pythonFormatNamedString'
attrs={{ title: true }}
>
<mark
className='placeable'
title='Python format string'
dir='ltr'
>
<mark className='placeable' title='Python format string' dir='ltr'>
{x}
</mark>
</Localized>

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

@ -21,11 +21,7 @@ const pythonFormatString = {
id='placeable-parser-pythonFormatString'
attrs={{ title: true }}
>
<mark
className='placeable'
title='Python format string'
dir='ltr'
>
<mark className='placeable' title='Python format string' dir='ltr'>
{x}
</mark>
</Localized>

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

@ -28,10 +28,7 @@ const qtFormatting = {
matchIndex: 0,
tag: (x: string): React.ReactElement<React.ElementType> => {
return (
<Localized
id='placeable-parser-qtFormatting'
attrs={{ title: true }}
>
<Localized id='placeable-parser-qtFormatting' attrs={{ title: true }}>
<mark
className='placeable'
title='Qt string formatting variable'

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше