diff --git a/.dumi/app.tsx b/.dumi/app.tsx new file mode 100644 index 0000000..3214087 --- /dev/null +++ b/.dumi/app.tsx @@ -0,0 +1,10 @@ +import { Navigate } from 'dumi'; +import * as React from 'react'; + +export function patchClientRoutes({ routes }) { + routes[0].children.unshift({ + id: 'demo-redirect', + path: '/demo', + element: , + }); +} diff --git a/.dumi/tsconfig.json b/.dumi/tsconfig.json new file mode 100644 index 0000000..79711a8 --- /dev/null +++ b/.dumi/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.json", + "include": [ + "**/*" + ] +} \ No newline at end of file diff --git a/.dumirc.ts b/.dumirc.ts index f7a1213..e6e98c8 100644 --- a/.dumirc.ts +++ b/.dumirc.ts @@ -1,6 +1,12 @@ import { defineConfig } from 'dumi'; import path from 'path'; +const isProdSite = + // 不是预览模式 同时是生产环境 + process.env.PREVIEW !== 'true' && process.env.NODE_ENV === 'production'; + +const name = 'tooltip'; + export default defineConfig({ alias: { 'rc-tooltip$': path.resolve('src'), @@ -12,4 +18,6 @@ export default defineConfig({ name: 'Tooltip', logo: 'https://avatars0.githubusercontent.com/u/9441414?s=200&v=4', }, + base: isProdSite ? `/${name}/` : '/', + publicPath: isProdSite ? `/${name}/` : '/', }); diff --git a/.github/workflows/react-component-ci.yml b/.github/workflows/react-component-ci.yml index 432a3fb..467f037 100644 --- a/.github/workflows/react-component-ci.yml +++ b/.github/workflows/react-component-ci.yml @@ -15,7 +15,7 @@ jobs: - uses: actions/setup-node@v1 with: - node-version: '12' + node-version: '14' - name: cache package-lock.json uses: actions/cache@v2 @@ -43,7 +43,7 @@ jobs: - name: install if: steps.node_modules_cache_id.outputs.cache-hit != 'true' run: npm ci - + lint: runs-on: ubuntu-latest steps: @@ -66,7 +66,7 @@ jobs: run: npm run lint needs: setup - + compile: runs-on: ubuntu-latest steps: @@ -89,7 +89,7 @@ jobs: run: npm run compile needs: setup - + coverage: runs-on: ubuntu-latest steps: diff --git a/README.md b/README.md index 4522ff5..42c4d80 100644 --- a/README.md +++ b/README.md @@ -2,20 +2,31 @@ React Tooltip -[![NPM version][npm-image]][npm-url] [![dumi](https://img.shields.io/badge/docs%20by-dumi-blue?style=flat-square)](https://github.com/umijs/dumi) [![build status][github-actions-image]][github-actions-url] [![Test coverage][coveralls-image]][coveralls-url] [![gemnasium deps][gemnasium-image]][gemnasium-url] [![node version][node-image]][node-url] [![npm download][download-image]][download-url] - -[npm-image]: https://img.shields.io/npm/v/rc-tooltip.svg?style=flat-square -[npm-url]: https://npmjs.org/package/rc-tooltip +[![NPM version][npm-image]][npm-url] +[![npm download][download-image]][download-url] +[![build status][github-actions-image]][github-actions-url] +[![Codecov][codecov-image]][codecov-url] +[![bundle size][bundlephobia-image]][bundlephobia-url] +[![dumi][dumi-image]][dumi-url] + +[npm-image]: http://img.shields.io/npm/v/rc-tooltip.svg?style=flat-square +[npm-url]: http://npmjs.org/package/rc-tooltip +[travis-image]: https://img.shields.io/travis/react-component/tooltip/master?style=flat-square +[travis-url]: https://travis-ci.com/react-component/tooltip [github-actions-image]: https://github.com/react-component/tooltip/workflows/CI/badge.svg [github-actions-url]: https://github.com/react-component/tooltip/actions -[coveralls-image]: https://img.shields.io/coveralls/react-component/tooltip.svg?style=flat-square -[coveralls-url]: https://coveralls.io/r/react-component/tooltip?branch=master -[gemnasium-image]: https://img.shields.io/gemnasium/react-component/tooltip.svg?style=flat-square -[gemnasium-url]: https://gemnasium.com/react-component/tooltip -[node-image]: https://img.shields.io/badge/node.js-%3E=_0.10-green.svg?style=flat-square -[node-url]: https://nodejs.org/download/ +[codecov-image]: https://img.shields.io/codecov/c/github/react-component/tooltip/master.svg?style=flat-square +[codecov-url]: https://app.codecov.io/gh/react-component/tooltip +[david-url]: https://david-dm.org/react-component/tooltip +[david-image]: https://david-dm.org/react-component/tooltip/status.svg?style=flat-square +[david-dev-url]: https://david-dm.org/react-component/tooltip?type=dev +[david-dev-image]: https://david-dm.org/react-component/tooltip/dev-status.svg?style=flat-square [download-image]: https://img.shields.io/npm/dm/rc-tooltip.svg?style=flat-square [download-url]: https://npmjs.org/package/rc-tooltip +[bundlephobia-url]: https://bundlephobia.com/package/rc-tooltip +[bundlephobia-image]: https://badgen.net/bundlephobia/minzip/rc-tooltip +[dumi-url]: https://github.com/umijs/dumi +[dumi-image]: https://img.shields.io/badge/docs%20by-dumi-blue?style=flat-square ## Screenshot @@ -24,9 +35,8 @@ React Tooltip ## Browsers support | [IE / Edge](http://godban.github.io/browsers-support-badges/)
IE / Edge | [Firefox](http://godban.github.io/browsers-support-badges/)
Firefox | [Chrome](http://godban.github.io/browsers-support-badges/)
Chrome | [Safari](http://godban.github.io/browsers-support-badges/)
Safari | [Opera](http://godban.github.io/browsers-support-badges/)
Opera | -| --------- | --------- | --------- | --------- | --------- | -| IE 8 + ✔ | Firefox 31.0+ ✔ | Chrome 31.0+ ✔ | Safari 7.0+ ✔ | Opera 30.0+ ✔ | - +| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| IE 8 + ✔ | Firefox 31.0+ ✔ | Chrome 31.0+ ✔ | Safari 7.0+ ✔ | Opera 30.0+ ✔ | ## Install @@ -47,159 +57,41 @@ ReactDOM.render( tooltip}> hover , - container + container, ); ``` ## Examples `npm start` and then go to - + -Online examples: +Online demo: ## API ### Props - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
nametypedefaultdescription
overlayClassNamestringadditional className added to popup overlay
triggerstring | string[]['hover']which actions cause tooltip shown. enum of 'hover','click','focus'
mouseEnterDelaynumber0delay time to show when mouse enter.unit: s.
mouseLeaveDelaynumber0.1delay time to hide when mouse leave.unit: s.
overlayStyleObjectadditional style of overlay node
prefixClsStringrc-tooltipprefix class name
transitionNameString|Objectsame as https://github.com/react-component/animate
onVisibleChangeFunctioncall when visible is changed
afterVisibleChangeFunctioncall after visible is changed
visiblebooleanwhether tooltip is visible
defaultVisiblebooleanwhether tooltip is visible initially
placementStringone of ['left','right','top','bottom', 'topLeft', 'topRight', 'bottomLeft', 'bottomRight', 'rightTop', 'rightBottom', 'leftTop', 'leftBottom']
alignObject: alignConfig of [dom-align](https://github.com/yiminghe/dom-align)value will be merged into placement's config
onPopupAlignfunction(popupDomNode, align)callback when popup node is aligned
overlayReact.Element | () => React.Elementpopup content
overlayInnerStyleObjectset overlay inner style
showArrowbooleantruearrow visible
arrowContentReact.Nodenullarrow content
getTooltipContainerfunctionFunction returning html node which will act as tooltip container. By default the tooltip attaches to the body. If you want to change the container, simply return a new element.
destroyTooltipOnHideboolean | { keepParent: boolean }falsewhether destroy tooltip when tooltip is hidden.In general, destroyTooltipOnHide will only remove itself instead of parent container of it. Parent container will be removed include tooltip when keepParent is true
idStringId which gets attached to the tooltip content. Can be used with aria-describedby to achieve Screenreader-Support.
+| name | type | default | description | +| -------------------- | ----------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| trigger | string \| string\[] | 'hover' | which actions cause tooltip shown. enum of 'hover','click','focus' | +| visible | boolean | false | whether tooltip is visible | +| defaultVisible | boolean | false | whether tooltip is visible by default | +| placement | string | 'right' | tooltip placement. enum of 'top','left','right','bottom', 'topLeft', 'topRight', 'bottomLeft', 'bottomRight', 'leftTop', 'leftBottom', 'rightTop', 'rightBottom' | +| motion | object | | Config popup motion. Please ref demo for example | +| onVisibleChange | (visible: boolean) => void; | | Callback when visible change | +| afterVisibleChange | (visible: boolean) => void; | | Callback after visible change | +| overlay | ReactNode \| () => ReactNode | | tooltip overlay content | +| overlayStyle | object | | style of tooltip overlay | +| overlayClassName | string | | className of tooltip overlay | +| prefixCls | string | 'rc-tooltip' | prefix class name of tooltip | +| mouseEnterDelay | number | 0 | delay time (in second) before tooltip shows when mouse enter | +| mouseLeaveDelay | number | 0.1 | delay time (in second) before tooltip hides when mouse leave | +| getTooltipContainer | (triggerNode: HTMLElement) => HTMLElement | () => document.body | get container of tooltip, default to body | +| destroyTooltipOnHide | boolean | false | destroy tooltip when it is hidden | +| align | object | | align config of tooltip. Please ref demo for usage example | +| showArrow | boolean \| object | false | whether to show arrow of tooltip | +| zIndex | number | | config popup tooltip zIndex | ## Important Note @@ -208,6 +100,7 @@ Online examples: ## Accessibility For accessibility purpose you can use the `id` prop to link your tooltip with another element. For example attaching it to an input element: + ```js ``` + If you do it like this, a screenreader would read the content of your tooltip if you focus the input element. **NOTE:** `role="tooltip"` is also added to expose the purpose of the tooltip element to a screenreader. diff --git a/assets/bootstrap_white.less b/assets/bootstrap_white.less index b6deb24..e123dd1 100644 --- a/assets/bootstrap_white.less +++ b/assets/bootstrap_white.less @@ -104,6 +104,7 @@ margin-top: -@tooltip-arrow-width; border-width: @tooltip-arrow-width @tooltip-arrow-width @tooltip-arrow-width 0; border-right-color: @tooltip-arrow-color; + transform: translateX(calc(-100% + @tooltip-shadow-width)); } &-placement-right &-arrow-inner, @@ -135,6 +136,7 @@ margin-top: -@tooltip-arrow-width; border-width: @tooltip-arrow-width 0 @tooltip-arrow-width @tooltip-arrow-width; border-left-color: @tooltip-arrow-color; + transform: translateX(calc(100% - @tooltip-shadow-width)); } &-placement-left &-arrow-inner, diff --git a/package.json b/package.json index 42efdda..721a396 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rc-tooltip", - "version": "6.0.1", + "version": "6.1.2", "description": "React Tooltip", "keywords": [ "react", @@ -23,7 +23,6 @@ "main": "lib/index", "module": "es/index", "files": [ - "dist", "lib", "es", "assets/*.css", @@ -31,32 +30,33 @@ ], "scripts": { "compile": "father build && lessc assets/bootstrap.less assets/bootstrap.css && lessc assets/bootstrap_white.less assets/bootstrap_white.css", + "prepare": "dumi setup", "docs:build": "dumi build", - "docs:deploy": "gh-pages -d .doc", + "docs:deploy": "gh-pages -d dist", "lint": "eslint src/ --ext .tsx,.ts,.jsx,.js", - "now-build": "npm run build", "prepublishOnly": "npm run compile && np --no-cleanup --yolo --no-publish", + "postpublish": "npm run docs:build && npm run docs:deploy", "start": "dumi dev", "test": "rc-test" }, "dependencies": { "@babel/runtime": "^7.11.2", - "@rc-component/trigger": "^1.0.4", + "@rc-component/trigger": "^1.18.0", "classnames": "^2.3.1" }, "devDependencies": { "@rc-component/father-plugin": "^1.0.0", "@testing-library/react": "^14.0.0", - "@types/jest": "^26.0.0", + "@types/jest": "^29.4.0", "@types/react": "^18.0.26", "@types/react-dom": "^18.0.10", "@types/warning": "^3.0.0", "cross-env": "^7.0.0", - "dumi": "^2.1.1", + "dumi": "^2.2.13", "eslint": "^7.1.0", "father": "^4.0.0", "gh-pages": "^3.1.0", - "less": "^3.11.1", + "less": "^4.1.1", "np": "^7.1.0", "rc-test": "^7.0.9", "react": "^18.2.0", diff --git a/src/Tooltip.tsx b/src/Tooltip.tsx index 56dfdff..944f711 100644 --- a/src/Tooltip.tsx +++ b/src/Tooltip.tsx @@ -1,4 +1,4 @@ -import type { TriggerProps, TriggerRef } from '@rc-component/trigger'; +import type { ArrowType, TriggerProps, TriggerRef } from '@rc-component/trigger'; import Trigger from '@rc-component/trigger'; import type { ActionType, AlignType, AnimationType } from '@rc-component/trigger/lib/interface'; import * as React from 'react'; @@ -6,7 +6,17 @@ import { forwardRef, useImperativeHandle, useRef } from 'react'; import { placements } from './placements'; import Popup from './Popup'; -export interface TooltipProps extends Pick { +export interface TooltipProps + extends Pick< + TriggerProps, + | 'onPopupAlign' + | 'builtinPlacements' + | 'fresh' + | 'children' + | 'mouseLeaveDelay' + | 'mouseEnterDelay' + | 'prefixCls' + > { trigger?: ActionType | ActionType[]; defaultVisible?: boolean; visible?: boolean; @@ -22,22 +32,18 @@ export interface TooltipProps extends Pick React.ReactNode) | React.ReactNode; overlayStyle?: React.CSSProperties; overlayClassName?: string; - prefixCls?: string; - mouseEnterDelay?: number; - mouseLeaveDelay?: number; getTooltipContainer?: (node: HTMLElement) => HTMLElement; destroyTooltipOnHide?: boolean; align?: AlignType; - showArrow?: boolean; + showArrow?: boolean | ArrowType; arrowContent?: React.ReactNode; id?: string; - children?: React.ReactElement; - popupVisible?: boolean; overlayInnerStyle?: React.CSSProperties; zIndex?: number; } export interface TooltipRef { + nativeElement: HTMLElement; forceAlign: VoidFunction; } @@ -71,7 +77,7 @@ const Tooltip = (props: TooltipProps, ref: React.Ref) => { const triggerRef = useRef(null); useImperativeHandle(ref, () => triggerRef.current); - const extraProps = { ...restProps }; + const extraProps: Partial = { ...restProps }; if ('visible' in props) { extraProps.popupVisible = props.visible; } diff --git a/src/index.tsx b/src/index.tsx index ff0529f..6da28f5 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,6 +1,7 @@ -import Tooltip from './Tooltip'; import Popup from './Popup'; +import Tooltip from './Tooltip'; +export type { TooltipRef } from './Tooltip'; export { Popup }; export default Tooltip; diff --git a/tests/index.test.tsx b/tests/index.test.tsx index 6784be1..d167d0c 100644 --- a/tests/index.test.tsx +++ b/tests/index.test.tsx @@ -1,6 +1,6 @@ import { act, fireEvent, render } from '@testing-library/react'; import React from 'react'; -import Tooltip from '../src'; +import Tooltip, { TooltipRef } from '../src'; const verifyContent = (wrapper: HTMLElement, content: string) => { expect(wrapper.querySelector('.x-content').textContent).toBe(content); @@ -178,9 +178,26 @@ describe('rc-tooltip', () => { , ); fireEvent.click(container.querySelector('.target')); - console.log(container.innerHTML); expect(container.querySelector('.rc-tooltip-arrow')).toBeTruthy(); }); + it('should show tooltip arrow when showArrow is object', () => { + const { container } = render( + Tooltip content} + showArrow={{ + className: 'abc', + }} + > +
Click this
+
, + ); + fireEvent.click(container.querySelector('.target')); + expect(container.querySelector('.rc-tooltip-arrow')).toBeTruthy(); + expect(container.querySelector('.rc-tooltip-arrow').classList.contains('abc')).toBeTruthy(); + }); it('should hide tooltip arrow when showArrow is false', () => { const { container } = render( { fireEvent.click(container.querySelector('.target')); expect(container.querySelector('.x-content')).toBeTruthy(); }); + + it('ref support nativeElement', () => { + const nodeRef = React.createRef(); + + const { container } = render( + }> +