From 336666aeff6edc3e730fea27751824d466004457 Mon Sep 17 00:00:00 2001 From: Spencer Date: Thu, 15 Aug 2019 11:56:45 -0700 Subject: [PATCH] add `@elastic/eslint-plugin-eui` package (#2218) * add `@elastic/eslint-plugin-eui` package * Check for all relevant component names * link to @elastic/eslint-plugin-eui readme from release docs * fix link to readme * add git repo and homepage to package.json --- .npmignore | 1 + package.json | 1 + packages/eslint-plugin/.npmignore | 1 + packages/eslint-plugin/README.md | 25 +++++ packages/eslint-plugin/index.js | 32 +++++++ packages/eslint-plugin/package.json | 13 +++ .../eslint-plugin/rules/href_or_on_click.js | 35 +++++++ .../rules/href_or_on_click.test.js | 95 +++++++++++++++++++ scripts/jest/config.json | 3 +- wiki/releasing-versions.md | 4 + yarn.lock | 5 + 11 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 packages/eslint-plugin/.npmignore create mode 100644 packages/eslint-plugin/README.md create mode 100644 packages/eslint-plugin/index.js create mode 100644 packages/eslint-plugin/package.json create mode 100644 packages/eslint-plugin/rules/href_or_on_click.js create mode 100644 packages/eslint-plugin/rules/href_or_on_click.test.js diff --git a/.npmignore b/.npmignore index cb76678baac..1538d294d5d 100644 --- a/.npmignore +++ b/.npmignore @@ -8,6 +8,7 @@ test/ src-docs/ src-framer/ packages/react-datepicker +packages/eslint-plugin .nvmrc # typescript output diff --git a/package.json b/package.json index b834384fe05..316ce7b836e 100644 --- a/package.json +++ b/package.json @@ -110,6 +110,7 @@ "cross-env": "^5.2.0", "css-loader": "^0.28.7", "cssnano": "^4.0.5", + "dedent": "^0.7.0", "dts-generator": "^2.1.0", "enzyme": "^3.10.0", "enzyme-adapter-react-16": "^1.14.0", diff --git a/packages/eslint-plugin/.npmignore b/packages/eslint-plugin/.npmignore new file mode 100644 index 00000000000..3b0aa630ba5 --- /dev/null +++ b/packages/eslint-plugin/.npmignore @@ -0,0 +1 @@ +rules/*.test.js diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md new file mode 100644 index 00000000000..0c1b45ab495 --- /dev/null +++ b/packages/eslint-plugin/README.md @@ -0,0 +1,25 @@ +# `@elastic/eslint-plugin-eui` + +This package contains an eslint plugin that enforces some default rules for using EUI. + +## Setup + +1. install `@elastic/eslint-plugin-eui` as a dev dependency +2. extend `plugin:@elastic/eui/recommended` in your eslint config + +## Rules + +### `@elastic/eui/href-or-on-click` + +`` should either be a button or a link, for a11y purposes. When given an `href` the button behaves as a link, otherwise an `onClick` handler is expected and it will behave as a button. + +In some cases it makes sense to disable this rule locally, such as when cmd+click should open the link in a new tab, but a standard click should use the `history.pushState()` API to change the URL without triggering a full page load. + +## Publishing + +This package is published separately from the rest of EUI, as required by eslint. The code is not transpiled, so make sure to use `require()` statements rather than `import`, and once the code is updated run: + +1. `npm version patch|minor|major` +2. commit version bump +3. `npm publish` in this directory +4. push the version bump upstream diff --git a/packages/eslint-plugin/index.js b/packages/eslint-plugin/index.js new file mode 100644 index 00000000000..e82fb4dfead --- /dev/null +++ b/packages/eslint-plugin/index.js @@ -0,0 +1,32 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + rules: { + 'href-or-on-click': require('./rules/href_or_on_click'), + }, + configs: { + recommended: { + plugins: ['@elastic/eslint-plugin-eui'], + rules: { + '@elastic/eui/href-or-on-click': 'error', + }, + }, + }, +}; diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json new file mode 100644 index 00000000000..6ffff9f7d68 --- /dev/null +++ b/packages/eslint-plugin/package.json @@ -0,0 +1,13 @@ +{ + "name": "@elastic/eslint-plugin-eui", + "version": "0.0.1", + "license": "Apache-2.0", + "repository": { + "type" : "git", + "url" : "https://github.com/elastic/eui.git" + }, + "homepage": "https://github.com/elastic/eui/blob/master/packages/eslint-plugin", + "peerDependencies": { + "eslint": "^5.0.0" + } +} diff --git a/packages/eslint-plugin/rules/href_or_on_click.js b/packages/eslint-plugin/rules/href_or_on_click.js new file mode 100644 index 00000000000..e0533fa9455 --- /dev/null +++ b/packages/eslint-plugin/rules/href_or_on_click.js @@ -0,0 +1,35 @@ +const componentNames = ['EuiButton', 'EuiButtonEmpty', 'EuiLink']; + +module.exports = { + meta: { + fixable: null, + }, + create(context) { + return { + JSXOpeningElement(node) { + if ( + node.name.type !== 'JSXIdentifier' || + !componentNames.includes(node.name.name) + ) { + return; + } + + const hasHref = node.attributes.some( + attr => attr.type === 'JSXAttribute' && attr.name.name === 'href' + ); + const hasOnClick = node.attributes.some( + attr => attr.type === 'JSXAttribute' && attr.name.name === 'onClick' + ); + + if (hasHref && hasOnClick) { + context.report( + node, + `<${ + node.name.name + }> accepts either \`href\` or \`onClick\`, not both.` + ); + } + }, + }; + }, +}; diff --git a/packages/eslint-plugin/rules/href_or_on_click.test.js b/packages/eslint-plugin/rules/href_or_on_click.test.js new file mode 100644 index 00000000000..820cddab37f --- /dev/null +++ b/packages/eslint-plugin/rules/href_or_on_click.test.js @@ -0,0 +1,95 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ + +const { RuleTester } = require('eslint'); +const rule = require('./href_or_on_click'); +const dedent = require('dedent'); + +const ruleTester = new RuleTester({ + parser: 'babel-eslint', + parserOptions: { + ecmaVersion: 2018, + }, +}); + +ruleTester.run('@elastic/eui/href-or-on-click', rule, { + valid: [ + { + code: dedent(` + module.export = () => ( + + ) + `), + }, + { + code: dedent(` + module.export = () => ( + + ) + `), + }, + { + code: dedent(` + module.export = () => ( + + ) + `), + }, + { + code: dedent(` + module.export = () => ( + + ) + `), + }, + { + code: dedent(` + module.export = () => ( + executeAction()} /> + ) + `), + }, + ], + + invalid: [ + { + code: dedent(` + module.export = () => ( + + ) + `), + + errors: [ + { + message: ' accepts either `href` or `onClick`, not both.', + }, + ], + }, + { + code: dedent(` + module.export = () => ( + + ) + `), + + errors: [ + { + message: + ' accepts either `href` or `onClick`, not both.', + }, + ], + }, + { + code: dedent(` + module.export = () => ( + + ) + `), + + errors: [ + { + message: ' accepts either `href` or `onClick`, not both.', + }, + ], + }, + ], +}); diff --git a/scripts/jest/config.json b/scripts/jest/config.json index 521a59f5116..c7f87a762d3 100644 --- a/scripts/jest/config.json +++ b/scripts/jest/config.json @@ -2,7 +2,8 @@ "rootDir": "../../", "roots": [ "/src/", - "/scripts/babel" + "/scripts/babel", + "/packages/eslint-plugin" ], "collectCoverageFrom": [ "src/components/**/*.js", diff --git a/wiki/releasing-versions.md b/wiki/releasing-versions.md index a37a9b5625a..d1ccd2b81ac 100644 --- a/wiki/releasing-versions.md +++ b/wiki/releasing-versions.md @@ -18,4 +18,8 @@ That's it. The latest changes were published to GitHub, a new `git` tag now exis _\* GitHub Pages sites are cached aggressively and can sometimes take a couple of minutes to update._ +## `@elastic/eslint-plugin-eui` + +For information on releasing the eslint plugin checkout the readme in [packages/eslint-plugin/README.md](../packages/eslint-plugin/README.md) + [docs]: https://elastic.github.io/eui/ diff --git a/yarn.lock b/yarn.lock index 4ea8d442f83..e7c0eb670cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4112,6 +4112,11 @@ decompress-response@^3.2.0: dependencies: mimic-response "^1.0.0" +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= + deep-eql@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df"