From 6d086673d8611999c8e314eb14f6b75b5ea369f8 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Tue, 15 Jan 2019 17:39:53 +0100 Subject: [PATCH 01/20] feat(Dropdown): add `loading` prop --- .../State/DropdownExampleLoading.knobs.tsx | 23 ++++ .../DropdownExampleLoading.shorthand.tsx | 26 ++++ .../components/Dropdown/State/index.tsx | 15 +++ ...ropdownExampleMultipleSearch.shorthand.tsx | 2 +- ...wnExampleMultipleSearchFluid.shorthand.tsx | 1 - ...ultipleSearchImageAndContent.shorthand.tsx | 1 - ...leMultipleSearchToggleButton.shorthand.tsx | 1 - .../examples/components/Dropdown/index.tsx | 3 + src/components/Dropdown/Dropdown.tsx | 126 +++++++++--------- src/components/Dropdown/DropdownIndicator.tsx | 51 +++++++ .../Dropdown/DropdownMessageLoading.tsx | 35 +++++ .../Dropdown/DropdownMessageNoResults.tsx | 36 +++++ src/themes/teams/componentStyles.ts | 7 + .../Dropdown/dropdownIndicatorStyles.ts | 20 +++ .../Dropdown/dropdownMessageLoadingStyles.ts | 11 ++ .../dropdownMessageNoResultsStyles.ts | 13 ++ .../components/Dropdown/dropdownStyles.ts | 18 --- 17 files changed, 307 insertions(+), 82 deletions(-) create mode 100644 docs/src/examples/components/Dropdown/State/DropdownExampleLoading.knobs.tsx create mode 100644 docs/src/examples/components/Dropdown/State/DropdownExampleLoading.shorthand.tsx create mode 100644 docs/src/examples/components/Dropdown/State/index.tsx create mode 100644 src/components/Dropdown/DropdownIndicator.tsx create mode 100644 src/components/Dropdown/DropdownMessageLoading.tsx create mode 100644 src/components/Dropdown/DropdownMessageNoResults.tsx create mode 100644 src/themes/teams/components/Dropdown/dropdownIndicatorStyles.ts create mode 100644 src/themes/teams/components/Dropdown/dropdownMessageLoadingStyles.ts create mode 100644 src/themes/teams/components/Dropdown/dropdownMessageNoResultsStyles.ts diff --git a/docs/src/examples/components/Dropdown/State/DropdownExampleLoading.knobs.tsx b/docs/src/examples/components/Dropdown/State/DropdownExampleLoading.knobs.tsx new file mode 100644 index 0000000000..9ddf0ac6dd --- /dev/null +++ b/docs/src/examples/components/Dropdown/State/DropdownExampleLoading.knobs.tsx @@ -0,0 +1,23 @@ +import * as React from 'react' +import Knobs from 'docs/src/components/Knobs/Knobs' + +type DropdownExampleLoadingKnobsProps = { + loading?: boolean + onKnobChange: () => void +} + +const DropdownExampleLoadingKnobs: React.FC = props => { + const { loading, onKnobChange } = props + + return ( + + + + ) +} + +DropdownExampleLoadingKnobs.defaultProps = { + loading: true, +} + +export default DropdownExampleLoadingKnobs diff --git a/docs/src/examples/components/Dropdown/State/DropdownExampleLoading.shorthand.tsx b/docs/src/examples/components/Dropdown/State/DropdownExampleLoading.shorthand.tsx new file mode 100644 index 0000000000..5f28b67856 --- /dev/null +++ b/docs/src/examples/components/Dropdown/State/DropdownExampleLoading.shorthand.tsx @@ -0,0 +1,26 @@ +import { Dropdown } from '@stardust-ui/react' +import * as React from 'react' + +const inputItems = [ + 'Bruce Wayne', + 'Natasha Romanoff', + 'Steven Strange', + 'Alfred Pennyworth', + `Scarlett O'Hara`, + 'Imperator Furiosa', + 'Bruce Banner', + 'Peter Parker', + 'Selina Kyle', +] + +const DropdownExampleLoading: React.FC<{ knobs: { loading: boolean } }> = ({ knobs }) => ( + +) + +export default DropdownExampleLoading diff --git a/docs/src/examples/components/Dropdown/State/index.tsx b/docs/src/examples/components/Dropdown/State/index.tsx new file mode 100644 index 0000000000..13dd9b39d6 --- /dev/null +++ b/docs/src/examples/components/Dropdown/State/index.tsx @@ -0,0 +1,15 @@ +import * as React from 'react' +import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample' +import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection' + +const State = () => ( + + + +) + +export default State diff --git a/docs/src/examples/components/Dropdown/Types/DropdownExampleMultipleSearch.shorthand.tsx b/docs/src/examples/components/Dropdown/Types/DropdownExampleMultipleSearch.shorthand.tsx index 6aba57a1d9..688da7bf25 100644 --- a/docs/src/examples/components/Dropdown/Types/DropdownExampleMultipleSearch.shorthand.tsx +++ b/docs/src/examples/components/Dropdown/Types/DropdownExampleMultipleSearch.shorthand.tsx @@ -19,7 +19,7 @@ const DropdownExample = () => ( search getA11ySelectionMessage={getA11ySelectionMessage} getA11yStatusMessage={getA11yStatusMessage} - noResultsMessage="We couldn't find any matches." + messageNoResults="We couldn't find any matches." placeholder="Start typing a name" items={inputItems} /> diff --git a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchFluid.shorthand.tsx b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchFluid.shorthand.tsx index 5d1de96419..7585476968 100644 --- a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchFluid.shorthand.tsx +++ b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchFluid.shorthand.tsx @@ -17,7 +17,6 @@ const DropdownExample = () => ( multiple getA11ySelectionMessage={getA11ySelectionMessage} getA11yStatusMessage={getA11yStatusMessage} - noResultsMessage="We couldn't find any matches." search fluid placeholder="Start typing a name" diff --git a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchImageAndContent.shorthand.tsx b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchImageAndContent.shorthand.tsx index 248055b67a..45f585093a 100644 --- a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchImageAndContent.shorthand.tsx +++ b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchImageAndContent.shorthand.tsx @@ -55,7 +55,6 @@ const DropdownExample = () => ( getA11yStatusMessage={getA11yStatusMessage} search getA11ySelectionMessage={getA11ySelectionMessage} - noResultsMessage="We couldn't find any matches." placeholder="Start typing a name" items={inputItems} /> diff --git a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx index 3fb6470421..58a09eea97 100644 --- a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx +++ b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx @@ -18,7 +18,6 @@ const DropdownExample = () => ( multiple getA11yStatusMessage={getA11yStatusMessage} getA11ySelectionMessage={getA11ySelectionMessage} - noResultsMessage="We couldn't find any matches." search placeholder="Start typing a name" toggleButton diff --git a/docs/src/examples/components/Dropdown/index.tsx b/docs/src/examples/components/Dropdown/index.tsx index 233ac6af92..3e6810b2cb 100644 --- a/docs/src/examples/components/Dropdown/index.tsx +++ b/docs/src/examples/components/Dropdown/index.tsx @@ -1,11 +1,14 @@ import * as React from 'react' + import Types from './Types' import Variations from './Variations' +import State from './State' const DropdownExamples = () => (
+
) diff --git a/src/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx index 1a0080ec5c..7efa175457 100644 --- a/src/components/Dropdown/Dropdown.tsx +++ b/src/components/Dropdown/Dropdown.tsx @@ -3,11 +3,7 @@ import * as PropTypes from 'prop-types' import * as _ from 'lodash' import { Extendable, ShorthandValue, ComponentEventHandler } from '../../../types/utils' -import { - ComponentSlotStylesInput, - ComponentVariablesInput, - ComponentSlotClasses, -} from '../../themes/types' +import { ComponentSlotStylesInput, ComponentVariablesInput } from '../../themes/types' import Downshift, { DownshiftState, StateChangeOptions, @@ -24,13 +20,15 @@ import { customPropTypes, commonPropTypes, handleRef, + UIComponentProps, } from '../../lib' import keyboardKey from 'keyboard-key' import List from '../List/List' -import Text from '../Text/Text' import Ref from '../Ref/Ref' -import { UIComponentProps } from '../../lib/commonPropInterfaces' +import DropdownIndicator from './DropdownIndicator' import DropdownItem from './DropdownItem' +import DropdownMessageLoading from './DropdownMessageLoading' +import DropdownMessageNoResults from './DropdownMessageNoResults' import DropdownSelectedItem, { DropdownSelectedItemProps } from './DropdownSelectedItem' import DropdownSearchInput, { DropdownSearchInputProps } from './DropdownSearchInput' import Button from '../Button/Button' @@ -70,6 +68,8 @@ export interface DropdownProps extends UIComponentProps) => string + indicator?: ShorthandValue + /** Array of props for generating list options (Dropdown.Item[]) and selected item labels(Dropdown.SelectedItem[]), if it's a multiple selection. */ items?: ShorthandValue[] @@ -78,11 +78,16 @@ export interface DropdownProps extends UIComponentProps string + loading?: boolean + /** A dropdown can perform a multiple selection. */ multiple?: boolean - /** A string to be displayed in the list when dropdown has no available items to show. */ - noResultsMessage?: string + /** A message to be displayed in the list when dropdown is loading. */ + messageLoading?: ShorthandValue + + /** A message to be displayed in the list when dropdown has no available items to show. */ + messageNoResults?: ShorthandValue /** * Callback for change in dropdown search query value. @@ -110,9 +115,6 @@ export interface DropdownProps extends UIComponentProps { if (!item || React.isValidElement(item)) { return '' @@ -186,6 +191,8 @@ export default class Dropdown extends AutoControlledComponent< return `${item}` }, + messageLoading: 'Loading...', + messageNoResults: "We couldn't find any matches.", } static autoControlledProps = ['searchQuery', 'value'] @@ -211,7 +218,15 @@ export default class Dropdown extends AutoControlledComponent< variables, unhandledProps, }: RenderResultConfig) { - const { search, multiple, toggleButton, getA11yStatusMessage, itemToString } = this.props + const { + fluid, + indicator, + loading, + search, + multiple, + getA11yStatusMessage, + itemToString, + } = this.props const { searchQuery } = this.state return ( @@ -262,7 +277,15 @@ export default class Dropdown extends AutoControlledComponent< variables, ) : this.renderTriggerButton(styles, getToggleButtonProps)} - {toggleButton && this.renderToggleButton(getToggleButtonProps, classes, isOpen)} + {DropdownIndicator.create(indicator, { + defaultProps: { + ...getToggleButtonProps(), + fluid, + loading, + open: isOpen, + variables, + }, + })} {this.renderItemsList( styles, variables, @@ -322,7 +345,7 @@ export default class Dropdown extends AutoControlledComponent< ) => void, variables, ): JSX.Element { - const { searchInput, multiple, placeholder, toggleButton } = this.props + const { indicator, searchInput, multiple, placeholder } = this.props const { searchQuery, value } = this.state const noPlaceholder = @@ -331,7 +354,7 @@ export default class Dropdown extends AutoControlledComponent< return DropdownSearchInput.create(searchInput || {}, { defaultProps: { placeholder: noPlaceholder ? '' : placeholder, - hasToggleButton: !!toggleButton, + hasToggleButton: !!indicator, variables, inputRef: this.inputRef, }, @@ -346,19 +369,6 @@ export default class Dropdown extends AutoControlledComponent< }) } - private renderToggleButton( - getToggleButtonProps: (options?: GetToggleButtonPropsOptions) => any, - classes: ComponentSlotClasses, - isOpen: boolean, - ) { - const { onClick } = getToggleButtonProps() - return ( - - {isOpen ? String.fromCharCode(9650) : String.fromCharCode(9660)} - - ) - } - private renderItemsList( styles: ComponentSlotStylesInput, variables: ComponentVariablesInput, @@ -370,7 +380,10 @@ export default class Dropdown extends AutoControlledComponent< getItemProps: (options: GetItemPropsOptions) => any, getInputProps: (options?: GetInputPropsOptions) => any, ) { - const accessibilityMenuProps = getMenuProps({ refKey: 'innerRef' }, { suppressRefError: true }) + const { innerRef, ...accessibilityMenuProps } = getMenuProps( + { refKey: 'innerRef' }, + { suppressRefError: true }, + ) const { search } = this.props // If it's just a selection, some attributes and listeners from Downshift input need to go on the menu list. if (!search) { @@ -387,7 +400,7 @@ export default class Dropdown extends AutoControlledComponent< ) } } - const { innerRef, ...accessibilityMenuPropsRest } = accessibilityMenuProps + return ( { @@ -396,49 +409,42 @@ export default class Dropdown extends AutoControlledComponent< }} > ) } private renderItems( - styles: ComponentSlotStylesInput, variables: ComponentVariablesInput, getItemProps: (options: GetItemPropsOptions) => any, highlightedIndex: number, ) { - const { noResultsMessage } = this.props + const { loading, messageLoading, messageNoResults } = this.props + const filteredItems = this.getItemsFilteredBySearchQuery() + const items = filteredItems.map((item, index) => + DropdownItem.create(item, { + defaultProps: { + active: highlightedIndex === index, + variables, + ...(typeof item === 'object' && + !item.hasOwnProperty('key') && { + key: (item as any).header, + }), + }, + overrideProps: () => this.handleItemOverrides(item, index, getItemProps), + }), + ) - if (filteredItems.length > 0) { - return filteredItems.map((item, index) => { - return DropdownItem.create(item, { - defaultProps: { - active: highlightedIndex === index, - variables, - ...(typeof item === 'object' && - !item.hasOwnProperty('key') && { - key: (item as any).header, - }), - }, - overrideProps: () => this.handleItemOverrides(item, index, getItemProps), - }) - }) - } - // render no match message. return [ - noResultsMessage - ? { - key: 'dropdown-no-results', - content: , - styles: styles.emptyListItem, - } - : null, + ...items, + loading && DropdownMessageLoading.create(messageLoading), + !loading && items.length === 0 && DropdownMessageNoResults.create(messageNoResults), ] } diff --git a/src/components/Dropdown/DropdownIndicator.tsx b/src/components/Dropdown/DropdownIndicator.tsx new file mode 100644 index 0000000000..89350517c3 --- /dev/null +++ b/src/components/Dropdown/DropdownIndicator.tsx @@ -0,0 +1,51 @@ +import * as _ from 'lodash' +import * as PropTypes from 'prop-types' +import * as React from 'react' + +import { ReactProps } from '../../../types/utils' +import { commonPropTypes, createShorthandFactory, UIComponent, UIComponentProps } from '../../lib' +import Loader from '../Loader/Loader' + +export interface DropdownIndicatorProps extends UIComponentProps { + /** A dropdown can take the width of its container. */ + fluid?: boolean + + loading: boolean + onClick?: (e: React.SyntheticEvent, props: DropdownIndicatorProps) => void + open: boolean +} + +/** + * A DropdownIndicator is a sub-component that outputs an indicator. + */ +class DropdownIndicator extends UIComponent> { + static className = 'ui-dropdown__indicator' + static create: Function + static displayName = 'DropdownIndicator' + static propTypes = { + ...commonPropTypes.createCommon({ children: false, content: false }), + fluid: PropTypes.bool, + loading: PropTypes.bool, + onClick: PropTypes.func, + open: PropTypes.bool, + } + + handleClick = (e: React.SyntheticEvent) => { + _.invoke(this.props, 'onClick', e, this.props) + } + + renderComponent({ classes, ElementType, unhandledProps }) { + const { loading, open } = this.props + const content = open ? String.fromCharCode(9650) : String.fromCharCode(9660) + + return ( + + {loading ? : {content}} + + ) + } +} + +DropdownIndicator.create = createShorthandFactory(DropdownIndicator) + +export default DropdownIndicator diff --git a/src/components/Dropdown/DropdownMessageLoading.tsx b/src/components/Dropdown/DropdownMessageLoading.tsx new file mode 100644 index 0000000000..a9187127d8 --- /dev/null +++ b/src/components/Dropdown/DropdownMessageLoading.tsx @@ -0,0 +1,35 @@ +import * as React from 'react' + +import { ReactProps } from '../../../types/utils' +import { + commonPropTypes, + ContentComponentProps, + createShorthandFactory, + UIComponent, + UIComponentProps, +} from '../../lib' +import ListItem from '../List/ListItem' + +export interface DropdownMessageLoadingProps + extends UIComponentProps, + ContentComponentProps {} + +/** + * A DropdownMessageLoading is a sub-component that outputs a message when the Dropdown is loading. + */ +class DropdownMessageLoading extends UIComponent> { + static className = 'ui-dropdown__message-loading' + static create: Function + static displayName = 'DropdownMessageLoading' + static propTypes = commonPropTypes.createCommon({ children: false }) + + renderComponent({ classes, ElementType, unhandledProps }) { + const { content } = this.props + + return + } +} + +DropdownMessageLoading.create = createShorthandFactory(DropdownMessageLoading, 'content') + +export default DropdownMessageLoading diff --git a/src/components/Dropdown/DropdownMessageNoResults.tsx b/src/components/Dropdown/DropdownMessageNoResults.tsx new file mode 100644 index 0000000000..ecbeb33f15 --- /dev/null +++ b/src/components/Dropdown/DropdownMessageNoResults.tsx @@ -0,0 +1,36 @@ +import * as React from 'react' + +import { ReactProps } from '../../../types/utils' +import { + commonPropTypes, + ContentComponentProps, + createShorthandFactory, + UIComponent, + UIComponentProps, +} from '../../lib' +import ListItem from '../List/ListItem' + +export interface DropdownMessageNoResultsProps + extends UIComponentProps, + ContentComponentProps {} + +/** + * A DropdownMessageNoResults is a sub-component that outputs a message when a list of filtered + * items is empty. + */ +class DropdownMessageNoResults extends UIComponent> { + static className = 'ui-dropdown__message-no-results' + static create: Function + static displayName = 'DropdownMessageNoResults' + static propTypes = commonPropTypes.createCommon({ children: false }) + + renderComponent({ classes, ElementType, unhandledProps }) { + const { content } = this.props + + return + } +} + +DropdownMessageNoResults.create = createShorthandFactory(DropdownMessageNoResults, 'content') + +export default DropdownMessageNoResults diff --git a/src/themes/teams/componentStyles.ts b/src/themes/teams/componentStyles.ts index b0d43027de..9b03cb040b 100644 --- a/src/themes/teams/componentStyles.ts +++ b/src/themes/teams/componentStyles.ts @@ -16,6 +16,13 @@ export { default as ChatMessage } from './components/Chat/chatMessageStyles' export { default as Divider } from './components/Divider/dividerStyles' export { default as Dropdown } from './components/Dropdown/dropdownStyles' +export { default as DropdownIndicator } from './components/Dropdown/dropdownIndicatorStyles' +export { + default as DropdownMessageLoading, +} from './components/Dropdown/dropdownMessageLoadingStyles' +export { + default as DropdownMessageNoResults, +} from './components/Dropdown/dropdownMessageNoResultsStyles' export { default as DropdownSearchInput } from './components/Dropdown/dropdownSearchInputStyles' export { default as DropdownSelectedItem } from './components/Dropdown/dropdownSelectedItemStyles' export { default as DropdownItem } from './components/Dropdown/dropdownItemStyles' diff --git a/src/themes/teams/components/Dropdown/dropdownIndicatorStyles.ts b/src/themes/teams/components/Dropdown/dropdownIndicatorStyles.ts new file mode 100644 index 0000000000..f6c502e81f --- /dev/null +++ b/src/themes/teams/components/Dropdown/dropdownIndicatorStyles.ts @@ -0,0 +1,20 @@ +import { DropdownMessageNoResultsProps } from '../../../../components/Dropdown/DropdownMessageNoResults' +import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types' + +const dropdownIndicatorStyles: ComponentSlotStylesInput = { + root: ({ props: p, variables: v }): ICSSInJSStyle => ({ + position: 'absolute', + height: v.toggleButtonSize, + width: v.toggleButtonSize, + cursor: 'pointer', + backgroundColor: 'transparent', + margin: 0, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + userSelect: 'none', + ...(p.fluid ? { right: 0 } : { left: `calc(${v.width} - ${v.toggleButtonSize})` }), + }), +} + +export default dropdownIndicatorStyles diff --git a/src/themes/teams/components/Dropdown/dropdownMessageLoadingStyles.ts b/src/themes/teams/components/Dropdown/dropdownMessageLoadingStyles.ts new file mode 100644 index 0000000000..66defe68f3 --- /dev/null +++ b/src/themes/teams/components/Dropdown/dropdownMessageLoadingStyles.ts @@ -0,0 +1,11 @@ +import ListItem from '../../../../components/List/ListItem' +import { DropdownMessageNoResultsProps } from '../../../../components/Dropdown/DropdownMessageNoResults' +import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types' + +const dropdownSelectedItemStyles: ComponentSlotStylesInput = { + root: ({ variables: v }): ICSSInJSStyle => ({ + [`&.${ListItem.className}`]: { backgroundColor: v.listItemBackgroundColor }, + }), +} + +export default dropdownSelectedItemStyles diff --git a/src/themes/teams/components/Dropdown/dropdownMessageNoResultsStyles.ts b/src/themes/teams/components/Dropdown/dropdownMessageNoResultsStyles.ts new file mode 100644 index 0000000000..310d4b0495 --- /dev/null +++ b/src/themes/teams/components/Dropdown/dropdownMessageNoResultsStyles.ts @@ -0,0 +1,13 @@ +import ListItem from '../../../../components/List/ListItem' +import { DropdownMessageNoResultsProps } from '../../../../components/Dropdown/DropdownMessageNoResults' +import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types' + +const dropdownSelectedItemStyles: ComponentSlotStylesInput = { + root: ({ variables: v }): ICSSInJSStyle => ({ + [`&.${ListItem.className}`]: { backgroundColor: v.listItemBackgroundColor }, + + fontWeight: 'bold', + }), +} + +export default dropdownSelectedItemStyles diff --git a/src/themes/teams/components/Dropdown/dropdownStyles.ts b/src/themes/teams/components/Dropdown/dropdownStyles.ts index f808ad6fd1..fcc9a3aa92 100644 --- a/src/themes/teams/components/Dropdown/dropdownStyles.ts +++ b/src/themes/teams/components/Dropdown/dropdownStyles.ts @@ -72,24 +72,6 @@ const dropdownStyles: ComponentSlotStylesInput top: 'calc(100% + 2px)', // leave room for container + its border background: listBackgroundColor, }), - - emptyListItem: ({ variables: { listItemBackgroundColor } }) => ({ - backgroundColor: listItemBackgroundColor, - }), - - toggleButton: ({ variables: { toggleButtonSize, width }, props: { fluid } }): ICSSInJSStyle => ({ - position: 'absolute', - height: toggleButtonSize, - width: toggleButtonSize, - cursor: 'pointer', - backgroundColor: 'transparent', - margin: 0, - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - userSelect: 'none', - ...(fluid ? { right: 0 } : { left: `calc(${width} - ${toggleButtonSize})` }), - }), } export default dropdownStyles From 00d8b83e48f6d31d8281ff8fcc70645831ab87ee Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Thu, 17 Jan 2019 11:35:32 +0100 Subject: [PATCH 02/20] push cleanups --- docs/src/examples/components/Dropdown/State/index.tsx | 1 + .../Types/DropdownExampleMultipleSearch.shorthand.tsx | 1 - src/components/Dropdown/Dropdown.tsx | 11 +++++++---- src/components/Dropdown/DropdownIndicator.tsx | 10 ++++++++++ 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/docs/src/examples/components/Dropdown/State/index.tsx b/docs/src/examples/components/Dropdown/State/index.tsx index 13dd9b39d6..ffe6ef9d9b 100644 --- a/docs/src/examples/components/Dropdown/State/index.tsx +++ b/docs/src/examples/components/Dropdown/State/index.tsx @@ -1,4 +1,5 @@ import * as React from 'react' + import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample' import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection' diff --git a/docs/src/examples/components/Dropdown/Types/DropdownExampleMultipleSearch.shorthand.tsx b/docs/src/examples/components/Dropdown/Types/DropdownExampleMultipleSearch.shorthand.tsx index 688da7bf25..da7e2d070b 100644 --- a/docs/src/examples/components/Dropdown/Types/DropdownExampleMultipleSearch.shorthand.tsx +++ b/docs/src/examples/components/Dropdown/Types/DropdownExampleMultipleSearch.shorthand.tsx @@ -19,7 +19,6 @@ const DropdownExample = () => ( search getA11ySelectionMessage={getA11ySelectionMessage} getA11yStatusMessage={getA11yStatusMessage} - messageNoResults="We couldn't find any matches." placeholder="Start typing a name" items={inputItems} /> diff --git a/src/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx index 7efa175457..3e08ee66c5 100644 --- a/src/components/Dropdown/Dropdown.tsx +++ b/src/components/Dropdown/Dropdown.tsx @@ -68,7 +68,8 @@ export interface DropdownProps extends UIComponentProps) => string - indicator?: ShorthandValue + /** Whether a toggle button (that shows/hides items list) or a loader should be rendered. */ + indicator?: boolean /** Array of props for generating list options (Dropdown.Item[]) and selected item labels(Dropdown.SelectedItem[]), if it's a multiple selection. */ items?: ShorthandValue[] @@ -78,6 +79,7 @@ export interface DropdownProps extends UIComponentProps string + /** A dropdown can show that it is currently loading data. */ loading?: boolean /** A dropdown can perform a multiple selection. */ @@ -157,7 +159,7 @@ export default class Dropdown extends AutoControlledComponent< fluid: PropTypes.bool, getA11ySelectionMessage: PropTypes.object, getA11yStatusMessage: PropTypes.func, - indicator: customPropTypes.itemShorthand, + indicator: PropTypes.bool, items: customPropTypes.collectionShorthand, itemToString: PropTypes.func, loading: PropTypes.bool, @@ -178,7 +180,7 @@ export default class Dropdown extends AutoControlledComponent< static defaultProps = { as: 'div', - indicator: '', + indicator: true, itemToString: item => { if (!item || React.isValidElement(item)) { return '' @@ -277,7 +279,8 @@ export default class Dropdown extends AutoControlledComponent< variables, ) : this.renderTriggerButton(styles, getToggleButtonProps)} - {DropdownIndicator.create(indicator, { + /* TODO: Make `indicator` a fully working shorthand. */ + {DropdownIndicator.create(indicator && '', { defaultProps: { ...getToggleButtonProps(), fluid, diff --git a/src/components/Dropdown/DropdownIndicator.tsx b/src/components/Dropdown/DropdownIndicator.tsx index 89350517c3..8b42be09d9 100644 --- a/src/components/Dropdown/DropdownIndicator.tsx +++ b/src/components/Dropdown/DropdownIndicator.tsx @@ -10,8 +10,18 @@ export interface DropdownIndicatorProps extends UIComponentProps void + + /** Controls whether or not the dropdown menu is displayed. */ open: boolean } From de8fcc1c43ee32299c7e9d0376cdbbd655c9294a Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Thu, 17 Jan 2019 11:43:01 +0100 Subject: [PATCH 03/20] add changelog entry --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14f64d70b5..111bf31d09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### BREAKING - Rename `DropdownLabel` to `DropdownSelectedItem` and extract styles @layershifter ([#725](https://github.com/stardust-ui/react/pull/725)) +- Changes in `Dropdown` @layershifter ([#729](https://github.com/stardust-ui/react/pull/729)) + - rename `noResultsMessage` prop to `messageNoResults` + - `messageNoResults` has default value by default + - rename `toggleButton` prop to `indicator` + - `indicator` is visible by default ### Fixes - Remove `render` from default factories options @layershifter ([#735](https://github.com/stardust-ui/react/pull/735)) @@ -31,6 +36,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Features - Add accessibility for submenu in toolbar and menu behavior @kolaps33 ([#686] (https://github.com/stardust-ui/react/pull/686)) +- Add `loading` prop for `Dropdown` @layershifter ([#729](https://github.com/stardust-ui/react/pull/729)) ## [v0.16.2](https://github.com/stardust-ui/react/tree/v0.16.2) (2019-01-14) From d2564b309267dfd9cad8d61de40b6d245174a5c1 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Thu, 17 Jan 2019 12:58:45 +0100 Subject: [PATCH 04/20] add prototype --- docs/src/components/CodeSnippet.tsx | 7 +- docs/src/components/Editor/Editor.tsx | 3 +- docs/src/components/Sidebar/Sidebar.tsx | 8 ++ .../AsyncDropdownSearch.tsx | 98 +++++++++++++++++++ .../prototypes/AsyncDropdownSearch/index.ts | 1 + docs/src/routes.tsx | 6 ++ src/components/Dropdown/Dropdown.tsx | 9 +- 7 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx create mode 100644 docs/src/prototypes/AsyncDropdownSearch/index.ts diff --git a/docs/src/components/CodeSnippet.tsx b/docs/src/components/CodeSnippet.tsx index 8231eed77d..b19fd2504c 100644 --- a/docs/src/components/CodeSnippet.tsx +++ b/docs/src/components/CodeSnippet.tsx @@ -6,20 +6,21 @@ import Editor, { EDITOR_BACKGROUND_COLOR } from './Editor' export interface CodeSnippetProps { fitted?: boolean label?: string - mode?: 'jsx' | 'html' | 'sh' - value: string + mode?: 'json' | 'jsx' | 'html' | 'sh' + value: string | Object style?: React.CSSProperties } const formatters = { sh: (val: string = ''): string => val.replace(/^/g, '$ '), html: (val: string = ''): string => formatCode(val, 'html'), + json: (val: Object = {}): string => JSON.stringify(val, null, 2), jsx: (val: string = ''): string => formatCode(val, 'babylon'), } const CodeSnippet = ({ fitted, label, value, mode = 'jsx', ...restProps }: CodeSnippetProps) => { const format = formatters[mode] - const formattedValue = format(value) + const formattedValue = format(value as any) // remove eof line break, they are not helpful for snippets .replace(/\n$/, '') diff --git a/docs/src/components/Editor/Editor.tsx b/docs/src/components/Editor/Editor.tsx index 5a7800b5bb..68af8df931 100644 --- a/docs/src/components/Editor/Editor.tsx +++ b/docs/src/components/Editor/Editor.tsx @@ -5,6 +5,7 @@ import AceEditor, { AceEditorProps } from 'react-ace' import * as ace from 'brace' import 'brace/ext/language_tools' import 'brace/mode/html' +import 'brace/mode/json' import 'brace/mode/jsx' import 'brace/mode/sh' import 'brace/theme/tomorrow_night' @@ -47,7 +48,7 @@ languageTools.addCompleter(semanticUIReactCompleter) export interface EditorProps extends AceEditorProps { active?: boolean highlightGutterLine?: boolean - mode?: 'html' | 'jsx' | 'sh' + mode?: 'html' | 'jsx' | 'sh' | 'json' value?: string showCursor?: boolean } diff --git a/docs/src/components/Sidebar/Sidebar.tsx b/docs/src/components/Sidebar/Sidebar.tsx index 281359c40b..202d1ea872 100644 --- a/docs/src/components/Sidebar/Sidebar.tsx +++ b/docs/src/components/Sidebar/Sidebar.tsx @@ -273,6 +273,14 @@ class Sidebar extends React.Component { > Chat message with popover + + Async Dropdown Search + ({ + image: faker.internet.avatar(), + header: `${faker.name.firstName()} ${faker.name.lastName()}`, + content: faker.commerce.department(), +}) + +// ---------------------------------------- +// Prototype Search Page View +// ---------------------------------------- +class AsyncDropdownSearch extends React.Component<{}, SearchPageState> { + state = { + loading: false, + searchQuery: '', + items: [], + value: [], + } + + searchTimer: number + + handleSelectedChange = (e: React.SyntheticEvent, { searchQuery, value }: DropdownProps) => { + this.setState({ value: value as Entry[], searchQuery }) + } + + handleSearchQueryChange = (e: React.SyntheticEvent, { searchQuery }: DropdownProps) => { + this.setState({ searchQuery }) + this.fetchItems() + } + + fetchItems = () => { + clearTimeout(this.searchTimer) + this.setState({ loading: true }) + + this.searchTimer = setTimeout(() => { + this.setState(prevState => ({ + loading: false, + items: [...prevState.items, ..._.times(10, createEntry)], + })) + }, 2000) + } + + render() { + const { items, loading, searchQuery, value } = this.state + + return ( +
+ +
+

Use the field to perform a simulated search.

+ + + + + + + +
+ ) + } +} + +export default AsyncDropdownSearch diff --git a/docs/src/prototypes/AsyncDropdownSearch/index.ts b/docs/src/prototypes/AsyncDropdownSearch/index.ts new file mode 100644 index 0000000000..4f527e450b --- /dev/null +++ b/docs/src/prototypes/AsyncDropdownSearch/index.ts @@ -0,0 +1 @@ +export { default } from './AsyncDropdownSearch' diff --git a/docs/src/routes.tsx b/docs/src/routes.tsx index e1060aaa85..f44f5b380d 100644 --- a/docs/src/routes.tsx +++ b/docs/src/routes.tsx @@ -60,6 +60,12 @@ const Router = () => ( path="/prototype-search-page" component={require('./prototypes/SearchPage/index').default} />, + , Date: Tue, 22 Jan 2019 11:09:41 +0200 Subject: [PATCH 05/20] revert some changes --- CHANGELOG.md | 2 -- ...ownExampleMultipleSearchFluid.shorthand.tsx | 1 + ...MultipleSearchImageAndContent.shorthand.tsx | 1 + ...pleMultipleSearchToggleButton.shorthand.tsx | 1 + src/components/Dropdown/Dropdown.tsx | 18 ++++++++---------- .../Dropdown/dropdownIndicatorStyles.ts | 4 ++-- .../Dropdown/dropdownMessageLoadingStyles.ts | 4 ++-- 7 files changed, 15 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 111bf31d09..736512caf1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,8 +20,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### BREAKING - Rename `DropdownLabel` to `DropdownSelectedItem` and extract styles @layershifter ([#725](https://github.com/stardust-ui/react/pull/725)) - Changes in `Dropdown` @layershifter ([#729](https://github.com/stardust-ui/react/pull/729)) - - rename `noResultsMessage` prop to `messageNoResults` - - `messageNoResults` has default value by default - rename `toggleButton` prop to `indicator` - `indicator` is visible by default diff --git a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchFluid.shorthand.tsx b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchFluid.shorthand.tsx index 7585476968..5d1de96419 100644 --- a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchFluid.shorthand.tsx +++ b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchFluid.shorthand.tsx @@ -17,6 +17,7 @@ const DropdownExample = () => ( multiple getA11ySelectionMessage={getA11ySelectionMessage} getA11yStatusMessage={getA11yStatusMessage} + noResultsMessage="We couldn't find any matches." search fluid placeholder="Start typing a name" diff --git a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchImageAndContent.shorthand.tsx b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchImageAndContent.shorthand.tsx index 45f585093a..248055b67a 100644 --- a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchImageAndContent.shorthand.tsx +++ b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchImageAndContent.shorthand.tsx @@ -55,6 +55,7 @@ const DropdownExample = () => ( getA11yStatusMessage={getA11yStatusMessage} search getA11ySelectionMessage={getA11ySelectionMessage} + noResultsMessage="We couldn't find any matches." placeholder="Start typing a name" items={inputItems} /> diff --git a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx index 58a09eea97..3fb6470421 100644 --- a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx +++ b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx @@ -18,6 +18,7 @@ const DropdownExample = () => ( multiple getA11yStatusMessage={getA11yStatusMessage} getA11ySelectionMessage={getA11ySelectionMessage} + noResultsMessage="We couldn't find any matches." search placeholder="Start typing a name" toggleButton diff --git a/src/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx index 9ac584c9dd..86221f74da 100644 --- a/src/components/Dropdown/Dropdown.tsx +++ b/src/components/Dropdown/Dropdown.tsx @@ -82,14 +82,14 @@ export interface DropdownProps extends UIComponentProps) => any, highlightedIndex: number, ) { - const { loading, messageLoading, messageNoResults } = this.props + const { loading, loadingMessage, noResultsMessage } = this.props const filteredItems = this.getItemsFilteredBySearchQuery() const items = filteredItems.map((item, index) => @@ -446,8 +444,8 @@ export default class Dropdown extends AutoControlledComponent< return [ ...items, - loading && DropdownMessageLoading.create(messageLoading), - !loading && items.length === 0 && DropdownMessageNoResults.create(messageNoResults), + loading && DropdownMessageLoading.create(loadingMessage), + !loading && items.length === 0 && DropdownMessageNoResults.create(noResultsMessage), ] } diff --git a/src/themes/teams/components/Dropdown/dropdownIndicatorStyles.ts b/src/themes/teams/components/Dropdown/dropdownIndicatorStyles.ts index f6c502e81f..7338f2ce16 100644 --- a/src/themes/teams/components/Dropdown/dropdownIndicatorStyles.ts +++ b/src/themes/teams/components/Dropdown/dropdownIndicatorStyles.ts @@ -1,7 +1,7 @@ -import { DropdownMessageNoResultsProps } from '../../../../components/Dropdown/DropdownMessageNoResults' +import { DropdownIndicatorProps } from '../../../../components/Dropdown/DropdownIndicator' import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types' -const dropdownIndicatorStyles: ComponentSlotStylesInput = { +const dropdownIndicatorStyles: ComponentSlotStylesInput = { root: ({ props: p, variables: v }): ICSSInJSStyle => ({ position: 'absolute', height: v.toggleButtonSize, diff --git a/src/themes/teams/components/Dropdown/dropdownMessageLoadingStyles.ts b/src/themes/teams/components/Dropdown/dropdownMessageLoadingStyles.ts index 66defe68f3..2c12cc934c 100644 --- a/src/themes/teams/components/Dropdown/dropdownMessageLoadingStyles.ts +++ b/src/themes/teams/components/Dropdown/dropdownMessageLoadingStyles.ts @@ -1,8 +1,8 @@ import ListItem from '../../../../components/List/ListItem' -import { DropdownMessageNoResultsProps } from '../../../../components/Dropdown/DropdownMessageNoResults' +import { DropdownMessageLoadingProps } from '../../../../components/Dropdown/DropdownMessageLoading' import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types' -const dropdownSelectedItemStyles: ComponentSlotStylesInput = { +const dropdownSelectedItemStyles: ComponentSlotStylesInput = { root: ({ variables: v }): ICSSInJSStyle => ({ [`&.${ListItem.className}`]: { backgroundColor: v.listItemBackgroundColor }, }), From 19e5d4563c422d219d0c4751968ff281da28df05 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Tue, 22 Jan 2019 11:13:19 +0200 Subject: [PATCH 06/20] fix export names --- .../components/Dropdown/dropdownMessageLoadingStyles.ts | 4 ++-- .../components/Dropdown/dropdownMessageNoResultsStyles.ts | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/themes/teams/components/Dropdown/dropdownMessageLoadingStyles.ts b/src/themes/teams/components/Dropdown/dropdownMessageLoadingStyles.ts index 2c12cc934c..00e6007b53 100644 --- a/src/themes/teams/components/Dropdown/dropdownMessageLoadingStyles.ts +++ b/src/themes/teams/components/Dropdown/dropdownMessageLoadingStyles.ts @@ -2,10 +2,10 @@ import ListItem from '../../../../components/List/ListItem' import { DropdownMessageLoadingProps } from '../../../../components/Dropdown/DropdownMessageLoading' import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types' -const dropdownSelectedItemStyles: ComponentSlotStylesInput = { +const dropdownMessageLoadingStyles: ComponentSlotStylesInput = { root: ({ variables: v }): ICSSInJSStyle => ({ [`&.${ListItem.className}`]: { backgroundColor: v.listItemBackgroundColor }, }), } -export default dropdownSelectedItemStyles +export default dropdownMessageLoadingStyles diff --git a/src/themes/teams/components/Dropdown/dropdownMessageNoResultsStyles.ts b/src/themes/teams/components/Dropdown/dropdownMessageNoResultsStyles.ts index 310d4b0495..01c878b426 100644 --- a/src/themes/teams/components/Dropdown/dropdownMessageNoResultsStyles.ts +++ b/src/themes/teams/components/Dropdown/dropdownMessageNoResultsStyles.ts @@ -2,7 +2,10 @@ import ListItem from '../../../../components/List/ListItem' import { DropdownMessageNoResultsProps } from '../../../../components/Dropdown/DropdownMessageNoResults' import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types' -const dropdownSelectedItemStyles: ComponentSlotStylesInput = { +const dropdownMessageNoResultsStyles: ComponentSlotStylesInput< + DropdownMessageNoResultsProps, + any +> = { root: ({ variables: v }): ICSSInJSStyle => ({ [`&.${ListItem.className}`]: { backgroundColor: v.listItemBackgroundColor }, @@ -10,4 +13,4 @@ const dropdownSelectedItemStyles: ComponentSlotStylesInput Date: Tue, 22 Jan 2019 11:37:44 +0200 Subject: [PATCH 07/20] more cleanups --- .../DropdownExampleLoading.shorthand.tsx | 1 + ...ropdownExampleMultipleSearch.shorthand.tsx | 1 + ...leMultipleSearchToggleButton.shorthand.tsx | 55 ------------------- .../components/Dropdown/Variations/index.tsx | 5 -- .../AsyncDropdownSearch.tsx | 5 +- src/components/Dropdown/Dropdown.tsx | 11 +++- src/components/Dropdown/DropdownIndicator.tsx | 14 +++-- 7 files changed, 24 insertions(+), 68 deletions(-) delete mode 100644 docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx diff --git a/docs/src/examples/components/Dropdown/State/DropdownExampleLoading.shorthand.tsx b/docs/src/examples/components/Dropdown/State/DropdownExampleLoading.shorthand.tsx index 5f28b67856..74ea305045 100644 --- a/docs/src/examples/components/Dropdown/State/DropdownExampleLoading.shorthand.tsx +++ b/docs/src/examples/components/Dropdown/State/DropdownExampleLoading.shorthand.tsx @@ -16,6 +16,7 @@ const inputItems = [ const DropdownExampleLoading: React.FC<{ knobs: { loading: boolean } }> = ({ knobs }) => ( ( search getA11ySelectionMessage={getA11ySelectionMessage} getA11yStatusMessage={getA11yStatusMessage} + noResultsMessage="We couldn't find any matches." placeholder="Start typing a name" items={inputItems} /> diff --git a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx b/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx deleted file mode 100644 index 3fb6470421..0000000000 --- a/docs/src/examples/components/Dropdown/Variations/DropdownExampleMultipleSearchToggleButton.shorthand.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import * as React from 'react' -import { Dropdown } from '@stardust-ui/react' - -const inputItems = [ - 'Bruce Wayne', - 'Natasha Romanoff', - 'Steven Strange', - 'Alfred Pennyworth', - `Scarlett O'Hara`, - 'Imperator Furiosa', - 'Bruce Banner', - 'Peter Parker', - 'Selina Kyle', -] - -const DropdownExample = () => ( - -) - -const getA11ySelectionMessage = { - onAdd: item => `${item} has been selected.`, - onRemove: item => `${item} has been removed.`, -} - -const getA11yStatusMessage = ({ - isOpen, - itemToString, - previousResultCount, - resultCount, - selectedItem, -}) => { - if (!isOpen) { - return selectedItem ? itemToString(selectedItem) : '' - } - if (!resultCount) { - return 'No results are available.' - } - if (resultCount !== previousResultCount) { - return `${resultCount} result${ - resultCount === 1 ? ' is' : 's are' - } available, use up and down arrow keys to navigate. Press Enter key to select.` - } - return '' -} - -export default DropdownExample diff --git a/docs/src/examples/components/Dropdown/Variations/index.tsx b/docs/src/examples/components/Dropdown/Variations/index.tsx index ca3ecfd32f..d750fec0bd 100644 --- a/docs/src/examples/components/Dropdown/Variations/index.tsx +++ b/docs/src/examples/components/Dropdown/Variations/index.tsx @@ -14,11 +14,6 @@ const Variations = () => ( description="A multiple search dropdown that fits the width of the container." examplePath="components/Dropdown/Variations/DropdownExampleMultipleSearchFluid" /> - ) diff --git a/docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx b/docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx index 1eb509ab15..0820bf0c14 100644 --- a/docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx +++ b/docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx @@ -1,4 +1,4 @@ -import { Divider, Dropdown, DropdownProps, Header, Segment } from '@stardust-ui/react' +import { Divider, Dropdown, DropdownProps, Header, Loader, Segment } from '@stardust-ui/react' import * as faker from 'faker' import * as _ from 'lodash' import * as React from 'react' @@ -79,6 +79,9 @@ class AsyncDropdownSearch extends React.Component<{}, SearchPageState> { indicator={false} items={items} loading={loading} + loadingMessage={{ + content: , + }} multiple onSearchQueryChange={this.handleSearchQueryChange} onSelectedChange={this.handleSelectedChange} diff --git a/src/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx index ecc2ee7fd7..16bb5f7bb5 100644 --- a/src/components/Dropdown/Dropdown.tsx +++ b/src/components/Dropdown/Dropdown.tsx @@ -2,7 +2,12 @@ import * as React from 'react' import * as PropTypes from 'prop-types' import * as _ from 'lodash' -import { Extendable, ShorthandRenderFunction, ShorthandValue, ComponentEventHandler } from '../../../types/utils' +import { + Extendable, + ShorthandRenderFunction, + ShorthandValue, + ComponentEventHandler, +} from '../../../types/utils' import { ComponentSlotStylesInput, ComponentVariablesInput } from '../../themes/types' import Downshift, { DownshiftState, @@ -181,9 +186,9 @@ export default class Dropdown extends AutoControlledComponent< items: customPropTypes.collectionShorthand, itemToString: PropTypes.func, loading: PropTypes.bool, + loadingMessage: customPropTypes.itemShorthand, multiple: PropTypes.bool, - messageLoading: PropTypes.string, - noResultsMessage: PropTypes.string, + noResultsMessage: customPropTypes.itemShorthand, onSearchQueryChange: PropTypes.func, onSelectedChange: PropTypes.func, placeholder: PropTypes.string, diff --git a/src/components/Dropdown/DropdownIndicator.tsx b/src/components/Dropdown/DropdownIndicator.tsx index 8b42be09d9..d8e54f3928 100644 --- a/src/components/Dropdown/DropdownIndicator.tsx +++ b/src/components/Dropdown/DropdownIndicator.tsx @@ -2,8 +2,9 @@ import * as _ from 'lodash' import * as PropTypes from 'prop-types' import * as React from 'react' -import { ReactProps } from '../../../types/utils' +import { ComponentEventHandler, ReactProps } from '../../../types/utils' import { commonPropTypes, createShorthandFactory, UIComponent, UIComponentProps } from '../../lib' +import Indicator from '../Indicator/Indicator' import Loader from '../Loader/Loader' export interface DropdownIndicatorProps extends UIComponentProps { @@ -19,7 +20,7 @@ export interface DropdownIndicatorProps extends UIComponentProps void + onClick?: ComponentEventHandler /** Controls whether or not the dropdown menu is displayed. */ open: boolean @@ -46,11 +47,16 @@ class DropdownIndicator extends UIComponent> renderComponent({ classes, ElementType, unhandledProps }) { const { loading, open } = this.props - const content = open ? String.fromCharCode(9650) : String.fromCharCode(9660) return ( - {loading ? : {content}} + {loading ? ( + + ) : ( + Indicator.create('', { + defaultProps: { direction: open ? 'top' : 'bottom' }, + }) + )} ) } From ec5bb2aa84d595faf378c484dd61ed4dde9254c9 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Tue, 22 Jan 2019 11:51:48 +0200 Subject: [PATCH 08/20] more cleanups --- docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx b/docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx index 0820bf0c14..6a715e3319 100644 --- a/docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx +++ b/docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx @@ -2,6 +2,7 @@ import { Divider, Dropdown, DropdownProps, Header, Loader, Segment } from '@star import * as faker from 'faker' import * as _ from 'lodash' import * as React from 'react' + import CodeSnippet from '../../components/CodeSnippet' // ---------------------------------------- From 8fe5741f8e4528ea54ad272bb66bde5b059e7505 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Tue, 22 Jan 2019 18:28:48 +0200 Subject: [PATCH 09/20] final cleanups --- docs/src/components/Editor/Editor.tsx | 2 +- .../AsyncDropdownSearch.tsx | 2 +- src/components/Dropdown/Dropdown.tsx | 37 ++++------ src/components/Dropdown/DropdownIndicator.tsx | 67 ------------------- src/themes/teams/componentStyles.ts | 1 - .../Dropdown/dropdownIndicatorStyles.ts | 20 ------ .../components/Dropdown/dropdownStyles.ts | 14 ++++ 7 files changed, 29 insertions(+), 114 deletions(-) delete mode 100644 src/components/Dropdown/DropdownIndicator.tsx delete mode 100644 src/themes/teams/components/Dropdown/dropdownIndicatorStyles.ts diff --git a/docs/src/components/Editor/Editor.tsx b/docs/src/components/Editor/Editor.tsx index 68af8df931..17ca6bf077 100644 --- a/docs/src/components/Editor/Editor.tsx +++ b/docs/src/components/Editor/Editor.tsx @@ -62,7 +62,7 @@ class Editor extends React.Component { static propTypes = { value: PropTypes.string.isRequired, - mode: PropTypes.oneOf(['html', 'jsx', 'sh']), + mode: PropTypes.oneOf(['html', 'json', 'jsx', 'sh']), active: PropTypes.bool, showCursor: PropTypes.bool, } diff --git a/docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx b/docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx index 6a715e3319..ef754b8d8b 100644 --- a/docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx +++ b/docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx @@ -77,7 +77,6 @@ class AsyncDropdownSearch extends React.Component<{}, SearchPageState> { { placeholder="Try to enter something..." search searchQuery={searchQuery} + toggleIndicator={false} value={value} /> diff --git a/src/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx index 16bb5f7bb5..f03efe83db 100644 --- a/src/components/Dropdown/Dropdown.tsx +++ b/src/components/Dropdown/Dropdown.tsx @@ -28,9 +28,9 @@ import { UIComponentProps, } from '../../lib' import keyboardKey from 'keyboard-key' +import Indicator from '../Indicator/Indicator' import List from '../List/List' import Ref from '../Ref/Ref' -import DropdownIndicator from './DropdownIndicator' import DropdownItem from './DropdownItem' import DropdownMessageLoading from './DropdownMessageLoading' import DropdownMessageNoResults from './DropdownMessageNoResults' @@ -73,9 +73,6 @@ export interface DropdownProps extends UIComponentProps) => string - /** Whether a toggle button (that shows/hides items list) or a loader should be rendered. */ - indicator?: boolean - /** Array of props for generating list options (Dropdown.Item[]) and selected item labels(Dropdown.SelectedItem[]), if it's a multiple selection. */ items?: ShorthandValue[] @@ -140,6 +137,9 @@ export interface DropdownProps extends UIComponentProps { if (!item || React.isValidElement(item)) { return '' @@ -218,6 +217,7 @@ export default class Dropdown extends AutoControlledComponent< return `${item}` }, + toggleIndicator: {}, } static autoControlledProps = ['searchQuery', 'value'] @@ -243,15 +243,7 @@ export default class Dropdown extends AutoControlledComponent< variables, unhandledProps, }: RenderResultConfig) { - const { - fluid, - indicator, - loading, - search, - multiple, - getA11yStatusMessage, - itemToString, - } = this.props + const { search, multiple, getA11yStatusMessage, itemToString, toggleIndicator } = this.props const { searchQuery } = this.state return ( @@ -302,14 +294,11 @@ export default class Dropdown extends AutoControlledComponent< variables, ) : this.renderTriggerButton(styles, getToggleButtonProps)} - {/* TODO: Make `indicator` a fully working shorthand. */} - {DropdownIndicator.create(indicator && '', { + {Indicator.create(toggleIndicator, { defaultProps: { - ...getToggleButtonProps(), - fluid, - loading, - open: isOpen, - variables, + direction: isOpen ? 'top' : 'bottom', + onClick: getToggleButtonProps().onClick, + styles: styles.toggleIndicator, }, })} {this.renderItemsList( @@ -371,7 +360,7 @@ export default class Dropdown extends AutoControlledComponent< ) => void, variables, ): JSX.Element { - const { indicator, searchInput, multiple, placeholder } = this.props + const { searchInput, multiple, placeholder, toggleIndicator } = this.props const { searchQuery, value } = this.state const noPlaceholder = @@ -380,7 +369,7 @@ export default class Dropdown extends AutoControlledComponent< return DropdownSearchInput.create(searchInput || {}, { defaultProps: { placeholder: noPlaceholder ? '' : placeholder, - hasToggleButton: !!indicator, + hasToggleButton: !!toggleIndicator, variables, inputRef: this.inputRef, }, diff --git a/src/components/Dropdown/DropdownIndicator.tsx b/src/components/Dropdown/DropdownIndicator.tsx deleted file mode 100644 index d8e54f3928..0000000000 --- a/src/components/Dropdown/DropdownIndicator.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import * as _ from 'lodash' -import * as PropTypes from 'prop-types' -import * as React from 'react' - -import { ComponentEventHandler, ReactProps } from '../../../types/utils' -import { commonPropTypes, createShorthandFactory, UIComponent, UIComponentProps } from '../../lib' -import Indicator from '../Indicator/Indicator' -import Loader from '../Loader/Loader' - -export interface DropdownIndicatorProps extends UIComponentProps { - /** A dropdown can take the width of its container. */ - fluid?: boolean - - /** A dropdown can show that it is currently loading data. */ - loading: boolean - - /** - * Called on click. - * - * @param {SyntheticEvent} event - React's original SyntheticEvent. - * @param {object} data - All props. - */ - onClick?: ComponentEventHandler - - /** Controls whether or not the dropdown menu is displayed. */ - open: boolean -} - -/** - * A DropdownIndicator is a sub-component that outputs an indicator. - */ -class DropdownIndicator extends UIComponent> { - static className = 'ui-dropdown__indicator' - static create: Function - static displayName = 'DropdownIndicator' - static propTypes = { - ...commonPropTypes.createCommon({ children: false, content: false }), - fluid: PropTypes.bool, - loading: PropTypes.bool, - onClick: PropTypes.func, - open: PropTypes.bool, - } - - handleClick = (e: React.SyntheticEvent) => { - _.invoke(this.props, 'onClick', e, this.props) - } - - renderComponent({ classes, ElementType, unhandledProps }) { - const { loading, open } = this.props - - return ( - - {loading ? ( - - ) : ( - Indicator.create('', { - defaultProps: { direction: open ? 'top' : 'bottom' }, - }) - )} - - ) - } -} - -DropdownIndicator.create = createShorthandFactory(DropdownIndicator) - -export default DropdownIndicator diff --git a/src/themes/teams/componentStyles.ts b/src/themes/teams/componentStyles.ts index 9b03cb040b..7789107d6a 100644 --- a/src/themes/teams/componentStyles.ts +++ b/src/themes/teams/componentStyles.ts @@ -16,7 +16,6 @@ export { default as ChatMessage } from './components/Chat/chatMessageStyles' export { default as Divider } from './components/Divider/dividerStyles' export { default as Dropdown } from './components/Dropdown/dropdownStyles' -export { default as DropdownIndicator } from './components/Dropdown/dropdownIndicatorStyles' export { default as DropdownMessageLoading, } from './components/Dropdown/dropdownMessageLoadingStyles' diff --git a/src/themes/teams/components/Dropdown/dropdownIndicatorStyles.ts b/src/themes/teams/components/Dropdown/dropdownIndicatorStyles.ts deleted file mode 100644 index 7338f2ce16..0000000000 --- a/src/themes/teams/components/Dropdown/dropdownIndicatorStyles.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { DropdownIndicatorProps } from '../../../../components/Dropdown/DropdownIndicator' -import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types' - -const dropdownIndicatorStyles: ComponentSlotStylesInput = { - root: ({ props: p, variables: v }): ICSSInJSStyle => ({ - position: 'absolute', - height: v.toggleButtonSize, - width: v.toggleButtonSize, - cursor: 'pointer', - backgroundColor: 'transparent', - margin: 0, - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - userSelect: 'none', - ...(p.fluid ? { right: 0 } : { left: `calc(${v.width} - ${v.toggleButtonSize})` }), - }), -} - -export default dropdownIndicatorStyles diff --git a/src/themes/teams/components/Dropdown/dropdownStyles.ts b/src/themes/teams/components/Dropdown/dropdownStyles.ts index fcc9a3aa92..a3c84fddfd 100644 --- a/src/themes/teams/components/Dropdown/dropdownStyles.ts +++ b/src/themes/teams/components/Dropdown/dropdownStyles.ts @@ -72,6 +72,20 @@ const dropdownStyles: ComponentSlotStylesInput top: 'calc(100% + 2px)', // leave room for container + its border background: listBackgroundColor, }), + + toggleIndicator: ({ props: p, variables: v }): ICSSInJSStyle => ({ + position: 'absolute', + height: v.toggleButtonSize, + width: v.toggleButtonSize, + cursor: 'pointer', + backgroundColor: 'transparent', + margin: 0, + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + userSelect: 'none', + ...(p.fluid ? { right: 0 } : { left: `calc(${v.width} - ${v.toggleButtonSize})` }), + }), } export default dropdownStyles From 1b4772e1ce51b6c588dae0a7d718cb5a7dc2774d Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Tue, 22 Jan 2019 18:35:40 +0200 Subject: [PATCH 10/20] update description --- docs/src/examples/components/Dropdown/State/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/examples/components/Dropdown/State/index.tsx b/docs/src/examples/components/Dropdown/State/index.tsx index ffe6ef9d9b..00564198a6 100644 --- a/docs/src/examples/components/Dropdown/State/index.tsx +++ b/docs/src/examples/components/Dropdown/State/index.tsx @@ -7,7 +7,7 @@ const State = () => ( From ec117856aaec88fc31f225e68290a37835e83609 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Tue, 22 Jan 2019 19:26:39 +0200 Subject: [PATCH 11/20] rename to `toggleIndicatorSize` --- .../teams/components/Dropdown/dropdownSearchInputStyles.ts | 4 ++-- src/themes/teams/components/Dropdown/dropdownStyles.ts | 6 +++--- src/themes/teams/components/Dropdown/dropdownVariables.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/themes/teams/components/Dropdown/dropdownSearchInputStyles.ts b/src/themes/teams/components/Dropdown/dropdownSearchInputStyles.ts index 16c52581ff..ad70ab946d 100644 --- a/src/themes/teams/components/Dropdown/dropdownSearchInputStyles.ts +++ b/src/themes/teams/components/Dropdown/dropdownSearchInputStyles.ts @@ -17,13 +17,13 @@ const dropdownSearchInputStyles: ComponentSlotStylesInput< }), combobox: ({ - variables: { comboboxFlexBasis, toggleButtonSize }, + variables: { comboboxFlexBasis, toggleIndicatorSize }, props: { hasToggleButton }, }): ICSSInJSStyle => ({ flexBasis: comboboxFlexBasis, flexGrow: 1, ...(hasToggleButton && { - marginRight: toggleButtonSize, + marginRight: toggleIndicatorSize, }), }), } diff --git a/src/themes/teams/components/Dropdown/dropdownStyles.ts b/src/themes/teams/components/Dropdown/dropdownStyles.ts index a3c84fddfd..1b9b8fdc57 100644 --- a/src/themes/teams/components/Dropdown/dropdownStyles.ts +++ b/src/themes/teams/components/Dropdown/dropdownStyles.ts @@ -75,8 +75,8 @@ const dropdownStyles: ComponentSlotStylesInput toggleIndicator: ({ props: p, variables: v }): ICSSInJSStyle => ({ position: 'absolute', - height: v.toggleButtonSize, - width: v.toggleButtonSize, + height: v.toggleIndicatorSize, + width: v.toggleIndicatorSize, cursor: 'pointer', backgroundColor: 'transparent', margin: 0, @@ -84,7 +84,7 @@ const dropdownStyles: ComponentSlotStylesInput justifyContent: 'center', alignItems: 'center', userSelect: 'none', - ...(p.fluid ? { right: 0 } : { left: `calc(${v.width} - ${v.toggleButtonSize})` }), + ...(p.fluid ? { right: 0 } : { left: `calc(${v.width} - ${v.toggleIndicatorSize})` }), }), } diff --git a/src/themes/teams/components/Dropdown/dropdownVariables.ts b/src/themes/teams/components/Dropdown/dropdownVariables.ts index bf50a4d00f..63879ae015 100644 --- a/src/themes/teams/components/Dropdown/dropdownVariables.ts +++ b/src/themes/teams/components/Dropdown/dropdownVariables.ts @@ -15,7 +15,7 @@ export interface DropdownVariables { listItemBackgroundColorActive: string listItemColorActive: string listMaxHeight: string - toggleButtonSize: string + toggleIndicatorSize: string width: string } @@ -37,6 +37,6 @@ export default (siteVars): DropdownVariables => ({ listItemBackgroundColorActive: siteVars.brand, listItemColorActive: siteVars.white, listMaxHeight: '20rem', - toggleButtonSize: pxToRem(32), + toggleIndicatorSize: pxToRem(32), width: pxToRem(356), }) From 0e3c61ca8261c3345a5ccc6d21730e106cf5b6c7 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Tue, 22 Jan 2019 19:51:58 +0200 Subject: [PATCH 12/20] fix JSON mode --- docs/src/components/CodeSnippet.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/src/components/CodeSnippet.tsx b/docs/src/components/CodeSnippet.tsx index 07565084bb..a825617211 100644 --- a/docs/src/components/CodeSnippet.tsx +++ b/docs/src/components/CodeSnippet.tsx @@ -11,9 +11,8 @@ export interface CodeSnippetProps { style?: React.CSSProperties } -const joinToString = (stringOrArray: string | string[]) => { - return typeof stringOrArray === 'string' ? stringOrArray : stringOrArray.join('\n') -} +const joinToString = (stringOrArray: string | string[]) => + Array.isArray(stringOrArray) ? stringOrArray.join('\n') : stringOrArray const formatters = { sh: (val: string = ''): string => val.replace(/^/g, '$ '), From d88dbce9d3a828d4c1b1a3c2e7faee94c3683aba Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Tue, 22 Jan 2019 19:57:07 +0200 Subject: [PATCH 13/20] reduce items in example --- .../State/DropdownExampleLoading.shorthand.tsx | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/docs/src/examples/components/Dropdown/State/DropdownExampleLoading.shorthand.tsx b/docs/src/examples/components/Dropdown/State/DropdownExampleLoading.shorthand.tsx index 74ea305045..1504c449ad 100644 --- a/docs/src/examples/components/Dropdown/State/DropdownExampleLoading.shorthand.tsx +++ b/docs/src/examples/components/Dropdown/State/DropdownExampleLoading.shorthand.tsx @@ -1,17 +1,7 @@ import { Dropdown } from '@stardust-ui/react' import * as React from 'react' -const inputItems = [ - 'Bruce Wayne', - 'Natasha Romanoff', - 'Steven Strange', - 'Alfred Pennyworth', - `Scarlett O'Hara`, - 'Imperator Furiosa', - 'Bruce Banner', - 'Peter Parker', - 'Selina Kyle', -] +const inputItems = ['Bruce Wayne', 'Natasha Romanoff', 'Steven Strange', 'Alfred Pennyworth'] const DropdownExampleLoading: React.FC<{ knobs: { loading: boolean } }> = ({ knobs }) => ( Date: Wed, 23 Jan 2019 11:14:55 +0200 Subject: [PATCH 14/20] wip --- docs/src/components/CodeSnippet.tsx | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/src/components/CodeSnippet.tsx b/docs/src/components/CodeSnippet.tsx index a825617211..071cfa6848 100644 --- a/docs/src/components/CodeSnippet.tsx +++ b/docs/src/components/CodeSnippet.tsx @@ -1,29 +1,34 @@ +import * as _ from 'lodash' import * as React from 'react' import formatCode from '../utils/formatCode' import Editor, { EDITOR_BACKGROUND_COLOR } from './Editor' +export type CodeSnippetValue = string | string[] | Object + export interface CodeSnippetProps { fitted?: boolean label?: string mode?: 'json' | 'jsx' | 'html' | 'sh' - value: string | string[] | Object + value: CodeSnippetValue style?: React.CSSProperties } -const joinToString = (stringOrArray: string | string[]) => - Array.isArray(stringOrArray) ? stringOrArray.join('\n') : stringOrArray +const normalizeToString = (value: CodeSnippetValue): string => { + if (_.isArray(value)) return value.join('\n') + return _.isObject(value) ? JSON.stringify(value, null, 2) : (value as string) +} const formatters = { sh: (val: string = ''): string => val.replace(/^/g, '$ '), html: (val: string = ''): string => formatCode(val, 'html'), - json: (val: Object = {}): string => JSON.stringify(val, null, 2), + json: (val: string): string => val, jsx: (val: string = ''): string => formatCode(val, 'babylon'), } -const CodeSnippet = ({ fitted, label, value, mode = 'jsx', ...restProps }: CodeSnippetProps) => { +const CodeSnippet = ({ fitted, label, mode, value, ...restProps }: CodeSnippetProps) => { const format = formatters[mode] - const formattedValue = format(joinToString(value as any)) + const formattedValue = format(normalizeToString(value)) // remove eof line break, they are not helpful for snippets .replace(/\n$/, '') @@ -66,4 +71,9 @@ const CodeSnippet = ({ fitted, label, value, mode = 'jsx', ...restProps }: CodeS ) } + +CodeSnippet.defaultProps = { + mode: 'jsx', +} + export default CodeSnippet From 313d4ff28f4fe36561186b251c9d95c5fde9f18f Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Wed, 23 Jan 2019 11:50:11 +0200 Subject: [PATCH 15/20] remove useless components --- .../AsyncDropdownSearch.tsx | 2 +- src/components/Dropdown/Dropdown.tsx | 19 +++++++--- .../Dropdown/DropdownMessageLoading.tsx | 35 ------------------ .../Dropdown/DropdownMessageNoResults.tsx | 36 ------------------- src/components/List/ListItem.tsx | 11 ++++-- src/themes/teams/componentStyles.ts | 6 ---- .../Dropdown/dropdownMessageLoadingStyles.ts | 11 ------ .../dropdownMessageNoResultsStyles.ts | 16 --------- .../components/Dropdown/dropdownStyles.ts | 11 ++++++ 9 files changed, 34 insertions(+), 113 deletions(-) delete mode 100644 src/components/Dropdown/DropdownMessageLoading.tsx delete mode 100644 src/components/Dropdown/DropdownMessageNoResults.tsx delete mode 100644 src/themes/teams/components/Dropdown/dropdownMessageLoadingStyles.ts delete mode 100644 src/themes/teams/components/Dropdown/dropdownMessageNoResultsStyles.ts diff --git a/docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx b/docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx index ef754b8d8b..d082cdee1d 100644 --- a/docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx +++ b/docs/src/prototypes/AsyncDropdownSearch/AsyncDropdownSearch.tsx @@ -80,7 +80,7 @@ class AsyncDropdownSearch extends React.Component<{}, SearchPageState> { items={items} loading={loading} loadingMessage={{ - content: , + content: , }} multiple onSearchQueryChange={this.handleSearchQueryChange} diff --git a/src/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx index f03efe83db..a711c284d2 100644 --- a/src/components/Dropdown/Dropdown.tsx +++ b/src/components/Dropdown/Dropdown.tsx @@ -32,11 +32,10 @@ import Indicator from '../Indicator/Indicator' import List from '../List/List' import Ref from '../Ref/Ref' import DropdownItem from './DropdownItem' -import DropdownMessageLoading from './DropdownMessageLoading' -import DropdownMessageNoResults from './DropdownMessageNoResults' import DropdownSelectedItem, { DropdownSelectedItemProps } from './DropdownSelectedItem' import DropdownSearchInput, { DropdownSearchInputProps } from './DropdownSearchInput' import Button from '../Button/Button' +import ListItem from '../List/ListItem' // TODO: To be replaced when Downshift will add highlightedItem in their interface. export interface A11yStatusMessageOptions extends DownshiftA11yStatusMessageOptions { @@ -428,13 +427,14 @@ export default class Dropdown extends AutoControlledComponent< styles={styles.list} tabIndex={search ? undefined : -1} // needs to be focused when trigger button is activated. aria-hidden={!isOpen} - items={isOpen ? this.renderItems(variables, getItemProps, highlightedIndex) : []} + items={this.renderItems(styles, variables, getItemProps, highlightedIndex)} /> ) } private renderItems( + styles: ComponentSlotStylesInput, variables: ComponentVariablesInput, getItemProps: (options: GetItemPropsOptions) => any, highlightedIndex: number, @@ -459,8 +459,17 @@ export default class Dropdown extends AutoControlledComponent< return [ ...items, - loading && DropdownMessageLoading.create(loadingMessage), - !loading && items.length === 0 && DropdownMessageNoResults.create(noResultsMessage), + loading && + ListItem.create(loadingMessage, { + defaultProps: { + styles: styles.loadingMessage, + }, + }), + !loading && + items.length === 0 && + ListItem.create(noResultsMessage, { + styles: styles.noResultsMessage, + }), ] } diff --git a/src/components/Dropdown/DropdownMessageLoading.tsx b/src/components/Dropdown/DropdownMessageLoading.tsx deleted file mode 100644 index a9187127d8..0000000000 --- a/src/components/Dropdown/DropdownMessageLoading.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import * as React from 'react' - -import { ReactProps } from '../../../types/utils' -import { - commonPropTypes, - ContentComponentProps, - createShorthandFactory, - UIComponent, - UIComponentProps, -} from '../../lib' -import ListItem from '../List/ListItem' - -export interface DropdownMessageLoadingProps - extends UIComponentProps, - ContentComponentProps {} - -/** - * A DropdownMessageLoading is a sub-component that outputs a message when the Dropdown is loading. - */ -class DropdownMessageLoading extends UIComponent> { - static className = 'ui-dropdown__message-loading' - static create: Function - static displayName = 'DropdownMessageLoading' - static propTypes = commonPropTypes.createCommon({ children: false }) - - renderComponent({ classes, ElementType, unhandledProps }) { - const { content } = this.props - - return - } -} - -DropdownMessageLoading.create = createShorthandFactory(DropdownMessageLoading, 'content') - -export default DropdownMessageLoading diff --git a/src/components/Dropdown/DropdownMessageNoResults.tsx b/src/components/Dropdown/DropdownMessageNoResults.tsx deleted file mode 100644 index ecbeb33f15..0000000000 --- a/src/components/Dropdown/DropdownMessageNoResults.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import * as React from 'react' - -import { ReactProps } from '../../../types/utils' -import { - commonPropTypes, - ContentComponentProps, - createShorthandFactory, - UIComponent, - UIComponentProps, -} from '../../lib' -import ListItem from '../List/ListItem' - -export interface DropdownMessageNoResultsProps - extends UIComponentProps, - ContentComponentProps {} - -/** - * A DropdownMessageNoResults is a sub-component that outputs a message when a list of filtered - * items is empty. - */ -class DropdownMessageNoResults extends UIComponent> { - static className = 'ui-dropdown__message-no-results' - static create: Function - static displayName = 'DropdownMessageNoResults' - static propTypes = commonPropTypes.createCommon({ children: false }) - - renderComponent({ classes, ElementType, unhandledProps }) { - const { content } = this.props - - return - } -} - -DropdownMessageNoResults.create = createShorthandFactory(DropdownMessageNoResults, 'content') - -export default DropdownMessageNoResults diff --git a/src/components/List/ListItem.tsx b/src/components/List/ListItem.tsx index a0c3fc73af..1592fe67f5 100644 --- a/src/components/List/ListItem.tsx +++ b/src/components/List/ListItem.tsx @@ -8,13 +8,18 @@ import { commonPropTypes, ContentComponentProps, isFromKeyboard, + childrenExist, + ChildrenComponentProps, } from '../../lib' import ItemLayout from '../ItemLayout/ItemLayout' import { listItemBehavior } from '../../lib/accessibility' import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibility/types' import { ReactProps, ComponentEventHandler } from '../../../types/utils' -export interface ListItemProps extends UIComponentProps, ContentComponentProps { +export interface ListItemProps + extends UIComponentProps, + ChildrenComponentProps, + ContentComponentProps { /** * Accessibility behavior if overridden by the user. * @default listItemBehavior @@ -70,7 +75,6 @@ class ListItem extends UIComponent, ListItemState> { static propTypes = { ...commonPropTypes.createCommon({ - children: false, content: false, }), contentMedia: PropTypes.any, @@ -127,6 +131,7 @@ class ListItem extends UIComponent, ListItemState> { debug, endMedia, media, + children, content, contentMedia, header, @@ -140,7 +145,7 @@ class ListItem extends UIComponent, ListItemState> { as={as} className={classes.root} rootCSS={styles.root} - content={content} + content={childrenExist(children) ? children : content} contentMedia={contentMedia} debug={debug} endMedia={endMedia} diff --git a/src/themes/teams/componentStyles.ts b/src/themes/teams/componentStyles.ts index 7789107d6a..b0d43027de 100644 --- a/src/themes/teams/componentStyles.ts +++ b/src/themes/teams/componentStyles.ts @@ -16,12 +16,6 @@ export { default as ChatMessage } from './components/Chat/chatMessageStyles' export { default as Divider } from './components/Divider/dividerStyles' export { default as Dropdown } from './components/Dropdown/dropdownStyles' -export { - default as DropdownMessageLoading, -} from './components/Dropdown/dropdownMessageLoadingStyles' -export { - default as DropdownMessageNoResults, -} from './components/Dropdown/dropdownMessageNoResultsStyles' export { default as DropdownSearchInput } from './components/Dropdown/dropdownSearchInputStyles' export { default as DropdownSelectedItem } from './components/Dropdown/dropdownSelectedItemStyles' export { default as DropdownItem } from './components/Dropdown/dropdownItemStyles' diff --git a/src/themes/teams/components/Dropdown/dropdownMessageLoadingStyles.ts b/src/themes/teams/components/Dropdown/dropdownMessageLoadingStyles.ts deleted file mode 100644 index 00e6007b53..0000000000 --- a/src/themes/teams/components/Dropdown/dropdownMessageLoadingStyles.ts +++ /dev/null @@ -1,11 +0,0 @@ -import ListItem from '../../../../components/List/ListItem' -import { DropdownMessageLoadingProps } from '../../../../components/Dropdown/DropdownMessageLoading' -import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types' - -const dropdownMessageLoadingStyles: ComponentSlotStylesInput = { - root: ({ variables: v }): ICSSInJSStyle => ({ - [`&.${ListItem.className}`]: { backgroundColor: v.listItemBackgroundColor }, - }), -} - -export default dropdownMessageLoadingStyles diff --git a/src/themes/teams/components/Dropdown/dropdownMessageNoResultsStyles.ts b/src/themes/teams/components/Dropdown/dropdownMessageNoResultsStyles.ts deleted file mode 100644 index 01c878b426..0000000000 --- a/src/themes/teams/components/Dropdown/dropdownMessageNoResultsStyles.ts +++ /dev/null @@ -1,16 +0,0 @@ -import ListItem from '../../../../components/List/ListItem' -import { DropdownMessageNoResultsProps } from '../../../../components/Dropdown/DropdownMessageNoResults' -import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types' - -const dropdownMessageNoResultsStyles: ComponentSlotStylesInput< - DropdownMessageNoResultsProps, - any -> = { - root: ({ variables: v }): ICSSInJSStyle => ({ - [`&.${ListItem.className}`]: { backgroundColor: v.listItemBackgroundColor }, - - fontWeight: 'bold', - }), -} - -export default dropdownMessageNoResultsStyles diff --git a/src/themes/teams/components/Dropdown/dropdownStyles.ts b/src/themes/teams/components/Dropdown/dropdownStyles.ts index 1b9b8fdc57..007fce432b 100644 --- a/src/themes/teams/components/Dropdown/dropdownStyles.ts +++ b/src/themes/teams/components/Dropdown/dropdownStyles.ts @@ -2,6 +2,7 @@ import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types' import { DropdownProps } from '../../../../components/Dropdown/Dropdown' import { DropdownVariables } from './dropdownVariables' import { pxToRem } from '../../../../lib' +import ListItem from '../../../../components/List/ListItem' const dropdownStyles: ComponentSlotStylesInput = { root: (): ICSSInJSStyle => ({}), @@ -73,6 +74,16 @@ const dropdownStyles: ComponentSlotStylesInput background: listBackgroundColor, }), + loadingMessage: ({ variables: v }): ICSSInJSStyle => ({ + [`&.${ListItem.className}`]: { backgroundColor: v.listItemBackgroundColor }, + }), + + noResultsMessage: ({ variables: v }): ICSSInJSStyle => ({ + [`&.${ListItem.className}`]: { backgroundColor: v.listItemBackgroundColor }, + + fontWeight: 'bold', + }), + toggleIndicator: ({ props: p, variables: v }): ICSSInJSStyle => ({ position: 'absolute', height: v.toggleIndicatorSize, From 0cdd20eed0bedbe0ce27dad6911b8e4f8eb21a10 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Wed, 23 Jan 2019 11:54:46 +0200 Subject: [PATCH 16/20] revert some changes --- src/components/Dropdown/Dropdown.tsx | 2 +- src/components/List/ListItem.tsx | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx index a711c284d2..c8c5ab6046 100644 --- a/src/components/Dropdown/Dropdown.tsx +++ b/src/components/Dropdown/Dropdown.tsx @@ -427,7 +427,7 @@ export default class Dropdown extends AutoControlledComponent< styles={styles.list} tabIndex={search ? undefined : -1} // needs to be focused when trigger button is activated. aria-hidden={!isOpen} - items={this.renderItems(styles, variables, getItemProps, highlightedIndex)} + items={isOpen ? this.renderItems(styles, variables, getItemProps, highlightedIndex) : []} /> ) diff --git a/src/components/List/ListItem.tsx b/src/components/List/ListItem.tsx index 1592fe67f5..e688aa03cf 100644 --- a/src/components/List/ListItem.tsx +++ b/src/components/List/ListItem.tsx @@ -8,18 +8,13 @@ import { commonPropTypes, ContentComponentProps, isFromKeyboard, - childrenExist, - ChildrenComponentProps, } from '../../lib' import ItemLayout from '../ItemLayout/ItemLayout' import { listItemBehavior } from '../../lib/accessibility' import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibility/types' import { ReactProps, ComponentEventHandler } from '../../../types/utils' -export interface ListItemProps - extends UIComponentProps, - ChildrenComponentProps, - ContentComponentProps { +export interface ListItemProps extends UIComponentProps, ContentComponentProps { /** * Accessibility behavior if overridden by the user. * @default listItemBehavior @@ -131,7 +126,6 @@ class ListItem extends UIComponent, ListItemState> { debug, endMedia, media, - children, content, contentMedia, header, @@ -145,7 +139,7 @@ class ListItem extends UIComponent, ListItemState> { as={as} className={classes.root} rootCSS={styles.root} - content={childrenExist(children) ? children : content} + content={content} contentMedia={contentMedia} debug={debug} endMedia={endMedia} From b00faa95799e333ca53ffbebee3505286b0f918c Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Wed, 23 Jan 2019 11:56:23 +0200 Subject: [PATCH 17/20] update comment --- src/components/Dropdown/Dropdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx index c8c5ab6046..37b23ae7cc 100644 --- a/src/components/Dropdown/Dropdown.tsx +++ b/src/components/Dropdown/Dropdown.tsx @@ -136,7 +136,7 @@ export interface DropdownProps extends UIComponentProps Date: Wed, 23 Jan 2019 12:01:51 +0200 Subject: [PATCH 18/20] fix keys --- src/components/Dropdown/Dropdown.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx index 37b23ae7cc..be814f6df9 100644 --- a/src/components/Dropdown/Dropdown.tsx +++ b/src/components/Dropdown/Dropdown.tsx @@ -462,12 +462,14 @@ export default class Dropdown extends AutoControlledComponent< loading && ListItem.create(loadingMessage, { defaultProps: { + key: 'loading-message', styles: styles.loadingMessage, }, }), !loading && items.length === 0 && ListItem.create(noResultsMessage, { + key: 'no-results-message', styles: styles.noResultsMessage, }), ] From 4d00a205f4c9b611ac0ddb56de2074ebb28cccfa Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Wed, 23 Jan 2019 12:16:48 +0200 Subject: [PATCH 19/20] fix broken UT --- src/components/Dropdown/Dropdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Dropdown/Dropdown.tsx b/src/components/Dropdown/Dropdown.tsx index be814f6df9..cf8eef9a3b 100644 --- a/src/components/Dropdown/Dropdown.tsx +++ b/src/components/Dropdown/Dropdown.tsx @@ -442,7 +442,7 @@ export default class Dropdown extends AutoControlledComponent< const { loading, loadingMessage, noResultsMessage, renderItem } = this.props const filteredItems = this.getItemsFilteredBySearchQuery() - const items = filteredItems.map((item, index) => + const items = _.map(filteredItems, (item, index) => DropdownItem.create(item, { defaultProps: { active: highlightedIndex === index, From d30c9f996ff08ffc79aa1b7b2f25a9b40ab93fcd Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Wed, 23 Jan 2019 12:40:39 +0200 Subject: [PATCH 20/20] remove selector --- src/themes/teams/components/Dropdown/dropdownStyles.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/themes/teams/components/Dropdown/dropdownStyles.ts b/src/themes/teams/components/Dropdown/dropdownStyles.ts index 007fce432b..7e87c34b49 100644 --- a/src/themes/teams/components/Dropdown/dropdownStyles.ts +++ b/src/themes/teams/components/Dropdown/dropdownStyles.ts @@ -2,7 +2,6 @@ import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types' import { DropdownProps } from '../../../../components/Dropdown/Dropdown' import { DropdownVariables } from './dropdownVariables' import { pxToRem } from '../../../../lib' -import ListItem from '../../../../components/List/ListItem' const dropdownStyles: ComponentSlotStylesInput = { root: (): ICSSInJSStyle => ({}), @@ -75,12 +74,11 @@ const dropdownStyles: ComponentSlotStylesInput }), loadingMessage: ({ variables: v }): ICSSInJSStyle => ({ - [`&.${ListItem.className}`]: { backgroundColor: v.listItemBackgroundColor }, + backgroundColor: v.listItemBackgroundColor, }), noResultsMessage: ({ variables: v }): ICSSInJSStyle => ({ - [`&.${ListItem.className}`]: { backgroundColor: v.listItemBackgroundColor }, - + backgroundColor: v.listItemBackgroundColor, fontWeight: 'bold', }),