@khanacademy/wonder-blocks-link
Advanced tools
Comparing version 3.6.3 to 3.7.0
@@ -10,3 +10,3 @@ // @flow | ||
import type { | ||
ClickableHandlers, | ||
ChildrenProps, | ||
ClickableState, | ||
@@ -19,3 +19,3 @@ } from "@khanacademy/wonder-blocks-clickable"; | ||
...SharedProps, | ||
...ClickableHandlers, | ||
...ChildrenProps, | ||
...ClickableState, | ||
@@ -22,0 +22,0 @@ href: string, |
@@ -10,3 +10,3 @@ // @flow | ||
export type SharedProps = {| | ||
type CommonProps = {| | ||
...AriaProps, | ||
@@ -46,24 +46,8 @@ | ||
/** | ||
* A target destination window for a link to open in. | ||
*/ | ||
target?: string, | ||
/** | ||
* Specifies the type of relationship between the current document and the | ||
* linked document. Should only be used when `href` is specified. | ||
* linked document. Should only be used when `href` is specified. This | ||
* defaults to "noopener noreferrer" when `target="_blank"`, but can be | ||
* overridden by setting this prop to something else. | ||
*/ | ||
rel?: | ||
| "alternate" | ||
| "author" | ||
| "bookmark" | ||
| "external" | ||
| "help" | ||
| "license" | ||
| "next" | ||
| "nofollow" | ||
| "noreferrer" | ||
| "noopener" | ||
| "prev" | ||
| "search" | ||
| "tag", | ||
rel?: string, | ||
@@ -130,11 +114,2 @@ /** | ||
/** | ||
* Run async code before navigating to the URL passed to `href`. If the | ||
* promise returned rejects then navigation will not occur. | ||
* | ||
* If both safeWithNav and beforeNav are provided, beforeNav will be run | ||
* first and safeWithNav will only be run if beforeNav does not reject. | ||
*/ | ||
beforeNav?: () => Promise<mixed>, | ||
/** | ||
* Run async code in the background while client-side navigating. If the | ||
@@ -158,2 +133,31 @@ * browser does a full page load navigation, the callback promise must be | ||
export type SharedProps = | ||
| {| | ||
...CommonProps, | ||
/** | ||
* A target destination window for a link to open in. We only support | ||
* "_blank" which opens the URL in a new tab. | ||
*/ | ||
target?: "_blank", | ||
|} | ||
| {| | ||
...CommonProps, | ||
/** | ||
* Run async code before navigating to the URL passed to `href`. If the | ||
* promise returned rejects then navigation will not occur. | ||
* | ||
* If both safeWithNav and beforeNav are provided, beforeNav will be run | ||
* first and safeWithNav will only be run if beforeNav does not reject. | ||
* | ||
* WARNING: Using this with `target="_blank"` will trigger built-in popup | ||
* blockers in Firefox and Safari. This is because we do navigation | ||
* programmatically and `beforeNav` causes a delay which means that the | ||
* browser can't make a directly link between a user action and the | ||
* navigation. | ||
*/ | ||
beforeNav?: () => Promise<mixed>, | ||
|}; | ||
/** | ||
@@ -187,3 +191,3 @@ * Reusable link component. | ||
onClick, | ||
beforeNav, | ||
beforeNav = undefined, | ||
safeWithNav, | ||
@@ -196,3 +200,3 @@ href, | ||
onKeyUp, | ||
target, | ||
target = undefined, | ||
...sharedProps | ||
@@ -207,35 +211,74 @@ } = this.props; | ||
return ( | ||
<ClickableBehavior | ||
disabled={false} | ||
href={href} | ||
role="link" | ||
onClick={onClick} | ||
beforeNav={beforeNav} | ||
safeWithNav={safeWithNav} | ||
target={target} | ||
onKeyDown={onKeyDown} | ||
onKeyUp={onKeyUp} | ||
> | ||
{(state, {tabIndex: clickableTabIndex, ...handlers}) => { | ||
return ( | ||
<LinkCore | ||
{...sharedProps} | ||
{...state} | ||
{...handlers} | ||
skipClientNav={skipClientNav} | ||
href={href} | ||
target={target} | ||
// If tabIndex is provide to the component we allow | ||
// it to override the tabIndex provide to use by | ||
// ClickableBehavior. | ||
tabIndex={tabIndex || clickableTabIndex} | ||
> | ||
{children} | ||
</LinkCore> | ||
); | ||
}} | ||
</ClickableBehavior> | ||
); | ||
if (beforeNav) { | ||
return ( | ||
<ClickableBehavior | ||
disabled={false} | ||
href={href} | ||
role="link" | ||
onClick={onClick} | ||
beforeNav={beforeNav} | ||
safeWithNav={safeWithNav} | ||
onKeyDown={onKeyDown} | ||
onKeyUp={onKeyUp} | ||
> | ||
{( | ||
state, | ||
{tabIndex: clickableTabIndex, ...childrenProps}, | ||
) => { | ||
return ( | ||
<LinkCore | ||
{...sharedProps} | ||
{...state} | ||
{...childrenProps} | ||
skipClientNav={skipClientNav} | ||
href={href} | ||
target={target} | ||
// If tabIndex is provide to the component we allow | ||
// it to override the tabIndex provide to use by | ||
// ClickableBehavior. | ||
tabIndex={tabIndex || clickableTabIndex} | ||
> | ||
{children} | ||
</LinkCore> | ||
); | ||
}} | ||
</ClickableBehavior> | ||
); | ||
} else { | ||
return ( | ||
<ClickableBehavior | ||
disabled={false} | ||
href={href} | ||
role="link" | ||
onClick={onClick} | ||
safeWithNav={safeWithNav} | ||
target={target} | ||
onKeyDown={onKeyDown} | ||
onKeyUp={onKeyUp} | ||
> | ||
{( | ||
state, | ||
{tabIndex: clickableTabIndex, ...childrenProps}, | ||
) => { | ||
return ( | ||
<LinkCore | ||
{...sharedProps} | ||
{...state} | ||
{...childrenProps} | ||
skipClientNav={skipClientNav} | ||
href={href} | ||
target={target} | ||
// If tabIndex is provide to the component we allow | ||
// it to override the tabIndex provide to use by | ||
// ClickableBehavior. | ||
tabIndex={tabIndex || clickableTabIndex} | ||
> | ||
{children} | ||
</LinkCore> | ||
); | ||
}} | ||
</ClickableBehavior> | ||
); | ||
} | ||
} | ||
} |
@@ -363,3 +363,4 @@ import { createElement, Component } from 'react'; | ||
onClick = _this$props.onClick, | ||
beforeNav = _this$props.beforeNav, | ||
_this$props$beforeNav = _this$props.beforeNav, | ||
beforeNav = _this$props$beforeNav === void 0 ? undefined : _this$props$beforeNav, | ||
safeWithNav = _this$props.safeWithNav, | ||
@@ -372,30 +373,57 @@ href = _this$props.href, | ||
onKeyUp = _this$props.onKeyUp, | ||
target = _this$props.target, | ||
_this$props$target = _this$props.target, | ||
target = _this$props$target === void 0 ? undefined : _this$props$target, | ||
sharedProps = _objectWithoutProperties(_this$props, ["onClick", "beforeNav", "safeWithNav", "href", "skipClientNav", "children", "tabIndex", "onKeyDown", "onKeyUp", "target"]); | ||
var ClickableBehavior = getClickableBehavior(href, skipClientNav, this.context.router); | ||
return /*#__PURE__*/createElement(ClickableBehavior, { | ||
disabled: false, | ||
href: href, | ||
role: "link", | ||
onClick: onClick, | ||
beforeNav: beforeNav, | ||
safeWithNav: safeWithNav, | ||
target: target, | ||
onKeyDown: onKeyDown, | ||
onKeyUp: onKeyUp | ||
}, function (state, _ref) { | ||
var clickableTabIndex = _ref.tabIndex, | ||
handlers = _objectWithoutProperties(_ref, ["tabIndex"]); | ||
return /*#__PURE__*/createElement(LinkCore, _extends({}, sharedProps, state, handlers, { | ||
skipClientNav: skipClientNav, | ||
if (beforeNav) { | ||
return /*#__PURE__*/createElement(ClickableBehavior, { | ||
disabled: false, | ||
href: href, | ||
target: target // If tabIndex is provide to the component we allow | ||
// it to override the tabIndex provide to use by | ||
// ClickableBehavior. | ||
, | ||
tabIndex: tabIndex || clickableTabIndex | ||
}), children); | ||
}); | ||
role: "link", | ||
onClick: onClick, | ||
beforeNav: beforeNav, | ||
safeWithNav: safeWithNav, | ||
onKeyDown: onKeyDown, | ||
onKeyUp: onKeyUp | ||
}, function (state, _ref) { | ||
var clickableTabIndex = _ref.tabIndex, | ||
childrenProps = _objectWithoutProperties(_ref, ["tabIndex"]); | ||
return /*#__PURE__*/createElement(LinkCore, _extends({}, sharedProps, state, childrenProps, { | ||
skipClientNav: skipClientNav, | ||
href: href, | ||
target: target // If tabIndex is provide to the component we allow | ||
// it to override the tabIndex provide to use by | ||
// ClickableBehavior. | ||
, | ||
tabIndex: tabIndex || clickableTabIndex | ||
}), children); | ||
}); | ||
} else { | ||
return /*#__PURE__*/createElement(ClickableBehavior, { | ||
disabled: false, | ||
href: href, | ||
role: "link", | ||
onClick: onClick, | ||
safeWithNav: safeWithNav, | ||
target: target, | ||
onKeyDown: onKeyDown, | ||
onKeyUp: onKeyUp | ||
}, function (state, _ref2) { | ||
var clickableTabIndex = _ref2.tabIndex, | ||
childrenProps = _objectWithoutProperties(_ref2, ["tabIndex"]); | ||
return /*#__PURE__*/createElement(LinkCore, _extends({}, sharedProps, state, childrenProps, { | ||
skipClientNav: skipClientNav, | ||
href: href, | ||
target: target // If tabIndex is provide to the component we allow | ||
// it to override the tabIndex provide to use by | ||
// ClickableBehavior. | ||
, | ||
tabIndex: tabIndex || clickableTabIndex | ||
}), children); | ||
}); | ||
} | ||
} | ||
@@ -402,0 +430,0 @@ }]); |
@@ -151,2 +151,6 @@ module.exports = | ||
// EXTERNAL MODULE: external "@khanacademy/wonder-blocks-color" | ||
var wonder_blocks_color_ = __webpack_require__(1); | ||
var wonder_blocks_color_default = /*#__PURE__*/__webpack_require__.n(wonder_blocks_color_); | ||
// CONCATENATED MODULE: ./packages/wonder-blocks-clickable/dist/es/index.js | ||
@@ -161,2 +165,3 @@ function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } | ||
function _classCallCheck(instance, Constructor) { | ||
@@ -485,4 +490,4 @@ if (!(instance instanceof Constructor)) { | ||
* `ClickableBehavior` accepts a function as `children` which is passed state | ||
* and an object containing event handlers. The `children` function should | ||
* return a clickable React Element of some sort. | ||
* and an object containing event handlers and some other props. The `children` | ||
* function should return a clickable React Element of some sort. | ||
* | ||
@@ -499,7 +504,7 @@ * Example: | ||
* > | ||
* {({hovered}, handlers) => | ||
* {({hovered}, childrenProps) => | ||
* <RoundRect | ||
* textcolor='white' | ||
* backgroundColor={hovered ? 'red' : 'blue'}} | ||
* {...handlers} | ||
* {...childrenProps} | ||
* > | ||
@@ -741,6 +746,12 @@ * {this.props.children} | ||
skipClientNav = _this$props4.skipClientNav, | ||
target = _this$props4.target; | ||
_this$props4$target = _this$props4.target, | ||
target = _this$props4$target === void 0 ? undefined : _this$props4$target; | ||
if (href) { | ||
if (history && !skipClientNav) { | ||
if (target === "_blank") { | ||
window.open(href, "_blank"); | ||
this.setState({ | ||
waiting: false | ||
}); | ||
} else if (history && !skipClientNav) { | ||
history.push(href); | ||
@@ -751,9 +762,4 @@ this.setState({ | ||
} else { | ||
if (target === "_blank") { | ||
window.open(href, "_blank"); | ||
} else { | ||
window.location.assign(href); | ||
} // We don't bother clearing the waiting state, the full page | ||
window.location.assign(href); // We don't bother clearing the waiting state, the full page | ||
// load navigation will do that for us by loading a new page. | ||
} | ||
@@ -776,3 +782,3 @@ } | ||
if (history && !skipClientNav) { | ||
if (history && !skipClientNav || this.props.target === "_blank") { | ||
// client-side nav | ||
@@ -878,3 +884,3 @@ safeWithNav(); | ||
value: function render() { | ||
var handlers = this.props.disabled ? disabledHandlers : { | ||
var childrenProps = this.props.disabled ? disabledHandlers : { | ||
onClick: this.handleClick, | ||
@@ -896,5 +902,10 @@ onMouseEnter: this.handleMouseEnter, | ||
tabIndex: 0 | ||
}; | ||
}; // When the link is set to open in a new window, we want to append some | ||
// `rel` attributes. This is to ensure that the links we're sending folks | ||
// to can't hijack the existing page. | ||
// More info: https://www.jitbit.com/alexblog/256-targetblank---the-most-underestimated-vulnerability-ever/ | ||
childrenProps.rel = this.props.rel || (this.props.target === "_blank" ? "noopener noreferrer" : undefined); | ||
var children = this.props.children; | ||
return children && children(this.state, handlers); | ||
return children && children(this.state, childrenProps); | ||
} | ||
@@ -1005,3 +1016,3 @@ }]); | ||
role: _this.props.role, | ||
target: _this.props.target, | ||
target: _this.props.target || undefined, | ||
"aria-disabled": _this.props.disabled ? "true" : undefined | ||
@@ -1013,3 +1024,3 @@ }), _this.props.children(clickableState)); | ||
role: _this.props.role, | ||
target: _this.props.target, | ||
target: _this.props.target || undefined, | ||
"aria-disabled": _this.props.disabled ? "true" : undefined | ||
@@ -1042,23 +1053,45 @@ }), _this.props.children(clickableState)); | ||
style = _this$props.style, | ||
target = _this$props.target, | ||
_this$props$target = _this$props.target, | ||
target = _this$props$target === void 0 ? undefined : _this$props$target, | ||
testId = _this$props.testId, | ||
onKeyDown = _this$props.onKeyDown, | ||
onKeyUp = _this$props.onKeyUp, | ||
restProps = _objectWithoutProperties(_this$props, ["href", "onClick", "skipClientNav", "beforeNav", "safeWithNav", "style", "target", "testId", "onKeyDown", "onKeyUp"]); | ||
hideDefaultFocusRing = _this$props.hideDefaultFocusRing, | ||
restProps = _objectWithoutProperties(_this$props, ["href", "onClick", "skipClientNav", "beforeNav", "safeWithNav", "style", "target", "testId", "onKeyDown", "onKeyUp", "hideDefaultFocusRing"]); | ||
var ClickableBehavior = getClickableBehavior(href, skipClientNav, this.context.router); | ||
return /*#__PURE__*/Object(external_react_["createElement"])(ClickableBehavior, { | ||
href: href, | ||
onClick: onClick, | ||
beforeNav: beforeNav, | ||
safeWithNav: safeWithNav, | ||
target: target, | ||
onKeyDown: onKeyDown, | ||
onKeyUp: onKeyUp | ||
}, function (state, handlers) { | ||
return _this2.getCorrectTag(state, _objectSpread2(_objectSpread2({}, restProps), {}, { | ||
"data-test-id": testId, | ||
style: [styles.reset, style] | ||
}, handlers)); | ||
}); | ||
var getStyle = function getStyle(state) { | ||
return [styles.reset, styles.link, !hideDefaultFocusRing && state.focused && styles.focused, style]; | ||
}; | ||
if (beforeNav) { | ||
return /*#__PURE__*/Object(external_react_["createElement"])(ClickableBehavior, { | ||
href: href, | ||
onClick: onClick, | ||
beforeNav: beforeNav, | ||
safeWithNav: safeWithNav, | ||
onKeyDown: onKeyDown, | ||
onKeyUp: onKeyUp | ||
}, function (state, childrenProps) { | ||
return _this2.getCorrectTag(state, _objectSpread2(_objectSpread2({}, restProps), {}, { | ||
"data-test-id": testId, | ||
style: getStyle(state) | ||
}, childrenProps)); | ||
}); | ||
} else { | ||
return /*#__PURE__*/Object(external_react_["createElement"])(ClickableBehavior, { | ||
href: href, | ||
onClick: onClick, | ||
safeWithNav: safeWithNav, | ||
onKeyDown: onKeyDown, | ||
onKeyUp: onKeyUp, | ||
target: target | ||
}, function (state, childrenProps) { | ||
return _this2.getCorrectTag(state, _objectSpread2(_objectSpread2({}, restProps), {}, { | ||
"data-test-id": testId, | ||
style: getStyle(state) | ||
}, childrenProps)); | ||
}); | ||
} | ||
} | ||
@@ -1108,2 +1141,8 @@ }]); | ||
MozOsxFontSmoothing: "inherit" | ||
}, | ||
link: { | ||
cursor: "pointer" | ||
}, | ||
focused: { | ||
outline: "solid 2px ".concat(wonder_blocks_color_default.a.blue) | ||
} | ||
@@ -1113,6 +1152,2 @@ }); | ||
// EXTERNAL MODULE: external "@khanacademy/wonder-blocks-color" | ||
var wonder_blocks_color_ = __webpack_require__(1); | ||
var wonder_blocks_color_default = /*#__PURE__*/__webpack_require__.n(wonder_blocks_color_); | ||
// CONCATENATED MODULE: ./packages/wonder-blocks-link/components/link-core.js | ||
@@ -1346,3 +1381,4 @@ function link_core_typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { link_core_typeof = function _typeof(obj) { return typeof obj; }; } else { link_core_typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return link_core_typeof(obj); } | ||
onClick = _this$props.onClick, | ||
beforeNav = _this$props.beforeNav, | ||
_this$props$beforeNav = _this$props.beforeNav, | ||
beforeNav = _this$props$beforeNav === void 0 ? undefined : _this$props$beforeNav, | ||
safeWithNav = _this$props.safeWithNav, | ||
@@ -1355,30 +1391,57 @@ href = _this$props.href, | ||
onKeyUp = _this$props.onKeyUp, | ||
target = _this$props.target, | ||
_this$props$target = _this$props.target, | ||
target = _this$props$target === void 0 ? undefined : _this$props$target, | ||
sharedProps = link_objectWithoutProperties(_this$props, ["onClick", "beforeNav", "safeWithNav", "href", "skipClientNav", "children", "tabIndex", "onKeyDown", "onKeyUp", "target"]); | ||
var ClickableBehavior = getClickableBehavior(href, skipClientNav, this.context.router); | ||
return /*#__PURE__*/external_react_["createElement"](ClickableBehavior, { | ||
disabled: false, | ||
href: href, | ||
role: "link", | ||
onClick: onClick, | ||
beforeNav: beforeNav, | ||
safeWithNav: safeWithNav, | ||
target: target, | ||
onKeyDown: onKeyDown, | ||
onKeyUp: onKeyUp | ||
}, function (state, _ref) { | ||
var clickableTabIndex = _ref.tabIndex, | ||
handlers = link_objectWithoutProperties(_ref, ["tabIndex"]); | ||
return /*#__PURE__*/external_react_["createElement"](link_core_LinkCore, link_extends({}, sharedProps, state, handlers, { | ||
skipClientNav: skipClientNav, | ||
if (beforeNav) { | ||
return /*#__PURE__*/external_react_["createElement"](ClickableBehavior, { | ||
disabled: false, | ||
href: href, | ||
target: target // If tabIndex is provide to the component we allow | ||
// it to override the tabIndex provide to use by | ||
// ClickableBehavior. | ||
, | ||
tabIndex: tabIndex || clickableTabIndex | ||
}), children); | ||
}); | ||
role: "link", | ||
onClick: onClick, | ||
beforeNav: beforeNav, | ||
safeWithNav: safeWithNav, | ||
onKeyDown: onKeyDown, | ||
onKeyUp: onKeyUp | ||
}, function (state, _ref) { | ||
var clickableTabIndex = _ref.tabIndex, | ||
childrenProps = link_objectWithoutProperties(_ref, ["tabIndex"]); | ||
return /*#__PURE__*/external_react_["createElement"](link_core_LinkCore, link_extends({}, sharedProps, state, childrenProps, { | ||
skipClientNav: skipClientNav, | ||
href: href, | ||
target: target // If tabIndex is provide to the component we allow | ||
// it to override the tabIndex provide to use by | ||
// ClickableBehavior. | ||
, | ||
tabIndex: tabIndex || clickableTabIndex | ||
}), children); | ||
}); | ||
} else { | ||
return /*#__PURE__*/external_react_["createElement"](ClickableBehavior, { | ||
disabled: false, | ||
href: href, | ||
role: "link", | ||
onClick: onClick, | ||
safeWithNav: safeWithNav, | ||
target: target, | ||
onKeyDown: onKeyDown, | ||
onKeyUp: onKeyUp | ||
}, function (state, _ref2) { | ||
var clickableTabIndex = _ref2.tabIndex, | ||
childrenProps = link_objectWithoutProperties(_ref2, ["tabIndex"]); | ||
return /*#__PURE__*/external_react_["createElement"](link_core_LinkCore, link_extends({}, sharedProps, state, childrenProps, { | ||
skipClientNav: skipClientNav, | ||
href: href, | ||
target: target // If tabIndex is provide to the component we allow | ||
// it to override the tabIndex provide to use by | ||
// ClickableBehavior. | ||
, | ||
tabIndex: tabIndex || clickableTabIndex | ||
}), children); | ||
}); | ||
} | ||
} | ||
@@ -1385,0 +1448,0 @@ }]); |
{ | ||
"name": "@khanacademy/wonder-blocks-link", | ||
"version": "3.6.3", | ||
"version": "3.7.0", | ||
"design": "v1", | ||
@@ -19,3 +19,3 @@ "publishConfig": { | ||
"@khanacademy/wonder-blocks-color": "^1.1.15", | ||
"@khanacademy/wonder-blocks-core": "^3.0.0" | ||
"@khanacademy/wonder-blocks-core": "^3.0.1" | ||
}, | ||
@@ -31,3 +31,3 @@ "peerDependencies": { | ||
}, | ||
"gitHead": "7aa04017e8c20479e33c5c0d6b59f8bd3f6379e9" | ||
"gitHead": "a8993049a08b602a0309155e4a1f4d969ef51c54" | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
124374
2485