Browse Source

ref(ui): Convert dropdownControl to ts (#18926)

Scott Cooper 4 years ago
parent
commit
9f982f2897

+ 4 - 2
docs-ui/components/dropdownControl.stories.js

@@ -11,16 +11,18 @@ storiesOf('UI|Dropdowns/DropdownControl', module)
     'basic label + knobs',
     withInfo('Using a string value for the button label')(() => {
       const menuWidth = text('menuWidth', undefined);
-      const menuOffset = text('menuOffset', undefined);
       const alwaysRenderMenu = boolean('alwaysRenderMenu', true);
+      const alignRight = boolean('alignRight', false);
+      const blendWithActor = boolean('blendWithActor', false);
 
       return (
         <div className="clearfix">
           <DropdownControl
             label="Open Me"
             menuWidth={menuWidth}
-            menuOffset={menuOffset}
             alwaysRenderMenu={alwaysRenderMenu}
+            alignRight={alignRight}
+            blendWithActor={blendWithActor}
           >
             <DropdownItem href="">Href Item</DropdownItem>
             <DropdownItem to="">Router Item</DropdownItem>

+ 2 - 2
src/sentry/static/sentry/app/components/dropdownBubble.tsx

@@ -8,7 +8,7 @@ type Params = {
   /**
    * If this is true, will make corners blend with its opener (so no border radius)
    */
-  blendWithActor: boolean;
+  blendWithActor?: boolean;
   /**
    * If this is true, will make a single corner blended with actor (depends on anchor orientation)
    */
@@ -24,7 +24,7 @@ type Params = {
   /**
    * enable the arrow on the menu
    */
-  menuWithArrow: boolean;
+  menuWithArrow?: boolean;
 
   theme: Theme;
 };

+ 47 - 32
src/sentry/static/sentry/app/components/dropdownControl.jsx → src/sentry/static/sentry/app/components/dropdownControl.tsx

@@ -1,51 +1,66 @@
-import PropTypes from 'prop-types';
 import React from 'react';
 import styled from '@emotion/styled';
 
 import DropdownBubble from 'app/components/dropdownBubble';
 import DropdownButton from 'app/components/dropdownButton';
-import DropdownMenu from 'app/components/dropdownMenu';
+import DropdownMenu, {GetActorPropsFn} from 'app/components/dropdownMenu';
 import MenuItem from 'app/components/menuItem';
+import theme from 'app/utils/theme';
+
+type DefaultProps = {
+  /**
+   * Should the menu contents always be rendered?  Defaults to true.
+   * Set to false to have menu contents removed from the DOM on close.
+   */
+  alwaysRenderMenu: boolean;
+  /**
+   * Width of the menu. Defaults to 100% of the button width.
+   */
+  menuWidth: string;
+};
+
+type Props = DefaultProps & {
+  /**
+   * String or element for the button contents.
+   */
+  label?: React.ReactNode;
+  /**
+   * A closure that returns a styled button. Function will get {isOpen, getActorProps}
+   * as arguments. Use this if you need to style/replace the dropdown button.
+   */
+  button?: (props: {isOpen: boolean; getActorProps: GetActorPropsFn}) => React.ReactNode;
+  /**
+   * Align the dropdown menu to the right. (Default aligns to left)
+   */
+  alignRight?: boolean;
+  /**
+   * Props to pass to DropdownButton
+   */
+  buttonProps?: React.ComponentProps<typeof DropdownButton>;
+  /**
+   * This makes the dropdown menu blend (e.g. corners are not rounded) with its
+   * actor (opener) component
+   */
+  blendWithActor?: boolean;
+};
 
 /*
  * A higher level dropdown component that helps with building complete dropdowns
  * including the button + menu options. Use the `button` or `label` prop to set
  * the button content and `children` to provide menu options.
  */
-class DropdownControl extends React.Component {
-  static propTypes = {
-    // String or element for the button contents.
-    label: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
-    // A closure that returns a styled button. Function will get {isOpen, getActorProps}
-    // as arguments. Use this if you need to style/replace the dropdown button.
-    button: PropTypes.func,
-    // Width of the menu. Defaults to 100% of the button width.
-    menuWidth: PropTypes.string,
-    // Height offset for the menu. Defaults to 39px as standard buttons are
-    // 40px tall
-    menuOffset: PropTypes.string,
-    // Should the menu contents always be rendered?  Defaults to true.
-    // Set to false to have menu contents removed from the DOM on close.
-    alwaysRenderMenu: PropTypes.bool,
-    // Align the dropdown menu to the right. (Default aligns to left)
-    alignRight: PropTypes.bool,
-    // Props to pass to DropdownButton
-    buttonProps: PropTypes.object,
-    // This makes the dropdown menu blend (e.g. corners are not rounded) with its
-    // actor (opener) component
-    blendWithActor: PropTypes.bool,
-  };
-
-  static defaultProps = {
+class DropdownControl extends React.Component<Props> {
+  static defaultProps: DefaultProps = {
     alwaysRenderMenu: true,
     menuWidth: '100%',
   };
 
-  renderButton(isOpen, getActorProps) {
+  renderButton(isOpen: boolean, getActorProps: GetActorPropsFn) {
     const {label, button, buttonProps} = this.props;
     if (button) {
       return button({isOpen, getActorProps});
     }
+
     return (
       <StyledDropdownButton {...getActorProps(buttonProps)} isOpen={isOpen}>
         {label}
@@ -58,10 +73,10 @@ class DropdownControl extends React.Component {
       children,
       alwaysRenderMenu,
       alignRight,
-      menuOffset,
       menuWidth,
       blendWithActor,
     } = this.props;
+    const alignMenu = alignRight ? 'right' : 'left';
 
     return (
       <Container>
@@ -71,12 +86,12 @@ class DropdownControl extends React.Component {
               {this.renderButton(isOpen, getActorProps)}
               <Content
                 {...getMenuProps()}
-                alignMenu={alignRight ? 'right' : 'left'}
+                alignMenu={alignMenu}
                 width={menuWidth}
-                menuOffset={menuOffset}
                 isOpen={isOpen}
                 blendCorner
                 blendWithActor={blendWithActor}
+                theme={theme}
               >
                 {children}
               </Content>
@@ -98,7 +113,7 @@ const StyledDropdownButton = styled(DropdownButton)`
   white-space: nowrap;
 `;
 
-const Content = styled(DropdownBubble.withComponent('div'))`
+const Content = styled(DropdownBubble)<{isOpen: boolean}>`
   display: ${p => (p.isOpen ? 'block' : 'none')};
   border-top: 0;
   top: 100%;

+ 3 - 3
src/sentry/static/sentry/app/components/dropdownMenu.tsx

@@ -35,8 +35,8 @@ type MenuProps = {
   onMouseLeave: (e: React.MouseEvent<Element>) => void;
 };
 
-type GetActorPropsFn = (opts: GetActorArgs) => ActorProps;
-type GetMenuPropsFn = (opts: GetMenuArgs) => MenuProps;
+export type GetActorPropsFn = (opts?: GetActorArgs) => ActorProps;
+type GetMenuPropsFn = (opts?: GetMenuArgs) => MenuProps;
 
 type RenderProps = {
   isOpen: boolean;
@@ -320,7 +320,7 @@ class DropdownMenu extends React.Component<Props, State> {
     onKeyDown,
     style = {},
     ...props
-  } = {}) => {
+  }: GetActorArgs = {}) => {
     const {isNestedDropdown, closeOnEscape} = this.props;
 
     const refProps = {ref: this.handleActorMount};