@khanacademy/wonder-blocks-link
Advanced tools
Comparing version 1.0.1 to 2.0.0
@@ -5,2 +5,3 @@ // @flow | ||
import {Link} from "react-router-dom"; | ||
import PropTypes from "prop-types"; | ||
@@ -23,20 +24,23 @@ import {addStyle} from "@khanacademy/wonder-blocks-core"; | ||
const StyledAnchor = addStyle("a"); | ||
// $FlowFixMe: pass props directly to StyledLink instead of to Tag | ||
const StyledLink = addStyle(Link); | ||
export default class LinkCore extends React.Component<Props> { | ||
getProps() { | ||
static contextTypes = {router: PropTypes.any}; | ||
render() { | ||
const { | ||
caret, // eslint-disable-line no-unused-vars | ||
children, | ||
skipClientNav, | ||
focused, | ||
hovered, | ||
href, | ||
kind, | ||
light, | ||
pressed, | ||
style, | ||
testId, | ||
style, | ||
hovered, | ||
focused, | ||
pressed, | ||
href, | ||
clientNav, | ||
...handlers | ||
} = this.props; | ||
const {router} = this.context; | ||
@@ -53,26 +57,18 @@ const linkStyles = _generateStyles(kind, light); | ||
const props = { | ||
const commonProps = { | ||
"data-test-id": testId, | ||
style: [defaultStyles, style], | ||
"data-test-id": testId, | ||
...handlers, | ||
}; | ||
if (clientNav) { | ||
// $FlowFixMe | ||
props.to = href; | ||
} else { | ||
// $FlowFixMe | ||
props.href = href; | ||
} | ||
return props; | ||
return router && !skipClientNav ? ( | ||
<StyledLink {...commonProps} to={href}> | ||
{children} | ||
</StyledLink> | ||
) : ( | ||
<StyledAnchor {...commonProps} href={href}> | ||
{children} | ||
</StyledAnchor> | ||
); | ||
} | ||
render() { | ||
const {children, clientNav} = this.props; | ||
const Tag = clientNav ? StyledLink : StyledAnchor; | ||
return <Tag {...this.getProps()}>{children}</Tag>; | ||
} | ||
} | ||
@@ -121,2 +117,3 @@ | ||
color: light ? mix(fade(blue, 0.32), white) : mix(offBlack32, blue), | ||
textDecoration: "underline currentcolor solid", | ||
":visited": { | ||
@@ -123,0 +120,0 @@ color: light |
// @flow | ||
import * as React from "react"; | ||
import PropTypes from "prop-types"; | ||
@@ -25,3 +26,3 @@ import LinkCore from "./link-core.js"; | ||
/** | ||
* Kind of Link. Note: Secondary light Links ar enot supported. | ||
* Kind of Link. Note: Secondary light Links are not supported. | ||
*/ | ||
@@ -41,7 +42,8 @@ kind: "primary" | "secondary", | ||
/** | ||
* Whether to use client-side navigation. | ||
* Whether to avoid using client-side navigation. | ||
* | ||
* If the URL passed to href is local to the client-side, e.g. | ||
* /math/algebra/eval-exprs, then it uses react-router-dom's Link | ||
* component which handles the client-side navigation. | ||
* /math/algebra/eval-exprs, then it tries to use react-router-dom's Link | ||
* component which handles the client-side navigation. You can set | ||
* `skipClientNav` to true avoid using client-side nav entirely. | ||
* | ||
@@ -52,3 +54,3 @@ * NOTE: All URLs containing a protocol are considered external, e.g. | ||
*/ | ||
clientNav?: boolean, | ||
skipClientNav?: boolean, | ||
@@ -68,6 +70,7 @@ /** | ||
*/ | ||
|}; | ||
type Props = {| | ||
...SharedProps, | ||
// NOTE(jeresig): Currently React Docgen (used by Styleguidist) doesn't | ||
// support ... inside of an exact object type. Thus we had to move the | ||
// following propers into this SharedProps, even though they should be | ||
// external. Once that's fixed we can split them back apart. | ||
@@ -103,3 +106,3 @@ /** | ||
*/ | ||
export default class Link extends React.Component<Props> { | ||
export default class Link extends React.Component<SharedProps> { | ||
static defaultProps = { | ||
@@ -111,8 +114,16 @@ caret: false, | ||
static contextTypes = {router: PropTypes.any}; | ||
render() { | ||
const {onClick, href, clientNav, children, ...sharedProps} = this.props; | ||
const { | ||
onClick, | ||
href, | ||
skipClientNav, | ||
children, | ||
...sharedProps | ||
} = this.props; | ||
const ClickableBehavior = getClickableBehavior( | ||
href, | ||
clientNav, | ||
skipClientNav, | ||
this.context.router, | ||
@@ -129,3 +140,3 @@ ); | ||
{...handlers} | ||
clientNav={clientNav} | ||
skipClientNav={skipClientNav} | ||
href={href} | ||
@@ -132,0 +143,0 @@ > |
// @flow | ||
import React from "react"; | ||
import {mount} from "enzyme"; | ||
import {MemoryRouter, Route, Switch} from "react-router-dom"; | ||
import {mount, unmountAll} from "../../../utils/testing/mount.js"; | ||
import Link from "./link.js"; | ||
describe("Link", () => { | ||
test("TODO", () => { | ||
const wrapper = mount(<Link href="#">Hello World!</Link>); | ||
wrapper.simulate("click"); | ||
beforeEach(() => { | ||
unmountAll(); | ||
}); | ||
}); | ||
test("client-side navigation", () => { | ||
// Arrange | ||
const wrapper = mount( | ||
<MemoryRouter> | ||
<div> | ||
<Link testId="link" href="/foo"> | ||
Click me! | ||
</Link> | ||
<Switch> | ||
<Route path="/foo"> | ||
<div id="foo">Hello, world!</div> | ||
</Route> | ||
</Switch> | ||
</div> | ||
</MemoryRouter>, | ||
); | ||
// Act | ||
const buttonWrapper = wrapper.find(`[data-test-id="link"]`).first(); | ||
buttonWrapper.simulate("click", {button: 0}); | ||
// Assert | ||
expect(wrapper.find("#foo").exists()).toBe(true); | ||
}); | ||
test("client-side navigation with unknown URL fails", () => { | ||
// Arrange | ||
const wrapper = mount( | ||
<MemoryRouter> | ||
<div> | ||
<Link testId="link" href="/unknown"> | ||
Click me! | ||
</Link> | ||
<Switch> | ||
<Route path="/foo"> | ||
<div id="foo">Hello, world!</div> | ||
</Route> | ||
</Switch> | ||
</div> | ||
</MemoryRouter>, | ||
); | ||
// Act | ||
const buttonWrapper = wrapper.find(`[data-test-id="link"]`).first(); | ||
buttonWrapper.simulate("click", {button: 0}); | ||
// Assert | ||
expect(wrapper.find("#foo").exists()).toBe(false); | ||
}); | ||
test("client-side navigation with `skipClientNav` set to `true` fails", () => { | ||
// Arrange | ||
const wrapper = mount( | ||
<MemoryRouter> | ||
<div> | ||
<Link testId="link" href="/foo" skipClientNav> | ||
Click me! | ||
</Link> | ||
<Switch> | ||
<Route path="/foo"> | ||
<div id="foo">Hello, world!</div> | ||
</Route> | ||
</Switch> | ||
</div> | ||
</MemoryRouter>, | ||
); | ||
// Act | ||
const buttonWrapper = wrapper.find(`[data-test-id="link"]`).first(); | ||
buttonWrapper.simulate("click", {button: 0}); | ||
// Assert | ||
expect(wrapper.find("#foo").exists()).toBe(false); | ||
}); | ||
}); |
@@ -18,3 +18,5 @@ // @flow | ||
onKeyUp: () => void 0, | ||
onFocus: () => void 0, | ||
onBlur: () => void 0, | ||
tabIndex: 0, | ||
}; | ||
@@ -44,3 +46,2 @@ | ||
{...defaultHandlers} | ||
tabIndex={0} | ||
> | ||
@@ -47,0 +48,0 @@ Click me |
@@ -85,3 +85,3 @@ module.exports = | ||
/******/ // Load entry module and return exports | ||
/******/ return __webpack_require__(__webpack_require__.s = 7); | ||
/******/ return __webpack_require__(__webpack_require__.s = 8); | ||
/******/ }) | ||
@@ -99,3 +99,3 @@ /************************************************************************/ | ||
module.exports = require("react"); | ||
module.exports = require("prop-types"); | ||
@@ -106,3 +106,3 @@ /***/ }), | ||
module.exports = require("@khanacademy/wonder-blocks-color"); | ||
module.exports = require("react"); | ||
@@ -113,3 +113,3 @@ /***/ }), | ||
module.exports = require("react-router-dom"); | ||
module.exports = require("@khanacademy/wonder-blocks-color"); | ||
@@ -120,6 +120,12 @@ /***/ }), | ||
module.exports = require("react-router-dom"); | ||
/***/ }), | ||
/* 5 */ | ||
/***/ (function(module, exports) { | ||
module.exports = require("aphrodite"); | ||
/***/ }), | ||
/* 5 */ | ||
/* 6 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
@@ -138,13 +144,17 @@ | ||
var _react = __webpack_require__(1); | ||
var _react = __webpack_require__(2); | ||
var _react2 = _interopRequireDefault(_react); | ||
var _aphrodite = __webpack_require__(4); | ||
var _aphrodite = __webpack_require__(5); | ||
var _reactRouterDom = __webpack_require__(3); | ||
var _reactRouterDom = __webpack_require__(4); | ||
var _propTypes = __webpack_require__(1); | ||
var _propTypes2 = _interopRequireDefault(_propTypes); | ||
var _wonderBlocksCore = __webpack_require__(0); | ||
var _wonderBlocksColor = __webpack_require__(2); | ||
var _wonderBlocksColor = __webpack_require__(3); | ||
@@ -164,3 +174,2 @@ var _wonderBlocksColor2 = _interopRequireDefault(_wonderBlocksColor); | ||
var StyledAnchor = (0, _wonderBlocksCore.addStyle)("a"); | ||
// $FlowFixMe: pass props directly to StyledLink instead of to Tag | ||
var StyledLink = (0, _wonderBlocksCore.addStyle)(_reactRouterDom.Link); | ||
@@ -178,17 +187,21 @@ | ||
_createClass(LinkCore, [{ | ||
key: "getProps", | ||
value: function getProps() { | ||
key: "render", | ||
value: function render() { | ||
var _props = this.props, | ||
caret = _props.caret, | ||
children = _props.children, | ||
skipClientNav = _props.skipClientNav, | ||
focused = _props.focused, | ||
hovered = _props.hovered, | ||
href = _props.href, | ||
kind = _props.kind, | ||
light = _props.light, | ||
pressed = _props.pressed, | ||
style = _props.style, | ||
testId = _props.testId, | ||
style = _props.style, | ||
hovered = _props.hovered, | ||
focused = _props.focused, | ||
pressed = _props.pressed, | ||
href = _props.href, | ||
clientNav = _props.clientNav, | ||
handlers = _objectWithoutProperties(_props, ["caret", "kind", "light", "testId", "style", "hovered", "focused", "pressed", "href", "clientNav"]); | ||
handlers = _objectWithoutProperties(_props, ["caret", "children", "skipClientNav", "focused", "hovered", "href", "kind", "light", "pressed", "style", "testId"]); | ||
var router = this.context.router; | ||
var linkStyles = _generateStyles(kind, light); | ||
@@ -198,31 +211,15 @@ | ||
var props = _extends({ | ||
style: [defaultStyles, style], | ||
"data-test-id": testId | ||
var commonProps = _extends({ | ||
"data-test-id": testId, | ||
style: [defaultStyles, style] | ||
}, handlers); | ||
if (clientNav) { | ||
// $FlowFixMe | ||
props.to = href; | ||
} else { | ||
// $FlowFixMe | ||
props.href = href; | ||
} | ||
return props; | ||
} | ||
}, { | ||
key: "render", | ||
value: function render() { | ||
var _props2 = this.props, | ||
children = _props2.children, | ||
clientNav = _props2.clientNav; | ||
var Tag = clientNav ? StyledLink : StyledAnchor; | ||
return _react2.default.createElement( | ||
Tag, | ||
this.getProps(), | ||
return router && !skipClientNav ? _react2.default.createElement( | ||
StyledLink, | ||
_extends({}, commonProps, { to: href }), | ||
children | ||
) : _react2.default.createElement( | ||
StyledAnchor, | ||
_extends({}, commonProps, { href: href }), | ||
children | ||
); | ||
@@ -235,2 +232,3 @@ } | ||
LinkCore.contextTypes = { router: _propTypes2.default.any }; | ||
exports.default = LinkCore; | ||
@@ -284,2 +282,3 @@ | ||
color: light ? (0, _wonderBlocksColor.mix)((0, _wonderBlocksColor.fade)(blue, 0.32), white) : (0, _wonderBlocksColor.mix)(offBlack32, blue), | ||
textDecoration: "underline currentcolor solid", | ||
":visited": { | ||
@@ -296,3 +295,3 @@ color: light ? (0, _wonderBlocksColor.mix)((0, _wonderBlocksColor.fade)(blue, 0.32), white) : (0, _wonderBlocksColor.mix)(offBlack32, linkPurple) | ||
/***/ }), | ||
/* 6 */ | ||
/* 7 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
@@ -311,8 +310,12 @@ | ||
var _react = __webpack_require__(1); | ||
var _react = __webpack_require__(2); | ||
var React = _interopRequireWildcard(_react); | ||
var _linkCore = __webpack_require__(5); | ||
var _propTypes = __webpack_require__(1); | ||
var _propTypes2 = _interopRequireDefault(_propTypes); | ||
var _linkCore = __webpack_require__(6); | ||
var _linkCore2 = _interopRequireDefault(_linkCore); | ||
@@ -366,7 +369,7 @@ | ||
href = _props.href, | ||
clientNav = _props.clientNav, | ||
skipClientNav = _props.skipClientNav, | ||
children = _props.children, | ||
sharedProps = _objectWithoutProperties(_props, ["onClick", "href", "clientNav", "children"]); | ||
sharedProps = _objectWithoutProperties(_props, ["onClick", "href", "skipClientNav", "children"]); | ||
var ClickableBehavior = (0, _wonderBlocksCore.getClickableBehavior)(href, clientNav, this.context.router); | ||
var ClickableBehavior = (0, _wonderBlocksCore.getClickableBehavior)(href, skipClientNav, this.context.router); | ||
@@ -380,3 +383,3 @@ return React.createElement( | ||
_extends({}, sharedProps, state, handlers, { | ||
clientNav: clientNav, | ||
skipClientNav: skipClientNav, | ||
href: href | ||
@@ -399,6 +402,7 @@ }), | ||
}; | ||
Link.contextTypes = { router: _propTypes2.default.any }; | ||
exports.default = Link; | ||
/***/ }), | ||
/* 7 */ | ||
/* 8 */ | ||
/***/ (function(module, exports, __webpack_require__) { | ||
@@ -414,3 +418,3 @@ | ||
var _link = __webpack_require__(6); | ||
var _link = __webpack_require__(7); | ||
@@ -417,0 +421,0 @@ var _link2 = _interopRequireDefault(_link); |
@@ -15,8 +15,37 @@ // This file is auto-generated by gen-snapshot-tests.js | ||
it("example 1", () => { | ||
const Color = require("@khanacademy/wonder-blocks-color").default; | ||
const {View} = require("@khanacademy/wonder-blocks-core"); | ||
const example = ( | ||
<p> | ||
Lorem ipsum <Link href="#nonexistent-link">Link</Link> dolor sit | ||
amet, consectetur <Link href="#">Visited Link</Link> adipiscing | ||
elit | ||
</p> | ||
<View> | ||
<p> | ||
I am a <Link href="#nonexistent-link">Primary Link</Link>.{" "} | ||
<span style={{color: Color.offBlack64}}> | ||
My friend the | ||
<Link | ||
href="#secondary-nonexistent-link" | ||
kind="secondary" | ||
> | ||
Secondary Link | ||
</Link>{" "} | ||
is used here with a lighter text. | ||
</span>{" "} | ||
We also have a | ||
<Link href="#">Visited Link</Link> friend. | ||
</p> | ||
<p | ||
style={{ | ||
backgroundColor: Color.darkBlue, | ||
color: Color.white64, | ||
padding: 10, | ||
}} | ||
> | ||
I am a{" "} | ||
<Link href="#dark-link" light={true}> | ||
Primary Link | ||
</Link>{" "} | ||
used on a dark background. My friend the Secondary Link | ||
isn't supported on this dark background. | ||
</p> | ||
</View> | ||
); | ||
@@ -23,0 +52,0 @@ const tree = renderer.create(example).toJSON(); |
{ | ||
"name": "@khanacademy/wonder-blocks-link", | ||
"version": "1.0.1", | ||
"version": "2.0.0", | ||
"design": "v1", | ||
@@ -17,5 +17,11 @@ "publishConfig": { | ||
"dependencies": { | ||
"@khanacademy/wonder-blocks-color": "^1.0.4", | ||
"@khanacademy/wonder-blocks-core": "^1.0.4" | ||
"@khanacademy/wonder-blocks-color": "^1.0.5", | ||
"@khanacademy/wonder-blocks-core": "^1.1.0" | ||
}, | ||
"peerDependencies": { | ||
"aphrodite": "^1.2.5", | ||
"prop-types": "^15.6.2", | ||
"react": "^16.4.1", | ||
"react-router-dom": "^4.2.2" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
48102
14
757
6