From 8a12257f8e46d6a19c6d20482f0eed6dae01ed1a Mon Sep 17 00:00:00 2001 From: Mark Stacey Date: Wed, 6 May 2020 11:48:38 -0300 Subject: [PATCH] Add dropdown component (#8524) This new dropdown component uses a native `select` element, thus avoiding various issues encountered in attempting to reuse our existing dropdown components for the new permission system alert modal. The prefixed forms of `appearance` have been added temporarily so that the component can be used in Storybook, as our Storybook config isn't setup to do autoprefixing yet. Our real build system does handle autoprefixing for this rule correctly already. --- ui/app/components/app/index.scss | 2 + ui/app/components/ui/dropdown/dropdown.js | 63 +++++++++++++ ui/app/components/ui/dropdown/dropdown.scss | 22 +++++ .../ui/dropdown/dropdown.stories.js | 90 +++++++++++++++++++ ui/app/components/ui/dropdown/index.js | 1 + 5 files changed, 178 insertions(+) create mode 100644 ui/app/components/ui/dropdown/dropdown.js create mode 100644 ui/app/components/ui/dropdown/dropdown.scss create mode 100644 ui/app/components/ui/dropdown/dropdown.stories.js create mode 100644 ui/app/components/ui/dropdown/index.js diff --git a/ui/app/components/app/index.scss b/ui/app/components/app/index.scss index 49468037a..1f91ad3e4 100644 --- a/ui/app/components/app/index.scss +++ b/ui/app/components/app/index.scss @@ -100,6 +100,8 @@ @import '../ui/check-box/index'; +@import '../ui/dropdown/dropdown'; + @import 'permissions-connect-header/index'; @import 'permissions-connect-footer/index'; diff --git a/ui/app/components/ui/dropdown/dropdown.js b/ui/app/components/ui/dropdown/dropdown.js new file mode 100644 index 000000000..c709793b1 --- /dev/null +++ b/ui/app/components/ui/dropdown/dropdown.js @@ -0,0 +1,63 @@ +import React, { useCallback } from 'react' +import PropTypes from 'prop-types' +import classnames from 'classnames' + +const Dropdown = ({ className, disabled, onChange, options, selectedOption, style, title }) => { + const _onChange = useCallback( + (event) => { + event.preventDefault() + event.stopPropagation() + onChange(event.target.value) + }, + [onChange], + ) + + return ( + + ) +} + +Dropdown.propTypes = { + className: PropTypes.string, + disabled: PropTypes.bool, + title: PropTypes.string, + onChange: PropTypes.func.isRequired, + options: PropTypes.arrayOf( + PropTypes.exact({ + name: PropTypes.string, + value: PropTypes.string.isRequired, + }) + ).isRequired, + selectedOption: PropTypes.string, + style: PropTypes.object, +} + +Dropdown.defaultProps = { + className: undefined, + disabled: false, + title: undefined, + selectedOption: null, + style: undefined, +} + +export default Dropdown diff --git a/ui/app/components/ui/dropdown/dropdown.scss b/ui/app/components/ui/dropdown/dropdown.scss new file mode 100644 index 000000000..064c0788b --- /dev/null +++ b/ui/app/components/ui/dropdown/dropdown.scss @@ -0,0 +1,22 @@ +.dropdown { + appearance: none; + + // TODO: remove these after getting autoprefixer working in Storybook + -moz-appearance: none; + -webkit-appearance: none; + + border: 1px solid $Grey-500; + border-radius: 6px; + background-image: url('/images/icons/caret-down.svg'); + background-repeat: no-repeat, repeat; + background-position: right 18px top 50%; + background-color: white; + padding: 8px 32px 8px 16px; + font-family: Roboto, 'sans-serif'; + font-size: 14px; + + [dir='rtl'] & { + background-position: left 18px top 50%; + padding: 8px 16px 8px 32px; + } +} diff --git a/ui/app/components/ui/dropdown/dropdown.stories.js b/ui/app/components/ui/dropdown/dropdown.stories.js new file mode 100644 index 000000000..6e864e2ed --- /dev/null +++ b/ui/app/components/ui/dropdown/dropdown.stories.js @@ -0,0 +1,90 @@ +import React from 'react' +import { action } from '@storybook/addon-actions' +import Dropdown from '.' +import { boolean, select, text } from '@storybook/addon-knobs/react' + +export default { + title: 'Dropdown', +} + +const unnamedOptions = [...Array(10).keys()].map((index) => { + return { value: `option${index}` } +}) + +const namedOptions = unnamedOptions.map((option, index) => { + return Object.assign({}, option, { name: `Option ${index}` }) +}) + +const namedOptionsWithVeryLongNames = unnamedOptions.map((option, index) => { + return Object.assign({}, option, { name: `Option ${index} with a very${', very'.repeat(index)} long name` }) +}) + + +export const simple = () => ( + option.value), + namedOptions[0].value + ) + } + /> +) + +export const optionsWithoutNames = () => ( + option.value), + unnamedOptions[0].value + ) + } + /> +) + +export const optionsWithLongNames = () => ( + option.value), + namedOptionsWithVeryLongNames[0].value + ) + } + /> +) + +export const optionsWithLongNamesAndShortWidth = () => ( + option.value), + namedOptionsWithVeryLongNames[0].value + ) + } + style={{ width: '200px' }} + /> +) diff --git a/ui/app/components/ui/dropdown/index.js b/ui/app/components/ui/dropdown/index.js new file mode 100644 index 000000000..8d23b5709 --- /dev/null +++ b/ui/app/components/ui/dropdown/index.js @@ -0,0 +1 @@ +export { default } from './dropdown'