@@ -46,3 +46,4 @@ package-lock.json
.webpack.meta
pip-wheel-metadata/
Brewfile.lock.json
+~/
@@ -4,7 +4,7 @@ import {withInfo} from '@storybook/addon-info';
import {number, boolean} from '@storybook/addon-knobs';
import styled from '@emotion/styled';
-import CheckboxFancy from 'app/components/checkboxFancy';
+import CheckboxFancy from 'app/components/checkboxFancy/checkboxFancy';
storiesOf('Style|Icons', module).add(
'CheckboxFancy',
@@ -13,7 +13,7 @@ storiesOf('Style|Icons', module).add(
<Container>
<CheckboxFancy
size={`${number('Size', 100)}px`}
- checked={boolean('Checked', true)}
+ isChecked={boolean('Checked', true)}
/>
</Container>
);
@@ -1,74 +0,0 @@
-import React from 'react';
-import styled from '@emotion/styled';
-import {css} from '@emotion/core';
-import PropTypes from 'prop-types';
-
-import InlineSvg from 'app/components/inlineSvg';
-type Props = {
- disabled?: boolean;
- checked?: boolean;
- size?: string;
- className?: string;
-};
-const getDisabledStyles = p =>
- p.disabled &&
- css`
- background: ${p.checked ? p.theme.gray1 : p.theme.offWhite};
- border-color: ${p.theme.gray1};
- `;
-const getHoverStyles = p =>
- !p.disabled &&
- border: 2px solid ${p.checked ? p.theme.purple : p.theme.gray4};
-const CheckboxFancy = styled(({checked, disabled, className}: Props) => (
- <div
- role="checkbox"
- aria-disabled={disabled}
- aria-checked={checked}
- className={className}
- >
- {checked && <Check src="icon-checkmark-sm" />}
- </div>
-))`
- width: ${p => p.size};
- height: ${p => p.size};
- border-radius: 5px;
- background: ${p => (p.checked ? p.theme.purple : null)};
- display: flex;
- align-items: center;
- justify-content: center;
- box-shadow: 1px 1px 5px 0px rgba(0, 0, 0, 0.05) inset;
- border: 2px solid ${p => (p.checked ? p.theme.purple : p.theme.gray2)};
- cursor: ${p => (p.disabled ? 'disabled' : 'pointer')};
- ${p => !p.checked && 'transition: 500ms border ease-out'};
- &:hover {
- ${getHoverStyles}
- }
- ${getDisabledStyles}
-`;
-CheckboxFancy.defaultProps = {
- checked: false,
- size: '16px',
-CheckboxFancy.propTypes = {
- checked: PropTypes.bool,
- disabled: PropTypes.bool,
- size: PropTypes.string,
-const Check = styled(InlineSvg)`
- width: 70%;
- height: 70%;
- color: #fff;
-export default CheckboxFancy;
@@ -0,0 +1,67 @@
+import React from 'react';
+import styled from '@emotion/styled';
+import {css} from '@emotion/core';
+
+import {Theme} from 'app/utils/theme';
+import CheckboxFancyContent from './checkboxFancyContent';
+type Props = {
+ isDisabled?: boolean;
+ size?: string;
+ className?: string;
+} & React.ComponentProps<typeof CheckboxFancyContent>;
+const disabledStyles = (p: Props & {theme: Theme}) =>
+ p.isDisabled &&
+ css`
+ background: ${p.isChecked || p.isIndeterminate ? p.theme.gray1 : p.theme.offWhite};
+ border-color: ${p.theme.gray1};
+ `;
+const hoverStyles = (p: Props & {theme: Theme}) =>
+ !p.isDisabled &&
+ border: 2px solid ${p.isChecked || p.isIndeterminate ? p.theme.purple : p.theme.gray4};
+const CheckboxFancy = styled(
+ ({isChecked, className, isDisabled, isIndeterminate}: Props) => (
+ <div
+ data-test-id="checkbox-fancy"
+ role="checkbox"
+ aria-disabled={isDisabled}
+ aria-checked={isChecked}
+ className={className}
+ >
+ <CheckboxFancyContent isIndeterminate={isIndeterminate} isChecked={isChecked} />
+ </div>
+ )
+)`
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: 1px 1px 5px 0px rgba(0, 0, 0, 0.05) inset;
+ width: ${p => p.size};
+ height: ${p => p.size};
+ border-radius: 5px;
+ background: ${p => (p.isChecked || p.isIndeterminate ? p.theme.purple : 'transparent')};
+ border: 2px solid
+ ${p => (p.isChecked || p.isIndeterminate ? p.theme.purple : p.theme.gray2)};
+ cursor: ${p => (p.isDisabled ? 'not-allowed' : 'pointer')};
+ ${p => (!p.isChecked || !p.isIndeterminate) && 'transition: 500ms border ease-out'};
+ &:hover {
+ ${hoverStyles}
+ }
+ ${disabledStyles}
+`;
+CheckboxFancy.defaultProps = {
+ size: '16px',
+ isChecked: false,
+ isIndeterminate: false,
+};
+export default CheckboxFancy;
@@ -0,0 +1,23 @@
+import {IconCheckmark} from 'app/icons/iconCheckmark';
+import {IconSubtract} from 'app/icons/iconSubtract';
+ isChecked?: boolean;
+ isIndeterminate?: boolean;
+const CheckboxFancyContent = ({isChecked, isIndeterminate}: Props) => {
+ if (isIndeterminate) {
+ return <IconSubtract size="70%" color="white" />;
+ if (isChecked) {
+ return <IconCheckmark size="70%" color="white" />;
+ return null;
+export default CheckboxFancyContent;
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
import React from 'react';
import space from 'app/styles/space';
class GlobalSelectionHeaderRow extends React.Component {
@@ -28,7 +28,7 @@ class GlobalSelectionHeaderRow extends React.Component {
render() {
const {checked, onCheckClick, multi, renderCheckbox, children, ...props} = this.props;
- const checkbox = <CheckboxFancy disabled={!multi} checked={checked} />;
+ const checkbox = <CheckboxFancy isDisabled={!multi} isChecked={checked} />;
return (
<Container isChecked={checked} {...props}>
@@ -16,7 +16,15 @@ export const IconCheckmark = React.forwardRef(function IconCheckmark(
const size = theme.iconSizes[providedSize] ?? providedSize;
- <svg viewBox="0 0 16 16" fill={color} height={size} width={size} {...props} ref={ref}>
+ <svg
+ data-test-id="icon-check-mark"
+ viewBox="0 0 16 16"
+ fill={color}
+ height={size}
+ width={size}
+ {...props}
+ ref={ref}
{providedCircle === true ? (
<g>
<path d="M7,12a.78.78,0,0,1-.57-.26L4,9.05A.76.76,0,0,1,4.07,8a.75.75,0,0,1,1.06.07l1.75,2L10.77,4.3A.75.75,0,0,1,12,5.14L7.58,11.7A.77.77,0,0,1,7,12Z" />
@@ -16,7 +16,15 @@ export const IconSubtract = React.forwardRef(function IconSubtract(
+ data-test-id="icon-subtract"
<path d="M8,16a8,8,0,1,1,8-8A8,8,0,0,1,8,16ZM8,1.53A6.47,6.47,0,1,0,14.47,8,6.47,6.47,0,0,0,8,1.53Z" />
@@ -1,6 +1,8 @@
+import {IconSizes} from 'app/utils/theme';
export type IconProps = React.SVGAttributes<SVGElement> & {
color?: string;
- size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | string;
+ size?: IconSizes | string;
direction?: 'up' | 'right' | 'down' | 'left';
solid?: boolean;
circle?: boolean;
@@ -191,18 +191,20 @@ const button = {
},
} as const;
+const iconSizes = {
+ xs: '12px',
+ sm: '16px',
+ md: '20px',
+ lg: '24px',
+ xl: '32px',
const theme = {
breakpoints: ['768px', '992px', '1200px', '1440px', '2560px'],
...colors,
- iconSizes: {
- xs: '12px',
- sm: '16px',
- md: '20px',
- lg: '24px',
- xl: '32px',
- },
+ iconSizes,
iconDirections: {
up: '0',
@@ -327,5 +329,6 @@ const theme = {
export type Theme = typeof theme;
export type Color = keyof typeof colors;
+export type IconSizes = keyof typeof iconSizes;
export default theme;