From 411d3e3e684a83547fe0c09ded12edb90a4e89b9 Mon Sep 17 00:00:00 2001 From: Aanand Prasad <aanand.prasad@gmail.com> Date: Wed, 31 Jan 2018 18:01:23 +0000 Subject: [PATCH] feat(ui): convert Menu to a styled component --- packages/ui/src/atoms/Menu.js | 185 ++++++++++++--- packages/ui/src/atoms/Menu.local.scss | 121 ---------- .../ui/test/__snapshots__/Menu.test.js.snap | 218 ++++++++++++++++-- 3 files changed, 354 insertions(+), 170 deletions(-) delete mode 100644 packages/ui/src/atoms/Menu.local.scss diff --git a/packages/ui/src/atoms/Menu.js b/packages/ui/src/atoms/Menu.js index ff22b84d5..95ce34eb5 100644 --- a/packages/ui/src/atoms/Menu.js +++ b/packages/ui/src/atoms/Menu.js @@ -1,10 +1,140 @@ import React from 'react' -import classnames from 'classnames' -import classes from './Menu.local.scss' +import styled from 'styled-components' // TODO: match the width of the container to the width of the widest option? // TODO: use a <select> element instead of divs? +const Label = styled.span` + display: block; + font-size: 1em; + margin-bottom: 0.5em; +` + +const OpenerContainer = styled.div`` + +const Opener = styled.button.attrs({ + type: 'button', +})` + background: transparent; + border: none; + cursor: pointer; + font-family: inherit; + font-size: 1.2em; +` + +const Placeholder = styled.span` + font-family: var(--font-interface); + font-style: italic; + font-weight: 400; + text-transform: normal; +` + +const Arrow = styled.span` + display: inline-block; + font-size: 50%; + margin-left: 10px; + transition: transform 0.2s; +` + +const Main = styled.div.attrs({ + role: 'listbox', +})` + position: relative; +` + +const OptionsContainer = styled.div` + position: absolute; +` + +const Options = styled.div` + background-color: white; + border-bottom: 2px solid var(--color-primary); + border-left: 2px solid var(--color-primary); + left: 0; + padding-bottom: 0.5em; + padding-top: 0.5em; + position: absolute; + top: 0; + transition: opacity 2s; + width: 0; + z-index: 10; +` + +const Option = styled.div.attrs({ + role: 'option', + tabIndex: '0', + 'aria-selected': props => props.active, +})` + color: ${props => (props.active ? 'black' : 'inherit')}; + font-weight: ${props => (props.active ? '600' : 'inherit')}; + cursor: pointer; + font-family: var(--font-author); + padding: 10px; + white-space: nowrap; + + &:hover { + color: var(--color-primary); + } +` + +const Root = styled.div` + font-size: 1em; + + ${Opener} { + border-left: 2px solid + ${props => (props.open ? 'var(--color-primary)' : 'lightgrey')}; + color: ${props => (props.open ? 'var(--color-primary)' : 'inherit')}; + + ${Placeholder} { + color: #aaa; + } + } + + ${Opener}:hover { + border-left: 2px solid var(--color-primary); + color: var(--color-primary); + + ${Placeholder} { + color: var(--color-primary); + } + } + + ${Arrow} { + transform: scaleX(2.2) scaleY(${props => (props.open ? -1.2 : 1.2)}); + } + + ${Options} { + min-width: ${props => (props.open ? '10em' : '0')}; + opacity: ${props => (props.open ? '1' : '0')}; + } +` + +/* Not used for now +.inline { + align-items: flex-end; + display: flex; + flex-direction: row; + justify-content: flex-start; + margin-right: 0.5em; +} + +.inline .label { + margin-right: 0.5em; +} + +.inline .opener { + margin-bottom: -4px; +} + +.root .inline { + flex-direction: columns; +} + +.root.author { + font-family: var(--font-author); +} +*/ + class Menu extends React.Component { constructor(props) { super(props) @@ -47,54 +177,41 @@ class Menu extends React.Component { const { open, selected } = this.state return ( - <div - className={classnames(classes.root, { - [classes.open]: open, - })} - > - {label && <span className={classes.label}>{label}</span>} - - <div className={classes.main} role="listbox"> - <div className={classes.openerContainer}> - <button - className={classes.opener} - onClick={this.toggleMenu} - type="button" - > + <Root open={open}> + {label && <Label>{label}</Label>} + + <Main> + <OpenerContainer> + <Opener onClick={this.toggleMenu}> {selected ? ( <span>{this.optionLabel(selected)}</span> ) : ( - <span className={classes.placeholder}>{placeholder}</span> + <Placeholder>{placeholder}</Placeholder> )} - <span className={classes.arrow}>â–¼</span> - </button> - </div> + <Arrow>â–¼</Arrow> + </Opener> + </OpenerContainer> - <div className={classes.optionsContainer}> + <OptionsContainer> {open && ( - <div className={classes.options}> + <Options> {options.map(option => ( - <div - aria-selected={option.value === selected} - className={classnames(classes.option, { - [classes.active]: option.value === selected, - })} + <Option + active={option.value === selected} key={option.value} onClick={() => this.handleSelect(option.value)} onKeyPress={event => this.handleKeyPress(event, option.value) } - role="option" - tabIndex="0" > {option.label || option.value} - </div> + </Option> ))} - </div> + </Options> )} - </div> - </div> - </div> + </OptionsContainer> + </Main> + </Root> ) } } diff --git a/packages/ui/src/atoms/Menu.local.scss b/packages/ui/src/atoms/Menu.local.scss deleted file mode 100644 index edeb0f22e..000000000 --- a/packages/ui/src/atoms/Menu.local.scss +++ /dev/null @@ -1,121 +0,0 @@ -.root { - font-size: 1em; -} - -.inline { - align-items: flex-end; - display: flex; - flex-direction: row; - justify-content: flex-start; - margin-right: 0.5em; -} - -.inline .label { - margin-right: 0.5em; -} - -.opener { - background: transparent; - border: none; - border-left: 2px solid lightgrey; - cursor: pointer; - font-family: inherit; - font-size: 1.2em; -} - -.opener:hover { - border-left: 2px solid var(--color-primary); - color: var(--color-primary); -} - -.inline .opener { - margin-bottom: -4px; -} - -.root .inline { - flex-direction: columns; -} - -.root:not(.inline) .label { - display: block; - font-size: 1em; - margin-bottom: 0.5em; -} - -.main { - position: relative; -} - -.optionsContainer { - position: absolute; -} - -.open .opener { - border-left: 2px solid var(--color-primary); - color: var(--color-primary); -} - -.placeholder { - color: #aaa; - font-family: var(--font-interface); - font-style: italic; - font-weight: 400; - text-transform: normal; -} - -.opener:hover .placeholder { - color: var(--color-primary); -} - -.arrow { - display: inline-block; - font-size: 50%; - margin-left: 10px; - transform: scaleY(1.2) scaleX(2.2); - transition: transform 0.2s; -} - -.open .arrow { - transform: scaleX(2.2) scaleY(-1.2); -} - -.options { - background-color: white; - border-bottom: 2px solid var(--color-primary); - border-left: 2px solid var(--color-primary); - left: 0; - min-width: 0; - opacity: 0; - padding-bottom: 0.5em; - padding-top: 0.5em; - position: absolute; - top: 0; - transition: opacity 2s; - width: 0; - z-index: 10; -} - -.open .options { - min-width: 10em; - opacity: 1; -} - -.option { - cursor: pointer; - font-family: var(--font-author); - padding: 10px; - white-space: nowrap; -} - -.option:hover { - color: var(--color-primary); -} - -.active { - color: black; - font-weight: 600; /* placeholder for the semibold */ -} - -.root.author { - font-family: var(--font-author); -} diff --git a/packages/ui/test/__snapshots__/Menu.test.js.snap b/packages/ui/test/__snapshots__/Menu.test.js.snap index d5be802b3..c45f23fff 100644 --- a/packages/ui/test/__snapshots__/Menu.test.js.snap +++ b/packages/ui/test/__snapshots__/Menu.test.js.snap @@ -1,18 +1,77 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Menu is rendered correctly 1`] = ` +.c3 { + background: transparent; + border: none; + cursor: pointer; + font-family: inherit; + font-size: 1.2em; +} + +.c5 { + display: inline-block; + font-size: 50%; + margin-left: 10px; + -webkit-transition: -webkit-transform 0.2s; + -webkit-transition: transform 0.2s; + transition: transform 0.2s; +} + +.c1 { + position: relative; +} + +.c6 { + position: absolute; +} + +.c0 { + font-size: 1em; +} + +.c0 .c2 { + border-left: 2px solid lightgrey; + color: inherit; +} + +.c0 .c2 .sc-bxivhb { + color: #aaa; +} + +.c0 .c2:hover { + border-left: 2px solid var(--color-primary); + color: var(--color-primary); +} + +.c0 .c2:hover .sc-bxivhb { + color: var(--color-primary); +} + +.c0 .c4 { + -webkit-transform: scaleX(2.2) scaleY(1.2); + -ms-transform: scaleX(2.2) scaleY(1.2); + transform: scaleX(2.2) scaleY(1.2); +} + +.c0 .sc-gzVnrw { + min-width: 0; + opacity: 0; +} + <div - className="root" + className="c0" + open={false} > <div - className="main" + className="c1" role="listbox" > <div - className="openerContainer" + className="" > <button - className="opener" + className="c2 c3" onClick={[Function]} type="button" > @@ -20,32 +79,161 @@ exports[`Menu is rendered correctly 1`] = ` Foo </span> <span - className="arrow" + className="c4 c5" > â–¼ </span> </button> </div> <div - className="optionsContainer" + className="c6" /> </div> </div> `; exports[`Menu is rendered correctly when open 1`] = ` +.c3 { + background: transparent; + border: none; + cursor: pointer; + font-family: inherit; + font-size: 1.2em; +} + +.c5 { + display: inline-block; + font-size: 50%; + margin-left: 10px; + -webkit-transition: -webkit-transform 0.2s; + -webkit-transition: transform 0.2s; + transition: transform 0.2s; +} + +.c1 { + position: relative; +} + +.c6 { + position: absolute; +} + +.c8 { + background-color: white; + border-bottom: 2px solid var(--color-primary); + border-left: 2px solid var(--color-primary); + left: 0; + padding-bottom: 0.5em; + padding-top: 0.5em; + position: absolute; + top: 0; + -webkit-transition: opacity 2s; + transition: opacity 2s; + width: 0; + z-index: 10; +} + +.c9 { + color: black; + font-weight: 600; + cursor: pointer; + font-family: var(--font-author); + padding: 10px; + white-space: nowrap; +} + +.c9:hover { + color: var(--color-primary); +} + +.c10 { + color: inherit; + font-weight: inherit; + cursor: pointer; + font-family: var(--font-author); + padding: 10px; + white-space: nowrap; +} + +.c10:hover { + color: var(--color-primary); +} + +.gQZhbO .c2 { + border-left: 2px solid lightgrey; + color: inherit; +} + +.gQZhbO .c2 .sc-bxivhb { + color: #aaa; +} + +.gQZhbO .c2:hover { + border-left: 2px solid var(--color-primary); + color: var(--color-primary); +} + +.gQZhbO .c2:hover .sc-bxivhb { + color: var(--color-primary); +} + +.gQZhbO .c4 { + -webkit-transform: scaleX(2.2) scaleY(1.2); + -ms-transform: scaleX(2.2) scaleY(1.2); + transform: scaleX(2.2) scaleY(1.2); +} + +.gQZhbO .c7 { + min-width: 0; + opacity: 0; +} + +.c0 { + font-size: 1em; +} + +.c0 .c2 { + border-left: 2px solid var(--color-primary); + color: var(--color-primary); +} + +.c0 .c2 .sc-bxivhb { + color: #aaa; +} + +.c0 .c2:hover { + border-left: 2px solid var(--color-primary); + color: var(--color-primary); +} + +.c0 .c2:hover .sc-bxivhb { + color: var(--color-primary); +} + +.c0 .c4 { + -webkit-transform: scaleX(2.2) scaleY(-1.2); + -ms-transform: scaleX(2.2) scaleY(-1.2); + transform: scaleX(2.2) scaleY(-1.2); +} + +.c0 .c7 { + min-width: 10em; + opacity: 1; +} + <div - className="root open" + className="c0" + open={true} > <div - className="main" + className="c1" role="listbox" > <div - className="openerContainer" + className="" > <button - className="opener" + className="c2 c3" onClick={[Function]} type="button" > @@ -53,21 +241,21 @@ exports[`Menu is rendered correctly when open 1`] = ` Foo </span> <span - className="arrow" + className="c4 c5" > â–¼ </span> </button> </div> <div - className="optionsContainer" + className="c6" > <div - className="options" + className="c7 c8" > <div aria-selected={true} - className="option active" + className="c9" onClick={[Function]} onKeyPress={[Function]} role="option" @@ -77,7 +265,7 @@ exports[`Menu is rendered correctly when open 1`] = ` </div> <div aria-selected={false} - className="option" + className="c10" onClick={[Function]} onKeyPress={[Function]} role="option" -- GitLab