Skip to content

Commit

Permalink
fix: Segment checked state (callstack#3701)
Browse files Browse the repository at this point in the history
  • Loading branch information
gedu committed Mar 6, 2023
1 parent e34c3e4 commit e961a87
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -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 (
<List.Section title={`Segmented Button - Custom Colors`}>
<List.Subheader>Via Theme</List.Subheader>
<SegmentedButtons
value={themeValue}
onValueChange={setThemeValue}
theme={themeMock}
buttons={[
{
value: 'walk',
icon: 'walk',
label: 'Walking',
disabled: true,
style: styles.button,
},
{
value: 'train',
icon: 'train',
label: 'Transit',
style: styles.button,
},
{
value: 'drive',
icon: 'car',
label: 'Driving',
style: styles.button,
},
]}
style={styles.group}
/>
<List.Subheader>Via Props</List.Subheader>
<SegmentedButtons
value={colorValue}
onValueChange={setColorValue}
theme={themeMock}
buttons={[
{
value: 'walk',
icon: 'walk',
label: 'Walking',
checkedColor: '#F9AA33',
style: styles.button,
},
{
value: 'train',
icon: 'train',
showSelectedCheck: true,
checkedColor: '#F9AA33',
uncheckedColor: '#000000',
label: 'Transit',
style: styles.button,
},
{
value: 'drive',
icon: 'car',
checkedColor: '#F9AA33',
label: 'Driving',
style: styles.button,
},
]}
style={styles.group}
/>
</List.Section>
);
};

const styles = StyleSheet.create({
button: {
flex: 1,
},
group: { paddingHorizontal: 20, justifyContent: 'center' },
});

export default SegmentButtonCustomColorCheck;
1 change: 1 addition & 0 deletions example/src/Examples/SegmentedButtons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
2 changes: 2 additions & 0 deletions example/src/Examples/SegmentedButtonsExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
SegmentedButtonOnlyIconsWithCheck,
SegmentedButtonWithDensity,
SegmentedButtonWithSelectedCheck,
SegmentButtonCustomColorCheck,
} from './SegmentedButtons';

type Props = {
Expand Down Expand Up @@ -45,6 +46,7 @@ const SegmentedButtonExample = ({ navigation }: Props) => {
<SegmentedButtonOnlyIcons />
<SegmentedButtonMultiselect />
<SegmentedButtonMultiselectIcons />
<SegmentButtonCustomColorCheck />
<SegmentedButtonDisabled />
</ScreenWrapper>
);
Expand Down
24 changes: 18 additions & 6 deletions src/components/SegmentedButtons/SegmentedButtonItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -81,6 +93,8 @@ const SegmentedButtonItem = ({
disabled,
style,
showSelectedCheck,
checkedColor,
uncheckedColor,
icon,
testID,
label,
Expand Down Expand Up @@ -116,6 +130,8 @@ const SegmentedButtonItem = ({
checked,
theme,
disabled,
checkedColor,
uncheckedColor,
});

const borderRadius = (isV3 ? 5 : 1) * roundness;
Expand Down Expand Up @@ -185,16 +201,12 @@ const SegmentedButtonItem = ({
testID={`${testID}-check-icon`}
style={[iconStyle, { transform: [{ scale: checkScale }] }]}
>
<Icon source={'check'} size={iconSize} />
<Icon source={'check'} size={iconSize} color={textColor} />
</Animated.View>
) : null}
{showIcon ? (
<Animated.View testID={`${testID}-icon`} style={iconStyle}>
<Icon
source={icon}
size={iconSize}
color={disabled ? textColor : undefined}
/>
<Icon source={icon} size={iconSize} color={textColor} />
</Animated.View>
) : null}
<Text
Expand Down
4 changes: 4 additions & 0 deletions src/components/SegmentedButtons/SegmentedButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export type Props = {
* - `icon`: icon to display for the item
* - `disabled`: whether the button is disabled
* - `accessibilityLabel`: acccessibility label for the button. This is read by the screen reader when the user taps the button.
* - `checkedColor`: custom color for checked Text and Icon
* - `uncheckedColor`: custom color for unchecked Text and Icon
* - `onPress`: callback that is called when button is pressed
* - `label`: label text of the button
* - `showSelectedCheck`: show optional check icon to indicate selected state
Expand All @@ -63,6 +65,8 @@ export type Props = {
icon?: IconSource;
disabled?: boolean;
accessibilityLabel?: string;
checkedColor?: string;
uncheckedColor?: string;
onPress?: (event: GestureResponderEvent) => void;
label?: string;
showSelectedCheck?: boolean;
Expand Down
36 changes: 28 additions & 8 deletions src/components/SegmentedButtons/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ type BaseProps = {
checked: boolean;
};

type SegmentedButtonProps = {
checkedColor?: string;
uncheckedColor?: string;
} & BaseProps;

const DEFAULT_PADDING = 9;

export const getSegmentedButtonDensityPadding = ({
Expand Down Expand Up @@ -124,25 +129,34 @@ const getSegmentedButtonBorderWidth = ({
const getSegmentedButtonTextColor = ({
theme,
disabled,
}: Omit<BaseProps, 'checked'>) => {
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,
Expand All @@ -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 };
Expand Down
32 changes: 32 additions & 0 deletions src/components/__tests__/SegmentedButton.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 [
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down

0 comments on commit e961a87

Please sign in to comment.