Skip to content

Commit 49d2c2f

Browse files
aldrinabastillasaabastillas-rcatim-lai
authored andcommitted
feat: Copy response to clipboard swagger-api#4300 (swagger-api#5278)
* Move next to download button and match styling Co-authored-by: Aldrin Abastillas <[email protected]> Co-authored-by: Tim Lai <[email protected]>
1 parent f2bdea9 commit 49d2c2f

File tree

9 files changed

+295
-164
lines changed

9 files changed

+295
-164
lines changed

package-lock.json

Lines changed: 178 additions & 156 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
"prop-types": "^15.7.2",
7070
"randombytes": "^2.1.0",
7171
"react": "^15.6.2",
72+
"react-copy-to-clipboard": "5.0.1",
7273
"react-debounce-input": "^3.2.0",
7374
"react-dom": "^15.6.2",
7475
"react-immutable-proptypes": "2.1.0",

src/core/components/highlight-code.jsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ import React, { Component } from "react"
22
import PropTypes from "prop-types"
33
import { highlight } from "core/utils"
44
import saveAs from "js-file-download"
5+
import { CopyToClipboard } from "react-copy-to-clipboard"
56

67
export default class HighlightCode extends Component {
78
static propTypes = {
89
value: PropTypes.string.isRequired,
910
className: PropTypes.string,
1011
downloadable: PropTypes.bool,
11-
fileName: PropTypes.string
12+
fileName: PropTypes.string,
13+
canCopy: PropTypes.bool
1214
}
1315

1416
componentDidMount() {
@@ -47,7 +49,7 @@ export default class HighlightCode extends Component {
4749
}
4850

4951
render () {
50-
let { value, className, downloadable } = this.props
52+
let { value, className, downloadable, canCopy } = this.props
5153
className = className || ""
5254

5355
return (
@@ -57,6 +59,13 @@ export default class HighlightCode extends Component {
5759
Download
5860
</div>
5961
}
62+
63+
{ !canCopy ? null :
64+
<div className="copy-to-clipboard">
65+
<CopyToClipboard text={value}><button/></CopyToClipboard>
66+
</div>
67+
}
68+
6069
<pre
6170
ref={this.initializeComponent}
6271
onWheel={this.preventYScrollingBeyondElement}

src/core/components/response-body.jsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,19 +99,19 @@ export default class ResponseBody extends React.PureComponent {
9999
body = "can't parse JSON. Raw result:\n\n" + content
100100
}
101101

102-
bodyEl = <HighlightCode downloadable fileName={`${downloadName}.json`} value={ body } />
102+
bodyEl = <HighlightCode downloadable fileName={`${downloadName}.json`} value={ body } canCopy />
103103

104104
// XML
105105
} else if (/xml/i.test(contentType)) {
106106
body = formatXml(content, {
107107
textNodesOnSameLine: true,
108108
indentor: " "
109109
})
110-
bodyEl = <HighlightCode downloadable fileName={`${downloadName}.xml`} value={ body } />
110+
bodyEl = <HighlightCode downloadable fileName={`${downloadName}.xml`} value={ body } canCopy />
111111

112112
// HTML or Plain Text
113113
} else if (toLower(contentType) === "text/html" || /text\/plain/.test(contentType)) {
114-
bodyEl = <HighlightCode downloadable fileName={`${downloadName}.html`} value={ content } />
114+
bodyEl = <HighlightCode downloadable fileName={`${downloadName}.html`} value={ content } canCopy />
115115

116116
// Image
117117
} else if (/^image\//i.test(contentType)) {
@@ -125,7 +125,7 @@ export default class ResponseBody extends React.PureComponent {
125125
} else if (/^audio\//i.test(contentType)) {
126126
bodyEl = <pre className="microlight"><audio controls><source src={ url } type={ contentType } /></audio></pre>
127127
} else if (typeof content === "string") {
128-
bodyEl = <HighlightCode downloadable fileName={`${downloadName}.txt`} value={ content } />
128+
bodyEl = <HighlightCode downloadable fileName={`${downloadName}.txt`} value={ content } canCopy />
129129
} else if ( content.size > 0 ) {
130130
// We don't know the contentType, but there was some content returned
131131
if(parsedContent) {
@@ -135,7 +135,7 @@ export default class ResponseBody extends React.PureComponent {
135135
<p className="i">
136136
Unrecognized response type; displaying content as text.
137137
</p>
138-
<HighlightCode downloadable fileName={`${downloadName}.txt`} value={ parsedContent } />
138+
<HighlightCode downloadable fileName={`${downloadName}.txt`} value={ parsedContent } canCopy />
139139
</div>
140140

141141
} else {

src/img/clipboard.svg

Lines changed: 1 addition & 0 deletions
Loading

src/style/_buttons.scss

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,23 @@ button
154154
@include invalidFormElement();
155155
}
156156
}
157+
158+
.copy-to-clipboard
159+
{
160+
position: absolute;
161+
bottom: 10px;
162+
right: 100px;
163+
width: 30px;
164+
height: 30px;
165+
background: #7d8293;
166+
border-radius: 4px;
167+
border: none;
168+
169+
button
170+
{
171+
padding-left: 25px;
172+
border: none;
173+
height: 25px;
174+
background: url("../img/clipboard.svg") center center no-repeat;
175+
}
176+
}

test/components/highlight-code.jsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import React from "react"
2+
import expect from "expect"
3+
import { shallow } from "enzyme"
4+
import HighlightCode from "components/highlight-code"
5+
6+
describe("<HighlightCode />", () => {
7+
it("should render a Download button if downloadable", () => {
8+
const props = {downloadable: true}
9+
const wrapper = shallow(<HighlightCode {...props} />)
10+
expect(wrapper.find(".download-contents").length).toEqual(1)
11+
})
12+
13+
it("should render a Copy To Clipboard button if copyable", () => {
14+
const props = {canCopy: true}
15+
const wrapper = shallow(<HighlightCode {...props} />)
16+
expect(wrapper.find("CopyToClipboard").length).toEqual(1)
17+
})
18+
19+
it("should render values in a preformatted element", () => {
20+
const value = "test text"
21+
const props = {value: value}
22+
const wrapper = shallow(<HighlightCode {...props} />)
23+
const preTag = wrapper.find("pre")
24+
25+
expect(preTag.length).toEqual(1)
26+
expect(preTag.contains(value)).toEqual(true)
27+
})
28+
})

test/components/response-body.jsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import React from "react"
2+
import expect from "expect"
3+
import { shallow } from "enzyme"
4+
import ResponseBody from "components/response-body"
5+
6+
describe("<ResponseBody />", function() {
7+
const highlightCodeComponent = () => null
8+
const components = {
9+
highlightCode: highlightCodeComponent
10+
}
11+
const props = {
12+
getComponent: c => components[c],
13+
}
14+
15+
it("renders ResponseBody as 'application/json'", function() {
16+
props.contentType = "application/json"
17+
props.content = "{\"key\": \"a test value\"}"
18+
const wrapper = shallow(<ResponseBody {...props}/>)
19+
expect(wrapper.find("highlightCodeComponent").length).toEqual(1)
20+
})
21+
22+
it("renders ResponseBody as 'text/html'", function() {
23+
props.contentType = "application/json"
24+
props.content = "<b>Result</b>"
25+
const wrapper = shallow(<ResponseBody {...props}/>)
26+
expect(wrapper.find("highlightCodeComponent").length).toEqual(1)
27+
})
28+
29+
it("renders ResponseBody as 'image/svg'", function() {
30+
props.contentType = "image/svg"
31+
const wrapper = shallow(<ResponseBody {...props}/>)
32+
console.warn(wrapper.debug())
33+
expect(wrapper.find("highlightCodeComponent").length).toEqual(0)
34+
})
35+
36+
it("should render a copyable highlightCodeComponent for text types", function() {
37+
props.contentType = "text/plain"
38+
props.content = "test text"
39+
const wrapper = shallow(<ResponseBody {...props}/>)
40+
console.warn(wrapper.debug())
41+
expect(wrapper.find("highlightCodeComponent[canCopy]").length).toEqual(1)
42+
})
43+
})

test/mocha/components/response-body.jsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React from "react"
22
import expect from "expect"
33
import { shallow } from "enzyme"
44
import ResponseBody from "components/response-body"
5-
import { inferSchema } from "corePlugins/samples/fn"
65

76
describe("<ResponseBody />", function() {
87
const highlightCodeComponent = () => null
@@ -33,4 +32,12 @@ describe("<ResponseBody />", function() {
3332
console.warn(wrapper.debug())
3433
expect(wrapper.find("highlightCodeComponent").length).toEqual(0)
3534
})
35+
36+
it("should render a copyable highlightCodeComponent for text types", function() {
37+
props.contentType = "text/plain"
38+
props.content = "test text"
39+
const wrapper = shallow(<ResponseBody {...props}/>)
40+
console.warn(wrapper.debug())
41+
expect(wrapper.find("highlightCodeComponent[canCopy]").length).toEqual(1)
42+
})
3643
})

0 commit comments

Comments
 (0)