diff --git a/superset-frontend/src/dashboard/components/menu/BackgroundStyleDropdown.jsx b/superset-frontend/src/dashboard/components/menu/BackgroundStyleDropdown.tsx similarity index 81% rename from superset-frontend/src/dashboard/components/menu/BackgroundStyleDropdown.jsx rename to superset-frontend/src/dashboard/components/menu/BackgroundStyleDropdown.tsx index 93b5de2af1..34dcdf9e07 100644 --- a/superset-frontend/src/dashboard/components/menu/BackgroundStyleDropdown.jsx +++ b/superset-frontend/src/dashboard/components/menu/BackgroundStyleDropdown.tsx @@ -17,19 +17,21 @@ * under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import cx from 'classnames'; import backgroundStyleOptions from '../../util/backgroundStyleOptions'; -import PopoverDropdown from './PopoverDropdown'; +import PopoverDropdown, { + OptionProps, + OnChangeHandler, +} from './PopoverDropdown'; -const propTypes = { - id: PropTypes.string.isRequired, - value: PropTypes.string.isRequired, - onChange: PropTypes.func.isRequired, -}; +interface BackgroundStyleDropdownProps { + id: string; + value: string; + onChange: OnChangeHandler; +} -function renderButton(option) { +function renderButton(option: OptionProps) { return (
{`${option.label} background`} @@ -37,7 +39,7 @@ function renderButton(option) { ); } -function renderOption(option) { +function renderOption(option: OptionProps) { return (
{option.label} @@ -45,7 +47,7 @@ function renderOption(option) { ); } -export default class BackgroundStyleDropdown extends React.PureComponent { +export default class BackgroundStyleDropdown extends React.PureComponent { render() { const { id, value, onChange } = this.props; return ( @@ -60,5 +62,3 @@ export default class BackgroundStyleDropdown extends React.PureComponent { ); } } - -BackgroundStyleDropdown.propTypes = propTypes; diff --git a/superset-frontend/src/dashboard/components/menu/HoverMenu.jsx b/superset-frontend/src/dashboard/components/menu/HoverMenu.tsx similarity index 74% rename from superset-frontend/src/dashboard/components/menu/HoverMenu.jsx rename to superset-frontend/src/dashboard/components/menu/HoverMenu.tsx index ef46b58a1a..6fa21eca11 100644 --- a/superset-frontend/src/dashboard/components/menu/HoverMenu.jsx +++ b/superset-frontend/src/dashboard/components/menu/HoverMenu.tsx @@ -16,23 +16,22 @@ * specific language governing permissions and limitations * under the License. */ -import React from 'react'; -import PropTypes from 'prop-types'; +import React, { RefObject } from 'react'; import cx from 'classnames'; -const propTypes = { - position: PropTypes.oneOf(['left', 'top']), - innerRef: PropTypes.func, - children: PropTypes.node, -}; +interface HoverMenuProps { + position: 'left' | 'top'; + innerRef: RefObject; + children: React.ReactNode; +} -const defaultProps = { - position: 'left', - innerRef: null, - children: null, -}; +export default class HoverMenu extends React.PureComponent { + static defaultProps = { + position: 'left', + innerRef: null, + children: null, + }; -export default class HoverMenu extends React.PureComponent { render() { const { innerRef, position, children } = this.props; return ( @@ -49,6 +48,3 @@ export default class HoverMenu extends React.PureComponent { ); } } - -HoverMenu.propTypes = propTypes; -HoverMenu.defaultProps = defaultProps; diff --git a/superset-frontend/src/dashboard/components/menu/MarkdownModeDropdown.jsx b/superset-frontend/src/dashboard/components/menu/MarkdownModeDropdown.tsx similarity index 82% rename from superset-frontend/src/dashboard/components/menu/MarkdownModeDropdown.jsx rename to superset-frontend/src/dashboard/components/menu/MarkdownModeDropdown.tsx index 62a289a371..c9fc042fc8 100644 --- a/superset-frontend/src/dashboard/components/menu/MarkdownModeDropdown.jsx +++ b/superset-frontend/src/dashboard/components/menu/MarkdownModeDropdown.tsx @@ -17,16 +17,15 @@ * under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import { t } from '@superset-ui/core'; -import PopoverDropdown from './PopoverDropdown'; +import PopoverDropdown, { OnChangeHandler } from './PopoverDropdown'; -const propTypes = { - id: PropTypes.string.isRequired, - value: PropTypes.string.isRequired, - onChange: PropTypes.func.isRequired, -}; +interface MarkdownModeDropdownProps { + id: string; + value: string; + onChange: OnChangeHandler; +} const dropdownOptions = [ { @@ -39,7 +38,7 @@ const dropdownOptions = [ }, ]; -export default class MarkdownModeDropdown extends React.PureComponent { +export default class MarkdownModeDropdown extends React.PureComponent { render() { const { id, value, onChange } = this.props; @@ -53,5 +52,3 @@ export default class MarkdownModeDropdown extends React.PureComponent { ); } } - -MarkdownModeDropdown.propTypes = propTypes; diff --git a/superset-frontend/src/dashboard/components/menu/PopoverDropdown.jsx b/superset-frontend/src/dashboard/components/menu/PopoverDropdown.tsx similarity index 69% rename from superset-frontend/src/dashboard/components/menu/PopoverDropdown.jsx rename to superset-frontend/src/dashboard/components/menu/PopoverDropdown.tsx index d67cd3836b..15f56edb5b 100644 --- a/superset-frontend/src/dashboard/components/menu/PopoverDropdown.jsx +++ b/superset-frontend/src/dashboard/components/menu/PopoverDropdown.tsx @@ -17,33 +17,29 @@ * under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import cx from 'classnames'; -import { styled, withTheme } from '@superset-ui/core'; +import { styled, withTheme, SupersetThemeProps } from '@superset-ui/core'; import { Dropdown, Menu } from 'src/common/components'; import Icon from 'src/components/Icon'; -const propTypes = { - id: PropTypes.string.isRequired, - options: PropTypes.arrayOf( - PropTypes.shape({ - value: PropTypes.string.isRequired, - label: PropTypes.string.isRequired, - className: PropTypes.string, - }), - ).isRequired, - onChange: PropTypes.func.isRequired, - value: PropTypes.string.isRequired, - renderButton: PropTypes.func, - renderOption: PropTypes.func, -}; +export interface OptionProps { + value: string; + label: string; + className?: string; +} -const defaultProps = { - renderButton: option => option.label, - renderOption: option => ( -
{option.label}
- ), -}; +export type OnChangeHandler = (key: React.Key) => void; +export type RenderElementHandler = (option: OptionProps) => JSX.Element; + +interface PopoverDropdownProps { + id: string; + options: OptionProps[]; + onChange: OnChangeHandler; + value: string; + theme: SupersetThemeProps['theme']; + renderButton: RenderElementHandler; + renderOption: RenderElementHandler; +} const MenuItem = styled(Menu.Item)` &.ant-menu-item { @@ -75,31 +71,34 @@ const MenuItem = styled(Menu.Item)` } `; -class PopoverDropdown extends React.PureComponent { - constructor(props) { +interface HandleSelectProps { + key: React.Key; +} + +class PopoverDropdown extends React.PureComponent { + constructor(props: PopoverDropdownProps) { super(props); this.handleSelect = this.handleSelect.bind(this); } - handleSelect({ key }) { + handleSelect({ key }: HandleSelectProps) { this.props.onChange(key); } + static defaultProps = { + renderButton: (option: OptionProps) => option.label, + renderOption: (option: OptionProps) => ( +
{option.label}
+ ), + }; + render() { - const { - id, - value, - options, - renderButton, - renderOption, - theme, - } = this.props; + const { value, options, renderButton, renderOption, theme } = this.props; const selected = options.find(opt => opt.value === value); return ( @@ -118,7 +117,7 @@ class PopoverDropdown extends React.PureComponent { } >
- {renderButton(selected)} + {selected && renderButton(selected)}
@@ -126,7 +125,4 @@ class PopoverDropdown extends React.PureComponent { } } -PopoverDropdown.propTypes = propTypes; -PopoverDropdown.defaultProps = defaultProps; - export default withTheme(PopoverDropdown); diff --git a/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.jsx b/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.tsx similarity index 67% rename from superset-frontend/src/dashboard/components/menu/WithPopoverMenu.jsx rename to superset-frontend/src/dashboard/components/menu/WithPopoverMenu.tsx index dcd8dcfdd2..df44b4e1c1 100644 --- a/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.jsx +++ b/superset-frontend/src/dashboard/components/menu/WithPopoverMenu.tsx @@ -17,44 +17,59 @@ * under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import cx from 'classnames'; -const propTypes = { - children: PropTypes.node, - disableClick: PropTypes.bool, - menuItems: PropTypes.arrayOf(PropTypes.node), - onChangeFocus: PropTypes.func, - isFocused: PropTypes.bool, - shouldFocus: PropTypes.func, - editMode: PropTypes.bool.isRequired, - style: PropTypes.object, +type ShouldFocusContainer = HTMLDivElement & { + contains: (event_target: EventTarget & HTMLElement) => Boolean; }; -const defaultProps = { - children: null, - disableClick: false, - onChangeFocus: null, - menuItems: [], - isFocused: false, - shouldFocus: (event, container) => - container?.contains(event.target) || - event.target.id === 'menu-item' || - event.target.parentNode?.id === 'menu-item', - style: null, -}; +interface WithPopoverMenuProps { + children: React.ReactNode; + disableClick: Boolean; + menuItems: React.ReactNode[]; + onChangeFocus: (focus: Boolean) => void; + isFocused: Boolean; + // Event argument is left as "any" because of the clash. In defaultProps it seems + // like it should be React.FocusEvent<>, however from handleClick() we can also + // derive that type is EventListenerOrEventListenerObject. + shouldFocus: (event: any, container: ShouldFocusContainer) => Boolean; + editMode: Boolean; + style: React.CSSProperties; +} -class WithPopoverMenu extends React.PureComponent { - constructor(props) { +interface WithPopoverMenuState { + isFocused: Boolean; +} + +export default class WithPopoverMenu extends React.PureComponent< + WithPopoverMenuProps, + WithPopoverMenuState +> { + container: ShouldFocusContainer; + + static defaultProps = { + children: null, + disableClick: false, + onChangeFocus: null, + menuItems: [], + isFocused: false, + shouldFocus: (event: any, container: ShouldFocusContainer) => + container?.contains(event.target) || + event.target.id === 'menu-item' || + event.target.parentNode?.id === 'menu-item', + style: null, + }; + + constructor(props: WithPopoverMenuProps) { super(props); this.state = { - isFocused: props.isFocused, + isFocused: props.isFocused!, }; this.setRef = this.setRef.bind(this); this.handleClick = this.handleClick.bind(this); } - UNSAFE_componentWillReceiveProps(nextProps) { + UNSAFE_componentWillReceiveProps(nextProps: WithPopoverMenuProps) { if (nextProps.editMode && nextProps.isFocused && !this.state.isFocused) { document.addEventListener('click', this.handleClick); document.addEventListener('drag', this.handleClick); @@ -71,11 +86,11 @@ class WithPopoverMenu extends React.PureComponent { document.removeEventListener('drag', this.handleClick); } - setRef(ref) { + setRef(ref: ShouldFocusContainer) { this.container = ref; } - handleClick(event) { + handleClick(event: any) { if (!this.props.editMode) { return; } @@ -84,6 +99,7 @@ class WithPopoverMenu extends React.PureComponent { shouldFocus: shouldFocusFunc, disableClick, } = this.props; + const shouldFocus = shouldFocusFunc(event, this.container); if (!disableClick && shouldFocus && !this.state.isFocused) { @@ -121,9 +137,9 @@ class WithPopoverMenu extends React.PureComponent { style={style} > {children} - {editMode && isFocused && menuItems.length > 0 && ( + {editMode && isFocused && (menuItems?.length ?? 0) > 0 && (
- {menuItems.map((node, i) => ( + {menuItems.map((node: React.ReactNode, i: Number) => (
{node}
@@ -134,8 +150,3 @@ class WithPopoverMenu extends React.PureComponent { ); } } - -WithPopoverMenu.propTypes = propTypes; -WithPopoverMenu.defaultProps = defaultProps; - -export default WithPopoverMenu;