components/select/select.react.tsx
53 lines and 5 functions in DrDustinEdwards/capsomer, at commit d3966cc on 2026-10-24. This file on GitHub.
Functions
| Function | Lines | Complexity | Covered | CRAP | Mutation |
|---|---|---|---|---|---|
| Select | 25 to 56 | 4 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| <anonymous:30> | 30 to 30 | 3 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| <anonymous:32> | 32 to 35 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| opt | 36 to 40 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
| <anonymous:44> | 44 to 51 | 2 | No data: no coverage report in this map | No data: no coverage report in this map | No data: mutation testing has not run on this repo yet |
Imports
It imports components/select/select.ts. No module of this repo imports it.
Source
import { useEffect, useId, useRef, type ReactNode, type SelectHTMLAttributes } from "react";
import { enhance, selectControlFor } from "./select.ts";
export interface SelectOption {
value: string;
label: ReactNode;
disabled?: boolean;
}
export interface SelectGroupOption {
group: string;
options: SelectOption[];
}
export interface SelectProps extends Omit<SelectHTMLAttributes<HTMLSelectElement>, "style" | "children" | "className" | "size"> {
// Options and groups: the same list the native select holds. Plain strings are fine for the label.
options: Array<SelectOption | SelectGroupOption>;
size?: "sm" | "default";
// Keep the browser's own select (no popup), as on a phone.
native?: boolean;
}
// Renders the HTML contract in select.md: the wrapper, with a real native select holding every
// option, so it server-renders. select.ts draws the button and the popup over it in an effect;
// React keeps owning the native select (value, options, disabled), and the trigger follows it.
export function Select(props: SelectProps) {
const { options, size = "default", native = false, id, ...rest } = props;
const own = useId();
const ref = useRef<HTMLDivElement>(null);
const selectId = id ?? own;
useEffect(() => (native || !ref.current ? undefined : enhance(ref.current)), [native]);
// The value is controlled by React: after each render the trigger is read again.
useEffect(() => {
const select = ref.current?.querySelector("select");
selectControlFor(select ?? null)?.sync();
});
const opt = (o: SelectOption) => (
<option key={o.value} value={o.value} disabled={o.disabled}>
{o.label}
</option>
);
return (
<div className="cap-select-wrap" data-cap="select" data-native={native ? "" : undefined} ref={ref}>
<select {...rest} id={selectId} className="cap-select" data-size={size === "default" ? undefined : size}>
{options.map((o) =>
"group" in o ? (
<optgroup key={o.group} label={o.group}>
{o.options.map(opt)}
</optgroup>
) : (
opt(o)
),
)}
</select>
</div>
);
}