diff --git a/.documentation.json b/.documentation.json index 3b0f8597..68ca695c 100644 --- a/.documentation.json +++ b/.documentation.json @@ -23,6 +23,8 @@ "description": "Functions that help write nicer code. They're shorter, of course, but they also make it easy to pass in multiple variables without resorting to string concatenation." }, "animation", + "buttons", + "textInputs", { "name": "Helpers", "description": "Tiny helper functions that make your life easier." diff --git a/docs/docs/index.html b/docs/docs/index.html index b8535a23..34bc9022 100644 --- a/docs/docs/index.html +++ b/docs/docs/index.html @@ -155,6 +155,26 @@

polished

+
  • + buttons + + + +
  • + + +
  • + textInputs + + + +
  • + +
  • @@ -239,7 +259,7 @@

    -
    + src/mixins/clearFix.js @@ -325,7 +345,7 @@

    - + src/mixins/ellipsis.js @@ -414,7 +434,7 @@

    - + src/mixins/hiDPI.js @@ -506,7 +526,7 @@

    - + src/mixins/hideText.js @@ -581,7 +601,7 @@

    - + src/mixins/placeholder.js @@ -684,7 +704,7 @@

    - + src/mixins/retinaImage.js @@ -809,7 +829,7 @@

    - + src/mixins/selection.js @@ -908,7 +928,7 @@

    - + src/mixins/size.js @@ -1001,7 +1021,7 @@

    - + src/mixins/timingFunctions.js @@ -1084,7 +1104,7 @@

    - + src/mixins/wordWrap.js @@ -1185,7 +1205,7 @@

    - + src/shorthands/animation.js @@ -1271,6 +1291,198 @@

    + + + + + +
    + + +
    + +

    + buttons +

    + + + + src/shorthands/buttons.js + + +
    + + +

    Populates selectors that target all buttons. You can pass optional states to append to the selectors.

    + + +
    buttons(states: ...Array<State>)
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + states (...Array<State>) + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +  [buttons('active')]: {
    +    'border': 'none'
    +  }
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  > ${buttons('active')} {
    +    border: none;
    +  }
    +`
    +
    +// CSS in JS Output
    +
    + 'button:active,
    + 'input[type="button"]:active,
    + 'input[type=\"reset\"]:active,
    + 'input[type=\"submit\"]:active: {
    +  'border': 'none'
    +}
    + + + + + + + + +
    + + + + +
    + + +
    + +

    + textInputs +

    + + + + src/shorthands/textInputs.js + + +
    + + +

    Populates selectors that target all text inputs. You can pass optional states to append to the selectors.

    + + +
    textInputs(states: ...Array<State>)
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + states (...Array<State>) + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +  [textInputs('active')]: {
    +    'border': 'none'
    +  }
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  > ${textInputs('active')} {
    +    border: none;
    +  }
    +`
    +
    +// CSS in JS Output
    +
    + ''input[type="color"]:active,
    + 'input[type="date"]:active,
    + 'input[type="datetime"]:active,
    + 'input[type="datetime-local"]:active,
    + 'input[type="email"]:active,
    + 'input[type="month"]:active,
    + 'input[type="number"]:active,
    + 'input[type="password"]:active,
    + 'input[type="search"]:active,
    + 'input[type="tel"]:active,
    + 'input[type="text"]:active,
    + 'input[type="time"]:active,
    + 'input[type="url"]:active,
    + 'input[type="week"]:active,
    + input:not([type]):active,
    + textarea:active': {
    +  'border': 'none'
    +}
    + + + + + + + +
    @@ -1301,7 +1513,7 @@

    - + src/helpers/em.js @@ -1394,7 +1606,7 @@

    - + src/helpers/modularScale.js @@ -1497,7 +1709,7 @@

    - + src/helpers/rem.js @@ -1590,7 +1802,7 @@

    - + src/helpers/stripUnit.js diff --git a/docs/index.html b/docs/index.html index ec7efec3..34bc9022 100644 --- a/docs/index.html +++ b/docs/index.html @@ -10,9 +10,1878 @@ +
    +
    + +
    + + +
    + +

    + Mixins +

    + + +

    These are reusable chunks of styles. By extracting common ones into small utilities you avoid repetition and mistakes.

    + + +
    +
    + + + +
    + + +
    + +

    + clearFix +

    + + + + src/mixins/clearFix.js + + +
    + + +

    CSS to contain a float (credit to CSSMojo).

    + + +
    clearFix(parent: [string])
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + parent ([string] + = '&') + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +   ...clearFix(),
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  ${clearFix()}
    +`
    +
    +// CSS as JS Output
    +
    +'&::after': {
    +  'clear': 'both',
    +  'content': '',
    +  'display': 'table'
    +}
    + + + + + + + + +
    + + + + +
    + + +
    + +

    + ellipsis +

    + + + + src/mixins/ellipsis.js + + +
    + + +

    CSS to represent truncated text with an ellipsis.

    + + +
    ellipsis(width: [string])
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + width ([string] + = '100%') + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +  ...ellipsis(250px)
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  ${ellipsis(250px)}
    +
    +
    +// CSS as JS Output
    +
    +div: {
    +  'display': 'inline-block',
    +  'max-width': '250px',
    +  'overflow': 'hidden',
    +  'text-overflow': 'ellipsis',
    +  'white-space': 'nowrap',
    +  'word-wrap': 'normal'
    +}
    + + + + + + + + +
    + + + + +
    + + +
    + +

    + hiDPI +

    + + + + src/mixins/hiDPI.js + + +
    + + +

    Generates a media query to target HiDPI devices.

    + + +
    hiDPI(ratio: [number])
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + ratio ([number] + = 1.3) + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    + [hiDPI(1.5)]: {
    +   width: 200px;
    + }
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  ${hiDPI(1.5)} {
    +    width: 200px;
    +  }
    +`
    +
    +// CSS as JS Output
    +
    +'@media only screen and (-webkit-min-device-pixel-ratio: 1.5),
    + only screen and (min--moz-device-pixel-ratio: 1.5),
    + only screen and (-o-min-device-pixel-ratio: 1.5/1),
    + only screen and (min-resolution: 144dpi),
    + only screen and (min-resolution: 1.5dppx)': {
    +  'width': '200px',
    +}
    + + + + + + + + +
    + + + + +
    + + +
    + +

    + hideText +

    + + + + src/mixins/hideText.js + + +
    + + +

    CSS to hide text to show a background image in a SEO-Friendly.

    + + +
    hideText()
    + + + + + + + + + + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +  'background-image': 'url(logo.png)',
    +  ...hideText(),
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  background-image: url(logo.png);
    +  ${hideText()};
    +`
    +
    +// CSS as JS Output
    +
    +'div': {
    +  'background-image': 'url(logo.png)',
    +  'text-indent': '101%',
    +  'overflow': 'hidden',
    +  'white-space': 'nowrap',
    +}
    + + + + + + + + +
    + + + + +
    + + +
    + +

    + placeholder +

    + + + + src/mixins/placeholder.js + + +
    + + +

    CSS to style the selection psuedo-element.

    + + +
    placeholder(styles: Object, parent: [string])
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + styles (Object) + +
    + +
    + +
    +
    + parent ([string] + = '&') + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +  ...placeholder(styles)
    +}
    +
    +// styled-components usage
    +const div = styled.input`
    +   ${placeholder(css`styles`)}
    +`
    +
    +// CSS as JS Output
    +
    +'input': {
    +  '&:-moz-placeholder': {
    +    'color': 'blue',
    +  },
    +  '&:-ms-input-placeholder': {
    +    'color': 'blue',
    +  },
    +  '&::-moz-placeholder': {
    +    'color': 'blue',
    +  },
    +  '&::-webkit-input-placeholder': {
    +    'color': 'blue',
    +  },
    +},
    + + + + + + + + +
    + + + + +
    + + +
    + +

    + retinaImage +

    + + + + src/mixins/retinaImage.js + + +
    + + +

    The retina-image mixin is a helper to generate a retina background image and non-retina +background image. The retina background image will output to a HiDPI media query. The mixin uses +a _2x.png filename suffix by default.

    + + +
    retinaImage(filename: string, backgroundSize: string, extension: [string], retinaFilename: string, retinaSuffix: [string])
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + filename (string) + +
    + +
    + +
    +
    + backgroundSize (string) + +
    + +
    + +
    +
    + extension ([string] + = 'png') + +
    + +
    + +
    +
    + retinaFilename (string) + +
    + +
    + +
    +
    + retinaSuffix ([string] + = '_2x') + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    + ...retinaImage('my-img')
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  ${retinaImage('my-img')}
    +`
    +
    +// CSS as JS Output
    +div {
    +  backgroundImage: 'url(my-img.png)',
    +  '@media only screen and (-webkit-min-device-pixel-ratio: 1.3),
    +   only screen and (min--moz-device-pixel-ratio: 1.3),
    +   only screen and (-o-min-device-pixel-ratio: 1.3/1),
    +   only screen and (min-resolution: 144dpi),
    +   only screen and (min-resolution: 1.5dppx)': {
    +    backgroundImage: 'url(my-img_2x.png)',
    +  }
    +}
    + + + + + + + + +
    + + + + +
    + + +
    + +

    + selection +

    + + + + src/mixins/selection.js + + +
    + + +

    CSS to style the selection psuedo-element.

    + + +
    selection(styles: Object, parent: [string])
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + styles (Object) + +
    + +
    + +
    +
    + parent ([string] + = '') + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +  ...selection({
    +    'background': 'blue'
    +  }, 'section')
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  ${selection({'background': 'blue'}, 'section')}
    +`
    +
    +// CSS as JS Output
    +
    +'div': {
    +  'section::-moz-selection': {
    +    'background-color':'blue',
    +  },
    +  'section::selection': {
    +    'background-color': 'blue',
    +  }
    +}
    + + + + + + + + +
    + + + + +
    + + +
    + +

    + size +

    + + + + src/mixins/size.js + + +
    + + +

    Mixin to set the height and width properties in a single statement.

    + + +
    size(height: string, width: [string])
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + height (string) + +
    + +
    + +
    +
    + width ([string] + = height) + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +  ...size('300px', '250px')
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  ${size('300px', '250px')}
    +`
    +
    +// CSS as JS Output
    +
    +div {
    +  'height': '300px',
    +  'width': '250px',
    +}
    + + + + + + + + +
    + + + + +
    + + +
    + +

    + timingFunctions +

    + + + + src/mixins/timingFunctions.js + + +
    + + +

    String to represent commong easing functions as demonstrated here: (github.com/jaukia/easie).

    + + +
    timingFunctions(timingFunction: TimingFunctions)
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + timingFunction (TimingFunctions) + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +  'transition-timing-function': timingFunctions('easeInQuad')
    +}
    +
    +// styled-components usage
    + const div = styled.div`
    +  transition-timing-function: ${timingFunctions('easeInQuad')};
    +`
    +
    +// CSS as JS Output
    +
    +'div': {
    +  'transition-timing-function': 'cubic-bezier(0.550,  0.085, 0.680, 0.530)',
    +}
    + + + + + + + + +
    + + + + +
    + + +
    + +

    + wordWrap +

    + + + + src/mixins/wordWrap.js + + +
    + + +

    Provides an easy way to change the word-wrap property

    + + +
    wordWrap(wrap: [string])
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + wrap ([string] + = 'break-word') + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +  ...wordWrap('break-all')
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  ${wordWrap('break-all')}
    +
    +
    +// CSS as JS Output
    +
    +const styles = {
    +  overflow-wrap: 'break-all',
    +  word-wrap: 'break-all',
    +  word-break: 'break-all',
    +}
    + + + + + + + + +
    + + + + +
    + +

    + Shorthands +

    + + +

    Functions that help write nicer code. They're shorter, of course, but they also make it easy to pass in multiple variables without resorting to string concatenation.

    + + +
    +
    + + + +
    + + +
    + +

    + animation +

    + + + + src/shorthands/animation.js + + +
    + + +

    Shorthand for easily setting the animation property. Allows either multiple arrays with animations +or a single animation spread over the arguments.

    + + +
    animation(args: ...Array<(Array<AnimationProperty> | AnimationProperty)>)
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + args (...Array<(Array<AnimationProperty> | AnimationProperty)>) + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +  ...animation(['rotate', '1s', 'ease-in-out'], ['colorchange', '2s'])
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  ${animation(['rotate', '1s', 'ease-in-out'], ['colorchange', '2s'])}
    +`
    +
    +// CSS as JS Output
    +
    +div {
    +  'animation': 'rotate 1s ease-in-out, colorchange 2s'
    +}
    + + +
    // Styles as object usage
    +const styles = {
    +  ...animation('rotate', '1s', 'ease-in-out')
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  ${animation('rotate', '1s', 'ease-in-out')}
    +`
    +
    +// CSS as JS Output
    +
    +div {
    +  'animation': 'rotate 1s ease-in-out'
    +}
    + + + + + + + + +
    + + + + +
    + + +
    + +

    + buttons +

    -

    polished

    -
    + + + src/shorthands/buttons.js + + +
    + + +

    Populates selectors that target all buttons. You can pass optional states to append to the selectors.

    + + +
    buttons(states: ...Array<State>)
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + states (...Array<State>) + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +  [buttons('active')]: {
    +    'border': 'none'
    +  }
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  > ${buttons('active')} {
    +    border: none;
    +  }
    +`
    +
    +// CSS in JS Output
    +
    + 'button:active,
    + 'input[type="button"]:active,
    + 'input[type=\"reset\"]:active,
    + 'input[type=\"submit\"]:active: {
    +  'border': 'none'
    +}
    + + + + + + + + +
    + + + + +
    + + +
    + +

    + textInputs +

    + + + + src/shorthands/textInputs.js + + +
    + + +

    Populates selectors that target all text inputs. You can pass optional states to append to the selectors.

    + + +
    textInputs(states: ...Array<State>)
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + states (...Array<State>) + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +  [textInputs('active')]: {
    +    'border': 'none'
    +  }
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  > ${textInputs('active')} {
    +    border: none;
    +  }
    +`
    +
    +// CSS in JS Output
    +
    + ''input[type="color"]:active,
    + 'input[type="date"]:active,
    + 'input[type="datetime"]:active,
    + 'input[type="datetime-local"]:active,
    + 'input[type="email"]:active,
    + 'input[type="month"]:active,
    + 'input[type="number"]:active,
    + 'input[type="password"]:active,
    + 'input[type="search"]:active,
    + 'input[type="tel"]:active,
    + 'input[type="text"]:active,
    + 'input[type="time"]:active,
    + 'input[type="url"]:active,
    + 'input[type="week"]:active,
    + input:not([type]):active,
    + textarea:active': {
    +  'border': 'none'
    +}
    + + + + + + + + +
    + + + + +
    + +

    + Helpers +

    + + +

    Tiny helper functions that make your life easier.

    + + +
    +
    + + + +
    + + +
    + +

    + em +

    + + + + src/helpers/em.js + + +
    + + +

    Convert pixel value to ems. The default base value is 16px, but can be changed by passing a +second argument to the function.

    + + +
    em(pxval: (string | number), base: [(string | number)])
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + pxval ((string | number)) + +
    + +
    + +
    +
    + base ([(string | number)] + = '16px') + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +  'height': em('16px')
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  height: ${em('16px')}
    +`
    +
    +// CSS in JS Output
    +
    +element {
    +  'height': '1em'
    +}
    + + + + + + + + +
    + + + + +
    + + +
    + +

    + modularScale +

    + + + + src/helpers/modularScale.js + + +
    + + +

    Establish consistent measurements and spacial relationships throughout your projects by incrementing up or down a defined scale. We provide a list of commonly used scales as pre-defined variables, see below.

    + + +
    modularScale(steps: number, base: [(number | string)], ratio: [Ratio])
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + steps (number) + +
    + +
    + +
    +
    + base ([(number | string)] + = '1em') + +
    + +
    + +
    +
    + ratio ([Ratio] + = 'perfectFourth') + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +   // Increment two steps up the default scale
    +  'font-size': modularScale(2)
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +   // Increment two steps up the default scale
    +  font-size: ${modularScale(2)}
    +`
    +
    +// CSS in JS Output
    +
    +element {
    +  'font-size': '1.77689em'
    +}
    + + + + + + + + +
    + + + + +
    + + +
    + +

    + rem +

    + + + + src/helpers/rem.js + + +
    + + +

    Convert pixel value to rems. The default base value is 16px, but can be changed by passing a +second argument to the function.

    + + +
    rem(pxval: (string | number), base: [(string | number)])
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + pxval ((string | number)) + +
    + +
    + +
    +
    + base ([(string | number)] + = '16px') + +
    + +
    + +
    + + + + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +  'height': rem('16px')
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  height: ${rem('16px')}
    +`
    +
    +// CSS in JS Output
    +
    +element {
    +  'height': '1rem'
    +}
    + + + + + + + + +
    + + + + +
    + + +
    + +

    + stripUnit +

    + + + + src/helpers/stripUnit.js + + +
    + + +

    Strip the unit from a given CSS value, returning just the number. (or the original value if an invalid string was passed)

    + + +
    stripUnit(value: string): (number | string)
    + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + value (string) + +
    + +
    + +
    + + + + + + +
    Returns
    + (number | string) + + + + + + +
    Example
    + + +
    // Styles as object usage
    +const styles = {
    +  '--dimension': stripUnit(100px)
    +}
    +
    +// styled-components usage
    +const div = styled.div`
    +  --dimension: ${stripUnit(100px)}
    +`
    +
    +// CSS in JS Output
    +
    +element {
    +  '--dimension': 100
    +}
    + + + + + + + + +
    + + + +
    +
    +
    diff --git a/src/index.js b/src/index.js index 114df926..21050933 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,9 @@ // @flow // Helpers -import stripUnit from './helpers/stripUnit' import em from './helpers/em' import modularScale from './helpers/modularScale' import rem from './helpers/rem' +import stripUnit from './helpers/stripUnit' // Mixins import clearFix from './mixins/clearFix' @@ -19,29 +19,12 @@ import wordWrap from './mixins/wordWrap' // Shorthands import animation from './shorthands/animation' - -const polished = { - animation, - clearFix, - ellipsis, - em, - modularScale, - hideText, - hiDPI, - placeholder, - rem, - retinaImage, - selection, - size, - stripUnit, - timingFunctions, - wordWrap, -} - -export default polished +import buttons from './shorthands/buttons' +import textInputs from './shorthands/textInputs' export { animation, + buttons, clearFix, ellipsis, em, @@ -54,6 +37,7 @@ export { selection, size, stripUnit, + textInputs, timingFunctions, wordWrap, } diff --git a/src/internalHelpers/_statefulSelectors.js b/src/internalHelpers/_statefulSelectors.js new file mode 100644 index 00000000..80580b43 --- /dev/null +++ b/src/internalHelpers/_statefulSelectors.js @@ -0,0 +1,28 @@ +// @flow +function generateSelectors(template: Function, state: string|null) { + const stateSuffix = state ? `:${state}` : '' + return template(stateSuffix) +} + +type State = + | typeof(undefined) + | null + | string; + +/** + * Function helper that adds an array of states to a template of selectors. Used in textInputs and buttons. + * @private + */ +function statefulSelectors(states: Array, template: Function, stateMap: ?Array) { + if (!template) throw new Error('You must provide a template to this method.') + if (states.length === 0) return generateSelectors(template, null) + let selectors = [] + for (let i = 0; i < states.length; i += 1) { + if (stateMap && !stateMap.includes(states[i])) throw new Error('You passed an unsupported selector state to this method.') + selectors.push(generateSelectors(template, states[i])) + } + selectors = selectors.join(',') + return selectors +} + +export default statefulSelectors diff --git a/src/internalHelpers/test/__snapshots__/_statefulSelectors.test.js.snap b/src/internalHelpers/test/__snapshots__/_statefulSelectors.test.js.snap new file mode 100644 index 00000000..a5ede982 --- /dev/null +++ b/src/internalHelpers/test/__snapshots__/_statefulSelectors.test.js.snap @@ -0,0 +1,38 @@ +exports[`statefulSelectors populates both base selectors and selectors for a single state 1`] = ` +Object { + "section a, + p a,section a::after, + p a::after": Object { + "content": "hello", + }, +} +`; + +exports[`statefulSelectors populates both base selectors and selectors for a single state when not passed a stateMap 1`] = ` +Object { + "section a, + p a,section a::after, + p a::after": Object { + "content": "hello", + }, +} +`; + +exports[`statefulSelectors populates selectors for a multiple states 1`] = ` +Object { + "section a::before, + p a::before,section a::after, + p a::after": Object { + "content": "hello", + }, +} +`; + +exports[`statefulSelectors populates selectors for a single state 1`] = ` +Object { + "section a::before, + p a::before": Object { + "content": "hello", + }, +} +`; diff --git a/src/internalHelpers/test/_statefulSelectors.test.js b/src/internalHelpers/test/_statefulSelectors.test.js new file mode 100644 index 00000000..0d33677c --- /dev/null +++ b/src/internalHelpers/test/_statefulSelectors.test.js @@ -0,0 +1,74 @@ +import statefulSelectors from '../_statefulSelectors' + +const mockStateMap = [null, ':before', ':after'] + +function mockTemplate(pseudoSelector) { + return `section a${pseudoSelector}, + p a${pseudoSelector}` +} + +describe('statefulSelectors', function() { + // Selectors + it('populates selectors for a single state', function() { + expect({ + [statefulSelectors([':before'], mockTemplate, mockStateMap)]: { + 'content': 'hello' + } + }).toMatchSnapshot() + }) + it('populates selectors for a multiple states', function() { + expect({ + [statefulSelectors([':before', ':after'], mockTemplate, mockStateMap)]: { + 'content': 'hello' + } + }).toMatchSnapshot() + }) + it('populates both base selectors and selectors for a single state', function() { + expect({ + [statefulSelectors([null, ':after'], mockTemplate, mockStateMap)]: { + 'content': 'hello' + } + }).toMatchSnapshot() + }) + it('populates both base selectors and selectors for a single state when not passed a stateMap', function() { + expect({ + [statefulSelectors([null, ':after'], mockTemplate)]: { + 'content': 'hello'} + }).toMatchSnapshot() + }) + + // Errors + it('throws an error when passed a state it does not recognize', function() { + expect( + function () { + return { + [statefulSelectors([':visited'], mockTemplate, mockStateMap)]: { + 'content': 'hello' + } + } + } + ).toThrow('You passed an unsupported selector state to this method') + }) + it('throws an error when passed one of the states it is passed is not recognized', function() { + expect( + function () { + return { + [statefulSelectors(['hover', ':visited'], mockTemplate, mockStateMap)]: { + 'content': 'hello' + } + } + } + ).toThrow('You passed an unsupported selector state to this method') + }) + it('throws an error when not passed a template', function() { + expect( + function () { + return { + [statefulSelectors([':visited'])]: { + 'content': 'hello' + } + } + } + ).toThrow('You must provide a template to this method.') + }) +}) diff --git a/src/shorthands/buttons.js b/src/shorthands/buttons.js new file mode 100644 index 00000000..0cd96c42 --- /dev/null +++ b/src/shorthands/buttons.js @@ -0,0 +1,51 @@ +// @flow +import statefulSelectors from '../internalHelpers/_statefulSelectors' + +const stateMap = [undefined, null, 'active', 'focus', 'hover'] + +function template(state) { + return `button${state}, + input[type="button"]${state}, + input[type="reset"]${state}, + input[type="submit"]${state}` +} + +type State = + | typeof(undefined) + | null + | 'active' + | 'focus' + | 'hover'; + +/** + * Populates selectors that target all buttons. You can pass optional states to append to the selectors. + * @example + * // Styles as object usage + * const styles = { + * [buttons('active')]: { + * 'border': 'none' + * } + * } + * + * // styled-components usage + * const div = styled.div` + * > ${buttons('active')} { + * border: none; + * } + * ` + * + * // CSS in JS Output + * + * 'button:active, + * 'input[type="button"]:active, + * 'input[type=\"reset\"]:active, + * 'input[type=\"submit\"]:active: { + * 'border': 'none' + * } + */ + +function buttons(...states: Array) { + return statefulSelectors(states, template, stateMap) +} + +export default buttons diff --git a/src/shorthands/test/__snapshots__/buttons.test.js.snap b/src/shorthands/test/__snapshots__/buttons.test.js.snap new file mode 100644 index 00000000..4202a750 --- /dev/null +++ b/src/shorthands/test/__snapshots__/buttons.test.js.snap @@ -0,0 +1,49 @@ +exports[`buttons populates base button selectors 1`] = ` +Object { + "button, + input[type=\"button\"], + input[type=\"reset\"], + input[type=\"submit\"]": Object { + "border-color": "black", + }, +} +`; + +exports[`buttons populates both base selectors and selectors for a single state 1`] = ` +Object { + "button, + input[type=\"button\"], + input[type=\"reset\"], + input[type=\"submit\"],button:focus, + input[type=\"button\"]:focus, + input[type=\"reset\"]:focus, + input[type=\"submit\"]:focus": Object { + "border-color": "black", + }, +} +`; + +exports[`buttons populates button selectors for multiple states 1`] = ` +Object { + "button:active, + input[type=\"button\"]:active, + input[type=\"reset\"]:active, + input[type=\"submit\"]:active,button:focus, + input[type=\"button\"]:focus, + input[type=\"reset\"]:focus, + input[type=\"submit\"]:focus": Object { + "border-color": "black", + }, +} +`; + +exports[`buttons populates buttons selectors for a single state 1`] = ` +Object { + "button:active, + input[type=\"button\"]:active, + input[type=\"reset\"]:active, + input[type=\"submit\"]:active": Object { + "border-color": "black", + }, +} +`; diff --git a/src/shorthands/test/__snapshots__/textInputs.test.js.snap b/src/shorthands/test/__snapshots__/textInputs.test.js.snap new file mode 100644 index 00000000..55dd154f --- /dev/null +++ b/src/shorthands/test/__snapshots__/textInputs.test.js.snap @@ -0,0 +1,121 @@ +exports[`textInputs populates base text input selectors 1`] = ` +Object { + "input[type=\"color\"], + input[type=\"date\"], + input[type=\"datetime\"], + input[type=\"datetime-local\"], + input[type=\"email\"], + input[type=\"month\"], + input[type=\"number\"], + input[type=\"password\"], + input[type=\"search\"], + input[type=\"tel\"], + input[type=\"text\"], + input[type=\"time\"], + input[type=\"url\"], + input[type=\"week\"], + input:not([type]), + textarea": Object { + "border-color": "black", + }, +} +`; + +exports[`textInputs populates both base selectors and selectors for a single state 1`] = ` +Object { + "input[type=\"color\"], + input[type=\"date\"], + input[type=\"datetime\"], + input[type=\"datetime-local\"], + input[type=\"email\"], + input[type=\"month\"], + input[type=\"number\"], + input[type=\"password\"], + input[type=\"search\"], + input[type=\"tel\"], + input[type=\"text\"], + input[type=\"time\"], + input[type=\"url\"], + input[type=\"week\"], + input:not([type]), + textarea,input[type=\"color\"]:focus, + input[type=\"date\"]:focus, + input[type=\"datetime\"]:focus, + input[type=\"datetime-local\"]:focus, + input[type=\"email\"]:focus, + input[type=\"month\"]:focus, + input[type=\"number\"]:focus, + input[type=\"password\"]:focus, + input[type=\"search\"]:focus, + input[type=\"tel\"]:focus, + input[type=\"text\"]:focus, + input[type=\"time\"]:focus, + input[type=\"url\"]:focus, + input[type=\"week\"]:focus, + input:not([type]):focus, + textarea:focus": Object { + "border-color": "black", + }, +} +`; + +exports[`textInputs populates text input selectors for a single state 1`] = ` +Object { + "input[type=\"color\"]:active, + input[type=\"date\"]:active, + input[type=\"datetime\"]:active, + input[type=\"datetime-local\"]:active, + input[type=\"email\"]:active, + input[type=\"month\"]:active, + input[type=\"number\"]:active, + input[type=\"password\"]:active, + input[type=\"search\"]:active, + input[type=\"tel\"]:active, + input[type=\"text\"]:active, + input[type=\"time\"]:active, + input[type=\"url\"]:active, + input[type=\"week\"]:active, + input:not([type]):active, + textarea:active": Object { + "border-color": "black", + }, +} +`; + +exports[`textInputs populates text input selectors for multiple states 1`] = ` +Object { + "input[type=\"color\"]:active, + input[type=\"date\"]:active, + input[type=\"datetime\"]:active, + input[type=\"datetime-local\"]:active, + input[type=\"email\"]:active, + input[type=\"month\"]:active, + input[type=\"number\"]:active, + input[type=\"password\"]:active, + input[type=\"search\"]:active, + input[type=\"tel\"]:active, + input[type=\"text\"]:active, + input[type=\"time\"]:active, + input[type=\"url\"]:active, + input[type=\"week\"]:active, + input:not([type]):active, + textarea:active,input[type=\"color\"]:focus, + input[type=\"date\"]:focus, + input[type=\"datetime\"]:focus, + input[type=\"datetime-local\"]:focus, + input[type=\"email\"]:focus, + input[type=\"month\"]:focus, + input[type=\"number\"]:focus, + input[type=\"password\"]:focus, + input[type=\"search\"]:focus, + input[type=\"tel\"]:focus, + input[type=\"text\"]:focus, + input[type=\"time\"]:focus, + input[type=\"url\"]:focus, + input[type=\"week\"]:focus, + input:not([type]):focus, + textarea:focus": Object { + "border-color": "black", + }, +} +`; diff --git a/src/shorthands/test/buttons.test.js b/src/shorthands/test/buttons.test.js new file mode 100644 index 00000000..a4ad764c --- /dev/null +++ b/src/shorthands/test/buttons.test.js @@ -0,0 +1,21 @@ +import buttons from '../buttons' + +describe('buttons', function() { + it('populates base button selectors', function() { + expect({[buttons()]: {'border-color': 'black'}}).toMatchSnapshot() + }) + it('populates buttons selectors for a single state', function() { + expect({[buttons('active')]: {'border-color': 'black'}}).toMatchSnapshot() + }) + it('populates both base selectors and selectors for a single state', function() { + expect({[buttons(null, 'focus')]: {'border-color': 'black'}}).toMatchSnapshot() + }) + it('populates button selectors for multiple states', function() { + expect({[buttons('active', 'focus')]: {'border-color': 'black'}}).toMatchSnapshot() + }) + it('throws an error when passed a state it does not recognize', function() { + expect( + function () { return {[buttons('clicked')]: {'border-color': 'black'}} } + ).toThrow('You passed an unsupported selector state to this method') + }) +}) diff --git a/src/shorthands/test/textInputs.test.js b/src/shorthands/test/textInputs.test.js new file mode 100644 index 00000000..8695001e --- /dev/null +++ b/src/shorthands/test/textInputs.test.js @@ -0,0 +1,21 @@ +import textInputs from '../textInputs' + +describe('textInputs', function() { + it('populates base text input selectors', function() { + expect({[textInputs()]: {'border-color': 'black'}}).toMatchSnapshot() + }) + it('populates text input selectors for a single state', function() { + expect({[textInputs('active')]: {'border-color': 'black'}}).toMatchSnapshot() + }) + it('populates both base selectors and selectors for a single state', function() { + expect({[textInputs(null, 'focus')]: {'border-color': 'black'}}).toMatchSnapshot() + }) + it('populates text input selectors for multiple states', function() { + expect({[textInputs('active', 'focus')]: {'border-color': 'black'}}).toMatchSnapshot() + }) + it('throws an error when passed a state it does not recognize', function() { + expect( + function () { return {[textInputs('clicked')]: {'border-color': 'black'}} } + ).toThrow('You passed an unsupported selector state to this method') + }) +}) diff --git a/src/shorthands/textInputs.js b/src/shorthands/textInputs.js new file mode 100644 index 00000000..f17f72b7 --- /dev/null +++ b/src/shorthands/textInputs.js @@ -0,0 +1,75 @@ +// @flow +import statefulSelectors from '../internalHelpers/_statefulSelectors' + +const stateMap = [undefined, null, 'active', 'focus', 'hover'] + +function template(state) { + return `input[type="color"]${state}, + input[type="date"]${state}, + input[type="datetime"]${state}, + input[type="datetime-local"]${state}, + input[type="email"]${state}, + input[type="month"]${state}, + input[type="number"]${state}, + input[type="password"]${state}, + input[type="search"]${state}, + input[type="tel"]${state}, + input[type="text"]${state}, + input[type="time"]${state}, + input[type="url"]${state}, + input[type="week"]${state}, + input:not([type])${state}, + textarea${state}` +} + +type State = + | typeof(undefined) + | null + | 'active' + | 'focus' + | 'hover'; + +/** + * Populates selectors that target all text inputs. You can pass optional states to append to the selectors. + * @example + * // Styles as object usage + * const styles = { + * [textInputs('active')]: { + * 'border': 'none' + * } + * } + * + * // styled-components usage + * const div = styled.div` + * > ${textInputs('active')} { + * border: none; + * } + * ` + * + * // CSS in JS Output + * + * ''input[type="color"]:active, + * 'input[type="date"]:active, + * 'input[type="datetime"]:active, + * 'input[type="datetime-local"]:active, + * 'input[type="email"]:active, + * 'input[type="month"]:active, + * 'input[type="number"]:active, + * 'input[type="password"]:active, + * 'input[type="search"]:active, + * 'input[type="tel"]:active, + * 'input[type="text"]:active, + * 'input[type="time"]:active, + * 'input[type="url"]:active, + * 'input[type="week"]:active, + * input:not([type]):active, + * textarea:active': { + * 'border': 'none' + * } + */ + +function textInputs(...states: Array) { + return statefulSelectors(states, template, stateMap) +} + +export default textInputs