From 38752abcdb9db6b933a39aca2707fd8d658da7f3 Mon Sep 17 00:00:00 2001 From: Dong Kyun Kim Date: Fri, 5 Mar 2021 01:36:57 -0500 Subject: [PATCH 1/4] introducing a radio button component and a radio button group component --- .../RadioButton/Documentation/RadioButton.md | 32 ++++ .../Documentation/RadioButton.stories.tsx | 151 ++++++++++++++++++ .../Documentation/RadioButtonGroup.md | 56 +++++++ src/Components/RadioButton/RadioButton.scss | 110 +++++++++++++ src/Components/RadioButton/RadioButton.tsx | 82 ++++++++++ .../RadioButton/RadioButtonGroup.scss | 38 +++++ .../RadioButton/RadioButtonGroup.tsx | 78 +++++++++ src/Components/RadioButton/index.tsx | 2 + 8 files changed, 549 insertions(+) create mode 100644 src/Components/RadioButton/Documentation/RadioButton.md create mode 100644 src/Components/RadioButton/Documentation/RadioButton.stories.tsx create mode 100644 src/Components/RadioButton/Documentation/RadioButtonGroup.md create mode 100644 src/Components/RadioButton/RadioButton.scss create mode 100644 src/Components/RadioButton/RadioButton.tsx create mode 100644 src/Components/RadioButton/RadioButtonGroup.scss create mode 100644 src/Components/RadioButton/RadioButtonGroup.tsx create mode 100644 src/Components/RadioButton/index.tsx 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..71fb470e --- /dev/null +++ b/src/Components/RadioButton/RadioButton.scss @@ -0,0 +1,110 @@ +@import '../../Styles/variables'; + +.cf-radio-button { + input { + position: fixed; + top: 0px; + left: 0px; + opacity: 0; + width: 0; + height: 0; + + & + .cf-radio-button--label .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 .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..678d01ff --- /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-b; + } + & .cf-radio-button__sm:not(:last-of-type) { + margin-bottom: $cf-marg-c; + } + & .cf-radio-button__md:not(:last-of-type) { + margin-bottom: $cf-marg-d; + } + & .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..d5302a48 --- /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 = 'test-1'}, 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' From ce7a4840dfca141fbf11051f84dc6c99548d9e1d Mon Sep 17 00:00:00 2001 From: Dong Kyun Kim Date: Fri, 5 Mar 2021 01:49:36 -0500 Subject: [PATCH 2/4] introduce default font color and active font color for contrast --- src/Components/RadioButton/RadioButton.scss | 68 +++++++++------------ 1 file changed, 29 insertions(+), 39 deletions(-) diff --git a/src/Components/RadioButton/RadioButton.scss b/src/Components/RadioButton/RadioButton.scss index 71fb470e..9cc80970 100644 --- a/src/Components/RadioButton/RadioButton.scss +++ b/src/Components/RadioButton/RadioButton.scss @@ -1,4 +1,4 @@ -@import '../../Styles/variables'; +@import "../../Styles/variables"; .cf-radio-button { input { @@ -9,29 +9,35 @@ width: 0; height: 0; - & + .cf-radio-button--label .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; + & + .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 .cf-radio-button--indicator { - &::before { - opacity: 1; - transform: scale(0.5); + &: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; @@ -81,30 +87,14 @@ } .cf-radio-button__xs { - @include radioButtonSizeModifier( - $cf-form-xs-font, - $cf-form-xs-height, - $cf-form-xs-padding - ); + @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 - ); + @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 - ); + @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 - ); + @include radioButtonSizeModifier($cf-form-lg-font, $cf-form-lg-height, $cf-form-lg-padding); } From cc4c18c41239d6adb04f2fb1c5f8aa2f3426b7df Mon Sep 17 00:00:00 2001 From: Dong Kyun Kim Date: Fri, 5 Mar 2021 01:59:13 -0500 Subject: [PATCH 3/4] returned the arbitrary margins to fit the style guide --- src/Components/RadioButton/RadioButtonGroup.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Components/RadioButton/RadioButtonGroup.scss b/src/Components/RadioButton/RadioButtonGroup.scss index 678d01ff..14a5048f 100644 --- a/src/Components/RadioButton/RadioButtonGroup.scss +++ b/src/Components/RadioButton/RadioButtonGroup.scss @@ -24,13 +24,13 @@ .cf-radio-button-group__vertical { flex-direction: column; & .cf-radio-button__xs:not(:last-of-type) { - margin-bottom: $cf-marg-b; + margin-bottom: $cf-marg-a; } & .cf-radio-button__sm:not(:last-of-type) { - margin-bottom: $cf-marg-c; + margin-bottom: $cf-marg-b; } & .cf-radio-button__md:not(:last-of-type) { - margin-bottom: $cf-marg-d; + margin-bottom: $cf-marg-c; } & .cf-radio-button__lg:not(:last-of-type) { margin-bottom: $cf-marg-d; From eeaf2e70b0a443dd799b1c623d2f90f078ab4c23 Mon Sep 17 00:00:00 2001 From: Dong Kyun Kim Date: Fri, 5 Mar 2021 10:32:36 -0500 Subject: [PATCH 4/4] clean up props that I was using for testing --- src/Components/RadioButton/RadioButtonGroup.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/RadioButton/RadioButtonGroup.tsx b/src/Components/RadioButton/RadioButtonGroup.tsx index d5302a48..d4585b9e 100644 --- a/src/Components/RadioButton/RadioButtonGroup.tsx +++ b/src/Components/RadioButton/RadioButtonGroup.tsx @@ -38,7 +38,7 @@ export type RadioButtonGroupRef = HTMLDivElement export const RadioButtonGroup = forwardRef< RadioButtonGroupRef, RadioButtonGroupProps ->(({orientation, children, name, onChange, activeValue = 'test-1'}, ref) => { +>(({orientation, children, name, onChange, activeValue = ''}, ref) => { const [selectedValue, setSelectedValue] = useState(activeValue) const radioButtonGroupClass = classnames('cf-radio-button-group', { [`cf-radio-button-group__${orientation}`]: orientation,