@pagopa/io-app-design-system 1.21.0 → 1.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/lib/commonjs/components/Advice/__test__/__snapshots__/advice.test.tsx.snap +2 -2
  2. package/lib/commonjs/components/accordion/AccordionItem.js +1 -1
  3. package/lib/commonjs/components/accordion/AccordionItem.js.map +1 -1
  4. package/lib/commonjs/components/avatar/Avatar.js +21 -8
  5. package/lib/commonjs/components/avatar/Avatar.js.map +1 -1
  6. package/lib/commonjs/components/layout/HeaderSecondLevel.js +2 -0
  7. package/lib/commonjs/components/layout/HeaderSecondLevel.js.map +1 -1
  8. package/lib/commonjs/components/listitems/ListItemHeader.js +2 -1
  9. package/lib/commonjs/components/listitems/ListItemHeader.js.map +1 -1
  10. package/lib/commonjs/components/listitems/ListItemNav.js +13 -3
  11. package/lib/commonjs/components/listitems/ListItemNav.js.map +1 -1
  12. package/lib/commonjs/components/listitems/__test__/__snapshots__/listitem.test.tsx.snap +6 -6
  13. package/lib/commonjs/components/modules/ModuleAttachment.js +196 -0
  14. package/lib/commonjs/components/modules/ModuleAttachment.js.map +1 -0
  15. package/lib/commonjs/components/modules/ModulePaymentNotice.js +7 -5
  16. package/lib/commonjs/components/modules/ModulePaymentNotice.js.map +1 -1
  17. package/lib/commonjs/components/modules/index.js +11 -0
  18. package/lib/commonjs/components/modules/index.js.map +1 -1
  19. package/lib/commonjs/components/tag/Tag.js +5 -2
  20. package/lib/commonjs/components/tag/Tag.js.map +1 -1
  21. package/lib/commonjs/components/toast/ToastNotification.js +7 -3
  22. package/lib/commonjs/components/toast/ToastNotification.js.map +1 -1
  23. package/lib/commonjs/components/typography/Body.js +18 -11
  24. package/lib/commonjs/components/typography/Body.js.map +1 -1
  25. package/lib/commonjs/components/typography/H3.js.map +1 -1
  26. package/lib/commonjs/components/typography/H5.js +20 -13
  27. package/lib/commonjs/components/typography/H5.js.map +1 -1
  28. package/lib/commonjs/components/typography/Label.js +7 -2
  29. package/lib/commonjs/components/typography/Label.js.map +1 -1
  30. package/lib/commonjs/components/typography/LabelLink.js +4 -3
  31. package/lib/commonjs/components/typography/LabelLink.js.map +1 -1
  32. package/lib/commonjs/components/typography/LabelSmall.js +3 -2
  33. package/lib/commonjs/components/typography/LabelSmall.js.map +1 -1
  34. package/lib/commonjs/utils/fonts.js +5 -0
  35. package/lib/commonjs/utils/fonts.js.map +1 -1
  36. package/lib/module/components/Advice/__test__/__snapshots__/advice.test.tsx.snap +2 -2
  37. package/lib/module/components/accordion/AccordionItem.js +1 -1
  38. package/lib/module/components/accordion/AccordionItem.js.map +1 -1
  39. package/lib/module/components/avatar/Avatar.js +22 -8
  40. package/lib/module/components/avatar/Avatar.js.map +1 -1
  41. package/lib/module/components/layout/HeaderSecondLevel.js +2 -0
  42. package/lib/module/components/layout/HeaderSecondLevel.js.map +1 -1
  43. package/lib/module/components/listitems/ListItemHeader.js +2 -1
  44. package/lib/module/components/listitems/ListItemHeader.js.map +1 -1
  45. package/lib/module/components/listitems/ListItemNav.js +13 -3
  46. package/lib/module/components/listitems/ListItemNav.js.map +1 -1
  47. package/lib/module/components/listitems/__test__/__snapshots__/listitem.test.tsx.snap +6 -6
  48. package/lib/module/components/modules/ModuleAttachment.js +186 -0
  49. package/lib/module/components/modules/ModuleAttachment.js.map +1 -0
  50. package/lib/module/components/modules/ModulePaymentNotice.js +7 -5
  51. package/lib/module/components/modules/ModulePaymentNotice.js.map +1 -1
  52. package/lib/module/components/modules/index.js +1 -0
  53. package/lib/module/components/modules/index.js.map +1 -1
  54. package/lib/module/components/tag/Tag.js +5 -2
  55. package/lib/module/components/tag/Tag.js.map +1 -1
  56. package/lib/module/components/toast/ToastNotification.js +7 -3
  57. package/lib/module/components/toast/ToastNotification.js.map +1 -1
  58. package/lib/module/components/typography/Body.js +18 -11
  59. package/lib/module/components/typography/Body.js.map +1 -1
  60. package/lib/module/components/typography/H3.js.map +1 -1
  61. package/lib/module/components/typography/H5.js +20 -13
  62. package/lib/module/components/typography/H5.js.map +1 -1
  63. package/lib/module/components/typography/Label.js +7 -2
  64. package/lib/module/components/typography/Label.js.map +1 -1
  65. package/lib/module/components/typography/LabelLink.js +4 -3
  66. package/lib/module/components/typography/LabelLink.js.map +1 -1
  67. package/lib/module/components/typography/LabelSmall.js +3 -2
  68. package/lib/module/components/typography/LabelSmall.js.map +1 -1
  69. package/lib/module/utils/fonts.js +5 -0
  70. package/lib/module/utils/fonts.js.map +1 -1
  71. package/lib/typescript/components/avatar/Avatar.d.ts +9 -1
  72. package/lib/typescript/components/avatar/Avatar.d.ts.map +1 -1
  73. package/lib/typescript/components/layout/HeaderSecondLevel.d.ts.map +1 -1
  74. package/lib/typescript/components/listitems/ListItemHeader.d.ts.map +1 -1
  75. package/lib/typescript/components/listitems/ListItemNav.d.ts +2 -1
  76. package/lib/typescript/components/listitems/ListItemNav.d.ts.map +1 -1
  77. package/lib/typescript/components/modules/ModuleAttachment.d.ts +32 -0
  78. package/lib/typescript/components/modules/ModuleAttachment.d.ts.map +1 -0
  79. package/lib/typescript/components/modules/ModulePaymentNotice.d.ts.map +1 -1
  80. package/lib/typescript/components/modules/index.d.ts +1 -0
  81. package/lib/typescript/components/modules/index.d.ts.map +1 -1
  82. package/lib/typescript/components/tag/Tag.d.ts +2 -1
  83. package/lib/typescript/components/tag/Tag.d.ts.map +1 -1
  84. package/lib/typescript/components/toast/ToastNotification.d.ts.map +1 -1
  85. package/lib/typescript/components/typography/Body.d.ts.map +1 -1
  86. package/lib/typescript/components/typography/H3.d.ts.map +1 -1
  87. package/lib/typescript/components/typography/H5.d.ts +1 -1
  88. package/lib/typescript/components/typography/H5.d.ts.map +1 -1
  89. package/lib/typescript/components/typography/Label.d.ts +1 -1
  90. package/lib/typescript/components/typography/Label.d.ts.map +1 -1
  91. package/lib/typescript/components/typography/LabelLink.d.ts +1 -1
  92. package/lib/typescript/components/typography/LabelLink.d.ts.map +1 -1
  93. package/lib/typescript/components/typography/LabelSmall.d.ts +1 -1
  94. package/lib/typescript/components/typography/LabelSmall.d.ts.map +1 -1
  95. package/lib/typescript/utils/fonts.d.ts +1 -0
  96. package/lib/typescript/utils/fonts.d.ts.map +1 -1
  97. package/package.json +1 -1
  98. package/src/components/Advice/__test__/__snapshots__/advice.test.tsx.snap +2 -2
  99. package/src/components/accordion/AccordionItem.tsx +1 -1
  100. package/src/components/avatar/Avatar.tsx +31 -13
  101. package/src/components/layout/HeaderSecondLevel.tsx +2 -0
  102. package/src/components/listitems/ListItemHeader.tsx +4 -1
  103. package/src/components/listitems/ListItemNav.tsx +15 -3
  104. package/src/components/listitems/__test__/__snapshots__/listitem.test.tsx.snap +6 -6
  105. package/src/components/modules/ModuleAttachment.tsx +254 -0
  106. package/src/components/modules/ModulePaymentNotice.tsx +14 -13
  107. package/src/components/modules/index.tsx +1 -0
  108. package/src/components/tag/Tag.tsx +9 -2
  109. package/src/components/toast/ToastNotification.tsx +9 -2
  110. package/src/components/typography/Body.tsx +10 -5
  111. package/src/components/typography/H3.tsx +1 -0
  112. package/src/components/typography/H5.tsx +9 -5
  113. package/src/components/typography/Label.tsx +13 -5
  114. package/src/components/typography/LabelLink.tsx +5 -4
  115. package/src/components/typography/LabelSmall.tsx +4 -5
  116. package/src/utils/fonts.ts +5 -0
@@ -82,7 +82,7 @@ exports[`Test Advice Components - Experimental Enabled Advice Snapshot 1`] = `
82
82
  color="bluegrey"
83
83
  defaultColor="bluegrey"
84
84
  defaultWeight="Regular"
85
- font="TitilliumWeb"
85
+ font="TitilliumSansPro"
86
86
  fontStyle={
87
87
  {
88
88
  "fontSize": 16,
@@ -97,7 +97,7 @@ exports[`Test Advice Components - Experimental Enabled Advice Snapshot 1`] = `
97
97
  },
98
98
  {
99
99
  "color": "#475A6D",
100
- "fontFamily": "Titillium Web",
100
+ "fontFamily": "Titillium Sans Pro",
101
101
  "fontStyle": "normal",
102
102
  "fontWeight": "400",
103
103
  },
@@ -174,7 +174,7 @@ const styles = StyleSheet.create({
174
174
  fontSize: 14,
175
175
  lineHeight: 21,
176
176
  color: IOColors["grey-700"],
177
- ...makeFontStyleObject("Regular", undefined, "TitilliumWeb")
177
+ ...makeFontStyleObject("Regular", undefined, "TitilliumSansPro")
178
178
  },
179
179
  textContainer: {
180
180
  padding: accordionBodySpacing,
@@ -1,20 +1,19 @@
1
- // A component to provide organization logo
2
1
  import * as React from "react";
3
2
  import { Image, ImageURISource, StyleSheet, View } from "react-native";
4
3
  import { Icon } from "../../components/icons";
5
4
  import {
6
5
  IOColors,
7
6
  IOSpacingScale,
8
- IOThemeContext,
9
7
  IOVisualCostants,
10
- hexToRgba
8
+ hexToRgba,
9
+ useIOTheme
11
10
  } from "../../core";
12
11
  import { addCacheTimestampToUri } from "../../utils/image";
13
12
 
14
13
  type Avatar = {
15
14
  shape: "circle" | "square";
16
15
  size: "small" | "medium";
17
- logoUri?: ImageURISource;
16
+ logoUri?: ImageURISource | ReadonlyArray<ImageURISource>;
18
17
  };
19
18
 
20
19
  const avatarBorderLightMode = hexToRgba(IOColors.black, 0.1);
@@ -55,16 +54,33 @@ const styles = StyleSheet.create({
55
54
  }
56
55
  });
57
56
 
57
+ /**
58
+ * Avatar component is used to display the logo of an organization. It accepts the following props:
59
+ * - `logoUri`: the uri of the image to display. If not provided, a placeholder icon will be displayed. It can be a single uri or an array of uris, in which case the first one that is available will be used.
60
+ * - `shape`: the shape of the avatar, can be "circle" or "square"
61
+ * - `size`: the size of the avatar, can be "small" or "medium"
62
+ * @param AvatarProps
63
+ * @returns
64
+ */
58
65
  export const Avatar = ({ logoUri, shape, size }: Avatar) => {
59
- const theme = React.useContext(IOThemeContext);
66
+ const theme = useIOTheme();
67
+ const indexValue = React.useRef<number>(0);
60
68
 
61
69
  const [imageSource, setImageSource] = React.useState(
62
- !logoUri ? undefined : addCacheTimestampToUri(logoUri)
70
+ logoUri === undefined
71
+ ? undefined
72
+ : Array.isArray(logoUri)
73
+ ? addCacheTimestampToUri(logoUri[0])
74
+ : addCacheTimestampToUri(logoUri as ImageURISource)
63
75
  );
64
76
 
65
- const isPlaceholder = !imageSource;
66
-
67
77
  const onError = () => {
78
+ if (Array.isArray(logoUri) && indexValue.current + 1 < logoUri.length) {
79
+ // eslint-disable-next-line functional/immutable-data
80
+ indexValue.current = indexValue.current + 1;
81
+ setImageSource(addCacheTimestampToUri(logoUri[indexValue.current]));
82
+ return;
83
+ }
68
84
  setImageSource(undefined);
69
85
  };
70
86
 
@@ -79,14 +95,16 @@ export const Avatar = ({ logoUri, shape, size }: Avatar) => {
79
95
  shape === "circle"
80
96
  ? getAvatarCircleShape(size)
81
97
  : dimensionsMap[size].radius,
82
- backgroundColor: isPlaceholder ? IOColors["grey-50"] : IOColors.white,
83
- padding: isPlaceholder
84
- ? dimensionsMap[size].internalSpacePlaceholder
85
- : dimensionsMap[size].internalSpace
98
+ backgroundColor:
99
+ imageSource === undefined ? IOColors["grey-50"] : IOColors.white,
100
+ padding:
101
+ imageSource === undefined
102
+ ? dimensionsMap[size].internalSpacePlaceholder
103
+ : dimensionsMap[size].internalSpace
86
104
  }
87
105
  ]}
88
106
  >
89
- {isPlaceholder ? (
107
+ {imageSource === undefined ? (
90
108
  <Icon
91
109
  name="institution"
92
110
  color={theme["icon-decorative"]}
@@ -143,6 +143,7 @@ export const HeaderSecondLevel = ({
143
143
  : "transparent"
144
144
  }));
145
145
 
146
+ const isTitleAccessible = !!title.trim();
146
147
  const titleAnimatedStyle = useAnimatedStyle(() => ({
147
148
  opacity: scrollValues
148
149
  ? interpolate(
@@ -183,6 +184,7 @@ export const HeaderSecondLevel = ({
183
184
  )}
184
185
  <Animated.Text
185
186
  numberOfLines={1}
187
+ accessible={isTitleAccessible}
186
188
  style={[
187
189
  styles.headerTitle,
188
190
  isExperimental
@@ -125,7 +125,10 @@ export const ListItemHeader = ({
125
125
  accessible={endElement === undefined ? true : false}
126
126
  accessibilityLabel={listItemAccessibilityLabel}
127
127
  >
128
- <View style={IOListItemStyles.listItemInner}>
128
+ <View
129
+ style={IOListItemStyles.listItemInner}
130
+ importantForAccessibility={endElement ? "auto" : "no-hide-descendants"}
131
+ >
129
132
  {iconName && (
130
133
  <View style={{ marginRight: iconMargin }}>
131
134
  <Icon
@@ -26,6 +26,7 @@ import { IOIcons, Icon } from "../icons";
26
26
  import { IOLogoPaymentType, LogoPayment } from "../logos";
27
27
  import { HSpacer, VSpacer } from "../spacer";
28
28
  import { Caption, H6, LabelSmall } from "../typography";
29
+ import { LoadingSpinner } from "../loadingSpinner";
29
30
 
30
31
  type ListItemTopElementProps =
31
32
  | {
@@ -40,6 +41,7 @@ type ListItemTopElementProps =
40
41
  type ListItemNavPartialProps = WithTestID<{
41
42
  value: string | React.ReactNode;
42
43
  description?: string | React.ReactNode;
44
+ loading?: boolean;
43
45
  onPress: (event: GestureResponderEvent) => void;
44
46
  // Accessibility
45
47
  accessibilityLabel: string;
@@ -64,7 +66,8 @@ export const ListItemNav = ({
64
66
  accessibilityLabel,
65
67
  testID,
66
68
  hideChevron = false,
67
- topElement
69
+ topElement,
70
+ loading
68
71
  }: ListItemNav) => {
69
72
  const isPressed = useSharedValue(0);
70
73
  const { isExperimental } = useIOExperimentalDesign();
@@ -167,9 +170,17 @@ export const ListItemNav = ({
167
170
  isPressed.value = 0;
168
171
  }, [isPressed]);
169
172
 
173
+ const handleOnPress = (event: GestureResponderEvent) => {
174
+ if (!loading) {
175
+ onPress(event);
176
+ }
177
+ };
178
+
179
+ const primaryColor: IOColors = isExperimental ? "blueIO-500" : "blue";
180
+
170
181
  return (
171
182
  <Pressable
172
- onPress={onPress}
183
+ onPress={handleOnPress}
173
184
  onPressIn={handlePressIn}
174
185
  onPressOut={handlePressOut}
175
186
  accessible={true}
@@ -201,7 +212,8 @@ export const ListItemNav = ({
201
212
  </View>
202
213
  )}
203
214
  <View style={IOStyles.flex}>{listItemNavContent}</View>
204
- {!hideChevron && (
215
+ {loading && <LoadingSpinner color={primaryColor} />}
216
+ {!loading && !hideChevron && (
205
217
  <View style={{ marginLeft: IOListItemVisualParams.iconMargin }}>
206
218
  <Icon
207
219
  name="chevronRightListItem"
@@ -233,7 +233,7 @@ exports[`Test List Item Components - Experimental Enabled ListItemInfo Snapshot
233
233
  color="grey-700"
234
234
  defaultColor="blue"
235
235
  defaultWeight="Bold"
236
- font="TitilliumWeb"
236
+ font="TitilliumSansPro"
237
237
  fontStyle={
238
238
  {
239
239
  "fontSize": 14,
@@ -248,7 +248,7 @@ exports[`Test List Item Components - Experimental Enabled ListItemInfo Snapshot
248
248
  },
249
249
  {
250
250
  "color": "#555C70",
251
- "fontFamily": "Titillium Web",
251
+ "fontFamily": "Titillium Sans Pro",
252
252
  "fontStyle": "normal",
253
253
  "fontWeight": "400",
254
254
  },
@@ -373,7 +373,7 @@ exports[`Test List Item Components - Experimental Enabled ListItemInfoCopy Snap
373
373
  color="grey-700"
374
374
  defaultColor="blue"
375
375
  defaultWeight="Bold"
376
- font="TitilliumWeb"
376
+ font="TitilliumSansPro"
377
377
  fontStyle={
378
378
  {
379
379
  "fontSize": 14,
@@ -388,7 +388,7 @@ exports[`Test List Item Components - Experimental Enabled ListItemInfoCopy Snap
388
388
  },
389
389
  {
390
390
  "color": "#555C70",
391
- "fontFamily": "Titillium Web",
391
+ "fontFamily": "Titillium Sans Pro",
392
392
  "fontStyle": "normal",
393
393
  "fontWeight": "400",
394
394
  },
@@ -1076,7 +1076,7 @@ exports[`Test List Item Components - Experimental Enabled ListItemRadioWithAmou
1076
1076
  color="hanPurple-500"
1077
1077
  defaultColor="blue"
1078
1078
  defaultWeight="Bold"
1079
- font="TitilliumWeb"
1079
+ font="TitilliumSansPro"
1080
1080
  fontStyle={
1081
1081
  {
1082
1082
  "fontSize": 14,
@@ -1091,7 +1091,7 @@ exports[`Test List Item Components - Experimental Enabled ListItemRadioWithAmou
1091
1091
  },
1092
1092
  {
1093
1093
  "color": "#5517E3",
1094
- "fontFamily": "Titillium Web",
1094
+ "fontFamily": "Titillium Sans Pro",
1095
1095
  "fontStyle": "normal",
1096
1096
  "fontWeight": "400",
1097
1097
  },
@@ -0,0 +1,254 @@
1
+ import React, { useCallback } from "react";
2
+ import {
3
+ GestureResponderEvent,
4
+ Pressable,
5
+ PressableProps,
6
+ StyleSheet,
7
+ View
8
+ } from "react-native";
9
+ import Animated, {
10
+ Extrapolate,
11
+ interpolate,
12
+ useAnimatedStyle,
13
+ useDerivedValue,
14
+ useSharedValue,
15
+ withSpring
16
+ } from "react-native-reanimated";
17
+ import Placeholder from "rn-placeholder";
18
+ import {
19
+ IOColors,
20
+ IOListItemVisualParams,
21
+ IOScaleValues,
22
+ IOSpringValues,
23
+ useIOTheme
24
+ } from "../../core";
25
+ import { WithTestID } from "../../utils/types";
26
+ import { Icon } from "../icons";
27
+ import { LabelSmall } from "../typography";
28
+ import { VSpacer } from "../spacer";
29
+ import { Badge } from "../badge";
30
+ import { LoadingSpinner } from "../loadingSpinner";
31
+
32
+ type PartialProps = WithTestID<{
33
+ title: string;
34
+ format: "doc" | "pdf";
35
+ isLoading?: boolean;
36
+ loadingAccessibilityLabel?: string;
37
+ isFetching?: boolean;
38
+ fetchingAccessibilityLabel?: string;
39
+ onPress: (event: GestureResponderEvent) => void;
40
+ }>;
41
+
42
+ export type ModuleAttachmentProps = PartialProps &
43
+ Pick<
44
+ PressableProps,
45
+ "onPress" | "accessibilityLabel" | "disabled" | "testID"
46
+ >;
47
+
48
+ type SkeletonComponentProps = {
49
+ loadingAccessibilityLabel?: string;
50
+ };
51
+
52
+ const styles = StyleSheet.create({
53
+ button: {
54
+ flexDirection: "row",
55
+ alignItems: "center",
56
+ paddingHorizontal: 16,
57
+ paddingVertical: 16,
58
+ borderRadius: 8,
59
+ borderColor: IOColors.bluegreyLight,
60
+ backgroundColor: IOColors.white,
61
+ borderStyle: "solid",
62
+ borderWidth: 1
63
+ },
64
+ innerContent: {
65
+ flex: 1,
66
+ flexDirection: "column"
67
+ },
68
+ rightSection: {
69
+ marginLeft: IOListItemVisualParams.iconMargin,
70
+ alignItems: "center"
71
+ }
72
+ });
73
+
74
+ const DISABLED_OPACITY = 0.5;
75
+
76
+ const ModuleAttachmentContent = ({
77
+ isFetching,
78
+ format,
79
+ title,
80
+ testID
81
+ }: Pick<
82
+ ModuleAttachmentProps,
83
+ "isFetching" | "format" | "title" | "testID"
84
+ >) => {
85
+ const theme = useIOTheme();
86
+ const IconOrActivityIndicatorComponent = () => {
87
+ if (isFetching) {
88
+ const activityIndicatorTestId = testID
89
+ ? `${testID}_activityIndicator`
90
+ : undefined;
91
+ return <LoadingSpinner testID={activityIndicatorTestId} />;
92
+ }
93
+
94
+ return (
95
+ <Icon
96
+ name="chevronRightListItem"
97
+ color={theme["interactiveElem-default"]}
98
+ size={IOListItemVisualParams.chevronSize}
99
+ />
100
+ );
101
+ };
102
+
103
+ return (
104
+ <>
105
+ <View style={styles.innerContent}>
106
+ <LabelSmall
107
+ numberOfLines={1}
108
+ weight="SemiBold"
109
+ font="ReadexPro"
110
+ color="blueIO-500"
111
+ >
112
+ {title}
113
+ </LabelSmall>
114
+ <VSpacer size={4} />
115
+ <View style={{ width: 44 }}>
116
+ <Badge text={format.toUpperCase()} variant="default" />
117
+ </View>
118
+ </View>
119
+ <View style={styles.rightSection}>
120
+ <IconOrActivityIndicatorComponent />
121
+ </View>
122
+ </>
123
+ );
124
+ };
125
+
126
+ /**
127
+ * The `ModuleAttachment` component is a custom button component with an extended outline style.
128
+ * It provides an animated scaling effect when pressed.
129
+ *
130
+ * @param {string} accessibilityLabel - Optional accessibility label.
131
+ * @param {boolean} disabled - If true, the button is disabled.
132
+ * @param {string} fetchingAccessibilityLabel - Optional accessibility label to use during fetching.
133
+ * @param {string} format - Badge content. PDF or DOC.
134
+ * @param {boolean} isLoading - If true, displays a skeleton loading component.
135
+ * @param {boolean} isFetching - If true, displays an activity indicator.
136
+ * @param {string} loadingAccessibilityLabel - Optional accessibility label to use during loading.
137
+ * @param {function} onPress - The function to be executed when the item is pressed.
138
+ * @param {string} testID - The test ID for testing purposes.
139
+ * @param {string} title - The title text to display.
140
+ *
141
+ */
142
+ export const ModuleAttachment = ({
143
+ accessibilityLabel,
144
+ disabled = false,
145
+ fetchingAccessibilityLabel,
146
+ format,
147
+ isLoading = false,
148
+ isFetching = false,
149
+ loadingAccessibilityLabel,
150
+ onPress,
151
+ testID,
152
+ title
153
+ }: ModuleAttachmentProps) => {
154
+ const isPressed: Animated.SharedValue<number> = useSharedValue(0);
155
+
156
+ // Scaling transformation applied when the button is pressed
157
+ const animationScaleValue = IOScaleValues?.magnifiedButton?.pressedState;
158
+
159
+ const scaleTraversed = useDerivedValue(() =>
160
+ withSpring(isPressed.value, IOSpringValues.button)
161
+ );
162
+
163
+ // Interpolate animation values from `isPressed` values
164
+ const animatedStyle = useAnimatedStyle(() => {
165
+ const scale = interpolate(
166
+ scaleTraversed.value,
167
+ [0, 1],
168
+ [1, animationScaleValue],
169
+ Extrapolate.CLAMP
170
+ );
171
+
172
+ return {
173
+ transform: [{ scale }]
174
+ };
175
+ });
176
+
177
+ const onPressIn = useCallback(() => {
178
+ // eslint-disable-next-line functional/immutable-data
179
+ isPressed.value = 1;
180
+ }, [isPressed]);
181
+
182
+ const onPressOut = useCallback(() => {
183
+ // eslint-disable-next-line functional/immutable-data
184
+ isPressed.value = 0;
185
+ }, [isPressed]);
186
+
187
+ const handleOnPress = useCallback(
188
+ (event: GestureResponderEvent) => {
189
+ if (isFetching) {
190
+ return;
191
+ }
192
+ onPress(event);
193
+ },
194
+ [isFetching, onPress]
195
+ );
196
+
197
+ if (isLoading) {
198
+ return (
199
+ <SkeletonComponent
200
+ loadingAccessibilityLabel={loadingAccessibilityLabel}
201
+ />
202
+ );
203
+ }
204
+
205
+ const pressableAccessibilityLabel =
206
+ (isFetching && !!fetchingAccessibilityLabel
207
+ ? fetchingAccessibilityLabel
208
+ : accessibilityLabel) ?? title;
209
+ return (
210
+ <Pressable
211
+ testID={testID}
212
+ onPress={handleOnPress}
213
+ onPressIn={onPressIn}
214
+ onPressOut={onPressOut}
215
+ accessible={true}
216
+ accessibilityRole={"button"}
217
+ accessibilityHint={format}
218
+ accessibilityLabel={pressableAccessibilityLabel}
219
+ disabled={disabled || isFetching}
220
+ >
221
+ <Animated.View
222
+ style={[
223
+ styles.button,
224
+ animatedStyle,
225
+ { opacity: disabled ? DISABLED_OPACITY : 1 }
226
+ ]}
227
+ accessibilityElementsHidden={true}
228
+ importantForAccessibility="no-hide-descendants"
229
+ >
230
+ <ModuleAttachmentContent
231
+ isFetching={isFetching}
232
+ title={title}
233
+ format={format}
234
+ />
235
+ </Animated.View>
236
+ </Pressable>
237
+ );
238
+ };
239
+
240
+ const SkeletonComponent = ({
241
+ loadingAccessibilityLabel
242
+ }: SkeletonComponentProps) => (
243
+ <View
244
+ style={styles.button}
245
+ accessible={true}
246
+ accessibilityLabel={loadingAccessibilityLabel}
247
+ >
248
+ <View style={styles.innerContent}>
249
+ <Placeholder.Box animate="fade" radius={8} width={107} height={22} />
250
+ <VSpacer size={4} />
251
+ <Placeholder.Box animate="fade" radius={8} width={44} height={22} />
252
+ </View>
253
+ </View>
254
+ );
@@ -1,18 +1,18 @@
1
1
  import * as React from "react";
2
2
  import { GestureResponderEvent, StyleSheet, View } from "react-native";
3
3
  import Placeholder from "rn-placeholder";
4
- import { WithTestID } from "../../utils/types";
5
- import { getAccessibleAmountText } from "../../utils/accessibility";
6
- import { H6, LabelSmall } from "../typography";
7
- import { Badge } from "../badge";
8
- import { Icon } from "../icons";
9
4
  import {
10
5
  IOListItemVisualParams,
11
6
  IOModuleStyles,
12
7
  IOStyles,
13
8
  useIOExperimentalDesign
14
9
  } from "../../core";
10
+ import { getAccessibleAmountText } from "../../utils/accessibility";
11
+ import { WithTestID } from "../../utils/types";
12
+ import { Badge } from "../badge";
13
+ import { Icon } from "../icons";
15
14
  import { VSpacer } from "../spacer";
15
+ import { H6, LabelSmall, LabelSmallAlt } from "../typography";
16
16
  import { PressableModuleBase } from "./PressableModuleBase";
17
17
 
18
18
  export type PaymentNoticeStatus =
@@ -97,14 +97,15 @@ const ModulePaymentNoticeContent = ({
97
97
  {title}
98
98
  </LabelSmall>
99
99
  )}
100
- <LabelSmall
101
- weight="SemiBold"
102
- font={isExperimental ? "ReadexPro" : "TitilliumWeb"}
103
- color={isExperimental ? "blueIO-500" : "bluegrey"}
104
- numberOfLines={2}
105
- >
106
- {subtitle}
107
- </LabelSmall>
100
+ {isExperimental ? (
101
+ <LabelSmallAlt color={"blueIO-500"} numberOfLines={2}>
102
+ {subtitle}
103
+ </LabelSmallAlt>
104
+ ) : (
105
+ <LabelSmall weight="SemiBold" color={"bluegrey"} numberOfLines={2}>
106
+ {subtitle}
107
+ </LabelSmall>
108
+ )}
108
109
  </View>
109
110
  <View style={[styles.rightSection, { flexShrink: 1 }]}>
110
111
  <AmountOrBadgeComponent />
@@ -2,3 +2,4 @@ export * from "./ModuleIDP";
2
2
  export * from "./ModulePaymentNotice";
3
3
  export * from "./PressableModuleBase";
4
4
  export * from "./ModuleCheckout";
5
+ export * from "./ModuleAttachment";
@@ -23,6 +23,7 @@ export type Tag = WithTestID<{
23
23
  | "success"
24
24
  | "attachment"
25
25
  | "noIcon";
26
+ iconAccessibilityLabel?: string;
26
27
  }>;
27
28
 
28
29
  type VariantProps = {
@@ -112,7 +113,7 @@ const styles = StyleSheet.create({
112
113
  /**
113
114
  * Tag component, used mainly for message list and details
114
115
  */
115
- export const Tag = ({ text, variant, testID }: Tag) => {
116
+ export const Tag = ({ text, variant, testID, iconAccessibilityLabel }: Tag) => {
116
117
  const { isExperimental } = useIOExperimentalDesign();
117
118
  return (
118
119
  <View testID={testID} style={styles.tag}>
@@ -123,7 +124,13 @@ export const Tag = ({ text, variant, testID }: Tag) => {
123
124
  () => null,
124
125
  ({ iconColor, iconName }) => (
125
126
  <View style={styles.iconWrapper}>
126
- <Icon name={iconName} color={iconColor} size={IOTagIconSize} />
127
+ <Icon
128
+ name={iconName}
129
+ color={iconColor}
130
+ size={IOTagIconSize}
131
+ accessible={!!iconAccessibilityLabel}
132
+ accessibilityLabel={iconAccessibilityLabel}
133
+ />
127
134
  </View>
128
135
  )
129
136
  )
@@ -1,7 +1,8 @@
1
1
  import React from "react";
2
2
  import { StyleSheet, View } from "react-native";
3
+ import { IOAlertRadius, IOColors } from "../../core";
3
4
  import { Icon } from "../icons";
4
- import { IOColors, IOAlertRadius } from "../../core";
5
+ import { HSpacer } from "../spacer";
5
6
  import { ButtonText } from "../typography";
6
7
  import { Toast, ToastVariant } from "./types";
7
8
 
@@ -54,7 +55,12 @@ const ToastNotification = ({ message, variant = "neutral", icon }: Props) => {
54
55
  <ButtonText color={colors.stroke} style={styles.content}>
55
56
  {message}
56
57
  </ButtonText>
57
- {icon && <Icon name={icon} size={24} color={colors.stroke} />}
58
+ {icon && (
59
+ <>
60
+ <HSpacer size={16} />
61
+ <Icon name={icon} size={24} color={colors.stroke} />
62
+ </>
63
+ )}
58
64
  </View>
59
65
  );
60
66
  };
@@ -69,6 +75,7 @@ const styles = StyleSheet.create({
69
75
  justifyContent: "space-between"
70
76
  },
71
77
  content: {
78
+ flexShrink: 1,
72
79
  paddingVertical: 2
73
80
  }
74
81
  });
@@ -1,4 +1,4 @@
1
- import { IOColors, IOTheme } from "../../core";
1
+ import { IOColors, IOTheme, useIOExperimentalDesign } from "../../core";
2
2
  import { FontFamily, IOFontWeight } from "../../utils/fonts";
3
3
  import { useTypographyFactory } from "./Factory";
4
4
  import { ExternalTypographyProps, TypographyProps } from "./common";
@@ -14,7 +14,9 @@ type BodyProps = ExternalTypographyProps<
14
14
  TypographyProps<AllowedWeight, AllowedColors>
15
15
  >;
16
16
 
17
- const fontName: FontFamily = "TitilliumWeb";
17
+ const fontName: FontFamily = "TitilliumSansPro";
18
+ const legacyFontName: FontFamily = "TitilliumWeb";
19
+
18
20
  export const bodyFontSize = 16;
19
21
  export const bodyLineHeight = 24;
20
22
  export const bodyDefaultColor: AllowedColors = "bluegrey";
@@ -23,11 +25,14 @@ export const bodyDefaultWeight: AllowedWeight = "Regular";
23
25
  /**
24
26
  * `Body` typographic style
25
27
  */
26
- export const Body = (props: BodyProps) =>
27
- useTypographyFactory<AllowedWeight, AllowedColors>({
28
+ export const Body = (props: BodyProps) => {
29
+ const { isExperimental } = useIOExperimentalDesign();
30
+
31
+ return useTypographyFactory<AllowedWeight, AllowedColors>({
28
32
  ...props,
29
33
  defaultWeight: bodyDefaultWeight,
30
34
  defaultColor: bodyDefaultColor,
31
- font: fontName,
35
+ font: isExperimental ? fontName : legacyFontName,
32
36
  fontStyle: { fontSize: bodyFontSize, lineHeight: bodyLineHeight }
33
37
  });
38
+ };
@@ -28,6 +28,7 @@ const legacyH3LineHeight = 34;
28
28
  */
29
29
  export const H3 = (props: H3Props) => {
30
30
  const { isExperimental } = useIOExperimentalDesign();
31
+
31
32
  return useTypographyFactory<AllowedWeight, AllowedColors>({
32
33
  ...props,
33
34
  defaultWeight: isExperimental ? defaultWeight : legacyDefaultWeight,