From da08be5d5596bf87f2a859207401eccaf9b902ba Mon Sep 17 00:00:00 2001 From: unix Date: Thu, 21 May 2020 16:45:36 +0800 Subject: [PATCH] feat(button): forward the ref of the original element --- .../__snapshots__/icon.test.tsx.snap | 228 ++++++------- .../__snapshots__/index.test.tsx.snap | 28 +- components/button/__tests__/index.test.tsx | 7 + components/button/button.tsx | 307 ++++++++++-------- 4 files changed, 288 insertions(+), 282 deletions(-) diff --git a/components/button/__tests__/__snapshots__/icon.test.tsx.snap b/components/button/__tests__/__snapshots__/icon.test.tsx.snap index 19a2c49..55796dc 100644 --- a/components/button/__tests__/__snapshots__/icon.test.tsx.snap +++ b/components/button/__tests__/__snapshots__/icon.test.tsx.snap @@ -33,66 +33,66 @@ exports[`ButtonIcon should render correctly 1`] = ` padding-right: 0; } " + .btn :global(.text p), + .btn :global(.text pre), + .btn :global(.text div) { + margin: 0; + } + " `; exports[`ButtonIcon should work with right 1`] = ` @@ -128,64 +128,64 @@ exports[`ButtonIcon should work with right 1`] = ` padding-right: 0; } " + .btn :global(.text p), + .btn :global(.text pre), + .btn :global(.text div) { + margin: 0; + } + " `; diff --git a/components/button/__tests__/__snapshots__/index.test.tsx.snap b/components/button/__tests__/__snapshots__/index.test.tsx.snap index 3c5757d..470d7a9 100644 --- a/components/button/__tests__/__snapshots__/index.test.tsx.snap +++ b/components/button/__tests__/__snapshots__/index.test.tsx.snap @@ -1,35 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Button should render empty button correctly 1`] = ` - -`; +exports[`Button should render empty button correctly 1`] = ``; exports[`Button should render special styles 1`] = `ReactWrapper {}`; exports[`Button should render special styles 2`] = ` - button - + `; diff --git a/components/button/__tests__/index.test.tsx b/components/button/__tests__/index.test.tsx index eb5434d..725786e 100644 --- a/components/button/__tests__/index.test.tsx +++ b/components/button/__tests__/index.test.tsx @@ -131,4 +131,11 @@ describe('Button', () => { await sleep(500) expect(wrapper.find('.loading-container').length).not.toBe(0) }) + + it('ref should be forwarded', () => { + const ref = React.createRef() + const wrapper = mount() + expect(wrapper.find('button').getDOMNode()).toEqual(ref.current) + expect(() => wrapper.unmount()).not.toThrow() + }) }) diff --git a/components/button/button.tsx b/components/button/button.tsx index 51a4140..e091003 100644 --- a/components/button/button.tsx +++ b/components/button/button.tsx @@ -1,6 +1,13 @@ -import React, { useRef, useState, MouseEvent, useMemo } from 'react' +import React, { + useRef, + useState, + MouseEvent, + useMemo, + useImperativeHandle, + PropsWithoutRef, + RefAttributes, +} from 'react' import useTheme from '../styles/use-theme' -import withDefaults from '../utils/with-defaults' import ButtonDrip from './button.drip' import ButtonLoading from '../loading' import { ButtonTypes, NormalSizes } from '../utils/prop-types' @@ -46,157 +53,171 @@ const defaultProps = { type NativeAttrs = Omit, keyof Props> export type ButtonProps = Props & typeof defaultProps & NativeAttrs -const Button: React.FC> = ({ ...btnProps }) => { - const theme = useTheme() - const buttonRef = useRef(null) - const [dripShow, setDripShow] = useState(false) - const [dripX, setDripX] = useState(0) - const [dripY, setDripY] = useState(0) - const groupConfig = useButtonGroupContext() - const filteredProps = filterPropsWithGroup(btnProps, groupConfig) - const { - children, - disabled, - type, - loading, - shadow, - ghost, - effect, - onClick, - auto, - size, - icon, - htmlType, - iconRight, - className, - ...props - } = filteredProps +const Button = React.forwardRef>( + ({ ...btnProps }, ref: React.Ref) => { + const theme = useTheme() + const buttonRef = useRef(null) + useImperativeHandle(ref, () => buttonRef.current) - const { bg, border, color } = useMemo(() => getButtonColors(theme.palette, filteredProps), [ - theme.palette, - filteredProps, - ]) - const hover = useMemo(() => getButtonHoverColors(theme.palette, filteredProps), [ - theme.palette, - filteredProps, - ]) - const { cursor, events } = useMemo(() => getButtonCursor(disabled, loading), [disabled, loading]) - const { height, minWidth, padding, width, fontSize } = useMemo(() => getButtonSize(size, auto), [ - size, - auto, - ]) - const dripColor = useMemo(() => getButtonDripColor(theme.palette, filteredProps), [ - theme.palette, - filteredProps, - ]) + const [dripShow, setDripShow] = useState(false) + const [dripX, setDripX] = useState(0) + const [dripY, setDripY] = useState(0) + const groupConfig = useButtonGroupContext() + const filteredProps = filterPropsWithGroup(btnProps, groupConfig) + const { + children, + disabled, + type, + loading, + shadow, + ghost, + effect, + onClick, + auto, + size, + icon, + htmlType, + iconRight, + className, + ...props + } = filteredProps - /* istanbul ignore next */ - const dripCompletedHandle = () => { - setDripShow(false) - setDripX(0) - setDripY(0) - } + const { bg, border, color } = useMemo(() => getButtonColors(theme.palette, filteredProps), [ + theme.palette, + filteredProps, + ]) + const hover = useMemo(() => getButtonHoverColors(theme.palette, filteredProps), [ + theme.palette, + filteredProps, + ]) + const { cursor, events } = useMemo(() => getButtonCursor(disabled, loading), [ + disabled, + loading, + ]) + const { height, minWidth, padding, width, fontSize } = useMemo( + () => getButtonSize(size, auto), + [size, auto], + ) + const dripColor = useMemo(() => getButtonDripColor(theme.palette, filteredProps), [ + theme.palette, + filteredProps, + ]) - const clickHandler = (event: MouseEvent) => { - if (disabled || loading) return - const showDrip = !shadow && !ghost && effect /* istanbul ignore next */ - if (showDrip && buttonRef.current) { - const rect = buttonRef.current.getBoundingClientRect() - setDripShow(true) - setDripX(event.clientX - rect.left) - setDripY(event.clientY - rect.top) + const dripCompletedHandle = () => { + setDripShow(false) + setDripX(0) + setDripY(0) } - onClick && onClick(event) - } + const clickHandler = (event: MouseEvent) => { + if (disabled || loading) return + const showDrip = !shadow && !ghost && effect + /* istanbul ignore next */ + if (showDrip && buttonRef.current) { + const rect = buttonRef.current.getBoundingClientRect() + setDripShow(true) + setDripX(event.clientX - rect.left) + setDripY(event.clientY - rect.top) + } - const childrenWithIcon = useMemo( - () => - getButtonChildrenWithIcon(auto, size, children, { - icon, - iconRight, - }), - [auto, size, children, icon, iconRight], - ) + onClick && onClick(event) + } - return ( - - ) -} + .btn :global(.text) { + position: relative; + z-index: 1; + display: inline-flex; + justify-content: center; + align-items: center; + text-align: center; + line-height: inherit; + top: -1px; + } -const MemoButton = React.memo>(Button) + .btn :global(.text p), + .btn :global(.text pre), + .btn :global(.text div) { + margin: 0; + } + `} + + ) + }, +) -export default withDefaults(MemoButton, defaultProps) +type ButtonComponent = React.ForwardRefExoticComponent< + PropsWithoutRef

& RefAttributes +> +type ComponentProps = Partial & + Omit & + NativeAttrs + +Button.defaultProps = defaultProps + +export default React.memo(Button) as ButtonComponent