From baf07bb5a1216681a57145d1e200752261e2688c Mon Sep 17 00:00:00 2001 From: Frankie Yan Date: Thu, 11 Aug 2022 02:13:39 +0800 Subject: [PATCH 1/7] Deprecate Dropdown component --- src/components/color-picker/color-picker.tsx | 14 +++++++------- .../__snapshots__/dropdown.test.tsx.snap | 0 .../dropdown.less | 0 .../dropdown.test.tsx | 0 .../{dropdown => deprecated-dropdown}/dropdown.tsx | 0 .../{dropdown => deprecated-dropdown}/index.ts | 0 src/index.ts | 2 +- stories/components/Dropdown.stories.tsx | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) rename src/components/{dropdown => deprecated-dropdown}/__snapshots__/dropdown.test.tsx.snap (100%) rename src/components/{dropdown => deprecated-dropdown}/dropdown.less (100%) rename src/components/{dropdown => deprecated-dropdown}/dropdown.test.tsx (100%) rename src/components/{dropdown => deprecated-dropdown}/dropdown.tsx (100%) rename src/components/{dropdown => deprecated-dropdown}/index.ts (100%) diff --git a/src/components/color-picker/color-picker.tsx b/src/components/color-picker/color-picker.tsx index be70f2069..ec885ab3c 100644 --- a/src/components/color-picker/color-picker.tsx +++ b/src/components/color-picker/color-picker.tsx @@ -1,7 +1,7 @@ import React from 'react' import classnames from 'classnames' -import Dropdown from '../dropdown' +import DeprecatedDropdown from '../deprecated-dropdown' import { Tooltip } from '../tooltip' import './color-picker.less' @@ -41,8 +41,8 @@ type Props = { function ColorPicker({ color = 0, small, onChange, colorList = COLORS }: Props) { return ( - - + + {(() => { const backgroundColor = _getColor(colorList, color) @@ -59,8 +59,8 @@ function ColorPicker({ color = 0, small, onChange, colorList = COLORS }: Props) ) })()} - - + +
{colorList.reduce((items, currentColor, currentIndex) => { items.push( @@ -82,8 +82,8 @@ function ColorPicker({ color = 0, small, onChange, colorList = COLORS }: Props) return items }, [])}
-
-
+ + ) } ColorPicker.displayName = 'ColorPicker' diff --git a/src/components/dropdown/__snapshots__/dropdown.test.tsx.snap b/src/components/deprecated-dropdown/__snapshots__/dropdown.test.tsx.snap similarity index 100% rename from src/components/dropdown/__snapshots__/dropdown.test.tsx.snap rename to src/components/deprecated-dropdown/__snapshots__/dropdown.test.tsx.snap diff --git a/src/components/dropdown/dropdown.less b/src/components/deprecated-dropdown/dropdown.less similarity index 100% rename from src/components/dropdown/dropdown.less rename to src/components/deprecated-dropdown/dropdown.less diff --git a/src/components/dropdown/dropdown.test.tsx b/src/components/deprecated-dropdown/dropdown.test.tsx similarity index 100% rename from src/components/dropdown/dropdown.test.tsx rename to src/components/deprecated-dropdown/dropdown.test.tsx diff --git a/src/components/dropdown/dropdown.tsx b/src/components/deprecated-dropdown/dropdown.tsx similarity index 100% rename from src/components/dropdown/dropdown.tsx rename to src/components/deprecated-dropdown/dropdown.tsx diff --git a/src/components/dropdown/index.ts b/src/components/deprecated-dropdown/index.ts similarity index 100% rename from src/components/dropdown/index.ts rename to src/components/deprecated-dropdown/index.ts diff --git a/src/index.ts b/src/index.ts index 3989f5dfd..32a576009 100644 --- a/src/index.ts +++ b/src/index.ts @@ -44,7 +44,6 @@ export * from './hooks/use-previous' // export { default as ColorPicker, COLORS } from './components/color-picker' -export { default as Dropdown } from './components/dropdown' export { default as Input } from './components/input' export { default as KeyboardShortcut } from './components/keyboard-shortcut' export { default as KeyCapturer, SUPPORTED_KEYS } from './components/key-capturer' @@ -62,3 +61,4 @@ export * from './components/menu' // export { default as DeprecatedButton } from './components/deprecated-button' +export { default as DeprecatedDropdown } from './components/deprecated-dropdown' diff --git a/stories/components/Dropdown.stories.tsx b/stories/components/Dropdown.stories.tsx index 5596a4e01..4072970af 100644 --- a/stories/components/Dropdown.stories.tsx +++ b/stories/components/Dropdown.stories.tsx @@ -1,7 +1,7 @@ import React from 'react' -import Dropdown from '../../src/components/dropdown' import Button from '../../src/components/deprecated-button' +import Dropdown from '../../src/components/deprecated-dropdown' export default { title: 'Components/Dropdown', From 445570d716daa534383fec01f0611f29a16615ab Mon Sep 17 00:00:00 2001 From: Frankie Yan Date: Thu, 11 Aug 2022 02:15:21 +0800 Subject: [PATCH 2/7] Deprecate Input component --- .../__snapshots__/input.test.tsx.snap | 0 src/components/{input => deprecated-input}/index.ts | 0 src/components/{input => deprecated-input}/input.less | 0 .../{input => deprecated-input}/input.test.tsx | 0 src/components/{input => deprecated-input}/input.tsx | 0 src/index.ts | 2 +- stories/components/Input.stories.tsx | 10 +++++++++- 7 files changed, 10 insertions(+), 2 deletions(-) rename src/components/{input => deprecated-input}/__snapshots__/input.test.tsx.snap (100%) rename src/components/{input => deprecated-input}/index.ts (100%) rename src/components/{input => deprecated-input}/input.less (100%) rename src/components/{input => deprecated-input}/input.test.tsx (100%) rename src/components/{input => deprecated-input}/input.tsx (100%) diff --git a/src/components/input/__snapshots__/input.test.tsx.snap b/src/components/deprecated-input/__snapshots__/input.test.tsx.snap similarity index 100% rename from src/components/input/__snapshots__/input.test.tsx.snap rename to src/components/deprecated-input/__snapshots__/input.test.tsx.snap diff --git a/src/components/input/index.ts b/src/components/deprecated-input/index.ts similarity index 100% rename from src/components/input/index.ts rename to src/components/deprecated-input/index.ts diff --git a/src/components/input/input.less b/src/components/deprecated-input/input.less similarity index 100% rename from src/components/input/input.less rename to src/components/deprecated-input/input.less diff --git a/src/components/input/input.test.tsx b/src/components/deprecated-input/input.test.tsx similarity index 100% rename from src/components/input/input.test.tsx rename to src/components/deprecated-input/input.test.tsx diff --git a/src/components/input/input.tsx b/src/components/deprecated-input/input.tsx similarity index 100% rename from src/components/input/input.tsx rename to src/components/deprecated-input/input.tsx diff --git a/src/index.ts b/src/index.ts index 32a576009..e9090c1d8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -44,7 +44,6 @@ export * from './hooks/use-previous' // export { default as ColorPicker, COLORS } from './components/color-picker' -export { default as Input } from './components/input' export { default as KeyboardShortcut } from './components/keyboard-shortcut' export { default as KeyCapturer, SUPPORTED_KEYS } from './components/key-capturer' export { default as Popover } from './components/popover' @@ -62,3 +61,4 @@ export * from './components/menu' export { default as DeprecatedButton } from './components/deprecated-button' export { default as DeprecatedDropdown } from './components/deprecated-dropdown' +export { default as DeprecatedInput } from './components/deprecated-input' diff --git a/stories/components/Input.stories.tsx b/stories/components/Input.stories.tsx index 49878c3ce..342073bc2 100644 --- a/stories/components/Input.stories.tsx +++ b/stories/components/Input.stories.tsx @@ -1,6 +1,7 @@ import React from 'react' -import Input from '../../src/components/input' +import Input from '../../src/components/deprecated-input' +import { Alert } from '../../src/new-components/alert' import './styles/input_story.less' // Story setup ================================================================ @@ -8,6 +9,9 @@ import './styles/input_story.less' export default { title: 'Components/Input', component: Input, + parameters: { + badges: ['deprecated'], + }, } // Story Definitions ========================================================== @@ -15,6 +19,10 @@ export default { export const InputStory = () => (
+ + Deprecated: Please use{' '} + TextField instead +

This component is a dumb wrapper around the <input /> element which justs add a class name to give it is From 34237f2bc0eac074188e5594ff5c0fffeb5a1f35 Mon Sep 17 00:00:00 2001 From: Frankie Yan Date: Thu, 11 Aug 2022 02:22:25 +0800 Subject: [PATCH 3/7] Remove popover --- .../__snapshots__/popover.test.tsx.snap | 68 ---- src/components/popover/index.ts | 3 - src/components/popover/popover.less | 116 ------ src/components/popover/popover.test.tsx | 174 --------- src/components/popover/popover.tsx | 269 -------------- .../popover/positioning-utils.test.ts | 332 ------------------ src/components/popover/positioning-utils.ts | 176 ---------- src/index.ts | 1 - 8 files changed, 1139 deletions(-) delete mode 100644 src/components/popover/__snapshots__/popover.test.tsx.snap delete mode 100644 src/components/popover/index.ts delete mode 100644 src/components/popover/popover.less delete mode 100644 src/components/popover/popover.test.tsx delete mode 100644 src/components/popover/popover.tsx delete mode 100644 src/components/popover/positioning-utils.test.ts delete mode 100644 src/components/popover/positioning-utils.ts diff --git a/src/components/popover/__snapshots__/popover.test.tsx.snap b/src/components/popover/__snapshots__/popover.test.tsx.snap deleted file mode 100644 index f7dadd728..000000000 --- a/src/components/popover/__snapshots__/popover.test.tsx.snap +++ /dev/null @@ -1,68 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Popover adds arrow class when prop is set 1`] = ` - - Trigger Content - - } - visible={true} - withArrow={true} -> - - - Trigger Content - - - - Popover Content - - - - -`; - -exports[`Popover renders without crashing 1`] = ` - - Trigger Content - - } - visible={true} -> - - - Trigger Content - - - - Popover Content - - - - -`; diff --git a/src/components/popover/index.ts b/src/components/popover/index.ts deleted file mode 100644 index 1eaee5e65..000000000 --- a/src/components/popover/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Popover } from './popover' - -export default Popover diff --git a/src/components/popover/popover.less b/src/components/popover/popover.less deleted file mode 100644 index cf337662d..000000000 --- a/src/components/popover/popover.less +++ /dev/null @@ -1,116 +0,0 @@ -@import '../styles/constants.less'; - -.reactist_popover { - &__wrapper { - display: inline-block; - max-width: 100%; - cursor: unset; - } - - .reactist_popover__content { - max-width: 100%; - display: inline-flex; - border-radius: 3px; - - color: @primary_font_color; - background-color: white; - border: 1px solid @color_border_grey; - box-shadow: 0 1px 8px 0 rgba(0, 0, 0, 0.08); - } - - white-space: normal; - z-index: 1000; - - position: fixed; - // real positional coordinates are set in code - top: 0; - left: 0; - - display: none; - visibility: hidden; - opacity: 0; - transition: opacity 0.1s linear; - &.visible { - display: inline-block; // initial isn't supported in IE11 - display: initial; - visibility: visible; - opacity: 1; - } - - // Arrow Definitions ====================================================== - @arrow_size: 5px; - .arrow() { - position: absolute; - content: ''; - height: 0; - width: 0; - } - - &.arrow_top:before { - .arrow(); - top: -@arrow_size; - left: calc(50% - @arrow_size); - border-left: @arrow_size solid transparent; - border-right: @arrow_size solid transparent; - border-bottom: @arrow_size solid @color_border_grey; - } - &.arrow_top:after { - .arrow(); - top: -@arrow_size + 1px; - left: calc(50% - @arrow_size); - border-left: @arrow_size solid transparent; - border-right: @arrow_size solid transparent; - border-bottom: @arrow_size solid white; - } - - &.arrow_right:before { - .arrow(); - bottom: calc(50% - @arrow_size); - right: -@arrow_size; - border-left: @arrow_size solid @color_border_grey; - border-bottom: @arrow_size solid transparent; - border-top: @arrow_size solid transparent; - } - &.arrow_right:after { - .arrow(); - bottom: calc(50% - @arrow_size); - right: -@arrow_size + 1px; - border-left: @arrow_size solid white; - border-bottom: @arrow_size solid transparent; - border-top: @arrow_size solid transparent; - } - - &.arrow_bottom:before { - .arrow(); - bottom: -@arrow_size; - left: calc(50% - @arrow_size); - border-left: @arrow_size solid transparent; - border-right: @arrow_size solid transparent; - border-top: @arrow_size solid @color_border_grey; - } - &.arrow_bottom:after { - .arrow(); - bottom: -@arrow_size + 1px; - left: calc(50% - @arrow_size); - border-left: @arrow_size solid transparent; - border-right: @arrow_size solid transparent; - border-top: @arrow_size solid white; - } - - &.arrow_left:before { - .arrow(); - bottom: calc(50% - @arrow_size); - left: -@arrow_size; - border-right: @arrow_size solid @color_border_grey; - border-bottom: @arrow_size solid transparent; - border-top: @arrow_size solid transparent; - } - &.arrow_left:after { - .arrow(); - bottom: calc(50% - @arrow_size); - left: -@arrow_size + 1px; - border-right: @arrow_size solid white; - border-bottom: @arrow_size solid transparent; - border-top: @arrow_size solid transparent; - } -} diff --git a/src/components/popover/popover.test.tsx b/src/components/popover/popover.test.tsx deleted file mode 100644 index 56034fa06..000000000 --- a/src/components/popover/popover.test.tsx +++ /dev/null @@ -1,174 +0,0 @@ -import React from 'react' -import { mount } from 'enzyme' - -import { Popover } from './popover' -import * as PositioningUtils from './positioning-utils' - -describe('Popover', () => { - it('renders without crashing', () => { - const popover = mount(getPopover()) - expect(popover).toMatchSnapshot() - }) - - it('adds arrow class when prop is set', () => { - const popover = mount(getPopover({ withArrow: true })) - expect(popover).toMatchSnapshot() - }) - - describe('Position Calculation', () => { - it('calculates tooltip position after becoming visible', () => { - const updatePopoverPositionSpy = jest.fn() - const popover = mount(getPopover({ visible: false })) - ;(popover.instance() as Popover)._updatePopoverPosition = updatePopoverPositionSpy - popover.setProps({ visible: true }) - - expect(updatePopoverPositionSpy).toHaveBeenCalled() - }) - - it('sets the tooltip to the given position if it is not set to `auto`', () => { - ;(PositioningUtils.hasEnoughSpace as jest.Mock) = jest.fn(() => true) - jest.spyOn(PositioningUtils, 'calculatePosition').mockImplementationOnce(() => ({ - x: 23, - y: 42, - })) - - const popover = mount(getPopover({ position: 'right' })).instance() as Popover - - expect(popover.popover.style.getPropertyValue('top')).toBe('42px') - expect(popover.popover.style.getPropertyValue('left')).toBe('23px') - }) - - it('allows vague positioning to avoid cut offs', () => { - ;(PositioningUtils.hasEnoughSpace as jest.Mock) = jest.fn(() => true) - jest.spyOn(PositioningUtils, 'calculatePosition').mockImplementationOnce(() => ({ - x: -23, - y: 42, - })) - - const popover = mount(getPopover({ allowVaguePositioning: true })).instance() as Popover - - expect(popover.popover.style.getPropertyValue('top')).toBe('-10px') - expect(popover.popover.style.getPropertyValue('left')).toBe('10px') - }) - - it('sets the tooltip to the first position that has enough space when `auto` is supplied', () => { - ;(PositioningUtils.hasEnoughSpace as jest.MockedFunction< - typeof PositioningUtils.hasEnoughSpace - >) = jest - .fn() - .mockReturnValueOnce(false) // top - .mockReturnValueOnce(false) // right - .mockReturnValueOnce(false) // bottom - .mockReturnValueOnce(true) // left - jest.spyOn(PositioningUtils, 'calculatePosition').mockImplementationOnce(() => ({ - x: 23, - y: 42, - })) - const popover = mount( - getPopover({ position: 'auto', withArrow: true }), - ).instance() as Popover - - expect(popover.popover.style.getPropertyValue('top')).toBe('42px') - expect(popover.popover.style.getPropertyValue('left')).toBe('23px') - expect(popover.popover.className).toContain('arrow_right') - }) - - it('sets the tooltip to the correct position when changing the gap size', () => { - ;(PositioningUtils.hasEnoughSpace as jest.Mock) = jest.fn(() => true) - - const popover = mount(getPopover({ position: 'top', visible: false })) - const instance = popover.instance() as Popover - - instance.wrapper.getBoundingClientRect = jest.fn(() => ({ - left: 500, - top: 500, - width: 70, - height: 20, - x: 0, - y: 0, - bottom: 0, - right: 0, - toJSON: jest.fn(), - })) - - instance.popover.getBoundingClientRect = jest.fn(() => ({ - width: 70, - height: 20, - left: 0, - top: 0, - x: 0, - y: 0, - bottom: 0, - right: 0, - toJSON: jest.fn(), - })) - - popover.setProps({ visible: true }) - - expect(instance.popover.style.getPropertyValue('top')).toBe('475px') - - popover.setProps({ gapSize: 20 }) - - expect(instance.popover.style.getPropertyValue('top')).toBe('460px') - }) - - it('updates position when props change', () => { - const updatePositionSpy = jest.fn() - const popover = mount( - getPopover({ - position: 'top', - allowVaguePosition: false, - withArrow: false, - gapSize: 10, - content: 'First content', - visible: false, - }), - ) - const instance = popover.instance() as Popover - instance._updatePopoverPosition = updatePositionSpy - expect(updatePositionSpy).toHaveBeenCalledTimes(0) - - popover.setProps({ visible: true }) - expect(updatePositionSpy).toHaveBeenCalledTimes(1) - - popover.setProps({ position: 'bottom' }) - expect(updatePositionSpy).toHaveBeenCalledTimes(2) - - popover.setProps({ allowVaguePositioning: true }) - expect(updatePositionSpy).toHaveBeenCalledTimes(3) - - popover.setProps({ withArrow: true }) - expect(updatePositionSpy).toHaveBeenCalledTimes(4) - - popover.setProps({ gapSize: 400 }) - expect(updatePositionSpy).toHaveBeenCalledTimes(5) - - popover.setProps({ content: 'New Content' }) - expect(updatePositionSpy).toHaveBeenCalledTimes(6) - - popover.setProps({ trigger: New Trigger }) - expect(updatePositionSpy).toHaveBeenCalledTimes(6) - }) - }) - - it('updates refs', () => { - const popoverRefSpy = jest.fn() - const wrapperRefSpy = jest.fn() - mount(getPopover({ popoverRef: popoverRefSpy, wrapperRef: wrapperRefSpy })) - - expect(popoverRefSpy).toHaveBeenCalled() - expect(wrapperRefSpy).toHaveBeenCalled() - }) - - // Helpers ================================================================ - function getPopover(props = {}) { - return ( - Trigger Content} - content="Popover Content" - visible - {...props} - /> - ) - } -}) diff --git a/src/components/popover/popover.tsx b/src/components/popover/popover.tsx deleted file mode 100644 index b27a98b7c..000000000 --- a/src/components/popover/popover.tsx +++ /dev/null @@ -1,269 +0,0 @@ -import React from 'react' -import classNames from 'classnames' - -import { hasEnoughSpace, calculatePosition, RelativePosition } from './positioning-utils' - -import './popover.less' - -/** - * Position of the popover. Defaults to `auto`. - * `auto` tries to position the tooltip to the top, - * if there's not enough space it tries to position the tooltip clockwise (right, bottom, left). - * Setting a distinct value like `right` will always position the popover right, regardless of available space. - * Specifying `horizontal` will only try to position the tooltip left and right in that order. - * Specifying `vertical` will only try to position the tooltip top and bottom in that order. - */ -type Position = 'left' | 'right' | 'top' | 'bottom' | 'vertical' | 'horizontal' | 'auto' - -type Props = { - visible?: boolean - /** ref of the popover in case you need to manipulate it. */ - popoverRef?: React.Ref - /** ref of the wrapper in case you need to manipulate it. */ - wrapperRef?: React.Ref - /** Function to be called when the mouse enters the trigger. */ - onMouseEnter?: React.MouseEventHandler - /** Function to be called when the mouse leaves the trigger. */ - onMouseLeave?: React.MouseEventHandler - onClick?: React.MouseEventHandler - /** Additional css class that is applied to the wrapper element. */ - wrapperClassName?: string - /** Additional css class that is applied to the popover element. */ - popoverClassName?: string - /** Additional css class that is applied to style the arrow. Not applied when `withArrow` is false. */ - arrowClassName?: string - /** Content prop of the popover. */ - content?: (() => React.ReactNode) | React.ReactNode - trigger?: React.ReactNode - position: Position - withArrow?: boolean - /** - * Whether vague positioning is allowed. When set to true the popover prefers to be fully visible over being correctly centered. - */ - allowVaguePositioning?: boolean - /** Gap between the popover wrapper and the arrow. */ - gapSize: number -} - -class Popover extends React.Component { - public static displayName: string - public static defaultProps: Props - - componentDidMount() { - if (this.props.visible) { - this._updatePopoverPosition() - } - } - - componentDidUpdate(prevProps: Props) { - if (this.wrapper && this.props.visible) { - const positionChanged = prevProps.position !== this.props.position - const vaguePositioningChanged = - prevProps.allowVaguePositioning !== this.props.allowVaguePositioning - const visibilityChanged = prevProps.visible !== this.props.visible - const arrowChanged = prevProps.withArrow !== this.props.withArrow - const gapSizeChanged = prevProps.gapSize !== this.props.gapSize - const contentChanged = prevProps.content !== this.props.content - if ( - positionChanged || - vaguePositioningChanged || - visibilityChanged || - arrowChanged || - gapSizeChanged || - contentChanged - ) { - this._updatePopoverPosition() - } - } - } - - popover!: HTMLElement - wrapper!: HTMLElement - - _updatePopoverPosition = () => { - const { position, allowVaguePositioning, gapSize } = this.props - const wrapperRect = this.wrapper.getBoundingClientRect() - const popoverRect = this.popover.getBoundingClientRect() - - // Instead of using the documentElement find the nearest absolutely positioned element - const documentEl = document.documentElement - let node = this.wrapper - let foundParent = false - while (!foundParent) { - const styles = getComputedStyle(node) - const position = styles.getPropertyValue('position') - if (position === 'absolute' || node === documentEl || !node.parentElement) { - foundParent = true - } else { - node = node.parentElement - } - } - const nodeRect = node.getBoundingClientRect() - const windowDimensions = { - height: nodeRect.height, - width: nodeRect.width, - } - - const popoverDimensions = { - height: popoverRect.height, - width: popoverRect.width, - } - const wrapperDimensions = { - height: wrapperRect.height, - width: wrapperRect.width, - } - const wrapperPositionRelative = { - x: wrapperRect.left - nodeRect.left, - y: wrapperRect.top - nodeRect.top, - } - const wrapperPositionAbsolute = { - x: wrapperRect.left, - y: wrapperRect.top, - } - - const positionsToTry: RelativePosition[] = - position === 'auto' - ? ['top', 'right', 'bottom', 'left', 'top'] - : position === 'vertical' - ? ['top', 'bottom'] - : position === 'horizontal' - ? ['left', 'right'] - : [position] - - for (let index = 0; index < positionsToTry.length; index++) { - const currentPosition = positionsToTry[index] - const enoughSpaceAtPosition = - currentPosition != null - ? hasEnoughSpace( - windowDimensions, - popoverDimensions, - wrapperDimensions, - wrapperPositionRelative, - currentPosition, - gapSize, - ) - : false - - if (enoughSpaceAtPosition || index === positionsToTry.length - 1) { - const popoverPosition = - currentPosition != null - ? calculatePosition( - currentPosition, - wrapperDimensions, - wrapperPositionAbsolute, - popoverDimensions, - gapSize, - ) - : wrapperPositionAbsolute - this.popover.style.top = `${popoverPosition.y}px` - this.popover.style.left = `${popoverPosition.x}px` - - /** - * Correct placement if vague positioning is allowed. - * When it's not allowed we "cut off" popovers and display them - * out of the viewport to maintain their centered position. - */ - if (allowVaguePositioning) { - // correct horizontally - if (popoverPosition.x < 0) { - this.popover.style.left = `${2 * gapSize}px` - } - // correct vertically - if (popoverPosition.y + popoverDimensions.height > windowDimensions.height) { - this.popover.style.top = `${ - windowDimensions.height - popoverDimensions.height - 2 * gapSize - }px` - } - } - - if (currentPosition !== position) { - this.popover.className = this._getClassNameForPosition(currentPosition) - } - break - } - } - } - - _getClassNameForPosition = (position: Position | undefined) => { - const { visible, withArrow, arrowClassName } = this.props - const className = classNames('reactist_popover', { visible }) - - if (visible && withArrow) { - return classNames(className, arrowClassName, { - arrow_top: position === 'bottom', - arrow_right: position === 'left', - arrow_bottom: position === 'auto' || position === 'top', - arrow_left: position === 'right', - }) - } - return className - } - - _updatePopoverRef = (popover: HTMLElement) => { - this.popover = popover - if (typeof this.props.popoverRef === 'function') { - this.props.popoverRef(popover) - } - } - - _updateWrapperRef = (wrapper: HTMLElement) => { - this.wrapper = wrapper - if (typeof this.props.wrapperRef === 'function') { - this.props.wrapperRef(wrapper) - } - } - - render() { - const { - position, - wrapperClassName, - popoverClassName, - onMouseEnter, - onMouseLeave, - onClick, - trigger, - content, - } = this.props - const popoverClass = position ? this._getClassNameForPosition(position) : '' - const popoverContentClass = classNames('reactist_popover__content', popoverClassName) - const wrapperClass = classNames('reactist_popover__wrapper', wrapperClassName) - const triggerElement = React.Children.only( - trigger as React.ReactElement, - ) - - function handleTriggerClick(event: React.SyntheticEvent) { - // @ts-expect-error This is temporary while we revisit the Popover interface - if (onClick) onClick(event) - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (typeof triggerElement.props.onClick === 'function') { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call - triggerElement.props.onClick(event) - } - } - - return ( - - {React.cloneElement(triggerElement, { onClick: handleTriggerClick })} - - {this.props.visible ? ( - - {typeof content === 'function' ? content() : content} - - ) : null} - - - ) - } -} -Popover.displayName = 'Popover' -Popover.defaultProps = { - position: 'auto', - gapSize: 5, // default size of the arrow (see `tooltip.less`) -} - -export { Popover } diff --git a/src/components/popover/positioning-utils.test.ts b/src/components/popover/positioning-utils.test.ts deleted file mode 100644 index 32709a41e..000000000 --- a/src/components/popover/positioning-utils.test.ts +++ /dev/null @@ -1,332 +0,0 @@ -import { - hasEnoughSpace, - calculatePosition, - calculateTopCenterPosition, - calculateBottomCenterPosition, - calculateRightCenterPosition, - calculateLeftCenterPosition, -} from './positioning-utils' - -describe('PositioningUtils', () => { - const windowDimensions = { height: 100, width: 100 } - const wrapperDimensions = { height: 20, width: 50 } - const elementDimensions = { height: 20, width: 40 } - const wrapperPosition = { x: 25, y: 40 } // centered in window - const gap = 5 - - describe('Position Calculations', () => { - const topCenterPosition = { x: 30, y: 15 } - const topCenterPositionWithoutGap = { x: 30, y: 20 } - const bottomCenterPosition = { x: 30, y: 65 } - const bottomCenterPositionWithoutGap = { x: 30, y: 60 } - const rightCenterPosition = { x: 80, y: 40 } - const rightCenterPositionWithoutGap = { x: 75, y: 40 } - const leftCenterPosition = { x: -20, y: 40 } - const leftCenterPositionWithoutGap = { x: -15, y: 40 } - - it('calculates the top center position correct', () => { - const position = calculateTopCenterPosition( - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap, - ) - expect(position).toEqual(topCenterPosition) - }) - it('calculates the top center position correct without gap', () => { - const position = calculateTopCenterPosition( - wrapperDimensions, - wrapperPosition, - elementDimensions, - ) - expect(position).toEqual(topCenterPositionWithoutGap) - }) - - it('calculates the bottom center position correct', () => { - const position = calculateBottomCenterPosition( - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap, - ) - expect(position).toEqual(bottomCenterPosition) - }) - it('calculates the bottom center position correct without gap', () => { - const position = calculateBottomCenterPosition( - wrapperDimensions, - wrapperPosition, - elementDimensions, - ) - expect(position).toEqual(bottomCenterPositionWithoutGap) - }) - - it('calculates the right center position correct', () => { - const position = calculateRightCenterPosition( - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap, - ) - expect(position).toEqual(rightCenterPosition) - }) - it('calculates the right center position correct without gap', () => { - const position = calculateRightCenterPosition( - wrapperDimensions, - wrapperPosition, - elementDimensions, - ) - expect(position).toEqual(rightCenterPositionWithoutGap) - }) - - it('calculates the left center position correct', () => { - const position = calculateLeftCenterPosition( - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap, - ) - expect(position).toEqual(leftCenterPosition) - }) - it('calculates the left center position correct without gap', () => { - const position = calculateLeftCenterPosition( - wrapperDimensions, - wrapperPosition, - elementDimensions, - ) - expect(position).toEqual(leftCenterPositionWithoutGap) - }) - - it('chooses correct calculation method based on given position', () => { - const topPosition = calculatePosition( - 'top', - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap, - ) - const rightPosition = calculatePosition( - 'right', - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap, - ) - const bottomPosition = calculatePosition( - 'bottom', - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap, - ) - const leftPosition = calculatePosition( - 'left', - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap, - ) - - expect(topPosition).toEqual(topCenterPosition) - expect(rightPosition).toEqual(rightCenterPosition) - expect(bottomPosition).toEqual(bottomCenterPosition) - expect(leftPosition).toEqual(leftCenterPosition) - }) - it('chooses correct calculation method based on given position without gap', () => { - const topPosition = calculatePosition( - 'top', - wrapperDimensions, - wrapperPosition, - elementDimensions, - ) - const rightPosition = calculatePosition( - 'right', - wrapperDimensions, - wrapperPosition, - elementDimensions, - ) - const bottomPosition = calculatePosition( - 'bottom', - wrapperDimensions, - wrapperPosition, - elementDimensions, - ) - const leftPosition = calculatePosition( - 'left', - wrapperDimensions, - wrapperPosition, - elementDimensions, - ) - - expect(topPosition).toEqual(topCenterPositionWithoutGap) - expect(rightPosition).toEqual(rightCenterPositionWithoutGap) - expect(bottomPosition).toEqual(bottomCenterPositionWithoutGap) - expect(leftPosition).toEqual(leftCenterPositionWithoutGap) - }) - it('returns the wrapper position if position for calculation is invalid', () => { - const position = calculatePosition( - // @ts-expect-error we are deliberately calling an invalid value. - 'invalid', - wrapperDimensions, - wrapperPosition, - elementDimensions, - ) - expect(position).toEqual(wrapperPosition) - }) - }) - - describe('Enough Space Calculations', () => { - const getParams = (params: { - wrapperPosition: { x: number; y: number } - position: 'top' | 'right' | 'bottom' | 'left' - gap?: number - elementDimensions?: { height: number; width: number } - }) => ({ - windowDimensions, - wrapperDimensions, - elementDimensions, - gap: 5, - ...params, - }) - const getTestCase = ( - description: string, - wrapperPosition: { x: number; y: number }, - position: 'top' | 'right' | 'bottom' | 'left', - expectedResult: boolean, - ) => ({ - description, - params: getParams({ wrapperPosition, position }), - expectedResult, - }) - - const testCases = [ - // TOP placement ================================================== - getTestCase( - 'has NOT enough space for top placement when wrapper is at the top edge', - { x: 0, y: 0 }, - 'top', - false, - ), - getTestCase( - 'has NOT enough space for top placement when wrapper is too close to top', - { x: 0, y: 24 }, - 'top', - false, - ), - getTestCase( - 'has enough space for top placement as soon as element + gap fits', - { x: 0, y: 25 }, - 'top', - true, - ), - // RIGHT placement ================================================== - getTestCase( - 'has NOT enough space for right placement when wrapper is at the right edge', - { x: 100, y: 50 }, - 'right', - false, - ), - getTestCase( - 'has NOT enough space for right placement when wrapper is too close to the right edge', - { x: 6, y: 50 }, - 'right', - false, - ), - getTestCase( - 'has enough space for right placement as soon as element + gap fits', - { x: 5, y: 50 }, - 'right', - true, - ), - // BOTTOM placement ================================================== - getTestCase( - 'has NOT enough space for bottom placement when wrapper is at the bottom edge', - { x: 0, y: 100 }, - 'bottom', - false, - ), - getTestCase( - 'has NOT enough space for bottom placement when wrapper is too close to the bottom', - { x: 0, y: 56 }, - 'bottom', - false, - ), - getTestCase( - 'has enough space for bottom placement as soon as element + gap fits', - { x: 0, y: 55 }, - 'bottom', - true, - ), - // LEFT placement ================================================== - getTestCase( - 'has NOT enough space for left placement when wrapper is at the left edge', - { x: 0, y: 50 }, - 'left', - false, - ), - getTestCase( - 'has NOT enough space for left placement when wrapper is too close to the left', - { x: 44, y: 50 }, - 'left', - false, - ), - getTestCase( - 'has enough space for left placement as soon as element + gap fits', - { x: 45, y: 50 }, - 'left', - true, - ), - // edge cases ===================================================== - { - description: 'invalid position has never enough space', - params: getParams({ - wrapperPosition: { x: 50, y: 50 }, - // @ts-expect-error deliberately passing in an error value - position: 'invalid', - }), - expectedResult: false, - }, - { - description: 'providing a gap value is optional', - params: getParams({ - wrapperPosition: { x: 0, y: 20 }, - position: 'top', - gap: undefined, - }), - expectedResult: true, - }, - { - description: 'has NOT enough space for top placement when wrapper is too wide', - params: getParams({ - elementDimensions: { height: 20, width: 51 }, - wrapperPosition: { x: 50, y: 30 }, - position: 'top', - }), - expectedResult: false, - }, - { - description: 'has NOT enough space for right placement when wrapper is too high', - params: getParams({ - elementDimensions: { height: 16, width: 41 }, - wrapperPosition: { x: 5, y: 10 }, - position: 'right', - }), - expectedResult: false, - }, - ] - testCases.forEach((testCase) => { - // Variable title. - // eslint-disable-next-line jest/valid-title - it(testCase.description, () => { - const result = hasEnoughSpace( - testCase.params.windowDimensions, - testCase.params.elementDimensions, - testCase.params.wrapperDimensions, - testCase.params.wrapperPosition, - testCase.params.position, - testCase.params.gap, - ) - expect(result).toBe(testCase.expectedResult) - }) - }) - }) -}) diff --git a/src/components/popover/positioning-utils.ts b/src/components/popover/positioning-utils.ts deleted file mode 100644 index b80894fd8..000000000 --- a/src/components/popover/positioning-utils.ts +++ /dev/null @@ -1,176 +0,0 @@ -type Dimensions = { width: number; height: number } -type AbsolutePosition = { x: number; y: number } -type RelativePosition = 'top' | 'right' | 'bottom' | 'left' - -type HasEnoughSpaceFn = ( - windowDimensions: Dimensions, - elementDimensions: Dimensions, - wrapperDimensions: Dimensions, - wrapperPosition: AbsolutePosition, - position: RelativePosition, - gap: number, -) => boolean - -const hasEnoughSpace: HasEnoughSpaceFn = ( - windowDimensions, - elementDimensions, - wrapperDimensions, - wrapperPosition, - position, - gap = 0, -) => { - const { height: windowHeight, width: windowWidth } = windowDimensions - const { height: elementHeight, width: elementWidth } = elementDimensions - const { height: wrapperHeight, width: wrapperWidth } = wrapperDimensions - const { x: wrapperX, y: wrapperY } = wrapperPosition - - const verticalPosition = _calculateVerticalPosition( - wrapperPosition, - wrapperDimensions, - elementDimensions, - ) - const horizontalPosition = _calculateHorizontalPosition( - wrapperPosition, - wrapperDimensions, - elementDimensions, - ) - const canPlaceVertically = - verticalPosition >= 0 && verticalPosition + elementWidth <= windowWidth - const canPlaceHorizontally = - horizontalPosition >= 0 && horizontalPosition + elementHeight <= windowHeight - - if (position === 'top') { - return canPlaceVertically && wrapperY - elementHeight - gap >= 0 - } else if (position === 'right') { - return canPlaceHorizontally && wrapperX + wrapperWidth + elementWidth + gap <= windowWidth - } else if (position === 'left') { - return canPlaceHorizontally && wrapperX - elementWidth - gap >= 0 - } else if (position === 'bottom') { - return canPlaceVertically && wrapperY + wrapperHeight + elementHeight + gap <= windowHeight - } - return false -} - -function _calculateVerticalPosition( - wrapperPosition: AbsolutePosition, - wrapperDimensions: Dimensions, - elementDimensions: Dimensions, -): number { - return wrapperPosition.x + (wrapperDimensions.width - elementDimensions.width) / 2 -} - -function _calculateHorizontalPosition( - wrapperPosition: AbsolutePosition, - wrapperDimensions: Dimensions, - elementDimensions: Dimensions, -): number { - return wrapperPosition.y + (wrapperDimensions.height - elementDimensions.height) / 2 -} - -type CenterPositionFn = ( - wrapperDimensions: Dimensions, - wrapperPosition: AbsolutePosition, - elementDimensions: Dimensions, - gap?: number, -) => AbsolutePosition - -const calculateTopCenterPosition: CenterPositionFn = ( - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap = 0, -) => { - const x = _calculateVerticalPosition(wrapperPosition, wrapperDimensions, elementDimensions) - const y = wrapperPosition.y - elementDimensions.height - gap - return { x, y } -} - -const calculateBottomCenterPosition: CenterPositionFn = ( - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap = 0, -) => { - const x = _calculateVerticalPosition(wrapperPosition, wrapperDimensions, elementDimensions) - const y = wrapperPosition.y + wrapperDimensions.height + gap - return { x, y } -} - -const calculateRightCenterPosition: CenterPositionFn = ( - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap = 0, -) => { - const x = wrapperPosition.x + wrapperDimensions.width + gap - const y = _calculateHorizontalPosition(wrapperPosition, wrapperDimensions, elementDimensions) - return { x, y } -} - -const calculateLeftCenterPosition: CenterPositionFn = ( - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap = 0, -) => { - const x = wrapperPosition.x - elementDimensions.width - gap - const y = _calculateHorizontalPosition(wrapperPosition, wrapperDimensions, elementDimensions) - return { x, y } -} - -type PositionFn = ( - position: 'top' | 'right' | 'bottom' | 'left', - wrapperDimensions: Dimensions, - wrapperPosition: AbsolutePosition, - elementDimensions: Dimensions, - gap?: number, -) => AbsolutePosition - -const calculatePosition: PositionFn = ( - position, - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap = 0, -) => { - if (position === 'top') { - return calculateTopCenterPosition( - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap, - ) - } else if (position === 'right') { - return calculateRightCenterPosition( - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap, - ) - } else if (position === 'bottom') { - return calculateBottomCenterPosition( - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap, - ) - } else if (position === 'left') { - return calculateLeftCenterPosition( - wrapperDimensions, - wrapperPosition, - elementDimensions, - gap, - ) - } - return wrapperPosition -} - -export { - hasEnoughSpace, - calculatePosition, - calculateTopCenterPosition, - calculateBottomCenterPosition, - calculateRightCenterPosition, - calculateLeftCenterPosition, - RelativePosition, -} diff --git a/src/index.ts b/src/index.ts index e9090c1d8..77e29c106 100644 --- a/src/index.ts +++ b/src/index.ts @@ -46,7 +46,6 @@ export * from './hooks/use-previous' export { default as ColorPicker, COLORS } from './components/color-picker' export { default as KeyboardShortcut } from './components/keyboard-shortcut' export { default as KeyCapturer, SUPPORTED_KEYS } from './components/key-capturer' -export { default as Popover } from './components/popover' export { default as ProgressBar } from './components/progress-bar' export { default as Select } from './components/select' export { default as Time } from './components/time' From 384df8250873a1e0250ded588bafd017e2257d42 Mon Sep 17 00:00:00 2001 From: Frankie Yan Date: Thu, 11 Aug 2022 03:29:55 +0800 Subject: [PATCH 4/7] Rename deprecated select export --- .../__snapshots__/select.test.tsx.snap | 0 .../{select => deprecated-select}/index.ts | 0 .../{select => deprecated-select}/select.less | 0 .../{select => deprecated-select}/select.test.tsx | 0 .../{select => deprecated-select}/select.tsx | 0 src/index.ts | 2 +- stories/components/Select.stories.tsx | 13 ++++++++++--- 7 files changed, 11 insertions(+), 4 deletions(-) rename src/components/{select => deprecated-select}/__snapshots__/select.test.tsx.snap (100%) rename src/components/{select => deprecated-select}/index.ts (100%) rename src/components/{select => deprecated-select}/select.less (100%) rename src/components/{select => deprecated-select}/select.test.tsx (100%) rename src/components/{select => deprecated-select}/select.tsx (100%) diff --git a/src/components/select/__snapshots__/select.test.tsx.snap b/src/components/deprecated-select/__snapshots__/select.test.tsx.snap similarity index 100% rename from src/components/select/__snapshots__/select.test.tsx.snap rename to src/components/deprecated-select/__snapshots__/select.test.tsx.snap diff --git a/src/components/select/index.ts b/src/components/deprecated-select/index.ts similarity index 100% rename from src/components/select/index.ts rename to src/components/deprecated-select/index.ts diff --git a/src/components/select/select.less b/src/components/deprecated-select/select.less similarity index 100% rename from src/components/select/select.less rename to src/components/deprecated-select/select.less diff --git a/src/components/select/select.test.tsx b/src/components/deprecated-select/select.test.tsx similarity index 100% rename from src/components/select/select.test.tsx rename to src/components/deprecated-select/select.test.tsx diff --git a/src/components/select/select.tsx b/src/components/deprecated-select/select.tsx similarity index 100% rename from src/components/select/select.tsx rename to src/components/deprecated-select/select.tsx diff --git a/src/index.ts b/src/index.ts index 77e29c106..02d326607 100644 --- a/src/index.ts +++ b/src/index.ts @@ -47,7 +47,6 @@ export { default as ColorPicker, COLORS } from './components/color-picker' export { default as KeyboardShortcut } from './components/keyboard-shortcut' export { default as KeyCapturer, SUPPORTED_KEYS } from './components/key-capturer' export { default as ProgressBar } from './components/progress-bar' -export { default as Select } from './components/select' export { default as Time } from './components/time' export { Notification } from './components/notification/notification' export { Tooltip } from './components/tooltip' @@ -61,3 +60,4 @@ export * from './components/menu' export { default as DeprecatedButton } from './components/deprecated-button' export { default as DeprecatedDropdown } from './components/deprecated-dropdown' export { default as DeprecatedInput } from './components/deprecated-input' +export { default as DeprecatedSelect } from './components/deprecated-select' diff --git a/stories/components/Select.stories.tsx b/stories/components/Select.stories.tsx index b020f4d95..171e69f0d 100644 --- a/stories/components/Select.stories.tsx +++ b/stories/components/Select.stories.tsx @@ -1,6 +1,8 @@ import React, { useState } from 'react' -import Select from '../../src/components/select' +import Select from '../../src/components/deprecated-select' +import { Alert } from '../../src/new-components/alert' +import { Stack } from '../../src/new-components/stack' const options = [ { value: 'intro', text: 'Select a fruit', disabled: true }, @@ -31,9 +33,14 @@ export const SelectStory = () => { } return ( -

+ + + Deprecated: Please use{' '} + SelectField instead + +