Skip to content

Commit

Permalink
feat: remove old radio button and components
Browse files Browse the repository at this point in the history
  • Loading branch information
kostasdano authored and panvourtsis committed Nov 30, 2023
1 parent daa77ce commit aba634d
Show file tree
Hide file tree
Showing 11 changed files with 0 additions and 470 deletions.
143 changes: 0 additions & 143 deletions src/components/Radio/Radio.style.ts
Original file line number Diff line number Diff line change
@@ -1,143 +0,0 @@
import type { SerializedStyles } from '@emotion/react';
import { css } from '@emotion/react';
import type { Theme } from 'theme';
import { BASE_SHADE } from 'theme/palette';
import type { ColorScheme } from 'theme/types';
import { rem } from 'theme/utils';

import type { RadioProps } from './Radio';

const lightHoverColor = 'rgba(23, 91, 245, 0.07)';
const darkHoverColor = 'rgba(255, 255, 255, 0.1)';

const boxShadow = ({ colorScheme }: { colorScheme: ColorScheme }) => css`
box-shadow: 0 0 0 ${rem('6px')} ${colorScheme === 'dark' ? darkHoverColor : lightHoverColor},
inset 0 0 0 ${rem('12px')} ${colorScheme === 'dark' ? darkHoverColor : lightHoverColor};
`;

export const inputStyles: SerializedStyles = css`
top: 0;
left: 0;
width: 100%;
cursor: inherit;
height: 100%;
margin: 0;
padding: 0;
z-index: 1;
position: absolute;
opacity: 0;
&:disabled {
cursor: default;
}
`;

export const customRadioInnerHover =
(isFocused: boolean, isDisabled: boolean) =>
(theme: Theme): SerializedStyles =>
css`
position: absolute;
border-radius: 50%;
width: ${rem('24px')};
height: ${rem('24px')};
transition: all 0.2s ease;
${isFocused && !isDisabled && boxShadow({ colorScheme: theme.colorScheme })};
`;

export const customRadioWrapperStyles =
(isFocused: boolean, isDisabled: boolean) =>
(theme: Theme): SerializedStyles =>
css`
position: relative;
border-radius: 50%;
width: ${rem('24px')};
height: ${rem('24px')};
transition: box-shadow 0.3s ease;
${isFocused && !isDisabled && boxShadow({ colorScheme: theme.colorScheme })};
`;

const determineColorBasedOnState =
({
isChecked,
isDisabled,
isFilled,
}: Pick<RadioProps, 'isChecked' | 'isDisabled' | 'isFilled'>) =>
(theme: Theme) => {
if (isChecked) {
return `currentColor`;
}
if (isDisabled) {
return `${theme.utils.getColor('lightGrey', 250)}`;
}
if (isFilled) {
return `${theme.utils.getColor('lightGrey', 300)}`;
}

return `${theme.utils.getColor('lightGrey', 300)}`;
};

export const customRadioStyles =
(props: Pick<RadioProps, 'isChecked' | 'isDisabled' | 'isFilled'>) =>
(theme: Theme): SerializedStyles => {
return css`
transition: all 0.2s ease;
border-radius: 50%;
width: 100%;
height: 100%;
box-sizing: border-box;
position: absolute;
&:before {
content: '';
display: inline-block;
box-sizing: border-box;
margin: ${rem('2px')} ${rem('12px')} ${rem('2px')} ${rem('2px')};
border: solid 2px ${determineColorBasedOnState(props)(theme)};
border-radius: 50%;
width: ${rem('20px')};
height: ${rem('20px')};
vertical-align: top;
transition: border-color 0.2s;
}
&:after {
content: '';
display: block;
position: absolute;
top: ${rem('1px')};
left: ${rem('1px')};
border-radius: 50%;
width: ${rem('12px')};
height: ${rem('12px')};
background-color: ${determineColorBasedOnState(props)(theme)};
transform: translate(5px, 5px) scale(${props.isChecked ? '1' : '0'});
transition: transform 0.2s;
}
`;
};

export const wrapperStyles =
(isDisabled: boolean) =>
(theme: Theme): SerializedStyles =>
css`
position: relative;
border-radius: 50%;
width: ${rem('36px')};
height: ${rem('36px')};
color: ${theme.utils.getColor('primary', BASE_SHADE, 'normal')};
border: 0;
opacity: ${isDisabled ? 0.5 : 1};
cursor: pointer;
margin: 0;
display: inline-flex;
outline: 0;
align-items: center;
user-select: none;
vertical-align: middle;
-moz-appearance: none;
justify-content: center;
text-decoration: none;
-webkit-appearance: none;
-webkit-tap-highlight-color: transparent;
`;
43 changes: 0 additions & 43 deletions src/components/Radio/Radio.test.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +0,0 @@
import React from 'react';
import { render } from 'test';

import { fireEvent } from '../../test';
import Radio from './Radio';
import { Mock } from 'vitest';

describe('Radio', () => {
let mockOnClick: Mock<any, any>;

beforeEach(() => {
mockOnClick = vi.fn();
});

it('should render correctly', () => {
const { container } = render(<Radio />);
expect(container).toMatchSnapshot();
});

it('should change to checked on click', () => {
const { container } = render(<Radio onChange={mockOnClick} dataTestId={'test-check'} />);

const radio = container.querySelector('input[type="radio"]') as HTMLInputElement;

fireEvent.click(radio);

expect(mockOnClick).toHaveBeenCalledTimes(1);
expect(radio.checked).toBeTruthy();
});

it('disabled should not change to checked on click', () => {
const { container } = render(
<Radio isDisabled={true} onChange={mockOnClick} dataTestId={'test-disabled'} />
);

const radio = container.querySelector('input[type="radio"]') as HTMLInputElement;

fireEvent.click(radio);

expect(mockOnClick).toHaveBeenCalledTimes(0);
expect(radio.checked).toBeFalsy();
});
});
135 changes: 0 additions & 135 deletions src/components/Radio/Radio.tsx
Original file line number Diff line number Diff line change
@@ -1,135 +0,0 @@
import * as React from 'react';

import {
customRadioInnerHover,
customRadioStyles,
customRadioWrapperStyles,
inputStyles,
wrapperStyles,
} from './Radio.style';
import useTheme from '../../hooks/useTheme';
import { generateTestDataId } from '../../utils/helpers';
import type { TestId } from '../../utils/types';
import useRadioGroup from '../RadioGroup/useRadioGroup';

export type RadioProps = {
/** The value of the radio input. If no value is passed the default value, according to spec, is "on"
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/radio#value
*
* @default on
* */
value?: string | number;
/** Whether the radio input is selected or not. Defining this prop means the radio input is controlled */
isChecked?: boolean;
/** The onChange event handler for the radio input */
onChange?: React.ReactEventHandler;
/** The name of the radio input, in case you want to manually form a radio group */
name?: string;
/** Whether the radio input is disabled
*
* @default false
* */
isDisabled?: boolean;
/** ID property of the radio input */
id?: string;
/** Whether the radio input is required to be selected in the context of a form
*
* @default false
* */
isRequired?: boolean;
/** Whether the radio input is filled or outlined
*
* @default true
* */
isFilled?: boolean;
dataTestId?: TestId;
};

const Radio = React.forwardRef<HTMLInputElement, RadioProps>((props, ref) => {
const {
isChecked: isExternallyControlledChecked,
onChange,
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/radio#value
value = 'on',
name,
isDisabled = false,
id,
isRequired = false,
isFilled = true,
dataTestId,
} = props;
const [isFocused, setIsFocused] = React.useState(false);
const [isInternallyControlledChecked, setIsInternallyControlledChecked] = React.useState(false);
const radioGroup = useRadioGroup();
const theme = useTheme();

const handleFocus = () => {
setIsFocused(true);
};

const handleBlur = () => {
setIsFocused(false);
};

const handleChange = (e: React.SyntheticEvent) => {
if (isDisabled) return;

if (isExternallyControlledChecked === undefined) {
setIsInternallyControlledChecked((e.target as HTMLInputElement).checked);
}

if (onChange) {
onChange(e);
}

if (radioGroup) {
radioGroup.onChange(e);
}
};

// Checked value is either the externally controlled value,
// or based on the radioGroup provided values,
// or the internally controlled value
const isCheckedValue =
isExternallyControlledChecked ??
(radioGroup ? (radioGroup && radioGroup.value) === value : isInternallyControlledChecked);
const nameValue = name ?? (radioGroup && radioGroup.name);

return (
<span
css={wrapperStyles(isDisabled)}
data-testid={generateTestDataId('radio-input', dataTestId)}
>
<input
css={inputStyles}
onFocus={handleFocus}
onBlur={handleBlur}
onMouseLeave={handleBlur}
onMouseOver={handleFocus}
type="radio"
onChange={handleChange}
name={nameValue}
value={value}
disabled={isDisabled}
id={id}
required={isRequired}
checked={isCheckedValue}
ref={ref}
/>
<span css={customRadioWrapperStyles(isFocused, isDisabled)(theme)}>
<span
css={customRadioStyles({
isChecked: isCheckedValue,
isDisabled,
isFilled,
})(theme)}
/>
<span css={customRadioInnerHover(isFocused, isDisabled)(theme)} />
</span>
</span>
);
});

Radio.displayName = 'Radio';

export default Radio;
2 changes: 0 additions & 2 deletions src/components/Radio/index.ts

This file was deleted.

45 changes: 0 additions & 45 deletions src/components/RadioGroup/RadioGroup.mdx
Original file line number Diff line number Diff line change
@@ -1,45 +0,0 @@
import { Meta, Canvas, ArgTypes } from '@storybook/blocks';
import { withKnobs, boolean, array, select, text } from '@storybook/addon-knobs';
import Radio from '../Radio';
import RadioGroup from './RadioGroup';
import * as RadioGroupStories from './RadioGroup.stories';

<Meta of={RadioGroupStories} />

# RadioGroup

A utility component for easily creating radio groups.
`value` represents the current value of the `RadioGroup` as a whole.
If the `value` prop is defined it means the `RadioGroup` is controlled.
If the `defaultValue` prop is defined it means the `RadioGroup` is not controlled.
`RadioGroup` does **not** wrap the children in any DOM element, that's left as a user implementation

## Usage

```jsx
import { RadioGroup } from '@orfium/ictinus';

<RadioGroup
value={value}
name="group"
onChange={(e) => {
console.log(e.target.value);
}}
>
<Radio value="a" />
<Radio value="b" />
<Radio value="c" />
</RadioGroup>;
```

## Props

<ArgTypes of={RadioGroup} />

# Self controlled radio group

<Canvas of={RadioGroupStories.RadioWithOptions} />

# Self controlled radio group with onChange handler

<Canvas of={RadioGroupStories.RadioWithOptionsWithOnChangeHandler} />
Loading

0 comments on commit aba634d

Please sign in to comment.