@tds/core-button-group
Advanced tools
Comparing version 2.0.10 to 2.1.0
@@ -12,3 +12,2 @@ import React from 'react' | ||
import safeRest from '../../shared/utils/safeRest' | ||
import { warn } from '../../shared/utils/warn' | ||
@@ -24,40 +23,37 @@ const StyledButtonGroup = styled(Box)({ | ||
*/ | ||
const ButtonGroup = ({ name, onChange, onFocus, onBlur, value, label, children, ...rest }) => { | ||
const passedButtons = React.Children.map(children, child => | ||
React.cloneElement(child, { | ||
name, | ||
onChange, | ||
onFocus, | ||
onBlur, | ||
checked: value === child.props.value, | ||
const ButtonGroup = React.forwardRef( | ||
({ name, onChange, onFocus, onBlur, value, label, children, readOnly, ...rest }, ref) => { | ||
const passedButtons = React.Children.map(children, child => | ||
React.cloneElement(child, { | ||
name, | ||
onChange, | ||
onFocus, | ||
onBlur, | ||
checked: typeof value !== 'undefined' ? value === child.props.value : undefined, | ||
readOnly, | ||
}) | ||
) | ||
const buttonValues = [] | ||
Object.keys(passedButtons).forEach(key => { | ||
buttonValues.push(passedButtons[key].props.value) | ||
}) | ||
) | ||
const buttonValues = [] | ||
Object.keys(passedButtons).forEach(key => { | ||
buttonValues.push(passedButtons[key].props.value) | ||
}) | ||
return ( | ||
<fieldset {...safeRest(rest)} name={name} ref={ref}> | ||
<legend> | ||
<Text bold size="medium"> | ||
{label} | ||
</Text> | ||
</legend> | ||
if (buttonValues.indexOf(value) === -1) { | ||
warn( | ||
'ButtonGroup', | ||
`Selected value "${value}" of ButtonGroup named "${name}" does not match the value of any button in the group. A button must be selected by default. Available button values are: ${buttonValues}` | ||
<StyledButtonGroup between={3} inline> | ||
{passedButtons} | ||
</StyledButtonGroup> | ||
</fieldset> | ||
) | ||
} | ||
) | ||
ButtonGroup.displayName = 'ButtonGroup' | ||
return ( | ||
<fieldset {...safeRest(rest)} name={name}> | ||
<legend> | ||
<Text bold size="medium"> | ||
{label} | ||
</Text> | ||
</legend> | ||
<StyledButtonGroup between={3} inline> | ||
{passedButtons} | ||
</StyledButtonGroup> | ||
</fieldset> | ||
) | ||
} | ||
ButtonGroup.propTypes = { | ||
@@ -71,3 +67,3 @@ /** | ||
*/ | ||
value: PropTypes.string.isRequired, | ||
value: PropTypes.string, | ||
/** | ||
@@ -82,3 +78,3 @@ * A label to be displayed above the ButtonGroup. | ||
*/ | ||
onChange: PropTypes.func.isRequired, | ||
onChange: PropTypes.func, | ||
/** | ||
@@ -97,5 +93,11 @@ * A callback function to be invoked when a button receives focus. Passed into all buttons. | ||
/** | ||
* @ignore | ||
* | ||
* A callback function to be invoked when a button loses focus. Passed into all buttons. | ||
*/ | ||
readOnly: PropTypes.bool, | ||
/** | ||
* A group of ButtonGroup.Item components. | ||
*/ | ||
children: componentWithName('ButtonGroupItem').isRequired, | ||
children: componentWithName('ButtonGroup.Item', true).isRequired, | ||
} | ||
@@ -106,2 +108,5 @@ | ||
onBlur: undefined, | ||
onChange: undefined, | ||
value: undefined, | ||
readOnly: undefined, | ||
} | ||
@@ -108,0 +113,0 @@ |
@@ -7,3 +7,2 @@ ### Minimal usage | ||
- A single `ButtonGroup.Item` within `ButtonGroup` must be selected by default | ||
- Use `ButtonGroup` when one selection needs to be made from multiple options | ||
@@ -19,13 +18,25 @@ - Use when there can only be exactly one choice from multiple options | ||
### Controlled ButtonGroup | ||
If it is required that the state of the `ButtonGroup` be controlled by the application or other external methods, `value` and `onChange` props must be passed to the `ButtonGroup`. | ||
If the `ButtonGroup` should not be changed by user input, a `readOnly` prop must be provided. | ||
If none of the `ButtonGroup.Item` should be pre-selected then the `ButtonGroup` value must be `null`. | ||
```js | ||
initialState = { | ||
choice: '64gb', | ||
choice: null, | ||
} | ||
const onChange = (event) => { | ||
setState({choice: event.target.value}) | ||
const onChange = event => { | ||
setState({ choice: event.target.value }) | ||
} | ||
<ButtonGroup name="storageSize" onChange={onChange} value={state.choice} label="Please select a storage size"> | ||
;<ButtonGroup | ||
name="storageSize" | ||
onChange={onChange} | ||
value={state.choice} | ||
label="Please select a storage size" | ||
> | ||
<ButtonGroup.Item value="64gb">64 GB</ButtonGroup.Item> | ||
@@ -37,2 +48,19 @@ <ButtonGroup.Item value="128gb">128 GB</ButtonGroup.Item> | ||
### Uncontrolled ButtonGroup | ||
If it is not neccessary to control `ButtonGroup` state. You can create a `ButtonGroup` without a `value` prop, in this case the `ButtonGroup` will act as a collection HTML `input` with the type of `radio`. Its value can be accessed by referencing the element via a `ref`. | ||
#### Default values | ||
Due to the nature of uncontrolled components, you cannot set an initial `checked` property on the component. If you need to set a default state for your uncontrolled `ButtonGroup`, you can use the `defaultChecked` property on the default `ButtonGroup.Item` as described [in the react documentation](https://reactjs.org/docs/uncontrolled-components.html#default-values). | ||
```js | ||
<ButtonGroup name="tv" label="Choose your TV"> | ||
<ButtonGroup.Item defaultChecked value="Optik TV"> | ||
Optik TV | ||
</ButtonGroup.Item> | ||
<ButtonGroup.Item value="Pik TV">Pik TV</ButtonGroup.Item> | ||
</ButtonGroup> | ||
``` | ||
### Using A11yContent | ||
@@ -47,12 +75,22 @@ | ||
const onChange = (event) => { | ||
setState({choice: event.target.value}) | ||
const onChange = event => { | ||
setState({ choice: event.target.value }) | ||
} | ||
<ButtonGroup name="purposeOfVisit" onChange={onChange} value={state.choice} label="What was the purpose of your visit?"> | ||
<ButtonGroup.Item value="buy">Buy<A11yContent> mobile phones</A11yContent></ButtonGroup.Item> | ||
<ButtonGroup.Item value="inquiry">Inquiry <A11yContent> about mobile phones</A11yContent></ButtonGroup.Item> | ||
<ButtonGroup.Item value="tradeIn">Trade-In <A11yContent> mobile phones</A11yContent></ButtonGroup.Item> | ||
;<ButtonGroup | ||
name="purposeOfVisit" | ||
onChange={onChange} | ||
value={state.choice} | ||
label="What was the purpose of your visit?" | ||
> | ||
<ButtonGroup.Item value="buy"> | ||
Buy<A11yContent> mobile phones</A11yContent> | ||
</ButtonGroup.Item> | ||
<ButtonGroup.Item value="inquiry"> | ||
Inquiry <A11yContent> about mobile phones</A11yContent> | ||
</ButtonGroup.Item> | ||
<ButtonGroup.Item value="tradeIn"> | ||
Trade-In <A11yContent> mobile phones</A11yContent> | ||
</ButtonGroup.Item> | ||
</ButtonGroup> | ||
``` |
@@ -70,29 +70,39 @@ import React from 'react' | ||
const ButtonGroupItem = ({ | ||
name, | ||
value, | ||
checked, | ||
onChange, | ||
onFocus, | ||
onBlur, | ||
children, | ||
...rest | ||
}) => { | ||
const itemId = generateId(name).postfix(value) | ||
return ( | ||
<StyledButtonGroupItem {...safeRest(rest)}> | ||
<StyledInput | ||
id={itemId} | ||
name={name} | ||
value={value} | ||
type="radio" | ||
checked={checked} | ||
onChange={onChange} | ||
onFocus={onFocus} | ||
onBlur={onBlur} | ||
/> | ||
<StyledLabel htmlFor={itemId}>{children}</StyledLabel> | ||
</StyledButtonGroupItem> | ||
) | ||
} | ||
const ButtonGroupItem = React.forwardRef( | ||
( | ||
{ | ||
name, | ||
value, | ||
checked, | ||
onChange, | ||
onFocus, | ||
onBlur, | ||
children, | ||
defaultChecked, | ||
readOnly, | ||
...rest | ||
}, | ||
ref | ||
) => { | ||
const itemId = generateId(name).postfix(value) | ||
return ( | ||
<StyledButtonGroupItem {...safeRest(rest)}> | ||
<StyledInput | ||
id={itemId} | ||
name={name} | ||
value={value} | ||
type="radio" | ||
checked={checked} | ||
onChange={onChange} | ||
onFocus={onFocus} | ||
onBlur={onBlur} | ||
defaultChecked={defaultChecked} | ||
readOnly={readOnly} | ||
ref={ref} | ||
/> | ||
<StyledLabel htmlFor={itemId}>{children}</StyledLabel> | ||
</StyledButtonGroupItem> | ||
) | ||
} | ||
) | ||
@@ -135,2 +145,16 @@ ButtonGroupItem.propTypes = { | ||
/** | ||
*@ignore | ||
* | ||
* React defaultChecked | ||
* https://reactjs.org/docs/uncontrolled-components.html#default-values | ||
*/ | ||
defaultChecked: PropTypes.bool, | ||
/** | ||
*@ignore | ||
* | ||
* HTML readOnly | ||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#readonly | ||
*/ | ||
readOnly: PropTypes.bool, | ||
/** | ||
* The button's label. It can include the `A11yContent` component or strings. | ||
@@ -148,2 +172,4 @@ */ | ||
onBlur: undefined, | ||
defaultChecked: undefined, | ||
readOnly: undefined, | ||
} | ||
@@ -150,0 +176,0 @@ |
@@ -6,2 +6,14 @@ # Change Log | ||
# [2.1.0](https://github.com/telusdigital/tds-core/compare/@tds/core-button-group@2.0.10...@tds/core-button-group@2.1.0) (2019-08-12) | ||
### Features | ||
* **core-button-group:** allow ButtonGroup to be uncontrolled ([63e26c2](https://github.com/telusdigital/tds-core/commit/63e26c2)) | ||
* **core-button-group:** allow unselected by default ([ddb99b8](https://github.com/telusdigital/tds-core/commit/ddb99b8)) | ||
## [2.0.10](https://github.com/telusdigital/tds-core/compare/@tds/core-button-group@2.0.9...@tds/core-button-group@2.0.10) (2019-08-09) | ||
@@ -8,0 +20,0 @@ |
@@ -169,4 +169,3 @@ 'use strict'; | ||
}); | ||
var ButtonGroupItem = function ButtonGroupItem(_ref) { | ||
var ButtonGroupItem = React.forwardRef(function (_ref, ref) { | ||
var name = _ref.name, | ||
@@ -179,3 +178,5 @@ value = _ref.value, | ||
children = _ref.children, | ||
rest = objectWithoutProperties(_ref, ["name", "value", "checked", "onChange", "onFocus", "onBlur", "children"]); | ||
defaultChecked = _ref.defaultChecked, | ||
readOnly = _ref.readOnly, | ||
rest = objectWithoutProperties(_ref, ["name", "value", "checked", "onChange", "onFocus", "onBlur", "children", "defaultChecked", "readOnly"]); | ||
@@ -191,8 +192,10 @@ var itemId = generateId(name).postfix(value); | ||
onFocus: onFocus, | ||
onBlur: onBlur | ||
onBlur: onBlur, | ||
defaultChecked: defaultChecked, | ||
readOnly: readOnly, | ||
ref: ref | ||
}), React.createElement(StyledLabel, { | ||
htmlFor: itemId | ||
}, children)); | ||
}; | ||
}); | ||
ButtonGroupItem.propTypes = { | ||
@@ -240,2 +243,18 @@ /** | ||
/** | ||
*@ignore | ||
* | ||
* React defaultChecked | ||
* https://reactjs.org/docs/uncontrolled-components.html#default-values | ||
*/ | ||
defaultChecked: PropTypes.bool, | ||
/** | ||
*@ignore | ||
* | ||
* HTML readOnly | ||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#readonly | ||
*/ | ||
readOnly: PropTypes.bool, | ||
/** | ||
* The button's label. It can include the `A11yContent` component or strings. | ||
@@ -250,15 +269,8 @@ */ | ||
onFocus: undefined, | ||
onBlur: undefined | ||
onBlur: undefined, | ||
defaultChecked: undefined, | ||
readOnly: undefined | ||
}; | ||
ButtonGroupItem.displayName = 'ButtonGroup.Item'; | ||
/* eslint-disable import/prefer-default-export */ | ||
var warn = function warn(componentName, message) { | ||
if (process.env.NODE_ENV === 'production') { | ||
return; | ||
} | ||
console.warn("[TDS] ".concat(componentName, ": ").concat(message)); // eslint-disable-line no-console | ||
}; | ||
var StyledButtonGroup = styled(Box)({ | ||
@@ -273,3 +285,3 @@ flexFlow: 'row wrap', | ||
var ButtonGroup = function ButtonGroup(_ref) { | ||
var ButtonGroup = React.forwardRef(function (_ref, ref) { | ||
var name = _ref.name, | ||
@@ -282,3 +294,4 @@ onChange = _ref.onChange, | ||
children = _ref.children, | ||
rest = objectWithoutProperties(_ref, ["name", "onChange", "onFocus", "onBlur", "value", "label", "children"]); | ||
readOnly = _ref.readOnly, | ||
rest = objectWithoutProperties(_ref, ["name", "onChange", "onFocus", "onBlur", "value", "label", "children", "readOnly"]); | ||
@@ -291,3 +304,4 @@ var passedButtons = React.Children.map(children, function (child) { | ||
onBlur: onBlur, | ||
checked: value === child.props.value | ||
checked: typeof value !== 'undefined' ? value === child.props.value : undefined, | ||
readOnly: readOnly | ||
}); | ||
@@ -299,9 +313,5 @@ }); | ||
}); | ||
if (buttonValues.indexOf(value) === -1) { | ||
warn('ButtonGroup', "Selected value \"".concat(value, "\" of ButtonGroup named \"").concat(name, "\" does not match the value of any button in the group. A button must be selected by default. Available button values are: ").concat(buttonValues)); | ||
} | ||
return React.createElement("fieldset", _extends_1({}, safeRest(rest), { | ||
name: name | ||
name: name, | ||
ref: ref | ||
}), React.createElement("legend", null, React.createElement(Text, { | ||
@@ -314,4 +324,4 @@ bold: true, | ||
}, passedButtons)); | ||
}; | ||
}); | ||
ButtonGroup.displayName = 'ButtonGroup'; | ||
ButtonGroup.propTypes = { | ||
@@ -326,3 +336,3 @@ /** | ||
*/ | ||
value: PropTypes.string.isRequired, | ||
value: PropTypes.string, | ||
@@ -339,3 +349,3 @@ /** | ||
*/ | ||
onChange: PropTypes.func.isRequired, | ||
onChange: PropTypes.func, | ||
@@ -357,9 +367,19 @@ /** | ||
/** | ||
* @ignore | ||
* | ||
* A callback function to be invoked when a button loses focus. Passed into all buttons. | ||
*/ | ||
readOnly: PropTypes.bool, | ||
/** | ||
* A group of ButtonGroup.Item components. | ||
*/ | ||
children: utilPropTypes.componentWithName('ButtonGroupItem').isRequired | ||
children: utilPropTypes.componentWithName('ButtonGroup.Item', true).isRequired | ||
}; | ||
ButtonGroup.defaultProps = { | ||
onFocus: undefined, | ||
onBlur: undefined | ||
onBlur: undefined, | ||
onChange: undefined, | ||
value: undefined, | ||
readOnly: undefined | ||
}; | ||
@@ -366,0 +386,0 @@ ButtonGroup.Item = ButtonGroupItem; |
@@ -165,4 +165,3 @@ import React from 'react'; | ||
}); | ||
var ButtonGroupItem = function ButtonGroupItem(_ref) { | ||
var ButtonGroupItem = React.forwardRef(function (_ref, ref) { | ||
var name = _ref.name, | ||
@@ -175,3 +174,5 @@ value = _ref.value, | ||
children = _ref.children, | ||
rest = objectWithoutProperties(_ref, ["name", "value", "checked", "onChange", "onFocus", "onBlur", "children"]); | ||
defaultChecked = _ref.defaultChecked, | ||
readOnly = _ref.readOnly, | ||
rest = objectWithoutProperties(_ref, ["name", "value", "checked", "onChange", "onFocus", "onBlur", "children", "defaultChecked", "readOnly"]); | ||
@@ -187,8 +188,10 @@ var itemId = generateId(name).postfix(value); | ||
onFocus: onFocus, | ||
onBlur: onBlur | ||
onBlur: onBlur, | ||
defaultChecked: defaultChecked, | ||
readOnly: readOnly, | ||
ref: ref | ||
}), React.createElement(StyledLabel, { | ||
htmlFor: itemId | ||
}, children)); | ||
}; | ||
}); | ||
ButtonGroupItem.propTypes = { | ||
@@ -236,2 +239,18 @@ /** | ||
/** | ||
*@ignore | ||
* | ||
* React defaultChecked | ||
* https://reactjs.org/docs/uncontrolled-components.html#default-values | ||
*/ | ||
defaultChecked: PropTypes.bool, | ||
/** | ||
*@ignore | ||
* | ||
* HTML readOnly | ||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#readonly | ||
*/ | ||
readOnly: PropTypes.bool, | ||
/** | ||
* The button's label. It can include the `A11yContent` component or strings. | ||
@@ -246,15 +265,8 @@ */ | ||
onFocus: undefined, | ||
onBlur: undefined | ||
onBlur: undefined, | ||
defaultChecked: undefined, | ||
readOnly: undefined | ||
}; | ||
ButtonGroupItem.displayName = 'ButtonGroup.Item'; | ||
/* eslint-disable import/prefer-default-export */ | ||
var warn = function warn(componentName, message) { | ||
if (process.env.NODE_ENV === 'production') { | ||
return; | ||
} | ||
console.warn("[TDS] ".concat(componentName, ": ").concat(message)); // eslint-disable-line no-console | ||
}; | ||
var StyledButtonGroup = styled(Box)({ | ||
@@ -269,3 +281,3 @@ flexFlow: 'row wrap', | ||
var ButtonGroup = function ButtonGroup(_ref) { | ||
var ButtonGroup = React.forwardRef(function (_ref, ref) { | ||
var name = _ref.name, | ||
@@ -278,3 +290,4 @@ onChange = _ref.onChange, | ||
children = _ref.children, | ||
rest = objectWithoutProperties(_ref, ["name", "onChange", "onFocus", "onBlur", "value", "label", "children"]); | ||
readOnly = _ref.readOnly, | ||
rest = objectWithoutProperties(_ref, ["name", "onChange", "onFocus", "onBlur", "value", "label", "children", "readOnly"]); | ||
@@ -287,3 +300,4 @@ var passedButtons = React.Children.map(children, function (child) { | ||
onBlur: onBlur, | ||
checked: value === child.props.value | ||
checked: typeof value !== 'undefined' ? value === child.props.value : undefined, | ||
readOnly: readOnly | ||
}); | ||
@@ -295,9 +309,5 @@ }); | ||
}); | ||
if (buttonValues.indexOf(value) === -1) { | ||
warn('ButtonGroup', "Selected value \"".concat(value, "\" of ButtonGroup named \"").concat(name, "\" does not match the value of any button in the group. A button must be selected by default. Available button values are: ").concat(buttonValues)); | ||
} | ||
return React.createElement("fieldset", _extends_1({}, safeRest(rest), { | ||
name: name | ||
name: name, | ||
ref: ref | ||
}), React.createElement("legend", null, React.createElement(Text, { | ||
@@ -310,4 +320,4 @@ bold: true, | ||
}, passedButtons)); | ||
}; | ||
}); | ||
ButtonGroup.displayName = 'ButtonGroup'; | ||
ButtonGroup.propTypes = { | ||
@@ -322,3 +332,3 @@ /** | ||
*/ | ||
value: PropTypes.string.isRequired, | ||
value: PropTypes.string, | ||
@@ -335,3 +345,3 @@ /** | ||
*/ | ||
onChange: PropTypes.func.isRequired, | ||
onChange: PropTypes.func, | ||
@@ -353,9 +363,19 @@ /** | ||
/** | ||
* @ignore | ||
* | ||
* A callback function to be invoked when a button loses focus. Passed into all buttons. | ||
*/ | ||
readOnly: PropTypes.bool, | ||
/** | ||
* A group of ButtonGroup.Item components. | ||
*/ | ||
children: componentWithName('ButtonGroupItem').isRequired | ||
children: componentWithName('ButtonGroup.Item', true).isRequired | ||
}; | ||
ButtonGroup.defaultProps = { | ||
onFocus: undefined, | ||
onBlur: undefined | ||
onBlur: undefined, | ||
onChange: undefined, | ||
value: undefined, | ||
readOnly: undefined | ||
}; | ||
@@ -362,0 +382,0 @@ ButtonGroup.Item = ButtonGroupItem; |
{ | ||
"name": "@tds/core-button-group", | ||
"version": "2.0.10", | ||
"version": "2.1.0", | ||
"description": "An input component utilizing buttons that act as radios.", | ||
@@ -42,3 +42,3 @@ "main": "index.cjs.js", | ||
}, | ||
"gitHead": "56cee5f41176b371248ff7d0325ecfa998c04aa4" | ||
"gitHead": "99d776ba7523acbddc66f60227355bc275773b4c" | ||
} |
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
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
76391
1003
0