Skip to content

Commit

Permalink
feat: allow List.Accordion to behave as a controlled component (#638)
Browse files Browse the repository at this point in the history
fixes #616, #635
closes #618
  • Loading branch information
vieiralucas authored and satya164 committed Nov 5, 2018
1 parent 8764f60 commit decbedb
Show file tree
Hide file tree
Showing 5 changed files with 336 additions and 27 deletions.
23 changes: 22 additions & 1 deletion example/src/ListAccordionExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,28 @@ type Props = {
theme: Theme,
};

class ListAccordionExample extends React.Component<Props> {
type State = {
expanded: boolean,
};

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

state = {
expanded: true,
};

_handlePress = () => {
this.setState({ expanded: !this.state.expanded });
};

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

return (
<ScrollView style={[styles.container, { backgroundColor: background }]}>
<List.Section title="Expandable list item">
Expand All @@ -28,6 +41,14 @@ class ListAccordionExample extends React.Component<Props> {
<List.Item title="List item 1" />
<List.Item title="List item 2" />
</List.Accordion>
<List.Accordion
left={props => <List.Icon {...props} icon="folder" />}
title="Start expanded"
expanded={this.state.expanded}
onPress={this._handlePress}
>
<List.Item title="List item 1" />
</List.Accordion>
</List.Section>
<Divider />
<List.Section title="Expandable & multiline list item">
Expand Down
91 changes: 65 additions & 26 deletions src/components/List/ListAccordion.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ type Props = {
* Callback which returns a React element to display on the left side.
*/
left?: (props: { color: string }) => React.Node,
/**
* Whether the accordion is expanded
* If this prop is provided, the accordion will behave as a "controlled component".
* You'll need to update this prop when you want to toggle the component or on `onPress`.
*/
expanded?: boolean,
/**
* Function to execute on press.
*/
onPress?: () => mixed,
/**
* Content of the section.
*/
Expand Down Expand Up @@ -51,15 +61,40 @@ type State = {
* import * as React from 'react';
* import { List, Checkbox } from 'react-native-paper';
*
* const MyComponent = () => (
* <List.Accordion
* title="Accordion"
* left={props => <List.Icon {...props} icon="folder" />}
* >
* <List.Item title="First item" />
* <List.Item title="Second item" />
* </List.Accordion>
* );
* class MyComponent extends React.Component {
* state = {
* expanded: true
* }
*
* _handlePress = () =>
* this.setState({
* expanded: !this.state.expanded
* });
*
* render() {
* return (
* <List.Section title="Accordions">
* <List.Accordion
* title="Uncontrolled Accordion"
* left={props => <List.Icon {...props} icon="folder" />}
* >
* <List.Item title="First item" />
* <List.Item title="Second item" />
* </List.Accordion>
*
* <List.Accordion
* title="Controlled Accordion"
* left={props => <List.Icon {...props} icon="folder" />}
* expanded={this.state.expanded}
* onPress={this._handlePress}
* >
* <List.Item title="First item" />
* <List.Item title="Second item" />
* </List.Accordion>
* </List.Section>
* );
* }
* }
*
* export default MyComponent;
* ```
Expand All @@ -68,13 +103,20 @@ class ListAccordion extends React.Component<Props, State> {
static displayName = 'List.Accordion';

state = {
expanded: false,
expanded: this.props.expanded || false,
};

_handlePress = () =>
this.setState(state => ({
expanded: !state.expanded,
}));
_handlePress = () => {
this.props.onPress && this.props.onPress();

if (this.props.expanded === undefined) {
// Only update state of the `expanded` prop was not passed
// If it was passed, the component will act as a controlled component
this.setState(state => ({
expanded: !state.expanded,
}));
}
};

render() {
const { left, title, description, children, theme, style } = this.props;
Expand All @@ -87,6 +129,11 @@ class ListAccordion extends React.Component<Props, State> {
.rgb()
.string();

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

return (
<View>
<TouchableRipple
Expand All @@ -99,9 +146,7 @@ class ListAccordion extends React.Component<Props, State> {
<View style={styles.row} pointerEvents="none">
{left
? left({
color: this.state.expanded
? theme.colors.primary
: descriptionColor,
color: expanded ? theme.colors.primary : descriptionColor,
})
: null}
<View style={[styles.item, styles.content]}>
Expand All @@ -110,9 +155,7 @@ class ListAccordion extends React.Component<Props, State> {
style={[
styles.title,
{
color: this.state.expanded
? theme.colors.primary
: titleColor,
color: expanded ? theme.colors.primary : titleColor,
},
]}
>
Expand All @@ -134,18 +177,14 @@ class ListAccordion extends React.Component<Props, State> {
</View>
<View style={[styles.item, description && styles.multiline]}>
<Icon
source={
this.state.expanded
? 'keyboard-arrow-up'
: 'keyboard-arrow-down'
}
source={expanded ? 'keyboard-arrow-up' : 'keyboard-arrow-down'}
color={titleColor}
size={24}
/>
</View>
</View>
</TouchableRipple>
{this.state.expanded
{expanded
? React.Children.map(children, child => {
if (
left &&
Expand Down
12 changes: 12 additions & 0 deletions src/components/__tests__/ListAccordion.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,15 @@ it('renders list accordion with left items', () => {

expect(tree).toMatchSnapshot();
});

it('renders expanded accordion', () => {
const tree = renderer
.create(
<ListAccordion title="Accordion item 1" expanded>
<ListItem title="List item 1" />
</ListAccordion>
)
.toJSON();

expect(tree).toMatchSnapshot();
});

0 comments on commit decbedb

Please sign in to comment.