mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Rewrite checkbox component (#8305)
This new checkbox component uses a plain `input` component internally, so the browser treats it like a native checkbox. It is styled by hiding the native checkbox and replacing it with Font Awesome icons (the same that we are using in Figma). Support for a 'disabled' state and an indeterminate state has been added as well. The `onClick` prop has been made optional, as it may not be required if the parent component is intercepting the click instead. The `regular` Font Awesome font style needed to be added so that we could use the `far fa-square` icon for the unchecked checkbox.
This commit is contained in:
parent
b0b99fa748
commit
b628ff05d1
@ -1,36 +1,60 @@
|
||||
import React, { PureComponent } from 'react'
|
||||
import React, { useLayoutEffect, useRef } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
|
||||
export default class CheckBox extends PureComponent {
|
||||
static propTypes = {
|
||||
className: PropTypes.string,
|
||||
checked: PropTypes.bool,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
className: '',
|
||||
checked: false,
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className, checked, onClick } = this.props
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={ () => onClick() }
|
||||
className={classnames('check-box', className, {
|
||||
'check-box--checked': checked,
|
||||
'check-box--un-checked': !checked,
|
||||
})}
|
||||
>
|
||||
{
|
||||
checked
|
||||
? <i className="fa fa-check" />
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
const CHECKBOX_STATE = {
|
||||
CHECKED: 'CHECKED',
|
||||
INDETERMINATE: 'INDETERMINATE',
|
||||
UNCHECKED: 'UNCHECKED',
|
||||
}
|
||||
|
||||
export const { CHECKED, INDETERMINATE, UNCHECKED } = CHECKBOX_STATE
|
||||
|
||||
const CheckBox = ({ className, disabled, onClick, checked }) => {
|
||||
if (typeof checked === 'boolean') {
|
||||
checked = checked
|
||||
? CHECKBOX_STATE.CHECKED
|
||||
: CHECKBOX_STATE.UNCHECKED
|
||||
}
|
||||
const ref = useRef(null)
|
||||
useLayoutEffect(() => {
|
||||
ref.current.indeterminate = checked === CHECKBOX_STATE.INDETERMINATE
|
||||
}, [checked])
|
||||
|
||||
return (
|
||||
<input
|
||||
checked={checked === CHECKBOX_STATE.CHECKED}
|
||||
className={classnames('check-box', className, {
|
||||
'far fa-square': checked === CHECKBOX_STATE.UNCHECKED,
|
||||
'fa fa-check-square': checked === CHECKBOX_STATE.CHECKED,
|
||||
'fa fa-minus-square': checked === CHECKBOX_STATE.INDETERMINATE,
|
||||
})}
|
||||
disabled={disabled}
|
||||
onClick={
|
||||
onClick
|
||||
? (event) => {
|
||||
event.preventDefault()
|
||||
onClick()
|
||||
}
|
||||
: null
|
||||
}
|
||||
readOnly
|
||||
ref={ref}
|
||||
type="checkbox"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
CheckBox.propTypes = {
|
||||
className: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
checked: PropTypes.oneOf([...Object.keys(CHECKBOX_STATE), true, false]).isRequired,
|
||||
}
|
||||
|
||||
CheckBox.defaultProps = {
|
||||
className: undefined,
|
||||
disabled: false,
|
||||
}
|
||||
|
||||
export default CheckBox
|
||||
|
24
ui/app/components/ui/check-box/check-box.stories.js
Normal file
24
ui/app/components/ui/check-box/check-box.stories.js
Normal file
@ -0,0 +1,24 @@
|
||||
import React from 'react'
|
||||
import { action } from '@storybook/addon-actions'
|
||||
import CheckBox, { CHECKED, INDETERMINATE, UNCHECKED } from './check-box.component'
|
||||
import { boolean, select } from '@storybook/addon-knobs/react'
|
||||
|
||||
export default {
|
||||
title: 'Check Box',
|
||||
}
|
||||
|
||||
const checkboxOptions = {
|
||||
[CHECKED]: CHECKED,
|
||||
[INDETERMINATE]: INDETERMINATE,
|
||||
[UNCHECKED]: UNCHECKED,
|
||||
True: true,
|
||||
False: false,
|
||||
}
|
||||
|
||||
export const primaryType = () => (
|
||||
<CheckBox
|
||||
checked={select('checked state', checkboxOptions, UNCHECKED)}
|
||||
disabled={boolean('Disabled', false)}
|
||||
onClick={action('checkbox clicked')}
|
||||
/>
|
||||
)
|
@ -1 +1 @@
|
||||
export { default } from './check-box.component'
|
||||
export { default, CHECKED, INDETERMINATE, UNCHECKED } from './check-box.component'
|
||||
|
@ -1,26 +1,23 @@
|
||||
.check-box {
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background: $white;
|
||||
color: $Grey-100;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
font-size: 21px;
|
||||
line-height: 0.9;
|
||||
border-radius: 2px;
|
||||
display: flex;
|
||||
|
||||
&--checked {
|
||||
background: $curious-blue;
|
||||
border: 2px solid $curious-blue;
|
||||
border-radius: 2px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
i {
|
||||
color: $white;
|
||||
}
|
||||
&:checked, &:indeterminate {
|
||||
color: $curious-blue;
|
||||
border-color: $curious-blue;
|
||||
}
|
||||
|
||||
&--un-checked {
|
||||
background: $white;
|
||||
border: 2px solid $Grey-100;
|
||||
border-radius: 2px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
&:disabled {
|
||||
color: $Grey-100;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ $fa-font-path: 'fonts/fontawesome';
|
||||
|
||||
@import '../../../../../node_modules/@fortawesome/fontawesome-free/scss/fontawesome.scss';
|
||||
@import '../../../../../node_modules/@fortawesome/fontawesome-free/scss/solid.scss';
|
||||
@import '../../../../../node_modules/@fortawesome/fontawesome-free/scss/regular.scss';
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
|
@ -3,7 +3,7 @@ import React, { Component } from 'react'
|
||||
import classnames from 'classnames'
|
||||
import Identicon from '../../../components/ui/identicon'
|
||||
import Button from '../../../components/ui/button'
|
||||
import CheckBox from '../../../components/ui/check-box'
|
||||
import CheckBox, { CHECKED, INDETERMINATE, UNCHECKED } from '../../../components/ui/check-box'
|
||||
import Tooltip from '../../../components/ui/tooltip-v2'
|
||||
import { PRIMARY } from '../../../helpers/constants/common'
|
||||
import UserPreferencedCurrencyDisplay from '../../../components/app/user-preferenced-currency-display'
|
||||
@ -90,7 +90,7 @@ export default class ChooseAccount extends Component {
|
||||
<div className="permissions-connect-choose-account__account-info-wrapper">
|
||||
<CheckBox
|
||||
className="permissions-connect-choose-account__list-check-box"
|
||||
checked={ selectedAccounts.has(address) }
|
||||
checked={selectedAccounts.has(address)}
|
||||
/>
|
||||
<Identicon
|
||||
diameter={34}
|
||||
@ -128,6 +128,17 @@ export default class ChooseAccount extends Component {
|
||||
renderAccountsListHeader () {
|
||||
const { t } = this.context
|
||||
const { selectNewAccountViaModal, accounts } = this.props
|
||||
const { selectedAccounts } = this.state
|
||||
|
||||
let checked
|
||||
if (this.allAreSelected()) {
|
||||
checked = CHECKED
|
||||
} else if (selectedAccounts.size === 0) {
|
||||
checked = UNCHECKED
|
||||
} else {
|
||||
checked = INDETERMINATE
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames({
|
||||
@ -140,7 +151,7 @@ export default class ChooseAccount extends Component {
|
||||
<div className="permissions-connect-choose-account__select-all">
|
||||
<CheckBox
|
||||
className="permissions-connect-choose-account__header-check-box"
|
||||
checked={this.allAreSelected()}
|
||||
checked={checked}
|
||||
onClick={() => (this.allAreSelected() ? this.deselectAll() : this.selectAll())}
|
||||
/>
|
||||
<div className="permissions-connect-choose-account__text-grey">{ this.context.t('selectAll') }</div>
|
||||
|
Loading…
Reference in New Issue
Block a user