add hover class to :hover related css rules
This commit is contained in:
@@ -17,11 +17,36 @@ export function resetId() {
|
|||||||
_id = 1;
|
_id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CSS_RULE = /(([#|\.]{0,1}[a-z0-9\[\]=:]+[\s|,]*)+)\s(\{[\s\S]?[^}]*})/;
|
||||||
|
const CSS_RULE_GLOBAL = /(([#|\.]{0,1}[a-z0-9\[\]=:]+[\s|,]*)+)\s(\{[\s\S]?[^}]*})/g;
|
||||||
|
const HOVER_SELECTOR = /([^\\]):hover/g;
|
||||||
|
export function addHoverClass(cssText: string): string {
|
||||||
|
const matches = cssText.match(CSS_RULE_GLOBAL) || [];
|
||||||
|
for (const match of matches) {
|
||||||
|
const [, selectorText = '', , rules = ''] = match.match(CSS_RULE) || [];
|
||||||
|
const selectors = selectorText
|
||||||
|
.split(',')
|
||||||
|
.map(selector => selector.trim())
|
||||||
|
.map(selector => {
|
||||||
|
if (HOVER_SELECTOR.test(selector)) {
|
||||||
|
const newSelector = selector.replace(HOVER_SELECTOR, '$1.\\:hover');
|
||||||
|
selector += `, ${newSelector}`;
|
||||||
|
}
|
||||||
|
return selector;
|
||||||
|
});
|
||||||
|
cssText = cssText.replace(match, selectors.join(', ') + ' ' + rules);
|
||||||
|
}
|
||||||
|
return cssText;
|
||||||
|
}
|
||||||
|
|
||||||
function getCssRulesString(s: CSSStyleSheet): string | null {
|
function getCssRulesString(s: CSSStyleSheet): string | null {
|
||||||
try {
|
try {
|
||||||
const rules = s.rules || s.cssRules;
|
const rules = s.rules || s.cssRules;
|
||||||
return rules
|
return rules
|
||||||
? Array.from(rules).reduce((prev, cur) => (prev += cur.cssText), '')
|
? Array.from(rules).reduce(
|
||||||
|
(prev, cur) => (prev += addHoverClass(cur.cssText)),
|
||||||
|
'',
|
||||||
|
)
|
||||||
: null;
|
: null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return null;
|
return null;
|
||||||
@@ -152,6 +177,9 @@ function serializeNode(n: Node, doc: Document): serializedNode | false {
|
|||||||
if (parentTagName === 'SCRIPT') {
|
if (parentTagName === 'SCRIPT') {
|
||||||
textContent = 'SCRIPT_PLACEHOLDER';
|
textContent = 'SCRIPT_PLACEHOLDER';
|
||||||
}
|
}
|
||||||
|
if (parentTagName === 'STYLE') {
|
||||||
|
textContent = addHoverClass(textContent || '');
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
type: NodeType.Text,
|
type: NodeType.Text,
|
||||||
textContent: textContent || '',
|
textContent: textContent || '',
|
||||||
|
|||||||
@@ -88,6 +88,29 @@ exports[`[html file]: form-fields.html 1`] = `
|
|||||||
</body></html>"
|
</body></html>"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`[html file]: hover.html 1`] = `
|
||||||
|
"<!DOCTYPE html><html xmlns=\\"http://www.w3.org/1999/xhtml\\" lang=\\"en\\"><head>
|
||||||
|
<meta charset=\\"UTF-8\\" />
|
||||||
|
<meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1.0\\" />
|
||||||
|
<meta http-equiv=\\"X-UA-Compatible\\" content=\\"ie=edge\\" />
|
||||||
|
<title>hover selector</title>
|
||||||
|
<style> div:hover, div.\\\\:hover {
|
||||||
|
background: orange;
|
||||||
|
} div:hover::after, div.\\\\:hover::after {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 100%;
|
||||||
|
content: 'dropdown';
|
||||||
|
width: 100px;
|
||||||
|
height: 200px;
|
||||||
|
background: lightblue;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head><body>
|
||||||
|
<div>hover me</div>
|
||||||
|
</body></html>"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`[html file]: iframe.html 1`] = `
|
exports[`[html file]: iframe.html 1`] = `
|
||||||
"<!DOCTYPE html><html xmlns=\\"http://www.w3.org/1999/xhtml\\" lang=\\"en\\"><head>
|
"<!DOCTYPE html><html xmlns=\\"http://www.w3.org/1999/xhtml\\" lang=\\"en\\"><head>
|
||||||
<meta charset=\\"UTF-8\\" />
|
<meta charset=\\"UTF-8\\" />
|
||||||
|
|||||||
31
test/html/hover.html
Normal file
31
test/html/hover.html
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>hover selector</title>
|
||||||
|
<style>
|
||||||
|
|
||||||
|
div:hover {
|
||||||
|
background: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
div:hover::after {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 100%;
|
||||||
|
content: 'dropdown';
|
||||||
|
width: 100px;
|
||||||
|
height: 200px;
|
||||||
|
background: lightblue;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div>hover me</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'mocha';
|
import 'mocha';
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { absoluteToStylesheet } from '../src/snapshot';
|
import { absoluteToStylesheet, addHoverClass } from '../src/snapshot';
|
||||||
|
|
||||||
describe('absolute url to stylesheet', () => {
|
describe('absolute url to stylesheet', () => {
|
||||||
const href = 'http://localhost/css/style.css';
|
const href = 'http://localhost/css/style.css';
|
||||||
@@ -29,3 +29,43 @@ describe('absolute url to stylesheet', () => {
|
|||||||
).to.equal(`url('http://localhost/a.jpg')`);
|
).to.equal(`url('http://localhost/a.jpg')`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('add hover class to hover selector related rules', () => {
|
||||||
|
it('will do nothing to css text without :hover', () => {
|
||||||
|
const cssText = 'body { color: white }';
|
||||||
|
expect(addHoverClass(cssText)).to.equal(cssText);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can add hover class to css text', () => {
|
||||||
|
const cssText = '.a:hover { color: white }';
|
||||||
|
expect(addHoverClass(cssText)).to.equal(
|
||||||
|
'.a:hover, .a.\\:hover { color: white }',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can add hover class when there is multi selector', () => {
|
||||||
|
const cssText = '.a, .b:hover, .c { color: white }';
|
||||||
|
expect(addHoverClass(cssText)).to.equal(
|
||||||
|
'.a, .b:hover, .b.\\:hover, .c { color: white }',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can add hover class when :hover is not the end of selector', () => {
|
||||||
|
const cssText = 'div:hover::after { color: white }';
|
||||||
|
expect(addHoverClass(cssText)).to.equal(
|
||||||
|
'div:hover::after, div.\\:hover::after { color: white }',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can add hover class when the selector has multi :hover', () => {
|
||||||
|
const cssText = 'a:hover b:hover { color: white }';
|
||||||
|
expect(addHoverClass(cssText)).to.equal(
|
||||||
|
'a:hover b:hover, a.\\:hover b.\\:hover { color: white }',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('will ignore :hover in css value', () => {
|
||||||
|
const cssText = '.a::after { content: ":hover" }';
|
||||||
|
expect(addHoverClass(cssText)).to.equal(cssText);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user