Skip to content
Merged
2 changes: 1 addition & 1 deletion playroom.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = {
title: 'Primer Components',
components: './src/index',
components: './playroom/components',
outputPath: './public',
snippets: './playroom/snippets.js',
frameComponent: './playroom/FrameComponent.js',
Expand Down
3 changes: 3 additions & 0 deletions playroom/components.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from '../src/index'
export {WithPseudoClass, StickerSheet, ComponentStickerSheet} from './util'
export {Check, Zap, X, Search, PrimitiveDot, Octoface, Person, Mail} from '@primer/octicons-react'
109 changes: 109 additions & 0 deletions playroom/util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React, {useRef, useEffect, useState} from 'react'
import {Flex, Text, Heading} from '../src'

const useAddRuleForPseudoClass = (ref, pseudoClass) => {
const [modifiedClassName, setModifiedClassName] = useState('')
useEffect(() => {
if (!ref.current) {
return
}
for (const className of ref.current.classList) {
const fullSelector = `.${className}${pseudoClass}`
const classNameWithSelector = fullSelector.replace(':', '-')
for (const ss of document.styleSheets) {
let newRule = ''
for (const rule of ss.cssRules) {
if (fullSelector === rule.selectorText) {
newRule = `${classNameWithSelector} { ${rule.style.cssText}}`
setModifiedClassName(classNameWithSelector.substring(1))
}
}
if (newRule) {
ss.insertRule(newRule, ss.cssRules.length)
break
}
}
}
}, [ref, pseudoClass])
return [modifiedClassName]
}

// Finds a CSS rule for the provided pseudo-class, adds a new
// class with the same css text to the stylesheet, and forwards
// that new class onto the child component.
export const WithPseudoClass = props => {
const ref = useRef()
const [modifiedClassName] = useAddRuleForPseudoClass(ref, props.pseudoClass)

return React.cloneElement(props.children, {
ref,
className: modifiedClassName
})
}

export const StickerSheet = ({children, title}) => {
return (
<Flex p={[3, 4]} flexDirection="column">
<Heading>{title}</Heading>
{children}
</Flex>
)
}

export const ComponentStickerSheet = ({props = [], pseudoClasses = [], title, children}) => {
const rows = [{}]
for (const prop of props) {
for (const value of prop.values) {
const modifiedProps = {}
modifiedProps[prop.prop] = value
rows.push(modifiedProps)
}
}
const propsModifiedChildren = rows.map(modifiedProps => {
const keys = Object.keys(modifiedProps)
const label = keys.length === 0 ? 'normal' : keys.map(key => `${key}: ${modifiedProps[key]}`).join(', ')
return (
<StickerSheetRow
label={label}
key={label}
childWrapper={child => React.cloneElement(child, {mr: 2, ...child.props, ...modifiedProps})}
>
{children}
</StickerSheetRow>
)
})
const pseudoClassChildren = pseudoClasses.map(pseudoClass => {
return (
<StickerSheetRow
label={pseudoClass}
key={pseudoClass}
childWrapper={child => (
<WithPseudoClass pseudoClass={pseudoClass}>
{React.cloneElement(child, {mr: 2, ...child.props})}
</WithPseudoClass>
)}
>
{children}
</StickerSheetRow>
)
})
return (
<Flex flexDirection="column" mb={2}>
<Text fontSize={3} fontWeight="bold">
{title}
</Text>
{propsModifiedChildren[0]}
{pseudoClassChildren}
{propsModifiedChildren.slice(1)}
</Flex>
)
}

const StickerSheetRow = ({children, label, childWrapper}) => {
return (
<Flex flexDirection="column">
<Text mr={2}>{label}</Text>
<Flex mb={2}>{React.Children.map(children, child => childWrapper(child))}</Flex>
</Flex>
)
}
1 change: 1 addition & 0 deletions src/ButtonStyles.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default css`
border-radius: ${get('radii.2')};
appearance: none;
text-decoration: none;
text-align: center;

&:hover {
// needed to override link styles
Expand Down
4 changes: 4 additions & 0 deletions src/__tests__/__snapshots__/Button.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ exports[`Button respects the "disabled" prop 1`] = `
appearance: none;
-webkit-text-decoration: none;
text-decoration: none;
text-align: center;
font-size: 14px;
color: #24292e;
background-color: #fafbfc;
Expand Down Expand Up @@ -92,6 +93,7 @@ exports[`ButtonDanger renders correct disabled styles 1`] = `
appearance: none;
-webkit-text-decoration: none;
text-decoration: none;
text-align: center;
font-size: 14px;
color: #cb2431;
border: 1px solid #e1e4e8;
Expand Down Expand Up @@ -167,6 +169,7 @@ exports[`ButtonOutline renders correct disabled styles 1`] = `
appearance: none;
-webkit-text-decoration: none;
text-decoration: none;
text-align: center;
font-size: 14px;
color: #0366d6;
border: 1px solid #e1e4e8;
Expand Down Expand Up @@ -242,6 +245,7 @@ exports[`ButtonPrimary renders correct disabled styles 1`] = `
appearance: none;
-webkit-text-decoration: none;
text-decoration: none;
text-align: center;
font-size: 14px;
color: #fff;
background-color: #2EA44F;
Expand Down
1 change: 1 addition & 0 deletions src/__tests__/__snapshots__/Dropdown.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ exports[`Dropdown.Button matches the snapshots 1`] = `
appearance: none;
-webkit-text-decoration: none;
text-decoration: none;
text-align: center;
font-size: 14px;
color: #24292e;
background-color: #fafbfc;
Expand Down