Skip to content
Open
Changes from all commits
Commits
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
96 changes: 91 additions & 5 deletions packages/rn-tester/js/examples/Playground/RNTesterPlayground.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,70 @@ import type {RNTesterModuleExample} from '../../types/RNTesterTypes';

import RNTesterText from '../../components/RNTesterText';
import * as React from 'react';
import {StyleSheet, View} from 'react-native';
import {Pressable, StyleSheet, View} from 'react-native';

/**
* Repro for #57780.
*
* Android only: a View with `borderRadius` + `overflow: 'hidden'` stops drawing
* ALL of its children once its `borderWidth` prop is *removed* (as opposed to
* being set to 0). The view's own background keeps painting, so the row becomes
* a correctly sized, correctly rounded, empty box.
*
* Tap "Row B" in group A: "Row A" keeps its grey background and loses its text.
* Group B is identical except its base style sets `borderWidth: 0`, so the prop
* is never removed - that one behaves correctly. Both groups undergo the same
* 1px geometry change, which is what rules out a layout/measurement cause and
* points at the `Float.NaN` that `ReactViewManager` sends for a removed
* borderWidth prop (see the issue for the full trace through
* BorderInsets.resolve -> clipToPaddingBox -> canvas.clipPath).
*
* iOS renders both groups correctly.
*/

const ROWS = ['Row A', 'Row B', 'Row C'];

function Group({
title,
keepBorderWidth,
}: {
title: string,
keepBorderWidth: boolean,
}): React.Node {
const [selected, setSelected] = React.useState<number>(0);

function Playground() {
return (
<View style={styles.group}>
<RNTesterText style={styles.groupTitle}>{title}</RNTesterText>
{ROWS.map((label, i) => (
<Pressable
key={label}
onPress={() => setSelected(i)}
style={[
styles.row,
// Group B only: keeps borderWidth present in every state.
keepBorderWidth ? styles.rowZeroBorder : null,
// borderWidth appears on select and DISAPPEARS on deselect.
i === selected ? styles.rowSelected : null,
]}>
<RNTesterText style={styles.rowLabel}>{label}</RNTesterText>
</Pressable>
))}
</View>
);
}

function Playground(): React.Node {
return (
<View style={styles.container}>
<RNTesterText>
Edit "RNTesterPlayground.js" to change this file
</RNTesterText>
<Group
keepBorderWidth={false}
title="A) BROKEN on Android - unselected style has no borderWidth"
/>
<Group
keepBorderWidth={true}
title="B) OK - unselected style has borderWidth: 0"
/>
</View>
);
}
Expand All @@ -28,6 +84,36 @@ const styles = StyleSheet.create({
container: {
padding: 10,
},
group: {
marginBottom: 24,
},
groupTitle: {
fontSize: 13,
marginBottom: 8,
},
row: {
backgroundColor: '#e6e8ee',
// ingredient 1: rounded corners, so the clip is built as a Path
borderRadius: 8,
// ingredient 2: installs the padding-box clip on children
overflow: 'hidden',
minHeight: 48,
justifyContent: 'center',
paddingHorizontal: 12,
marginBottom: 8,
},
rowZeroBorder: {
borderWidth: 0,
borderColor: 'transparent',
},
rowSelected: {
borderWidth: 1,
borderColor: '#3b5afb',
},
rowLabel: {
fontSize: 16,
color: '#111111',
},
});

export default {
Expand Down