/
index.jsx
97 lines (88 loc) 路 2.92 KB
/
index.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import isRequiredIf from 'react-proptype-conditional-require';
import Icon from '../Icon';
import { Launch } from '../../icons';
import withDeprecatedProps, { DEPR_TYPES } from '../withDeprecatedProps';
function Hyperlink(props) {
const {
destination,
children,
target,
onClick,
externalLinkAlternativeText,
externalLinkTitle,
variant,
isInline,
...other
} = props;
let externalLinkIcon;
if (target === '_blank') {
// Add this rel attribute to prevent Reverse Tabnabbing
other.rel = other.rel ? `noopener ${other.rel}` : 'noopener';
externalLinkIcon = (
// Space between content and icon
<span className="d-inline-block align-text-top">{' '}
<Icon src={Launch} style={{ height: '1em', width: '1em' }} />
</span>
);
}
return (
<a
className={classNames(
`${variant}-link`,
{
'standalone-link': !isInline,
'inline-link': isInline,
},
)}
href={destination}
target={target}
onClick={onClick}
{...other}
>{children}{externalLinkIcon}
</a>
);
}
Hyperlink.defaultProps = {
target: '_self',
onClick: () => {},
externalLinkAlternativeText: 'Opens in a new window',
externalLinkTitle: 'Opens in a new window',
variant: 'default',
isInline: false,
};
Hyperlink.propTypes = {
/** specifies the URL */
destination: PropTypes.string.isRequired,
children: PropTypes.node.isRequired,
/** specifies where the link should open. The default behavior is `_self`, which means that the URL will be loaded into the same browsing context as the current one. If the target is `_blank` (opening a new window) `rel='noopener'` will be added to the anchor tag to prevent any potential [reverse tabnabbing attack](https://www.owasp.org/index.php/Reverse_Tabnabbing).
*/
target: PropTypes.string,
/** specifies the callback function when the link is clicked */
onClick: PropTypes.func,
// eslint-disable-next-line max-len
/** specifies the text for links with a `_blank` target (which loads the URL in a new browsing context). */
externalLinkAlternativeText: isRequiredIf(
PropTypes.string,
props => props.target === '_blank',
),
// eslint-disable-next-line max-len
/** specifies the title for links with a `_blank` target (which loads the URL in a new browsing context). */
externalLinkTitle: isRequiredIf(
PropTypes.string,
props => props.target === '_blank',
),
/** type of hyperlink */
variant: PropTypes.oneOf(['default', 'muted', 'brand']),
/** specify the link style. By default it will be underlined. */
isInline: PropTypes.bool,
};
export default withDeprecatedProps(Hyperlink, 'Hyperlink', {
/** specifies the text or element that a URL should be associated with */
content: {
deprType: DEPR_TYPES.MOVED,
newName: 'children',
},
});