feat: add SplitButton component#4998
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new Material Design 3 SplitButton component to React Native Paper as part of the v6 modernization effort, and wires it into the library’s exports, docs, and example app.
Changes:
- Introduces
SplitButtoncomponent implementation with size/mode tokens and styling utilities. - Adds unit tests for SplitButton rendering/handlers and several utility helpers.
- Exposes the component publicly and integrates it into docs + the example app (screen + docs config/theme color docs data).
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/index.tsx | Exports SplitButton and SplitButtonProps from the public entrypoint. |
| src/components/SplitButton/utils.ts | Adds MD3 color/shape/size helpers, ripple color derivation, and hitSlop expansion logic. |
| src/components/SplitButton/tokens.ts | Introduces SplitButton size/shape/elevation tokens. |
| src/components/SplitButton/SplitButton.tsx | Implements the SplitButton component UI, interaction handling, accessibility, and pressed-elevation/shape behavior. |
| src/components/SplitButton/index.ts | Barrel export for the new component and its props. |
| src/components/tests/SplitButton.test.tsx | Adds tests for SplitButton rendering, interaction separation, accessibility state, and several utils. |
| example/src/Examples/SplitButtonExample.tsx | Adds a SplitButton example screen with Menu-based playground and showcases. |
| example/src/ExampleList.tsx | Registers the SplitButton example in the example app’s main list. |
| docs/static/llms.txt | Adds SplitButton docs link to the LLMs index. |
| docs/src/data/themeColors.js | Adds SplitButton entries to the theme color documentation data. |
| docs/docusaurus.config.js | Registers SplitButton in the docs sidebar/config. |
| export type Props = $Omit< | ||
| React.ComponentProps<typeof Surface>, | ||
| 'children' | 'mode' | ||
| > & { |
| export const getSplitButtonRippleColor = ({ | ||
| contentColor, | ||
| customRippleColor, | ||
| }: { | ||
| contentColor: ColorValue; | ||
| customRippleColor?: ColorValue; | ||
| }): ColorValue | undefined => { | ||
| if (customRippleColor) { | ||
| return customRippleColor; | ||
| } | ||
|
|
||
| if (typeof contentColor !== 'string') { | ||
| return undefined; | ||
| } | ||
|
|
||
| return color(contentColor).alpha(stateOpacity.pressed).rgb().string(); | ||
| }; |
satya164
left a comment
There was a problem hiding this comment.
the outline split button design doesn't seem to match the screenshots on md guidelines. they look very dark
| export const resolveSplitButtonCorner = ( | ||
| theme: InternalTheme, | ||
| key: SplitButtonShapeKey | ||
| ) => (key === 'full' ? cornerFull : (theme as Theme).shapes.corner[key]); |
| import * as React from 'react'; | ||
| import { | ||
| AccessibilityState, | ||
| Animated, |
| * export default MyComponent; | ||
| * ``` | ||
| */ | ||
| const SplitButton = forwardRef<View, Props>( |
There was a problem hiding this comment.
remove forwardRef. the codebase in on react 19
though even then why does this component need ref?
| hitSlop, | ||
| trailingHitSlop, | ||
| theme: themeOverrides, | ||
| testID = 'split-button', |
There was a problem hiding this comment.
don't add default test ids. such a default will leave multiple test ids and may conflict with the user's own test ids - both of which maybe confusing
| const handleLeadingPressIn = React.useCallback( | ||
| (e: GestureResponderEvent) => { | ||
| onPressIn?.(e); | ||
| setPressedButton('leading'); | ||
| animateElevation(splitButtonElevation.pressed); | ||
| }, |
There was a problem hiding this comment.
use reanimated shared values instead of state for animation
Motivation
Implements the new Material Design 3 SplitButton component as part of the v6 component modernization work.
The component provides a primary leading action and a separate trailing action for contextual options. It reuses the modernized Button API direction
from #4928/#4943: explicit
label/iconprops, MD3 mode names (filled,tonal,elevated,outlined), derived ripple/state-layer colors,extracted component tokens, size metrics, shape tokens, disabled/loading states, and separate accessibility/press handlers for each button segment.
This also adds the component to the public exports, documentation generation config, theme color docs data, and the example app. The example includes a
Menu-based playground, mode showcase, loading/disabled controls, and custom color/label styling.
Related issue
Closes #4986
Related to #4928 and #4943
Test plan
yarn lint-no-fixsrc/components/__tests__/TextInput.test.tsx.yarn typescriptyarn test --watchman=falseyarn docs buildManual verification in the example app:
SplitButtonexample screen.Sendaction and trailing menu action separately.Disabledand verify both press targets are disabled and styled correctly.Loadingand verify the leading icon is replaced with a spinner.filled,tonal,elevated, andoutlined.