Repository: uber-web/baseui
Pull request #687 (2018-12-21 00:22:23)

feat(select): add StyledDropdown and StyledDropdownListItem to exports and overrides


Patch:
diff --git a/src/menu/styled-components.js b/src/menu/styled-components.js
index 127030688..33053565e 100644
--- a/src/menu/styled-components.js
+++ b/src/menu/styled-components.js
@@ -43,7 +43,9 @@ export const ListItem = styled(
     display: 'block',
     color: $isHighlighted ? $theme.colors.primary : $theme.colors.foreground,
     cursor: 'pointer',
-    backgroundColor: $isHighlighted ? $theme.colors.mono300 : 'transparent',
+    backgroundColor: $isHighlighted
+      ? $theme.colors.menuFillHover
+      : 'transparent',
     transitionProperty: 'color, background-color',
     transitionDuration: $theme.animation.timing100,
     transitionTimingFunction: $theme.animation.easeOutCurve,
diff --git a/src/select/README.md b/src/select/README.md
index e37c8ad3f..9f70bc36c 100644
--- a/src/select/README.md
+++ b/src/select/README.md
@@ -207,6 +207,9 @@ export default () => {
   * `DropdownContainer?: ReactComponent | {props: {}, style: {}, component: ReactComponent}`
   * `Dropdown?: ReactComponent | {props: {}, style: {}, component: ReactComponent}`
   * `DropdownOption?: ReactComponent | {props: {}, style: {}, component: ReactComponent}`
+    * DropdownOption is an override for the Option component of the Menu. Option component is **not a styled  element** but a react component that is replaceable, for example with a profile like option, and accepts overrides for rendered elements. See Menu component API for more details. Since it's not a styled element/component it can't be restyled neither it accepts style overrides, but it accepts props that are passed down to rendered ListItem styled element or can be replaced completely, for example with the `OptionProfile` provided in the menu component.
+  * `DropdownListItem?: ReactComponent | {props: {}, style: {}, component: ReactComponent}`
+    * Is a styled element that is rendered by the default dropdown list option 
   * `OptionContent?: ReactComponent | {props: {}, style: {}, component: ReactComponent}`
 * `placeholder?: React.Node = 'Select...'`
   * Sets the placeholder.
diff --git a/src/select/dropdown.js b/src/select/dropdown.js
index a957fc87a..6a6d3be38 100644
--- a/src/select/dropdown.js
+++ b/src/select/dropdown.js
@@ -8,6 +8,8 @@ LICENSE file in the root directory of this source tree.
 import * as React from 'react';
 import {
   StyledDropdownContainer,
+  StyledDropdown,
+  StyledDropdownListItem,
   StyledOptionContent,
 } from './styled-components.js';
 import {StatefulMenu} from '../menu/index.js';
@@ -91,6 +93,10 @@ export default class SelectDropdown extends React.Component<DropdownPropsT> {
       overrides.DropdownContainer,
       StyledDropdownContainer,
     );
+    const [ListItem, listItemProps] = getOverrides(
+      overrides.DropdownListItem,
+      StyledDropdownListItem,
+    );
     return (
       <DropdownContainer {...this.getSharedProps()} {...dropdownContainerProps}>
         <StatefulMenu
@@ -106,10 +112,9 @@ export default class SelectDropdown extends React.Component<DropdownPropsT> {
           overrides={mergeOverrides(
             {
               List: {
-                style: ({$theme: {sizing}}) => ({
-                  maxHeight: maxDropdownHeight,
-                }),
+                component: StyledDropdown,
                 props: {
+                  $maxHeight: maxDropdownHeight,
                   'aria-multiselectable': multi,
                 },
               },
@@ -119,6 +124,16 @@ export default class SelectDropdown extends React.Component<DropdownPropsT> {
                   // figure out why the onClick handler is not
                   // triggered without this temporary fix
                   onMouseDown: this.onMouseDown,
+                  overrides: {
+                    ListItem: {
+                      component: ListItem,
+                      props: listItemProps,
+                      // slightly a hacky way to handle the list item style overrides
+                      // since the menu component doesn't provide a top level overrides for it
+                      // $FlowFixMe
+                      style: listItemProps.$style,
+                    },
+                  },
                 },
               },
             },
diff --git a/src/select/examples.js b/src/select/examples.js
index 3b4086a48..2a35aa460 100644
--- a/src/select/examples.js
+++ b/src/select/examples.js
@@ -173,6 +173,18 @@ export default {
           <CustomOptionLabel option={option} showColor />
         )}
         getValueLabel={({option}) => <CustomOptionLabel option={option} />}
+        overrides={{
+          DropdownListItem: {
+            style: ({$theme, $isHighlighted}) => ({
+              backgroundColor: $isHighlighted
+                ? $theme.colors.primary50
+                : 'transparent',
+              ':hover': {
+                backgroundColor: $theme.colors.primary50,
+              },
+            }),
+          },
+        }}
       />
     );
   },
diff --git a/src/select/index.js b/src/select/index.js
index 3795c5e8f..1fff38012 100644
--- a/src/select/index.js
+++ b/src/select/index.js
@@ -28,6 +28,8 @@ export {
   StyledClearIcon,
   StyledSearchIcon,
   StyledDropdownContainer,
+  StyledDropdown,
+  StyledDropdownListItem,
   StyledOptionContent,
 } from './styled-components.js';
 export {default as filterOptions} from './utils/default-filter-options.js';
diff --git a/src/select/styled-components.js b/src/select/styled-components.js
index d1146df11..d78e91c5c 100644
--- a/src/select/styled-components.js
+++ b/src/select/styled-components.js
@@ -8,6 +8,7 @@ LICENSE file in the root directory of this source tree.
 import {styled} from '../styles/index.js';
 import {TYPE} from './constants.js';
 import {getSvgStyles} from '../icon/styled-components.js';
+import {StyledList, StyledListItem} from '../menu/index.js';
 import {SIZE} from './constants.js';
 import type {SharedStylePropsT} from './types.js';
 import {ellipsisText} from '../styles/util.js';
@@ -79,6 +80,16 @@ export const StyledDropdownContainer = styled('div', props => {
 });
 StyledDropdownContainer.displayName = 'StyledDropdownContainer';
 
+export const StyledDropdown = styled(StyledList, props => {
+  const {$maxHeight} = props;
+  return {
+    maxHeight: $maxHeight,
+  };
+});
+StyledDropdown.displayName = 'StyledDropdown';
+
+export const StyledDropdownListItem = StyledListItem;
+
 export const StyledOptionContent = styled('div', props => {
   const {$isHighlighted, $selected, $disabled, $theme} = props;
   const {
diff --git a/src/select/types.js b/src/select/types.js
index 719140cc5..01928cd79 100644
--- a/src/select/types.js
+++ b/src/select/types.js
@@ -49,13 +49,16 @@ export type OverridesT = {
   DropdownContainer?: OverrideT<*>,
   Dropdown?: OverrideT<*>,
   DropdownOption?: OverrideT<*>,
+  DropdownListItem?: OverrideT<*>,
   OptionContent?: OverrideT<*>,
 };
 
 export type OverridesDropdownT = {
   DropdownContainer?: OverrideT<*>,
   Dropdown?: OverrideT<*>,
+  // Not a styled component
   DropdownOption?: OverrideT<*>,
+  DropdownListItem?: OverrideT<*>,
   OptionContent?: OverrideT<*>,
 };
 


Test patch:
diff --git a/src/menu/__tests__/__snapshots__/styled-components.test.js.snap b/src/menu/__tests__/__snapshots__/styled-components.test.js.snap
index 179e50065..9fa776eb4 100644
--- a/src/menu/__tests__/__snapshots__/styled-components.test.js.snap
+++ b/src/menu/__tests__/__snapshots__/styled-components.test.js.snap
@@ -89,7 +89,7 @@ Object {
   ":last-child": Object {
     "marginBottom": "$theme.sizing.scale300",
   },
-  "backgroundColor": "$theme.colors.mono300",
+  "backgroundColor": "$theme.colors.menuFillHover",
   "color": "$theme.colors.primary",
   "cursor": "pointer",
   "display": "block",
diff --git a/src/select/__tests__/__snapshots__/dropdown.test.js.snap b/src/select/__tests__/__snapshots__/dropdown.test.js.snap
index 8c2d58bc4..88e727092 100644
--- a/src/select/__tests__/__snapshots__/dropdown.test.js.snap
+++ b/src/select/__tests__/__snapshots__/dropdown.test.js.snap
@@ -43,15 +43,23 @@ Object {
 exports[`SelectDropdown renders StatefulMenu: Passes correct overrides to the StatefulMenu 1`] = `
 Object {
   "List": Object {
+    "component": [Function],
     "props": Object {
+      "$maxHeight": "1000px",
       "aria-multiselectable": false,
     },
-    "style": [Function],
   },
   "Option": Object {
     "props": Object {
       "getItemLabel": [Function],
       "onMouseDown": [Function],
+      "overrides": Object {
+        "ListItem": Object {
+          "component": [MockFunction],
+          "props": Object {},
+          "style": undefined,
+        },
+      },
     },
   },
 }