Skip to content

Commit 25a969a

Browse files
milesjMiles Johnson
authored andcommitted
update: Migrate some class components to function components. (#319)
* Convert things. * Fix some tests. * Fix tests. * Fix copy. * Fix tests.
1 parent 988ba64 commit 25a969a

File tree

28 files changed

+491
-685
lines changed

28 files changed

+491
-685
lines changed

configs/eslint.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ module.exports = {
99

1010
// Disabled until we migrate to hooks
1111
'react/no-did-update-set-state': 'off',
12-
'react/prefer-stateless-function': 'off',
1312
},
1413

1514
overrides: [
Lines changed: 39 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useState } from 'react';
22
import copy from 'copy-to-clipboard';
33
import IconCopy from '@airbnb/lunar-icons/lib/interface/IconCopy';
44
import T from '../Translate';
@@ -27,60 +27,57 @@ export type CopyState = {
2727
};
2828

2929
/** A component for easily copying a string of text to the clipboard. */
30-
export default class Copy extends React.Component<CopyProps, CopyState> {
31-
state = {
32-
copied: false,
33-
};
30+
export default function Copy({
31+
children,
32+
text,
33+
id,
34+
trackingName,
35+
underlined,
36+
prompt,
37+
onCopy,
38+
}: CopyProps) {
39+
const [copied, setCopied] = useState(false);
3440

35-
private handleClick = (event: React.MouseEvent) => {
36-
const { text, onCopy } = this.props;
41+
const handleClick = (event: React.MouseEvent) => {
3742
const result = copy(text);
3843

3944
event.preventDefault();
40-
41-
this.setState({
42-
copied: true,
43-
});
45+
setCopied(true);
4446

4547
if (onCopy) {
4648
onCopy(text, result);
4749
}
4850
};
4951

50-
private handleMouseLeave = () => {
52+
const handleMouseLeave = () => {
5153
window.setTimeout(() => {
52-
this.setState({
53-
copied: false,
54-
});
54+
setCopied(false);
5555
}, 500);
5656
};
5757

58-
render() {
59-
const { prompt, children, id, trackingName, underlined } = this.props;
60-
const element = children || (
61-
// eslint-disable-next-line jsx-a11y/anchor-is-valid
62-
<Link trackingName={trackingName} id={id}>
63-
<IconCopy decorative />
64-
</Link>
65-
);
58+
const element = children || (
59+
// eslint-disable-next-line jsx-a11y/anchor-is-valid
60+
<Link trackingName={trackingName} id={id}>
61+
<IconCopy decorative />
62+
</Link>
63+
);
6664

67-
return (
68-
<Tooltip
69-
remainOnMouseDown
70-
content={
71-
this.state.copied ? (
72-
<T k="lunar.copy.copied" phrase="Copied!" />
73-
) : (
74-
prompt || <T k="lunar.copy.copyToClipboard" phrase="Copy to clipboard" />
75-
)
76-
}
77-
underlined={underlined}
78-
>
79-
{React.cloneElement(element, {
80-
onClick: this.handleClick,
81-
onMouseLeave: this.handleMouseLeave,
82-
})}
83-
</Tooltip>
84-
);
85-
}
65+
return (
66+
<Tooltip
67+
remainOnMouseDown
68+
content={
69+
copied ? (
70+
<T k="lunar.copy.copied" phrase="Copied!" />
71+
) : (
72+
prompt || <T k="lunar.copy.copyToClipboard" phrase="Copy to clipboard" />
73+
)
74+
}
75+
underlined={underlined}
76+
>
77+
{React.cloneElement(element, {
78+
onClick: handleClick,
79+
onMouseLeave: handleMouseLeave,
80+
})}
81+
</Tooltip>
82+
);
8683
}

packages/core/src/components/DateTimeRange/index.tsx

Lines changed: 38 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -21,54 +21,50 @@ export type DateTimeRangeProps = {
2121
};
2222

2323
/** Display a range between 2 timestamps. */
24-
export default class DateTimeRange extends React.PureComponent<DateTimeRangeProps> {
25-
static defaultProps = {
26-
from: null,
27-
separator: ' – ',
28-
to: null,
29-
};
24+
export default function DateTimeRange({
25+
from,
26+
locale,
27+
separator = ' – ',
28+
timezone,
29+
to,
30+
}: DateTimeRangeProps) {
31+
if (!from || !to) {
32+
return <Empty />;
33+
}
3034

31-
render() {
32-
const { from, locale, separator, timezone, to } = this.props;
35+
const fromTimeStamp = createDateTime(from, { locale, timezone });
36+
const toTimeStamp = createDateTime(to, { locale, timezone });
3337

34-
if (!from || !to) {
35-
return <Empty />;
38+
if (__DEV__) {
39+
if (!fromTimeStamp.isValid || !toTimeStamp.isValid) {
40+
throw new Error('Invalid timestamps passed to `DateTimeRange`.');
3641
}
3742

38-
const fromTimeStamp = createDateTime(from, { locale, timezone });
39-
const toTimeStamp = createDateTime(to, { locale, timezone });
40-
41-
if (__DEV__) {
42-
if (!fromTimeStamp.isValid || !toTimeStamp.isValid) {
43-
throw new Error('Invalid timestamps passed to `DateTimeRange`.');
44-
}
45-
46-
if (toTimeStamp < fromTimeStamp) {
47-
throw new Error('Invalid chronological order of timestamps passed to `DateTimeRange`.');
48-
}
43+
if (toTimeStamp < fromTimeStamp) {
44+
throw new Error('Invalid chronological order of timestamps passed to `DateTimeRange`.');
4945
}
46+
}
5047

51-
const props = { locale, timezone };
52-
let fromFormat = rangeFromDayBundle.get(locale);
53-
let toFormat;
54-
55-
if (fromTimeStamp.year !== toTimeStamp.year) {
56-
fromFormat = dateMediumBundle.get(locale);
57-
toFormat = dateMediumBundle.get(locale);
58-
} else if (fromTimeStamp.month !== toTimeStamp.month) {
59-
toFormat = dateMediumBundle.get(locale);
60-
} else if (fromTimeStamp.day !== toTimeStamp.day) {
61-
toFormat = rangeToDayBundle.get(locale);
62-
} else {
63-
return <DateTime {...props} medium noTime noTimezone at={toTimeStamp} />;
64-
}
48+
const props = { locale, timezone };
49+
let fromFormat = rangeFromDayBundle.get(locale);
50+
let toFormat;
6551

66-
return (
67-
<span>
68-
<DateTime {...props} at={fromTimeStamp} format={fromFormat} />
69-
{separator}
70-
<DateTime {...props} at={toTimeStamp} format={toFormat} />
71-
</span>
72-
);
52+
if (fromTimeStamp.year !== toTimeStamp.year) {
53+
fromFormat = dateMediumBundle.get(locale);
54+
toFormat = dateMediumBundle.get(locale);
55+
} else if (fromTimeStamp.month !== toTimeStamp.month) {
56+
toFormat = dateMediumBundle.get(locale);
57+
} else if (fromTimeStamp.day !== toTimeStamp.day) {
58+
toFormat = rangeToDayBundle.get(locale);
59+
} else {
60+
return <DateTime {...props} medium noTime noTimezone at={toTimeStamp} />;
7361
}
62+
63+
return (
64+
<span>
65+
<DateTime {...props} at={fromTimeStamp} format={fromFormat} />
66+
{separator}
67+
<DateTime {...props} at={toTimeStamp} format={toFormat} />
68+
</span>
69+
);
7470
}

packages/core/src/components/ErrorMessage/index.tsx

Lines changed: 33 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -48,45 +48,41 @@ export type ErrorMessageProps = {
4848
};
4949

5050
/** Display an error message from an `Error` instance or API endpoint. */
51-
export default class ErrorMessage extends React.PureComponent<ErrorMessageProps> {
52-
static defaultProps = {
53-
inline: false,
54-
subtitle: null,
55-
title: null,
56-
};
57-
58-
render() {
59-
const { error, inline, title, subtitle, onClose } = this.props;
60-
61-
if (!error) {
62-
return null;
63-
}
51+
export default function ErrorMessage({
52+
error,
53+
inline,
54+
title,
55+
subtitle,
56+
onClose,
57+
}: ErrorMessageProps) {
58+
if (!error) {
59+
return null;
60+
}
6461

65-
const message = subtitle || getErrorMessage(error);
66-
const code = error instanceof Error ? null : error.error_code;
67-
const id = error instanceof Error ? null : error.error_id;
68-
const url = error instanceof Error ? '' : error.error_url;
62+
const message = subtitle || getErrorMessage(error);
63+
const code = error instanceof Error ? null : error.error_code;
64+
const id = error instanceof Error ? null : error.error_id;
65+
const url = error instanceof Error ? '' : error.error_url;
6966

70-
if (inline) {
71-
return <StatusText danger>{message}</StatusText>;
72-
}
67+
if (inline) {
68+
return <StatusText danger>{message}</StatusText>;
69+
}
7370

74-
return (
75-
<Alert
76-
danger
77-
title={title || code || <T k="lunar.error.unknown" phrase="Unknown error" />}
78-
onClose={onClose}
79-
>
80-
{message}
71+
return (
72+
<Alert
73+
danger
74+
title={title || code || <T k="lunar.error.unknown" phrase="Unknown error" />}
75+
onClose={onClose}
76+
>
77+
{message}
8178

82-
{id && (
83-
<Spacing top={1}>
84-
<MutedButton inverted onClick={createRedirectURL(id, url)}>
85-
<T k="lunar.error.viewDetails" phrase="View error details" />
86-
</MutedButton>
87-
</Spacing>
88-
)}
89-
</Alert>
90-
);
91-
}
79+
{id && (
80+
<Spacing top={1}>
81+
<MutedButton inverted onClick={createRedirectURL(id, url)}>
82+
<T k="lunar.error.viewDetails" phrase="View error details" />
83+
</MutedButton>
84+
</Spacing>
85+
)}
86+
</Alert>
87+
);
9288
}

packages/core/src/components/FormActions/index.tsx

Lines changed: 42 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -35,71 +35,49 @@ export type FormActionsProps = {
3535
};
3636

3737
/** A pair of action buttons to display at the bottom of a form. */
38-
export default class FormActions extends React.PureComponent<FormActionsProps> {
39-
static defaultProps = {
40-
cancelText: null,
41-
continueText: null,
42-
danger: false,
43-
disabled: false,
44-
hideCancel: false,
45-
processing: false,
46-
processingText: null,
47-
resetText: null,
48-
showReset: false,
49-
small: false,
50-
};
38+
export default function FormActions({
39+
block,
40+
cancelText,
41+
continueText,
42+
danger,
43+
disabled,
44+
hideCancel,
45+
onCancel,
46+
onContinue,
47+
processing,
48+
processingText,
49+
resetText,
50+
showReset,
51+
small,
52+
}: FormActionsProps) {
53+
const Button = danger ? DangerButton : NormalButton;
5154

52-
render() {
53-
const {
54-
block,
55-
cancelText,
56-
continueText,
57-
danger,
58-
disabled,
59-
hideCancel,
60-
onCancel,
61-
onContinue,
62-
processing,
63-
processingText,
64-
resetText,
65-
showReset,
66-
small,
67-
} = this.props;
68-
const Button = danger ? DangerButton : NormalButton;
55+
return (
56+
<ButtonGroup stacked={block}>
57+
<Button
58+
type="submit"
59+
block={block}
60+
disabled={disabled}
61+
loading={processing}
62+
small={small}
63+
onClick={onContinue}
64+
>
65+
{processing
66+
? processingText || <T k="lunar.common.saving" phrase="Saving" />
67+
: continueText || <T k="lunar.common.save" phrase="Save" />}
68+
</Button>
6969

70-
return (
71-
<ButtonGroup stacked={block}>
72-
<Button
73-
type="submit"
74-
block={block}
75-
disabled={disabled}
76-
loading={processing}
77-
small={small}
78-
onClick={onContinue}
79-
>
80-
{processing
81-
? processingText || <T k="lunar.common.saving" phrase="Saving" />
82-
: continueText || <T k="lunar.common.save" phrase="Save" />}
83-
</Button>
70+
{!hideCancel && (
71+
<MutedButton inverted block={block} small={small} disabled={processing} onClick={onCancel}>
72+
{cancelText || <T k="lunar.common.cancel" phrase="Cancel" />}
73+
</MutedButton>
74+
)}
8475

85-
{!hideCancel && (
86-
<MutedButton
87-
inverted
88-
block={block}
89-
small={small}
90-
disabled={processing}
91-
onClick={onCancel}
92-
>
93-
{cancelText || <T k="lunar.common.cancel" phrase="Cancel" />}
94-
</MutedButton>
95-
)}
96-
97-
{showReset && (
98-
<MutedButton inverted block={block} type="reset" small={small} disabled={processing}>
99-
{resetText || <T k="lunar.common.reset" phrase="Reset" />}
100-
</MutedButton>
101-
)}
102-
</ButtonGroup>
103-
);
104-
}
76+
{showReset && (
77+
<MutedButton inverted block={block} type="reset" small={small} disabled={processing}>
78+
{resetText || <T k="lunar.common.reset" phrase="Reset" />}
79+
</MutedButton>
80+
)}
81+
</ButtonGroup>
82+
);
10583
}

0 commit comments

Comments
 (0)