Skip to content
This repository was archived by the owner on Mar 4, 2020. It is now read-only.

Commit edca816

Browse files
authored
feat(prototypes): Allow attaching notification to a target ref in CopyToClipboard prototype (#1900)
* add target ref to notification * closer to the spec * v2 * small fixes * changelog * fix * prettier * fix
1 parent db0932f commit edca816

File tree

7 files changed

+146
-168
lines changed

7 files changed

+146
-168
lines changed

.github/add-a-feature.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
44
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
55

6+
67
- [Propose feature](#propose-feature)
78
- [Prototype](#prototype)
89
- [Spec out the API](#spec-out-the-api)

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
3131
- Simplify rendering when tooltip is not visible @jurokapsiar ([#1981](https://github.com/stardust-ui/react/pull/1981))
3232
- Add `thumbtack`, `thumbtack-slash` and `question-circle` icons to Teams theme @codepretty ([#2000](https://github.com/stardust-ui/react/pull/2000))
3333

34+
### Documentation
35+
- Copy to clipboard prototype - attached confirmation @jurokapsiar ([#1900](https://github.com/stardust-ui/react/pull/1900))
36+
37+
3438
<!--------------------------------[ v0.39.0 ]------------------------------- -->
3539
## [v0.39.0](https://github.com/stardust-ui/react/tree/v0.39.0) (2019-09-23)
3640
[Compare changes](https://github.com/stardust-ui/react/compare/v0.38.1...v0.39.0)

docs/src/prototypes/CopyToClipboard/CopyToClipboard.tsx

Lines changed: 25 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,25 @@
1-
import { Text, Tooltip, ShorthandValue, TextProps, TooltipProps } from '@stardust-ui/react'
1+
import { ShorthandValue, Tooltip, TooltipProps } from '@stardust-ui/react'
22
import * as copyToClipboard from 'copy-to-clipboard'
33
import * as _ from 'lodash'
44
import * as React from 'react'
55

6-
import { NotificationContext } from './NotificationProvider'
6+
import { Notification, NotificationContext } from './NotificationProvider'
77

88
export type CopyToClipboardProps = {
9+
tooltip?: ShorthandValue<TooltipProps>
910
attached?: boolean
10-
pointing?: boolean
11+
target?: HTMLElement
12+
notification?: React.ReactNode
1113
timeout?: number
1214
value: string
13-
14-
noticeText?: ShorthandValue<TextProps>
15-
promptText?: ShorthandValue<TextProps>
16-
17-
align?: TooltipProps['align']
18-
position?: TooltipProps['position']
19-
2015
trigger: JSX.Element
2116
}
2217

2318
const CopyToClipboard: React.FC<CopyToClipboardProps> = props => {
24-
const {
25-
align,
26-
attached,
27-
noticeText,
28-
pointing,
29-
position,
30-
promptText,
31-
timeout,
32-
trigger,
33-
value,
34-
} = props
19+
const { value, trigger, tooltip, attached, notification, timeout, target } = props
3520

3621
const setNotification = React.useContext(NotificationContext)
3722
const [copied, setCopied] = React.useState<boolean>(false)
38-
const [promptOpen, setPromptOpen] = React.useState<boolean>(false)
3923
const timeoutId = React.useRef<number>()
4024

4125
React.useEffect(() => {
@@ -50,7 +34,7 @@ const CopyToClipboard: React.FC<CopyToClipboardProps> = props => {
5034
(e: React.MouseEvent, ...args) => {
5135
setCopied(true)
5236
if (!attached) {
53-
setNotification(Text.create(noticeText), timeout)
37+
setNotification(notification, target, timeout)
5438
}
5539

5640
copyToClipboard(value)
@@ -59,31 +43,27 @@ const CopyToClipboard: React.FC<CopyToClipboardProps> = props => {
5943
[value],
6044
)
6145

62-
const tooltipContent = copied
63-
? { content: Text.create(noticeText), variables: { primary: true } }
64-
: {
65-
content: Text.create(promptText),
66-
variables: { basic: true },
67-
}
68-
const tooltipOpen = (promptOpen && !copied) || (copied && attached)
69-
return (
70-
<Tooltip
71-
align={align}
72-
content={tooltipContent}
73-
pointing={pointing}
74-
position={position}
75-
onOpenChange={(e, data) => setPromptOpen(data.open)}
76-
open={tooltipOpen}
77-
trigger={React.cloneElement(trigger, { onClick: handleTriggerClick })}
78-
/>
79-
)
46+
const renderedTrigger = React.cloneElement(trigger, { onClick: handleTriggerClick })
47+
48+
if (copied && attached) {
49+
return <Notification trigger={renderedTrigger} content={notification} />
50+
}
51+
52+
if (copied || !tooltip) {
53+
return renderedTrigger
54+
}
55+
56+
return Tooltip.create(tooltip, {
57+
overrideProps: {
58+
trigger: renderedTrigger,
59+
children: undefined, // force-reset `children` defined for `Tooltip` as it collides with the `trigger
60+
},
61+
})
8062
}
8163

8264
CopyToClipboard.defaultProps = {
83-
align: 'center',
84-
noticeText: 'Copied to clipboard',
85-
position: 'below',
86-
promptText: 'Click to copy',
65+
notification: 'Copied to clipboard',
66+
tooltip: 'Click to copy',
8767
timeout: 4000,
8868
}
8969

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,34 @@
1-
import { Portal, createComponent } from '@stardust-ui/react'
1+
import { Portal, Tooltip, createComponent, TooltipProps } from '@stardust-ui/react'
22
import * as React from 'react'
33

44
type NotificationProps = {
5-
children?: React.ReactNode
5+
content: React.ReactNode
6+
target?: HTMLElement
7+
trigger?: JSX.Element
68
}
79

8-
type NotificationContextValue = (value: React.ReactNode, timeout: number) => void
10+
type NotificationContextValue = (
11+
content: React.ReactNode,
12+
target: HTMLElement | null,
13+
timeout: number,
14+
) => void
15+
916
export const NotificationContext = React.createContext<NotificationContextValue>(() => {
1017
throw new Error('No matching NotificationContext.Provider')
1118
})
1219

1320
export const NotificationProvider: React.FC = props => {
1421
const { children } = props
1522
const [notification, setNotification] = React.useState<React.ReactNode>()
23+
const [target, setTarget] = React.useState<HTMLElement | null>()
1624
const timeoutId = React.useRef<number>()
1725

18-
const update = React.useCallback<NotificationContextValue>((value, timeout) => {
19-
setNotification(value)
26+
const update = React.useCallback<NotificationContextValue>((notification, target, timeout) => {
27+
setNotification(notification)
28+
setTarget(target)
2029
timeoutId.current = window.setTimeout(() => {
2130
setNotification(null)
31+
setTarget(null)
2232
}, timeout)
2333
}, [])
2434

@@ -28,23 +38,38 @@ export const NotificationProvider: React.FC = props => {
2838

2939
return (
3040
<>
31-
<Portal open={!!notification}>
32-
<Notification>{notification}</Notification>
33-
</Portal>
41+
{!!notification && <Notification target={target} content={notification} />}
3442
<NotificationContext.Provider value={update}>{children}</NotificationContext.Provider>
3543
</>
3644
)
3745
}
3846

3947
export const Notification = createComponent<NotificationProps>({
4048
displayName: 'Notification',
41-
render: ({ children, stardust: { classes } }) => {
49+
render: ({ target, trigger, content, stardust: { classes } }) => {
50+
const tooltipProps: TooltipProps = {
51+
content,
52+
open: true,
53+
pointing: false,
54+
target,
55+
trigger,
56+
}
57+
58+
if (target || trigger) {
59+
return Tooltip.create({ ...tooltipProps, offset: '0 10' })
60+
}
61+
4262
return (
43-
<div className={classes.root}>
44-
<div className={classes.overlay}>
45-
<div className={classes.content}>{children}</div>
63+
<Portal open>
64+
<div className={classes.root}>
65+
<div className={classes.overlay}>
66+
{Tooltip.create({
67+
...tooltipProps,
68+
trigger: <div className={classes.content} />,
69+
})}
70+
</div>
4671
</div>
47-
</div>
72+
</Portal>
4873
)
4974
},
5075
})

docs/src/prototypes/CopyToClipboard/index.tsx

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import * as React from 'react'
2-
import { Flex, Provider, Text, Button, Menu } from '@stardust-ui/react'
2+
import { Flex, Provider, Text, Button, Menu, Ref } from '@stardust-ui/react'
33
import CopyToClipboard from './CopyToClipboard'
44
import { PrototypeSection, ComponentPrototype } from '../Prototypes'
55
import themeOverrides from './themeOverrides'
66
import { NotificationProvider } from './NotificationProvider'
77

88
type CopyToClipboardPrototypeProps = {
99
value: string
10+
target?: HTMLElement
1011
attached?: boolean
1112
}
1213

@@ -18,7 +19,7 @@ const CopyToClipboardPrototype: React.FC<CopyToClipboardPrototypeProps> = props
1819

1920
<CopyToClipboard
2021
attached={props.attached}
21-
pointing
22+
target={props.target}
2223
value={props.value}
2324
trigger={<Button iconOnly icon="clipboard-copied-to" />}
2425
/>
@@ -27,27 +28,51 @@ const CopyToClipboardPrototype: React.FC<CopyToClipboardPrototypeProps> = props
2728
}
2829

2930
const CopyToClipboardInMenu: React.FC = props => {
31+
const item = {
32+
key: 'edit',
33+
content: 'Edit',
34+
menu: [
35+
'Open File...',
36+
'Save File...',
37+
render =>
38+
render('Copy text', (Component, props) => {
39+
return <CopyToClipboard value={'Julius Caesar'} trigger={<Component {...props} />} />
40+
}),
41+
],
42+
}
43+
44+
return <Menu items={[item]} />
45+
}
46+
47+
const CopyToClipboardAttached: React.FC = props => {
48+
const [target, setTarget] = React.useState<HTMLElement>(null)
49+
50+
const item = {
51+
key: 'edit',
52+
content: 'Edit',
53+
menu: [
54+
'Open File...',
55+
'Save File...',
56+
render =>
57+
render('Copy text', (Component, props) => {
58+
return (
59+
<CopyToClipboard
60+
target={target}
61+
value="Julius Caesar"
62+
trigger={<Component {...props} />}
63+
/>
64+
)
65+
}),
66+
],
67+
}
68+
3069
const items = [
31-
{
32-
key: 'edit',
33-
content: 'Edit',
34-
menu: [
35-
'Open File...',
36-
'Save File...',
37-
render =>
38-
render('Copy text', (Component, props) => {
39-
return (
40-
<CopyToClipboard
41-
position="after"
42-
align="bottom"
43-
pointing
44-
value={'Julius Caesar'}
45-
trigger={<Component {...props} />}
46-
/>
47-
)
48-
}),
49-
],
50-
},
70+
render =>
71+
render(item, (Component, props) => (
72+
<Ref innerRef={setTarget}>
73+
<Component {...props} />
74+
</Ref>
75+
)),
5176
]
5277
return <Menu items={items} />
5378
}
@@ -66,7 +91,7 @@ const CopyToClipboardPrototypes: React.FC = () => {
6691
title="Attached"
6792
description="Attached version of Copy to Clipboard prototype"
6893
>
69-
<CopyToClipboardPrototype attached value={commitID} />
94+
<CopyToClipboardPrototype attached={true} value={commitID} />
7095
</ComponentPrototype>
7196
<ComponentPrototype
7297
title="Not Attached"
@@ -80,6 +105,12 @@ const CopyToClipboardPrototypes: React.FC = () => {
80105
>
81106
<CopyToClipboardInMenu />
82107
</ComponentPrototype>
108+
<ComponentPrototype
109+
title="In Menu Attached"
110+
description="Copy to Clipboard can be attached to a different element"
111+
>
112+
<CopyToClipboardAttached />
113+
</ComponentPrototype>
83114
</NotificationProvider>
84115
</Provider>
85116
</PrototypeSection>

0 commit comments

Comments
 (0)