@cypress-design/react-tooltip 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/ReadMe.md +28 -0
- package/Tooltip.rootstory.tsx +43 -0
- package/Tooltip.stories.mdx +43 -0
- package/Tooltip.tsx +167 -0
- package/TooltipReact.cy.tsx +14 -0
- package/dist/Tooltip.d.ts +13 -0
- package/dist/Tooltip.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.es.js +33301 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +33326 -0
- package/dist/index.umd.js.map +1 -0
- package/index.ts +1 -0
- package/package.json +26 -0
- package/rollup.config.js +3 -0
- package/tsconfig.build.json +7 -0
- package/tsconfig.json +4 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# @cypress-design/react-tooltip
|
|
2
|
+
|
|
3
|
+
## 0.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#30](https://github.com/cypress-io/cypress-design/pull/30) [`7194ec4`](https://github.com/cypress-io/cypress-design/commit/7194ec428b96cdb5b66e4f23f63379000db19e08) Thanks [@elevatebart](https://github.com/elevatebart)! - creation of tooltips component
|
package/ReadMe.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Tooltip
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
Use the tooltip component to display a message when the user hovers over an element.
|
|
6
|
+
For accessibility, it also triggers on focus.
|
|
7
|
+
|
|
8
|
+
To allow focus, one has to add `tabIndex="0"` to the Tooltip element.
|
|
9
|
+
|
|
10
|
+
```jsx
|
|
11
|
+
const comp = () => (
|
|
12
|
+
<Tooltip tabIndex="0">
|
|
13
|
+
<span>Focus me</span>
|
|
14
|
+
</Tooltip>
|
|
15
|
+
)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @cypress-design/react-tooltip
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
or with yarn
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
yarn add @cypress-design/react-tooltip
|
|
28
|
+
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Placement } from '@floating-ui/react-dom-interactions'
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
import Tooltip from './Tooltip'
|
|
4
|
+
|
|
5
|
+
export default ({
|
|
6
|
+
color = 'light',
|
|
7
|
+
placement,
|
|
8
|
+
tabIndex,
|
|
9
|
+
}: {
|
|
10
|
+
color?: 'light' | 'dark'
|
|
11
|
+
placement?: Placement
|
|
12
|
+
tabIndex?: number
|
|
13
|
+
} = {}) => (
|
|
14
|
+
<div className="center flex flex-col items-center gap-20">
|
|
15
|
+
<Tooltip
|
|
16
|
+
className="bg-red-200 p-4 w-1/2"
|
|
17
|
+
popper={<div className="bg-jade-200 text-gray-800 p-4">Popover</div>}
|
|
18
|
+
color={color}
|
|
19
|
+
placement={placement}
|
|
20
|
+
tabIndex={tabIndex}
|
|
21
|
+
>
|
|
22
|
+
<div>Hover Me (dynamic: {placement?.toString()})</div>
|
|
23
|
+
</Tooltip>
|
|
24
|
+
{(['top', 'right', 'bottom', 'left', 'top-start'] as const).map(
|
|
25
|
+
(placement) => (
|
|
26
|
+
<Tooltip
|
|
27
|
+
className="bg-indigo-200 p-4 w-1/2"
|
|
28
|
+
placement={placement}
|
|
29
|
+
key={placement}
|
|
30
|
+
color={color}
|
|
31
|
+
tabIndex={tabIndex}
|
|
32
|
+
popper={
|
|
33
|
+
<div className="bg-jade-200 text-gray-800 p-4">
|
|
34
|
+
Popover ({placement})
|
|
35
|
+
</div>
|
|
36
|
+
}
|
|
37
|
+
>
|
|
38
|
+
<div>Hover Me ({placement})</div>
|
|
39
|
+
</Tooltip>
|
|
40
|
+
)
|
|
41
|
+
)}
|
|
42
|
+
</div>
|
|
43
|
+
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { ArgsTable, Canvas, Meta, Story, Description } from '@storybook/addon-docs'
|
|
2
|
+
import { version } from "./package.json"
|
|
3
|
+
import changelog from "./CHANGELOG.md"
|
|
4
|
+
import TooltipStory from './Tooltip.rootstory';
|
|
5
|
+
|
|
6
|
+
import { Tooltip } from './Tooltip'
|
|
7
|
+
|
|
8
|
+
<h1>Tooltip<span className="text-lg font-normal"> - v{version}</span></h1>
|
|
9
|
+
|
|
10
|
+
<Meta
|
|
11
|
+
title="Tooltip"
|
|
12
|
+
component={ Tooltip }
|
|
13
|
+
argTypes={{
|
|
14
|
+
color: {
|
|
15
|
+
control: { type: 'select' },
|
|
16
|
+
},
|
|
17
|
+
placement: {
|
|
18
|
+
control: { type: 'select' },
|
|
19
|
+
},
|
|
20
|
+
}}
|
|
21
|
+
parameters={{
|
|
22
|
+
design: {
|
|
23
|
+
type: "figma",
|
|
24
|
+
url: 'https://www.figma.com/file/1WJ3GVQyMV5e7xVxPg3yID/Design-System?node-id=893%3A0',
|
|
25
|
+
}
|
|
26
|
+
}}
|
|
27
|
+
/>
|
|
28
|
+
|
|
29
|
+
A tooltip is used to provide more information to help a user complete a task.
|
|
30
|
+
|
|
31
|
+
<Canvas withSource="none">
|
|
32
|
+
<Story name="Tooltip">
|
|
33
|
+
{(args) => {
|
|
34
|
+
return <TooltipStory {...args} />
|
|
35
|
+
}}
|
|
36
|
+
</Story>
|
|
37
|
+
</Canvas>
|
|
38
|
+
|
|
39
|
+
<ArgsTable />
|
|
40
|
+
|
|
41
|
+
<Description>
|
|
42
|
+
{changelog.replace(/^# .+/, '## Changelog')}
|
|
43
|
+
</Description>
|
package/Tooltip.tsx
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import clsx from 'clsx'
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
import {
|
|
4
|
+
Placement,
|
|
5
|
+
offset,
|
|
6
|
+
flip,
|
|
7
|
+
arrow,
|
|
8
|
+
autoUpdate,
|
|
9
|
+
useFloating,
|
|
10
|
+
useInteractions,
|
|
11
|
+
useHover,
|
|
12
|
+
useFocus,
|
|
13
|
+
useRole,
|
|
14
|
+
useDismiss,
|
|
15
|
+
safePolygon,
|
|
16
|
+
FloatingPortal,
|
|
17
|
+
} from '@floating-ui/react-dom-interactions'
|
|
18
|
+
|
|
19
|
+
export interface TooltipProps {
|
|
20
|
+
placement?: Placement
|
|
21
|
+
color?: 'light' | 'dark'
|
|
22
|
+
className?: string
|
|
23
|
+
tabIndex?: number | string
|
|
24
|
+
children?: React.ReactNode
|
|
25
|
+
popper?: React.ReactNode
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const ROTATE_MAP = {
|
|
29
|
+
bottom: 180,
|
|
30
|
+
left: 270,
|
|
31
|
+
top: 0,
|
|
32
|
+
right: 90,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const Tooltip: React.FC<
|
|
36
|
+
TooltipProps & React.HTMLProps<HTMLDivElement>
|
|
37
|
+
> = ({ placement, color = 'light', className, children, popper, ...rest }) => {
|
|
38
|
+
const arrowRef = React.useRef(null)
|
|
39
|
+
const [open, setOpen] = React.useState(false)
|
|
40
|
+
|
|
41
|
+
const [container] = React.useState(() => {
|
|
42
|
+
// This will be executed only on the initial render
|
|
43
|
+
// https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
|
|
44
|
+
return document.createElement('div')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
React.useEffect(() => {
|
|
48
|
+
document.body.appendChild(container)
|
|
49
|
+
return () => {
|
|
50
|
+
document.body.removeChild(container)
|
|
51
|
+
}
|
|
52
|
+
}, [])
|
|
53
|
+
|
|
54
|
+
const {
|
|
55
|
+
x,
|
|
56
|
+
y,
|
|
57
|
+
reference,
|
|
58
|
+
floating,
|
|
59
|
+
strategy,
|
|
60
|
+
placement: calculatedPlacement,
|
|
61
|
+
context,
|
|
62
|
+
middlewareData: { arrow: { x: arrowX, y: arrowY } = {} },
|
|
63
|
+
} = useFloating({
|
|
64
|
+
placement,
|
|
65
|
+
open,
|
|
66
|
+
onOpenChange: setOpen,
|
|
67
|
+
middleware: [flip(), offset(0), arrow({ element: arrowRef, padding: 24 })],
|
|
68
|
+
whileElementsMounted: autoUpdate,
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const { getReferenceProps, getFloatingProps } = useInteractions([
|
|
72
|
+
useHover(context, {
|
|
73
|
+
handleClose: safePolygon(),
|
|
74
|
+
}),
|
|
75
|
+
useFocus(context),
|
|
76
|
+
useRole(context, { role: 'tooltip' }),
|
|
77
|
+
useDismiss(context),
|
|
78
|
+
])
|
|
79
|
+
|
|
80
|
+
const placementSide = calculatedPlacement.split('-')[0]
|
|
81
|
+
const arrowRotate = ROTATE_MAP[placementSide]
|
|
82
|
+
|
|
83
|
+
let arrowXRule = 'left',
|
|
84
|
+
arrowYRule = 'top'
|
|
85
|
+
|
|
86
|
+
if (arrowX && arrowY) {
|
|
87
|
+
arrowXRule = 'left'
|
|
88
|
+
arrowYRule = 'top'
|
|
89
|
+
} else if (arrowX) {
|
|
90
|
+
arrowXRule = 'left'
|
|
91
|
+
arrowYRule = placementSide === 'top' ? 'bottom' : 'top'
|
|
92
|
+
} else if (arrowY) {
|
|
93
|
+
arrowXRule = placementSide === 'left' ? 'right' : 'left'
|
|
94
|
+
arrowYRule = 'top'
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<>
|
|
99
|
+
<div
|
|
100
|
+
{...getReferenceProps()}
|
|
101
|
+
ref={reference}
|
|
102
|
+
className={className}
|
|
103
|
+
{...rest}
|
|
104
|
+
>
|
|
105
|
+
{children}
|
|
106
|
+
</div>
|
|
107
|
+
<FloatingPortal>
|
|
108
|
+
{open && (
|
|
109
|
+
<div
|
|
110
|
+
{...getFloatingProps({
|
|
111
|
+
ref: floating,
|
|
112
|
+
style: {
|
|
113
|
+
position: strategy,
|
|
114
|
+
top: y ?? '',
|
|
115
|
+
left: x ?? '',
|
|
116
|
+
padding: '16px',
|
|
117
|
+
},
|
|
118
|
+
})}
|
|
119
|
+
>
|
|
120
|
+
<svg
|
|
121
|
+
ref={arrowRef}
|
|
122
|
+
viewBox="0 0 48 48"
|
|
123
|
+
width="24"
|
|
124
|
+
height="24"
|
|
125
|
+
className={clsx('absolute', [
|
|
126
|
+
color === 'dark' && 'stroke-gray-800 fill-gray-900',
|
|
127
|
+
color === 'light' && 'stroke-gray-100 fill-white',
|
|
128
|
+
])}
|
|
129
|
+
style={{
|
|
130
|
+
transform: `rotate(${arrowRotate}deg)`,
|
|
131
|
+
filter:
|
|
132
|
+
placementSide === 'bottom' || color === 'dark'
|
|
133
|
+
? undefined
|
|
134
|
+
: 'drop-shadow(0 1px 1px rgba(225, 227, 237, .8))',
|
|
135
|
+
[arrowXRule]: `${arrowX ?? -6}px`,
|
|
136
|
+
[arrowYRule]: `${arrowY ?? -6}px`,
|
|
137
|
+
}}
|
|
138
|
+
fill="none"
|
|
139
|
+
>
|
|
140
|
+
<rect x="0" y="0" width="48" height="4" strokeWidth="0" />
|
|
141
|
+
<path
|
|
142
|
+
d="M 0 3 C 12 3 18 18 24 18 C 30 18 36 3 48 3"
|
|
143
|
+
stroke-width="2"
|
|
144
|
+
/>
|
|
145
|
+
</svg>
|
|
146
|
+
<div
|
|
147
|
+
className={clsx(
|
|
148
|
+
'rounded shadow border p-8px text-16px leading-24px min-w-160px text-center',
|
|
149
|
+
[
|
|
150
|
+
color === 'dark' &&
|
|
151
|
+
'bg-gray-900 shadow-gray-800 border-gray-800',
|
|
152
|
+
color === 'light' &&
|
|
153
|
+
'bg-white shadow-gray-100 border-gray-100',
|
|
154
|
+
(!x || !y) && 'invisible',
|
|
155
|
+
]
|
|
156
|
+
)}
|
|
157
|
+
>
|
|
158
|
+
{popper}
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
)}
|
|
162
|
+
</FloatingPortal>
|
|
163
|
+
</>
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export default Tooltip
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/// <reference types="cypress" />
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { mount } from 'cypress/react'
|
|
5
|
+
import TooltipStory from './Tooltip.rootstory'
|
|
6
|
+
import assertions from '../assertions'
|
|
7
|
+
|
|
8
|
+
describe('Tooltip', { viewportHeight: 800, viewportWidth: 800 }, () => {
|
|
9
|
+
function mountStory(options: Parameters<typeof TooltipStory>[0] = {}) {
|
|
10
|
+
mount(<TooltipStory {...options} tabIndex={1} />)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
assertions(mountStory)
|
|
14
|
+
})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Placement } from '@floating-ui/react-dom-interactions';
|
|
3
|
+
export interface TooltipProps {
|
|
4
|
+
placement?: Placement;
|
|
5
|
+
color?: 'light' | 'dark';
|
|
6
|
+
className?: string;
|
|
7
|
+
tabIndex?: number | string;
|
|
8
|
+
children?: React.ReactNode;
|
|
9
|
+
popper?: React.ReactNode;
|
|
10
|
+
}
|
|
11
|
+
export declare const Tooltip: React.FC<TooltipProps & React.HTMLProps<HTMLDivElement>>;
|
|
12
|
+
export default Tooltip;
|
|
13
|
+
//# sourceMappingURL=Tooltip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tooltip.d.ts","sourceRoot":"","sources":["../Tooltip.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EACL,SAAS,EAaV,MAAM,qCAAqC,CAAA;AAE5C,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC1B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CACzB;AASD,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAC5B,YAAY,GAAG,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAiI/C,CAAA;AAED,eAAe,OAAO,CAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA"}
|