Skip to content

Commit ac5fe16

Browse files
committed
feat: add badge support in bottom navigation. closes #288
1 parent 1de5095 commit ac5fe16

5 files changed

Lines changed: 1023 additions & 0 deletions

File tree

example/src/BottomNavigationExample.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type State = {
1111
title: string,
1212
icon: string,
1313
color: string,
14+
badge?: boolean,
1415
}>,
1516
};
1617

@@ -45,6 +46,7 @@ export default class BottomNavigationExample extends React.Component<
4546
title: 'Library',
4647
icon: 'inbox',
4748
color: '#2962ff',
49+
badge: true,
4850
},
4951
{
5052
key: 'favorites',

src/components/Badge.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class Badge extends React.Component<Props, State> {
8181

8282
return (
8383
<Animated.Text
84+
numberOfLines={1}
8485
style={[
8586
{
8687
opacity,

src/components/BottomNavigation.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { polyfill } from 'react-lifecycles-compat';
1515
import color from 'color';
1616
import Icon from './Icon';
1717
import Surface from './Surface';
18+
import Badge from './Badge';
1819
import TouchableRipple from './TouchableRipple';
1920
import Text from './Typography/Text';
2021
import { black, white } from '../styles/colors';
@@ -28,6 +29,7 @@ type Route = $Shape<{
2829
key: string,
2930
title: string,
3031
icon: IconSource,
32+
badge: string | number | boolean,
3133
color: string,
3234
accessibilityLabel: string,
3335
testID: string,
@@ -60,6 +62,7 @@ type Props<T> = {
6062
* - `title`: title of the route to use as the tab label
6163
* - `icon`: icon to use as the tab icon, can be a string, an image source or a react component
6264
* - `color`: color to use as background color for shifting bottom navigation
65+
* - `badge`: badge to show on the tab icon, can be `true` to show a dot, `string` or `number` to show text.
6366
* - `accessibilityLabel`: accessibility label for the tab button
6467
* - `testID`: test id for the tab button
6568
*
@@ -153,6 +156,11 @@ type Props<T> = {
153156
* Get the id to locate this tab button in tests, uses `route.testID` by default.
154157
*/
155158
getTestID?: (props: { route: T }) => ?string,
159+
/**
160+
* Get badge for the tab, uses `route.badge` by default.
161+
*/
162+
getBadge?: (props: { route: T }) => boolean | number | string,
163+
/**
156164
/**
157165
* Get color for the tab, uses `route.color` by default.
158166
*/
@@ -535,6 +543,7 @@ class BottomNavigation<T: *> extends React.Component<Props<T>, State> {
535543
renderIcon,
536544
renderLabel,
537545
getLabelText = ({ route }: Object) => route.title,
546+
getBadge = ({ route }: Object) => route.badge,
538547
getColor = ({ route }: Object) => route.color,
539548
getAccessibilityLabel = ({ route }: Object) => route.accessibilityLabel,
540549
getTestID = ({ route }: Object) => route.testID,
@@ -734,6 +743,8 @@ class BottomNavigation<T: *> extends React.Component<Props<T>, State> {
734743
outputRange: [1, 0],
735744
});
736745

746+
const badge = getBadge({ route });
747+
737748
return (
738749
<Touchable
739750
key={route.key}
@@ -797,6 +808,25 @@ class BottomNavigation<T: *> extends React.Component<Props<T>, State> {
797808
/>
798809
)}
799810
</Animated.View>
811+
<View
812+
style={[
813+
styles.badgeContainer,
814+
{
815+
right:
816+
(badge != null && typeof badge !== 'boolean'
817+
? String(badge).length * -2
818+
: 0) - 2,
819+
},
820+
]}
821+
>
822+
{typeof badge === 'boolean' ? (
823+
<Badge visible={badge} size={8} />
824+
) : (
825+
<Badge visible={badge != null} size={16}>
826+
{badge}
827+
</Badge>
828+
)}
829+
</View>
800830
</Animated.View>
801831
{labeled ? (
802832
<Animated.View
@@ -924,4 +954,9 @@ const styles = StyleSheet.create({
924954
textAlign: 'center',
925955
backgroundColor: 'transparent',
926956
},
957+
badgeContainer: {
958+
position: 'absolute',
959+
left: 0,
960+
top: -2,
961+
},
927962
});

src/components/__tests__/__snapshots__/Badge.test.js.snap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
exports[`renders badge 1`] = `
44
<Text
5+
numberOfLines={1}
56
style={
67
Object {
78
"alignSelf": "flex-end",
@@ -24,6 +25,7 @@ exports[`renders badge 1`] = `
2425

2526
exports[`renders badge as hidden 1`] = `
2627
<Text
28+
numberOfLines={1}
2729
style={
2830
Object {
2931
"alignSelf": "flex-end",
@@ -48,6 +50,7 @@ exports[`renders badge as hidden 1`] = `
4850

4951
exports[`renders badge in different color 1`] = `
5052
<Text
53+
numberOfLines={1}
5154
style={
5255
Object {
5356
"alignSelf": "flex-end",
@@ -72,6 +75,7 @@ exports[`renders badge in different color 1`] = `
7275

7376
exports[`renders badge in different size 1`] = `
7477
<Text
78+
numberOfLines={1}
7579
style={
7680
Object {
7781
"alignSelf": "flex-end",
@@ -96,6 +100,7 @@ exports[`renders badge in different size 1`] = `
96100

97101
exports[`renders badge with content 1`] = `
98102
<Text
103+
numberOfLines={1}
99104
style={
100105
Object {
101106
"alignSelf": "flex-end",

0 commit comments

Comments
 (0)