From 4b96abf7b4fe2e2c1916b41d0c88b959dca42ea8 Mon Sep 17 00:00:00 2001 From: Brandon Scott Date: Fri, 4 Nov 2022 15:23:48 -0400 Subject: [PATCH] Add support for selectors/pseudo classes (#119) Co-authored-by: Matt Shwery --- README.md | 50 ++++++++++------- package.json | 7 +++ src/box.tsx | 4 +- src/cache.ts | 8 +-- src/enhance-props.ts | 55 +++++++++++------- src/enhancers/background.ts | 18 +++--- src/enhancers/border-radius.ts | 8 +-- src/enhancers/borders.ts | 34 +++++------ src/enhancers/box-shadow.ts | 2 +- src/enhancers/dimensions.ts | 12 ++-- src/enhancers/flex.ts | 34 +++++------ src/enhancers/grid.ts | 42 +++++++------- src/enhancers/interaction.ts | 8 +-- src/enhancers/layout.ts | 10 ++-- src/enhancers/list.ts | 8 +-- src/enhancers/opacity.ts | 2 +- src/enhancers/outline.ts | 4 +- src/enhancers/overflow.ts | 4 +- src/enhancers/position.ts | 10 ++-- src/enhancers/resize.ts | 4 +- src/enhancers/spacing.ts | 16 +++--- src/enhancers/text.ts | 36 ++++++------ src/enhancers/transform.ts | 4 +- src/enhancers/transition.ts | 10 ++-- src/get-class-name.ts | 4 +- src/get-css.ts | 24 ++++---- src/types/box-types.ts | 3 + src/types/enhancers.ts | 13 ++++- test/enhance-props.ts | 1 + test/get-class-name.ts | 24 ++++---- test/get-css.ts | 16 ++++++ test/snapshots/box.tsx.md | 5 +- test/snapshots/box.tsx.snap | Bin 4810 -> 4830 bytes tools/all-properties-component.tsx | 5 ++ tools/story.tsx | 87 ++++++++++++++++++++++------- 35 files changed, 343 insertions(+), 229 deletions(-) diff --git a/README.md b/README.md index c8fbf53..8d05be9 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,10 @@ npm install --save ui-box ## Usage ```jsx -import Box from "ui-box"; +import Box from 'ui-box' function Button(props) { - return ; + return } function Example() { @@ -38,7 +38,7 @@ function Example() { - ); + ) } ``` @@ -74,6 +74,18 @@ Type: `string` The className prop you know and love. Internally it gets enhanced with additional class names for the CSS properties you specify. +##### selectors + +Type: `object` + +This prop allows you to define selectors and custom styles to apply when the selector condition is met. This can be used to create richer element interactions, such as hover or focus states, without the use of another css-in-js library. + +```tsx + + Hello world + +``` + ##### CSS properties All of these CSS properties are supported. You can pass either a string or a number (which gets converted to a `px` value). The shorthand properties with repeated values only accept a single value, e.g. `margin="10px"` works but `margin="10px 20px"` does not. You can use the x/y props (e.g. `marginX`/`marginY`) to achieve the same thing. @@ -299,8 +311,8 @@ These enhancer groups are also exported. They're all objects with `{ propTypes, By default `ui-box` uses `ub-` as the classname prefix before all ui-box generated classnames. You can alter this by using `setClassNamePrefix('whatever-you-want-')`. Note that the delimiter is included in the prefix... this is to support backwards compatibility with the old classnames (< v3), which you can achieve using something like this: ```js -import { setClassNamePrefix } from "ui-box"; -setClassNamePrefix("📦"); +import { setClassNamePrefix } from 'ui-box' +setClassNamePrefix('📦') ``` ### Safe `href`s @@ -308,10 +320,10 @@ setClassNamePrefix("📦"); By default `ui-box` ensures that urls use safe protocols when passed to an element. We built this functionality into `ui-box` to protect the end users of the products you are building. You can opt-out of this by using `configureSafeHref({enabled?: boolean, origin?: string})`. This allows you to configure which protocols are acceptable (`http:`, `https:`, `mailto:`, `tel:`, and `data:`) and that the correct `rel` values are added (`noopener`, `noreferrer`(for external links)). ```js -import { configureSafeHref } from "ui-box"; +import { configureSafeHref } from 'ui-box' configureSafeHref({ - enabled: true, // the default behavior -}); + enabled: true // the default behavior +}) ``` ```js @@ -337,19 +349,15 @@ To render the styles on the server side just use [`ReactDOMServer.renderToString For example: ```js -"use strict"; -const React = require("react"); -const ReactDOMServer = require("react-dom/server"); -const { default: Box, extractStyles } = require("."); +'use strict' +const React = require('react') +const ReactDOMServer = require('react-dom/server') +const { default: Box, extractStyles } = require('.') -const element = React.createElement( - Box, - { margin: "10px", color: "red" }, - "hi" -); +const element = React.createElement(Box, { margin: '10px', color: 'red' }, 'hi') -const html = ReactDOMServer.renderToString(element); -const { styles, cache } = extractStyles(); +const html = ReactDOMServer.renderToString(element) +const { styles, cache } = extractStyles() const page = ` @@ -370,8 +378,8 @@ const page = ` -`; -console.log(page); +` +console.log(page) ``` ## Development diff --git a/package.json b/package.json index f23fb50..9f6d612 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,13 @@ "webpack": "^4.30.0", "xo": "^0.24.0" }, + "prettier": { + "semi": false, + "singleQuote": true, + "printWidth": 120, + "tabWidth": 2, + "useTabs": false + }, "xo": { "parser": "@typescript-eslint/parser", "extends": [ diff --git a/src/box.tsx b/src/box.tsx index 25bedbf..ded4c4b 100644 --- a/src/box.tsx +++ b/src/box.tsx @@ -1,11 +1,11 @@ -import React, { forwardRef } from 'react' +import React from 'react' import PropTypes from 'prop-types' import { BoxProps } from './types/box-types' import { propTypes } from './enhancers' import enhanceProps from './enhance-props' import { extractAnchorProps, getUseSafeHref } from './utils/safeHref' -const Box = forwardRef(({ is, children, allowUnsafeHref, ...props }: BoxProps, ref: React.Ref) => { +const Box = React.forwardRef(({ is, children, allowUnsafeHref, ...props }: BoxProps, ref: React.Ref) => { // Convert the CSS props to class names (and inject the styles) const {className, enhancedProps: parsedProps} = enhanceProps(props) diff --git a/src/cache.ts b/src/cache.ts index 0d640f2..48a3b54 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -3,11 +3,11 @@ import {BoxPropValue} from './types/enhancers' type CacheValue = BoxPropValue let cache = new Map() -export function get(property: string, value: CacheValue) { - return cache.get(property + value) +export function get(property: string, value: CacheValue, selectorHead = '') { + return cache.get(selectorHead + property + value) } -export function set(property: string, value: CacheValue | object, className: string) { +export function set(property: string, value: CacheValue | object, className: string, selectorHead = '') { if (process.env.NODE_ENV !== 'production') { const valueType = typeof value if ( @@ -22,7 +22,7 @@ export function set(property: string, value: CacheValue | object, className: str } } - cache.set(property + value, className) + cache.set(selectorHead + property + value, className) } export function entries() { diff --git a/src/enhance-props.ts b/src/enhance-props.ts index 5a8630b..4d5487f 100644 --- a/src/enhance-props.ts +++ b/src/enhance-props.ts @@ -1,9 +1,9 @@ -import {propEnhancers} from './enhancers' +import { propEnhancers } from './enhancers' import expandAliases from './expand-aliases' import * as cache from './cache' import * as styles from './styles' -import {Without} from './types/box-types' -import {EnhancerProps} from './types/enhancers' +import { Without } from './types/box-types' +import { EnhancerProps } from './types/enhancers' type PreservedProps = Without, keyof EnhancerProps> @@ -12,45 +12,58 @@ interface EnhancedPropsResult { enhancedProps: PreservedProps } +function noAnd(s: string): string { + return s.replace(/&/g, '') +} + /** * Converts the CSS props to class names and inserts the styles. */ -export default function enhanceProps(rawProps: EnhancerProps & React.ComponentPropsWithoutRef): EnhancedPropsResult { - const propsMap = expandAliases(rawProps) +export default function enhanceProps( + props: EnhancerProps & React.ComponentPropsWithoutRef, + selectorHead = '' +): EnhancedPropsResult { + const propsMap = expandAliases(props) const preservedProps: PreservedProps = {} - let className = rawProps.className || '' + let className: string = props.className || '' - for (const [propName, propValue] of propsMap) { - const cachedClassName = cache.get(propName, propValue) - if (cachedClassName) { - className = `${className} ${cachedClassName}` + for (const [property, value] of propsMap) { + if (value && typeof value === 'object') { + const prop = property === 'selectors' ? '' : property + const parsed = enhanceProps(value, noAnd(selectorHead + prop)) + className = `${className} ${parsed.className}` + continue + } + + const enhancer = propEnhancers[property] + if (!enhancer) { + // Pass through native props. e.g: disabled, value, type + preservedProps[property] = value continue } - const enhancer = propEnhancers[propName] // Skip false boolean enhancers. e.g: `clearfix={false}` // Also allows omitting props via overriding with `null` (i.e: neutralising props) - if ( - enhancer && - (propValue === null || propValue === undefined || propValue === false) - ) { + if (value === null || value === undefined || value === false) { continue - } else if (!enhancer) { - // Pass through native props. e.g: disabled, value, type - preservedProps[propName] = propValue + } + + const cachedClassName = cache.get(property, value, selectorHead) + if (cachedClassName) { + className = `${className} ${cachedClassName}` continue } - const newCss = enhancer(propValue) + const newCss = enhancer(value, selectorHead) // Allow enhancers to return null for invalid values if (newCss) { styles.add(newCss.styles) - cache.set(propName, propValue, newCss.className) + cache.set(property, value, newCss.className, selectorHead) className = `${className} ${newCss.className}` } } className = className.trim() - return {className, enhancedProps: preservedProps} + return { className, enhancedProps: preservedProps } } diff --git a/src/enhancers/background.ts b/src/enhancers/background.ts index 3f675ba..f8b8c82 100644 --- a/src/enhancers/background.ts +++ b/src/enhancers/background.ts @@ -69,13 +69,13 @@ const backgroundBlendMode = { } export const propEnhancers: PropEnhancers = { - background: (value: PropEnhancerValueType) => getCss(background, value), - backgroundBlendMode: (value: PropEnhancerValueType) => getCss(backgroundBlendMode, value), - backgroundClip: (value: PropEnhancerValueType) => getCss(backgroundClip, value), - backgroundColor: (value: PropEnhancerValueType) => getCss(backgroundColor, value), - backgroundImage: (value: PropEnhancerValueType) => getCss(backgroundImage, value), - backgroundOrigin: (value: PropEnhancerValueType) => getCss(backgroundOrigin, value), - backgroundPosition: (value: PropEnhancerValueType) => getCss(backgroundPosition, value), - backgroundRepeat: (value: PropEnhancerValueType) => getCss(backgroundRepeat, value), - backgroundSize: (value: PropEnhancerValueType) => getCss(backgroundSize, value) + background: (value: PropEnhancerValueType, selector: string) => getCss(background, value, selector), + backgroundBlendMode: (value: PropEnhancerValueType, selector: string) => getCss(backgroundBlendMode, value, selector), + backgroundClip: (value: PropEnhancerValueType, selector: string) => getCss(backgroundClip, value, selector), + backgroundColor: (value: PropEnhancerValueType, selector: string) => getCss(backgroundColor, value, selector), + backgroundImage: (value: PropEnhancerValueType, selector: string) => getCss(backgroundImage, value, selector), + backgroundOrigin: (value: PropEnhancerValueType, selector: string) => getCss(backgroundOrigin, value, selector), + backgroundPosition: (value: PropEnhancerValueType, selector: string) => getCss(backgroundPosition, value, selector), + backgroundRepeat: (value: PropEnhancerValueType, selector: string) => getCss(backgroundRepeat, value, selector), + backgroundSize: (value: PropEnhancerValueType, selector: string) => getCss(backgroundSize, value, selector) } diff --git a/src/enhancers/border-radius.ts b/src/enhancers/border-radius.ts index 62bf729..638d699 100644 --- a/src/enhancers/border-radius.ts +++ b/src/enhancers/border-radius.ts @@ -66,8 +66,8 @@ const borderBottomRightRadius = { } export const propEnhancers: PropEnhancers = { - borderBottomLeftRadius: (value: PropEnhancerValueType) => getCss(borderBottomLeftRadius, value), - borderBottomRightRadius: (value: PropEnhancerValueType) => getCss(borderBottomRightRadius, value), - borderTopLeftRadius: (value: PropEnhancerValueType) => getCss(borderTopLeftRadius, value), - borderTopRightRadius: (value: PropEnhancerValueType) => getCss(borderTopRightRadius, value) + borderBottomLeftRadius: (value: PropEnhancerValueType, selector: string) => getCss(borderBottomLeftRadius, value, selector), + borderBottomRightRadius: (value: PropEnhancerValueType, selector: string) => getCss(borderBottomRightRadius, value, selector), + borderTopLeftRadius: (value: PropEnhancerValueType, selector: string) => getCss(borderTopLeftRadius, value, selector), + borderTopRightRadius: (value: PropEnhancerValueType, selector: string) => getCss(borderTopRightRadius, value, selector) } diff --git a/src/enhancers/borders.ts b/src/enhancers/borders.ts index 6bd24ed..5d93511 100644 --- a/src/enhancers/borders.ts +++ b/src/enhancers/borders.ts @@ -48,7 +48,7 @@ export const propAliases = { ] } -export const propValidators: PropValidators = { } +export const propValidators: PropValidators = {} if (process.env.NODE_ENV !== 'production') { propValidators.borderColor = value => { @@ -160,20 +160,20 @@ const borderBottomWidth = { } export const propEnhancers: PropEnhancers = { - borderBottom: (value: PropEnhancerValueType) => getCss(borderBottom, value), - borderBottomColor: (value: PropEnhancerValueType) => getCss(borderBottomColor, value), - borderBottomStyle: (value: PropEnhancerValueType) => getCss(borderBottomStyle, value), - borderBottomWidth: (value: PropEnhancerValueType) => getCss(borderBottomWidth, value), - borderLeft: (value: PropEnhancerValueType) => getCss(borderLeft, value), - borderLeftColor: (value: PropEnhancerValueType) => getCss(borderLeftColor, value), - borderLeftStyle: (value: PropEnhancerValueType) => getCss(borderLeftStyle, value), - borderLeftWidth: (value: PropEnhancerValueType) => getCss(borderLeftWidth, value), - borderRight: (value: PropEnhancerValueType) => getCss(borderRight, value), - borderRightColor: (value: PropEnhancerValueType) => getCss(borderRightColor, value), - borderRightStyle: (value: PropEnhancerValueType) => getCss(borderRightStyle, value), - borderRightWidth: (value: PropEnhancerValueType) => getCss(borderRightWidth, value), - borderTop: (value: PropEnhancerValueType) => getCss(borderTop, value), - borderTopColor: (value: PropEnhancerValueType) => getCss(borderTopColor, value), - borderTopStyle: (value: PropEnhancerValueType) => getCss(borderTopStyle, value), - borderTopWidth: (value: PropEnhancerValueType) => getCss(borderTopWidth, value) + borderBottom: (value: PropEnhancerValueType, selector: string) => getCss(borderBottom, value, selector), + borderBottomColor: (value: PropEnhancerValueType, selector: string) => getCss(borderBottomColor, value, selector), + borderBottomStyle: (value: PropEnhancerValueType, selector: string) => getCss(borderBottomStyle, value, selector), + borderBottomWidth: (value: PropEnhancerValueType, selector: string) => getCss(borderBottomWidth, value, selector), + borderLeft: (value: PropEnhancerValueType, selector: string) => getCss(borderLeft, value, selector), + borderLeftColor: (value: PropEnhancerValueType, selector: string) => getCss(borderLeftColor, value, selector), + borderLeftStyle: (value: PropEnhancerValueType, selector: string) => getCss(borderLeftStyle, value, selector), + borderLeftWidth: (value: PropEnhancerValueType, selector: string) => getCss(borderLeftWidth, value, selector), + borderRight: (value: PropEnhancerValueType, selector: string) => getCss(borderRight, value, selector), + borderRightColor: (value: PropEnhancerValueType, selector: string) => getCss(borderRightColor, value, selector), + borderRightStyle: (value: PropEnhancerValueType, selector: string) => getCss(borderRightStyle, value, selector), + borderRightWidth: (value: PropEnhancerValueType, selector: string) => getCss(borderRightWidth, value, selector), + borderTop: (value: PropEnhancerValueType, selector: string) => getCss(borderTop, value, selector), + borderTopColor: (value: PropEnhancerValueType, selector: string) => getCss(borderTopColor, value, selector), + borderTopStyle: (value: PropEnhancerValueType, selector: string) => getCss(borderTopStyle, value, selector), + borderTopWidth: (value: PropEnhancerValueType, selector: string) => getCss(borderTopWidth, value, selector) } diff --git a/src/enhancers/box-shadow.ts b/src/enhancers/box-shadow.ts index 7a27936..a57e550 100644 --- a/src/enhancers/box-shadow.ts +++ b/src/enhancers/box-shadow.ts @@ -18,5 +18,5 @@ const boxShadow = { } export const propEnhancers: PropEnhancers = { - boxShadow: (value: PropEnhancerValueType) => getCss(boxShadow, value) + boxShadow: (value: PropEnhancerValueType, selector: string) => getCss(boxShadow, value, selector) } diff --git a/src/enhancers/dimensions.ts b/src/enhancers/dimensions.ts index 212bdf9..4014056 100644 --- a/src/enhancers/dimensions.ts +++ b/src/enhancers/dimensions.ts @@ -46,10 +46,10 @@ const maxHeight = { } export const propEnhancers: PropEnhancers = { - height: (value: PropEnhancerValueType) => getCss(height, value), - maxHeight: (value: PropEnhancerValueType) => getCss(maxHeight, value), - maxWidth: (value: PropEnhancerValueType) => getCss(maxWidth, value), - minHeight: (value: PropEnhancerValueType) => getCss(minHeight, value), - minWidth: (value: PropEnhancerValueType) => getCss(minWidth, value), - width: (value: PropEnhancerValueType) => getCss(width, value) + height: (value: PropEnhancerValueType, selector: string) => getCss(height, value, selector), + maxHeight: (value: PropEnhancerValueType, selector: string) => getCss(maxHeight, value, selector), + maxWidth: (value: PropEnhancerValueType, selector: string) => getCss(maxWidth, value, selector), + minHeight: (value: PropEnhancerValueType, selector: string) => getCss(minHeight, value, selector), + minWidth: (value: PropEnhancerValueType, selector: string) => getCss(minWidth, value, selector), + width: (value: PropEnhancerValueType, selector: string) => getCss(width, value, selector) } diff --git a/src/enhancers/flex.ts b/src/enhancers/flex.ts index 3575480..15af5c0 100644 --- a/src/enhancers/flex.ts +++ b/src/enhancers/flex.ts @@ -134,21 +134,21 @@ const placeSelf = { } export const propEnhancers: PropEnhancers = { - alignContent: (value: PropEnhancerValueType) => getCss(alignContent, value), - alignItems: (value: PropEnhancerValueType) => getCss(alignItems, value), - alignSelf: (value: PropEnhancerValueType) => getCss(alignSelf, value), - flex: (value: PropEnhancerValueType) => getCss(flex, value), - flexBasis: (value: PropEnhancerValueType) => getCss(flexBasis, value), - flexDirection: (value: PropEnhancerValueType) => getCss(flexDirection, value), - flexFlow: (value: PropEnhancerValueType) => getCss(flexFlow, value), - flexGrow: (value: PropEnhancerValueType) => getCss(flexGrow, value), - flexShrink: (value: PropEnhancerValueType) => getCss(flexShrink, value), - flexWrap: (value: PropEnhancerValueType) => getCss(flexWrap, value), - justifyContent: (value: PropEnhancerValueType) => getCss(justifyContent, value), - justifyItems: (value: PropEnhancerValueType) => getCss(justifyItems, value), - justifySelf: (value: PropEnhancerValueType) => getCss(justifySelf, value), - order: (value: PropEnhancerValueType) => getCss(order, value), - placeContent: (value: PropEnhancerValueType) => getCss(placeContent, value), - placeItems: (value: PropEnhancerValueType) => getCss(placeItems, value), - placeSelf: (value: PropEnhancerValueType) => getCss(placeSelf, value) + alignContent: (value: PropEnhancerValueType, selector: string) => getCss(alignContent, value, selector), + alignItems: (value: PropEnhancerValueType, selector: string) => getCss(alignItems, value, selector), + alignSelf: (value: PropEnhancerValueType, selector: string) => getCss(alignSelf, value, selector), + flex: (value: PropEnhancerValueType, selector: string) => getCss(flex, value, selector), + flexBasis: (value: PropEnhancerValueType, selector: string) => getCss(flexBasis, value, selector), + flexDirection: (value: PropEnhancerValueType, selector: string) => getCss(flexDirection, value, selector), + flexFlow: (value: PropEnhancerValueType, selector: string) => getCss(flexFlow, value, selector), + flexGrow: (value: PropEnhancerValueType, selector: string) => getCss(flexGrow, value, selector), + flexShrink: (value: PropEnhancerValueType, selector: string) => getCss(flexShrink, value, selector), + flexWrap: (value: PropEnhancerValueType, selector: string) => getCss(flexWrap, value, selector), + justifyContent: (value: PropEnhancerValueType, selector: string) => getCss(justifyContent, value, selector), + justifyItems: (value: PropEnhancerValueType, selector: string) => getCss(justifyItems, value, selector), + justifySelf: (value: PropEnhancerValueType, selector: string) => getCss(justifySelf, value, selector), + order: (value: PropEnhancerValueType, selector: string) => getCss(order, value, selector), + placeContent: (value: PropEnhancerValueType, selector: string) => getCss(placeContent, value, selector), + placeItems: (value: PropEnhancerValueType, selector: string) => getCss(placeItems, value, selector), + placeSelf: (value: PropEnhancerValueType, selector: string) => getCss(placeSelf, value, selector) } diff --git a/src/enhancers/grid.ts b/src/enhancers/grid.ts index 1999620..5ca15d1 100644 --- a/src/enhancers/grid.ts +++ b/src/enhancers/grid.ts @@ -153,25 +153,25 @@ const rowGap = { } export const propEnhancers: PropEnhancers = { - columnGap: (value: PropEnhancerValueType) => getCss(columnGap, value), - gap: (value: PropEnhancerValueType) => getCss(gap, value), - grid: (value: PropEnhancerValueType) => getCss(grid, value), - gridArea: (value: PropEnhancerValueType) => getCss(gridArea, value), - gridAutoColumns: (value: PropEnhancerValueType) => getCss(gridAutoColumns, value), - gridAutoFlow: (value: PropEnhancerValueType) => getCss(gridAutoFlow, value), - gridAutoRows: (value: PropEnhancerValueType) => getCss(gridAutoRows, value), - gridColumn: (value: PropEnhancerValueType) => getCss(gridColumn, value), - gridColumnEnd: (value: PropEnhancerValueType) => getCss(gridColumnEnd, value), - gridColumnGap: (value: PropEnhancerValueType) => getCss(gridColumnGap, value), - gridColumnStart: (value: PropEnhancerValueType) => getCss(gridColumnStart, value), - gridGap: (value: PropEnhancerValueType) => getCss(gridGap, value), - gridRow: (value: PropEnhancerValueType) => getCss(gridRow, value), - gridRowEnd: (value: PropEnhancerValueType) => getCss(gridRowEnd, value), - gridRowGap: (value: PropEnhancerValueType) => getCss(gridRowGap, value), - gridRowStart: (value: PropEnhancerValueType) => getCss(gridRowStart, value), - gridTemplate: (value: PropEnhancerValueType) => getCss(gridTemplate, value), - gridTemplateAreas: (value: PropEnhancerValueType) => getCss(gridTemplateAreas, value), - gridTemplateColumns: (value: PropEnhancerValueType) => getCss(gridTemplateColumns, value), - gridTemplateRows: (value: PropEnhancerValueType) => getCss(gridTemplateRows, value), - rowGap: (value: PropEnhancerValueType) => getCss(rowGap, value) + columnGap: (value: PropEnhancerValueType, selector: string) => getCss(columnGap, value, selector), + gap: (value: PropEnhancerValueType, selector: string) => getCss(gap, value, selector), + grid: (value: PropEnhancerValueType, selector: string) => getCss(grid, value, selector), + gridArea: (value: PropEnhancerValueType, selector: string) => getCss(gridArea, value, selector), + gridAutoColumns: (value: PropEnhancerValueType, selector: string) => getCss(gridAutoColumns, value, selector), + gridAutoFlow: (value: PropEnhancerValueType, selector: string) => getCss(gridAutoFlow, value, selector), + gridAutoRows: (value: PropEnhancerValueType, selector: string) => getCss(gridAutoRows, value, selector), + gridColumn: (value: PropEnhancerValueType, selector: string) => getCss(gridColumn, value, selector), + gridColumnEnd: (value: PropEnhancerValueType, selector: string) => getCss(gridColumnEnd, value, selector), + gridColumnGap: (value: PropEnhancerValueType, selector: string) => getCss(gridColumnGap, value, selector), + gridColumnStart: (value: PropEnhancerValueType, selector: string) => getCss(gridColumnStart, value, selector), + gridGap: (value: PropEnhancerValueType, selector: string) => getCss(gridGap, value, selector), + gridRow: (value: PropEnhancerValueType, selector: string) => getCss(gridRow, value, selector), + gridRowEnd: (value: PropEnhancerValueType, selector: string) => getCss(gridRowEnd, value, selector), + gridRowGap: (value: PropEnhancerValueType, selector: string) => getCss(gridRowGap, value, selector), + gridRowStart: (value: PropEnhancerValueType, selector: string) => getCss(gridRowStart, value, selector), + gridTemplate: (value: PropEnhancerValueType, selector: string) => getCss(gridTemplate, value, selector), + gridTemplateAreas: (value: PropEnhancerValueType, selector: string) => getCss(gridTemplateAreas, value, selector), + gridTemplateColumns: (value: PropEnhancerValueType, selector: string) => getCss(gridTemplateColumns, value, selector), + gridTemplateRows: (value: PropEnhancerValueType, selector: string) => getCss(gridTemplateRows, value, selector), + rowGap: (value: PropEnhancerValueType, selector: string) => getCss(rowGap, value, selector) } diff --git a/src/enhancers/interaction.ts b/src/enhancers/interaction.ts index ae5fdd6..d8074b4 100644 --- a/src/enhancers/interaction.ts +++ b/src/enhancers/interaction.ts @@ -39,8 +39,8 @@ const pointerEvents = { } export const propEnhancers: PropEnhancers = { - cursor: (value: PropEnhancerValueType) => getCss(cursor, value), - pointerEvents: (value: PropEnhancerValueType) => getCss(pointerEvents, value), - userSelect: (value: PropEnhancerValueType) => getCss(userSelect, value), - visibility: (value: PropEnhancerValueType) => getCss(visibility, value) + cursor: (value: PropEnhancerValueType, selector: string) => getCss(cursor, value, selector), + pointerEvents: (value: PropEnhancerValueType, selector: string) => getCss(pointerEvents, value, selector), + userSelect: (value: PropEnhancerValueType, selector: string) => getCss(userSelect, value, selector), + visibility: (value: PropEnhancerValueType, selector: string) => getCss(visibility, value, selector) } diff --git a/src/enhancers/layout.ts b/src/enhancers/layout.ts index 160927a..3c834ce 100644 --- a/src/enhancers/layout.ts +++ b/src/enhancers/layout.ts @@ -49,8 +49,8 @@ const boxSizing = { } export const propEnhancers: PropEnhancers = { - boxSizing: (value: PropEnhancerValueType) => getCss(boxSizing, value), - clear: (value: PropEnhancerValueType) => getCss(clear, value), + boxSizing: (value: PropEnhancerValueType, selector: string) => getCss(boxSizing, value, selector), + clear: (value: PropEnhancerValueType, selector: string) => getCss(clear, value, selector), clearfix: () => ({ className: `${getClassNamePrefix()}clearfix`, styles: ` @@ -60,7 +60,7 @@ export const propEnhancers: PropEnhancers = { content: ""; }` }), - display: (value: PropEnhancerValueType) => getCss(display, value), - float: (value: PropEnhancerValueType) => getCss(float, value), - zIndex: (value: PropEnhancerValueType) => getCss(zIndex, value) + display: (value: PropEnhancerValueType, selector: string) => getCss(display, value, selector), + float: (value: PropEnhancerValueType, selector: string) => getCss(float, value, selector), + zIndex: (value: PropEnhancerValueType, selector: string) => getCss(zIndex, value, selector) } diff --git a/src/enhancers/list.ts b/src/enhancers/list.ts index 0d17c73..c86ddc4 100644 --- a/src/enhancers/list.ts +++ b/src/enhancers/list.ts @@ -38,8 +38,8 @@ const listStylePosition = { } export const propEnhancers: PropEnhancers = { - listStyle: (value: PropEnhancerValueType) => getCss(listStyle, value), - listStyleType: (value: PropEnhancerValueType) => getCss(listStyleType, value), - listStyleImage: (value: PropEnhancerValueType) => getCss(listStyleImage, value), - listStylePosition: (value: PropEnhancerValueType) => getCss(listStylePosition, value) + listStyle: (value: PropEnhancerValueType, selector: string) => getCss(listStyle, value, selector), + listStyleType: (value: PropEnhancerValueType, selector: string) => getCss(listStyleType, value, selector), + listStyleImage: (value: PropEnhancerValueType, selector: string) => getCss(listStyleImage, value, selector), + listStylePosition: (value: PropEnhancerValueType, selector: string) => getCss(listStylePosition, value, selector) } diff --git a/src/enhancers/opacity.ts b/src/enhancers/opacity.ts index b218770..afd1f46 100644 --- a/src/enhancers/opacity.ts +++ b/src/enhancers/opacity.ts @@ -18,5 +18,5 @@ const opacity = { } export const propEnhancers: PropEnhancers = { - opacity: (value: PropEnhancerValueType) => getCss(opacity, value) + opacity: (value: PropEnhancerValueType, selector: string) => getCss(opacity, value, selector) } diff --git a/src/enhancers/outline.ts b/src/enhancers/outline.ts index deb7a69..0fe9685 100644 --- a/src/enhancers/outline.ts +++ b/src/enhancers/outline.ts @@ -8,7 +8,7 @@ export const propTypes: PropTypesMapping = { export const propAliases = {} -export const propValidators: PropValidators = { } +export const propValidators: PropValidators = {} const outline = { className: 'otln', @@ -18,5 +18,5 @@ const outline = { } export const propEnhancers: PropEnhancers = { - outline: (value: PropEnhancerValueType) => getCss(outline, value) + outline: (value: PropEnhancerValueType, selector: string) => getCss(outline, value, selector) } diff --git a/src/enhancers/overflow.ts b/src/enhancers/overflow.ts index 7b86f5e..b51fa40 100644 --- a/src/enhancers/overflow.ts +++ b/src/enhancers/overflow.ts @@ -28,6 +28,6 @@ const overflowX = { } export const propEnhancers: PropEnhancers = { - overflowX: (value: PropEnhancerValueType) => getCss(overflowX, value), - overflowY: (value: PropEnhancerValueType) => getCss(overflowY, value) + overflowX: (value: PropEnhancerValueType, selector: string) => getCss(overflowX, value, selector), + overflowY: (value: PropEnhancerValueType, selector: string) => getCss(overflowY, value, selector) } diff --git a/src/enhancers/position.ts b/src/enhancers/position.ts index 5c2bd03..a361a41 100644 --- a/src/enhancers/position.ts +++ b/src/enhancers/position.ts @@ -42,9 +42,9 @@ const left = { } export const propEnhancers: PropEnhancers = { - bottom: (value: PropEnhancerValueType) => getCss(bottom, value), - left: (value: PropEnhancerValueType) => getCss(left, value), - position: (value: PropEnhancerValueType) => getCss(position, value), - right: (value: PropEnhancerValueType) => getCss(right, value), - top: (value: PropEnhancerValueType) => getCss(top, value) + bottom: (value: PropEnhancerValueType, selector: string) => getCss(bottom, value, selector), + left: (value: PropEnhancerValueType, selector: string) => getCss(left, value, selector), + position: (value: PropEnhancerValueType, selector: string) => getCss(position, value, selector), + right: (value: PropEnhancerValueType, selector: string) => getCss(right, value, selector), + top: (value: PropEnhancerValueType, selector: string) => getCss(top, value, selector) } diff --git a/src/enhancers/resize.ts b/src/enhancers/resize.ts index 77f0ec2..39a5e07 100644 --- a/src/enhancers/resize.ts +++ b/src/enhancers/resize.ts @@ -8,7 +8,7 @@ export const propTypes: PropTypesMapping = { export const propAliases = {} -export const propValidators: PropValidators = { } +export const propValidators: PropValidators = {} const resize = { className: 'rsz', @@ -17,5 +17,5 @@ const resize = { } export const propEnhancers: PropEnhancers = { - resize: (value: PropEnhancerValueType) => getCss(resize, value) + resize: (value: PropEnhancerValueType, selector: string) => getCss(resize, value, selector) } diff --git a/src/enhancers/spacing.ts b/src/enhancers/spacing.ts index 10f3706..e5b47a2 100644 --- a/src/enhancers/spacing.ts +++ b/src/enhancers/spacing.ts @@ -117,12 +117,12 @@ const paddingLeft = { } export const propEnhancers: PropEnhancers = { - marginBottom: (value: PropEnhancerValueType) => getCss(marginBottom, value), - marginLeft: (value: PropEnhancerValueType) => getCss(marginLeft, value), - marginRight: (value: PropEnhancerValueType) => getCss(marginRight, value), - marginTop: (value: PropEnhancerValueType) => getCss(marginTop, value), - paddingBottom: (value: PropEnhancerValueType) => getCss(paddingBottom, value), - paddingLeft: (value: PropEnhancerValueType) => getCss(paddingLeft, value), - paddingRight: (value: PropEnhancerValueType) => getCss(paddingRight, value), - paddingTop: (value: PropEnhancerValueType) => getCss(paddingTop, value) + marginBottom: (value: PropEnhancerValueType, selector: string) => getCss(marginBottom, value, selector), + marginLeft: (value: PropEnhancerValueType, selector: string) => getCss(marginLeft, value, selector), + marginRight: (value: PropEnhancerValueType, selector: string) => getCss(marginRight, value, selector), + marginTop: (value: PropEnhancerValueType, selector: string) => getCss(marginTop, value, selector), + paddingBottom: (value: PropEnhancerValueType, selector: string) => getCss(paddingBottom, value, selector), + paddingLeft: (value: PropEnhancerValueType, selector: string) => getCss(paddingLeft, value, selector), + paddingRight: (value: PropEnhancerValueType, selector: string) => getCss(paddingRight, value, selector), + paddingTop: (value: PropEnhancerValueType, selector: string) => getCss(paddingTop, value, selector) } diff --git a/src/enhancers/text.ts b/src/enhancers/text.ts index de61568..3d36b94 100644 --- a/src/enhancers/text.ts +++ b/src/enhancers/text.ts @@ -132,22 +132,22 @@ const letterSpacing = { } export const propEnhancers: PropEnhancers = { - color: (value: PropEnhancerValueType) => getCss(color, value), - font: (value: PropEnhancerValueType) => getCss(font, value), - fontFamily: (value: PropEnhancerValueType) => getCss(fontFamily, value), - fontSize: (value: PropEnhancerValueType) => getCss(fontSize, value), - fontStyle: (value: PropEnhancerValueType) => getCss(fontStyle, value), - fontVariant: (value: PropEnhancerValueType) => getCss(fontVariant, value), - fontWeight: (value: PropEnhancerValueType) => getCss(fontWeight, value), - letterSpacing: (value: PropEnhancerValueType) => getCss(letterSpacing, value), - lineHeight: (value: PropEnhancerValueType) => getCss(lineHeight, value), - textAlign: (value: PropEnhancerValueType) => getCss(textAlign, value), - textDecoration: (value: PropEnhancerValueType) => getCss(textDecoration, value), - textOverflow: (value: PropEnhancerValueType) => getCss(textOverflow, value), - textShadow: (value: PropEnhancerValueType) => getCss(textShadow, value), - textTransform: (value: PropEnhancerValueType) => getCss(textTransform, value), - verticalAlign: (value: PropEnhancerValueType) => getCss(verticalAlign, value), - whiteSpace: (value: PropEnhancerValueType) => getCss(whiteSpace, value), - wordBreak: (value: PropEnhancerValueType) => getCss(wordBreak, value), - wordWrap: (value: PropEnhancerValueType) => getCss(wordWrap, value) + color: (value: PropEnhancerValueType, selector: string) => getCss(color, value, selector), + font: (value: PropEnhancerValueType, selector: string) => getCss(font, value, selector), + fontFamily: (value: PropEnhancerValueType, selector: string) => getCss(fontFamily, value, selector), + fontSize: (value: PropEnhancerValueType, selector: string) => getCss(fontSize, value, selector), + fontStyle: (value: PropEnhancerValueType, selector: string) => getCss(fontStyle, value, selector), + fontVariant: (value: PropEnhancerValueType, selector: string) => getCss(fontVariant, value, selector), + fontWeight: (value: PropEnhancerValueType, selector: string) => getCss(fontWeight, value, selector), + letterSpacing: (value: PropEnhancerValueType, selector: string) => getCss(letterSpacing, value, selector), + lineHeight: (value: PropEnhancerValueType, selector: string) => getCss(lineHeight, value, selector), + textAlign: (value: PropEnhancerValueType, selector: string) => getCss(textAlign, value, selector), + textDecoration: (value: PropEnhancerValueType, selector: string) => getCss(textDecoration, value, selector), + textOverflow: (value: PropEnhancerValueType, selector: string) => getCss(textOverflow, value, selector), + textShadow: (value: PropEnhancerValueType, selector: string) => getCss(textShadow, value, selector), + textTransform: (value: PropEnhancerValueType, selector: string) => getCss(textTransform, value, selector), + verticalAlign: (value: PropEnhancerValueType, selector: string) => getCss(verticalAlign, value, selector), + whiteSpace: (value: PropEnhancerValueType, selector: string) => getCss(whiteSpace, value, selector), + wordBreak: (value: PropEnhancerValueType, selector: string) => getCss(wordBreak, value, selector), + wordWrap: (value: PropEnhancerValueType, selector: string) => getCss(wordWrap, value, selector) } diff --git a/src/enhancers/transform.ts b/src/enhancers/transform.ts index b6a5bc5..6540ad3 100644 --- a/src/enhancers/transform.ts +++ b/src/enhancers/transform.ts @@ -25,6 +25,6 @@ const transformOrigin = { } export const propEnhancers: PropEnhancers = { - transform: (value: PropEnhancerValueType) => getCss(transform, value), - transformOrigin: (value: PropEnhancerValueType) => getCss(transformOrigin, value) + transform: (value: PropEnhancerValueType, selector: string) => getCss(transform, value, selector), + transformOrigin: (value: PropEnhancerValueType, selector: string) => getCss(transformOrigin, value, selector) } diff --git a/src/enhancers/transition.ts b/src/enhancers/transition.ts index e0b6563..e87b8ab 100644 --- a/src/enhancers/transition.ts +++ b/src/enhancers/transition.ts @@ -46,9 +46,9 @@ const transitionTimingFunction = { } export const propEnhancers: PropEnhancers = { - transition: (value: PropEnhancerValueType) => getCss(transition, value), - transitionDelay: (value: PropEnhancerValueType) => getCss(transitionDelay, value), - transitionDuration: (value: PropEnhancerValueType) => getCss(transitionDuration, value), - transitionProperty: (value: PropEnhancerValueType) => getCss(transitionProperty, value), - transitionTimingFunction: (value: PropEnhancerValueType) => getCss(transitionTimingFunction, value) + transition: (value: PropEnhancerValueType, selector: string) => getCss(transition, value, selector), + transitionDelay: (value: PropEnhancerValueType, selector: string) => getCss(transitionDelay, value, selector), + transitionDuration: (value: PropEnhancerValueType, selector: string) => getCss(transitionDuration, value, selector), + transitionProperty: (value: PropEnhancerValueType, selector: string) => getCss(transitionProperty, value, selector), + transitionTimingFunction: (value: PropEnhancerValueType, selector: string) => getCss(transitionTimingFunction, value, selector) } diff --git a/src/get-class-name.ts b/src/get-class-name.ts index 473617d..c7071b3 100644 --- a/src/get-class-name.ts +++ b/src/get-class-name.ts @@ -23,7 +23,7 @@ export interface PropertyInfo { /** * Generates the class name. */ -export default function getClassName(propertyInfo: PropertyInfo, value: string) { +export default function getClassName(propertyInfo: PropertyInfo, value: string, selector: string = '') { const { className, safeValue = false, // Value never contains unsafe characters. e.g: 10, hidden, border-box @@ -37,7 +37,7 @@ export default function getClassName(propertyInfo: PropertyInfo, value: string) /* Always hash values that contain a calc() because the operators get stripped which can result in class name collisions */ - } else if (complexValue || value.includes('calc(')) { + } else if (selector || complexValue || value.includes('calc(')) { valueKey = hash(value) } else if (safeValue) { valueKey = value diff --git a/src/get-css.ts b/src/get-css.ts index 7f397c7..1ceb366 100644 --- a/src/get-css.ts +++ b/src/get-css.ts @@ -1,12 +1,12 @@ -import prefixer, {Rule} from './prefixer' +import prefixer, { Rule } from './prefixer' import valueToString from './value-to-string' -import getClassName, {PropertyInfo} from './get-class-name' +import getClassName, { PropertyInfo } from './get-class-name' import { EnhancedProp } from './types/enhancers' /** * Generates the class name and styles. */ -export default function getCss(propertyInfo: PropertyInfo, value: string | number): EnhancedProp | null { +export default function getCss(propertyInfo: PropertyInfo, value: string | number, selector = ''): EnhancedProp | null { let rules: Rule[] // Protect against unexpected values @@ -24,30 +24,26 @@ export default function getCss(propertyInfo: PropertyInfo, value: string | numbe } const valueString = valueToString(value, propertyInfo.defaultUnit) - const className = getClassName(propertyInfo, valueString) + const className = getClassName(propertyInfo, valueString, selector) // Avoid running the prefixer when possible because it's slow if (propertyInfo.isPrefixed) { rules = prefixer(propertyInfo.jsName || '', valueString) } else { - rules = [{property: propertyInfo.cssName || '', value: valueString}] + rules = [{ property: propertyInfo.cssName || '', value: valueString }] } let styles: string if (process.env.NODE_ENV === 'production') { - const rulesString = rules - .map(rule => `${rule.property}:${rule.value}`) - .join(';') - styles = `.${className}{${rulesString}}` + const rulesString = rules.map(rule => `${rule.property}:${rule.value}`).join(';') + styles = `.${className}${selector}{${rulesString}}` } else { - const rulesString = rules - .map(rule => ` ${rule.property}: ${rule.value};`) - .join('\n') + const rulesString = rules.map(rule => ` ${rule.property}: ${rule.value};`).join('\n') styles = ` -.${className} { +.${className}${selector} { ${rulesString} }` } - return {className, styles} + return { className, styles } } diff --git a/src/types/box-types.ts b/src/types/box-types.ts index a205fd2..4fe7b1e 100644 --- a/src/types/box-types.ts +++ b/src/types/box-types.ts @@ -9,6 +9,9 @@ export { EnhancerProps } */ export type Without = Pick> +/** + * @see {@link https://github.com/emotion-js/emotion/blob/b4214b8757c7ede1db1688075251946b2082f9d1/packages/styled-base/types/helper.d.ts#L6-L8} + */ export type PropsOf< E extends keyof JSX.IntrinsicElements | React.JSXElementConstructor > = JSX.LibraryManagedAttributes> diff --git a/src/types/enhancers.ts b/src/types/enhancers.ts index a9ab753..e57037d 100644 --- a/src/types/enhancers.ts +++ b/src/types/enhancers.ts @@ -174,6 +174,17 @@ export type EnhancerProps = BoxCssProps & { * Utility property for easily adding clearfix styles to the element. */ clearfix?: boolean + + /** + * Map of selectors and styles to apply when the selector conditions are met + * @example + * + * Hello world + * + */ + selectors?: { + [selector: string]: BoxCssProps + } } export type PropEnhancerValueType = string | number @@ -187,7 +198,7 @@ export interface PropAliases { } export interface PropEnhancers { - [key: string]: (value: any) => EnhancedProp | null + [key: string]: (value: PropEnhancerValueType, selector: string) => EnhancedProp | null } export interface PropValidators { diff --git a/test/enhance-props.ts b/test/enhance-props.ts index 126dfec..d06f640 100644 --- a/test/enhance-props.ts +++ b/test/enhance-props.ts @@ -69,6 +69,7 @@ test.serial('passes through falsey non-enhancer props', t => { }) test.serial('handles invalid values', t => { + // @ts-ignore const {className, enhancedProps} = enhanceProps({minWidth: true}) t.is(className, '') t.deepEqual(enhancedProps, {}) diff --git a/test/get-class-name.ts b/test/get-class-name.ts index 5599507..08c6e30 100644 --- a/test/get-class-name.ts +++ b/test/get-class-name.ts @@ -2,44 +2,46 @@ import test from 'ava' import getClassName, { setClassNamePrefix, getClassNamePrefix } from '../src/get-class-name' test('supports inherit', t => { - t.is(getClassName({className: 'w'}, 'inherit'), 'ub-w_inherit') + t.is(getClassName({ className: 'w' }, 'inherit'), 'ub-w_inherit') }) test('supports initial', t => { - t.is(getClassName({className: 'w'}, 'initial'), 'ub-w_initial') + t.is(getClassName({ className: 'w' }, 'initial'), 'ub-w_initial') }) test('supports unset', t => { - t.is(getClassName({className: 'w'}, 'unset'), 'ub-w_unset') + t.is(getClassName({ className: 'w' }, 'unset'), 'ub-w_unset') }) test('safeValue does not transform value', t => { - const result = getClassName({className: 'w', safeValue: true}, '50.5%') + const result = getClassName({ className: 'w', safeValue: true }, '50.5%') t.is(result, 'ub-w_50.5%') }) test('hashes complex values', t => { const result = getClassName( - {className: 'bg', complexValue: true}, + { className: 'bg', complexValue: true }, 'url(https://s-media-cache-ak0.pinimg.com/736x/07/c3/45/07c345d0eca11d0bc97c894751ba1b46.jpg)' ) t.is(result, 'ub-bg_181xl07') }) test('removes all unsafe values by default', t => { - const result = getClassName({className: 'w'}, '50.5%') + const result = getClassName({ className: 'w' }, '50.5%') t.is(result, 'ub-w_50-5prcnt') }) test('always hashes values that contain a calc()', t => { - const result = getClassName( - {className: 'w', safeValue: true}, - 'calc(50% + 20px)' - ) + const result = getClassName({ className: 'w', safeValue: true }, 'calc(50% + 20px)') t.is(result, 'ub-w_1vuvdht') }) +test('always hashes when a selector is provided', t => { + const result = getClassName({ className: 'bg-clr' }, 'blue', '&:hover') + t.is(result, 'ub-bg-clr_nfznl2') +}) + test('allows custom classname prefixes', t => { setClassNamePrefix('📦') - t.is(getClassName({className: 'w'}, 'inherit'), '📦w_inherit') + t.is(getClassName({ className: 'w' }, 'inherit'), '📦w_inherit') }) diff --git a/test/get-css.ts b/test/get-css.ts index e0b1e4a..883a65d 100644 --- a/test/get-css.ts +++ b/test/get-css.ts @@ -74,3 +74,19 @@ test.serial('returns minified css in production', t => { '.ub-usr-slct_none{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}' ) }) + +test('appends selector when present', t => { + const propInfo = { + className: 'bg-clr', + cssName: 'background-color', + jsName: 'backgroundColor' + } + const result = getCss(propInfo, 'blue', ':hover') + t.deepEqual(result, { + className: 'ub-bg-clr_nfznl2', + styles: ` +.ub-bg-clr_nfznl2:hover { + background-color: blue; +}` + }) +}) diff --git a/test/snapshots/box.tsx.md b/test/snapshots/box.tsx.md index 95bb4b5..8602dbd 100644 --- a/test/snapshots/box.tsx.md +++ b/test/snapshots/box.tsx.md @@ -9,7 +9,7 @@ Generated by [AVA](https://ava.li). > DOM
@@ -360,6 +360,9 @@ Generated by [AVA](https://ava.li). .ub-row-gap_3px {␊ row-gap: 3px;␊ }␊ + .ub-bg-clr_nfznl2:hover {␊ + background-color: blue;␊ + }␊ .ub-txt-algn_right {␊ text-align: right;␊ }␊ diff --git a/test/snapshots/box.tsx.snap b/test/snapshots/box.tsx.snap index 9078d3f177bd1e245ffd4013d97217f515d5dbf8..09a9c4d853447f19d7a894bb397fb1539a830487 100644 GIT binary patch literal 4830 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 E07MybJ^%m! literal 4810 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 { resize="none" right={10} rowGap={3} + selectors={{ + '&:hover': { + backgroundColor: 'blue' + } + }} textAlign="right" textDecoration="underline dotted" textOverflow="ellipsis" diff --git a/tools/story.tsx b/tools/story.tsx index 1a621f9..12f8857 100644 --- a/tools/story.tsx +++ b/tools/story.tsx @@ -1,6 +1,6 @@ import React from 'react' -import {default as Box, configureSafeHref} from '../src' -import {storiesOf} from '@storybook/react' +import { default as Box, configureSafeHref } from '../src' +import { storiesOf } from '@storybook/react' import allPropertiesComponent from './all-properties-component' import { BoxProps } from '../src/types/box-types' @@ -11,7 +11,10 @@ const RedBox: React.FunctionComponent> = redBoxProps => ( const logRef = (ref: Element | null) => console.log(ref) const reactRef = React.createRef() -interface CustomProps { children: React.ReactNode } +interface CustomProps { + children: React.ReactNode +} + const CustomComp: React.FunctionComponent = props => { return (
@@ -41,11 +44,21 @@ storiesOf('Box', module) return ( Links - Internal Link - Same Origin Link - External Link - Javascript protocol Link - Overwride Safe Href + + Internal Link + + + Same Origin Link + + + External Link + + + Javascript protocol Link + + + Overwride Safe Href + ) }) @@ -56,11 +69,21 @@ storiesOf('Box', module) return ( Links - Internal Link - Same Origin Link - External Link - Javascript protocol Link - Overwride Safe Href + + Internal Link + + + Same Origin Link + + + External Link + + + Javascript protocol Link + + + Overwride Safe Href + ) }) @@ -148,12 +171,7 @@ storiesOf('Box', module) )) .add('spacing', () => ( - + )) .add('text', () => ( @@ -207,3 +225,34 @@ storiesOf('Box', module) )) + .add('selectors', () => { + return ( + + + Border style on hover + + + + No border style on hover - :not(:disabled) selector + + + + Red background on child hover + + + + + + ) + })