Skip to content

Commit 8124db0

Browse files
committed
ENH Convert to functional components
1 parent 7b01f05 commit 8124db0

File tree

20 files changed

+71401
-2282
lines changed

20 files changed

+71401
-2282
lines changed

client/dist/js/bundle-cms.js

+4,481-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/dist/js/bundle.js

+64,272-1
Large diffs are not rendered by default.

client/dist/js/injector.js

+20-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/dist/styles/bundle-cms.css

+408-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/dist/styles/bundle.css

+415-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/src/components/BackupCodes/Register.js

+76-139
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* global window */
22

3-
import React, { Component } from 'react';
3+
import React, { useState, useRef } from 'react';
44
import PropTypes from 'prop-types';
55
import Printd from 'printd';
66
import { CopyToClipboard } from 'react-copy-to-clipboard';
@@ -10,148 +10,105 @@ import { formatCode } from 'lib/formatCode';
1010
* This component provides the user interface for registering backup codes with a user. This process
1111
* only involves showing the user the backup codes. User input is not required to set up the codes.
1212
*/
13-
class Register extends Component {
14-
constructor(props) {
15-
super(props);
16-
17-
this.state = {
18-
recentlyCopied: false
19-
};
20-
21-
// Prepare a ref (in a React 15 compatible way) to use for the DOM node that will be printed
22-
this.printRef = null;
23-
this.setPrintRef = element => {
24-
this.printRef = element;
25-
};
26-
// Prepare a class member to store a timeout ref that provides feedback on copy to clipboard
27-
this.copyMessageTimeout = null;
28-
29-
this.handlePrint = this.handlePrint.bind(this);
30-
this.handleCopy = this.handleCopy.bind(this);
31-
}
13+
function Register(props) {
14+
const {
15+
codes,
16+
method,
17+
onCompleteRegistration,
18+
copyFeedbackDuration,
19+
} = props;
20+
const [recentlyCopied, setRecentlyCopied] = useState(false);
21+
const copyMessageTimeout = useRef(null);
22+
const printRef = useRef(null);
23+
const i18n = window.ss.i18n;
3224

3325
/**
3426
* Get codes from component properties and format them with spaces every 3 (or 4) characters.
3527
* The number of characters in each group will never be less than 3 - the groups towards the end
3628
* will have four characters instead.
37-
*
38-
* @return {string[]}
3929
*/
40-
getFormattedCodes() {
41-
const { codes } = this.props;
42-
30+
function getFormattedCodes() {
4331
return codes.map(code => formatCode(code));
4432
}
4533

4634
/**
4735
* Handle an event triggered requesting the backup codes to be printed
48-
*
49-
* @param {Event} event
5036
*/
51-
handlePrint(event) {
37+
function handlePrint(event) {
5238
event.preventDefault();
53-
5439
(new Printd()).print(
55-
this.printRef,
40+
printRef,
5641
['body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif }']
5742
);
5843
}
5944

6045
/**
6146
* Handle an event triggered requesting the backup codes to be copied to clipboard
62-
*
63-
* @param {Event} event
6447
*/
65-
handleCopy(event) {
48+
function handleCopy(event) {
6649
event.preventDefault();
67-
const { copyFeedbackDuration } = this.props;
68-
69-
this.setState({
70-
recentlyCopied: true,
71-
});
72-
50+
setRecentlyCopied(true);
7351
// Clear an existing timeout to reset the text on the copy link
74-
if (this.copyMessageTimeout) {
75-
clearTimeout(this.copyMessageTimeout);
52+
if (copyMessageTimeout.current) {
53+
clearTimeout(copyMessageTimeout.current);
7654
}
77-
7855
// And set that timeout too
79-
this.copyMessageTimeout = setTimeout(() => {
80-
this.setState({
81-
recentlyCopied: false,
82-
});
56+
copyMessageTimeout.current = setTimeout(() => {
57+
setRecentlyCopied(false);
8358
}, copyFeedbackDuration);
8459
}
8560

8661
/**
8762
* Render a grid of formatted backup codes
88-
*
89-
* @return {HTMLElement}
9063
*/
91-
renderCodes() {
64+
function renderCodes() {
9265
return (
93-
<pre ref={this.setPrintRef} className="mfa-register-backup-codes__code-grid">
94-
{this.getFormattedCodes().map(code => <div key={code}>{code}</div>)}
66+
<pre ref={printRef} className="mfa-register-backup-codes__code-grid">
67+
{getFormattedCodes().map(code => <div key={code}>{code}</div>)}
9568
</pre>
9669
);
9770
}
9871

9972
/**
10073
* Render the description for registering in with this method
101-
*
102-
* @return {HTMLElement}
10374
*/
104-
renderDescription() {
105-
const { ss: { i18n } } = window;
106-
const { method: { supportLink, supportText } } = this.props;
107-
108-
return (
109-
<p>
110-
{i18n._t(
111-
'MFABackupCodesRegister.DESCRIPTION',
112-
'Recovery codes enable you to log into your account in the event your primary ' +
113-
'authentication is not available. Each code can only be used once. Store these codes ' +
114-
'somewhere safe, as they will not be viewable after leaving this page.'
115-
)}
116-
&nbsp;
117-
{supportLink &&
118-
<a
119-
href={supportLink}
120-
target="_blank"
121-
rel="noopener noreferrer"
122-
>
123-
{supportText || i18n._t('MFARegister.RECOVERY_HELP', 'Learn more about recovery codes.')}
124-
</a>
125-
}
126-
</p>
127-
);
75+
function renderDescription() {
76+
const { supportLink, supportText } = method;
77+
return <p>
78+
{i18n._t(
79+
'MFABackupCodesRegister.DESCRIPTION',
80+
'Recovery codes enable you to log into your account in the event your primary ' +
81+
'authentication is not available. Each code can only be used once. Store these codes ' +
82+
'somewhere safe, as they will not be viewable after leaving this page.'
83+
)}
84+
&nbsp;
85+
{supportLink &&
86+
<a
87+
href={supportLink}
88+
target="_blank"
89+
rel="noopener noreferrer"
90+
>
91+
{supportText || i18n._t('MFARegister.RECOVERY_HELP', 'Learn more about recovery codes.')}
92+
</a>
93+
}
94+
</p>;
12895
}
12996

13097
/**
13198
* Render the "print" action. A link allowing the user to trigger a print dialog for the codes
132-
*
133-
* @return {HTMLElement}
13499
*/
135-
renderPrintAction() {
136-
const { ss: { i18n } } = window;
137-
138-
return (
139-
<button type="button" onClick={this.handlePrint} className="btn btn-link">
140-
{i18n._t('MFABackupCodesRegister.PRINT', 'Print codes')}
141-
</button>
142-
);
100+
function renderPrintAction() {
101+
return <button type="button" onClick={handlePrint} className="btn btn-link">
102+
{i18n._t('MFABackupCodesRegister.PRINT', 'Print codes')}
103+
</button>;
143104
}
144105

145106
/**
146107
* Render the "download" action. A link allowing the user to trigger a download of a text file
147108
* containing the codes
148-
*
149-
* @return {HTMLElement}
150109
*/
151-
renderDownloadAction() {
152-
const { codes, method } = this.props;
153-
const { Blob, URL, ss: { i18n }, navigator } = window;
154-
110+
function renderDownloadAction() {
111+
const { Blob, URL, navigator } = window;
155112
const filename = `${method.name}.txt`;
156113
const codesText = codes.join('\r\n');
157114
const codesBlob = new Blob([codesText], { type: 'text/plain;charset=UTF-8' });
@@ -162,62 +119,42 @@ class Register extends Component {
162119
navigator.msSaveBlob(codesBlob, filename);
163120
}
164121
};
165-
166-
return (
167-
<a download={filename} href={codesURL} className="btn btn-link" onClick={supportInternetExplorer}>
168-
{i18n._t('MFABackupCodesRegister.DOWNLOAD', 'Download')}
169-
</a>
170-
);
122+
return <a download={filename} href={codesURL} className="btn btn-link" onClick={supportInternetExplorer}>
123+
{i18n._t('MFABackupCodesRegister.DOWNLOAD', 'Download')}
124+
</a>;
171125
}
172126

173127
/**
174128
* Render the "copy" action. A link allowing the user to easily copy the backup codes to clipboard
175-
*
176-
* @return {CopyToClipboard}
177129
*/
178-
renderCopyAction() {
179-
const { codes } = this.props;
180-
const { recentlyCopied } = this.state;
181-
const { ss: { i18n } } = window;
182-
130+
function renderCopyAction() {
183131
const label = recentlyCopied
184132
? i18n._t('MFABackupCodesRegister.COPY_RECENT', 'Copied!')
185133
: i18n._t('MFABackupCodesRegister.COPY', 'Copy codes');
186-
187-
return (
188-
<CopyToClipboard text={codes.join('\n')}>
189-
<button
190-
type="button"
191-
className="mfa-register-backup-codes__copy-to-clipboard btn btn-link"
192-
onClick={this.handleCopy}
193-
>
194-
{label}
195-
</button>
196-
</CopyToClipboard>
197-
);
134+
return <CopyToClipboard text={codes.join('\n')}>
135+
<button
136+
type="button"
137+
className="mfa-register-backup-codes__copy-to-clipboard btn btn-link"
138+
onClick={() => handleCopy()}
139+
>
140+
{label}
141+
</button>
142+
</CopyToClipboard>;
198143
}
199144

200-
render() {
201-
const { onCompleteRegistration } = this.props;
202-
const { ss: { i18n } } = window;
203-
204-
return (
205-
<div className="mfa-register-backup-codes__container">
206-
{this.renderDescription()}
207-
{this.renderCodes()}
208-
209-
<div className="mfa-register-backup-codes__helper-links">
210-
{this.renderPrintAction()}
211-
{this.renderDownloadAction()}
212-
{this.renderCopyAction()}
213-
</div>
214-
215-
<button className="btn btn-primary" onClick={() => onCompleteRegistration()}>
216-
{i18n._t('MFABackupCodesRegister.FINISH', 'Finish')}
217-
</button>
218-
</div>
219-
);
220-
}
145+
// Render component
146+
return <div className="mfa-register-backup-codes__container">
147+
{renderDescription()}
148+
{renderCodes()}
149+
<div className="mfa-register-backup-codes__helper-links">
150+
{renderPrintAction()}
151+
{renderDownloadAction()}
152+
{renderCopyAction()}
153+
</div>
154+
<button className="btn btn-primary" onClick={() => onCompleteRegistration()}>
155+
{i18n._t('MFABackupCodesRegister.FINISH', 'Finish')}
156+
</button>
157+
</div>;
221158
}
222159

223160
Register.propTypes = {

0 commit comments

Comments
 (0)