diff --git a/.eslintignore b/.eslintignore
index 841de086c..fc2805bac 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,2 +1,3 @@
/artifacts
/node_modules
+/storybook-static
\ No newline at end of file
diff --git a/lib/Checkbox/Checkbox.css b/lib/Checkbox/Checkbox.css
index 2791ce6cb..064e43836 100644
--- a/lib/Checkbox/Checkbox.css
+++ b/lib/Checkbox/Checkbox.css
@@ -6,57 +6,28 @@
.checkbox {
display: flex;
+ flex-direction: column;
position: relative;
color: var(--color-text);
line-height: var(--line-height);
+ margin-bottom: 0;
&.fullWidth {
width: 100%;
}
-
- &.marginBottom0 {
- margin-bottom: 0;
- }
-
- /**
- * Inline checkbox
- */
- &.inline {
- display: inline-flex;
-
- /**
- * If we only render the checkbox (no label)
- */
-
- &.noLabel {
- height: auto;
-
- & label.checkboxLabel {
- height: auto;
- padding: 6px;
- margin: 0;
-
- &::before {
- left: 0;
- right: 0;
- }
- }
- }
- }
-}
-
-/* Apply spacing between inline checkboxes when stacked */
-.inline + .inline {
- margin-left: var(--gutter-static-two-thirds);
- margin-right: 0;
}
-[dir="rtl"] .inline + .inline {
- margin-right: var(--gutter-static-two-thirds);
- margin-left: 0;
+/**
+ * Label
+ */
+.label {
+ margin-bottom: 0;
}
-.labelCheck {
+/**
+ * Custom checkbox styling
+ */
+.checkboxIcon {
position: relative;
width: var(--checkbox-size);
height: var(--checkbox-size);
@@ -66,31 +37,103 @@
display: flex;
overflow: hidden;
flex-shrink: 0;
- top: 1px;
& svg {
opacity: 0;
+ }
+}
+
+.inner {
+ min-height: var(--control-min-size-desktop);
+ display: flex;
+ align-items: baseline;
+ cursor: pointer;
+ flex-grow: 2;
+ position: relative;
+ border-radius: var(--radius);
+ font-size: var(--font-size-medium);
+}
+
+/**
+ * Label text
+ */
+.labelText {
+ margin: 0 0 0 var(--gutter-static-one-third);
+ display: flex;
+ align-items: center;
+ min-height: var(--control-min-size-desktop);
+}
+
+[dir="rtl"] .labelText {
+ margin: 0 var(--gutter-static-one-third) 0 0;
+}
+
+/**
+ * Input
+ */
+.input {
+ position: absolute;
+ z-index: 10;
+ opacity: 0;
+ cursor: pointer;
+}
+
+.noLabel .input {
+ transform: scale(1.9);
+}
+
+/**
+ * Vertical label alignment
+ */
+.vertical .label {
+ flex-direction: column;
+ align-items: flex-start;
+
+ & .labelText {
+ margin: 0 0 var(--control-label-margin-bottom) 0;
+ min-height: 0;
+ }
- & path {
- stroke: #000;
- }
+ & .inner {
+ align-items: center;
}
}
+/**
+ * Inline checkbox
+ */
+.inline {
+ display: inline-flex;
+
+ &.noLabel .inner {
+ align-items: center;
+ }
+}
+
+/* Apply spacing between inline checkboxes when stacked */
+.inline + .inline {
+ margin-left: var(--gutter-static-two-thirds);
+ margin-right: 0;
+}
+
+[dir="rtl"] .inline + .inline {
+ margin-right: var(--gutter-static-two-thirds);
+ margin-left: 0;
+}
+
/**
* Checkbox feedback
*/
.checkboxFeedback {
- font-size: var(--font-size-small);
+ font-size: var(--font-size-medium);
}
-/* Warning and error states */
.hasWarning {
& .checkboxFeedback {
color: var(--warn);
}
- & .labelCheck {
+ & .checkboxIcon {
border-color: var(--warn);
}
}
@@ -100,30 +143,16 @@
color: var(--error);
}
- & .labelCheck {
+ & .checkboxIcon {
border-color: var(--error);
}
}
-.checkboxLabel {
- padding: var(--input-vertical-padding) 0;
- display: flex;
- align-items: baseline;
- cursor: pointer;
- flex-grow: 2;
- position: relative;
- border-radius: var(--radius);
- font-size: var(--font-size-medium);
- margin-bottom: 0;
-
- &.disabledLabel {
- cursor: not-allowed;
- color: var(--color-text-p2);
-
- & .labelCheck {
- opacity: 0.5;
- }
- }
+/**
+ * Interaction styles (hover, focus, active)
+ */
+.checkboxInteractionStylesControl {
+ composes: interactionStylesControl from "../sharedStyles/interactionStyles.css";
}
.checkboxInteractionStyles {
@@ -134,48 +163,24 @@
composes: isFocused from "../sharedStyles/interactionStyles.css";
}
-.formLabel {
- composes: checkboxLabel;
- font-weight: bold;
- text-transform: Uppercase;
-}
-
-.labelText {
- margin: 0 0 0 var(--gutter-static-one-third);
- display: flex;
- align-items: center;
- min-height: var(--control-min-size-desktop);
-}
-
-[dir="rtl"] .labelText {
- margin: 0 var(--gutter-static-one-third) 0 0;
-}
-
-/* fix for shrinkage issues with checkbox as a flex-child */
-:global input[type='checkbox'] {
- min-width: 16px;
-}
-
/**
- * Custom input styling
+ * Disabled
*/
-input.checkboxInput {
- margin: 0 9px;
- margin-bottom: 2px;
- position: absolute;
- clip: rect(1px, 1px, 1px, 1px);
- z-index: -1;
-}
+.disabled {
+ & .checkboxIcon {
+ opacity: 0.5;
+ }
-/* disabled styles */
-.checkboxInput:disabled .labelCheck {
- opacity: 0.65;
+ &:hover .inner {
+ cursor: default;
+ }
}
/**
* Checked state
*/
-.checkboxInput:checked + .labelCheck {
+
+.input:checked + .checkboxIcon {
background-color: #000;
& svg {
diff --git a/lib/Checkbox/Checkbox.js b/lib/Checkbox/Checkbox.js
index 989f95ddd..1fd88531c 100644
--- a/lib/Checkbox/Checkbox.js
+++ b/lib/Checkbox/Checkbox.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { createRef } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import uniqueId from 'lodash/uniqueId';
@@ -22,13 +22,11 @@ class Checkbox extends React.Component {
disabled: PropTypes.bool,
error: PropTypes.node,
fullWidth: PropTypes.bool,
- hover: PropTypes.bool,
id: PropTypes.string,
inline: PropTypes.bool,
+ inputRef: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
label: PropTypes.node,
labelClass: PropTypes.string,
- labelStyle: PropTypes.object,
- marginBottom0: PropTypes.bool,
name: PropTypes.string,
onBlur: PropTypes.func,
onChange: PropTypes.func,
@@ -39,85 +37,84 @@ class Checkbox extends React.Component {
PropTypes.bool,
PropTypes.string
]),
+ vertical: PropTypes.bool,
warning: PropTypes.node,
};
+ static defaultProps = {
+ vertical: false,
+ };
+
constructor(props) {
super(props);
-
- this.state = {
- inputInFocus: false
- };
-
this.id = this.props.id || uniqueId('checkbox-');
- this.handleChange = this.handleChange.bind(this);
- this.handleFocus = this.handleFocus.bind(this);
- this.handleBlur = this.handleBlur.bind(this);
- this.getFeedbackClasses = this.getFeedbackClasses.bind(this);
- this.getFeedbackElement = this.getFeedbackElement.bind(this);
+ // Typecheck for ref callbacks
+ if (typeof props.inputRef === 'function') {
+ this.input = (ref) => {
+ props.inputRef(ref);
+ this.input.current = ref;
+ };
+ } else {
+ this.input = props.inputRef || createRef();
+ }
}
- getLabelStyle() {
+ state = {
+ inputInFocus: false
+ };
+
+ getInnerClasses = () => {
+ const { inputInFocus } = this.state;
+ const { disabled, innerClass } = this.props;
+
return classNames(
- css.checkboxLabel,
- { [css[this.props.labelStyle]]: !this.props.labelClass && this.props.labelStyle },
- { [css.checkboxInteractionStyles]: !this.props.disabled },
- { [css.disabledLabel]: this.props.disabled },
- { [css.labelFocused]: this.state.inputInFocus },
- this.props.labelClass,
+ css.inner,
+ { [css.checkboxInteractionStyles]: !disabled },
+ { [css.disabledLabel]: disabled },
+ { [css.labelFocused]: inputInFocus },
+ innerClass,
);
}
- getRootStyle() {
+ getRootClasses = () => {
+ const { className, fullWidth, vertical, label, disabled, inline, error, warning } = this.props;
+
return classNames(
[`${css.checkbox}`],
- { [`${css.noLabel}`]: !this.props.label },
- { [`${css.hover}`]: this.props.hover },
- { [`${css.hovered}`]: (this.props.hover && this.state.inputInFocus) },
- { [`${css.fullWidth}`]: this.props.fullWidth },
- { [`${css.marginBottom0}`]: this.props.marginBottom0 },
- { [`${css.inline}`]: this.props.inline },
- this.props.className,
- this.getFeedbackClasses(),
+ { [`${css.fullWidth}`]: fullWidth },
+ { [`${css.inline}`]: inline || vertical || !label },
+ { [`${css.vertical}`]: vertical },
+ { [`${css.disabled}`]: disabled },
+ { [`${css.noLabel}`]: !label },
+ { [`${css.hasError}`]: !!error },
+ { [`${css.hasWarning}`]: !!warning },
+ className,
);
}
- // Add feedback class on warning or error
- getFeedbackClasses() {
- const { warning, error } = this.props;
- const classes = [];
-
- // Check if checkbox has error or warning
- if (error) {
- classes.push(css.hasError);
- } else if (warning) {
- classes.push(css.hasWarning);
- }
-
- // Add the feedback class if we have feedback classes
- if (classes.length) {
- classes.push(css.hasFeedback);
- }
+ getLabelClasses = () => {
+ const { disabled, labelClass } = this.props;
- return classes;
+ return classNames(
+ [`${css.label}`],
+ { [css.checkboxInteractionStylesControl]: !disabled },
+ labelClass,
+ );
}
- // Get feedback element (if the checkbox has any feedback to show)
- getFeedbackElement() {
- const hasFeedback = !!this.getFeedbackClasses().length;
+ renderFeedbackElement = () => {
+ const { error, warning } = this.props;
+ const hasFeedback = error || warning;
+
if (!hasFeedback) {
return false;
}
- const error = this.props.error;
- const warning = this.props.warning;
-
- // Currently prefering error over warning
return (
{error || warning}
);
}
- handleChange(e) {
+ handleChange = (e) => {
const { readOnly, onChange } = this.props;
// Disable onChange handler if the field is read-only
@@ -128,7 +125,7 @@ class Checkbox extends React.Component {
}
}
- handleFocus(e) {
+ handleFocus = (e) => {
this.setState({
inputInFocus: true,
});
@@ -138,7 +135,7 @@ class Checkbox extends React.Component {
}
}
- handleBlur(e) {
+ handleBlur = (e) => {
this.setState({
inputInFocus: false,
});
@@ -148,6 +145,24 @@ class Checkbox extends React.Component {
}
}
+ renderLabelText = () => {
+ const { label, required, readOnly } = this.props;
+
+ return (
+
+ { label }
+ { required && }
+ { readOnly && (
+
+
+
+
+
+ ) }
+
+ );
+ }
+
render() {
const {
autoFocus,
@@ -157,62 +172,57 @@ class Checkbox extends React.Component {
name,
readOnly,
required,
- value
+ value,
+ vertical,
+ error,
} = this.props;
// eslint-disable-next-line react/forbid-foreign-prop-types
const [, inputCustom] = separateComponentProps(this.props, Checkbox.propTypes);
return (
-
-