Skip to content

Commit

Permalink
feat: add List.AccordionGroup component (#1307)
Browse files Browse the repository at this point in the history
  • Loading branch information
jayu authored and Trancever committed Sep 12, 2019
1 parent 9749453 commit ce0bd3e
Show file tree
Hide file tree
Showing 5 changed files with 304 additions and 69 deletions.
2 changes: 2 additions & 0 deletions example/src/ExampleList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import DividerExample from './Examples/DividerExample';
import FABExample from './Examples/FABExample';
import IconButtonExample from './Examples/IconButtonExample';
import ListAccordionExample from './Examples/ListAccordionExample';
import ListAccordionExampleGroup from './Examples/ListAccordionGroupExample';
import ListSectionExample from './Examples/ListSectionExample';
import MenuExample from './Examples/MenuExample';
import ProgressBarExample from './Examples/ProgressBarExample';
Expand Down Expand Up @@ -54,6 +55,7 @@ export const examples: { [key: string]: any } = {
fab: FABExample,
iconButton: IconButtonExample,
listAccordion: ListAccordionExample,
listAccordionGroup: ListAccordionExampleGroup,
listSection: ListSectionExample,
menu: MenuExample,
progressbar: ProgressBarExample,
Expand Down
103 changes: 103 additions & 0 deletions example/src/Examples/ListAccordionGroupExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import * as React from 'react';
import { ScrollView, StyleSheet } from 'react-native';
import { List, withTheme, Theme } from 'react-native-paper';

type Props = {
theme: Theme;
};

type State = {
expandedId: string | number | undefined;
};

class ListAccordionGroupExample extends React.Component<Props, State> {
static title = 'List.AccordionGroup';

state = {
expandedId: undefined,
};

_onAccordionPress = (expandedId: string | number) => {
this.setState(({ expandedId: currentExpandedId }) => ({
expandedId: currentExpandedId === expandedId ? undefined : expandedId,
}));
};

render() {
const {
theme: {
colors: { background },
},
} = this.props;

const { expandedId } = this.state;

return (
<ScrollView style={[styles.container, { backgroundColor: background }]}>
<List.AccordionGroup>
<List.Section title="Uncontrolled Accordion Group example">
<List.Accordion
left={props => <List.Icon {...props} icon="folder" />}
title="Expandable list item"
id="1"
>
<List.Item title="List item 1" />
<List.Item title="List item 2" />
</List.Accordion>
<List.Accordion
left={props => <List.Icon {...props} icon="folder" />}
title="Expandable list item 2"
id="2"
>
<List.Item title="List item 1" />
</List.Accordion>
<List.Accordion
left={props => <List.Icon {...props} icon="folder" />}
title="Expandable list item 2"
id="3"
>
<List.Item title="Another item" />
</List.Accordion>
</List.Section>
</List.AccordionGroup>
<List.AccordionGroup
expandedId={expandedId}
onAccordionPress={this._onAccordionPress}
>
<List.Section title="Controlled Accordion Group example">
<List.Accordion
left={props => <List.Icon {...props} icon="folder" />}
title="Expandable list item"
id="1"
>
<List.Item title="List item 1" />
<List.Item title="List item 2" />
</List.Accordion>
<List.Accordion
left={props => <List.Icon {...props} icon="folder" />}
title="Expandable list item 2"
id="2"
>
<List.Item title="List item 1" />
</List.Accordion>
<List.Accordion
left={props => <List.Icon {...props} icon="folder" />}
title="Expandable list item 2"
id="3"
>
<List.Item title="Another item" />
</List.Accordion>
</List.Section>
</List.AccordionGroup>
</ScrollView>
);
}
}

const styles = StyleSheet.create({
container: {
flex: 1,
},
});

export default withTheme(ListAccordionGroupExample);
3 changes: 3 additions & 0 deletions src/components/List/List.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// @component ./ListAccordion.tsx
export { default as Accordion } from './ListAccordion';

// @component ./ListAccordionGroup.tsx
export { default as AccordionGroup } from './ListAccordionGroup';

// @component ./ListIcon.tsx
export { default as Icon } from './ListIcon';

Expand Down
171 changes: 102 additions & 69 deletions src/components/List/ListAccordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import Text from '../Typography/Text';
import { withTheme } from '../../core/theming';
import { Theme } from '../../types';

import {
ListAccordionGroupContext,
ListAccordionGroupContextType,
} from './ListAccordionGroup';

type Props = {
/**
* Title text for the list accordion.
Expand Down Expand Up @@ -66,6 +71,10 @@ type Props = {
* exceed this number.
*/
descriptionNumberOfLines?: number;
/**
* Id is used for distinguishing specific accordion when using List.AccordionGroup. Property is required when using List.AccordionGroup and has no impact on behavior when using standalone List.Accordion.
*/
id?: string | number;
};

type State = {
Expand Down Expand Up @@ -160,6 +169,7 @@ class ListAccordion extends React.Component<Props, State> {
titleNumberOfLines,
descriptionNumberOfLines,
style,
id,
} = this.props;
const titleColor = color(theme.colors.text)
.alpha(0.87)
Expand All @@ -170,82 +180,105 @@ class ListAccordion extends React.Component<Props, State> {
.rgb()
.string();

const expanded =
const expandedInternal =
this.props.expanded !== undefined
? this.props.expanded
: this.state.expanded;

return (
<View>
<TouchableRipple
style={[styles.container, style]}
onPress={this._handlePress}
accessibilityTraits="button"
accessibilityComponentType="button"
accessibilityRole="button"
>
<View style={styles.row} pointerEvents="none">
{left
? left({
color: expanded ? theme.colors.primary : descriptionColor,
})
: null}
<View style={[styles.item, styles.content]}>
<Text
numberOfLines={titleNumberOfLines}
style={[
styles.title,
{
color: expanded ? theme.colors.primary : titleColor,
},
titleStyle,
]}
<ListAccordionGroupContext.Consumer>
{(groupContext: ListAccordionGroupContextType) => {
if (groupContext !== null && !id) {
throw new Error(
'List.Accordion is used inside a List.AccordionGroup without specifying an id prop.'
);
}
const expanded = groupContext
? groupContext.expandedId === id
: expandedInternal;
const handlePress =
groupContext && id !== undefined
? () => groupContext.onAccordionPress(id)
: this._handlePress;
return (
<View>
<TouchableRipple
style={[styles.container, style]}
onPress={handlePress}
accessibilityTraits="button"
accessibilityComponentType="button"
accessibilityRole="button"
>
{title}
</Text>
{description && (
<Text
numberOfLines={descriptionNumberOfLines}
style={[
styles.description,
{
color: descriptionColor,
},
descriptionStyle,
]}
>
{description}
</Text>
)}
</View>
<View
style={[styles.item, description ? styles.multiline : undefined]}
>
<Icon
source={expanded ? 'chevron-up' : 'chevron-down'}
color={titleColor}
size={24}
/>
</View>
</View>
</TouchableRipple>
{expanded
? React.Children.map(children, child => {
if (
left &&
React.isValidElement(child) &&
!child.props.left &&
!child.props.right
) {
return React.cloneElement(child, {
style: [styles.child, child.props.style],
});
}
<View style={styles.row} pointerEvents="none">
{left
? left({
color: expanded
? theme.colors.primary
: descriptionColor,
})
: null}
<View style={[styles.item, styles.content]}>
<Text
numberOfLines={titleNumberOfLines}
style={[
styles.title,
{
color: expanded ? theme.colors.primary : titleColor,
},
titleStyle,
]}
>
{title}
</Text>
{description && (
<Text
numberOfLines={descriptionNumberOfLines}
style={[
styles.description,
{
color: descriptionColor,
},
descriptionStyle,
]}
>
{description}
</Text>
)}
</View>
<View
style={[
styles.item,
description ? styles.multiline : undefined,
]}
>
<Icon
source={expanded ? 'chevron-up' : 'chevron-down'}
color={titleColor}
size={24}
/>
</View>
</View>
</TouchableRipple>
{expanded
? React.Children.map(children, child => {
if (
left &&
React.isValidElement(child) &&
!child.props.left &&
!child.props.right
) {
return React.cloneElement(child, {
style: [styles.child, child.props.style],
});
}

return child;
})
: null}
</View>
return child;
})
: null}
</View>
);
}}
</ListAccordionGroupContext.Consumer>
);
}
}
Expand Down
Loading

0 comments on commit ce0bd3e

Please sign in to comment.