Skip to content

Commit

Permalink
[IOAPPX-419] Adjust the size of Icon, Pictogram and some componen…
Browse files Browse the repository at this point in the history
…ts based on the value of `fontScale` (#348)

>[!caution]
> This PR depends on the following PR:
> * #347

## Short description
This PR adds a dynamic size to some components, based on the value of
`fontScale`. Dynamic size is currently supported on the following
components:
- `Tag`, `Badge`, `Alert` and `FeatureInfo`
- All the selection components (`ListItemCheckbox`, `ListItemRadio`,
etc…)
- All the `ListItem…` components

## List of changes proposed in this pull request
- Add the new `useIOFontDynamicScale` hook to get the current
`fontScale` value
- Add the new `allowFontScaling` to `Icon`, `AnimatedIcon` and
`Pictogram` components to enable dynamic size based on the `fontScale`
value
- Add the new `allowScaleSpacing` to `Stack` components to enable the
same behavior
- Add dynamic spacing to `Tag`, `Badge`, all the selection and
`ListItem…` components
- Increase value of the `maxFontSizeMultiplier` from `1.25` to `1.5`
- Hide decorative icons from `ListItem…` and `Module…` components if the
text size multiplier is quite big (>= 1.5)

### Preview

#### `ListItemCheckbox`
As you can see, the size of the margins, icons and checkboxes also
changes depending on the value of `fontScale`:
| Default size | Larger text size |
|--------|--------|
| ![Simulator Screenshot - iPhone 16 Pro - 2024-11-06 at 17 14
09](https://github.com/user-attachments/assets/42d1c209-c7c9-4ac2-a7e3-739ef7d28a74)
| ![Simulator Screenshot - iPhone 16 Pro - 2024-11-06 at 17 12
20](https://github.com/user-attachments/assets/fc9e689e-6219-4f15-9ffc-89c53a344899)
|

#### `Alert`
Same as above, but with boldEnabled set to _ON._
| Default size | Larger text size |
|--------|--------|
| ![Simulator Screenshot - iPhone 16 Pro - 2024-11-07 at 16 26
33](https://github.com/user-attachments/assets/9169214b-69d6-48d6-8d75-0fdfdf9be1e0)
| ![Simulator Screenshot - iPhone 16 Pro - 2024-11-07 at 16 23
46](https://github.com/user-attachments/assets/737d7113-8170-469d-91dc-2d3a05d577af)
|





## How to test
1. Launch the example app
2. Go to the **Accessibility → Display & Text Size → Larger text**
3. Change the values
4. Go back to the example app to see the applied changes
  • Loading branch information
dmnplb authored Dec 16, 2024
1 parent 03950e8 commit ad30a4a
Show file tree
Hide file tree
Showing 54 changed files with 1,283 additions and 957 deletions.
2 changes: 1 addition & 1 deletion example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|fontScale"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
android:exported="true">
Expand Down
12 changes: 12 additions & 0 deletions example/android/app/src/main/java/com/exampleapp/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactActivityDelegate;

import android.os.Bundle;

public class MainActivity extends ReactActivity {

/**
Expand All @@ -16,6 +18,16 @@ protected String getMainComponentName() {
return "ExampleApp";
}

/**
* Avoid crashing the application when the user changes the `fontScale` attribute
* and the UI is updated accordingly.
* To learn more: https://github.com/pagopa/io-app-design-system/pull/348
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
}

/**
* Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
* DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
Expand Down
7 changes: 7 additions & 0 deletions example/src/pages/Badges.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,5 +142,12 @@ const renderTag = () => (
<Tag text={"Looooooooong string"} variant="error" />
</View>
</ComponentViewerBox>
<ComponentViewerBox name={"Tag, font scaling not allowed"}>
<Tag
text={"Entro il 30 mag"}
variant="warning"
allowFontScaling={false}
/>
</ComponentViewerBox>
</View>
);
3 changes: 3 additions & 0 deletions example/src/pages/ListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ const renderListItemInfoCopy = () => (
}}
accessibilityLabel="Empty just for testing purposes"
/>
<Divider />
<ListItemInfoCopy
label={"Codice fiscale"}
value="01199250158"
Expand All @@ -252,6 +253,7 @@ const renderListItemInfoCopy = () => (
accessibilityLabel="Empty just for testing purposes"
icon="institution"
/>
<Divider />
<ListItemInfoCopy
label={"Carta di credito"}
value="4975 3013 5042 7899"
Expand All @@ -261,6 +263,7 @@ const renderListItemInfoCopy = () => (
accessibilityLabel="Empty just for testing purposes"
icon="creditCard"
/>
<Divider />
<ListItemInfoCopy
label={"Indirizzo"}
value={`P.za Colonna, 370\n00186 Roma (RM)`}
Expand Down
6 changes: 5 additions & 1 deletion jestSetup.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,9 @@ global.AbortController = AbortController;
global.__reanimatedWorkletInit = jest.fn();

jest.mock("./src/utils/accessibility", () => ({
useBoldTextEnabled: () => false
useBoldTextEnabled: () => false,
useIOFontDynamicScale: () => ({
dynamicFontScale: 1,
spacingScaleMultiplier: 1
})
}));
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ exports[`Test Advice Components - Experimental Enabled Advice Snapshot 1`] = `
<Text
allowFontScaling={true}
dynamicTypeRamp="body"
maxFontSizeMultiplier={1.25}
maxFontSizeMultiplier={1.5}
style={
[
{},
Expand Down Expand Up @@ -192,7 +192,7 @@ exports[`Test Advice Components Advice Snapshot 1`] = `
<Text
allowFontScaling={false}
dynamicTypeRamp="body"
maxFontSizeMultiplier={1.25}
maxFontSizeMultiplier={1.5}
style={
[
{},
Expand Down
104 changes: 43 additions & 61 deletions src/components/alert/Alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,26 @@ import {
ColorValue,
GestureResponderEvent,
NativeSyntheticEvent,
PixelRatio,
Pressable,
StyleSheet,
TextLayoutEventData,
View
} from "react-native";
import Animated from "react-native-reanimated";
import { IOVisualCostants, useIOThemeContext } from "../../core";
import { IOColors, hexToRgba } from "../../core/IOColors";
import { IOAlertRadius } from "../../core/IOShapes";
import { IOAlertSpacing } from "../../core/IOSpacing";
import { IOAlertSpacing, IOSpacer } from "../../core/IOSpacing";
import { useScaleAnimation } from "../../hooks";
import { useIOFontDynamicScale } from "../../utils/accessibility";
import { WithTestID } from "../../utils/types";
import { IOIconSizeScale, IOIcons, Icon } from "../icons";
import { VSpacer } from "../spacer";
import { HStack, VStack } from "../stack";
import { Body, ButtonText } from "../typography";
import { H4 } from "../typography/H4";

const iconSize: IOIconSizeScale = 24;
const ICON_SIZE: IOIconSizeScale = 24;

const [spacingDefault, spacingFullWidth] = IOAlertSpacing;

const styles = StyleSheet.create({
container: {
flexDirection: "row",
alignItems: "flex-start",
alignContent: "center"
},
spacingDefault: {
padding: spacingDefault,
borderRadius: IOAlertRadius,
borderCurve: "continuous"
},
spacingFullWidth: {
padding: spacingFullWidth
}
});
const [padding, paddingFullWidth] = IOAlertSpacing;

type AlertProps = WithTestID<{
variant: "error" | "warning" | "info" | "success";
Expand Down Expand Up @@ -140,6 +123,8 @@ export const Alert = forwardRef<View, AlertType>(
): JSX.Element => {
const { onPressIn, onPressOut, scaleAnimatedStyle } =
useScaleAnimation("medium");
const { dynamicFontScale, spacingScaleMultiplier } =
useIOFontDynamicScale();
const { themeType } = useIOThemeContext();

const [isMultiline, setIsMultiline] = useState(false);
Expand All @@ -151,73 +136,71 @@ export const Alert = forwardRef<View, AlertType>(
[]
);

const paddingDefaultVariant = {
padding,
borderRadius: IOAlertRadius * dynamicFontScale * spacingScaleMultiplier,
borderCurve: "continuous"
};

const mapVariantStates =
themeType === "light"
? mapVariantStatesLightMode
: mapVariantStatesDarkMode;

const renderMainBlock = () => (
<>
<View
style={{
marginRight: IOVisualCostants.iconMargin,
alignSelf: "flex-start"
}}
>
<Icon
name={mapVariantStates[variant].icon}
size={iconSize}
color={mapVariantStates[variant].foreground}
/>
</View>
<HStack
space={IOVisualCostants.iconMargin as IOSpacer}
allowScaleSpacing
style={{ alignItems: isMultiline ? "flex-start" : "center" }}
>
<Icon
allowFontScaling
name={mapVariantStates[variant].icon}
size={ICON_SIZE}
color={mapVariantStates[variant].foreground}
/>
{/* Sadly we don't have specific alignments style for text
in React Native, like `text-box-trim` for CSS. So we
have to put these magic numbers after manual adjustments.
Tested on both Android and iOS. */}
<View
style={[
!title &&
isMultiline && { marginTop: -5 * PixelRatio.getFontScale() },
isMultiline && { marginBottom: -3 * PixelRatio.getFontScale() },
!title && isMultiline && { marginTop: -6 * dynamicFontScale },
isMultiline && { marginBottom: -4 * dynamicFontScale },
{ flex: 1 }
]}
>
{title && (
<>
<VStack space={8} allowScaleSpacing>
{title && (
<H4 color={mapVariantStates[variant].foreground}>{title}</H4>
<VSpacer size={8} />
</>
)}
<Body
color={mapVariantStates[variant].foreground}
weight={"Regular"}
accessibilityRole="text"
onTextLayout={onTextLayout}
>
{content}
</Body>
{action && (
<>
<VSpacer size={8} />
)}
<Body
color={mapVariantStates[variant].foreground}
weight={"Regular"}
accessibilityRole="text"
onTextLayout={onTextLayout}
>
{content}
</Body>
{action && (
<ButtonText
color={mapVariantStates[variant].foreground}
numberOfLines={1}
ellipsizeMode="tail"
>
{action}
</ButtonText>
</>
)}
)}
</VStack>
</View>
</>
</HStack>
);

const StaticComponent = () => (
<View
ref={viewRef}
style={[
styles.container,
fullWidth ? styles.spacingFullWidth : styles.spacingDefault,
fullWidth ? { padding } : paddingDefaultVariant,
{ backgroundColor: mapVariantStates[variant].background }
]}
testID={testID}
Expand All @@ -244,8 +227,7 @@ export const Alert = forwardRef<View, AlertType>(
>
<Animated.View
style={[
styles.container,
fullWidth ? styles.spacingFullWidth : styles.spacingDefault,
fullWidth ? { padding: paddingFullWidth } : paddingDefaultVariant,
{ backgroundColor: mapVariantStates[variant].background },
// Disable pressed animation when component is full width
!fullWidth && scaleAnimatedStyle
Expand Down
33 changes: 29 additions & 4 deletions src/components/badge/Badge.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import React from "react";
import { ColorValue, Platform, StyleSheet, View } from "react-native";
import {
ColorValue,
Platform,
StyleSheet,
View,
ViewStyle
} from "react-native";
import {
hexToRgba,
IOBadgeHSpacing,
Expand All @@ -10,12 +16,14 @@ import {
useIOTheme,
useIOThemeContext
} from "../../core";
import { useIOFontDynamicScale } from "../../utils/accessibility";
import { WithTestID } from "../../utils/types";
import { IOText } from "../typography";

export type Badge = WithTestID<{
outline?: boolean;
text: string;
allowFontScaling?: boolean;
variant:
| "default"
| "info"
Expand Down Expand Up @@ -44,12 +52,14 @@ const styles = StyleSheet.create({
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
borderCurve: "continuous",
...Platform.select({
android: {
textAlignVertical: "center"
}
}),
borderCurve: "continuous",
})
},
badgeStaticStyle: {
borderRadius: IOBadgeRadius,
paddingHorizontal: IOBadgeHSpacing,
paddingVertical: IOBadgeVSpacing
Expand All @@ -59,9 +69,16 @@ const styles = StyleSheet.create({
/**
* Official badge component
*/
export const Badge = ({ text, outline = false, variant, testID }: Badge) => {
export const Badge = ({
text,
outline = false,
allowFontScaling = true,
variant,
testID
}: Badge) => {
const { isExperimental } = useIOExperimentalDesign();
const theme = useIOTheme();
const { dynamicFontScale } = useIOFontDynamicScale();
const { themeType } = useIOThemeContext();

const bgOpacityDarkMode = 0.2;
Expand Down Expand Up @@ -237,12 +254,19 @@ export const Badge = ({ text, outline = false, variant, testID }: Badge) => {

const { background, foreground } = variantMap[variant];

const dynamicStyle: ViewStyle = {
borderRadius: IOBadgeRadius * dynamicFontScale,
paddingHorizontal: IOBadgeHSpacing * dynamicFontScale,
paddingVertical: IOBadgeVSpacing * dynamicFontScale
};

return (
<View
accessible={true}
testID={testID}
style={[
styles.badge,
allowFontScaling ? dynamicStyle : styles.badgeStaticStyle,
outline
? {
borderWidth: 1,
Expand All @@ -254,6 +278,7 @@ export const Badge = ({ text, outline = false, variant, testID }: Badge) => {
]}
>
<IOText
allowFontScaling={allowFontScaling}
font={isExperimental ? "Titillio" : "TitilliumSansPro"}
weight={"Semibold"}
size={12}
Expand Down
Loading

0 comments on commit ad30a4a

Please sign in to comment.