-
Notifications
You must be signed in to change notification settings - Fork 209
/
RadioGroup.tsx
124 lines (109 loc) · 3.52 KB
/
RadioGroup.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import * as React from 'react';
import styled from '@emotion/styled';
import Radio, {RadioProps} from './Radio';
import {borderRadius, spacing, inputColors, colors} from '@workday/canvas-kit-react-core';
import {ErrorType, GrowthBehavior} from '@workday/canvas-kit-react-common';
export interface RadioGroupProps extends GrowthBehavior {
/**
* React children must be of type Radio and have at least two.
*/
children: React.ReactElement<RadioProps>[];
/**
* The value or index of the Radio that should be toggled on.
* If a string is passed, the Radio with the corresponding value will be selected.
* If a number is passed, ihe Radio with the corresponding index will be selected.
*/
value?: string | number;
name?: string;
error?: ErrorType;
/**
* Callback function when a button is selected, optional.
* If the selected button has a value, it will be returned.
* Otherwise, the index of the button in the group will be returned.
*/
onChange?: (value: string | number) => void;
}
const Container = styled('div')<Pick<RadioGroupProps, 'error' | 'grow'>>(
{
display: 'inline-block',
boxSizing: 'border-box',
'& > div': {
margin: `${spacing.xxs} ${spacing.zero}`,
'&:first-child': {
marginTop: spacing.xxxs,
},
'&:last-child': {
marginBottom: spacing.xxxs,
},
},
},
({grow}) => grow && {width: '100%'},
({error}) => {
let errorBorderColor;
let errorRingColor;
if (error === ErrorType.Error) {
errorRingColor = inputColors.error.border;
} else if (error === ErrorType.Alert) {
errorRingColor = inputColors.warning.border;
errorBorderColor = colors.cantaloupe600;
} else {
return {};
}
return {
borderRadius: borderRadius.m,
transition: '100ms box-shadow',
boxShadow: errorBorderColor
? `inset 0 0 0 1px ${errorBorderColor}, inset 0 0 0 3px ${errorRingColor}`
: `inset 0 0 0 2px ${errorRingColor}`,
padding: `${spacing.xxxs} ${spacing.xs}`,
margin: `-${spacing.xxxs} -${spacing.xs}`,
};
}
);
export default class RadioGroup extends React.Component<RadioGroupProps> {
static ErrorType = ErrorType;
static defaultProps = {
value: 0,
};
render(): React.ReactNode {
const {children, error, onChange, value, grow, ...elemProps} = this.props;
return (
<Container error={error} grow={grow} {...elemProps}>
{React.Children.map(children, this.renderChild)}
</Container>
);
}
private renderChild = (child: React.ReactElement<RadioProps>, index: number): React.ReactNode => {
if (typeof child.type === typeof Radio) {
const childProps = child.props;
const checked =
typeof this.props.value === 'number'
? index === this.props.value
: childProps.value === this.props.value;
const name = this.props.name ? this.props.name : childProps.name;
return React.cloneElement(child, {
checked,
name,
onChange: this.onRadioChange.bind(this, childProps.onChange, index),
});
}
return child;
};
private onRadioChange = (
existingOnChange: (e: React.SyntheticEvent) => void | undefined,
index: number,
event: React.MouseEvent<HTMLButtonElement>
): void => {
if (existingOnChange) {
existingOnChange(event);
}
const target = event.currentTarget;
if (target && this.props.onChange) {
if (target.value) {
this.props.onChange(target.value);
} else {
this.props.onChange(index);
}
}
};
}