Created
June 1, 2023 16:34
-
-
Save jhancock532/05b59514651558e14dd71ab7dc80f74a to your computer and use it in GitHub Desktop.
Storybook pseudo states addon code for preview.js to get nested classes working
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let pseudoStyleRules: any = { | |
':hover': [], | |
':visited': [], | |
}; | |
function removeMatchingSubstrings(str: string, patterns: string[]) { | |
const regex = new RegExp(patterns.join('|'), 'g'); | |
return str.replace(regex, ''); | |
} | |
function matchCSSSelectors(element: Element, rules: any[]): any { | |
const matchedSelectors = []; | |
for (let i = 0; i < element.children.length; i++) { | |
const child = element.children[i]; | |
const matches = rules.filter(rule => { | |
// Remove pseudo classes from selector | |
const selectorWithoutPseudoClass = removeMatchingSubstrings( | |
rule.selectorText, | |
Object.keys(pseudoStyleRules) | |
); | |
return child.matches(selectorWithoutPseudoClass); | |
}); | |
if (matches.length > 0) { | |
matchedSelectors.push(...matches); | |
} | |
// Recursively check child's children | |
matchedSelectors.push(...matchCSSSelectors(child, rules)); | |
} | |
return matchedSelectors; | |
} | |
document.addEventListener('DOMContentLoaded', function () { | |
const rules = []; | |
// Iterate over all the style sheets in the document | |
for (let i = 0; i < document.styleSheets.length; i++) { | |
const styleSheet = document.styleSheets[i]; | |
// Check if the style sheet is accessible | |
try { | |
styleSheet.cssRules; | |
} catch (e: any) { | |
if (e.name === 'SecurityError') { | |
console.log('The style sheet is not accessible'); | |
continue; | |
} | |
} | |
for (let j = 0; j < styleSheet.cssRules.length; j++) { | |
rules.push(styleSheet.cssRules[j]); | |
} | |
} | |
const filteredRules = rules.filter(rule => { | |
if (rule.type !== CSSRule.STYLE_RULE) { | |
return false; | |
} | |
return Object.keys(pseudoStyleRules).some(r => (rule as CSSStyleRule).selectorText.includes(r)); | |
}); | |
let componentContainer = document.getElementById('root'); | |
const rulesRelatedToComponent = matchCSSSelectors(componentContainer as Element, filteredRules); | |
for (let i = 0; i < rulesRelatedToComponent.length; i++) { | |
//console.log(rulesRelatedToComponent[i].selectorText); | |
// split the selector text by `,` | |
let splitSelectorTextRules = rulesRelatedToComponent[i].selectorText.split(','); | |
// for each selector text, | |
for (let j = 0; j < splitSelectorTextRules.length; j++) { | |
// find which pseudo state it is related to | |
let pseudoState = Object.keys(pseudoStyleRules).find(pseudoState => { | |
return splitSelectorTextRules[j].includes(pseudoState); | |
}); | |
// If a split rule does not include a pseudo state, skip it | |
if (!pseudoState) { | |
continue; | |
} | |
// remove the pseudo state from the selector text | |
let selectorWithoutPseudoState = removeMatchingSubstrings(splitSelectorTextRules[j], [ | |
pseudoState as string, | |
]); | |
// trim the whitespace from the selector text | |
selectorWithoutPseudoState = selectorWithoutPseudoState.trim(); | |
// append each split selector paired with the desired styles to the pseudoState dictionary | |
pseudoStyleRules[pseudoState as string].push({ | |
selector: selectorWithoutPseudoState, | |
style: rulesRelatedToComponent[i].style, | |
}); | |
} | |
} | |
console.log(pseudoStyleRules); | |
// for each pseudo state in the dictionary | |
for (let pseudoState in pseudoStyleRules) { | |
// create a new class | |
let pseudoStateClass = `pseudo-${pseudoState.replace(':', '')}`; | |
// create a new style element | |
let style = document.createElement('style'); | |
// for each style rule in the pseudo state | |
for (let i = 0; i < pseudoStyleRules[pseudoState].length; i++) { | |
// create a new style rule | |
let pseudoStateStyleRule = `.${pseudoStateClass} `; | |
// append the selector to the new style rule | |
pseudoStateStyleRule += `${pseudoStyleRules[pseudoState][i].selector} {`; | |
// for each style in the style rule | |
for (let j = 0; j < pseudoStyleRules[pseudoState][i].style.length; j++) { | |
// append the style to the new style rule | |
pseudoStateStyleRule += `${pseudoStyleRules[pseudoState][i].style[j]}: ${pseudoStyleRules[pseudoState][ | |
i | |
].style.getPropertyValue(pseudoStyleRules[pseudoState][i].style[j])};`; | |
} | |
// close the style rule | |
pseudoStateStyleRule += '}\n'; | |
// add the new style rule to the style element | |
style.innerHTML = pseudoStateStyleRule; | |
} | |
// append the style element to the document | |
document.head.appendChild(style); | |
// add the new class to the component container | |
componentContainer?.classList.add(pseudoStateClass); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment