Skip to content

Commit

Permalink
Fix handling of controlled state in toggle-able controls (#3834)
Browse files Browse the repository at this point in the history
* Split up checkbox tests into their own files

* Change files

* Add new Checkbox test

* Fix controlled toggling

* Remove console.log

* Change files

* isChecked should always follow internal state
  • Loading branch information
rurikoaraki authored Jan 15, 2025
1 parent 7bec589 commit b91697e
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import React from 'react';
import { Platform, View } from 'react-native';
import { Platform, Pressable, View, type ViewStyle } from 'react-native';

import { ButtonV1 as Button } from '@fluentui-react-native/button';
import { Checkbox } from '@fluentui-react-native/experimental-checkbox';
import { TextV1 } from '@fluentui-react-native/text';

import { mobileStyles } from '../Common/styles';

const pressableStyle: ViewStyle = { borderWidth: 1, padding: 4, gap: 4, flexDirection: 'row', alignItems: 'center' };

export const OtherCheckbox: React.FunctionComponent = () => {
const [isChecked, setisChecked] = React.useState(false);

Expand All @@ -17,24 +20,44 @@ export const OtherCheckbox: React.FunctionComponent = () => {
setisChecked(false);
}, []);

const memoizedStyles = React.useMemo(() => (Platform.OS === 'android' ? { ...mobileStyles.containerSpacedEvenly, height: 150 } : {}), []);
const onChange = React.useCallback((_e, checked: boolean) => {
setisChecked(checked);
}, []);

const onPress = React.useCallback(() => {
setisChecked(!isChecked);
}, [isChecked]);

const memoizedStyles = React.useMemo(
() => (Platform.OS === 'android' ? { ...mobileStyles.containerSpacedEvenly, height: 150 } : { gap: 8 }),
[],
);

return (
<View style={memoizedStyles}>
<Button onClick={setCheckedTrue} size="small">
Check controlled checkboxes below
</Button>
<Button onClick={setCheckedFalse} size="small">
Uncheck controlled checkboxes below
</Button>

<Checkbox label="This is a controlled Checkbox" checked={isChecked} />
{Platform.OS !== 'android' && (
<>
<Checkbox label="Checkbox rendered with labelPosition 'before' (controlled)" labelPosition="before" checked={isChecked} />
<Checkbox label="A required checkbox with other required text" required="**" />
</>
)}
<View>
<Button onClick={setCheckedTrue} size="small">
Check controlled checkboxes below
</Button>
<Button onClick={setCheckedFalse} size="small">
Uncheck controlled checkboxes below
</Button>
</View>

<View>
<Checkbox label="This is a controlled Checkbox" checked={isChecked} onChange={onChange} />
<Pressable style={pressableStyle} onPress={onPress}>
<Checkbox checked={isChecked} onChange={onChange} />
<TextV1>A controlled checkbox in a Pressable. Pressing Pressable also toggles Checkbox</TextV1>
</Pressable>

{Platform.OS !== 'android' && (
<>
<Checkbox label="Checkbox rendered with labelPosition 'before' (controlled)" labelPosition="before" checked={isChecked} />
<Checkbox label="A required checkbox with other required text" required="**" />
</>
)}
</View>
</View>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Fix controlled toggling",
"packageName": "@fluentui-react-native/interactive-hooks",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Add new Checkbox test",
"packageName": "@fluentui-react-native/tester",
"email": "[email protected]",
"dependentChangeType": "patch"
}
9 changes: 5 additions & 4 deletions packages/utils/interactive-hooks/src/useAsToggleWithEvent.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';

import type { InteractionEvent } from '.';
import type { InteractionEvent } from './events.types';
import { useControllableValue } from './useControllableValue';

export type OnToggleWithEventCallback = (e: InteractionEvent, value?: boolean) => void;
export type OnChangeWithEventCallback = (e: InteractionEvent) => void;
Expand All @@ -21,15 +22,15 @@ export function useAsToggleWithEvent(
checked?: boolean,
userCallback?: OnToggleWithEventCallback,
): [boolean, OnChangeWithEventCallback] {
const [isChecked, setChecked] = React.useState(defaultChecked ?? checked);
const [isChecked, setChecked] = useControllableValue(checked, defaultChecked);

const onChange = React.useCallback(
(e: any) => {
userCallback && userCallback(e, !isChecked);
setChecked(!isChecked);
},
[isChecked, setChecked],
[isChecked, setChecked, userCallback],
);

return [checked ?? isChecked, onChange];
return [isChecked, onChange];
}

0 comments on commit b91697e

Please sign in to comment.