@openedx/paragon 22.4.0 → 22.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Hyperlink/index.d.ts +24 -0
- package/dist/Hyperlink/index.js +20 -32
- package/dist/Hyperlink/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/setupTest.d.ts +2 -0
- package/dist/setupTest.js.map +1 -0
- package/package.json +3 -3
- package/src/Hyperlink/{Hyperlink.test.jsx → Hyperlink.test.tsx} +21 -10
- package/src/Hyperlink/{index.jsx → index.tsx} +41 -37
- package/src/index.d.ts +1 -1
- package/src/index.js +1 -1
- package/src/{setupTest.js → setupTest.ts} +3 -2
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export declare const HYPER_LINK_EXTERNAL_LINK_ALT_TEXT = "in a new tab";
|
|
3
|
+
export declare const HYPER_LINK_EXTERNAL_LINK_TITLE = "Opens in a new tab";
|
|
4
|
+
interface Props extends Omit<React.ComponentPropsWithRef<'a'>, 'href' | 'target'> {
|
|
5
|
+
/** specifies the URL */
|
|
6
|
+
destination: string;
|
|
7
|
+
/** Content of the hyperlink */
|
|
8
|
+
children: React.ReactNode;
|
|
9
|
+
/** Custom class names for the hyperlink */
|
|
10
|
+
className?: string;
|
|
11
|
+
/** Alt text for the icon indicating that this link opens in a new tab, if target="_blank". e.g. _("in a new tab") */
|
|
12
|
+
externalLinkAlternativeText?: string;
|
|
13
|
+
/** Tooltip text for the "opens in new tab" icon, if target="_blank". e.g. _("Opens in a new tab"). */
|
|
14
|
+
externalLinkTitle?: string;
|
|
15
|
+
/** type of hyperlink */
|
|
16
|
+
variant?: 'default' | 'muted' | 'brand';
|
|
17
|
+
/** Display the link with an underline. By default, it is only underlined on hover. */
|
|
18
|
+
isInline?: boolean;
|
|
19
|
+
/** specify if we need to show launch Icon. By default, it will be visible. */
|
|
20
|
+
showLaunchIcon?: boolean;
|
|
21
|
+
target?: '_blank' | '_self';
|
|
22
|
+
}
|
|
23
|
+
declare const Hyperlink: React.ForwardRefExoticComponent<Pick<Props, "variant" | "className" | "children" | "key" | "slot" | "style" | "title" | "onClick" | "tabIndex" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "id" | "lang" | "placeholder" | "spellCheck" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "download" | "hrefLang" | "media" | "ping" | "rel" | "target" | "type" | "referrerPolicy" | "destination" | "externalLinkAlternativeText" | "externalLinkTitle" | "isInline" | "showLaunchIcon"> & React.RefAttributes<HTMLAnchorElement>>;
|
|
24
|
+
export default Hyperlink;
|
package/dist/Hyperlink/index.js
CHANGED
|
@@ -5,24 +5,22 @@ function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) r
|
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import PropTypes from 'prop-types';
|
|
7
7
|
import classNames from 'classnames';
|
|
8
|
-
import isRequiredIf from 'react-proptype-conditional-require';
|
|
9
8
|
import { Launch } from '../../icons';
|
|
10
9
|
import Icon from '../Icon';
|
|
11
|
-
import withDeprecatedProps, { DeprTypes } from '../withDeprecatedProps';
|
|
12
10
|
export var HYPER_LINK_EXTERNAL_LINK_ALT_TEXT = 'in a new tab';
|
|
13
11
|
export var HYPER_LINK_EXTERNAL_LINK_TITLE = 'Opens in a new tab';
|
|
14
|
-
var Hyperlink = /*#__PURE__*/React.forwardRef(function (
|
|
15
|
-
var className =
|
|
16
|
-
destination =
|
|
17
|
-
children =
|
|
18
|
-
target =
|
|
19
|
-
onClick =
|
|
20
|
-
externalLinkAlternativeText =
|
|
21
|
-
externalLinkTitle =
|
|
22
|
-
variant =
|
|
23
|
-
isInline =
|
|
24
|
-
showLaunchIcon =
|
|
25
|
-
attrs = _objectWithoutProperties(
|
|
12
|
+
var Hyperlink = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
|
|
13
|
+
var className = _ref.className,
|
|
14
|
+
destination = _ref.destination,
|
|
15
|
+
children = _ref.children,
|
|
16
|
+
target = _ref.target,
|
|
17
|
+
onClick = _ref.onClick,
|
|
18
|
+
externalLinkAlternativeText = _ref.externalLinkAlternativeText,
|
|
19
|
+
externalLinkTitle = _ref.externalLinkTitle,
|
|
20
|
+
variant = _ref.variant,
|
|
21
|
+
isInline = _ref.isInline,
|
|
22
|
+
showLaunchIcon = _ref.showLaunchIcon,
|
|
23
|
+
attrs = _objectWithoutProperties(_ref, _excluded);
|
|
26
24
|
var externalLinkIcon;
|
|
27
25
|
if (target === '_blank') {
|
|
28
26
|
var generateRel = function generateRel() {
|
|
@@ -88,30 +86,20 @@ Hyperlink.propTypes = {
|
|
|
88
86
|
* loaded into the same browsing context as the current one.
|
|
89
87
|
* If the target is `_blank` (opening a new window) `rel='noopener'` will be added to the anchor tag to prevent
|
|
90
88
|
* any potential [reverse tabnabbing attack](https://www.owasp.org/index.php/Reverse_Tabnabbing).
|
|
91
|
-
|
|
92
|
-
target: PropTypes.
|
|
89
|
+
*/
|
|
90
|
+
target: PropTypes.oneOf(['_blank', '_self']),
|
|
93
91
|
/** specifies the callback function when the link is clicked */
|
|
94
92
|
onClick: PropTypes.func,
|
|
95
|
-
/**
|
|
96
|
-
externalLinkAlternativeText:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
/** specifies the title for links with a `_blank` target (which loads the URL in a new browsing context). */
|
|
100
|
-
externalLinkTitle: isRequiredIf(PropTypes.string, function (props) {
|
|
101
|
-
return props.target === '_blank';
|
|
102
|
-
}),
|
|
93
|
+
/** Alt text for the icon indicating that this link opens in a new tab, if target="_blank". e.g. _("in a new tab") */
|
|
94
|
+
externalLinkAlternativeText: PropTypes.string,
|
|
95
|
+
/** Tooltip text for the "opens in new tab" icon, if target="_blank". e.g. _("Opens in a new tab"). */
|
|
96
|
+
externalLinkTitle: PropTypes.string,
|
|
103
97
|
/** type of hyperlink */
|
|
104
98
|
variant: PropTypes.oneOf(['default', 'muted', 'brand']),
|
|
105
|
-
/**
|
|
99
|
+
/** Display the link with an underline. By default, it is only underlined on hover. */
|
|
106
100
|
isInline: PropTypes.bool,
|
|
107
101
|
/** specify if we need to show launch Icon. By default, it will be visible. */
|
|
108
102
|
showLaunchIcon: PropTypes.bool
|
|
109
103
|
};
|
|
110
|
-
export default
|
|
111
|
-
/** specifies the text or element that a URL should be associated with */
|
|
112
|
-
content: {
|
|
113
|
-
deprType: DeprTypes.MOVED,
|
|
114
|
-
newName: 'children'
|
|
115
|
-
}
|
|
116
|
-
});
|
|
104
|
+
export default Hyperlink;
|
|
117
105
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["React","PropTypes","classNames","
|
|
1
|
+
{"version":3,"file":"index.js","names":["React","PropTypes","classNames","Launch","Icon","HYPER_LINK_EXTERNAL_LINK_ALT_TEXT","HYPER_LINK_EXTERNAL_LINK_TITLE","Hyperlink","forwardRef","_ref","ref","className","destination","children","target","onClick","externalLinkAlternativeText","externalLinkTitle","variant","isInline","showLaunchIcon","attrs","_objectWithoutProperties","_excluded","externalLinkIcon","generateRel","rel","includes","createElement","title","src","screenReaderText","style","height","width","_extends","concat","href","defaultProps","undefined","propTypes","string","isRequired","node","oneOf","func","bool"],"sources":["../../src/Hyperlink/index.tsx"],"sourcesContent":["import React from 'react';\nimport PropTypes from 'prop-types';\nimport classNames from 'classnames';\nimport { Launch } from '../../icons';\nimport Icon from '../Icon';\n\nexport const HYPER_LINK_EXTERNAL_LINK_ALT_TEXT = 'in a new tab';\nexport const HYPER_LINK_EXTERNAL_LINK_TITLE = 'Opens in a new tab';\n\ninterface Props extends Omit<React.ComponentPropsWithRef<'a'>, 'href' | 'target'> {\n /** specifies the URL */\n destination: string;\n /** Content of the hyperlink */\n children: React.ReactNode;\n /** Custom class names for the hyperlink */\n className?: string;\n /** Alt text for the icon indicating that this link opens in a new tab, if target=\"_blank\". e.g. _(\"in a new tab\") */\n externalLinkAlternativeText?: string;\n /** Tooltip text for the \"opens in new tab\" icon, if target=\"_blank\". e.g. _(\"Opens in a new tab\"). */\n externalLinkTitle?: string;\n /** type of hyperlink */\n variant?: 'default' | 'muted' | 'brand';\n /** Display the link with an underline. By default, it is only underlined on hover. */\n isInline?: boolean;\n /** specify if we need to show launch Icon. By default, it will be visible. */\n showLaunchIcon?: boolean;\n target?: '_blank' | '_self';\n}\n\nconst Hyperlink = React.forwardRef<HTMLAnchorElement, Props>(({\n className,\n destination,\n children,\n target,\n onClick,\n externalLinkAlternativeText,\n externalLinkTitle,\n variant,\n isInline,\n showLaunchIcon,\n ...attrs\n}, ref) => {\n let externalLinkIcon;\n\n if (target === '_blank') {\n const generateRel = () => {\n let { rel } = attrs;\n if (!rel) {\n return 'noopener noreferrer';\n }\n if (!rel.includes('noopener')) {\n rel += ' noopener';\n }\n if (!rel.includes('noreferrer')) {\n rel += ' noreferrer';\n }\n return rel;\n };\n\n // Add this rel attribute to prevent Reverse Tabnabbing\n attrs.rel = generateRel();\n if (showLaunchIcon) {\n externalLinkIcon = (\n <span\n className=\"pgn__hyperlink__external-icon\"\n title={externalLinkTitle}\n >\n <Icon\n src={Launch}\n screenReaderText={externalLinkAlternativeText}\n style={{ height: '1em', width: '1em' }}\n data-testid=\"hyperlink-icon\"\n />\n </span>\n );\n }\n }\n\n return (\n <a\n ref={ref}\n className={classNames(\n 'pgn__hyperlink',\n `${variant}-link`,\n {\n 'standalone-link': !isInline,\n 'inline-link': isInline,\n },\n className,\n )}\n href={destination}\n target={target}\n onClick={onClick}\n {...attrs}\n >\n {children}\n {externalLinkIcon}\n </a>\n );\n});\n\nHyperlink.defaultProps = {\n className: undefined,\n target: '_self',\n onClick: () => {},\n externalLinkAlternativeText: HYPER_LINK_EXTERNAL_LINK_ALT_TEXT,\n externalLinkTitle: HYPER_LINK_EXTERNAL_LINK_TITLE,\n variant: 'default',\n isInline: false,\n showLaunchIcon: true,\n};\n\nHyperlink.propTypes = {\n /** specifies the URL */\n destination: PropTypes.string.isRequired,\n /** Content of the hyperlink */\n children: PropTypes.node.isRequired,\n /** Custom class names for the hyperlink */\n className: PropTypes.string,\n /** specifies where the link should open. The default behavior is `_self`, which means that the URL will be\n * loaded into the same browsing context as the current one.\n * If the target is `_blank` (opening a new window) `rel='noopener'` will be added to the anchor tag to prevent\n * any potential [reverse tabnabbing attack](https://www.owasp.org/index.php/Reverse_Tabnabbing).\n */\n target: PropTypes.oneOf(['_blank', '_self']),\n /** specifies the callback function when the link is clicked */\n onClick: PropTypes.func,\n /** Alt text for the icon indicating that this link opens in a new tab, if target=\"_blank\". e.g. _(\"in a new tab\") */\n externalLinkAlternativeText: PropTypes.string,\n /** Tooltip text for the \"opens in new tab\" icon, if target=\"_blank\". e.g. _(\"Opens in a new tab\"). */\n externalLinkTitle: PropTypes.string,\n /** type of hyperlink */\n variant: PropTypes.oneOf(['default', 'muted', 'brand']),\n /** Display the link with an underline. By default, it is only underlined on hover. */\n isInline: PropTypes.bool,\n /** specify if we need to show launch Icon. By default, it will be visible. */\n showLaunchIcon: PropTypes.bool,\n};\n\nexport default Hyperlink;\n"],"mappings":";;;;AAAA,OAAOA,KAAK,MAAM,OAAO;AACzB,OAAOC,SAAS,MAAM,YAAY;AAClC,OAAOC,UAAU,MAAM,YAAY;AACnC,SAASC,MAAM,QAAQ,aAAa;AACpC,OAAOC,IAAI,MAAM,SAAS;AAE1B,OAAO,IAAMC,iCAAiC,GAAG,cAAc;AAC/D,OAAO,IAAMC,8BAA8B,GAAG,oBAAoB;AAsBlE,IAAMC,SAAS,gBAAGP,KAAK,CAACQ,UAAU,CAA2B,UAAAC,IAAA,EAY1DC,GAAG,EAAK;EAAA,IAXTC,SAAS,GAAAF,IAAA,CAATE,SAAS;IACTC,WAAW,GAAAH,IAAA,CAAXG,WAAW;IACXC,QAAQ,GAAAJ,IAAA,CAARI,QAAQ;IACRC,MAAM,GAAAL,IAAA,CAANK,MAAM;IACNC,OAAO,GAAAN,IAAA,CAAPM,OAAO;IACPC,2BAA2B,GAAAP,IAAA,CAA3BO,2BAA2B;IAC3BC,iBAAiB,GAAAR,IAAA,CAAjBQ,iBAAiB;IACjBC,OAAO,GAAAT,IAAA,CAAPS,OAAO;IACPC,QAAQ,GAAAV,IAAA,CAARU,QAAQ;IACRC,cAAc,GAAAX,IAAA,CAAdW,cAAc;IACXC,KAAK,GAAAC,wBAAA,CAAAb,IAAA,EAAAc,SAAA;EAER,IAAIC,gBAAgB;EAEpB,IAAIV,MAAM,KAAK,QAAQ,EAAE;IACvB,IAAMW,WAAW,GAAG,SAAdA,WAAWA,CAAA,EAAS;MACxB,IAAMC,GAAG,GAAKL,KAAK,CAAbK,GAAG;MACT,IAAI,CAACA,GAAG,EAAE;QACR,OAAO,qBAAqB;MAC9B;MACA,IAAI,CAACA,GAAG,CAACC,QAAQ,CAAC,UAAU,CAAC,EAAE;QAC7BD,GAAG,IAAI,WAAW;MACpB;MACA,IAAI,CAACA,GAAG,CAACC,QAAQ,CAAC,YAAY,CAAC,EAAE;QAC/BD,GAAG,IAAI,aAAa;MACtB;MACA,OAAOA,GAAG;IACZ,CAAC;;IAED;IACAL,KAAK,CAACK,GAAG,GAAGD,WAAW,CAAC,CAAC;IACzB,IAAIL,cAAc,EAAE;MAClBI,gBAAgB,gBACdxB,KAAA,CAAA4B,aAAA;QACEjB,SAAS,EAAC,+BAA+B;QACzCkB,KAAK,EAAEZ;MAAkB,gBAEzBjB,KAAA,CAAA4B,aAAA,CAACxB,IAAI;QACH0B,GAAG,EAAE3B,MAAO;QACZ4B,gBAAgB,EAAEf,2BAA4B;QAC9CgB,KAAK,EAAE;UAAEC,MAAM,EAAE,KAAK;UAAEC,KAAK,EAAE;QAAM,CAAE;QACvC,eAAY;MAAgB,CAC7B,CACG,CACP;IACH;EACF;EAEA,oBACElC,KAAA,CAAA4B,aAAA,MAAAO,QAAA;IACEzB,GAAG,EAAEA,GAAI;IACTC,SAAS,EAAET,UAAU,CACnB,gBAAgB,KAAAkC,MAAA,CACblB,OAAO,YACV;MACE,iBAAiB,EAAE,CAACC,QAAQ;MAC5B,aAAa,EAAEA;IACjB,CAAC,EACDR,SACF,CAAE;IACF0B,IAAI,EAAEzB,WAAY;IAClBE,MAAM,EAAEA,MAAO;IACfC,OAAO,EAAEA;EAAQ,GACbM,KAAK,GAERR,QAAQ,EACRW,gBACA,CAAC;AAER,CAAC,CAAC;AAEFjB,SAAS,CAAC+B,YAAY,GAAG;EACvB3B,SAAS,EAAE4B,SAAS;EACpBzB,MAAM,EAAE,OAAO;EACfC,OAAO,EAAE,SAAAA,QAAA,EAAM,CAAC,CAAC;EACjBC,2BAA2B,EAAEX,iCAAiC;EAC9DY,iBAAiB,EAAEX,8BAA8B;EACjDY,OAAO,EAAE,SAAS;EAClBC,QAAQ,EAAE,KAAK;EACfC,cAAc,EAAE;AAClB,CAAC;AAEDb,SAAS,CAACiC,SAAS,GAAG;EACpB;EACA5B,WAAW,EAAEX,SAAS,CAACwC,MAAM,CAACC,UAAU;EACxC;EACA7B,QAAQ,EAAEZ,SAAS,CAAC0C,IAAI,CAACD,UAAU;EACnC;EACA/B,SAAS,EAAEV,SAAS,CAACwC,MAAM;EAC3B;AACF;AACA;AACA;AACA;EACE3B,MAAM,EAAEb,SAAS,CAAC2C,KAAK,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;EAC5C;EACA7B,OAAO,EAAEd,SAAS,CAAC4C,IAAI;EACvB;EACA7B,2BAA2B,EAAEf,SAAS,CAACwC,MAAM;EAC7C;EACAxB,iBAAiB,EAAEhB,SAAS,CAACwC,MAAM;EACnC;EACAvB,OAAO,EAAEjB,SAAS,CAAC2C,KAAK,CAAC,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;EACvD;EACAzB,QAAQ,EAAElB,SAAS,CAAC6C,IAAI;EACxB;EACA1B,cAAc,EAAEnB,SAAS,CAAC6C;AAC5B,CAAC;AAED,eAAevC,SAAS","ignoreList":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
export { default as Bubble } from './Bubble';
|
|
8
8
|
export { default as Chip, CHIP_PGN_CLASS } from './Chip';
|
|
9
9
|
export { default as ChipCarousel } from './ChipCarousel';
|
|
10
|
+
export { default as Hyperlink, HYPER_LINK_EXTERNAL_LINK_ALT_TEXT, HYPER_LINK_EXTERNAL_LINK_TITLE } from './Hyperlink';
|
|
10
11
|
export { default as Icon } from './Icon';
|
|
11
12
|
|
|
12
13
|
// // // // // // // // // // // // // // // // // // // // // // // // // // //
|
|
@@ -72,7 +73,6 @@ export const
|
|
|
72
73
|
FormAutosuggestOption: any,
|
|
73
74
|
InputGroup: any;
|
|
74
75
|
// from './Form';
|
|
75
|
-
export const Hyperlink: any, HYPER_LINK_EXTERNAL_LINK_ALT_TEXT: string, HYPER_LINK_EXTERNAL_LINK_TITLE: string; // from './Hyperlink';
|
|
76
76
|
export const IconButton: any, IconButtonWithTooltip: any; // from './IconButton';
|
|
77
77
|
export const IconButtonToggle: any; // from './IconButtonToggle';
|
|
78
78
|
export const Input: any; // from './Input';
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
export { default as Bubble } from './Bubble';
|
|
8
8
|
export { default as Chip, CHIP_PGN_CLASS } from './Chip';
|
|
9
9
|
export { default as ChipCarousel } from './ChipCarousel';
|
|
10
|
+
export { default as Hyperlink, HYPER_LINK_EXTERNAL_LINK_ALT_TEXT, HYPER_LINK_EXTERNAL_LINK_TITLE } from './Hyperlink';
|
|
10
11
|
export { default as Icon } from './Icon';
|
|
11
12
|
|
|
12
13
|
// // // // // // // // // // // // // // // // // // // // // // // // // // //
|
|
@@ -72,7 +73,6 @@ export {
|
|
|
72
73
|
FormAutosuggestOption,
|
|
73
74
|
InputGroup,
|
|
74
75
|
} from './Form';
|
|
75
|
-
export { default as Hyperlink, HYPER_LINK_EXTERNAL_LINK_ALT_TEXT, HYPER_LINK_EXTERNAL_LINK_TITLE } from './Hyperlink';
|
|
76
76
|
export { default as IconButton, IconButtonWithTooltip } from './IconButton';
|
|
77
77
|
export { default as IconButtonToggle } from './IconButtonToggle';
|
|
78
78
|
export { default as Input } from './Input';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setupTest.js","names":["crypto","require","ResizeObserver","_classCallCheck","_createClass","key","value","observe","unobserve","disconnect","window","getRandomValues","arr","randomBytes","length"],"sources":["../src/setupTest.ts"],"sourcesContent":["/* eslint-disable import/no-extraneous-dependencies */\nimport 'regenerator-runtime/runtime';\n\nimport '@testing-library/jest-dom';\n\nconst crypto = require('crypto');\n\nclass ResizeObserver {\n observe() {\n // do nothing\n }\n\n unobserve() {\n // do nothing\n }\n\n disconnect() {\n // do nothing\n }\n}\n\nwindow.ResizeObserver = ResizeObserver;\n\n(window as any).crypto = {\n getRandomValues: (arr: any) => crypto.randomBytes(arr.length),\n};\n"],"mappings":";;;;;;AAAA;AACA,OAAO,6BAA6B;AAEpC,OAAO,2BAA2B;AAElC,IAAMA,MAAM,GAAGC,OAAO,CAAC,QAAQ,CAAC;AAAC,IAE3BC,cAAc;EAAA,SAAAA,eAAA;IAAAC,eAAA,OAAAD,cAAA;EAAA;EAAAE,YAAA,CAAAF,cAAA;IAAAG,GAAA;IAAAC,KAAA,EAClB,SAAAC,QAAA,EAAU;MACR;IAAA;EACD;IAAAF,GAAA;IAAAC,KAAA,EAED,SAAAE,UAAA,EAAY;MACV;IAAA;EACD;IAAAH,GAAA;IAAAC,KAAA,EAED,SAAAG,WAAA,EAAa;MACX;IAAA;EACD;EAAA,OAAAP,cAAA;AAAA;AAGHQ,MAAM,CAACR,cAAc,GAAGA,cAAc;AAErCQ,MAAM,CAASV,MAAM,GAAG;EACvBW,eAAe,EAAE,SAAAA,gBAACC,GAAQ;IAAA,OAAKZ,MAAM,CAACa,WAAW,CAACD,GAAG,CAACE,MAAM,CAAC;EAAA;AAC/D,CAAC","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openedx/paragon",
|
|
3
|
-
"version": "22.
|
|
3
|
+
"version": "22.5.0",
|
|
4
4
|
"description": "Accessible, responsive UI component library based on Bootstrap.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
"^.+\\.tsx?$": "ts-jest"
|
|
154
154
|
},
|
|
155
155
|
"setupFilesAfterEnv": [
|
|
156
|
-
"./src/setupTest.
|
|
156
|
+
"./src/setupTest.ts"
|
|
157
157
|
],
|
|
158
158
|
"moduleNameMapper": {
|
|
159
159
|
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
|
|
@@ -164,7 +164,7 @@
|
|
|
164
164
|
],
|
|
165
165
|
"coveragePathIgnorePatterns": [
|
|
166
166
|
"/node_modules/",
|
|
167
|
-
"src/setupTest.
|
|
167
|
+
"src/setupTest.ts",
|
|
168
168
|
"src/index.js",
|
|
169
169
|
"/tests/",
|
|
170
170
|
"/www/",
|
|
@@ -4,30 +4,34 @@ import userEvent from '@testing-library/user-event';
|
|
|
4
4
|
|
|
5
5
|
import Hyperlink from '.';
|
|
6
6
|
|
|
7
|
-
const content = 'content';
|
|
8
7
|
const destination = 'destination';
|
|
8
|
+
const content = 'content';
|
|
9
9
|
const onClick = jest.fn();
|
|
10
10
|
const props = {
|
|
11
|
-
content,
|
|
12
11
|
destination,
|
|
13
12
|
onClick,
|
|
14
13
|
};
|
|
15
14
|
const externalLinkAlternativeText = 'externalLinkAlternativeText';
|
|
16
15
|
const externalLinkTitle = 'externalLinkTitle';
|
|
17
16
|
const externalLinkProps = {
|
|
18
|
-
target: '_blank',
|
|
17
|
+
target: '_blank' as const,
|
|
19
18
|
externalLinkAlternativeText,
|
|
20
19
|
externalLinkTitle,
|
|
21
20
|
...props,
|
|
22
21
|
};
|
|
23
22
|
|
|
24
23
|
describe('correct rendering', () => {
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
onClick.mockClear();
|
|
26
|
+
});
|
|
27
|
+
|
|
25
28
|
it('renders Hyperlink', async () => {
|
|
26
|
-
const { getByRole } = render(<Hyperlink {...props}
|
|
29
|
+
const { getByRole } = render(<Hyperlink {...props}>{content}</Hyperlink>);
|
|
27
30
|
const wrapper = getByRole('link');
|
|
28
31
|
expect(wrapper).toBeInTheDocument();
|
|
29
32
|
|
|
30
33
|
expect(wrapper).toHaveClass('pgn__hyperlink');
|
|
34
|
+
expect(wrapper).toHaveClass('standalone-link');
|
|
31
35
|
expect(wrapper).toHaveTextContent(content);
|
|
32
36
|
expect(wrapper).toHaveAttribute('href', destination);
|
|
33
37
|
expect(wrapper).toHaveAttribute('target', '_self');
|
|
@@ -36,8 +40,17 @@ describe('correct rendering', () => {
|
|
|
36
40
|
expect(onClick).toHaveBeenCalledTimes(1);
|
|
37
41
|
});
|
|
38
42
|
|
|
43
|
+
it('renders an underlined Hyperlink', async () => {
|
|
44
|
+
const { getByRole } = render(<Hyperlink isInline {...props}>{content}</Hyperlink>);
|
|
45
|
+
const wrapper = getByRole('link');
|
|
46
|
+
expect(wrapper).toBeInTheDocument();
|
|
47
|
+
expect(wrapper).toHaveClass('pgn__hyperlink');
|
|
48
|
+
expect(wrapper).not.toHaveClass('standalone-link');
|
|
49
|
+
expect(wrapper).toHaveClass('inline-link');
|
|
50
|
+
});
|
|
51
|
+
|
|
39
52
|
it('renders external Hyperlink', () => {
|
|
40
|
-
const { getByRole, getByTestId } = render(<Hyperlink {...externalLinkProps}
|
|
53
|
+
const { getByRole, getByTestId } = render(<Hyperlink {...externalLinkProps}>{content}</Hyperlink>);
|
|
41
54
|
const wrapper = getByRole('link');
|
|
42
55
|
const icon = getByTestId('hyperlink-icon');
|
|
43
56
|
const iconSvg = icon.querySelector('svg');
|
|
@@ -53,18 +66,16 @@ describe('correct rendering', () => {
|
|
|
53
66
|
|
|
54
67
|
describe('security', () => {
|
|
55
68
|
it('prevents reverse tabnabbing for links with target="_blank"', () => {
|
|
56
|
-
const { getByRole } = render(<Hyperlink {...externalLinkProps}
|
|
69
|
+
const { getByRole } = render(<Hyperlink {...externalLinkProps}>{content}</Hyperlink>);
|
|
57
70
|
const wrapper = getByRole('link');
|
|
58
71
|
expect(wrapper).toHaveAttribute('rel', 'noopener noreferrer');
|
|
59
72
|
});
|
|
60
73
|
});
|
|
61
74
|
|
|
62
75
|
describe('event handlers are triggered correctly', () => {
|
|
63
|
-
let spy;
|
|
64
|
-
beforeEach(() => { spy = jest.fn(); });
|
|
65
|
-
|
|
66
76
|
it('should fire onClick', async () => {
|
|
67
|
-
const
|
|
77
|
+
const spy = jest.fn();
|
|
78
|
+
const { getByRole } = render(<Hyperlink {...props} onClick={spy}>{content}</Hyperlink>);
|
|
68
79
|
const wrapper = getByRole('link');
|
|
69
80
|
expect(spy).toHaveBeenCalledTimes(0);
|
|
70
81
|
await userEvent.click(wrapper);
|
|
@@ -1,29 +1,45 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import classNames from 'classnames';
|
|
4
|
-
import isRequiredIf from 'react-proptype-conditional-require';
|
|
5
4
|
import { Launch } from '../../icons';
|
|
6
5
|
import Icon from '../Icon';
|
|
7
6
|
|
|
8
|
-
import withDeprecatedProps, { DeprTypes } from '../withDeprecatedProps';
|
|
9
|
-
|
|
10
7
|
export const HYPER_LINK_EXTERNAL_LINK_ALT_TEXT = 'in a new tab';
|
|
11
8
|
export const HYPER_LINK_EXTERNAL_LINK_TITLE = 'Opens in a new tab';
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
10
|
+
interface Props extends Omit<React.ComponentPropsWithRef<'a'>, 'href' | 'target'> {
|
|
11
|
+
/** specifies the URL */
|
|
12
|
+
destination: string;
|
|
13
|
+
/** Content of the hyperlink */
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
/** Custom class names for the hyperlink */
|
|
16
|
+
className?: string;
|
|
17
|
+
/** Alt text for the icon indicating that this link opens in a new tab, if target="_blank". e.g. _("in a new tab") */
|
|
18
|
+
externalLinkAlternativeText?: string;
|
|
19
|
+
/** Tooltip text for the "opens in new tab" icon, if target="_blank". e.g. _("Opens in a new tab"). */
|
|
20
|
+
externalLinkTitle?: string;
|
|
21
|
+
/** type of hyperlink */
|
|
22
|
+
variant?: 'default' | 'muted' | 'brand';
|
|
23
|
+
/** Display the link with an underline. By default, it is only underlined on hover. */
|
|
24
|
+
isInline?: boolean;
|
|
25
|
+
/** specify if we need to show launch Icon. By default, it will be visible. */
|
|
26
|
+
showLaunchIcon?: boolean;
|
|
27
|
+
target?: '_blank' | '_self';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const Hyperlink = React.forwardRef<HTMLAnchorElement, Props>(({
|
|
31
|
+
className,
|
|
32
|
+
destination,
|
|
33
|
+
children,
|
|
34
|
+
target,
|
|
35
|
+
onClick,
|
|
36
|
+
externalLinkAlternativeText,
|
|
37
|
+
externalLinkTitle,
|
|
38
|
+
variant,
|
|
39
|
+
isInline,
|
|
40
|
+
showLaunchIcon,
|
|
41
|
+
...attrs
|
|
42
|
+
}, ref) => {
|
|
27
43
|
let externalLinkIcon;
|
|
28
44
|
|
|
29
45
|
if (target === '_blank') {
|
|
@@ -105,32 +121,20 @@ Hyperlink.propTypes = {
|
|
|
105
121
|
* loaded into the same browsing context as the current one.
|
|
106
122
|
* If the target is `_blank` (opening a new window) `rel='noopener'` will be added to the anchor tag to prevent
|
|
107
123
|
* any potential [reverse tabnabbing attack](https://www.owasp.org/index.php/Reverse_Tabnabbing).
|
|
108
|
-
|
|
109
|
-
target: PropTypes.
|
|
124
|
+
*/
|
|
125
|
+
target: PropTypes.oneOf(['_blank', '_self']),
|
|
110
126
|
/** specifies the callback function when the link is clicked */
|
|
111
127
|
onClick: PropTypes.func,
|
|
112
|
-
/**
|
|
113
|
-
externalLinkAlternativeText:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
),
|
|
117
|
-
/** specifies the title for links with a `_blank` target (which loads the URL in a new browsing context). */
|
|
118
|
-
externalLinkTitle: isRequiredIf(
|
|
119
|
-
PropTypes.string,
|
|
120
|
-
props => props.target === '_blank',
|
|
121
|
-
),
|
|
128
|
+
/** Alt text for the icon indicating that this link opens in a new tab, if target="_blank". e.g. _("in a new tab") */
|
|
129
|
+
externalLinkAlternativeText: PropTypes.string,
|
|
130
|
+
/** Tooltip text for the "opens in new tab" icon, if target="_blank". e.g. _("Opens in a new tab"). */
|
|
131
|
+
externalLinkTitle: PropTypes.string,
|
|
122
132
|
/** type of hyperlink */
|
|
123
133
|
variant: PropTypes.oneOf(['default', 'muted', 'brand']),
|
|
124
|
-
/**
|
|
134
|
+
/** Display the link with an underline. By default, it is only underlined on hover. */
|
|
125
135
|
isInline: PropTypes.bool,
|
|
126
136
|
/** specify if we need to show launch Icon. By default, it will be visible. */
|
|
127
137
|
showLaunchIcon: PropTypes.bool,
|
|
128
138
|
};
|
|
129
139
|
|
|
130
|
-
export default
|
|
131
|
-
/** specifies the text or element that a URL should be associated with */
|
|
132
|
-
content: {
|
|
133
|
-
deprType: DeprTypes.MOVED,
|
|
134
|
-
newName: 'children',
|
|
135
|
-
},
|
|
136
|
-
});
|
|
140
|
+
export default Hyperlink;
|
package/src/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
export { default as Bubble } from './Bubble';
|
|
8
8
|
export { default as Chip, CHIP_PGN_CLASS } from './Chip';
|
|
9
9
|
export { default as ChipCarousel } from './ChipCarousel';
|
|
10
|
+
export { default as Hyperlink, HYPER_LINK_EXTERNAL_LINK_ALT_TEXT, HYPER_LINK_EXTERNAL_LINK_TITLE } from './Hyperlink';
|
|
10
11
|
export { default as Icon } from './Icon';
|
|
11
12
|
|
|
12
13
|
// // // // // // // // // // // // // // // // // // // // // // // // // // //
|
|
@@ -72,7 +73,6 @@ export const
|
|
|
72
73
|
FormAutosuggestOption: any,
|
|
73
74
|
InputGroup: any;
|
|
74
75
|
// from './Form';
|
|
75
|
-
export const Hyperlink: any, HYPER_LINK_EXTERNAL_LINK_ALT_TEXT: string, HYPER_LINK_EXTERNAL_LINK_TITLE: string; // from './Hyperlink';
|
|
76
76
|
export const IconButton: any, IconButtonWithTooltip: any; // from './IconButton';
|
|
77
77
|
export const IconButtonToggle: any; // from './IconButtonToggle';
|
|
78
78
|
export const Input: any; // from './Input';
|
package/src/index.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
export { default as Bubble } from './Bubble';
|
|
8
8
|
export { default as Chip, CHIP_PGN_CLASS } from './Chip';
|
|
9
9
|
export { default as ChipCarousel } from './ChipCarousel';
|
|
10
|
+
export { default as Hyperlink, HYPER_LINK_EXTERNAL_LINK_ALT_TEXT, HYPER_LINK_EXTERNAL_LINK_TITLE } from './Hyperlink';
|
|
10
11
|
export { default as Icon } from './Icon';
|
|
11
12
|
|
|
12
13
|
// // // // // // // // // // // // // // // // // // // // // // // // // // //
|
|
@@ -72,7 +73,6 @@ export {
|
|
|
72
73
|
FormAutosuggestOption,
|
|
73
74
|
InputGroup,
|
|
74
75
|
} from './Form';
|
|
75
|
-
export { default as Hyperlink, HYPER_LINK_EXTERNAL_LINK_ALT_TEXT, HYPER_LINK_EXTERNAL_LINK_TITLE } from './Hyperlink';
|
|
76
76
|
export { default as IconButton, IconButtonWithTooltip } from './IconButton';
|
|
77
77
|
export { default as IconButtonToggle } from './IconButtonToggle';
|
|
78
78
|
export { default as Input } from './Input';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable import/no-extraneous-dependencies */
|
|
1
2
|
import 'regenerator-runtime/runtime';
|
|
2
3
|
|
|
3
4
|
import '@testing-library/jest-dom';
|
|
@@ -20,6 +21,6 @@ class ResizeObserver {
|
|
|
20
21
|
|
|
21
22
|
window.ResizeObserver = ResizeObserver;
|
|
22
23
|
|
|
23
|
-
window.crypto = {
|
|
24
|
-
getRandomValues: arr => crypto.randomBytes(arr.length),
|
|
24
|
+
(window as any).crypto = {
|
|
25
|
+
getRandomValues: (arr: any) => crypto.randomBytes(arr.length),
|
|
25
26
|
};
|