Skip to content

Commit

Permalink
Add ignore:["delay"] to time-min-milliseconds (#4743)
Browse files Browse the repository at this point in the history
  • Loading branch information
srawlins committed May 12, 2020
1 parent a09fd82 commit f40a7a1
Show file tree
Hide file tree
Showing 3 changed files with 255 additions and 17 deletions.
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 @@ -64,7 +64,10 @@ testRule({
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 @@ -103,7 +106,10 @@ testRule({
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 @@ -119,7 +125,7 @@ testRule({
},
{
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 @@ -217,10 +223,22 @@ testRule({
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 @@ -271,10 +289,157 @@ testRule({
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

0 comments on commit f40a7a1

Please sign in to comment.