Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/salary and benefits calculator #506

Merged
merged 10 commits into from
Aug 26, 2024
8 changes: 7 additions & 1 deletion src/components/forms/inputField/InputField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ interface InputFieldProps {
autoComplete?: HTMLInputAutoCompleteAttribute;
autoCorrect?: string;
type?: HTMLInputTypeAttribute;
max?: number;
min?: number;
spellCheck?: "true" | "false";
autoCapitalize?: string;
value: string;
value: string | number;
onChange: (name: string, value: string) => void;
required?: boolean;
}
Expand All @@ -24,6 +26,8 @@ const InputField = ({
autoComplete,
autoCorrect = "off",
type = "text",
max,
min,
spellCheck,
autoCapitalize,
value,
Expand Down Expand Up @@ -56,6 +60,8 @@ const InputField = ({
autoComplete={autoComplete}
autoCorrect={autoCorrect}
type={type}
max={type === "number" ? max : undefined}
min={type === "number" ? min : undefined}
className={styles.input}
spellCheck={spellCheck}
value={value}
anemne marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
118 changes: 59 additions & 59 deletions src/components/forms/radioButtonGroup/RadioButtonGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,69 +1,43 @@
import React from "react";
import styles from "src/components/forms/radioButtonGroup/radioButtonGroup.module.css";
import { RadioButton } from "./components/RadioButton";
import textStyles from "src/components/text/text.module.css";

interface IOption {
export interface IOption {
id: string;
label: string;
disabled: boolean;
currentChecked: boolean;
disabled?: boolean;
}

interface RenderOptionsProps {
options: IOption[];
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
interface RadioButtonProps {
id: string;
name: string;
label: string;
value?: string;
checked?: boolean;
disabled?: boolean;
defaultChecked?: boolean;
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
}

interface RadioButtonGroupProps {
id: string;
label: string;
options: IOption[];
selectedId: string;
onValueChange: (option: IOption) => void;
}

/**
* Important Note on RadioButtons:
*
* - The `RadioButton` component, defined in this code, should not be used in isolation.
* Radio buttons are designed to be part of a group where only one option can be selected at a time.
*
* - When used individually, a radio button loses its intended functionality of providing mutually exclusive choices
* and may confuse users or lead to unexpected behavior.
*
* - Instead, radio buttons should always be used within a group, typically managed by a parent component
* such as `RadioButtonGroup`, which ensures that only one radio button in the group can be selected at any given time.
*
* - The parent component should handle the state management and changes, ensuring that the user can only select
* one option from the group and that this selection is properly communicated back to the application.
*
* - Example of usage within a group:
*
* ```
* const options = [
* { id: 'radio1', label: 'Option 1', value: '1', currentChecked: false },
* { id: 'radio2', label: 'Option 2', value: '2', currentChecked: true },
* ];
*
* <RadioButtonGroup
* id="example-group"
* label="Choose an option"
* options={options}
* onValueChange={(name, value) => console.log(`Selected ${name}: ${value}`)}
* />
* ```
* - In this example, the `RadioButtonGroup` component renders multiple `RadioButton` components
* as part of a cohesive group, enabling proper radio button functionality.
*/

export const RadioButtonGroup = ({
id,
label,
options,
selectedId,
onValueChange,
}: RadioButtonGroupProps) => {
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const selectedOption = options.find((option) => option.id === e.target.id);
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const selectedOption = options.find(
(option) => option.id === e.target.value,
);
if (selectedOption) {
onValueChange(selectedOption);
}
Expand All @@ -73,27 +47,53 @@ export const RadioButtonGroup = ({
<fieldset className={styles.fieldset} id={id}>
<legend className={textStyles.h3}>{label}</legend>
<div className={styles.wrapper}>
<RenderOptions options={options} onChange={handleChange} />
{options.map(({ id, label, disabled }) => (
<RadioButton
key={id}
id={id}
label={label}
name="radio"
disabled={disabled}
value={id}
checked={id === selectedId}
onChange={onChange}
/>
))}
</div>
</fieldset>
);
};

const RenderOptions = ({ options, onChange }: RenderOptionsProps) => {
const RadioButton = ({
id,
name,
value,
label,
checked,
disabled,
defaultChecked,
onChange,
}: RadioButtonProps) => {
return (
<>
{options.map(({ id, label, disabled, currentChecked }) => (
<RadioButton
key={id}
id={id}
label={label}
name="radio"
disabled={disabled}
value={label}
defaultChecked={currentChecked}
onChange={onChange}
/>
))}
</>
<label
htmlFor={id}
className={`${styles.container} ${textStyles.caption} ${disabled ? styles.disabledLabel : styles.label}`}
>
<input
className={styles.input}
type="radio"
name={name}
id={id}
value={value}
checked={checked}
disabled={disabled}
aria-disabled={disabled ? "true" : "false"}
defaultChecked={defaultChecked}
onChange={onChange}
aria-label={label}
/>

{label}
</label>
);
};
48 changes: 0 additions & 48 deletions src/components/forms/radioButtonGroup/components/RadioButton.tsx

This file was deleted.

This file was deleted.

54 changes: 54 additions & 0 deletions src/components/forms/radioButtonGroup/radioButtonGroup.module.css
Original file line number Diff line number Diff line change
@@ -1,9 +1,63 @@
.fieldset {
border: 0 none;
padding: 0;
}

.wrapper {
display: flex;
flex-direction: column;
gap: 0.5rem;
}

.container {
display: flex;
gap: 1rem;
align-items: center;
}

.label {
font-size: 1rem;
color: var(--dark-purple);
}

.disabledLabel {
color: gray;
cursor: not-allowed;
}

.input {
appearance: none;
width: 24px;
height: 24px;
border: 2px solid black;
border-radius: 50%;
transition: all 0.1s ease-in-out;
box-sizing: border-box;
background-color: white;
position: relative;
}

.input:checked::after {
content: "";
position: absolute;
top: 4px;
left: 4px;
width: 12px;
height: 12px;
border-radius: 50%;
background-color: var(--primary-green-dark);
}

.input:hover {
background-color: var(--primary-green-light);
outline: 2px solid var(--primary-green-light);
}

.input:focus {
outline: 2px solid var(--primary-green-light);
}

.input[disabled] {
cursor: not-allowed;
border-color: grey;
}
Loading