add hover class to :hover related css rules
This commit is contained in:
@@ -17,11 +17,36 @@ export function resetId() {
|
||||
_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 {
|
||||
try {
|
||||
const rules = s.rules || s.cssRules;
|
||||
return rules
|
||||
? Array.from(rules).reduce((prev, cur) => (prev += cur.cssText), '')
|
||||
? Array.from(rules).reduce(
|
||||
(prev, cur) => (prev += addHoverClass(cur.cssText)),
|
||||
'',
|
||||
)
|
||||
: null;
|
||||
} catch (error) {
|
||||
return null;
|
||||
@@ -152,6 +177,9 @@ function serializeNode(n: Node, doc: Document): serializedNode | false {
|
||||
if (parentTagName === 'SCRIPT') {
|
||||
textContent = 'SCRIPT_PLACEHOLDER';
|
||||
}
|
||||
if (parentTagName === 'STYLE') {
|
||||
textContent = addHoverClass(textContent || '');
|
||||
}
|
||||
return {
|
||||
type: NodeType.Text,
|
||||
textContent: textContent || '',
|
||||
|
||||
@@ -88,6 +88,29 @@ exports[`[html file]: form-fields.html 1`] = `
|
||||
</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`] = `
|
||||
"<!DOCTYPE html><html xmlns=\\"http://www.w3.org/1999/xhtml\\" lang=\\"en\\"><head>
|
||||
<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 { expect } from 'chai';
|
||||
import { absoluteToStylesheet } from '../src/snapshot';
|
||||
import { absoluteToStylesheet, addHoverClass } from '../src/snapshot';
|
||||
|
||||
describe('absolute url to stylesheet', () => {
|
||||
const href = 'http://localhost/css/style.css';
|
||||
@@ -29,3 +29,43 @@ describe('absolute url to stylesheet', () => {
|
||||
).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