fix(appbar): make back icon respect the icon from Provider settings - #5064
fix(appbar): make back icon respect the icon from Provider settings#5064giaBaoJS wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
could we also respect configured icon renderer on iOS while preserving bundled chevron as the default?
Currently, Platform.OS === 'ios' always returns back-chevron.png so PaperProvider settings={{ icon: CustomIcon }} remains ignored on iOS
so maybe we can do smth like that:
const { direction } = useLocale();
const { icon } = React.useContext(SettingsContext);
const shouldUseIOSAsset =
Platform.OS === 'ios' && (!icon || icon === MaterialCommunityIcon);
return shouldUseIOSAsset ? (
// existing bundled iOS chevron
) : (
<Icon
source={{ source: 'arrow-left', direction }}
color={color}
size={size}
/>
);this should retain existing iOS appearance with default settings while satisfying reported expectation that components use the icon renderer configured through PaperProvider
There was a problem hiding this comment.
Done. Applied close to your sketch, with two adjustments after looking at the surrounding code.
I read settings via React.useContext<Settings>(SettingsContext) to match how TouchableRipple does it (src/components/TouchableRipple/TouchableRipple.tsx:128).
I kept the !icon || half of the guard, it is not dead. PaperProvider builds the value as { icon: MaterialCommunityIcon, rippleEffectEnabled: true, ...settings } (src/core/PaperProvider.tsx:51-58), so a caller passing settings={{ icon: undefined }} spreads icon back to undefined and the identity check alone would miss it.
The identity comparison does hold, which was the part of the sketch I was least sure about: SettingsContext's own default (src/core/settings.tsx:17-20) and PaperProvider's default both point at the same MaterialCommunityIcon module export, so an app that configures nothing still gets the bundled chevron on iOS.
| it('renders the icon provided through PaperProvider settings', async () => { | ||
| Platform.OS = 'android'; | ||
|
|
||
| await renderBackAction(); | ||
|
|
||
| expect( | ||
| screen.getByText('custom-arrow-left', { includeHiddenElements: true }) | ||
| ).toBeOnTheScreen(); | ||
| }); | ||
|
|
||
| it('keeps the icon mirrored in RTL', async () => { | ||
| Platform.OS = 'android'; | ||
|
|
||
| await renderBackAction('rtl'); | ||
|
|
||
| expect( | ||
| screen.getByText('custom-arrow-left', { includeHiddenElements: true }) | ||
| ).toHaveStyle({ transform: [{ scaleX: -1 }] }); | ||
| }); | ||
|
|
||
| it('keeps the icon unmirrored in LTR', async () => { | ||
| Platform.OS = 'android'; | ||
|
|
||
| await renderBackAction('ltr'); | ||
|
|
||
| expect( | ||
| screen.getByText('custom-arrow-left', { includeHiddenElements: true }) | ||
| ).toHaveStyle({ transform: [{ scaleX: 1 }] }); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
could we also add an iOS case alongside the existing tests?
smth like that:
it('renders the icon provided through PaperProvider settings on iOS', async () => {
Platform.OS = 'ios';
await renderBackAction();
expect(
screen.getByText('custom-arrow-left', { includeHiddenElements: true })
).toBeOnTheScreen();
});There was a problem hiding this comment.
Added, as written. Appbar.test.tsx goes from 33 to 34 tests.
Counterfactual: keeping the test and reverting AppbarBackIcon.tsx to the previous Platform.OS === 'ios' ? branch, the new case fails with Unable to find an element with text: custom-arrow-left, and the rendered tree it dumps shows the bundled <Image /> where the custom icon should be. The three Android cases stay green, so the failure is specific to the iOS path. Restoring the fix puts it back to 34/34.
Keep the bundled chevron only while the default renderer is in place, so an icon renderer passed through PaperProvider settings is also used for the back icon on iOS.
Motivation
Iconis the only component that reads theiconentry fromSettingsContext:react-native-paper/src/components/Icon.tsx
Lines 145 to 158 in df3cdfd
A number of components import
MaterialCommunityIcondirectly instead, which bypasses that consumer. Becausesrc/core/settings.tsx#L18also usesMaterialCommunityIconas the default context value, those call sites look correct until an app actually passessettings={{ icon }}toPaperProvider— at which point the override is silently ignored.Appbar.BackActionis one of the two components named in #4760.Scope
Deliberately narrow: this PR only changes
Appbar/AppbarBackIcon.tsx.The same bypass exists at these call sites, which I left alone because they all sit under active/competing PRs and are better done separately (happy to follow up):
Chip/Chip.tsx(2)Snackbar.tsx(1)List/ListAccordion.tsx(1)DataTable/DataTablePagination.tsx(4)DataTable/DataTableTitle.tsx(1)RadioButton/RadioButtonIOS.tsx(1)Searchbar.tsx(2)Also out of scope on purpose: the
Platform.OS === 'ios'branch ofAppbarBackIconrenders the bundledback-chevron.pngasset rather than an icon-font glyph. Routing that throughsettings.iconwould replace the iOS chevron with anarrow-leftglyph for every app that does not customise icons, which is a visual change I did not want to smuggle into a bug fix. Let me know if you'd like it changed and I'll do it here or in a follow-up.Note on RTL
The object form
{ source: 'arrow-left', direction }is used rather than the bare string"arrow-left".Iconcomputesdirectionasnullfor a bare string source, which drops thescaleX: -1mirroring that the previousdirection={direction}prop provided — the back arrow is direction-sensitive, so that would have been a regression. There is a dedicated test for both RTL and LTR to keep it that way.Test plan
Added three cases to
src/components/__tests__/Appbar/Appbar.test.tsx(Platform.OSis forced toandroidbecause the jest preset defaults toios, where this branch is not reachable):PaperProvider settings={{ icon }}is renderedBefore the fix all three fail (the default
arrow-leftglyph renders instead of the custom one). With the bare-string form, the RTL case fails on its own.yarn test: 55 suites, 738 passed / 1 skipped, 169 snapshots — no snapshot churn.yarn lintandyarn typecheckclean.Fixes part of #4760.