Socket
Socket
Sign inDemoInstall

@khanacademy/wonder-blocks-button

Package Overview
Dependencies
Maintainers
1
Versions
348
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@khanacademy/wonder-blocks-button - npm Package Compare versions

Comparing version 2.0.1 to 2.1.0

dist/es/index.js

61

components/button-core.js
// @flow
import React from "react";
import * as React from "react";
import {StyleSheet} from "aphrodite";

@@ -14,2 +14,4 @@ import {Link} from "react-router-dom";

import {addStyle} from "@khanacademy/wonder-blocks-core";
import {CircularSpinner} from "@khanacademy/wonder-blocks-progress-spinner";
import type {ClickableHandlers} from "@khanacademy/wonder-blocks-core";

@@ -33,8 +35,2 @@ import type {SharedProps} from "./button.js";

handleClick = (e: SyntheticEvent<>) => {
if (this.props.disabled) {
e.preventDefault();
}
};
render() {

@@ -45,3 +41,3 @@ const {

color,
disabled,
disabled: disabledProp,
focused,

@@ -56,2 +52,4 @@ hovered,

testId,
spinner,
"aria-label": ariaLabel,
...handlers

@@ -68,2 +66,4 @@ } = this.props;

const disabled = spinner || disabledProp;
const defaultStyle = [

@@ -83,3 +83,5 @@ sharedStyles.shared,

"aria-disabled": disabled ? "true" : undefined,
"aria-label": ariaLabel,
"data-test-id": testId,
role: "button",
style: [defaultStyle, style],

@@ -91,19 +93,17 @@ ...handlers,

const label = <Label style={sharedStyles.text}>{children}</Label>;
const label = (
<Label
style={[sharedStyles.text, spinner && sharedStyles.hiddenText]}
>
{children}
</Label>
);
if (href) {
return router && !skipClientNav ? (
<StyledLink
{...commonProps}
onClick={this.handleClick}
to={href}
>
<StyledLink {...commonProps} to={href}>
{label}
</StyledLink>
) : (
<StyledAnchor
{...commonProps}
onClick={this.handleClick}
href={href}
>
<StyledAnchor {...commonProps} href={href}>
{label}

@@ -114,4 +114,15 @@ </StyledAnchor>

return (
<StyledButton {...commonProps} disabled={disabled}>
<StyledButton
type="button"
{...commonProps}
disabled={disabled}
>
{label}
{spinner && (
<CircularSpinner
style={sharedStyles.spinner}
size={{medium: "small", small: "xsmall"}[size]}
light={kind === "primary"}
/>
)}
</StyledButton>

@@ -149,4 +160,14 @@ );

fontWeight: "bold",
userSelect: "none",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
pointerEvents: "none", // fix Safari bug where the browser was eating mouse events
},
hiddenText: {
visibility: "hidden",
},
spinner: {
position: "absolute",
},
});

@@ -153,0 +174,0 @@

@@ -6,2 +6,3 @@ // @flow

import {getClickableBehavior} from "@khanacademy/wonder-blocks-core";
import type {StyleType} from "@khanacademy/wonder-blocks-core";
import ButtonCore from "./button-core.js";

@@ -23,7 +24,17 @@

* If true, replaces the contents with a spinner.
*
* Note: setting this prop to `true` will disable the button.
*
* TODO(kevinb): support spinner + light once we have designs
*/
// TODO(yejia): Implement once spinner is implemented.
// spinner: boolean,
spinner: boolean,
/**
* This should be use when `spinner={true}` to let people using screen
* readers that the action taken by clicking the button will take some
* time to complete.
*/
"aria-label": string,
/**
* The color of the button, either blue or red.

@@ -89,5 +100,5 @@ */

/**
* The content of the modal, appearing between the titlebar and footer.
* Optional custom styles.
*/
style?: any,
style?: StyleType,
// TODO(yejia): use this if ADR #47 has been implemented

@@ -147,2 +158,4 @@ /*

disabled: false,
spinner: false,
"aria-label": "",
};

@@ -158,2 +171,4 @@

skipClientNav,
spinner,
disabled,
...sharedProps

@@ -170,5 +185,6 @@ } = this.props;

<ClickableBehavior
disabled={sharedProps.disabled}
disabled={spinner || disabled}
href={href}
onClick={onClick}
href={href}
role="button"
>

@@ -181,2 +197,4 @@ {(state, handlers) => {

{...handlers}
disabled={disabled}
spinner={spinner}
skipClientNav={skipClientNav}

@@ -183,0 +201,0 @@ href={href}

@@ -311,4 +311,32 @@ There are three `kind`s of buttons: `"primary"` (default), `"secondary"`, and

Buttons can show a `spinner`. This is useful when indicating to a user that
their input has been recognized but that the operation will take some time.
While the `spinner` property is set to `true` the button is disabled.
```jsx
const {View} = require("@khanacademy/wonder-blocks-core");
const {StyleSheet} = require("aphrodite");
const styles = StyleSheet.create({
row: {
flexDirection: "row",
alignItems: "center",
},
button: {
marginRight: 10,
}
});
<View style={styles.row}>
<Button spinner={true} aria-label="loading" style={styles.button}>
Click me!
</Button>
<Button spinner={true} aria-label="loading" size="small" style={styles.button}>
Click me!
</Button>
</View>
```
Buttons can have a `style` props which supports width, position, margin,
and flex styles:
and flex styles.

@@ -367,9 +395,10 @@ ### Best Practices

Layouts often specify a specific width of button. When implementing such
designs use `minWidth` instead of `width`. `minWidth` allows the button
to resize to fit the content whereas `width` does not. This is important
Layouts often specify a specific width of button. When implementing such
designs use `minWidth` instead of `width`. `minWidth` allows the button
to resize to fit the content whereas `width` does not. This is important
for international sites since sometimes strings for UI elements can be much
longer in other languages. Both of the buttons below have a "natural" width
of 144px. The one on the right is wider but it accommodates the full string
instead of wrapping or truncating it.
longer in other languages. Both of the buttons below have a "natural" width
of 144px. The one on the right is wider but it accommodates the full string
instead of wrapping it. Note that if the parent container of the button doesn't
have enough room to accommodate the width of the button, the text will truncate.
```jsx

@@ -437,1 +466,51 @@ const {View} = require("@khanacademy/wonder-blocks-core");

```
When an action is going to take a while, show a spinner during that time.
```jsx
const {View} = require("@khanacademy/wonder-blocks-core");
const {StyleSheet} = require("aphrodite");
const styles = StyleSheet.create({
row: {
flexDirection: "row",
},
button: {
marginRight: 10,
},
});
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
waiting: false,
}
}
componentWillUnmount() {
this.timeout.clear();
}
handleClick() {
this.setState({waiting: true});
this.timeout = setTimeout(() => {
this.setState({waiting: false});
}, 2000);
}
render() {
return <View style={styles.row}>
<Button
spinner={this.state.waiting}
aria-label={this.state.waiting ? "waiting" : ""}
onClick={() => this.handleClick()}
>
Click me!
</Button>
</View>
}
}
<Example />
```

@@ -51,2 +51,4 @@ // @flow

tabIndex={disabled ? -1 : 0}
spinner={false}
aria-label={""}
{...stateProps}

@@ -66,2 +68,34 @@ {...defaultHandlers}

}
for (const kind of ["primary", "secondary", "tertiary"]) {
for (const size of ["medium", "small"]) {
test(`kind:${kind} size:${size} spinner:true`, () => {
const spinner = true;
const disabled = spinner;
const stateProps = {
disabled,
focused: false,
hovered: false,
pressed: false,
};
const tree = renderer
.create(
<ButtonCore
kind={kind}
size={size}
color="default"
light={false}
tabIndex={disabled ? -1 : 0}
spinner={spinner}
aria-label={"loading"}
{...stateProps}
{...defaultHandlers}
>
Click me
</ButtonCore>,
)
.toJSON();
expect(tree).toMatchSnapshot();
});
}
}
});

@@ -85,3 +85,3 @@ module.exports =

/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 9);
/******/ return __webpack_require__(__webpack_require__.s = 10);
/******/ })

@@ -111,3 +111,3 @@ /************************************************************************/

module.exports = require("@khanacademy/wonder-blocks-color");
module.exports = require("@khanacademy/wonder-blocks-progress-spinner");

@@ -118,3 +118,3 @@ /***/ }),

module.exports = require("@khanacademy/wonder-blocks-typography");
module.exports = require("@khanacademy/wonder-blocks-color");

@@ -125,3 +125,3 @@ /***/ }),

module.exports = require("react-router-dom");
module.exports = require("@khanacademy/wonder-blocks-typography");

@@ -132,6 +132,12 @@ /***/ }),

module.exports = require("react-router-dom");
/***/ }),
/* 7 */
/***/ (function(module, exports) {
module.exports = require("aphrodite");
/***/ }),
/* 7 */
/* 8 */
/***/ (function(module, exports, __webpack_require__) {

@@ -152,7 +158,7 @@

var _react2 = _interopRequireDefault(_react);
var React = _interopRequireWildcard(_react);
var _aphrodite = __webpack_require__(6);
var _aphrodite = __webpack_require__(7);
var _reactRouterDom = __webpack_require__(5);
var _reactRouterDom = __webpack_require__(6);

@@ -163,5 +169,5 @@ var _propTypes = __webpack_require__(1);

var _wonderBlocksTypography = __webpack_require__(4);
var _wonderBlocksTypography = __webpack_require__(5);
var _wonderBlocksColor = __webpack_require__(3);
var _wonderBlocksColor = __webpack_require__(4);

@@ -172,4 +178,8 @@ var _wonderBlocksColor2 = _interopRequireDefault(_wonderBlocksColor);

var _wonderBlocksProgressSpinner = __webpack_require__(3);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }

@@ -191,17 +201,5 @@

function ButtonCore() {
var _ref;
var _temp, _this, _ret;
_classCallCheck(this, ButtonCore);
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = ButtonCore.__proto__ || Object.getPrototypeOf(ButtonCore)).call.apply(_ref, [this].concat(args))), _this), _this.handleClick = function (e) {
if (_this.props.disabled) {
e.preventDefault();
}
}, _temp), _possibleConstructorReturn(_this, _ret);
return _possibleConstructorReturn(this, (ButtonCore.__proto__ || Object.getPrototypeOf(ButtonCore)).apply(this, arguments));
}

@@ -216,3 +214,3 @@

color = _props.color,
disabled = _props.disabled,
disabledProp = _props.disabled,
focused = _props.focused,

@@ -227,3 +225,5 @@ hovered = _props.hovered,

testId = _props.testId,
handlers = _objectWithoutProperties(_props, ["children", "skipClientNav", "color", "disabled", "focused", "hovered", "href", "kind", "light", "pressed", "size", "style", "testId"]);
spinner = _props.spinner,
ariaLabel = _props["aria-label"],
handlers = _objectWithoutProperties(_props, ["children", "skipClientNav", "color", "disabled", "focused", "hovered", "href", "kind", "light", "pressed", "size", "style", "testId", "spinner", "aria-label"]);

@@ -237,2 +237,4 @@ var router = this.context.router;

var disabled = spinner || disabledProp;
var defaultStyle = [sharedStyles.shared, disabled && sharedStyles.disabled, buttonStyles.default, disabled && buttonStyles.disabled, !disabled && (pressed ? buttonStyles.active : (hovered || focused) && buttonStyles.focus), size === "small" && sharedStyles.small];

@@ -242,3 +244,5 @@

"aria-disabled": disabled ? "true" : undefined,
"aria-label": ariaLabel,
"data-test-id": testId,
role: "button",
style: [defaultStyle, style]

@@ -249,5 +253,7 @@ }, handlers);

var label = _react2.default.createElement(
var label = React.createElement(
Label,
{ style: sharedStyles.text },
{
style: [sharedStyles.text, spinner && sharedStyles.hiddenText]
},
children

@@ -257,22 +263,25 @@ );

if (href) {
return router && !skipClientNav ? _react2.default.createElement(
return router && !skipClientNav ? React.createElement(
StyledLink,
_extends({}, commonProps, {
onClick: this.handleClick,
to: href
}),
_extends({}, commonProps, { to: href }),
label
) : _react2.default.createElement(
) : React.createElement(
StyledAnchor,
_extends({}, commonProps, {
onClick: this.handleClick,
href: href
}),
_extends({}, commonProps, { href: href }),
label
);
} else {
return _react2.default.createElement(
return React.createElement(
StyledButton,
_extends({}, commonProps, { disabled: disabled }),
label
_extends({
type: "button"
}, commonProps, {
disabled: disabled
}),
label,
spinner && React.createElement(_wonderBlocksProgressSpinner.CircularSpinner, {
style: sharedStyles.spinner,
size: { medium: "small", small: "xsmall" }[size],
light: kind === "primary"
})
);

@@ -284,3 +293,3 @@ }

return ButtonCore;
}(_react2.default.Component);
}(React.Component);

@@ -317,3 +326,13 @@ ButtonCore.contextTypes = { router: _propTypes2.default.any };

fontWeight: "bold",
userSelect: "none",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
pointerEvents: "none" // fix Safari bug where the browser was eating mouse events
},
hiddenText: {
visibility: "hidden"
},
spinner: {
position: "absolute"
}

@@ -439,3 +458,3 @@ });

/***/ }),
/* 8 */
/* 9 */
/***/ (function(module, exports, __webpack_require__) {

@@ -464,3 +483,3 @@

var _buttonCore = __webpack_require__(7);
var _buttonCore = __webpack_require__(8);

@@ -515,3 +534,5 @@ var _buttonCore2 = _interopRequireDefault(_buttonCore);

skipClientNav = _props.skipClientNav,
sharedProps = _objectWithoutProperties(_props, ["onClick", "href", "children", "skipClientNav"]);
spinner = _props.spinner,
disabled = _props.disabled,
sharedProps = _objectWithoutProperties(_props, ["onClick", "href", "children", "skipClientNav", "spinner", "disabled"]);

@@ -523,5 +544,6 @@ var ClickableBehavior = (0, _wonderBlocksCore.getClickableBehavior)(href, skipClientNav, this.context.router);

{
disabled: sharedProps.disabled,
disabled: spinner || disabled,
href: href,
onClick: onClick,
href: href
role: "button"
},

@@ -532,2 +554,4 @@ function (state, handlers) {

_extends({}, sharedProps, state, handlers, {
disabled: disabled,
spinner: spinner,
skipClientNav: skipClientNav,

@@ -551,3 +575,5 @@ href: href

size: "medium",
disabled: false
disabled: false,
spinner: false,
"aria-label": ""
};

@@ -558,3 +584,3 @@ Button.contextTypes = { router: _propTypes2.default.any };

/***/ }),
/* 9 */
/* 10 */
/***/ (function(module, exports, __webpack_require__) {

@@ -570,3 +596,3 @@

var _button = __webpack_require__(8);
var _button = __webpack_require__(9);

@@ -573,0 +599,0 @@ var _button2 = _interopRequireDefault(_button);

@@ -331,4 +331,40 @@ // This file is auto-generated by gen-snapshot-tests.js

const {View} = require("@khanacademy/wonder-blocks-core");
const {StyleSheet} = require("aphrodite");
const styles = StyleSheet.create({
row: {
flexDirection: "row",
alignItems: "center",
},
button: {
marginRight: 10,
},
});
const example = (
<View style={styles.row}>
<Button
spinner={true}
aria-label="loading"
style={styles.button}
>
Click me!
</Button>
<Button
spinner={true}
aria-label="loading"
size="small"
style={styles.button}
>
Click me!
</Button>
</View>
);
const tree = renderer.create(example).toJSON();
expect(tree).toMatchSnapshot();
});
it("example 9", () => {
const {View} = require("@khanacademy/wonder-blocks-core");
const example = (
<View>

@@ -341,3 +377,3 @@ <Button>Label</Button>

});
it("example 9", () => {
it("example 10", () => {
const {View} = require("@khanacademy/wonder-blocks-core");

@@ -375,3 +411,3 @@ const {StyleSheet} = require("aphrodite");

});
it("example 10", () => {
it("example 11", () => {
const {View} = require("@khanacademy/wonder-blocks-core");

@@ -406,3 +442,3 @@ const {StyleSheet} = require("aphrodite");

});
it("example 11", () => {
it("example 12", () => {
const {View} = require("@khanacademy/wonder-blocks-core");

@@ -433,2 +469,53 @@ const {StyleSheet} = require("aphrodite");

});
it("example 13", () => {
const {View} = require("@khanacademy/wonder-blocks-core");
const {StyleSheet} = require("aphrodite");
const styles = StyleSheet.create({
row: {
flexDirection: "row",
},
button: {
marginRight: 10,
},
});
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
waiting: false,
};
}
componentWillUnmount() {
this.timeout.clear();
}
handleClick() {
this.setState({waiting: true});
this.timeout = setTimeout(() => {
this.setState({waiting: false});
}, 2000);
}
render() {
return (
<View style={styles.row}>
<Button
spinner={this.state.waiting}
aria-label={this.state.waiting ? "waiting" : ""}
onClick={() => this.handleClick()}
>
Click me!
</Button>
</View>
);
}
}
const example = <Example />;
const tree = renderer.create(example).toJSON();
expect(tree).toMatchSnapshot();
});
});
{
"name": "@khanacademy/wonder-blocks-button",
"version": "2.0.1",
"version": "2.1.0",
"design": "v1",

@@ -10,2 +10,3 @@ "publishConfig": {

"main": "dist/index.js",
"module": "dist/es/index.js",
"source": "index.js",

@@ -18,5 +19,6 @@ "scripts": {

"dependencies": {
"@khanacademy/wonder-blocks-color": "^1.0.6",
"@khanacademy/wonder-blocks-core": "^1.1.1",
"@khanacademy/wonder-blocks-typography": "^1.0.6"
"@khanacademy/wonder-blocks-color": "^1.0.7",
"@khanacademy/wonder-blocks-core": "^1.2.0",
"@khanacademy/wonder-blocks-progress-spinner": "^1.0.4",
"@khanacademy/wonder-blocks-typography": "^1.0.7"
},

@@ -23,0 +25,0 @@ "peerDependencies": {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc