Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Segment checked state #3701

Merged
merged 7 commits into from
Mar 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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