-
Notifications
You must be signed in to change notification settings - Fork 100
Expand file tree
/
Copy pathindex.tsx
More file actions
133 lines (123 loc) 路 3.94 KB
/
Copy pathindex.tsx
File metadata and controls
133 lines (123 loc) 路 3.94 KB
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import React, {
forwardRef, ForwardedRef, ElementType, ReactNode,
} from 'react';
import classNames from 'classnames';
import {
type BsPrefixRefForwardingComponent as ComponentWithAsProp,
type BsPrefixProps,
} from 'react-bootstrap/esm/helpers';
import { defineMessages, useIntl } from 'react-intl';
import { Launch } from '../../icons';
import Icon from '../Icon';
export interface HyperlinkProps extends BsPrefixProps, Omit<React.ComponentPropsWithRef<'a'>, 'href' | 'target'> {
/** specifies the component element type to render for the hyperlink. */
as?: ElementType;
/** specifies the URL; required if `as` prop is a standard anchor tag */
destination?: string;
/** Content of the hyperlink */
children: ReactNode;
/** Custom class names for the hyperlink */
className?: string;
/** Alt text for the icon indicating that this link opens in a new tab, if target="_blank". e.g. _("in a new tab") */
externalLinkAlternativeText?: string;
/** Tooltip text for the "opens in new tab" icon, if target="_blank". e.g. _("Opens in a new tab"). */
externalLinkTitle?: string;
/** type of hyperlink */
variant?: 'default' | 'muted' | 'brand';
/** Display the link with an underline. By default, it is only underlined on hover. */
isInline?: boolean;
/** specify if we need to show launch Icon. By default, it will be visible. */
showLaunchIcon?: boolean;
/** 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?: '_blank' | '_self';
}
export type HyperlinkType = ComponentWithAsProp<'a', HyperlinkProps>;
const messages = defineMessages({
externalLinkAltText: {
id: 'Hyperlink.externalLinkAltText',
defaultMessage: 'in a new tab',
},
externalLinkTitle: {
id: 'Hyperlink.externalLinkTitle',
defaultMessage: 'Opens in a new tab',
},
});
const Hyperlink = forwardRef(({
as: Component = 'a',
className,
destination,
children,
target = '_self',
onClick,
externalLinkAlternativeText,
externalLinkTitle,
variant = 'default',
isInline = false,
showLaunchIcon = true,
...attrs
}: HyperlinkProps, ref: ForwardedRef<HTMLAnchorElement>) => {
const intl = useIntl();
let externalLinkIcon;
if (target === '_blank') {
const generateRel = () => {
let { rel } = attrs;
if (!rel) {
return 'noopener noreferrer';
}
if (!rel.includes('noopener')) {
rel += ' noopener';
}
if (!rel.includes('noreferrer')) {
rel += ' noreferrer';
}
return rel;
};
// Add this rel attribute to prevent Reverse Tabnabbing
attrs.rel = generateRel();
if (showLaunchIcon) {
externalLinkIcon = (
<span
className="pgn__hyperlink__external-icon"
title={externalLinkTitle || intl.formatMessage(messages.externalLinkTitle)}
>
<Icon
src={Launch}
screenReaderText={externalLinkAlternativeText || intl.formatMessage(messages.externalLinkAltText)}
style={{ height: '1em', width: '1em' }}
data-testid="hyperlink-icon"
/>
</span>
);
}
}
const additionalProps: Record<string, any> = { ...attrs };
if (destination) {
additionalProps.href = destination;
}
return (
<Component
ref={ref}
className={classNames(
'pgn__hyperlink',
`${variant}-link`,
{
'standalone-link': !isInline,
'inline-link': isInline,
},
className,
)}
target={target}
onClick={onClick}
{...additionalProps}
>
{children}
{externalLinkIcon}
</Component>
);
});
Hyperlink.displayName = 'Hyperlink';
export default Hyperlink;