diff --git a/apps/fluent-tester/src/TestComponents/Switch/SwitchTest.tsx b/apps/fluent-tester/src/TestComponents/Switch/SwitchTest.tsx
index d52285588a..84f3be88d0 100644
--- a/apps/fluent-tester/src/TestComponents/Switch/SwitchTest.tsx
+++ b/apps/fluent-tester/src/TestComponents/Switch/SwitchTest.tsx
@@ -96,6 +96,7 @@ const OnOffText: React.FunctionComponent = () => {
+
);
};
diff --git a/change/@fluentui-react-native-switch-407aa2c6-eb30-4bca-9b93-dc14436e8786.json b/change/@fluentui-react-native-switch-407aa2c6-eb30-4bca-9b93-dc14436e8786.json
new file mode 100644
index 0000000000..436156816f
--- /dev/null
+++ b/change/@fluentui-react-native-switch-407aa2c6-eb30-4bca-9b93-dc14436e8786.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "On/off text renders with labelPosition=before",
+ "packageName": "@fluentui-react-native/switch",
+ "email": "winlarry@microsoft.com",
+ "dependentChangeType": "patch"
+}
diff --git a/change/@fluentui-react-native-tester-3b07212a-3b7c-48bd-a9ba-516497167dc8.json b/change/@fluentui-react-native-tester-3b07212a-3b7c-48bd-a9ba-516497167dc8.json
new file mode 100644
index 0000000000..bd4600bc4d
--- /dev/null
+++ b/change/@fluentui-react-native-tester-3b07212a-3b7c-48bd-a9ba-516497167dc8.json
@@ -0,0 +1,7 @@
+{
+ "type": "patch",
+ "comment": "Add new example for Switch on/off text section",
+ "packageName": "@fluentui-react-native/tester",
+ "email": "winlarry@microsoft.com",
+ "dependentChangeType": "patch"
+}
diff --git a/packages/components/Switch/src/Switch.styling.ts b/packages/components/Switch/src/Switch.styling.ts
index 7bae5dec0b..364bd4d797 100644
--- a/packages/components/Switch/src/Switch.styling.ts
+++ b/packages/components/Switch/src/Switch.styling.ts
@@ -102,6 +102,16 @@ export const stylingSettings: UseStylingOptions ({
+ style: {
+ flexDirection: 'column',
+ alignItems: 'flex-start',
+ justifyContent: 'center',
+ },
+ }),
+ [],
+ ),
onOffText: buildProps(
(tokens: SwitchTokens, theme: Theme) => ({
style: {
diff --git a/packages/components/Switch/src/Switch.tsx b/packages/components/Switch/src/Switch.tsx
index 8a701ac09c..17ccf5eb80 100644
--- a/packages/components/Switch/src/Switch.tsx
+++ b/packages/components/Switch/src/Switch.tsx
@@ -1,9 +1,11 @@
/** @jsx withSlots */
+import type { StyleProp } from 'react-native';
import { View, AccessibilityInfo, Pressable, Animated, Platform } from 'react-native';
import type { UseSlots } from '@fluentui-react-native/framework';
-import { compose, mergeProps, withSlots } from '@fluentui-react-native/framework';
+import { compose, memoize, mergeProps, withSlots } from '@fluentui-react-native/framework';
import { Text } from '@fluentui-react-native/text';
+import type { TextStyle } from '@office-iss/react-native-win32';
import { stylingSettings } from './Switch.styling';
import type { SwitchType, SwitchState, SwitchProps } from './Switch.types';
@@ -44,6 +46,7 @@ export const Switch = compose({
track: Animated.View, // Conversion from View to Animated.View for Animated API to work
thumb: Animated.View,
toggleContainer: View,
+ onOffTextContainer: View,
onOffText: Text,
},
useRender: (userProps: SwitchProps, useSlots: UseSlots) => {
@@ -68,10 +71,10 @@ export const Switch = compose({
// now return the handler for finishing render
return (final: SwitchProps) => {
const { label, offText, onText, labelPosition, ...mergedProps } = mergeProps(switchInfo.props, final);
- const onOffText = switchInfo.state.toggled ? onText : offText;
const displayOnOffText = !!offText || !!onText;
const isReduceMotionEnabled = AccessibilityInfo.isReduceMotionEnabled;
const thumbAnimation = isReduceMotionEnabled ? { animationClass: 'Ribbon_SwitchThumb' } : null;
+
return (
{label}
@@ -80,10 +83,25 @@ export const Switch = compose({
- {displayOnOffText && {onOffText}}
+ {displayOnOffText && (
+ /**
+ * If the onText and offText are different lengths, this can cause unwanted shifting of the track, if labelPosition = "after",
+ * or the label, if labelPosition = "above". We fix this by rendering both texts and setting the height of the text to hide to
+ * zero. This way, even when not visible, the hidden text still takes up its width to prevent shifting.
+ */
+
+ {onText}
+ {offText}
+
+ )}
);
};
},
});
+
+const onOffTextStyleWorker = (text: 'on' | 'off', isOn: boolean): StyleProp => ({
+ height: (text === 'on' && isOn) || (text === 'off' && !isOn) ? undefined : 0,
+});
+const getOnOffTextStyle = memoize(onOffTextStyleWorker);
diff --git a/packages/components/Switch/src/Switch.types.ts b/packages/components/Switch/src/Switch.types.ts
index 25f17db8b3..049251505c 100644
--- a/packages/components/Switch/src/Switch.types.ts
+++ b/packages/components/Switch/src/Switch.types.ts
@@ -202,6 +202,7 @@ export interface SwitchSlotProps {
track: IViewProps;
thumb: IViewProps;
toggleContainer: IViewProps;
+ onOffTextContainer: IViewProps;
onOffText: TextProps;
}
diff --git a/packages/components/Switch/src/useSwitch.ts b/packages/components/Switch/src/useSwitch.ts
index faa6e12539..a06eac16bf 100644
--- a/packages/components/Switch/src/useSwitch.ts
+++ b/packages/components/Switch/src/useSwitch.ts
@@ -97,16 +97,6 @@ export const useSwitch = (props: SwitchProps, animationConfig?: AnimationConfig)
console.warn("The props 'defaultChecked' and 'checked' are mutually exclusive. Use only one of the props, do not use both.");
}
- if (labelPosition === 'after' || labelPosition === undefined) {
- if (__DEV__ && (!!props.onText || !!props.offText)) {
- console.warn(
- "The prop labelPosition's value of \"after\" and the props 'onText' or 'offText' are mutually exclusive. Try setting 'labelPosition' value to \"before\" or \"above\" instead.",
- );
- }
- props.onText = null;
- props.offText = null;
- }
-
const onClickWithFocus = useOnPressWithFocus(focusRef, toggleCallback);
const pressable = usePressableState({ ...rest, onPress: onClickWithFocus });
const onKeyUpProps = useKeyProps(toggleCallback, ' ', 'Enter');