Skip to content

Commit 7e069ee

Browse files
authoredJan 8, 2024
Merge pull request #2360 from lindapaiste/refactor/copyable-input
Convert `CopyableInput` to a function component
2 parents d961e49 + fcc3f58 commit 7e069ee

File tree

1 file changed

+67
-71
lines changed

1 file changed

+67
-71
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,91 @@
11
import PropTypes from 'prop-types';
2-
import React from 'react';
2+
import React, { useEffect, useRef, useState } from 'react';
33
import Clipboard from 'clipboard';
44
import classNames from 'classnames';
5-
import { withTranslation } from 'react-i18next';
5+
import { useTranslation } from 'react-i18next';
66

77
import ShareIcon from '../../../images/share.svg';
88

9-
class CopyableInput extends React.Component {
10-
constructor(props) {
11-
super(props);
12-
this.onMouseLeaveHandler = this.onMouseLeaveHandler.bind(this);
13-
}
9+
const CopyableInput = ({ label, value, hasPreviewLink }) => {
10+
const { t } = useTranslation();
1411

15-
componentDidMount() {
16-
this.clipboard = new Clipboard(this.input, {
17-
target: () => this.input
18-
});
12+
const [isCopied, setIsCopied] = useState(false);
1913

20-
this.clipboard.on('success', (e) => {
21-
this.tooltip.classList.add('tooltipped');
22-
this.tooltip.classList.add('tooltipped-n');
23-
});
24-
}
14+
const inputRef = useRef(null);
2515

26-
componentWillUnmount() {
27-
this.clipboard.destroy();
28-
}
16+
useEffect(() => {
17+
const input = inputRef.current;
2918

30-
onMouseLeaveHandler() {
31-
this.tooltip.classList.remove('tooltipped');
32-
this.tooltip.classList.remove('tooltipped-n');
33-
}
19+
if (!input) return; // should never happen
3420

35-
render() {
36-
const { label, value, hasPreviewLink } = this.props;
37-
const copyableInputClass = classNames({
38-
'copyable-input': true,
39-
'copyable-input--with-preview': hasPreviewLink
21+
const clipboard = new Clipboard(input, {
22+
target: () => input
4023
});
41-
return (
42-
<div className={copyableInputClass}>
43-
<div
44-
className="copyable-input__value-container tooltipped-no-delay"
45-
aria-label={this.props.t('CopyableInput.CopiedARIA')}
46-
ref={(element) => {
47-
this.tooltip = element;
48-
}}
49-
onMouseLeave={this.onMouseLeaveHandler}
50-
>
51-
<label
52-
className="copyable-input__label"
53-
htmlFor={`copyable-input__value-${label}`}
54-
>
55-
<div className="copyable-input__label-container">{label}</div>
56-
<input
57-
type="text"
58-
className="copyable-input__value"
59-
id={`copyable-input__value-${label}`}
60-
value={value}
61-
ref={(element) => {
62-
this.input = element;
63-
}}
64-
readOnly
65-
/>
66-
</label>
67-
</div>
68-
{hasPreviewLink && (
69-
<a
70-
target="_blank"
71-
rel="noopener noreferrer"
72-
href={value}
73-
className="copyable-input__preview"
74-
aria-label={this.props.t('CopyableInput.CopiedARIA', { label })}
75-
>
76-
<ShareIcon focusable="false" aria-hidden="true" />
77-
</a>
24+
25+
clipboard.on('success', () => {
26+
setIsCopied(true);
27+
});
28+
29+
// eslint-disable-next-line consistent-return
30+
return () => {
31+
clipboard.destroy();
32+
};
33+
}, [inputRef, setIsCopied]);
34+
35+
return (
36+
<div
37+
className={classNames(
38+
'copyable-input',
39+
hasPreviewLink && 'copyable-input--with-preview'
40+
)}
41+
>
42+
<div
43+
className={classNames(
44+
'copyable-input__value-container',
45+
'tooltipped-no-delay',
46+
isCopied && 'tooltipped tooltipped-n'
7847
)}
48+
aria-label={t('CopyableInput.CopiedARIA')}
49+
onMouseLeave={() => setIsCopied(false)}
50+
>
51+
<label
52+
className="copyable-input__label"
53+
htmlFor={`copyable-input__value-${label}`}
54+
>
55+
<div className="copyable-input__label-container">{label}</div>
56+
<input
57+
type="text"
58+
className="copyable-input__value"
59+
id={`copyable-input__value-${label}`}
60+
value={value}
61+
ref={inputRef}
62+
readOnly
63+
/>
64+
</label>
7965
</div>
80-
);
81-
}
82-
}
66+
{hasPreviewLink && (
67+
<a
68+
target="_blank"
69+
rel="noopener noreferrer"
70+
href={value}
71+
className="copyable-input__preview"
72+
aria-label={t('CopyableInput.CopiedARIA', { label })}
73+
>
74+
<ShareIcon focusable="false" aria-hidden="true" />
75+
</a>
76+
)}
77+
</div>
78+
);
79+
};
8380

8481
CopyableInput.propTypes = {
8582
label: PropTypes.string.isRequired,
8683
value: PropTypes.string.isRequired,
87-
hasPreviewLink: PropTypes.bool,
88-
t: PropTypes.func.isRequired
84+
hasPreviewLink: PropTypes.bool
8985
};
9086

9187
CopyableInput.defaultProps = {
9288
hasPreviewLink: false
9389
};
9490

95-
export default withTranslation()(CopyableInput);
91+
export default CopyableInput;

0 commit comments

Comments
 (0)
Please sign in to comment.