react-dropdown-aria
Advanced tools
Comparing version 1.0.1 to 1.1.0
'use strict'; | ||
var React = require('react'); | ||
var emotion = require('emotion'); | ||
var PropTypes = require('prop-types'); | ||
function createCommonjsModule(fn, module) { | ||
return module = { exports: {} }, fn(module, module.exports), module.exports; | ||
} | ||
var classnames = createCommonjsModule(function (module) { | ||
/*! | ||
Copyright (c) 2017 Jed Watson. | ||
Licensed under the MIT License (MIT), see | ||
http://jedwatson.github.io/classnames | ||
*/ | ||
/* global define */ | ||
(function () { | ||
var hasOwn = {}.hasOwnProperty; | ||
function classNames() { | ||
var classes = []; | ||
for (var i = 0; i < arguments.length; i++) { | ||
var arg = arguments[i]; | ||
if (!arg) continue; | ||
var argType = typeof arg; | ||
if (argType === 'string' || argType === 'number') { | ||
classes.push(arg); | ||
} else if (Array.isArray(arg) && arg.length) { | ||
var inner = classNames.apply(null, arg); | ||
if (inner) { | ||
classes.push(inner); | ||
} | ||
} else if (argType === 'object') { | ||
for (var key in arg) { | ||
if (hasOwn.call(arg, key) && arg[key]) { | ||
classes.push(key); | ||
} | ||
} | ||
} | ||
} | ||
return classes.join(' '); | ||
} | ||
if (module.exports) { | ||
classNames.default = classNames; | ||
module.exports = classNames; | ||
} else { | ||
window.classNames = classNames; | ||
} | ||
})(); | ||
}); | ||
var classnames_1 = classnames.classnames; | ||
const KEY_CODES = { | ||
@@ -74,3 +21,2 @@ UP_ARROW: 38, | ||
/* | ||
@@ -80,3 +26,3 @@ This file needs changes once Enzyme updates to be able to handle forwardRef in tests | ||
function defaultOptionRenderer(selectedOption, options, selectedOptionClassName, optionClassName, onOptionClicked, elementsRef) { | ||
function defaultOptionRenderer(selectedOption, options, selectedOptionClassName, optionClassName, onOptionClicked, elementsRef, getStyle) { | ||
return options.map(option => { | ||
@@ -89,6 +35,6 @@ const { groupOptions, label, value, className } = option; | ||
'div', | ||
{ key: label, className: 'dropdown-group' }, | ||
{ key: label, className: getStyle('groupContainer') }, | ||
React.createElement( | ||
'div', | ||
{ className: 'dropdown-group-heading' }, | ||
{ className: getStyle('groupHeading') }, | ||
React.createElement( | ||
@@ -106,3 +52,3 @@ 'div', | ||
option.groupOptions.map(groupOption => { | ||
const groupOptionClass = classnames(groupOption.className, groupOption.value === selectedOption ? selectedOptionClassName || 'dropdown-option-selected' : optionClassName || 'dropdown-option'); | ||
const groupOptionClass = emotion.cx(groupOption.className, getStyle('optionItem', groupOption.value === selectedOption)); | ||
return React.createElement( | ||
@@ -137,3 +83,3 @@ 'button', | ||
const optionClass = classnames(className, value === selectedOption ? selectedOptionClassName || 'dropdown-option-selected' : optionClassName || 'dropdown-option'); | ||
const optionClass = emotion.cx(className, getStyle('optionItem', value === selectedOption)); | ||
return React.createElement( | ||
@@ -167,2 +113,199 @@ 'button', | ||
const colours = { | ||
greys: { | ||
lighter: '#d9dadd', | ||
light: '#b5b6b7', | ||
base: '#808080', | ||
dark: '#595959', | ||
darker: '#404040' | ||
}, | ||
purple: { | ||
lighter: '#ccd1ed', | ||
light: '#a7aedf', | ||
base: '#990099' | ||
} | ||
}; | ||
const optionItemStyle = (props, state, selected) => ({ | ||
fontSize: '0.95em', | ||
overflow: 'hidden', | ||
textOverflow: 'ellipsis', | ||
whiteSpace: 'nowrap', | ||
padding: '5px 10px', | ||
width: '100%', | ||
textAlign: 'left', | ||
cursor: 'pointer', | ||
outline: 'none', | ||
backgroundColor: selected ? colours.purple.lighter : 'white', | ||
border: 'none', | ||
'&:hover': { | ||
backgroundColor: selected ? colours.purple.light : colours.greys.lighter | ||
}, | ||
'&:focus': { | ||
backgroundColor: selected ? colours.purple.light : colours.greys.lighter | ||
}, | ||
'.option-icon': { | ||
paddingRight: '5px' | ||
} | ||
}); | ||
const OptionItem = React.forwardRef((props, ref) => { | ||
const { | ||
onOptionClicked, | ||
option, | ||
optionClass | ||
} = props; | ||
return React.createElement( | ||
'button', | ||
{ | ||
'aria-label': option.ariaLabel, | ||
className: optionClass, | ||
onClick: onOptionClicked, | ||
onKeyDown: onOptionClicked, | ||
ref: ref, | ||
tabIndex: '-1', | ||
title: option.title, | ||
type: 'button' | ||
}, | ||
option.iconClass && React.createElement('i', { className: `${option.iconClass} option-icon` }), | ||
option.value | ||
); | ||
}); | ||
// Please Keep Alphabetical | ||
OptionItem.propTypes = { | ||
ariaLabel: PropTypes.string, | ||
option: PropTypes.shape({ | ||
ariaLabel: PropTypes.string, | ||
className: PropTypes.string, | ||
title: PropTypes.string, | ||
value: PropTypes.string.isRequired | ||
}).isRequired, | ||
optionClass: PropTypes.string | ||
}; | ||
const dropdownWrapper = ({ width, height }) => ({ | ||
width, | ||
height, | ||
position: 'relative', | ||
display: 'flex', | ||
flexDirection: 'column' | ||
}); | ||
const dropdownButton = (props, { open }) => ({ | ||
fontSize: '1em', | ||
display: 'flex', | ||
flexDirection: 'row', | ||
alignItems: 'center', | ||
width: '100%', | ||
height: '100%', | ||
backgroundColor: 'white', | ||
padding: '9px 5px', | ||
margin: '0', | ||
borderRadius: '0', | ||
borderBottom: `2px solid ${colours.greys.light}`, | ||
borderTop: 'none', | ||
borderRight: 'none', | ||
borderLeft: 'none', | ||
textAlign: 'left', | ||
cursor: 'pointer', | ||
outline: 'none', | ||
boxShadow: open ? `0px 1px 3px 2px ${colours.greys.lighter}` : 'none', | ||
'&:hover': { | ||
boxShadow: `0px 1px 3px 2px ${colours.greys.lighter}` | ||
}, | ||
'&:focus': { | ||
boxShadow: `0px 1px 3px 2px ${colours.greys.lighter}` | ||
}, | ||
'&:disabled': { | ||
cursor: 'not-allowed' | ||
} | ||
}); | ||
const displayedValue = ({ hideArrow, selectedOption, centerText }, { internalSelectedOption }) => ({ | ||
flex: '1', | ||
borderRight: hideArrow ? 'none' : `1px solid ${colours.greys.light}`, | ||
overflow: 'hidden', | ||
textOverflow: 'ellipsis', | ||
whiteSpace: 'nowrap', | ||
color: selectedOption || internalSelectedOption ? 'black' : colours.greys.base, | ||
textAlign: centerText ? 'center' : 'left' | ||
}); | ||
const arrow = (props, { open }) => ({ | ||
content: '""', | ||
width: '0', | ||
height: '0', | ||
marginLeft: '8px', | ||
marginRight: '5px', | ||
borderRight: '5px solid transparent', | ||
borderLeft: '5px solid transparent', | ||
borderTop: open ? '0' : `5px solid ${colours.greys.base}`, | ||
borderBottom: open ? `5px solid ${colours.greys.base}` : '0' | ||
}); | ||
const optionContainer = ({ openUp, maxContentHeight }, { open }) => ({ | ||
width: '100%', | ||
maxHeight: maxContentHeight || '175px', | ||
overflowY: maxContentHeight ? 'scroll' : null, | ||
zIndex: '9999', | ||
overflowX: 'hidden', | ||
position: 'absolute', | ||
left: '0', | ||
listStyleType: 'none', | ||
margin: '0', | ||
padding: '2px 0', | ||
backgroundColor: 'white', | ||
color: 'black', | ||
borderRadius: '2px', | ||
display: open ? 'block' : 'none', | ||
boxSizing: 'border-box', | ||
top: openUp ? null : '100%', | ||
bottom: openUp ? '105%' : null, | ||
boxShadow: openUp ? `0px -3px 3px 2px ${colours.greys.lighter}` : `0px 3px 3px 2px ${colours.greys.lighter}`, | ||
'&::-webkit-scrollbar': { | ||
width: '5px' | ||
}, | ||
'&::-webkit-scrollbar-track': { | ||
background: '#ddd' | ||
}, | ||
'&::-webkit-scrollbar-thumb': { | ||
background: '#666' | ||
} | ||
}); | ||
const groupContainer = () => ({ | ||
padding: '1em 0 0 0' | ||
}); | ||
const groupHeading = () => ({ | ||
color: 'grey', | ||
fontSize: '0.9em', | ||
padding: '0 10px 3px 5px', | ||
display: 'flex', | ||
flexDirection: 'row', | ||
justifyContent: 'space-between' | ||
}); | ||
const defaultStyles = { | ||
arrow, | ||
dropdownButton, | ||
displayedValue, | ||
dropdownWrapper, | ||
groupContainer, | ||
groupHeading, | ||
optionContainer, | ||
optionItem: optionItemStyle | ||
}; | ||
class Dropdown extends React.Component { | ||
@@ -259,2 +402,9 @@ constructor(props) { | ||
this.getStyle = (key, extraState) => { | ||
const { style } = this.props; | ||
const baseStyle = defaultStyles[key](this.props, this.state, extraState); | ||
const customStyle = style[key]; | ||
return customStyle ? emotion.css(customStyle(baseStyle, this.state, extraState)) : emotion.css(baseStyle); | ||
}; | ||
this.setFocus = () => { | ||
@@ -306,6 +456,6 @@ const { focusedIndex } = this.state; | ||
if (optionRenderer) { | ||
return optionRenderer(selectedOption || internalSelectedOption, options, this.onOptionClicked, this.elements); | ||
return optionRenderer(selectedOption || internalSelectedOption, options, this.onOptionClicked, this.elements, this.getStyle); | ||
} | ||
return defaultOptionRenderer(selectedOption, options, selectedOptionClassName, optionClassName, this.onOptionClicked, this.elements); | ||
return defaultOptionRenderer(selectedOption, options, selectedOptionClassName, optionClassName, this.onOptionClicked, this.elements, this.getStyle); | ||
}; | ||
@@ -340,15 +490,10 @@ | ||
arrowRenderer, | ||
centerText, | ||
contentClassName, | ||
buttonClassName, | ||
disabled, | ||
height, | ||
hideArrow, | ||
id, | ||
maxContentHeight, | ||
openUp, | ||
placeholder, | ||
selectedOption, | ||
selectedValueClassName, | ||
width | ||
selectedValueClassName | ||
} = this.props; | ||
@@ -362,7 +507,7 @@ | ||
const displayedValue = selectedOption || internalSelectedOption || placeholder || ''; | ||
const dropdownButtonClass = classnames('dropdown-select', buttonClassName); | ||
const displayedValueClass = classnames('displayed-value', selectedValueClassName, { grey: !selectedOption && !internalSelectedOption, 'no-arrow': hideArrow, 'center-text': centerText }); | ||
const contentClass = classnames('dropdown-content', contentClassName, { 'dropdown-content-open': open, 'dropdown-content-down': !openUp, 'dropdown-content-up': openUp }); | ||
const arrowClass = open ? 'dropdown-arrow up' : 'dropdown-arrow down'; | ||
const listStyle = maxContentHeight ? { maxHeight: maxContentHeight, overflowY: 'scroll' } : {}; | ||
const wrapperClass = this.getStyle('dropdownWrapper'); | ||
const dropdownButtonClass = emotion.cx(buttonClassName, this.getStyle('dropdownButton')); | ||
const displayedValueClass = emotion.cx(selectedValueClassName, this.getStyle('displayedValue')); | ||
const contentClass = emotion.cx(contentClassName, this.getStyle('optionContainer')); | ||
const arrowClass = this.getStyle('arrow'); | ||
@@ -372,6 +517,5 @@ return React.createElement( | ||
{ | ||
className: 'dropdown', | ||
className: wrapperClass, | ||
onKeyDown: this.onKeyDown, | ||
ref: div => this.container = div, | ||
style: { width, height } | ||
ref: div => this.container = div | ||
}, | ||
@@ -402,3 +546,3 @@ React.createElement( | ||
'ul', | ||
{ className: contentClass, style: listStyle }, | ||
{ className: contentClass }, | ||
this.renderOptions() | ||
@@ -435,2 +579,12 @@ ) | ||
setSelected: PropTypes.func.isRequired, | ||
style: PropTypes.shape({ | ||
arrow: PropTypes.func, | ||
dropdownButton: PropTypes.func, | ||
displayedValue: PropTypes.func, | ||
dropdownWrapper: PropTypes.func, | ||
groupContainer: PropTypes.func, | ||
groupHeading: PropTypes.func, | ||
optionContainer: PropTypes.func, | ||
optionItem: PropTypes.func | ||
}), | ||
width: PropTypes.number | ||
@@ -463,2 +617,3 @@ }; | ||
selectedValueClassName: undefined, | ||
style: {}, | ||
width: null | ||
@@ -465,0 +620,0 @@ }; |
'use strict'; | ||
var React = require('react'); | ||
var emotion = require('emotion'); | ||
var PropTypes = require('prop-types'); | ||
function createCommonjsModule(fn, module) { | ||
return module = { exports: {} }, fn(module, module.exports), module.exports; | ||
} | ||
var classnames = createCommonjsModule(function (module) { | ||
/*! | ||
Copyright (c) 2017 Jed Watson. | ||
Licensed under the MIT License (MIT), see | ||
http://jedwatson.github.io/classnames | ||
*/ | ||
/* global define */ | ||
(function () { | ||
var hasOwn = {}.hasOwnProperty; | ||
function classNames() { | ||
var classes = []; | ||
for (var i = 0; i < arguments.length; i++) { | ||
var arg = arguments[i]; | ||
if (!arg) continue; | ||
var argType = typeof arg; | ||
if (argType === 'string' || argType === 'number') { | ||
classes.push(arg); | ||
} else if (Array.isArray(arg) && arg.length) { | ||
var inner = classNames.apply(null, arg); | ||
if (inner) { | ||
classes.push(inner); | ||
} | ||
} else if (argType === 'object') { | ||
for (var key in arg) { | ||
if (hasOwn.call(arg, key) && arg[key]) { | ||
classes.push(key); | ||
} | ||
} | ||
} | ||
} | ||
return classes.join(' '); | ||
} | ||
if (module.exports) { | ||
classNames.default = classNames; | ||
module.exports = classNames; | ||
} else { | ||
window.classNames = classNames; | ||
} | ||
})(); | ||
}); | ||
var classnames_1 = classnames.classnames; | ||
const KEY_CODES = { | ||
@@ -74,3 +21,2 @@ UP_ARROW: 38, | ||
/* | ||
@@ -80,3 +26,3 @@ This file needs changes once Enzyme updates to be able to handle forwardRef in tests | ||
function defaultOptionRenderer(selectedOption, options, selectedOptionClassName, optionClassName, onOptionClicked, elementsRef) { | ||
function defaultOptionRenderer(selectedOption, options, selectedOptionClassName, optionClassName, onOptionClicked, elementsRef, getStyle) { | ||
return options.map(option => { | ||
@@ -89,6 +35,6 @@ const { groupOptions, label, value, className } = option; | ||
'div', | ||
{ key: label, className: 'dropdown-group' }, | ||
{ key: label, className: getStyle('groupContainer') }, | ||
React.createElement( | ||
'div', | ||
{ className: 'dropdown-group-heading' }, | ||
{ className: getStyle('groupHeading') }, | ||
React.createElement( | ||
@@ -106,3 +52,3 @@ 'div', | ||
option.groupOptions.map(groupOption => { | ||
const groupOptionClass = classnames(groupOption.className, groupOption.value === selectedOption ? selectedOptionClassName || 'dropdown-option-selected' : optionClassName || 'dropdown-option'); | ||
const groupOptionClass = emotion.cx(groupOption.className, getStyle('optionItem', groupOption.value === selectedOption)); | ||
return React.createElement( | ||
@@ -137,3 +83,3 @@ 'button', | ||
const optionClass = classnames(className, value === selectedOption ? selectedOptionClassName || 'dropdown-option-selected' : optionClassName || 'dropdown-option'); | ||
const optionClass = emotion.cx(className, getStyle('optionItem', value === selectedOption)); | ||
return React.createElement( | ||
@@ -167,2 +113,199 @@ 'button', | ||
const colours = { | ||
greys: { | ||
lighter: '#d9dadd', | ||
light: '#b5b6b7', | ||
base: '#808080', | ||
dark: '#595959', | ||
darker: '#404040' | ||
}, | ||
purple: { | ||
lighter: '#ccd1ed', | ||
light: '#a7aedf', | ||
base: '#990099' | ||
} | ||
}; | ||
const optionItemStyle = (props, state, selected) => ({ | ||
fontSize: '0.95em', | ||
overflow: 'hidden', | ||
textOverflow: 'ellipsis', | ||
whiteSpace: 'nowrap', | ||
padding: '5px 10px', | ||
width: '100%', | ||
textAlign: 'left', | ||
cursor: 'pointer', | ||
outline: 'none', | ||
backgroundColor: selected ? colours.purple.lighter : 'white', | ||
border: 'none', | ||
'&:hover': { | ||
backgroundColor: selected ? colours.purple.light : colours.greys.lighter | ||
}, | ||
'&:focus': { | ||
backgroundColor: selected ? colours.purple.light : colours.greys.lighter | ||
}, | ||
'.option-icon': { | ||
paddingRight: '5px' | ||
} | ||
}); | ||
const OptionItem = React.forwardRef((props, ref) => { | ||
const { | ||
onOptionClicked, | ||
option, | ||
optionClass | ||
} = props; | ||
return React.createElement( | ||
'button', | ||
{ | ||
'aria-label': option.ariaLabel, | ||
className: optionClass, | ||
onClick: onOptionClicked, | ||
onKeyDown: onOptionClicked, | ||
ref: ref, | ||
tabIndex: '-1', | ||
title: option.title, | ||
type: 'button' | ||
}, | ||
option.iconClass && React.createElement('i', { className: `${option.iconClass} option-icon` }), | ||
option.value | ||
); | ||
}); | ||
// Please Keep Alphabetical | ||
OptionItem.propTypes = { | ||
ariaLabel: PropTypes.string, | ||
option: PropTypes.shape({ | ||
ariaLabel: PropTypes.string, | ||
className: PropTypes.string, | ||
title: PropTypes.string, | ||
value: PropTypes.string.isRequired | ||
}).isRequired, | ||
optionClass: PropTypes.string | ||
}; | ||
const dropdownWrapper = ({ width, height }) => ({ | ||
width, | ||
height, | ||
position: 'relative', | ||
display: 'flex', | ||
flexDirection: 'column' | ||
}); | ||
const dropdownButton = (props, { open }) => ({ | ||
fontSize: '1em', | ||
display: 'flex', | ||
flexDirection: 'row', | ||
alignItems: 'center', | ||
width: '100%', | ||
height: '100%', | ||
backgroundColor: 'white', | ||
padding: '9px 5px', | ||
margin: '0', | ||
borderRadius: '0', | ||
borderBottom: `2px solid ${colours.greys.light}`, | ||
borderTop: 'none', | ||
borderRight: 'none', | ||
borderLeft: 'none', | ||
textAlign: 'left', | ||
cursor: 'pointer', | ||
outline: 'none', | ||
boxShadow: open ? `0px 1px 3px 2px ${colours.greys.lighter}` : 'none', | ||
'&:hover': { | ||
boxShadow: `0px 1px 3px 2px ${colours.greys.lighter}` | ||
}, | ||
'&:focus': { | ||
boxShadow: `0px 1px 3px 2px ${colours.greys.lighter}` | ||
}, | ||
'&:disabled': { | ||
cursor: 'not-allowed' | ||
} | ||
}); | ||
const displayedValue = ({ hideArrow, selectedOption, centerText }, { internalSelectedOption }) => ({ | ||
flex: '1', | ||
borderRight: hideArrow ? 'none' : `1px solid ${colours.greys.light}`, | ||
overflow: 'hidden', | ||
textOverflow: 'ellipsis', | ||
whiteSpace: 'nowrap', | ||
color: selectedOption || internalSelectedOption ? 'black' : colours.greys.base, | ||
textAlign: centerText ? 'center' : 'left' | ||
}); | ||
const arrow = (props, { open }) => ({ | ||
content: '""', | ||
width: '0', | ||
height: '0', | ||
marginLeft: '8px', | ||
marginRight: '5px', | ||
borderRight: '5px solid transparent', | ||
borderLeft: '5px solid transparent', | ||
borderTop: open ? '0' : `5px solid ${colours.greys.base}`, | ||
borderBottom: open ? `5px solid ${colours.greys.base}` : '0' | ||
}); | ||
const optionContainer = ({ openUp, maxContentHeight }, { open }) => ({ | ||
width: '100%', | ||
maxHeight: maxContentHeight || '175px', | ||
overflowY: maxContentHeight ? 'scroll' : null, | ||
zIndex: '9999', | ||
overflowX: 'hidden', | ||
position: 'absolute', | ||
left: '0', | ||
listStyleType: 'none', | ||
margin: '0', | ||
padding: '2px 0', | ||
backgroundColor: 'white', | ||
color: 'black', | ||
borderRadius: '2px', | ||
display: open ? 'block' : 'none', | ||
boxSizing: 'border-box', | ||
top: openUp ? null : '100%', | ||
bottom: openUp ? '105%' : null, | ||
boxShadow: openUp ? `0px -3px 3px 2px ${colours.greys.lighter}` : `0px 3px 3px 2px ${colours.greys.lighter}`, | ||
'&::-webkit-scrollbar': { | ||
width: '5px' | ||
}, | ||
'&::-webkit-scrollbar-track': { | ||
background: '#ddd' | ||
}, | ||
'&::-webkit-scrollbar-thumb': { | ||
background: '#666' | ||
} | ||
}); | ||
const groupContainer = () => ({ | ||
padding: '1em 0 0 0' | ||
}); | ||
const groupHeading = () => ({ | ||
color: 'grey', | ||
fontSize: '0.9em', | ||
padding: '0 10px 3px 5px', | ||
display: 'flex', | ||
flexDirection: 'row', | ||
justifyContent: 'space-between' | ||
}); | ||
const defaultStyles = { | ||
arrow, | ||
dropdownButton, | ||
displayedValue, | ||
dropdownWrapper, | ||
groupContainer, | ||
groupHeading, | ||
optionContainer, | ||
optionItem: optionItemStyle | ||
}; | ||
class Dropdown extends React.Component { | ||
@@ -259,2 +402,9 @@ constructor(props) { | ||
this.getStyle = (key, extraState) => { | ||
const { style } = this.props; | ||
const baseStyle = defaultStyles[key](this.props, this.state, extraState); | ||
const customStyle = style[key]; | ||
return customStyle ? emotion.css(customStyle(baseStyle, this.state, extraState)) : emotion.css(baseStyle); | ||
}; | ||
this.setFocus = () => { | ||
@@ -306,6 +456,6 @@ const { focusedIndex } = this.state; | ||
if (optionRenderer) { | ||
return optionRenderer(selectedOption || internalSelectedOption, options, this.onOptionClicked, this.elements); | ||
return optionRenderer(selectedOption || internalSelectedOption, options, this.onOptionClicked, this.elements, this.getStyle); | ||
} | ||
return defaultOptionRenderer(selectedOption, options, selectedOptionClassName, optionClassName, this.onOptionClicked, this.elements); | ||
return defaultOptionRenderer(selectedOption, options, selectedOptionClassName, optionClassName, this.onOptionClicked, this.elements, this.getStyle); | ||
}; | ||
@@ -340,15 +490,10 @@ | ||
arrowRenderer, | ||
centerText, | ||
contentClassName, | ||
buttonClassName, | ||
disabled, | ||
height, | ||
hideArrow, | ||
id, | ||
maxContentHeight, | ||
openUp, | ||
placeholder, | ||
selectedOption, | ||
selectedValueClassName, | ||
width | ||
selectedValueClassName | ||
} = this.props; | ||
@@ -362,7 +507,7 @@ | ||
const displayedValue = selectedOption || internalSelectedOption || placeholder || ''; | ||
const dropdownButtonClass = classnames('dropdown-select', buttonClassName); | ||
const displayedValueClass = classnames('displayed-value', selectedValueClassName, { grey: !selectedOption && !internalSelectedOption, 'no-arrow': hideArrow, 'center-text': centerText }); | ||
const contentClass = classnames('dropdown-content', contentClassName, { 'dropdown-content-open': open, 'dropdown-content-down': !openUp, 'dropdown-content-up': openUp }); | ||
const arrowClass = open ? 'dropdown-arrow up' : 'dropdown-arrow down'; | ||
const listStyle = maxContentHeight ? { maxHeight: maxContentHeight, overflowY: 'scroll' } : {}; | ||
const wrapperClass = this.getStyle('dropdownWrapper'); | ||
const dropdownButtonClass = emotion.cx(buttonClassName, this.getStyle('dropdownButton')); | ||
const displayedValueClass = emotion.cx(selectedValueClassName, this.getStyle('displayedValue')); | ||
const contentClass = emotion.cx(contentClassName, this.getStyle('optionContainer')); | ||
const arrowClass = this.getStyle('arrow'); | ||
@@ -372,6 +517,5 @@ return React.createElement( | ||
{ | ||
className: 'dropdown', | ||
className: wrapperClass, | ||
onKeyDown: this.onKeyDown, | ||
ref: div => this.container = div, | ||
style: { width, height } | ||
ref: div => this.container = div | ||
}, | ||
@@ -402,3 +546,3 @@ React.createElement( | ||
'ul', | ||
{ className: contentClass, style: listStyle }, | ||
{ className: contentClass }, | ||
this.renderOptions() | ||
@@ -435,2 +579,12 @@ ) | ||
setSelected: PropTypes.func.isRequired, | ||
style: PropTypes.shape({ | ||
arrow: PropTypes.func, | ||
dropdownButton: PropTypes.func, | ||
displayedValue: PropTypes.func, | ||
dropdownWrapper: PropTypes.func, | ||
groupContainer: PropTypes.func, | ||
groupHeading: PropTypes.func, | ||
optionContainer: PropTypes.func, | ||
optionItem: PropTypes.func | ||
}), | ||
width: PropTypes.number | ||
@@ -463,2 +617,3 @@ }; | ||
selectedValueClassName: undefined, | ||
style: {}, | ||
width: null | ||
@@ -465,0 +620,0 @@ }; |
@@ -0,0 +0,0 @@ if (process.env.NODE_ENV === 'production') { |
{ | ||
"name": "react-dropdown-aria", | ||
"version": "1.0.1", | ||
"version": "1.1.0", | ||
"description": "Simple and accessible React dropdown component", | ||
@@ -18,3 +18,3 @@ "main": "index.js", | ||
"test:update": "jest --updateSnapshot", | ||
"gzip-size": "node -e \"process.stdout.write('JS gzip size: ')\" && gzip-size --raw dist/react-dropdown-aria.min.js && node -e \"process.stdout.write('CSS gzip size: ')\" && gzip-size --raw dist/react-dropdown-aria.min.css", | ||
"gzip-size": "node -e \"process.stdout.write('JS gzip size: ')\" && gzip-size --raw dist/react-dropdown-aria.min.js", | ||
"analyze-bundle": "webpack --mode production --profile --json > stats.json && webpack-bundle-analyzer stats.json demo/dist -s gzip" | ||
@@ -45,2 +45,3 @@ }, | ||
"babel-loader": "^7.1.4", | ||
"babel-plugin-emotion": "^9.2.6", | ||
"babel-plugin-transform-react-remove-prop-types": "^0.4.13", | ||
@@ -63,2 +64,3 @@ "babel-preset-env": "^1.7.0", | ||
"jest": "^23.4.1", | ||
"jest-emotion": "^9.2.7", | ||
"jsx-loader": "^0.13.2", | ||
@@ -74,3 +76,2 @@ "node-sass": "^4.9.0", | ||
"rollup-plugin-node-resolve": "^3.3.0", | ||
"rollup-plugin-scss": "^0.4.0", | ||
"rollup-plugin-uglify": "^4.0.0", | ||
@@ -81,3 +82,2 @@ "sass-loader": "^7.0.3", | ||
"uglify-es": "^3.3.9", | ||
"uglifycss": "0.0.29", | ||
"webpack": "^4.12.0", | ||
@@ -89,3 +89,3 @@ "webpack-bundle-analyzer": "^2.13.1", | ||
"dependencies": { | ||
"classnames": "^2.2.6", | ||
"emotion": "^9.2.6", | ||
"prop-types": "^15.6.1" | ||
@@ -97,2 +97,7 @@ }, | ||
}, | ||
"bundlesize": [ | ||
{ | ||
"path": "./dist/react-dropdown-aria.min.js" | ||
} | ||
], | ||
"jest": { | ||
@@ -99,0 +104,0 @@ "modulePathIgnorePatterns": [ |
@@ -1,2 +0,3 @@ | ||
[![CircleCI](https://circleci.com/gh/jfangrad/react-aria-dropdown.svg?style=svg&circle-token=c8db79d70dddf853273a5964b860ec0bf53f5163)](https://circleci.com/gh/jfangrad/react-aria-dropdown/tree/master) [![npm](https://img.shields.io/npm/v/react-dropdown-aria.svg)](https://www.npmjs.com/package/react-dropdown-aria) | ||
[![CircleCI](https://circleci.com/gh/jfangrad/react-dropdown-aria.svg?style=svg&circle-token=c8db79d70dddf853273a5964b860ec0bf53f5163)](https://circleci.com/gh/jfangrad/react-dropdown-aria/tree/master) [![npm](https://img.shields.io/npm/v/react-dropdown-aria.svg)](https://www.npmjs.com/package/react-dropdown-aria) | ||
[![gzip size](http://img.badgesize.io/https://unpkg.com/react-dropdown-aria/dist/react-dropdown-aria.min.js?compression=gzip)](https://unpkg.com/react-dropdown-aria/dist/react-dropdown-aria.min.js) | ||
# react-dropdown-aria | ||
@@ -8,2 +9,9 @@ Simple, lightweight, and accessible React dropdown component. | ||
## Features | ||
1. Fully customizable styling | ||
2. Grouped options | ||
3. Accessible | ||
4. Type to find item | ||
5. Custom render function props | ||
# Demo And Examples | ||
@@ -24,3 +32,3 @@ For demo and examples checkout [https://jfangrad.github.io/react-dropdown-aria/](https://jfangrad.github.io/react-dropdown-aria/) | ||
import Dropdown from 'react-dropdown-aria'; | ||
import 'react-dropdown-aria/dist/react-dropdown-aria.min.scss'; | ||
import 'react-dropdown-aria/dist/react-dropdown-aria.min.css'; | ||
``` | ||
@@ -65,2 +73,36 @@ (You only need to include the styles once in your project) | ||
## Styling | ||
Custom styling can be applied to the dropdown through 2 ways: | ||
1. CSS className props (As seen in props table below) | ||
2. Emotion JavaScript Objects passed to `style` prop | ||
The suggested method is by using the style prop as shown in the [Custom Styling Example](https://github.com/jfangrad/react-aria-dropdown/blob/master/demo/src/Components/CustomStyles.jsx). | ||
The following object keys are supported to apply style to the corresponding part of the dropdown: | ||
``` | ||
arrow, dropdownButton, displayedValue, dropdownWrapper, groupContainer, groupHeading, optionContainer, optionItem | ||
``` | ||
The style prop should be passed and object containing one or more of the above properties. Each of those properties should be a function that returns an object. An example object is shown below: | ||
```js | ||
const style = { | ||
optionItem: (base, state, selected) => ({ | ||
...base, | ||
fontSize: '0.95em', | ||
color: selected ? 'white' : 'black', | ||
backgroundColor: selected ? '#00A3EF' : 'white', | ||
'&:hover': { | ||
backgroundColor: selected ? '#0092d6' : '#e0f5ff', | ||
}, | ||
'&:focus': { | ||
backgroundColor: selected ? '#0092d6' : '#e0f5ff', | ||
}, | ||
}), | ||
... | ||
} | ||
``` | ||
As shown above, each function will be called with `base` and `state` at minimum and some will be provided a third `extraState` parameter as shown with `optionStyle` where the extraState is whether it is currently selected or not. | ||
`base` - is the default styles object. It can either be spread out to extend the default styles (as shown above) or ommitted to ignore all default styles | ||
`state` - is the internal state of the dropdown | ||
## Dropdown Props | ||
@@ -92,2 +134,3 @@ | Property | Type | Default | Description | | ||
| `setSelected` | function | undefined | Function used to update the state of the selected value | | ||
| `style` | object | {} | Use to change the style of the dropdown through js instead of css (see styling section) | | ||
| `width` | number | null | Use to set the dropdown width manually | | ||
@@ -94,0 +137,0 @@ |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
46176
1095
146
1
6
+ Addedemotion@^9.2.6
+ Added@babel/code-frame@7.26.2(transitive)
+ Added@babel/generator@7.26.5(transitive)
+ Added@babel/helper-module-imports@7.25.9(transitive)
+ Added@babel/helper-string-parser@7.25.9(transitive)
+ Added@babel/helper-validator-identifier@7.25.9(transitive)
+ Added@babel/parser@7.26.7(transitive)
+ Added@babel/runtime@7.26.7(transitive)
+ Added@babel/template@7.25.9(transitive)
+ Added@babel/traverse@7.26.7(transitive)
+ Added@babel/types@7.26.7(transitive)
+ Added@emotion/babel-utils@0.6.10(transitive)
+ Added@emotion/hash@0.6.6(transitive)
+ Added@emotion/memoize@0.6.6(transitive)
+ Added@emotion/serialize@0.9.1(transitive)
+ Added@emotion/stylis@0.7.1(transitive)
+ Added@emotion/unitless@0.6.7(transitive)
+ Added@emotion/utils@0.8.2(transitive)
+ Added@jridgewell/gen-mapping@0.3.8(transitive)
+ Added@jridgewell/resolve-uri@3.1.2(transitive)
+ Added@jridgewell/set-array@1.2.1(transitive)
+ Added@jridgewell/sourcemap-codec@1.5.0(transitive)
+ Added@jridgewell/trace-mapping@0.3.25(transitive)
+ Added@types/parse-json@4.0.2(transitive)
+ Addedabbrev@1.1.1(transitive)
+ Addedbabel-plugin-emotion@9.2.11(transitive)
+ Addedbabel-plugin-macros@2.8.0(transitive)
+ Addedbabel-plugin-syntax-jsx@6.18.0(transitive)
+ Addedcallsites@3.1.0(transitive)
+ Addedconvert-source-map@1.9.0(transitive)
+ Addedcosmiconfig@6.0.0(transitive)
+ Addedcreate-emotion@9.2.12(transitive)
+ Addedcsstype@2.6.21(transitive)
+ Addeddebug@4.4.0(transitive)
+ Addedemotion@9.2.12(transitive)
+ Addederror-ex@1.3.2(transitive)
+ Addedfind-root@1.1.0(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedglobals@11.12.0(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addedimport-fresh@3.3.1(transitive)
+ Addedis-arrayish@0.2.1(transitive)
+ Addedis-core-module@2.16.1(transitive)
+ Addedjsesc@3.1.0(transitive)
+ Addedjson-parse-even-better-errors@2.3.1(transitive)
+ Addedlines-and-columns@1.2.4(transitive)
+ Addedminimist@1.2.8(transitive)
+ Addedmkdirp@0.5.6(transitive)
+ Addedms@2.1.3(transitive)
+ Addednopt@1.0.10(transitive)
+ Addedparent-module@1.0.1(transitive)
+ Addedparse-json@5.2.0(transitive)
+ Addedpath-parse@1.0.7(transitive)
+ Addedpath-type@4.0.0(transitive)
+ Addedpicocolors@1.1.1(transitive)
+ Addedregenerator-runtime@0.14.1(transitive)
+ Addedresolve@1.22.10(transitive)
+ Addedresolve-from@4.0.0(transitive)
+ Addedsource-map@0.5.70.7.4(transitive)
+ Addedstylis@3.5.4(transitive)
+ Addedstylis-rule-sheet@0.0.10(transitive)
+ Addedsupports-preserve-symlinks-flag@1.0.0(transitive)
+ Addedtouch@2.0.2(transitive)
+ Addedyaml@1.10.2(transitive)
- Removedclassnames@^2.2.6
- Removedclassnames@2.5.1(transitive)