wave
wave copied to clipboard
New ButtonGroup component
We recently needed a group of buttons to be able to select different weekdays.
So I went ahead and created this using the Button and Box component and styling them to our needs.
Styling code:
const ButtonGroup = styled(Box)`
border-radius: 4px;
overflow: hidden;
border: ${props =>
props.error ? `1px solid ${Colors.NEGATIVE_ORANGE_900}` : `1px solid ${Colors.AUTHENTIC_BLUE_200}`};
`;
const ButtonGroupItem = styled(Button)`
background: ${props => (props.selected ? Colors.ACTION_BLUE_900 : Colors.WHITE)};
border-top: none;
border-bottom: none;
border-left: none;
border-right: ${props =>
props.selected ? `1px solid ${Colors.ACTION_BLUE_900}` : `1px solid ${Colors.AUTHENTIC_BLUE_200}`};
padding: 0 8px;
min-width: 0;
border-radius: 0;
:nth-child(1) {
border-left: ${props =>
props.selected ? `1px solid ${Colors.ACTION_BLUE_900}` : `1px solid ${Colors.AUTHENTIC_BLUE_200}`};
}
&:hover {
background: ${props => (props.selected ? Colors.ACTION_BLUE_1000 : Colors.AUTHENTIC_BLUE_50)};
}
`;
JSX code:
<ButtonGroup mt={3} error={error && !rule.daysOfWeek.length} display="flex">
{days.map((day, index) => (
<ButtonGroupItem
type="button"
key={`time-restriction-day-${index}`}
variant={rule.daysOfWeek.includes(day.value) ? 'primary' : 'secondary'}
selected={rule.daysOfWeek.includes(day.value)}
size="small"
onClick={() => handleDayChange(day.value)}
>
{day.label}
</ButtonGroupItem>
))}
</ButtonGroup>
It feels a little bit hacky overriding a bunch of the styles, but it seemed like the best approach to have the elements look like all other buttons. What do you think about this solution and do you see this as something worth adding to wave?
Cool! We are using a hardcoded version of this UI-element already in GAME. And if I'm not mistaking @jonotrujillo implemented it. It is not looking 100% like in the design, but maybe he has somethings that could help you.
It's quite similar. The one I implemented is looking like this:
![Screen Shot 2021-06-02 at 17 21 49](https://user-images.githubusercontent.com/313951/120508057-accdaa80-c3c7-11eb-9030-b9a1eedd5901.png)
In my case, I implemented the options as checkboxes as I felt this was the closest native behaviour:
export interface WeekdaySelectorProp {
error?: boolean;
}
export const WeekdaySelector = styled(Box)<WeekdaySelectorProp>`
box-shadow: inset 0 0 0 0.0625rem
${({ error }) => (error ? Colors.NEGATIVE_ORANGE_900 : 'transparent')};
border-radius: 4px;
border: 1px solid
${({ error }) =>
error ? Colors.NEGATIVE_ORANGE_900 : Colors.AUTHENTIC_BLUE_200};
display: flex;
margin: calc(${Spaces[1]} - 1px) 0;
overflow: hidden;
`;
const OptionLabel = styled.label<{
inverted?: boolean;
}>`
background-color: ${({ inverted }) =>
inverted ? Colors.ACTION_BLUE_900 : 'transparent'};
border-right: 1px solid ${Colors.AUTHENTIC_BLUE_200};
color: ${({ inverted }) =>
inverted ? Colors.WHITE : Colors.AUTHENTIC_BLUE_1100};
cursor: pointer;
flex: 1;
overflow: hidden;
padding: 0 ${Spaces[1]};
text-align: center;
text-overflow: ellipsis;
white-space: nowrap;
&:last-child {
border-right: 0;
}
`;
const OptionText = styled(Text)`
font-size: 14px;
font-weight: 600;
line-height: ${Spaces[4]};
`;
export interface WeekdaySelectorOptionProps {
checked?: boolean;
label?: string;
onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
}
export const WeekdaySelectorOption: VFC<WeekdaySelectorOptionProps> = ({
checked,
label,
onChange,
}) => (
<OptionLabel inverted={checked}>
<input type="checkbox" checked={checked} onChange={onChange} hidden></input>
<OptionText inverted={checked}>{label}</OptionText>
</OptionLabel>
);
And used like this:
<WeekdaySelector error={hasError}>
<WeekdaySelectorOption
checked={modayChecked}
onChange={(event) => onChange(event.target.checked)}
label="Tuesday"
/>
<WeekdaySelectorOption
checked={tuesdayChecked}
onChange={(event) => onChange(event.target.checked)}
label="Tuesday"
/>
...
</WekdaySelector>
@phllipo: what is the use case to have variant
and selected
separately in ButtonGroupItem
?
@jonotrujillo oh that's also an interesting approach, wouldn't have thought of using checkbox, but from an API perspective it makes a lot of sense.
Initially I used the variant to have the usual button behaviour (colors, hover etc.) and then the selected for my styled component, but I probably overwrote most of the variant styling, so I can probably get rid of the variant.
Good point, I can also see something a little more compact when it comes to the selected button (checkboxes) here: https://baseweb.design/components/button-group/#checkbox-mode. Maybe we can make the checkbox-style "you can select multiple buttons" as one of more use cases. I can also imagine having two buttons in the table in the "actions" column for example, which both need to function as buttons.