diff --git a/src/Components/RadioButton/Documentation/RadioButton.md b/src/Components/RadioButton/Documentation/RadioButton.md new file mode 100644 index 00000000..dea56fc6 --- /dev/null +++ b/src/Components/RadioButton/Documentation/RadioButton.md @@ -0,0 +1,32 @@ +# Radio Button + +This is a variation of an input with type radio. + +### Usage + +```tsx +import {Button} from '@influxdata/clockface' +``` + +```tsx + {}} +> +``` + +### Example + + + + + + + + diff --git a/src/Components/RadioButton/Documentation/RadioButton.stories.tsx b/src/Components/RadioButton/Documentation/RadioButton.stories.tsx new file mode 100644 index 00000000..00c41605 --- /dev/null +++ b/src/Components/RadioButton/Documentation/RadioButton.stories.tsx @@ -0,0 +1,151 @@ +// Libraries +import React, {RefObject, createRef} from 'react' +import marked from 'marked' + +// Storybook +import {storiesOf} from '@storybook/react' +import {withKnobs, select, text} from '@storybook/addon-knobs' +import {mapEnumKeys} from '../../../Utils/storybook' + +// Components +import { + RadioButton, + RadioButtonRef, + RadioButtonGroup, + RadioButtonGroupRef, +} from '../' + +// Types + +// Notes +import RadioButtonReadMe from './RadioButton.md' +import RadioButtonGroupReadMe from './RadioButtonGroup.md' +import { + InputToggleType, + ComponentSize, + ComponentOrientation, +} from '../../../Types' + +const RadioButtonStories = storiesOf( + 'Component|RadioButton', + module +).addDecorator(withKnobs) + +RadioButtonStories.add( + 'RadioButton', + () => { + const radioButtonRef: RefObject = createRef() + + const logRef = (): void => { + /* eslint-disable */ + console.log(radioButtonRef.current) + /* eslint-enable */ + } + + return ( +
+ console.log(value)} + /* eslint-disable */ + > +
+ +
+
+ ) + }, + { + readme: { + content: marked(RadioButtonReadMe), + }, + } +) + +RadioButtonStories.add( + 'RadioButtonGroup', + () => { + const radioButtonGroupRef: RefObject = createRef() + const radioButtonRef1: RefObject = createRef() + const radioButtonRef2: RefObject = createRef() + const radioButtonRef3: RefObject = createRef() + + const logRef = (): void => { + /* eslint-disable */ + console.log(radioButtonGroupRef.current) + /* eslint-enable */ + } + + return ( +
+ console.log(value)} + /* eslint-disable */ + > + + + + +
+ +
+
+ ) + }, + { + readme: { + content: marked(RadioButtonGroupReadMe), + }, + } +) diff --git a/src/Components/RadioButton/Documentation/RadioButtonGroup.md b/src/Components/RadioButton/Documentation/RadioButtonGroup.md new file mode 100644 index 00000000..4d0b705a --- /dev/null +++ b/src/Components/RadioButton/Documentation/RadioButtonGroup.md @@ -0,0 +1,56 @@ +# Radio Button + +This is a group wrapper for when you have multiple radio buttons. + +### Usage + +```tsx +import {Button} from '@influxdata/clockface' +``` + +```tsx + {}} +> + + + + +``` + +### Example + + + + + + + + diff --git a/src/Components/RadioButton/RadioButton.scss b/src/Components/RadioButton/RadioButton.scss new file mode 100644 index 00000000..9cc80970 --- /dev/null +++ b/src/Components/RadioButton/RadioButton.scss @@ -0,0 +1,100 @@ +@import "../../Styles/variables"; + +.cf-radio-button { + input { + position: fixed; + top: 0px; + left: 0px; + opacity: 0; + width: 0; + height: 0; + + & + .cf-radio-button--label { + color: $cf-label--default; + + .cf-radio-button--indicator { + &::before { + position: relative; + display: inline-block; + width: 100%; + height: 100%; + background-color: $c-pool; + content: ""; + border-radius: 50%; + transform: scale(1); + transition: opacity 0.25s ease, transform 0.25s ease, color 0.25s ease, background-color 0.25s ease; + opacity: 0; + } + } + } + + &:checked + .cf-radio-button--label { + color: $cf-label--active; + + .cf-radio-button--indicator { + &::before { + opacity: 1; + transform: scale(0.5); + } + } + } + &:focus + .cf-radio-button--label .cf-radio-button--indicator { + background-color: $cf-input-background--focused; + border-color: $cf-input-border--focused; + box-shadow: $cf-input--box-shadow; + } + } +} + +.cf-radio-button--label { + display: flex; + align-items: center; + flex-direction: row; + cursor: pointer; + font-weight: $cf-font-weight--medium; + &:hover > .cf-radio-button--indicator { + background-color: $cf-input-background--hover; + border-color: $cf-input-border--hover; + } +} + +.cf-radio-button--indicator { + display: inline-block; + border-radius: 50%; + border-width: $cf-border; + border-style: solid; + margin: 0px; + background-color: $cf-input-background--default; + border-color: $cf-input-border--default; +} + +/* + Size Modifiers + ------------------------------------------------------------------------------ +*/ +@mixin radioButtonSizeModifier($fontSize, $size, $labelGap) { + height: $size; + + .cf-radio-button--indicator { + height: $size; + width: $size; + margin-right: $labelGap; + } + + .cf-radio-button--label { + font-size: $fontSize; + } +} + +.cf-radio-button__xs { + @include radioButtonSizeModifier($cf-form-xs-font, $cf-form-xs-height, $cf-form-xs-padding); +} +.cf-radio-button__sm { + @include radioButtonSizeModifier($cf-form-sm-font, $cf-form-sm-height, $cf-form-sm-padding); +} +.cf-radio-button__md { + @include radioButtonSizeModifier($cf-form-md-font, $cf-form-md-height, $cf-form-md-padding); +} +.cf-radio-button__lg { + @include radioButtonSizeModifier($cf-form-lg-font, $cf-form-lg-height, $cf-form-lg-padding); +} diff --git a/src/Components/RadioButton/RadioButton.tsx b/src/Components/RadioButton/RadioButton.tsx new file mode 100644 index 00000000..31f6635b --- /dev/null +++ b/src/Components/RadioButton/RadioButton.tsx @@ -0,0 +1,82 @@ +// Libraries +import React, {forwardRef, ReactNode} from 'react' +import classnames from 'classnames' + +// Styles +import './RadioButton.scss' + +// Types +import { + StandardFunctionProps, + ComponentSize, + InputToggleType, +} from '../../Types' + +export interface RadioButtonProps extends StandardFunctionProps { + id: string + type: InputToggleType + checked?: boolean + name?: string + value: string + size?: ComponentSize + toolTipText?: string + labelText?: ReactNode + onChange?: (value?: string) => void +} + +export type RadioButtonRef = HTMLDivElement + +export const RadioButton = forwardRef( + ( + { + id, + checked, + name, + size = ComponentSize.Small, + value, + type, + labelText, + /* eslint-disable */ + onChange = () => {}, + /* eslint-disable */ + + toolTipText = '', + }, + ref + ) => { + const radioButtonClass = classnames('cf-radio-button', { + [`cf-radio-button__${size}`]: size, + }) + + const radioButtonIndicatorClass = classnames('cf-radio-button--indicator') + + const handleChange = (): void => { + onChange(value) + } + + return ( +
+ + +
+ ) + } +) + +RadioButton.displayName = 'RadioButton' diff --git a/src/Components/RadioButton/RadioButtonGroup.scss b/src/Components/RadioButton/RadioButtonGroup.scss new file mode 100644 index 00000000..14a5048f --- /dev/null +++ b/src/Components/RadioButton/RadioButtonGroup.scss @@ -0,0 +1,38 @@ +@import '../../Styles/variables'; + +.cf-radio-button-group { + display: flex; +} + +.cf-radio-button-group__horizontal { + flex-direction: row; + + & .cf-radio-button__xs:not(:last-of-type) { + margin-right: $cf-marg-a; + } + & .cf-radio-button__sm:not(:last-of-type) { + margin-right: $cf-marg-b; + } + & .cf-radio-button__md:not(:last-of-type) { + margin-right: $cf-marg-c; + } + & .cf-radio-button__lg:not(:last-of-type) { + margin-right: $cf-marg-d; + } +} + +.cf-radio-button-group__vertical { + flex-direction: column; + & .cf-radio-button__xs:not(:last-of-type) { + margin-bottom: $cf-marg-a; + } + & .cf-radio-button__sm:not(:last-of-type) { + margin-bottom: $cf-marg-b; + } + & .cf-radio-button__md:not(:last-of-type) { + margin-bottom: $cf-marg-c; + } + & .cf-radio-button__lg:not(:last-of-type) { + margin-bottom: $cf-marg-d; + } +} diff --git a/src/Components/RadioButton/RadioButtonGroup.tsx b/src/Components/RadioButton/RadioButtonGroup.tsx new file mode 100644 index 00000000..d4585b9e --- /dev/null +++ b/src/Components/RadioButton/RadioButtonGroup.tsx @@ -0,0 +1,78 @@ +// Libraries +// Libraries +import React, {forwardRef, ReactNode, useState} from 'react' +import classnames from 'classnames' + +// Styles +import './RadioButtonGroup.scss' + +// Types +import {StandardFunctionProps, ComponentOrientation} from '../../Types' +import {RadioButton, RadioButtonProps} from './RadioButton' + +export interface RadioButtonGroupProps extends StandardFunctionProps { + /** + * Use this to pass the radio Button that will be included in the group + */ + children: ReactNode + /** + * Vertical or horizontal orientation + */ + orientation: ComponentOrientation + /** + * name of the group of radio buttons + */ + name: string + /** + * Use optionally to pass a function + */ + onChange: (value?: string) => void + /** + * Use optionally to define a default active value + */ + activeValue?: string +} + +export type RadioButtonGroupRef = HTMLDivElement + +export const RadioButtonGroup = forwardRef< + RadioButtonGroupRef, + RadioButtonGroupProps +>(({orientation, children, name, onChange, activeValue = ''}, ref) => { + const [selectedValue, setSelectedValue] = useState(activeValue) + const radioButtonGroupClass = classnames('cf-radio-button-group', { + [`cf-radio-button-group__${orientation}`]: orientation, + }) + const mapRadioButtons = () => { + const radioButtons = React.Children.map(children, radioButton => { + if (!React.isValidElement(radioButton)) { + return radioButton + } + const child: React.ReactElement = radioButton + const {value, ...other} = child.props + + return ( + + ) + }) + return radioButtons + } + + const handleChange = (value: string): void => { + setSelectedValue(value) + onChange(value) + } + return ( +
+ {mapRadioButtons()} +
+ ) +}) + +RadioButton.displayName = 'RadioButtonGroup' diff --git a/src/Components/RadioButton/index.tsx b/src/Components/RadioButton/index.tsx new file mode 100644 index 00000000..c970aee1 --- /dev/null +++ b/src/Components/RadioButton/index.tsx @@ -0,0 +1,2 @@ +export * from './RadioButton' +export * from './RadioButtonGroup'