From a2c6ab951318bae1e99e10a0206098ca8a0f7805 Mon Sep 17 00:00:00 2001 From: Oleksandr Zavarzin Date: Wed, 2 Sep 2026 16:38:53 +0200 Subject: [PATCH] refactor: remove ToggleButton --- docs/6.x/docs/guides/migration.md | 29 +- docs/component-docs.config.ts | 5 - docs/src/data/screenshots.ts | 3 - example/src/ExampleList.tsx | 2 - example/src/Examples/ToggleButtonExample.tsx | 123 ------ example/src/Examples/TooltipExample.tsx | 38 +- src/components/ToggleButton/ToggleButton.tsx | 162 -------- .../ToggleButton/ToggleButtonGroup.tsx | 74 ---- .../ToggleButton/ToggleButtonRow.tsx | 108 ----- src/components/ToggleButton/index.ts | 16 - src/components/ToggleButton/utils.ts | 14 - .../__tests__/ToggleButton.test.tsx | 54 --- .../__snapshots__/ToggleButton.test.tsx.snap | 391 ------------------ src/index.tsx | 4 - 14 files changed, 53 insertions(+), 970 deletions(-) delete mode 100644 example/src/Examples/ToggleButtonExample.tsx delete mode 100644 src/components/ToggleButton/ToggleButton.tsx delete mode 100644 src/components/ToggleButton/ToggleButtonGroup.tsx delete mode 100644 src/components/ToggleButton/ToggleButtonRow.tsx delete mode 100644 src/components/ToggleButton/index.ts delete mode 100644 src/components/ToggleButton/utils.ts delete mode 100644 src/components/__tests__/ToggleButton.test.tsx delete mode 100644 src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap diff --git a/docs/6.x/docs/guides/migration.md b/docs/6.x/docs/guides/migration.md index a4d7123a09..ae8aa27f84 100644 --- a/docs/6.x/docs/guides/migration.md +++ b/docs/6.x/docs/guides/migration.md @@ -25,7 +25,6 @@ The following props now accept animated styles returned from `useAnimatedStyle`. - `Searchbar`: `style` - `Snackbar`: `style` - `Surface`: `style` -- `ToggleButton`: `style` So you can use Reanimated's `useSharedValue` and `useAnimatedStyle` to animate these components instead of the React Native `Animated` API. @@ -279,3 +278,31 @@ const theme = { style={{ fontSize: 16, color: '#1C1B1F' }} /> ``` + +### ToggleButton + +`ToggleButton`, `ToggleButton.Group` and `ToggleButton.Row` were removed. For an +icon-only toggle, use `IconButton` with the `selected` prop. For a set of +mutually exclusive options, use `SegmentedButtons`. + +```tsx +// Before (v5) + + + + + +// After (v6) +<> + setValue('bold')} + /> + setValue('italic')} + /> + +``` diff --git a/docs/component-docs.config.ts b/docs/component-docs.config.ts index 4196e8e7bd..2cf106849c 100644 --- a/docs/component-docs.config.ts +++ b/docs/component-docs.config.ts @@ -149,11 +149,6 @@ const pages = { props: 'TextInputAccessoryProps', }, }, - ToggleButton: { - ToggleButton: 'ToggleButton/ToggleButton', - ToggleButtonGroup: 'ToggleButton/ToggleButtonGroup', - ToggleButtonRow: 'ToggleButton/ToggleButtonRow', - }, Tooltip: { Tooltip: 'Tooltip/Tooltip', }, diff --git a/docs/src/data/screenshots.ts b/docs/src/data/screenshots.ts index 92bf8f2788..b7c59d424d 100644 --- a/docs/src/data/screenshots.ts +++ b/docs/src/data/screenshots.ts @@ -149,9 +149,6 @@ export const screenshots = { filled: 'screenshots/text-input-filled.png', outlined: 'screenshots/text-input-outlined.png', }, - ToggleButton: 'screenshots/toggle-button.png', - 'ToggleButton.Group': 'screenshots/toggle-button-group.gif', - 'ToggleButton.Row': 'screenshots/toggle-button-row.gif', Tooltip: 'screenshots/tooltip.png', TouchableRipple: 'screenshots/touchable-ripple.gif', }; diff --git a/example/src/ExampleList.tsx b/example/src/ExampleList.tsx index 8a2646d798..2af1eed385 100644 --- a/example/src/ExampleList.tsx +++ b/example/src/ExampleList.tsx @@ -44,7 +44,6 @@ import TextExample from './Examples/TextExample'; import TextInputExample from './Examples/TextInputExample'; import ThemeExample from './Examples/ThemeExample'; import ThemingWithReactNavigation from './Examples/ThemingWithReactNavigation'; -import ToggleButtonExample from './Examples/ToggleButtonExample'; import TooltipExample from './Examples/TooltipExample'; import TouchableRippleExample from './Examples/TouchableRippleExample'; @@ -83,7 +82,6 @@ export const mainExamples = { Switch: SwitchExample, Text: TextExample, TextInput: TextInputExample, - ToggleButton: ToggleButtonExample, TooltipExample, TouchableRipple: TouchableRippleExample, Theme: ThemeExample, diff --git a/example/src/Examples/ToggleButtonExample.tsx b/example/src/Examples/ToggleButtonExample.tsx deleted file mode 100644 index f07caaaffb..0000000000 --- a/example/src/Examples/ToggleButtonExample.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import * as React from 'react'; -import { View, StyleSheet, ImageBackground } from 'react-native'; - -import { ToggleButton, List } from 'react-native-paper'; - -import ScreenWrapper from '../ScreenWrapper'; - -type StatusState = 'checked' | 'unchecked'; -type Fruits = 'watermelon' | 'strawberries'; - -const fonts = { - noFormat: 'no-format', - italic: 'italic', - bold: 'bold', - underline: 'underlined', - colorText: 'format-color', -} as const; - -type Font = (typeof fonts)[keyof typeof fonts]; - -const ToggleButtonExample = () => { - const [first, setFirst] = React.useState('bold'); - const [fruit, setFruit] = React.useState('watermelon'); - const [status, setStatus] = React.useState('checked'); - const [font, setFont] = React.useState(fonts.noFormat); - - const handleFruit = (value: Fruits) => setFruit(value); - - return ( - - - - - setStatus(status === 'checked' ? 'unchecked' : 'checked') - } - /> - - - - setFirst(value)} - style={styles.padding} - > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; - -ToggleButtonExample.title = 'Toggle Button'; - -const styles = StyleSheet.create({ - padding: { - paddingHorizontal: 16, - }, - row: { - flexDirection: 'row', - }, - customImage: { - width: 143, - height: 153, - margin: 2, - }, - customButton: { - position: 'absolute', - right: 0, - }, -}); - -export default ToggleButtonExample; diff --git a/example/src/Examples/TooltipExample.tsx b/example/src/Examples/TooltipExample.tsx index 8e0802d4a4..50e5fc2285 100644 --- a/example/src/Examples/TooltipExample.tsx +++ b/example/src/Examples/TooltipExample.tsx @@ -10,7 +10,6 @@ import { FAB, IconButton, List, - ToggleButton, Tooltip, Card, } from 'react-native-paper'; @@ -40,7 +39,7 @@ const formOfTransport = [ const TooltipExample = () => { const navigation = useNavigation('TooltipExample'); - const [textAlign, setTextAlign] = React.useState('bold'); + const [textAlign, setTextAlign] = React.useState('left'); React.useLayoutEffect(() => { navigation.setOptions({ header: () => ( @@ -92,22 +91,34 @@ const TooltipExample = () => { ))} - - + + - + setTextAlign('left')} + /> - + setTextAlign('center')} + /> - + setTextAlign('right')} + /> - + @@ -179,7 +190,8 @@ const styles = StyleSheet.create({ cardContainer: { margin: 16, }, - toggleButtonRow: { + toggleRow: { + flexDirection: 'row', paddingHorizontal: 16, }, iconButtonContainer: { diff --git a/src/components/ToggleButton/ToggleButton.tsx b/src/components/ToggleButton/ToggleButton.tsx deleted file mode 100644 index 50bb50547e..0000000000 --- a/src/components/ToggleButton/ToggleButton.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import * as React from 'react'; -import { StyleSheet, View } from 'react-native'; -import type { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native'; - -import type { AnimatedStyle } from 'react-native-reanimated'; - -import { ToggleButtonGroupContext } from './ToggleButtonGroup'; -import { getToggleButtonColor } from './utils'; -import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; -import type { IconSource } from '../Icon'; -import IconButton from '../IconButton/IconButton'; - -export type Props = { - /** - * Icon to display for the `ToggleButton`. - */ - icon: IconSource; - /** - * Size of the icon. - */ - size?: number; - /** - * Custom text color for button. - */ - iconColor?: string; - /** - * Whether the button is disabled. - */ - disabled?: boolean; - /** - * Accessibility label for the `ToggleButton`. This is read by the screen reader when the user taps the button. - */ - 'aria-label'?: string; - /** - * Function to execute on press. - */ - onPress?: (value?: GestureResponderEvent | string) => void; - /** - * Value of button. - */ - value?: string; - /** - * Status of button. - */ - status?: 'checked' | 'unchecked'; - style?: StyleProp>; - /** - * @optional - */ - theme?: ThemeProp; - ref?: React.Ref; - /** - * testID to be used on tests. - */ - testID?: string; -}; - -/** - * Toggle buttons can be used to group related options. To emphasize groups of related toggle buttons, - * a group should share a common container. - * - * ## Usage - * ```js - * import * as React from 'react'; - * import { ToggleButton } from 'react-native-paper'; - * - * const ToggleButtonExample = () => { - * const [status, setStatus] = React.useState('checked'); - * - * const onButtonToggle = value => { - * setStatus(status === 'checked' ? 'unchecked' : 'checked'); - * }; - * - * return ( - * - * ); - * }; - * - * export default ToggleButtonExample; - * - * ``` - */ -const ToggleButton = ({ - icon, - size, - theme: themeOverrides, - 'aria-label': ariaLabel, - disabled, - style, - value, - status, - onPress, - ref, - ...rest -}: Props) => { - const theme = useInternalTheme(themeOverrides); - const borderRadius = theme.shapes.corner.extraSmall; - - return ( - - {(context: { value: string | null; onValueChange: Function } | null) => { - const checked: boolean | null = - (context && context.value === value) || status === 'checked'; - - const backgroundColor = getToggleButtonColor({ theme, checked }); - const borderColor = theme.colors.outline; - - return ( - { - if (onPress) { - onPress(e); - } - - if (context) { - context.onValueChange(!checked ? value : null); - } - }} - size={size} - aria-label={ariaLabel} - aria-disabled={disabled} - aria-selected={checked} - disabled={disabled} - style={[ - styles.content, - { - backgroundColor, - borderRadius, - borderColor, - }, - style, - ]} - ref={ref} - theme={theme} - {...rest} - /> - ); - }} - - ); -}; - -const styles = StyleSheet.create({ - content: { - width: 42, - height: 42, - margin: 0, - }, -}); - -export default ToggleButton; - -// @component-docs ignore-next-line -export { ToggleButton }; diff --git a/src/components/ToggleButton/ToggleButtonGroup.tsx b/src/components/ToggleButton/ToggleButtonGroup.tsx deleted file mode 100644 index f3e52356d8..0000000000 --- a/src/components/ToggleButton/ToggleButtonGroup.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import * as React from 'react'; - -export type Props = { - /** - * Function to execute on selection change. - */ - onValueChange: (value: Value) => void; - /** - * Value of the currently selected toggle button. - */ - value: Value | null; - /** - * React elements containing toggle buttons. - */ - children: React.ReactNode; -}; - -type ToggleButtonContextType = { - value: Value | null; - onValueChange: (item: Value) => void; -}; - -export const ToggleButtonGroupContext = - //@ts-expect-error: TS can't ensure the type from Group to children - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - React.createContext(null as any); - -/** - * Toggle group allows to control a group of toggle buttons.
- * It doesn't change the appearance of the toggle buttons. If you want to group them in a row, check out [ToggleButton.Row](ToggleButtonRow). - * - * ## Usage - * ```js - * import * as React from 'react'; - * import { ToggleButton } from 'react-native-paper'; - * - * const MyComponent = () => { - * const [value, setValue] = React.useState('left'); - * - * return ( - * setValue(value)} - * value={value} - * > - * - * - * - * ); - * }; - * - * export default MyComponent; - *``` - */ -const ToggleButtonGroup = ({ - value, - onValueChange, - children, -}: Props) => ( - - {children} - -); - -ToggleButtonGroup.displayName = 'ToggleButton.Group'; - -export default ToggleButtonGroup; - -// @component-docs ignore-next-line -export { ToggleButtonGroup }; diff --git a/src/components/ToggleButton/ToggleButtonRow.tsx b/src/components/ToggleButton/ToggleButtonRow.tsx deleted file mode 100644 index 46e6aee48f..0000000000 --- a/src/components/ToggleButton/ToggleButtonRow.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import * as React from 'react'; -import { StyleSheet, View } from 'react-native'; -import type { StyleProp, ViewStyle } from 'react-native'; - -import ToggleButton from './ToggleButton'; -import ToggleButtonGroup from './ToggleButtonGroup'; - -export type Props = { - /** - * Function to execute on selection change. - */ - onValueChange: (value: string) => void; - /** - * Value of the currently selected toggle button. - */ - value: string; - /** - * React elements containing toggle buttons. - */ - children: React.ReactNode; - style?: StyleProp; -}; - -/** - * Toggle button row renders a group of toggle buttons in a row. - * - * ## Usage - * ```js - * import * as React from 'react'; - * import { ToggleButton } from 'react-native-paper'; - * - * const MyComponent = () => { - * const [value, setValue] = React.useState('left'); - * - * return ( - * setValue(value)} value={value}> - * - * - * - * ); - * }; - * - * export default MyComponent; - * - *``` - */ -const ToggleButtonRow = ({ value, onValueChange, children, style }: Props) => { - const count = React.Children.count(children); - - return ( - - - {React.Children.map(children, (child, i) => { - // @ts-expect-error: TypeScript complains about child.type but it doesn't matter - if (child && child.type === ToggleButton) { - // @ts-expect-error: We're sure that child is a React Element - return React.cloneElement(child, { - style: [ - styles.button, - i === 0 - ? styles.first - : i === count - 1 - ? styles.last - : styles.middle, - // @ts-expect-error: We're sure that child is a React Element - child.props.style, - ], - }); - } - - return child; - })} - - - ); -}; - -ToggleButtonRow.displayName = 'ToggleButton.Row'; - -const styles = StyleSheet.create({ - row: { - flexDirection: 'row', - }, - button: { - borderWidth: StyleSheet.hairlineWidth, - }, - - first: { - borderTopRightRadius: 0, - borderBottomRightRadius: 0, - }, - - middle: { - borderRadius: 0, - borderLeftWidth: 0, - }, - - last: { - borderLeftWidth: 0, - borderTopLeftRadius: 0, - borderBottomLeftRadius: 0, - }, -}); - -export default ToggleButtonRow; - -// @component-docs ignore-next-line -export { ToggleButtonRow }; diff --git a/src/components/ToggleButton/index.ts b/src/components/ToggleButton/index.ts deleted file mode 100644 index 222d93fb52..0000000000 --- a/src/components/ToggleButton/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import ToggleButtonComponent from './ToggleButton'; -import ToggleButtonGroup from './ToggleButtonGroup'; -import ToggleButtonRow from './ToggleButtonRow'; - -const ToggleButton = Object.assign( - // @component ./ToggleButton.tsx - ToggleButtonComponent, - { - // @component ./ToggleButtonGroup.tsx - Group: ToggleButtonGroup, - // @component ./ToggleButtonRow.tsx - Row: ToggleButtonRow, - } -); - -export default ToggleButton; diff --git a/src/components/ToggleButton/utils.ts b/src/components/ToggleButton/utils.ts deleted file mode 100644 index e471a5b4b6..0000000000 --- a/src/components/ToggleButton/utils.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { InternalTheme } from '../../types'; - -export const getToggleButtonColor = ({ - theme, - checked, -}: { - theme: InternalTheme; - checked: boolean | null; -}) => { - if (checked) { - return theme.colors.surfaceContainerHighest; - } - return theme.colors.surfaceContainer; -}; diff --git a/src/components/__tests__/ToggleButton.test.tsx b/src/components/__tests__/ToggleButton.test.tsx deleted file mode 100644 index bd798e8180..0000000000 --- a/src/components/__tests__/ToggleButton.test.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { describe, expect, it } from '@jest/globals'; - -import { getTheme } from '../../core/theming'; -import { render } from '../../test-utils'; -import ToggleButton from '../ToggleButton'; -import { getToggleButtonColor } from '../ToggleButton/utils'; - -it('renders toggle button', async () => { - const tree = ( - await render( - {}} icon="heart" /> - ) - ).toJSON(); - - expect(tree).toMatchSnapshot(); -}); - -it('renders disabled toggle button', async () => { - const tree = ( - await render( - - ) - ).toJSON(); - - expect(tree).toMatchSnapshot(); -}); - -it('renders unchecked toggle button', async () => { - const tree = ( - await render() - ).toJSON(); - - expect(tree).toMatchSnapshot(); -}); - -describe('getToggleButtonColor', () => { - it('should return correct color when checked and theme version 3', () => { - expect(getToggleButtonColor({ theme: getTheme(), checked: true })).toBe( - getTheme().colors.surfaceContainerHighest - ); - }); - - it('should return correct color when checked and theme version 3, dark theme', () => { - expect(getToggleButtonColor({ theme: getTheme(true), checked: true })).toBe( - getTheme(true).colors.surfaceContainerHighest - ); - }); - - it('should return correct color when not checked', () => { - expect(getToggleButtonColor({ theme: getTheme(), checked: false })).toBe( - getTheme().colors.surfaceContainer - ); - }); -}); diff --git a/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap b/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap deleted file mode 100644 index 07f5de55eb..0000000000 --- a/src/components/__tests__/__snapshots__/ToggleButton.test.tsx.snap +++ /dev/null @@ -1,391 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders disabled toggle button 1`] = ` - - - - - heart - - - - -`; - -exports[`renders toggle button 1`] = ` - - - - - heart - - - - -`; - -exports[`renders unchecked toggle button 1`] = ` - - - - - heart - - - - -`; diff --git a/src/index.tsx b/src/index.tsx index 8863e2fa20..6811115258 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -47,7 +47,6 @@ export { default as Switch } from './components/Switch/Switch'; export { default as Appbar } from './components/Appbar'; export { default as TouchableRipple } from './components/TouchableRipple/TouchableRipple'; export { default as TextInput } from './components/TextInput'; -export { default as ToggleButton } from './components/ToggleButton'; export { default as SegmentedButtons } from './components/SegmentedButtons/SegmentedButtons'; export { default as Tooltip } from './components/Tooltip/Tooltip'; @@ -138,9 +137,6 @@ export type { TextInputAccessoryProps, TextInputIconProps, } from './components/TextInput/TextInputIcon'; -export type { Props as ToggleButtonProps } from './components/ToggleButton/ToggleButton'; -export type { Props as ToggleButtonGroupProps } from './components/ToggleButton/ToggleButtonGroup'; -export type { Props as ToggleButtonRowProps } from './components/ToggleButton/ToggleButtonRow'; export type { Props as TouchableRippleProps } from './components/TouchableRipple/TouchableRipple'; export type { Props as TextProps } from './components/Typography/Text'; export type { Props as SegmentedButtonsProps } from './components/SegmentedButtons/SegmentedButtons';