Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
8d5158c
refactor(list): replace React.Children injection in List.Accordion wi…
matkoson Jun 18, 2026
1d2ab26
refactor(dialog): remove child composition injection
matkoson Jun 23, 2026
cd944b1
refactor(card): remove child composition injection
matkoson Jun 23, 2026
07cc50e
refactor(toggle-button): style row buttons via context
matkoson Jun 23, 2026
7bca1e1
test: align composition tests with upstream utilities
matkoson Jun 29, 2026
4aebe0c
fix: preserve dialog icon title spacing
matkoson Jun 29, 2026
eec56a6
refactor(toggle-button): align segmented row to MD3 spec
matkoson Jun 30, 2026
62bb663
test(toggle-button): assert MD3 secondaryContainer selected fill in row
matkoson Jun 30, 2026
53906e3
refactor(appbar): provide shared values via context
matkoson Jun 30, 2026
0ee29f6
revert(toggle-button): drop MD3 redesign, keep legacy visual identity
matkoson Jun 30, 2026
08c27f1
fix(toggle-button, dialog): address review feedback on composition re…
matkoson Jul 2, 2026
31d0231
fix(card, dialog, appbar): resolve remaining Copilot review findings
matkoson Jul 6, 2026
a922bed
test: stabilise the Tooltip pressOut timing
adam-sajko Aug 25, 2026
33e5141
chore: avoid unnecessary type assertion in Appbar
adam-sajko Aug 25, 2026
04aca35
fix(card, dialog): preserve child keys in action rows
adam-sajko Aug 25, 2026
f7b86a0
docs: document Appbar, Card and Dialog breaking changes
adam-sajko Aug 25, 2026
dd22197
revert(appbar): drop Appbar from this PR
adam-sajko Aug 27, 2026
4f718bb
docs: swap the Appbar migration notes for List.Accordion
adam-sajko Aug 27, 2026
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
68 changes: 68 additions & 0 deletions docs/6.x/docs/guides/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,71 @@ const theme = {
style={{ fontSize: 16, color: '#1C1B1F' }}
/>
```

### Card

#### `Card.Actions`

`Card.Actions` no longer assigns `mode` to its buttons, previously `outlined` for the first one and `contained` for the rest, and no longer injects `compact`. Set both on the buttons.

```tsx
// Before (v5)
<Card.Actions>
<Button>Cancel</Button>
<Button>Ok</Button>
</Card.Actions>

// After (v6)
<Card.Actions>
<Button mode="outlined">Cancel</Button>
<Button mode="contained">Ok</Button>
</Card.Actions>
```

#### `Card.Content`

`Card.Content` uses the same vertical padding everywhere. Before it changed depending on the sections next to it.

### Dialog

#### `Dialog.Actions`

`Dialog.Actions` no longer injects `compact` and `uppercase` into the action buttons. Set them yourself if you want the old look.

```tsx
// Before (v5)
<Dialog.Actions>
<Button onPress={hide}>Done</Button>
</Dialog.Actions>

// After (v6)
<Dialog.Actions>
<Button compact uppercase onPress={hide}>
Done
</Button>
</Dialog.Actions>
```

### List

#### `List.Accordion`

When `List.Accordion` has a `left` element, it used to indent every child that rendered no `left` or `right` of its own, whatever the child was. The indent now comes from context and only `List.Item` reads it, so a custom child keeps its own padding. Indent it yourself if you need the old alignment.

```tsx
// Before (v5)
<List.Accordion title="Group" left={props => <List.Icon {...props} icon="folder" />}>
<View>
<Text>Custom row</Text>
</View>
</List.Accordion>

// After (v6)
<List.Accordion title="Group" left={props => <List.Icon {...props} icon="folder" />}>
<View style={{ paddingLeft: 40 }}>
<Text>Custom row</Text>
</View>
</List.Accordion>
```

`theme` set on `List.Accordion` no longer reaches its children either. Pass it to the child that needs the override.
12 changes: 8 additions & 4 deletions example/src/Examples/CardExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,12 @@ const CardExample = () => {
<Card style={styles.card} mode={selectedMode}>
<Card.Cover source={require('../../assets/images/forest.jpg')} />
<Card.Actions>
<Button onPress={() => {}}>Share</Button>
<Button onPress={() => {}}>Explore</Button>
<Button mode="outlined" onPress={() => {}}>
Share
</Button>
<Button mode="contained" onPress={() => {}}>
Explore
</Button>
</Card.Actions>
</Card>
<Card style={styles.card} mode={selectedMode}>
Expand Down Expand Up @@ -104,10 +108,10 @@ const CardExample = () => {
/>
<Card.Title title="Custom Button styles" />
<Card.Actions>
<Button style={styles.button} onPress={() => {}}>
<Button mode="outlined" style={styles.button} onPress={() => {}}>
Share
</Button>
<Button style={styles.button} onPress={() => {}}>
<Button mode="contained" style={styles.button} onPress={() => {}}>
Explore
</Button>
</Card.Actions>
Expand Down
16 changes: 12 additions & 4 deletions example/src/Examples/TeamDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,12 @@ const News = () => {
</Text>
</Card.Content>
<Card.Actions>
<Button onPress={() => {}}>Share</Button>
<Button onPress={() => {}}>Read more</Button>
<Button mode="outlined" onPress={() => {}}>
Share
</Button>
<Button mode="contained" onPress={() => {}}>
Read more
</Button>
</Card.Actions>
</Card>
<Card style={styles.card} mode="contained">
Expand All @@ -110,8 +114,12 @@ const News = () => {
</Text>
</Card.Content>
<Card.Actions>
<Button onPress={() => {}}>Share</Button>
<Button onPress={() => {}}>Read more</Button>
<Button mode="outlined" onPress={() => {}}>
Share
</Button>
<Button mode="contained" onPress={() => {}}>
Read more
</Button>
</Card.Actions>
</Card>
</View>
Expand Down
32 changes: 10 additions & 22 deletions src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ export type Props = $Omit<React.ComponentProps<typeof Surface>, 'mode'> & {

/**
* A card is a sheet of material that serves as an entry point to more detailed information.
* Card clips its inner content to the card shape and renders children directly;
* section spacing is owned by the section components themselves.
*
* ## Usage
* ```js
Expand All @@ -112,8 +114,8 @@ export type Props = $Omit<React.ComponentProps<typeof Surface>, 'mode'> & {
* </Card.Content>
* <Card.Cover source={{ uri: 'https://picsum.photos/700' }} />
* <Card.Actions>
* <Button>Cancel</Button>
* <Button>Ok</Button>
* <Button mode="outlined">Cancel</Button>
* <Button mode="contained">Ok</Button>
* </Card.Actions>
* </Card>
* );
Expand Down Expand Up @@ -185,15 +187,6 @@ const Card = ({
runElevationAnimation('out');
});

const total = React.Children.count(children);
const siblings = React.Children.map(children, (child) =>
React.isValidElement(child) && child.type
? typeof child.type !== 'string' && 'displayName' in child.type
? child.type.displayName
: null
: null
);

const { backgroundColor, borderColor: themedBorderColor } = getCardColors({
theme,
mode: cardMode,
Expand All @@ -215,17 +208,11 @@ const Card = ({
};

const content = (
<View style={[styles.innerContainer, contentStyle]} testID={testID}>
{React.Children.map(children, (child, index) =>
React.isValidElement(child)
? React.cloneElement(child as React.ReactElement<any>, {
index,
total,
siblings,
borderRadiusStyles,
})
: child
)}
<View
style={[styles.innerContainer, borderRadiusCombinedStyles, contentStyle]}
testID={testID}
>
{children}
</View>
);

Expand Down Expand Up @@ -291,6 +278,7 @@ Card.Title = CardTitle;
const styles = StyleSheet.create({
innerContainer: {
flexShrink: 1,
overflow: 'hidden',
},
outline: {
borderWidth: 1,
Expand Down
39 changes: 17 additions & 22 deletions src/components/Card/CardActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import * as React from 'react';
import { StyleSheet, View } from 'react-native';
import type { StyleProp, ViewProps, ViewStyle } from 'react-native';

import type { CardActionChildProps } from './utils';
import { useInternalTheme } from '../../core/theming';
import type { ThemeProp } from '../../types';

Expand All @@ -17,6 +16,8 @@ export type Props = ViewProps & {

/**
* A component to show a list of actions inside a Card.
* Actions are rendered directly, so set button `mode`, `compact`, and custom
* spacing props explicitly on each action when needed.
*
* ## Usage
* ```js
Expand All @@ -26,8 +27,8 @@ export type Props = ViewProps & {
* const MyComponent = () => (
* <Card>
* <Card.Actions>
* <Button>Cancel</Button>
* <Button>Ok</Button>
* <Button mode="outlined">Cancel</Button>
* <Button mode="contained">Ok</Button>
* </Card.Actions>
* </Card>
* );
Expand All @@ -43,26 +44,20 @@ const CardActions = ({ theme, style, children, ...rest }: Props) => {
{ justifyContent: 'flex-end' } satisfies ViewStyle,
style,
];
const items = React.Children.toArray(children);

return (
<View {...rest} style={containerStyle}>
{React.Children.map(children, (child, index) => {
if (!React.isValidElement<CardActionChildProps>(child)) {
return child;
}

const compact = child.props.compact;
const mode =
child.props.mode ?? (index === 0 ? 'outlined' : 'contained');
const childStyle = [styles.button, child.props.style];

return React.cloneElement(child, {
...child.props,
compact,
mode,
style: childStyle,
});
})}
{items.map((child, index) => (
<React.Fragment
key={
React.isValidElement(child) && child.key != null ? child.key : index
}
>
{index > 0 && <View style={styles.spacer} />}
{child}
</React.Fragment>
))}
</View>
);
};
Expand All @@ -75,8 +70,8 @@ const styles = StyleSheet.create({
alignItems: 'center',
padding: 8,
},
button: {
marginLeft: 8,
spacer: {
width: 8,
},
});

Expand Down
61 changes: 5 additions & 56 deletions src/components/Card/CardContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,13 @@ export type Props = ViewProps & {
* Items inside the `Card.Content`.
*/
children: React.ReactNode;
/**
* @internal
*/
index?: number;
/**
* @internal
*/
total?: number;
/**
* @internal
*/
siblings?: Array<string | null>;
style?: StyleProp<ViewStyle>;
};

/**
* A component to show content inside a Card.
* Content uses uniform vertical padding and does not depend on neighboring
* card sections.
*
* ## Usage
* ```js
Expand All @@ -42,59 +32,18 @@ export type Props = ViewProps & {
* export default MyComponent;
* ```
*/
const CardContent = ({ index, total, siblings, style, ...rest }: Props) => {
const cover = 'withInternalTheme(CardCover)';
const title = 'withInternalTheme(CardTitle)';

let contentStyle, prev, next;

if (typeof index === 'number' && siblings) {
prev = siblings[index - 1];
next = siblings[index + 1];
}

if (
(prev === cover && next === cover) ||
(prev === title && next === title) ||
total === 1
) {
contentStyle = styles.only;
} else if (index === 0) {
if (next === cover || next === title) {
contentStyle = styles.only;
} else {
contentStyle = styles.first;
}
} else if (typeof total === 'number' && index === total - 1) {
if (prev === cover || prev === title) {
contentStyle = styles.only;
} else {
contentStyle = styles.last;
}
} else if (prev === cover || prev === title) {
contentStyle = styles.first;
} else if (next === cover || next === title) {
contentStyle = styles.last;
}

return <View {...rest} style={[styles.container, contentStyle, style]} />;
};
const CardContent = ({ style, ...rest }: Props) => (
<View {...rest} style={[styles.container, style]} />
);

CardContent.displayName = 'Card.Content';

const styles = StyleSheet.create({
container: {
paddingHorizontal: 16,
},
first: {
paddingTop: 16,
},
last: {
paddingBottom: 16,
},
only: {
paddingVertical: 16,
},
});

export default CardContent;
Loading