From f64e2e6d78a7fd35529fafc9d4c204c449cb3e8d Mon Sep 17 00:00:00 2001 From: frknltrk Date: Sun, 25 Jan 2026 00:57:44 +0100 Subject: [PATCH 1/4] feat: add expandDirection prop to List.Accordion/Group (#4852) --- example/src/Examples/ListAccordionExample.tsx | 11 ++++ .../Examples/ListAccordionGroupExample.tsx | 19 +++++++ src/components/List/ListAccordion.tsx | 53 ++++++++++++------- src/components/List/ListAccordionGroup.tsx | 8 +++ 4 files changed, 71 insertions(+), 20 deletions(-) diff --git a/example/src/Examples/ListAccordionExample.tsx b/example/src/Examples/ListAccordionExample.tsx index 58c3841de5..ac2ab35771 100644 --- a/example/src/Examples/ListAccordionExample.tsx +++ b/example/src/Examples/ListAccordionExample.tsx @@ -56,6 +56,17 @@ const ListAccordionExample = () => { /> + + + } + title="Expandable list item" + expandDirection="upwards" + > + + + + ); }; diff --git a/example/src/Examples/ListAccordionGroupExample.tsx b/example/src/Examples/ListAccordionGroupExample.tsx index 51861858bb..8aa81bd001 100644 --- a/example/src/Examples/ListAccordionGroupExample.tsx +++ b/example/src/Examples/ListAccordionGroupExample.tsx @@ -71,6 +71,25 @@ const ListAccordionGroupExample = () => { + + + } + title="Expandable list item" + id="1" + > + + + + } + title="Expandable list item 2" + id="2" + > + + + + ); }; diff --git a/src/components/List/ListAccordion.tsx b/src/components/List/ListAccordion.tsx index 75505c7367..aecf6fa58c 100644 --- a/src/components/List/ListAccordion.tsx +++ b/src/components/List/ListAccordion.tsx @@ -136,6 +136,11 @@ export type Props = { * This can be used to enlarge the touchable area beyond the visible component. */ hitSlop?: TouchableRippleProps['hitSlop']; + /** + * Sets expansion direction for the accordion. + * Can be 'downwards' (default) or 'upwards'. + */ + expandDirection?: 'downwards' | 'upwards'; }; /** @@ -202,6 +207,7 @@ const ListAccordion = ({ titleMaxFontSizeMultiplier, descriptionMaxFontSizeMultiplier, hitSlop, + expandDirection: expandDirectionProp, }: Props) => { const theme = useInternalTheme(themeOverrides); const [expanded, setExpanded] = React.useState( @@ -252,8 +258,34 @@ const ListAccordion = ({ groupContext && id !== undefined ? () => groupContext.onAccordionPress(id) : handlePressAction; + + const expandDirection = + expandDirectionProp || groupContext?.expandDirection || 'downwards'; + + const expandedContent = isExpanded + ? React.Children.map(children, (child) => { + if ( + left && + React.isValidElement(child) && + !child.props.left && + !child.props.right + ) { + return React.cloneElement(child, { + style: [ + theme.isV3 ? styles.childV3 : styles.child, + child.props.style, + ], + theme, + }); + } + + return child; + }) + : null; + return ( + {expandDirection === 'upwards' ? expandedContent : null} - {isExpanded - ? React.Children.map(children, (child) => { - if ( - left && - React.isValidElement(child) && - !child.props.left && - !child.props.right - ) { - return React.cloneElement(child, { - style: [ - theme.isV3 ? styles.childV3 : styles.child, - child.props.style, - ], - theme, - }); - } - - return child; - }) - : null} + {expandDirection === 'downwards' ? expandedContent : null} ); }; diff --git a/src/components/List/ListAccordionGroup.tsx b/src/components/List/ListAccordionGroup.tsx index 15e148ae6e..a9b2596b86 100644 --- a/src/components/List/ListAccordionGroup.tsx +++ b/src/components/List/ListAccordionGroup.tsx @@ -13,11 +13,17 @@ export type Props = { * React elements containing list accordions */ children: React.ReactNode; + /** + * Sets expansion direction for all accordions in the group. + * Can be 'downwards' (default) or 'upwards'. + */ + expandDirection?: 'downwards' | 'upwards'; }; export type ListAccordionGroupContextType = { expandedId: string | number | undefined; onAccordionPress: (expandedId: string | number) => void; + expandDirection?: 'downwards' | 'upwards'; } | null; export const ListAccordionGroupContext = @@ -60,6 +66,7 @@ const ListAccordionGroup = ({ expandedId: expandedIdProp, onAccordionPress, children, + expandDirection, }: Props) => { const [expandedId, setExpandedId] = React.useState< string | number | undefined @@ -76,6 +83,7 @@ const ListAccordionGroup = ({ value={{ expandedId: expandedIdProp || expandedId, // component can be controlled or uncontrolled onAccordionPress: onAccordionPress || onAccordionPressDefault, + expandDirection, }} > {children} From 2ea0067ee334f8eb5411d829cd573ebd17bd1929 Mon Sep 17 00:00:00 2001 From: frknltrk Date: Fri, 6 Feb 2026 21:22:32 +0100 Subject: [PATCH 2/4] refactor(chore): use guard clause to simplify logic and reduce indentat --- src/components/List/ListAccordion.tsx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/List/ListAccordion.tsx b/src/components/List/ListAccordion.tsx index aecf6fa58c..78c7918dec 100644 --- a/src/components/List/ListAccordion.tsx +++ b/src/components/List/ListAccordion.tsx @@ -265,21 +265,21 @@ const ListAccordion = ({ const expandedContent = isExpanded ? React.Children.map(children, (child) => { if ( - left && - React.isValidElement(child) && - !child.props.left && - !child.props.right + !left || + !React.isValidElement(child) || + child.props.left || + child.props.right ) { - return React.cloneElement(child, { - style: [ - theme.isV3 ? styles.childV3 : styles.child, - child.props.style, - ], - theme, - }); + return child; } - return child; + return React.cloneElement(child, { + style: [ + theme.isV3 ? styles.childV3 : styles.child, + child.props.style, + ], + theme, + }); }) : null; From 56aa3b50fa60538587c1f2763101bd07fc81a8e2 Mon Sep 17 00:00:00 2001 From: frknltrk Date: Fri, 6 Feb 2026 21:39:29 +0100 Subject: [PATCH 3/4] refactor(chore): create a helper func to simplify expandedContent logic --- src/components/List/ListAccordion.tsx | 61 ++++++++++++++++++--------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/src/components/List/ListAccordion.tsx b/src/components/List/ListAccordion.tsx index 78c7918dec..73ca9875e0 100644 --- a/src/components/List/ListAccordion.tsx +++ b/src/components/List/ListAccordion.tsx @@ -18,7 +18,7 @@ import { ListAccordionGroupContext } from './ListAccordionGroup'; import type { ListChildProps, Style } from './utils'; import { getAccordionColors, getLeftStyles } from './utils'; import { useInternalTheme } from '../../core/theming'; -import type { ThemeProp } from '../../types'; +import type { InternalTheme, ThemeProp } from '../../types'; import MaterialCommunityIcon from '../MaterialCommunityIcon'; import TouchableRipple, { Props as TouchableRippleProps, @@ -180,6 +180,39 @@ export type Props = { * export default MyComponent; * ``` */ +function getExpandedContent({ + isExpanded, + children, + left, + theme, +}: { + isExpanded: boolean; + children: React.ReactNode; + left?: Props['left']; + theme: InternalTheme; +}) { + return isExpanded + ? React.Children.map(children, (child) => { + if ( + !left || + !React.isValidElement(child) || + child.props.left || + child.props.right + ) { + return child; + } + + return React.cloneElement(child, { + style: [ + theme.isV3 ? styles.childV3 : styles.child, + child.props.style, + ], + theme, + }); + }) + : null; +} + const ListAccordion = ({ left, right, @@ -262,26 +295,12 @@ const ListAccordion = ({ const expandDirection = expandDirectionProp || groupContext?.expandDirection || 'downwards'; - const expandedContent = isExpanded - ? React.Children.map(children, (child) => { - if ( - !left || - !React.isValidElement(child) || - child.props.left || - child.props.right - ) { - return child; - } - - return React.cloneElement(child, { - style: [ - theme.isV3 ? styles.childV3 : styles.child, - child.props.style, - ], - theme, - }); - }) - : null; + const expandedContent = getExpandedContent({ + isExpanded, + children, + left, + theme, + }); return ( From 770225e3f81206113aaaaf96566ee4176dfb9442 Mon Sep 17 00:00:00 2001 From: frknltrk Date: Fri, 6 Feb 2026 22:31:25 +0100 Subject: [PATCH 4/4] fix(docs): discard expandDirectionProp to ensure def value is documented --- src/components/List/ListAccordion.tsx | 6 +----- src/components/List/ListAccordionGroup.tsx | 3 +-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/components/List/ListAccordion.tsx b/src/components/List/ListAccordion.tsx index 73ca9875e0..2691035188 100644 --- a/src/components/List/ListAccordion.tsx +++ b/src/components/List/ListAccordion.tsx @@ -138,7 +138,6 @@ export type Props = { hitSlop?: TouchableRippleProps['hitSlop']; /** * Sets expansion direction for the accordion. - * Can be 'downwards' (default) or 'upwards'. */ expandDirection?: 'downwards' | 'upwards'; }; @@ -240,7 +239,7 @@ const ListAccordion = ({ titleMaxFontSizeMultiplier, descriptionMaxFontSizeMultiplier, hitSlop, - expandDirection: expandDirectionProp, + expandDirection = 'downwards', }: Props) => { const theme = useInternalTheme(themeOverrides); const [expanded, setExpanded] = React.useState( @@ -292,9 +291,6 @@ const ListAccordion = ({ ? () => groupContext.onAccordionPress(id) : handlePressAction; - const expandDirection = - expandDirectionProp || groupContext?.expandDirection || 'downwards'; - const expandedContent = getExpandedContent({ isExpanded, children, diff --git a/src/components/List/ListAccordionGroup.tsx b/src/components/List/ListAccordionGroup.tsx index a9b2596b86..c56a9e5dba 100644 --- a/src/components/List/ListAccordionGroup.tsx +++ b/src/components/List/ListAccordionGroup.tsx @@ -15,7 +15,6 @@ export type Props = { children: React.ReactNode; /** * Sets expansion direction for all accordions in the group. - * Can be 'downwards' (default) or 'upwards'. */ expandDirection?: 'downwards' | 'upwards'; }; @@ -66,7 +65,7 @@ const ListAccordionGroup = ({ expandedId: expandedIdProp, onAccordionPress, children, - expandDirection, + expandDirection = 'downwards', }: Props) => { const [expandedId, setExpandedId] = React.useState< string | number | undefined