diff --git a/packages/big-design-theme/src/system/z-index.ts b/packages/big-design-theme/src/system/z-index.ts index 4f5765f42..38f17054d 100644 --- a/packages/big-design-theme/src/system/z-index.ts +++ b/packages/big-design-theme/src/system/z-index.ts @@ -12,6 +12,6 @@ export const zIndex: ZIndex = { fixed: 1030, modalBackdrop: 1040, modal: 1050, - tooltip: 1060, - popover: 1070, + popover: 1060, + tooltip: 1070, }; diff --git a/packages/big-design/src/components/Dropdown/Dropdown.tsx b/packages/big-design/src/components/Dropdown/Dropdown.tsx index 2bc6c4d7d..96365706d 100644 --- a/packages/big-design/src/components/Dropdown/Dropdown.tsx +++ b/packages/big-design/src/components/Dropdown/Dropdown.tsx @@ -8,8 +8,9 @@ import { FlexItem } from '../Flex/Item'; import { Link } from '../Link'; import { List } from '../List'; import { ListItem } from '../List/Item'; +import { Tooltip, TooltipProps } from '../Tooltip'; -import { DropdownItem, DropdownLinkItem, DropdownProps } from './types'; +import { DropdownLinkItem, DropdownOption, DropdownProps } from './types'; interface DropdownState { highlightedItem: HTMLLIElement | null; @@ -25,6 +26,10 @@ export class Dropdown extends React.PureComponent extends React.PureComponent - {option.type === 'link' && !option.disabled ? ( - - - {icon && {this.renderIcon(option, isHighlighted)}} - {content} - - - ) : ( - - {icon && {this.renderIcon(option, isHighlighted)}} - {content} - - )} + {this.getContent(option, isHighlighted)} ); }) ); } + private wrapInLink(option: DropdownLinkItem, content: React.ReactChild) { + return ( + + {content} + + ); + } + + private getContent(option: DropdownOption, isHighlighted: boolean) { + const { disabled, icon, tooltip } = option; + + const baseContent = ( + + {icon && {this.renderIcon(option, isHighlighted)}} + {option.content} + + ); + + const content = option.type === 'link' && !disabled ? this.wrapInLink(option, baseContent) : baseContent; + + return disabled && tooltip ? this.wrapInTooltip(tooltip, content) : content; + } + + private wrapInTooltip(tooltip: DropdownOption['tooltip'], trigger: React.ReactChild) { + return ( + + {tooltip} + + ); + } + private renderTrigger(ref: RefHandler) { const { trigger } = this.props; @@ -129,7 +153,7 @@ export class Dropdown extends React.PureComponent | DropdownLinkItem, isHighlighted: boolean) { + private renderIcon(item: DropdownOption, isHighlighted: boolean) { return ( React.isValidElement(item.icon) && React.cloneElement(item.icon, { @@ -139,7 +163,7 @@ export class Dropdown extends React.PureComponent | DropdownLinkItem, isHighlighted: boolean) { + private iconColor(item: DropdownOption, isHighlighted: boolean) { if (item.disabled) { return 'secondary40'; } @@ -179,7 +203,7 @@ export class Dropdown extends React.PureComponent | DropdownLinkItem, index: number) { + private getItemId(item: DropdownOption, index: number) { const { id } = item; return id || `${this.getDropdownId()}-item-${index}`; @@ -220,7 +244,7 @@ export class Dropdown extends React.PureComponent | DropdownLinkItem) => { + private handleOnItemClick = (item: DropdownOption) => { if (item.disabled) { return; } diff --git a/packages/big-design/src/components/Dropdown/spec.tsx b/packages/big-design/src/components/Dropdown/spec.tsx index fb4f3b7ea..59b2f5e0c 100644 --- a/packages/big-design/src/components/Dropdown/spec.tsx +++ b/packages/big-design/src/components/Dropdown/spec.tsx @@ -276,3 +276,56 @@ test('does not forward styles', () => { expect(container.getElementsByClassName('test').length).toBe(0); expect(getByRole('listbox')).not.toHaveStyle('background: red'); }); + +test('renders tooltip with disabled item', () => { + const tooltipContent = 'Option with tooltip'; + const tooltipText = 'This is tooltip message'; + const { getByRole, getByText } = render( + Button} + />, + ); + const trigger = getByRole('button'); + + fireEvent.click(trigger); + fireEvent.mouseEnter(getByText(tooltipContent)); + + expect(getByText(tooltipText)).toBeInTheDocument(); +}); + +test("doesn't render tooltip on enabled item", () => { + const tooltipContent = 'Option with tooltip'; + const tooltipText = 'This is tooltip message'; + const { getByRole, getByText, queryByText } = render( + Button} + />, + ); + const trigger = getByRole('button'); + + fireEvent.click(trigger); + fireEvent.mouseEnter(getByText(tooltipContent)); + + expect(queryByText(tooltipText)).not.toBeInTheDocument(); +}); diff --git a/packages/big-design/src/components/Dropdown/types.ts b/packages/big-design/src/components/Dropdown/types.ts index f7d86269a..5ad287546 100644 --- a/packages/big-design/src/components/Dropdown/types.ts +++ b/packages/big-design/src/components/Dropdown/types.ts @@ -2,9 +2,11 @@ import { Placement } from 'popper.js'; import { ListItemProps } from '../List/Item'; +export type DropdownOption = DropdownItem | DropdownLinkItem; + export interface DropdownProps extends Omit, 'children'> { maxHeight?: number; - options: Array | DropdownLinkItem>; + options: Array>; placement?: Placement; trigger: React.ReactElement; } @@ -12,8 +14,9 @@ export interface DropdownProps extends Omit extends Omit { content: string; icon?: React.ReactElement; + tooltip?: string; value?: T; - onClick?(item: DropdownItem | DropdownLinkItem): void; + onClick?(item: DropdownOption): void; } export interface DropdownItem extends BaseItem { diff --git a/packages/big-design/src/components/Pagination/__snapshots__/spec.tsx.snap b/packages/big-design/src/components/Pagination/__snapshots__/spec.tsx.snap index fdeb1eaed..9cacb804c 100644 --- a/packages/big-design/src/components/Pagination/__snapshots__/spec.tsx.snap +++ b/packages/big-design/src/components/Pagination/__snapshots__/spec.tsx.snap @@ -100,7 +100,7 @@ exports[`render pagination component 1`] = ` outline: none; overflow-y: scroll; padding: 0.5rem 0; - z-index: 1070; + z-index: 1060; } .c8 { @@ -596,7 +596,7 @@ exports[`render pagination component with invalid page info 1`] = ` outline: none; overflow-y: scroll; padding: 0.5rem 0; - z-index: 1070; + z-index: 1060; } .c8 { @@ -1092,7 +1092,7 @@ exports[`render pagination component with invalid range info 1`] = ` outline: none; overflow-y: scroll; padding: 0.5rem 0; - z-index: 1070; + z-index: 1060; } .c8 { @@ -1589,7 +1589,7 @@ exports[`render pagination component with no items 1`] = ` outline: none; overflow-y: scroll; padding: 0.5rem 0; - z-index: 1070; + z-index: 1060; } .c8 { diff --git a/packages/big-design/src/components/Table/__snapshots__/spec.tsx.snap b/packages/big-design/src/components/Table/__snapshots__/spec.tsx.snap index e0915daff..0abf05b96 100644 --- a/packages/big-design/src/components/Table/__snapshots__/spec.tsx.snap +++ b/packages/big-design/src/components/Table/__snapshots__/spec.tsx.snap @@ -129,7 +129,7 @@ exports[`renders a pagination component 1`] = ` outline: none; overflow-y: scroll; padding: 0.5rem 0; - z-index: 1070; + z-index: 1060; } .c11 { diff --git a/packages/big-design/src/components/Tooltip/Tooltip.tsx b/packages/big-design/src/components/Tooltip/Tooltip.tsx index de8ceb5f8..51e76ba5c 100644 --- a/packages/big-design/src/components/Tooltip/Tooltip.tsx +++ b/packages/big-design/src/components/Tooltip/Tooltip.tsx @@ -6,9 +6,11 @@ import { Small } from '../Typography'; import { StyledTooltip, StyledTooltipTrigger } from './styled'; -export interface TooltipProps { +export interface TooltipProps extends React.HTMLAttributes { placement: PopperProps['placement']; trigger: React.ReactChild; + modifiers?: PopperProps['modifiers']; + inline?: boolean; } interface State { @@ -18,6 +20,7 @@ interface State { export class Tooltip extends React.PureComponent { static defaultProps: Partial = { placement: 'top', + inline: true, }; state = { @@ -38,13 +41,14 @@ export class Tooltip extends React.PureComponent { } render() { - const { children, trigger } = this.props; + const { children, trigger, inline } = this.props; return ( {({ ref }) => ( { {this.tooltipContainer ? createPortal( - + {({ placement, ref, style }) => this.state.visible && ( diff --git a/packages/big-design/src/components/Tooltip/styled.tsx b/packages/big-design/src/components/Tooltip/styled.tsx index b26defbfd..6b18c9eb5 100644 --- a/packages/big-design/src/components/Tooltip/styled.tsx +++ b/packages/big-design/src/components/Tooltip/styled.tsx @@ -1,8 +1,15 @@ import { theme as defaultTheme } from '@bigcommerce/big-design-theme'; -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; -export const StyledTooltipTrigger = styled.div` +export const StyledTooltipTrigger = styled.div<{ inline?: boolean }>` display: inline-block; + + ${({ inline }) => + !inline && + css` + display: block; + flex-grow: 1; + `} `; export const StyledTooltip = styled.div` diff --git a/packages/docs/PropTables/DropdownPropTable.tsx b/packages/docs/PropTables/DropdownPropTable.tsx index 539e643b1..6b883de69 100644 --- a/packages/docs/PropTables/DropdownPropTable.tsx +++ b/packages/docs/PropTables/DropdownPropTable.tsx @@ -88,6 +88,15 @@ const dropdownItemProps: Prop[] = [ types: '(item: DropdownItem): void', description: 'Returns the item object.', }, + { + name: 'tooltip', + types: 'string', + description: ( + <> + Adds tooltip for disabled item. Default placement is set to right. + + ), + }, { name: 'type', types: "'string'", @@ -136,6 +145,11 @@ const dropdownLinkProps: Prop[] = [ ), }, + { + name: 'tooltip', + types: "{ message: string, placement?: 'left' | 'right' }", + description: "Adds tooltip for disabled item. Placement is optional, if not passed - 'left' is set.", + }, { name: 'type', types: "'link'", diff --git a/packages/docs/pages/Dropdown/DropdownPage.tsx b/packages/docs/pages/Dropdown/DropdownPage.tsx index 25d2d68c7..aaa71e504 100644 --- a/packages/docs/pages/Dropdown/DropdownPage.tsx +++ b/packages/docs/pages/Dropdown/DropdownPage.tsx @@ -35,6 +35,7 @@ export default () => ( value: 'copy', icon: , disabled: true, + tooltip: 'You cannot copy this item...', }, { content: 'Delete', @@ -43,7 +44,12 @@ export default () => ( icon: , actionType: 'destructive', }, - { content: 'Link', icon: , type: 'link', url: '#' }, + { + content: 'Link', + icon: , + type: 'link', + url: '#', + }, ]} placement="bottom-start" trigger={}