Skip to content

Commit 3979c8d

Browse files
authored
feat: add a copy to clipboard mechanism for the invokable url (#6246)
1 parent b03d7dc commit 3979c8d

File tree

3 files changed

+80
-31
lines changed

3 files changed

+80
-31
lines changed

src/dataExplorer/components/DataExplorerPage.tsx

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import {SCRIPT_EDITOR_PARAMS} from 'src/dataExplorer/components/resources'
4444

4545
const DataExplorerPageHeader: FC = () => {
4646
const {fluxQueryBuilder, setFluxQueryBuilder} = useContext(AppSettingContext)
47-
const {resource, setResource} = useContext(PersistanceContext)
47+
const {resource, save} = useContext(PersistanceContext)
4848
const history = useHistory()
4949

5050
const toggleSlider = () => {
@@ -60,32 +60,23 @@ const DataExplorerPageHeader: FC = () => {
6060
}
6161

6262
const handleRename = (name: string) => {
63-
setResource({
64-
...resource,
65-
data: {
66-
...resource.data,
67-
name,
68-
},
69-
})
63+
resource.data.name = name
64+
save(resource?.language)
7065
}
7166

7267
const showNewExplorer = fluxQueryBuilder && isFlagEnabled('newDataExplorer')
7368

7469
let pageTitle = <Page.Title title="Data Explorer" />
7570

7671
if (showNewExplorer && resource?.data?.hasOwnProperty('name')) {
77-
if (resource?.data?.type === ResourceType.Scripts) {
78-
pageTitle = <Page.Title title={resource?.data?.name ?? ''} />
79-
} else {
80-
pageTitle = (
81-
<RenamablePageTitle
82-
onRename={handleRename}
83-
name={resource?.data?.name || ''}
84-
placeholder="Untitled Script"
85-
maxLength={100}
86-
/>
87-
)
88-
}
72+
pageTitle = (
73+
<RenamablePageTitle
74+
onRename={handleRename}
75+
name={resource?.data?.name || ''}
76+
placeholder="Untitled Script"
77+
maxLength={100}
78+
/>
79+
)
8980
}
9081

9182
return (

src/dataExplorer/components/SaveAsScript.scss

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,22 @@
2323
}
2424

2525
.edit-overlay__delete-script {
26-
margin-top: 16px;
26+
margin-top: $cf-space-s;
27+
}
28+
29+
.invokable-script__url {
30+
display: flex;
31+
justify-content: space-between;
32+
33+
input {
34+
pointer-events: none;
35+
}
36+
37+
input:hover {
38+
cursor: pointer;
39+
}
40+
}
41+
42+
.script-description__input {
43+
margin-bottom: $cf-space-s;
2744
}

src/dataExplorer/components/SaveAsScript.tsx

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import React, {FC, useContext, useCallback, ChangeEvent, useState} from 'react'
22
import {
33
Button,
4+
ButtonShape,
45
ComponentColor,
6+
ComponentSize,
57
ComponentStatus,
68
Form,
79
IconFont,
810
Input,
911
InputLabel,
1012
InputType,
1113
Overlay,
14+
SquareButton,
1215
} from '@influxdata/clockface'
1316
import {useHistory} from 'react-router-dom'
1417
import {QueryContext} from 'src/shared/contexts/query'
@@ -30,7 +33,11 @@ import {DeleteScript} from 'src/dataExplorer/components/DeleteScript'
3033
import {LanguageType} from 'src/dataExplorer/components/resources'
3134
import {isFlagEnabled} from 'src/shared/utils/featureFlag'
3235
import {SCRIPT_EDITOR_PARAMS} from 'src/dataExplorer/components/resources'
33-
36+
import CopyToClipboard from 'src/shared/components/CopyToClipboard'
37+
import {
38+
copyToClipboardFailed,
39+
copyToClipboardSuccess,
40+
} from 'src/shared/copy/notifications'
3441
interface Props {
3542
language: LanguageType
3643
onClose: () => void
@@ -47,6 +54,9 @@ const SaveAsScript: FC<Props> = ({language, onClose, setOverlayType, type}) => {
4754
const {cancel} = useContext(QueryContext)
4855
const {setStatus, setResult} = useContext(ResultsContext)
4956
const [error, setError] = useState<string>()
57+
// Setting the name to state rather than persisting it to session storage
58+
// so that we can cancel out of a name change if needed
59+
const [newName, setNewName] = useState<string>(resource?.data?.name)
5060
const org = useSelector(getOrg)
5161

5262
const handleClose = () => {
@@ -80,13 +90,7 @@ const SaveAsScript: FC<Props> = ({language, onClose, setOverlayType, type}) => {
8090
}
8191

8292
const handleUpdateName = (event: ChangeEvent<HTMLInputElement>) => {
83-
setResource({
84-
...resource,
85-
data: {
86-
...(resource?.data ?? {}),
87-
name: event.target.value,
88-
},
89-
})
93+
setNewName(event.target.value)
9094
}
9195

9296
const clear = useCallback(() => {
@@ -109,6 +113,7 @@ const SaveAsScript: FC<Props> = ({language, onClose, setOverlayType, type}) => {
109113
}, [onClose, setStatus, setResult, cancel, history, org?.id, type])
110114

111115
const handleSaveScript = () => {
116+
resource.data.name = newName
112117
save(language)
113118
.then(() => {
114119
setError(null)
@@ -131,6 +136,17 @@ const SaveAsScript: FC<Props> = ({language, onClose, setOverlayType, type}) => {
131136
setOverlayType(OverlayType.DELETE)
132137
}
133138

139+
const handleCopyAttempt = (
140+
copiedText: string,
141+
isSuccessful: boolean
142+
): void => {
143+
if (isSuccessful) {
144+
dispatch(notify(copyToClipboardSuccess(copiedText, 'Invokable URL')))
145+
} else {
146+
dispatch(notify(copyToClipboardFailed(copiedText, 'Invokable URL')))
147+
}
148+
}
149+
134150
if (type == null) {
135151
return null
136152
}
@@ -186,7 +202,7 @@ const SaveAsScript: FC<Props> = ({language, onClose, setOverlayType, type}) => {
186202
name="name"
187203
required
188204
type={InputType.Text}
189-
value={resource?.data?.name}
205+
value={newName}
190206
onChange={handleUpdateName}
191207
status={error ? ComponentStatus.Error : ComponentStatus.Default}
192208
/>
@@ -197,12 +213,37 @@ const SaveAsScript: FC<Props> = ({language, onClose, setOverlayType, type}) => {
197213
)}
198214
<InputLabel>Description</InputLabel>
199215
<Input
216+
className="script-description__input"
200217
name="description"
201218
required
202219
type={InputType.Text}
203220
value={resource?.data?.description}
204221
onChange={handleUpdateDescription}
205222
/>
223+
{type === OverlayType.EDIT && (
224+
<>
225+
<InputLabel>Invokable URL</InputLabel>
226+
<CopyToClipboard
227+
text={`${window.location.origin}/api/v2/scripts/${resource.data.id}`}
228+
onCopy={handleCopyAttempt}
229+
>
230+
<div className="invokable-script__url">
231+
<Input
232+
type={InputType.Text}
233+
value={`${window.location.origin}/api/v2/scripts/${resource.data.id}`}
234+
/>
235+
<SquareButton
236+
testID="copy-to-clipboard--script"
237+
size={ComponentSize.Small}
238+
color={ComponentColor.Secondary}
239+
icon={IconFont.Duplicate_New}
240+
shape={ButtonShape.StretchToFit}
241+
titleText="Copy to clipboard"
242+
/>
243+
</div>
244+
</CopyToClipboard>
245+
</>
246+
)}
206247
{type === OverlayType.EDIT && (
207248
<Button
208249
icon={IconFont.Trash_New}
@@ -233,7 +274,7 @@ const SaveAsScript: FC<Props> = ({language, onClose, setOverlayType, type}) => {
233274
<Button
234275
color={ComponentColor.Primary}
235276
status={
236-
(resource?.data?.name?.length ?? 0) === 0
277+
(newName?.length ?? 0) === 0
237278
? ComponentStatus.Disabled
238279
: ComponentStatus.Default
239280
}

0 commit comments

Comments
 (0)