Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an ignore option to time-min-milliseconds #4743

Merged
merged 2 commits into from May 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions lib/rules/time-min-milliseconds/README.md
Expand Up @@ -60,3 +60,18 @@ a { transition: background-color 600ms linear; }
```css
a { animation-delay: 1s; }
```

## Optional secondary options

### `ignore: ["delay"]`

Ignore time values for an animation or transition delay.

For example, with a minimum of `200` milliseconds.

The following is _not_ considered a violation:

<!-- prettier-ignore -->
```css
a { animation-delay: 100ms; }
```
177 changes: 171 additions & 6 deletions lib/rules/time-min-milliseconds/__tests__/index.js
Expand Up @@ -65,7 +65,10 @@ testRule(rule, {
code: 'a { TRANSITION: foo 0.8s linear; }',
},
{
code: 'a { transition: foo 0.8s 200ms ease-in-out; }',
code: 'a { transition: foo 0.8s ease-in-out 200ms; }',
},
{
code: 'a { transition: foo 0.8s, bar 0.8s; }',
},
{
code: 'a { animation-delay: 0.2s; }',
Expand Down Expand Up @@ -104,7 +107,10 @@ testRule(rule, {
code: 'a { -webkit-animation: foo 0.8s linear; }',
},
{
code: 'a { animation: foo 0.8s 200ms ease-in-out; }',
code: 'a { animation: foo 0.8s ease-in-out 200ms; }',
},
{
code: 'a { animation: foo 0.8s, bar 0.8s; }',
},
{
description: 'negative values should be ignored',
Expand All @@ -120,7 +126,7 @@ testRule(rule, {
},
{
description: 'this test should fail',
code: 'a { animation: foo 0.8s -20ms ease-in-out; }',
code: 'a { animation: foo 0.8s ease-in-out -20ms; }',
},
{
description: '0ms is acceptable',
Expand Down Expand Up @@ -218,10 +224,22 @@ testRule(rule, {
column: 21,
},
{
code: 'a { transition: foo 0.8s 20ms ease-in-out; }',
code: 'a { transition: foo 0.8s ease-in-out 20ms; }',
message: messages.expected(MIN_VALUE),
line: 1,
column: 26,
column: 38,
},
{
code: 'a { transition: foo 0.8s ease 200ms, bar 0.8s ease 20ms; }',
message: messages.expected(MIN_VALUE),
line: 1,
column: 52,
},
{
code: 'a { transition: foo 0.8s ease, bar 30ms ease; }',
message: messages.expected(MIN_VALUE),
line: 1,
column: 36,
},
{
code: 'a { animation-delay: 0.006s; }',
Expand Down Expand Up @@ -272,10 +290,157 @@ testRule(rule, {
column: 28,
},
{
code: 'a { animation: foo 0.8s 20ms ease-in-out; }',
code: 'a { animation: foo 0.8s ease-in-out 20ms; }',
message: messages.expected(MIN_VALUE),
line: 1,
column: 37,
},
{
code: 'a { animation: foo 0.8s ease, bar 20ms ease; }',
message: messages.expected(MIN_VALUE),
line: 1,
column: 35,
},
{
code: 'a { animation: foo 0.8s ease 0.1s, bar 0.8s ease 0.01s; }',
message: messages.expected(MIN_VALUE),
line: 1,
column: 50,
},
],
});

testRule(rule, {
ruleName,
config: [MIN_VALUE, { ignore: ['delay'] }],

accept: [
{
code: 'a { transition-duration: 0.15s; }',
},
{
code: 'a { -webkit-transition-duration: 0.15s; }',
},
{
description: 'transition delay should be ignored',
code: 'a { transition-delay: 0.006s; }',
},
{
description: 'transition delay should be ignored',
code: 'a { -webkit-transition-delay: 0.006s; }',
},
{
description: 'transition delay should be ignored',
code: 'a { transition-delay: 0.1s; }',
},
{
description: 'transition delay should be ignored',
code: 'a { -webkit-transition-delay: 0.2s; }',
},
{
description: 'transition delay should be ignored',
code: 'a { TRANSITION-DELAY: 0.2s; }',
},
{
description: 'transition delay should be ignored',
code: 'a { TRANSITION-DELAY: 0.006s; }',
},
{
code: 'a { transition: foo 0.8s linear; }',
},
{
code: 'a { -webkit-transition: foo 0.8s linear; }',
},
{
code: 'a { animation-duration: 0.15s; }',
},
{
code: 'a { -webkit-animation-duration: 0.15s; }',
},
{
description: 'animation delay should be ignored',
code: 'a { animation-delay: 0.2s; }',
},
{
description: 'animation delay should be ignored',
code: 'a { -webkit-animation-delay: 0.2s; }',
},
{
description: 'animation delay should be ignored',
code: 'a { animation-delay: 200ms; }',
},
{
description: 'animation delay should be ignored',
code: 'a { animation-delay: 0.006s; }',
},
{
description: 'animation delay should be ignored',
code: 'a { -webkit-animation-delay: 0.006s; }',
},
{
description: 'animation delay should be ignored',
code: 'a { animation: foo 0.8s ease 0.2s, bar 0.7s ease 0.2s; }',
},
{
description: 'animation delay should be ignored',
code: 'a { animation: foo 0.8s ease 0.2s, bar 0.7s ease 20ms; }',
},
],

reject: [
{
code: 'a { transition-duration: 0.009s; }',
message: messages.expected(MIN_VALUE),
line: 1,
column: 26,
},
{
code: 'a { -webkit-transition-duration: 0.009s; }',
message: messages.expected(MIN_VALUE),
line: 1,
column: 34,
},
{
code: 'a { transition-duration: 80ms; }',
message: messages.expected(MIN_VALUE),
line: 1,
column: 26,
},
{
code: 'a { transition: foo 0.008s linear; }',
message: messages.expected(MIN_VALUE),
line: 1,
column: 21,
},
{
code: 'a { -webkit-transition: foo 0.008s linear; }',
message: messages.expected(MIN_VALUE),
line: 1,
column: 29,
},
{
code: 'a { animation-duration: 0.009s; }',
message: messages.expected(MIN_VALUE),
line: 1,
column: 25,
},
{
code: 'a { -webkit-animation-duration: 0.009s; }',
message: messages.expected(MIN_VALUE),
line: 1,
column: 33,
},
{
code: 'a { animation-duration: 80ms; }',
message: messages.expected(MIN_VALUE),
line: 1,
column: 25,
},
{
code: 'a { animation: foo 0.8s ease 0.2s, bar 20ms ease 0.2s; }',
message: messages.expected(MIN_VALUE),
line: 1,
column: 40,
},
],
});
80 changes: 69 additions & 11 deletions lib/rules/time-min-milliseconds/index.js
Expand Up @@ -5,6 +5,7 @@
const _ = require('lodash');
const declarationValueIndex = require('../../utils/declarationValueIndex');
const keywordSets = require('../../reference/keywordSets');
const optionsMatches = require('../../utils/optionsMatches');
const postcss = require('postcss');
const report = require('../../utils/report');
const ruleMessages = require('../../utils/ruleMessages');
Expand All @@ -17,12 +18,25 @@ const messages = ruleMessages(ruleName, {
expected: (time) => `Expected a minimum of ${time} milliseconds`,
});

function rule(minimum) {
const DELAY_PROPERTIES = ['animation-delay', 'transition-delay'];

function rule(minimum, options) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: minimum,
possible: _.isNumber,
});
const validOptions = validateOptions(
result,
ruleName,
{
actual: minimum,
possible: _.isNumber,
},
{
actual: options,
possible: {
ignore: ['delay'],
},
optional: true,
},
);

if (!validOptions) {
return;
Expand All @@ -31,21 +45,65 @@ function rule(minimum) {
root.walkDecls((decl) => {
const propertyName = postcss.vendor.unprefixed(decl.prop.toLowerCase());

if (keywordSets.longhandTimeProperties.has(propertyName) && !isAcceptableTime(decl.value)) {
if (
keywordSets.longhandTimeProperties.has(propertyName) &&
!isIgnoredProperty(propertyName) &&
!isAcceptableTime(decl.value)
) {
complain(decl);
}

if (keywordSets.shorthandTimeProperties.has(propertyName)) {
const valueList = postcss.list.space(decl.value);

for (const value of valueList) {
if (!isAcceptableTime(value)) {
complain(decl, decl.value.indexOf(value));
const valueListList = postcss.list.comma(decl.value);

for (const valueListString of valueListList) {
const valueList = postcss.list.space(valueListString);

if (optionsMatches(options, 'ignore', 'delay')) {
// Check only duration time values
const duration = getDuration(valueList);

if (!isAcceptableTime(duration)) {
complain(decl, decl.value.indexOf(duration));
}
} else {
// Check all time values
for (const value of valueList) {
if (!isAcceptableTime(value)) {
complain(decl, decl.value.indexOf(value));
}
}
}
}
}
});

/**
* Get the duration within an `animation` or `transition` shorthand property value.
*
* @param {Node[]} valueList
*
* @returns {Node}
*/
function getDuration(valueList) {
for (const value of valueList) {
const parsedTime = valueParser.unit(value);

if (!parsedTime) continue;

// The first numeric value in an animation shorthand is the duration.
return value;
}
}

function isIgnoredProperty(propertyName) {
if (optionsMatches(options, 'ignore', 'delay') && DELAY_PROPERTIES.includes(propertyName)) {
return true;
}

return false;
}

function isAcceptableTime(time) {
const parsedTime = valueParser.unit(time);

Expand Down