| name | handle-modus-checkbox-value-bug |
| description | Apply the critical checkbox value inversion workaround when working with ModusCheckbox components |
Handle Modus Checkbox Value Bug
Apply the critical value inversion workaround for ModusWcCheckbox components.
Critical Bug
The ModusWcCheckbox web component has a value inversion bug where the value property returns the opposite of the actual checked state:
- When checkbox is checked:
target.value returns false
- When checkbox is unchecked:
target.value returns true
This is the opposite of what developers expect.
When to Use
Use this skill when:
- Implementing checkbox functionality with ModusCheckbox
- Fixing checkbox-related bugs
- Handling checkbox value changes
- Creating forms with checkboxes
Reference Implementation
See src/components/ModusCheckbox.tsx:116-130 for the complete workaround implementation.
The Fix
Always invert the value when reading from checkbox events:
const handleValueChange = (event: Event) => {
const customEvent = event as CustomEvent<InputEvent>;
const rawValue = (customEvent.target as HTMLModusWcCheckboxElement).value;
const actualValue = !rawValue;
setChecked(actualValue);
};
Complete Pattern
import { useEffect, useRef } from "react";
import { ModusWcCheckbox } from "@trimble-oss/moduswebcomponents-react";
export default function ModusCheckbox({
value = false,
onValueChange,
}: {
value?: boolean;
onValueChange?: (event: CustomEvent<boolean>) => void;
}) {
const checkboxRef = useRef<HTMLModusWcCheckboxElement>(null);
useEffect(() => {
const checkbox = checkboxRef.current;
if (!checkbox) return;
const handleValueChange = (event: Event) => {
const customEvent = event as CustomEvent<InputEvent>;
const rawValue = (customEvent.target as HTMLModusWcCheckboxElement).value;
const actualValue = !rawValue;
const correctedEvent = new CustomEvent("valueChange", {
detail: actualValue,
bubbles: true,
cancelable: true,
});
onValueChange?.(correctedEvent);
};
if (onValueChange) {
checkbox.addEventListener("inputChange", handleValueChange);
}
return () => {
if (onValueChange) {
checkbox.removeEventListener("inputChange", handleValueChange);
}
};
}, [onValueChange]);
return (
<ModusWcCheckbox
ref={checkboxRef}
value={value}
/>
);
}
Usage in Forms
When using checkboxes in forms:
function MyForm() {
const [agreeToTerms, setAgreeToTerms] = useState(false);
return (
<ModusCheckbox
value={agreeToTerms}
onValueChange={(event) => {
// event.detail is already corrected (inverted)
setAgreeToTerms(event.detail);
}}
label="I agree to the terms and conditions"
/>
);
}
Multiple Checkboxes
When handling multiple checkboxes:
function CheckboxGroup() {
const [checkboxes, setCheckboxes] = useState({
option1: false,
option2: true,
option3: false,
});
const handleCheckboxChange = (key: string, event: CustomEvent<boolean>) => {
setCheckboxes(prev => ({
...prev,
[key]: event.detail,
}));
};
return (
<>
<ModusCheckbox
value={checkboxes.option1}
onValueChange={(e) => handleCheckboxChange('option1', e)}
label="Option 1"
/>
<ModusCheckbox
value={checkboxes.option2}
onValueChange={(e) => handleCheckboxChange('option2', e)}
label="Option 2"
/>
<ModusCheckbox
value={checkboxes.option3}
onValueChange={(e) => handleCheckboxChange('option3', e)}
label="Option 3"
/>
</>
);
}
Important Notes
- The bug is in
target.value: The inversion happens when reading event.target.value, not in event.detail
- Always invert: Never trust the raw value - always invert it
- Wrapper handles it: The
ModusCheckbox wrapper component already handles this, so if you're using the wrapper, you don't need to invert again
- Document the bug: Add comments explaining the workaround
Testing
To verify the fix works:
<ModusCheckbox
value={isChecked}
onValueChange={(event) => {
console.log('Raw value (inverted):', event.detail);
console.log('Expected checked:', isChecked);
}}
/>
Common Mistakes
- Forgetting to invert: Using raw value directly
- Double inversion: Inverting when using the wrapper component (it already handles it)
- Wrong event: Using
inputChange instead of reading target.value
Related Files
src/components/ModusCheckbox.tsx - Complete wrapper implementation
.cursor/rules/modus-checkbox-value-inversion-react.mdc - Detailed documentation