Skip to content

Commit 23fa9b1

Browse files
add arbitrary value support & tests (#10)
* feat: add arbitrary value support * test: add tests * merge * Match typography plugin test setup * Use matchUtilities * Simplify Co-authored-by: Jordan Pittman <[email protected]>
1 parent d2320ea commit 23fa9b1

File tree

4 files changed

+237
-11
lines changed

4 files changed

+237
-11
lines changed

Diff for: jest/customMatchers.js

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
const prettier = require('prettier')
2+
const { diff } = require('jest-diff')
3+
4+
function format(input) {
5+
return prettier.format(input, {
6+
parser: 'css',
7+
printWidth: 100,
8+
})
9+
}
10+
11+
expect.extend({
12+
// Compare two CSS strings with all whitespace removed
13+
// This is probably naive but it's fast and works well enough.
14+
toMatchCss(received, argument) {
15+
function stripped(str) {
16+
return str.replace(/\s/g, '').replace(/;/g, '')
17+
}
18+
19+
const options = {
20+
comment: 'stripped(received) === stripped(argument)',
21+
isNot: this.isNot,
22+
promise: this.promise,
23+
}
24+
25+
const pass = stripped(received) === stripped(argument)
26+
27+
const message = pass
28+
? () => {
29+
return (
30+
this.utils.matcherHint('toMatchCss', undefined, undefined, options) +
31+
'\n\n' +
32+
`Expected: not ${this.utils.printExpected(format(received))}\n` +
33+
`Received: ${this.utils.printReceived(format(argument))}`
34+
)
35+
}
36+
: () => {
37+
const actual = format(received)
38+
const expected = format(argument)
39+
40+
const diffString = diff(expected, actual, {
41+
expand: this.expand,
42+
})
43+
44+
return (
45+
this.utils.matcherHint('toMatchCss', undefined, undefined, options) +
46+
'\n\n' +
47+
(diffString && diffString.includes('- Expect')
48+
? `Difference:\n\n${diffString}`
49+
: `Expected: ${this.utils.printExpected(expected)}\n` +
50+
`Received: ${this.utils.printReceived(actual)}`)
51+
)
52+
}
53+
54+
return { actual: received, message, pass }
55+
},
56+
toIncludeCss(received, argument) {
57+
const options = {
58+
comment: 'stripped(received).includes(stripped(argument))',
59+
isNot: this.isNot,
60+
promise: this.promise,
61+
}
62+
63+
const actual = format(received)
64+
const expected = format(argument)
65+
66+
const pass = actual.includes(expected)
67+
68+
const message = pass
69+
? () => {
70+
return (
71+
this.utils.matcherHint('toIncludeCss', undefined, undefined, options) +
72+
'\n\n' +
73+
`Expected: not ${this.utils.printExpected(format(received))}\n` +
74+
`Received: ${this.utils.printReceived(format(argument))}`
75+
)
76+
}
77+
: () => {
78+
const diffString = diff(expected, actual, {
79+
expand: this.expand,
80+
})
81+
82+
return (
83+
this.utils.matcherHint('toIncludeCss', undefined, undefined, options) +
84+
'\n\n' +
85+
(diffString && diffString.includes('- Expect')
86+
? `Difference:\n\n${diffString}`
87+
: `Expected: ${this.utils.printExpected(expected)}\n` +
88+
`Received: ${this.utils.printReceived(actual)}`)
89+
)
90+
}
91+
92+
return { actual: received, message, pass }
93+
},
94+
})
95+
96+
expect.extend({
97+
// Compare two CSS strings with all whitespace removed
98+
// This is probably naive but it's fast and works well enough.
99+
toMatchFormattedCss(received, argument) {
100+
function format(input) {
101+
return prettier.format(input.replace(/\n/g, ''), {
102+
parser: 'css',
103+
printWidth: 100,
104+
})
105+
}
106+
const options = {
107+
comment: 'stripped(received) === stripped(argument)',
108+
isNot: this.isNot,
109+
promise: this.promise,
110+
}
111+
112+
let formattedReceived = format(received)
113+
let formattedArgument = format(argument)
114+
115+
const pass = formattedReceived === formattedArgument
116+
117+
const message = pass
118+
? () => {
119+
return (
120+
this.utils.matcherHint('toMatchCss', undefined, undefined, options) +
121+
'\n\n' +
122+
`Expected: not ${this.utils.printExpected(formattedReceived)}\n` +
123+
`Received: ${this.utils.printReceived(formattedArgument)}`
124+
)
125+
}
126+
: () => {
127+
const actual = formattedReceived
128+
const expected = formattedArgument
129+
130+
const diffString = diff(expected, actual, {
131+
expand: this.expand,
132+
})
133+
134+
return (
135+
this.utils.matcherHint('toMatchCss', undefined, undefined, options) +
136+
'\n\n' +
137+
(diffString && diffString.includes('- Expect')
138+
? `Difference:\n\n${diffString}`
139+
: `Expected: ${this.utils.printExpected(expected)}\n` +
140+
`Received: ${this.utils.printReceived(actual)}`)
141+
)
142+
}
143+
144+
return { actual: received, message, pass }
145+
},
146+
})

Diff for: package.json

+13
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,20 @@
1313
"singleQuote": true,
1414
"trailingComma": "es5"
1515
},
16+
"scripts": {
17+
"test": "jest"
18+
},
19+
"devDependencies": {
20+
"jest": "^27.4.4",
21+
"postcss": "^8.2.2",
22+
"prettier": "^2.5.1"
23+
},
1624
"peerDependencies": {
1725
"tailwindcss": ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1"
26+
},
27+
"jest": {
28+
"setupFilesAfterEnv": [
29+
"<rootDir>/jest/customMatchers.js"
30+
]
1831
}
1932
}

Diff for: src/index.js

+17-11
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,27 @@
11
const plugin = require('tailwindcss/plugin')
22

3+
const baseStyles = {
4+
overflow: 'hidden',
5+
display: '-webkit-box',
6+
'-webkit-box-orient': 'vertical',
7+
}
8+
39
const lineClamp = plugin(
4-
function ({ addUtilities, theme, variants, e }) {
10+
function ({ matchUtilities, addUtilities, theme, variants, e }) {
511
const values = theme('lineClamp')
612

13+
matchUtilities(
14+
{
15+
'line-clamp': (value) => ({
16+
...baseStyles,
17+
'-webkit-line-clamp': `${value}`,
18+
})
19+
},
20+
{ values }
21+
)
22+
723
addUtilities(
824
[
9-
Object.entries(values).map(([key, value]) => {
10-
return {
11-
[`.${e(`line-clamp-${key}`)}`]: {
12-
overflow: 'hidden',
13-
display: '-webkit-box',
14-
'-webkit-box-orient': 'vertical',
15-
'-webkit-line-clamp': `${value}`,
16-
},
17-
}
18-
}),
1925
{
2026
'.line-clamp-none': {
2127
'-webkit-line-clamp': 'unset',

Diff for: src/index.test.js

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const path = require('path')
2+
const postcss = require('postcss')
3+
const tailwindcss = require('tailwindcss')
4+
const lineClampPlugin = require('.')
5+
6+
function run(config, plugin = tailwindcss) {
7+
let { currentTestName } = expect.getState()
8+
config = {
9+
...{ plugins: [lineClampPlugin], corePlugins: { preflight: false } },
10+
...config,
11+
}
12+
13+
return postcss(plugin(config)).process('@tailwind utilities', {
14+
from: `${path.resolve(__filename)}?test=${currentTestName}`,
15+
})
16+
}
17+
18+
it('should add the `line-clamp-{n}` components', () => {
19+
const config = {
20+
content: [{ raw: String.raw`<div class="line-clamp-2 line-clamp-[33] line-clamp-[var(--line-clamp-variable)]"></div>` }],
21+
}
22+
23+
return run(config).then((result) => {
24+
expect(result.css).toMatchCss(String.raw`
25+
.line-clamp-2 {
26+
overflow: hidden;
27+
display: -webkit-box;
28+
-webkit-box-orient: vertical;
29+
-webkit-line-clamp: 2;
30+
}
31+
32+
.line-clamp-\[33\] {
33+
overflow: hidden;
34+
display: -webkit-box;
35+
-webkit-box-orient: vertical;
36+
-webkit-line-clamp: 33;
37+
}
38+
39+
.line-clamp-\[var\(--line-clamp-variable\)\] {
40+
overflow: hidden;
41+
display: -webkit-box;
42+
-webkit-box-orient: vertical;
43+
-webkit-line-clamp: var(--line-clamp-variable);
44+
}
45+
`)
46+
})
47+
})
48+
49+
it('should add the `line-clamp-none` utility', () => {
50+
const config = {
51+
content: [{ raw: String.raw`<div class="line-clamp-none"></div>` }],
52+
}
53+
54+
return run(config).then((result) => {
55+
expect(result.css).toMatchCss(String.raw`
56+
.line-clamp-none {
57+
-webkit-line-clamp: unset;
58+
}
59+
`)
60+
})
61+
})

0 commit comments

Comments
 (0)