Skip to content

Commit

Permalink
feat(RadioButton, RadioCard, RadioButtonGroup): extract radio input (#…
Browse files Browse the repository at this point in the history
…999)

* feat(RadioButton): extract radio input

* feat(RadioButton): fix lint

* feat(RadioButton): fix lint

* feat(RadioButton): fix lint

* feat(RadioButton): fix Eslint

* feat(RadioButton): fix Eslint

* feat(RadioButton): try to fix Eslint

* feat(RadioButton): try to fix Eslint rmv useId

* feat(RadioButton): fix max comments

* feat(RadioButton): fix lint

* feat(RadioButton): move className

---------

Co-authored-by: Maxime Meriouma-Caron <[email protected]>
  • Loading branch information
LarryMatte and meriouma authored Oct 29, 2024
1 parent 4d8cdab commit c691a0c
Show file tree
Hide file tree
Showing 10 changed files with 741 additions and 890 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ChangeEvent, useCallback, useRef, useState, VoidFunctionComponent } from 'react';
import styled from 'styled-components';
import { useDataAttributes } from '../../hooks/use-data-attributes';
import { focus } from '../../utils/css-state';
import { Tooltip, TooltipProps } from '../tooltip/tooltip';
import { RadioButton } from '../radio-button/radio-button';
import { useDeviceContext } from '../device-context-provider/device-context-provider';

const StyledFieldset = styled.fieldset`
Expand All @@ -18,68 +18,16 @@ const StyledLegend = styled.legend<{ isMobile: boolean }>`
font-weight: var(--font-normal);
letter-spacing: 0.02rem;
line-height: ${({ isMobile }) => (isMobile ? '1.5rem' : '1.25rem')};
margin: 0;
margin-top: var(--spacing-1x);
margin-bottom: var(--spacing-1x);
width: fit-content;
`;

const StyledTooltip = styled(Tooltip)`
margin-left: calc(var(--spacing-1x) * 1.5);
const StyledRadioButton = styled(RadioButton)`
margin-bottom: var(--spacing-1x);
`;

const RadioWrapper = styled.div``;

const StyledLabel = styled.label<{ disabled?: boolean }>`
align-items: center;
display: inline-flex;
font-size: 0.875rem;
line-height: 1.5rem;
margin-top: var(--spacing-1x);
position: relative;
user-select: none;
input {
height: var(--size-1x);
left: 0;
margin: 0;
opacity: 0;
position: absolute;
width: var(--size-1x);
${(theme) => focus(theme, { selector: '+ .radioInput' })}
&:checked + .radioInput {
border: 2px solid ${({ theme }) => theme.component['radio-button-checked-border-color']};
&::after {
background-color: ${({ theme }) => theme.component['radio-button-checked-background-color']};
border-radius: 50%;
content: '';
height: var(--size-half);
left: 50%;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
width: var(--size-half);
}
}
}
.radioInput {
background-color: ${({ theme, disabled }) => (disabled ? theme.component['radio-button-disabled-background-color'] : theme.component['radio-button-background-color'])};
border: 1px solid ${({ theme, disabled }) => (disabled ? theme.component['radio-button-disabled-border-color'] : theme.component['radio-button-border-color'])};
border-radius: 50%;
box-sizing: border-box;
display: inline-block;
height: var(--size-1x);
margin-right: var(--spacing-1x);
position: relative;
width: var(--size-1x);
}
&:hover .radioInput {
border: 1px solid ${({ theme, disabled }) => (disabled ? theme.component['radio-button-disabled-hover-border-color'] : theme.component['radio-button-hover-border-color'])};
}
const StyledTooltip = styled(Tooltip)`
margin-left: calc(var(--spacing-1x) * 1.5);
`;

const ContentWrapper = styled.div<{ $isExpanded: boolean, $maxHeight?: number, $transitionDuration: number }>(({ $isExpanded, $maxHeight = 500, $transitionDuration }) => `
Expand All @@ -95,6 +43,7 @@ const InnerContent = styled.div<{ $isExpanded: boolean, $transitionStarted: bool
interface RadioButtonProps {
label: string;
value: string;
id?: string;
defaultChecked?: boolean;
disabled?: boolean;
content?: {
Expand All @@ -104,6 +53,8 @@ interface RadioButtonProps {
}

interface RadioButtonGroupProps {
ariaLabel?: string;
ariaLabelledBy?: string[];
className?: string;
id?: string;
label?: string;
Expand All @@ -119,6 +70,8 @@ interface RadioButtonGroupProps {
}

export const RadioButtonGroup: VoidFunctionComponent<RadioButtonGroupProps> = ({
ariaLabel,
ariaLabelledBy,
buttons,
className,
groupName,
Expand Down Expand Up @@ -177,22 +130,22 @@ export const RadioButtonGroup: VoidFunctionComponent<RadioButtonGroupProps> = ({
const isExpanded = currentChecked === button.value;

return (
<RadioWrapper key={`${groupName}-${button.value}`}>
<StyledLabel disabled={button.disabled}>
{' '}
<input
data-testid={`${dataTestId}-${button.value}`}
type="radio"
name={groupName}
value={button.value}
checked={checkedValue ? checkedValue === button.value : undefined}
defaultChecked={button.defaultChecked}
disabled={button.disabled}
onChange={handleChange}
/>
<span className="radioInput" />
{button.label}
</StyledLabel>
<>
<StyledRadioButton
key={`${groupName}-${button.value}`}
aria-label={ariaLabel}
aria-labelledby={ariaLabelledBy?.join(' ')}
checked={checkedValue ? checkedValue === button.value : undefined}
className={className}
data-testid={`${dataTestId}-${button.value}`}
defaultChecked={button.defaultChecked}
disabled={button.disabled}
id={button.id}
label={button.label}
name={groupName}
onChange={handleChange}
value={button.value}
/>
{button.content && (
<ContentWrapper
data-testid="content-wrapper"
Expand All @@ -209,7 +162,7 @@ export const RadioButtonGroup: VoidFunctionComponent<RadioButtonGroupProps> = ({
</InnerContent>
</ContentWrapper>
)}
</RadioWrapper>
</>
);
})}
</StyledFieldset>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Radio button matches snapshot 1`] = `
.c1 {
.c2 {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
Expand All @@ -14,44 +14,44 @@ exports[`Radio button matches snapshot 1`] = `
-ms-flex-negative: 0;
flex-shrink: 0;
height: var(--size-1x);
margin: var(--spacing-half) var(--spacing-1x) 0 0;
margin: 0;
position: relative;
width: var(--size-1x);
}
.c1 + {
.c2 {
outline: 2px solid transparent;
outline-offset: -2px;
}
.c1:focus + {
.c2:focus {
box-shadow: 0 0 0 2px #006296;
outline: 2px solid #84C6EA;
outline-offset: -2px;
}
.c1:checked {
.c2:checked {
background-image: url('data:image/svg+xml;utf8,%0A%20%20%20%20%20%20%20%20%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Ccircle%20cx%3D%228%22%20cy%3D%228%22%20r%3D%224%22%20fill%3D%22%23006296%22%2F%3E%0A%20%20%20%20%20%20%20%20%3C%2Fsvg%3E');
background-position: center;
background-repeat: no-repeat;
border: 2px solid #006296;
}
.c1:disabled + label {
.c2:disabled + label {
color: #B7BBC2;
}
.c1:hover:checked:not(:disabled) {
.c2:hover:checked:not(:disabled) {
border: 1px solid #000000;
}
.c1:hover:not(:checked) {
.c2:hover:not(:checked) {
border: 1px solid #000000;
}
.c2 {
.c3 {
font-size: 0.875rem;
line-height: 1.5rem;
line-height: 1.25rem;
margin-left: var(--spacing-1x);
}
Expand All @@ -67,31 +67,47 @@ exports[`Radio button matches snapshot 1`] = `
position: relative;
}
.c1 {
margin-top: var(--spacing-quarter);
}
<RadioButton
label="This is a label"
>
<styled.div>
<div
className="c0"
>
<styled.input
<Styled(RadioInput)
data-testid="radiobutton-uuid1"
id="uuid1"
type="radio"
>
<input
<RadioInput
className="c1"
data-testid="radiobutton-uuid1"
id="uuid1"
type="radio"
/>
</styled.input>
>
<styled.input
className="c1"
data-testid="radiobutton-uuid1"
id="uuid1"
type="radio"
>
<input
className="c2 c1"
data-testid="radiobutton-uuid1"
id="uuid1"
type="radio"
/>
</styled.input>
</RadioInput>
</Styled(RadioInput)>
<styled.label
data-testid="radiobutton-uuid1_label"
htmlFor="uuid1"
>
<label
className="c2"
className="c3"
data-testid="radiobutton-uuid1_label"
htmlFor="uuid1"
>
Expand Down
61 changes: 8 additions & 53 deletions packages/react/src/components/radio-button/radio-button.tsx
Original file line number Diff line number Diff line change
@@ -1,58 +1,11 @@
import { ChangeEvent, FunctionComponent } from 'react';
import styled from 'styled-components';
import { focus } from '../../utils/css-state';
import { RadioInput } from './radio-input';
import { useId } from '../../hooks/use-id';

const getDotSvgDataUrl = (color: string): string => {
const svg = `
<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="8" cy="8" r="4" fill="${color}"/>
</svg>`;
return `url('data:image/svg+xml;utf8,${encodeURIComponent(svg)}')`;
};

const StyledInput = styled.input<{ disabled?: boolean }>`
appearance: none;
background-color: ${({ theme, disabled }) => (disabled ? theme.component['radio-button-disabled-background-color'] : theme.component['radio-button-background-color'])};
border: 1px solid ${({ theme, disabled }) => (disabled ? theme.component['radio-button-disabled-border-color'] : theme.component['radio-button-border-color'])};
border-radius: 50%;
color: ${({ theme, disabled }) => (disabled ? theme.component['radio-button-disabled-background-color'] : theme.component['radio-button-background-color'])};
display: inline-block;
flex-shrink: 0;
height: var(--size-1x);
margin: var(--spacing-half) var(--spacing-1x) 0 0;
position: relative;
width: var(--size-1x);
${(theme) => focus(theme, { selector: '+' })}
&:checked {
background-image: ${({ theme, disabled }) => getDotSvgDataUrl(disabled ? theme.component['radio-button-disabled-checked-dot-color'] : theme.component['radio-button-checked-dot-color'])};
background-position: center;
background-repeat: no-repeat;
border: 2px solid ${({ theme, disabled }) => (disabled ? theme.component['radio-button-disabled-border-color'] : theme.component['radio-button-checked-border-color'])};
}
&:disabled {
& + label {
color: ${({ theme }) => theme.component['radio-button-disabled-label-color']};
}
}
&:hover {
&:checked:not(:disabled) {
border: 1px solid ${({ theme }) => theme.component['radio-button-hover-border-color']};
}
&:not(:checked) {
border: 1px solid ${({ theme, disabled }) => (disabled ? theme.component['radio-button-disabled-hover-border-color'] : theme.component['radio-button-hover-border-color'])};
}
}
`;

const StyledLabel = styled.label`
font-size: 0.875rem;
line-height: 1.5rem;
line-height: 1.25rem;
margin-left: var(--spacing-1x);
`;

Expand All @@ -62,6 +15,10 @@ const StyledContainer = styled.div`
position: relative;
`;

const StyledRadioInput = styled(RadioInput)`
margin-top: var(--spacing-quarter);
`;

interface RadioButtonProps {
ariaLabel?: string;
ariaLabelledBy?: string[];
Expand Down Expand Up @@ -93,13 +50,11 @@ export const RadioButton: FunctionComponent<RadioButtonProps> = ({
const inputId = useId(id);

return (
<StyledContainer>
<StyledInput
<StyledContainer className={className}>
<StyledRadioInput
data-testid={`radiobutton-${inputId}`}
id={inputId}
type="radio"
name={name}
className={className}
value={value}
defaultChecked={defaultChecked}
checked={checked}
Expand Down
Loading

0 comments on commit c691a0c

Please sign in to comment.