Skip to content

Commit

Permalink
RadioGroup Component
Browse files Browse the repository at this point in the history
Summary:
This is perhaps the first in our custom components. We encountered some bugs when using some components from the vscode-webview-ui-toolkit library. We can work around this, but for our purposes, it'll be easier to just have some custom components that work for our uses.

This diff begins with a RadioGroup element, specifically due to an issue that happens with the vscode library Radio, where [it won't listen to onChange events correctly, often missing the first instance](microsoft/vscode-webview-ui-toolkit#404). It's possible to work around, but it's an easy footgun you have to test for.

This is a simple radio implementation. It changes the API slightly from the microsoft one. Since you always expect to have multiple radio options, instead of having you render <Radio>s yourself, you just render one <RadioGroup> with a list of labels. Basically equivalent, but slightly easier IMO, since you don't need to set the value and name and everything yourself on each one, just define it once.

We use very similar styles to the vscode webview ui toolkit, plus theme variables, so it doens't look out of place at all.

Our focus behavior is slightly different, since we focus each radio option instead  of the whole group. I actually thought that was weird behavior from the ui toolkit. Now we justused the normal DOM `<input type="radio">`  behavior.

Reviewed By: quark-zju

Differential Revision: D54317950

fbshipit-source-id: f4310d04545a807f43d7db4c0105bbc8947fe13f
  • Loading branch information
evangrayk authored and facebook-github-bot committed Feb 28, 2024
1 parent 3599c18 commit 34bb744
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 0 deletions.
40 changes: 40 additions & 0 deletions addons/isl/src/components/Radio.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

.isl-radio {
background: var(--checkbox-background);
border-radius: 999px;
cursor: pointer;
height: 16px;
position: relative;
outline: none;
width: 16px;
appearance: none;
border: 1px solid var(--checkbox-border);
margin: 0;
}

.isl-radio:focus {
border-color: var(--focus-border);
}

.isl-radio:before {
display: inline-block;
position: absolute;
left: 0;
top: 0;
content: ' ';
inset: 4px;
border-radius: 50%;
transform: scale(0);
transition: 60ms transform ease-in-out;
background-color: var(--checkbox-foreground);
}

.isl-radio:checked:before {
transform: scale(1);
}
89 changes: 89 additions & 0 deletions addons/isl/src/components/Radio.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import type react from 'react';

import {Column} from '../ComponentUtils';
import {layout} from '../stylexUtils';
import {spacing} from '../tokens.stylex';
import * as stylex from '@stylexjs/stylex';
import {useId} from 'react';

// stylex doesn't support :checked and :before simultaneously very well
import './Radio.css';

const styles = stylex.create({
group: {
appearance: 'none',
border: 'none',
boxSizing: 'border-box',
alignItems: 'flex-start',
marginInline: 0,
marginBlock: spacing.pad,
padding: 0,
},
label: {
cursor: 'pointer',
},
});

export function RadioGroup<T extends string>({
title,
choices,
current,
onChange,
}: {
title?: string;
choices: Array<{value: T; title: react.ReactNode}>;
current: T;
onChange: (t: T) => unknown;
}) {
return (
<Column>
<strong>{title}</strong>
<fieldset {...stylex.props(layout.flexCol, styles.group)}>
{choices.map(({value, title}) => (
<Radio
key={value}
value={value}
title={title}
checked={current === value}
onChange={() => onChange(value)}
/>
))}
</fieldset>
</Column>
);
}

function Radio({
title,
value,
checked,
onChange,
}: {
title: react.ReactNode;
value: string;
checked: boolean;
onChange: () => unknown;
}) {
const id = useId();
return (
<label htmlFor={id} {...stylex.props(layout.flexRow, styles.label)}>
<input
type="radio"
id={id}
name={value}
value={value}
checked={checked}
onChange={onChange}
className="isl-radio"
/>
{title}
</label>
);
}

0 comments on commit 34bb744

Please sign in to comment.