From e961a87d3fc60418825b9886fabe81a855336e01 Mon Sep 17 00:00:00 2001 From: edug Date: Mon, 6 Mar 2023 11:13:23 +0100 Subject: [PATCH] fix: Segment checked state (#3701) --- .../SegmentedButtonCustomColorCheck.tsx | 91 +++++++++++++++++++ .../src/Examples/SegmentedButtons/index.ts | 1 + .../src/Examples/SegmentedButtonsExample.tsx | 2 + .../SegmentedButtons/SegmentedButtonItem.tsx | 24 +++-- .../SegmentedButtons/SegmentedButtons.tsx | 4 + src/components/SegmentedButtons/utils.ts | 36 ++++++-- .../__tests__/SegmentedButton.test.tsx | 32 +++++++ .../SegmentedButton.test.tsx.snap | 8 +- 8 files changed, 180 insertions(+), 18 deletions(-) create mode 100644 example/src/Examples/SegmentedButtons/SegmentedButtonCustomColorCheck.tsx diff --git a/example/src/Examples/SegmentedButtons/SegmentedButtonCustomColorCheck.tsx b/example/src/Examples/SegmentedButtons/SegmentedButtonCustomColorCheck.tsx new file mode 100644 index 0000000000..919865d28b --- /dev/null +++ b/example/src/Examples/SegmentedButtons/SegmentedButtonCustomColorCheck.tsx @@ -0,0 +1,91 @@ +import * as React from 'react'; +import { StyleSheet } from 'react-native'; + +import { List, SegmentedButtons } from 'react-native-paper'; + +const themeMock = { + colors: { + onSurface: '#3700B3', + secondaryContainer: '#3700B3', + onSecondaryContainer: '#FFFFFF', + }, +}; + +const SegmentButtonCustomColorCheck = () => { + const [themeValue, setThemeValue] = React.useState(''); + const [colorValue, setColorValue] = React.useState(''); + + return ( + + Via Theme + + Via Props + + + ); +}; + +const styles = StyleSheet.create({ + button: { + flex: 1, + }, + group: { paddingHorizontal: 20, justifyContent: 'center' }, +}); + +export default SegmentButtonCustomColorCheck; diff --git a/example/src/Examples/SegmentedButtons/index.ts b/example/src/Examples/SegmentedButtons/index.ts index d430fd3212..9ec49d8c9a 100644 --- a/example/src/Examples/SegmentedButtons/index.ts +++ b/example/src/Examples/SegmentedButtons/index.ts @@ -6,3 +6,4 @@ export { default as SegmentedButtonOnlyIconsWithCheck } from './SegmentedButtonO export { default as SegmentedButtonMultiselect } from './SegmentedButtonMultiselect'; export { default as SegmentedButtonMultiselectIcons } from './SegmentedButtonMultiselectIcons'; export { default as SegmentedButtonDisabled } from './SegmentedButtonDisabled'; +export { default as SegmentButtonCustomColorCheck } from './SegmentedButtonCustomColorCheck'; diff --git a/example/src/Examples/SegmentedButtonsExample.tsx b/example/src/Examples/SegmentedButtonsExample.tsx index c6bf1df3ce..8b4d1cfa56 100644 --- a/example/src/Examples/SegmentedButtonsExample.tsx +++ b/example/src/Examples/SegmentedButtonsExample.tsx @@ -13,6 +13,7 @@ import { SegmentedButtonOnlyIconsWithCheck, SegmentedButtonWithDensity, SegmentedButtonWithSelectedCheck, + SegmentButtonCustomColorCheck, } from './SegmentedButtons'; type Props = { @@ -45,6 +46,7 @@ const SegmentedButtonExample = ({ navigation }: Props) => { + ); diff --git a/src/components/SegmentedButtons/SegmentedButtonItem.tsx b/src/components/SegmentedButtons/SegmentedButtonItem.tsx index f50186ae0a..ad33861651 100644 --- a/src/components/SegmentedButtons/SegmentedButtonItem.tsx +++ b/src/components/SegmentedButtons/SegmentedButtonItem.tsx @@ -32,6 +32,18 @@ export type Props = { * Icon to display for the `SegmentedButtonItem`. */ icon?: IconSource; + + /** + * @supported Available in v5.x with theme version 3 + * Custom color for unchecked Text and Icon. + */ + uncheckedColor?: string; + + /** + * @supported Available in v5.x with theme version 3 + * Custom color for checked Text and Icon. + */ + checkedColor?: string; /** * Whether the button is disabled. */ @@ -81,6 +93,8 @@ const SegmentedButtonItem = ({ disabled, style, showSelectedCheck, + checkedColor, + uncheckedColor, icon, testID, label, @@ -116,6 +130,8 @@ const SegmentedButtonItem = ({ checked, theme, disabled, + checkedColor, + uncheckedColor, }); const borderRadius = (isV3 ? 5 : 1) * roundness; @@ -185,16 +201,12 @@ const SegmentedButtonItem = ({ testID={`${testID}-check-icon`} style={[iconStyle, { transform: [{ scale: checkScale }] }]} > - + ) : null} {showIcon ? ( - + ) : null} void; label?: string; showSelectedCheck?: boolean; diff --git a/src/components/SegmentedButtons/utils.ts b/src/components/SegmentedButtons/utils.ts index 701fb90a84..87d343c8a9 100644 --- a/src/components/SegmentedButtons/utils.ts +++ b/src/components/SegmentedButtons/utils.ts @@ -11,6 +11,11 @@ type BaseProps = { checked: boolean; }; +type SegmentedButtonProps = { + checkedColor?: string; + uncheckedColor?: string; +} & BaseProps; + const DEFAULT_PADDING = 9; export const getSegmentedButtonDensityPadding = ({ @@ -124,25 +129,34 @@ const getSegmentedButtonBorderWidth = ({ const getSegmentedButtonTextColor = ({ theme, disabled, -}: Omit) => { + checked, + checkedColor, + uncheckedColor, +}: SegmentedButtonProps) => { if (theme.isV3) { if (disabled) { return theme.colors.onSurfaceDisabled; } - return theme.colors.onSurface; - } else { - if (disabled) { - return theme.colors.disabled; + if (checked) { + return checkedColor ?? theme.colors.onSecondaryContainer; } - return theme.colors.primary; + return uncheckedColor ?? theme.colors.onSurface; + } + + if (disabled) { + return theme.colors.disabled; } + // Primary color is used for checked state too. + return theme.colors.primary; }; export const getSegmentedButtonColors = ({ theme, disabled, checked, -}: BaseProps) => { + checkedColor, + uncheckedColor, +}: SegmentedButtonProps) => { const backgroundColor = getSegmentedButtonBackgroundColor({ theme, checked, @@ -152,7 +166,13 @@ export const getSegmentedButtonColors = ({ disabled, checked, }); - const textColor = getSegmentedButtonTextColor({ theme, disabled }); + const textColor = getSegmentedButtonTextColor({ + theme, + disabled, + checked, + checkedColor, + uncheckedColor, + }); const borderWidth = getSegmentedButtonBorderWidth({ theme }); return { backgroundColor, borderColor, textColor, borderWidth }; diff --git a/src/components/__tests__/SegmentedButton.test.tsx b/src/components/__tests__/SegmentedButton.test.tsx index 57613fce4f..2a3f169c7b 100644 --- a/src/components/__tests__/SegmentedButton.test.tsx +++ b/src/components/__tests__/SegmentedButton.test.tsx @@ -58,6 +58,38 @@ it('renders checked segmented button with selected check', () => { }); describe('getSegmentedButtonColors', () => { + it.each` + theme | disabled | checked | checkedColor | uncheckedColor | expected + ${getTheme()} | ${false} | ${true} | ${undefined} | ${undefined} | ${getTheme().colors.onSecondaryContainer} + ${getTheme()} | ${false} | ${false} | ${undefined} | ${undefined} | ${getTheme().colors.onSurface} + ${getTheme()} | ${true} | ${true} | ${undefined} | ${undefined} | ${getTheme().colors.onSurfaceDisabled} + ${getTheme()} | ${true} | ${false} | ${undefined} | ${undefined} | ${getTheme().colors.onSurfaceDisabled} + ${getTheme()} | ${false} | ${true} | ${'a125f5'} | ${undefined} | ${'a125f5'} + ${getTheme()} | ${false} | ${false} | ${undefined} | ${'000'} | ${'000'} + ${getTheme()} | ${false} | ${false} | ${'a125f5'} | ${'000'} | ${'000'} + ${getTheme()} | ${false} | ${false} | ${'a125f5'} | ${undefined} | ${getTheme().colors.onSurface} + ${getTheme()} | ${false} | ${true} | ${undefined} | ${'000'} | ${getTheme().colors.onSecondaryContainer} + ${getTheme(false, false)} | ${false} | ${false} | ${undefined} | ${undefined} | ${getTheme(false, false).colors.primary} + ${getTheme(false, false)} | ${false} | ${true} | ${undefined} | ${undefined} | ${getTheme(false, false).colors.primary} + ${getTheme(false, false)} | ${true} | ${false} | ${undefined} | ${undefined} | ${getTheme(false, false).colors.disabled} + ${getTheme(false, false)} | ${true} | ${true} | ${undefined} | ${undefined} | ${getTheme(false, false).colors.disabled} + ${getTheme(false, false)} | ${false} | ${false} | ${'a125f5'} | ${undefined} | ${getTheme(false, false).colors.primary} + ${getTheme(false, false)} | ${false} | ${true} | ${undefined} | ${'000'} | ${getTheme(false, false).colors.primary} + `( + 'returns $expected when disabled: $disabled, checked: $checked, checkedColor is $checkedColor and uncheckedColor is $uncheckedColor and isV3: $theme.isV3', + ({ theme, disabled, checked, checkedColor, uncheckedColor, expected }) => { + expect( + getSegmentedButtonColors({ + theme, + disabled, + checked, + checkedColor, + uncheckedColor, + }) + ).toMatchObject({ textColor: expected }); + } + ); + it('should return correct background color when checked and theme version 3', () => { expect( getSegmentedButtonColors({ diff --git a/src/components/__tests__/__snapshots__/SegmentedButton.test.tsx.snap b/src/components/__tests__/__snapshots__/SegmentedButton.test.tsx.snap index a22ece169a..069ef978c8 100644 --- a/src/components/__tests__/__snapshots__/SegmentedButton.test.tsx.snap +++ b/src/components/__tests__/__snapshots__/SegmentedButton.test.tsx.snap @@ -111,7 +111,7 @@ exports[`renders checked segmented button with selected check 1`] = ` style={ Array [ Object { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 25, 43, 1)", "fontSize": 18, }, Array [ @@ -163,7 +163,7 @@ exports[`renders checked segmented button with selected check 1`] = ` "textAlign": "center", }, Object { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 25, 43, 1)", "fontFamily": "System", "fontSize": 14, "fontWeight": "500", @@ -403,7 +403,7 @@ exports[`renders disabled segmented button 1`] = ` "textAlign": "center", }, Object { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 25, 43, 1)", "fontFamily": "System", "fontSize": 14, "fontWeight": "500", @@ -641,7 +641,7 @@ exports[`renders segmented button 1`] = ` "textAlign": "center", }, Object { - "color": "rgba(28, 27, 31, 1)", + "color": "rgba(29, 25, 43, 1)", "fontFamily": "System", "fontSize": 14, "fontWeight": "500",