From 9fe7f1e2ceebb02b25be0ddb835b99d05dd77d61 Mon Sep 17 00:00:00 2001 From: unix Date: Thu, 7 May 2020 23:53:45 +0800 Subject: [PATCH 1/5] feat(button): support to display icons in button --- components/button/button-icon.tsx | 57 +++++++++++++++++++++++++++++++ components/button/button.tsx | 47 +++++++++++++++++++++---- components/button/index.ts | 2 -- components/button/styles.ts | 4 +-- 4 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 components/button/button-icon.tsx diff --git a/components/button/button-icon.tsx b/components/button/button-icon.tsx new file mode 100644 index 0000000..f4c6b48 --- /dev/null +++ b/components/button/button-icon.tsx @@ -0,0 +1,57 @@ +import React from 'react' +import withDefaults from '../utils/with-defaults' + +interface Props { + isRight?: boolean + className?: string +} + +const defaultProps = { + isRight: false, + className: '', +} + +type NativeAttrs = Omit, keyof Props> +export type ButtonIconProps = Props & typeof defaultProps & NativeAttrs + +const ButtonIcon: React.FC> = ({ + isRight, + children, + className, + ...props +}) => { + return ( + + {children} + + + ) +} + +const MemoButtonIcon = React.memo(ButtonIcon) + +export default withDefaults(MemoButtonIcon, defaultProps) diff --git a/components/button/button.tsx b/components/button/button.tsx index 31b54e9..3817103 100644 --- a/components/button/button.tsx +++ b/components/button/button.tsx @@ -4,6 +4,7 @@ import useTheme from '../styles/use-theme' import { ButtonTypes, NormalSizes } from '../utils/prop-types' import ButtonDrip from './button.drip' import ButtonLoading from '../loading' +import ButtonIcon from './button-icon' import { getButtonColors, getButtonCursor, getButtonHoverColors, getButtonSize } from './styles' interface Props { @@ -15,6 +16,8 @@ interface Props { auto?: boolean effect?: boolean disabled?: boolean + icon?: React.ReactNode + iconRight?: React.ReactNode onClick?: React.MouseEventHandler className?: string } @@ -45,6 +48,8 @@ const Button: React.FC> = ({ onClick, auto, size, + icon, + iconRight, className, ...props }) => { @@ -94,6 +99,32 @@ const Button: React.FC> = ({ onClick && onClick(event) } + const childrenWithIcon = useMemo(() => { + const hasIcon = icon || iconRight + const isRight = Boolean(iconRight) + const paddingForAutoMode = + auto || size === 'mini' + ? `calc(var(--zeit-ui-button-height) / 2 + var(--zeit-ui-button-padding) * .5)` + : 0 + if (!hasIcon) return
{children}
+ return ( + <> + {hasIcon} +
+ {children} + +
+ + ) + }, [children, icon, auto, size]) + return ( + + + + + + + + + + + +`} /> + Button.Props | Attribute | Description | Type | Accepted values | Default | ---------- | ---------- | ---- | -------------- | ------ | -| **type** | button type | `ButtonTypes` | `'default', 'secondary', 'success', 'warning', 'error', 'abort'` | `default` | -| **size** | button size | `NormalSizes` | `'mini', 'small', 'medium', 'large'` | `medium` | +| **type** | button type | `ButtonTypes` | [ButtonTypes](#buttontypes) | `default` | +| **size** | button size | `NormalSizes` | [NormalSizes](#normalsizes) | `medium` | | **ghost** | the opposite color | `boolean` | - | `false` | | **loading** | display loading indicator | `boolean` | - | `false` | | **shadow** | display shadow | `boolean` | - | `false` | | **auto** | autoscale width | `boolean` | - | `false` | | **effect** | display animation | `boolean` | - | `true` | | **disabled** | disable button | `boolean` | - | `false` | +| **icon** | show icon in button | `ReactNode` | - | - | +| **iconRight** | show icon on the other side of the button | `ReactNode` | - | - | | **onClick** | click handler | `MouseEventHandler` | - | - | -| ... | native props | `ButtonHTMLAttributes` | `'autoFocus', 'name', 'className', ...` | - | +| ... | native props | `ButtonHTMLAttributes` | `'id', 'className', ...` | - | + +ButtonTypes + +```ts +type ButtonTypes = 'default' | 'secondary' | 'success' | 'warning' | 'error' | 'abort' +``` + +NormalSizes + +```ts +type NormalSizes = 'mini' | 'small' | 'medium' | 'large' +``` diff --git a/pages/zh-cn/components/button.mdx b/pages/zh-cn/components/button.mdx index 8fa6934..9c1ce7f 100644 --- a/pages/zh-cn/components/button.mdx +++ b/pages/zh-cn/components/button.mdx @@ -1,6 +1,9 @@ import { Layout, Playground, Attributes } from 'lib/components' import { Button, Spacer } from 'components' import Router from 'next/router' +import Settings from '@zeit-ui/react-icons/settings' +import UserX from '@zeit-ui/react-icons/userX' +import Power from '@zeit-ui/react-icons/power' export const meta = { title: '按钮 Button', @@ -85,13 +88,33 @@ export const meta = { `} /> + + + + + + + + + + + + + +`} /> + Button.Props | 属性 | 描述 | 类型 | 推荐值 | 默认 | ---------- | ---------- | ---- | -------------- | ------ | -| **type** | 按钮的类型 | `ButtonTypes` | `'default', 'secondary', 'success', 'warning', 'error', 'abort'` | `default` | -| **size** | 按钮的大小 | `NormalSizes` | `'mini', 'small', 'medium', 'large'` | `medium` | +| **type** | 按钮的类型 | `ButtonTypes` | [ButtonTypes](#buttontypes) | `default` | +| **size** | 按钮的大小 | `NormalSizes` | [NormalSizes](#normalsizes) | `medium` | | **ghost** | 是否为反色按钮 | `boolean` | - | `false` | | **loading** | 是否显示加载中的指示器 | `boolean` | - | `false` | | **shadow** | 是否显示阴影 | `boolean` | - | `false` | @@ -99,7 +122,21 @@ export const meta = { | **effect** | 是否显示动画效果 | `boolean` | - | `true` | | **disabled** | 是否禁用按钮 | `boolean` | - | `false` | | **onClick** | 点击事件 | `MouseEventHandler` | - | - | -| ... | 原生属性 | `ButtonHTMLAttributes` | `'autoFocus', 'name', 'className', ...` | - | +| **icon** | 在按钮内显示图标 | `ReactNode` | - | - | +| **iconRight** | 在按钮的另一侧显示图标 | `ReactNode` | - | - | +| ... | 原生属性 | `ButtonHTMLAttributes` | `'id', 'className', ...` | - | + +ButtonTypes + +```ts +type ButtonTypes = 'default' | 'secondary' | 'success' | 'warning' | 'error' | 'abort' +``` + +NormalSizes + +```ts +type NormalSizes = 'mini' | 'small' | 'medium' | 'large' +``` From 3eb90ef76ad808d06b7499ba7385784a14544911 Mon Sep 17 00:00:00 2001 From: unix Date: Thu, 7 May 2020 23:54:30 +0800 Subject: [PATCH 3/5] test(button): add testcase for icon --- .../__snapshots__/icon.test.tsx.snap | 189 ++++++++++++++++++ components/button/__tests__/icon.test.tsx | 41 ++++ components/button/__tests__/index.test.tsx | 2 +- 3 files changed, 231 insertions(+), 1 deletion(-) create mode 100644 components/button/__tests__/__snapshots__/icon.test.tsx.snap create mode 100644 components/button/__tests__/icon.test.tsx diff --git a/components/button/__tests__/__snapshots__/icon.test.tsx.snap b/components/button/__tests__/__snapshots__/icon.test.tsx.snap new file mode 100644 index 0000000..07c3288 --- /dev/null +++ b/components/button/__tests__/__snapshots__/icon.test.tsx.snap @@ -0,0 +1,189 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ButtonIcon should render correctly 1`] = ` +"" +`; + +exports[`ButtonIcon should work with right 1`] = ` +"" +`; diff --git a/components/button/__tests__/icon.test.tsx b/components/button/__tests__/icon.test.tsx new file mode 100644 index 0000000..02470d6 --- /dev/null +++ b/components/button/__tests__/icon.test.tsx @@ -0,0 +1,41 @@ +import React from 'react' +import { mount } from 'enzyme' +import { Button } from 'components' +const Icon: React.FC = () => + +describe('ButtonIcon', () => { + it('should render correctly', () => { + const wrapper = mount() + expect(wrapper.html()).toMatchSnapshot() + expect(() => wrapper.unmount()).not.toThrow() + }) + + it('should work with right', () => { + const wrapper = mount() + expect(wrapper.html()).toMatchSnapshot() + expect(() => wrapper.unmount()).not.toThrow() + }) + + it('the width of the text should be filled', () => { + const autoWrapper = mount( + , + ) + const wrapper = mount() + + const autoHtml = autoWrapper.find('.text').html() + const html = wrapper.find('.text').html() + expect(html).not.toEqual(autoHtml) + + const mini = mount() + const miniIcon = mount( + , + ) + const miniHtml = mini.find('.text').html() + const miniIconHtml = miniIcon.find('.text').html() + expect(miniHtml).not.toEqual(miniIconHtml) + }) +}) diff --git a/components/button/__tests__/index.test.tsx b/components/button/__tests__/index.test.tsx index babf279..a11dce0 100644 --- a/components/button/__tests__/index.test.tsx +++ b/components/button/__tests__/index.test.tsx @@ -1,6 +1,6 @@ import React from 'react' import { mount } from 'enzyme' -import { Button } from '../../' +import { Button } from 'components' import { sleep } from 'tests/utils' describe('Button', () => { From 07d7858151605e0bf6b023821dd4b81a12f8feea Mon Sep 17 00:00:00 2001 From: unix Date: Fri, 8 May 2020 00:02:15 +0800 Subject: [PATCH 4/5] test(toast): remove testcase dependency on button component --- components/toast/__tests__/index.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/toast/__tests__/index.test.tsx b/components/toast/__tests__/index.test.tsx index 4755cb0..0dba30c 100644 --- a/components/toast/__tests__/index.test.tsx +++ b/components/toast/__tests__/index.test.tsx @@ -15,9 +15,9 @@ const MockToast: React.FC<{}> = () => { setToast(params) } return ( - + ) } From 080ed933311a243fd9623850392feb01fb2d362a Mon Sep 17 00:00:00 2001 From: unix Date: Fri, 8 May 2020 00:02:26 +0800 Subject: [PATCH 5/5] test: update snapshots --- .../__snapshots__/index.test.tsx.snap | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/components/toast/__tests__/__snapshots__/index.test.tsx.snap b/components/toast/__tests__/__snapshots__/index.test.tsx.snap index a23fd59..b35324e 100644 --- a/components/toast/__tests__/__snapshots__/index.test.tsx.snap +++ b/components/toast/__tests__/__snapshots__/index.test.tsx.snap @@ -19,7 +19,7 @@ exports[`UseToast should render different actions 1`] = ` justify-content: center; text-align: center; white-space: nowrap; - transition: all 0.2s ease; + transition: all 0.2s ease 0s; position: relative; overflow: hidden; color: #fff; @@ -28,10 +28,14 @@ exports[`UseToast should render different actions 1`] = ` cursor: pointer; pointer-events: auto; box-shadow: none; + --zeit-ui-button-padding: 0.625rem; + --zeit-ui-button-height: 1.5rem; + --zeit-ui-button-color: #fff; } .btn:hover { color: #000; + --zeit-ui-button-color: #000; background-color: #fff; border-color: #000; cursor: pointer; @@ -40,7 +44,7 @@ exports[`UseToast should render different actions 1`] = ` transform: translate3d(0px, 0px, 0px); } - .text { + .btn :global(.text) { position: relative; z-index: 1; display: inline-flex; @@ -51,9 +55,9 @@ exports[`UseToast should render different actions 1`] = ` top: -1px; } - .text :global(p), - .text :global(pre), - .text :global(div) { + .btn :global(.text p), + .btn :global(.text pre), + .btn :global(.text div) { margin: 0; }